This commit is contained in:
Hanh 2021-06-29 12:49:00 +08:00
parent 3432ea15bd
commit f1d948c0f6
9 changed files with 254 additions and 43 deletions

View File

@ -216,18 +216,19 @@ impl Builder<Witness, CTreeBuilder> for WitnessBuilder {
}
}
let right = if depth != 0 && !context.first_block {
context.right
} else {
None
};
// println!("D {}", depth);
// println!("O {:?}", offset.map(|r| hex::encode(r.repr)));
// println!("R {:?}", right.map(|r| hex::encode(r.repr)));
// for c in commitments.iter() {
// println!("{}", hex::encode(c.repr));
// }
let right = if depth != 0 && !context.first_block {
context.right
} else {
None
};
let p1 = self.p + 1;
// println!("P {} P1 {} S {} AS {}", self.p, p1, context.start, context.adjusted_start(&right));
let has_p1 = p1 >= context.adjusted_start(&right) && p1 < context.start + commitments.len();
if has_p1 {
let p1 = CTreeBuilder::get(commitments, p1 - context.adjusted_start(&right), &right);
@ -342,6 +343,7 @@ impl BlockProcessor {
}
pub fn add_nodes(&mut self, nodes: &mut [Node], new_witnesses: &[Witness]) {
if nodes.is_empty() { return }
self.prev_witnesses.extend_from_slice(new_witnesses);
let (t, ws) = advance_tree(
&self.prev_tree,
@ -349,13 +351,19 @@ impl BlockProcessor {
nodes,
self.first_block,
);
self.first_block = false;
self.prev_tree = t;
self.prev_witnesses = ws;
}
pub fn finalize(self) -> (CTree, Vec<Witness>) {
let (t, ws) = advance_tree(&self.prev_tree, &self.prev_witnesses, &mut [], false);
(t, ws)
if self.first_block {
(self.prev_tree, self.prev_witnesses)
}
else {
let (t, ws) = advance_tree(&self.prev_tree, &self.prev_witnesses, &mut [], false);
(t, ws)
}
}
}
@ -369,6 +377,121 @@ mod tests {
use zcash_primitives::merkle_tree::{CommitmentTree, IncrementalWitness};
use zcash_primitives::sapling::Node;
fn make_nodes(p: usize, len: usize) -> Vec<Node> {
let nodes: Vec<_> = (p..p+len).map(|v| {
let mut bb = [0u8; 32];
bb[0..8].copy_from_slice(&v.to_be_bytes());
Node::new(bb)
}).collect();
nodes
}
fn make_witnesses(p: usize, len: usize) -> Vec<Witness> {
let witnesses: Vec<_> = (p..p+len).map(|v| {
Witness::new(v, 0, None)
}).collect();
witnesses
}
fn update_witnesses1(tree: &mut CommitmentTree<Node>, ws: &mut Vec<IncrementalWitness<Node>>, nodes: &[Node]) {
for n in nodes.iter() {
tree.append(n.clone()).unwrap();
for w in ws.iter_mut() {
w.append(n.clone()).unwrap();
}
let w = IncrementalWitness::<Node>::from_tree(&tree);
ws.push(w);
}
}
fn compare_witness(w1: &IncrementalWitness<Node>, w2: &Witness) {
let mut bb1: Vec<u8> = vec![];
w1.write(&mut bb1).unwrap();
let mut bb2: Vec<u8> = vec![];
w2.write(&mut bb2).unwrap();
if bb1 != bb2 {
print_witness(&w1);
print_witness2(&w2);
assert!(false);
}
}
#[test]
fn test_simple() {
let v = [0u8; 32];
let mut bp = BlockProcessor::new(&CTree::new(), &[]);
let mut nodes = [Node::new(v)];
bp.add_nodes(&mut [], &[]);
bp.add_nodes(&mut nodes, &[Witness::new(0, 0, None)]);
bp.finalize();
}
#[test]
fn test_bp_1run() {
for n1 in 0..=40 {
for n2 in 0..=40 {
println!("{} {}", n1, n2);
let mut bp = BlockProcessor::new(&CTree::new(), &[]);
let mut tree1: CommitmentTree<Node> = CommitmentTree::empty();
let mut ws1: Vec<IncrementalWitness<Node>> = vec![];
let mut nodes = make_nodes(0, n1);
update_witnesses1(&mut tree1, &mut ws1, &nodes);
bp.add_nodes(&mut nodes, &make_witnesses(0, n1));
let mut nodes = make_nodes(n1, n2);
update_witnesses1(&mut tree1, &mut ws1, &nodes);
bp.add_nodes(&mut nodes, &make_witnesses(n1, n2));
let (_, ws2) = bp.finalize();
for (i, (w1, w2)) in ws1.iter().zip(ws2.iter()).enumerate() {
println!("Compare {}", i);
compare_witness(w1, w2);
}
}
}
}
#[test]
fn test_bp_2run() {
for n1 in 0..=40 {
for n2 in 0..=40 {
println!("{} {}", n1, n2);
let mut tree1: CommitmentTree<Node> = CommitmentTree::empty();
let mut ws1: Vec<IncrementalWitness<Node>> = vec![];
let mut tree2 = CTree::new();
let mut ws2: Vec<Witness> = vec![];
{
let mut bp = BlockProcessor::new(&tree2, &ws2);
let mut nodes = make_nodes(0, n1);
update_witnesses1(&mut tree1, &mut ws1, &nodes);
bp.add_nodes(&mut nodes, &make_witnesses(0, n1));
let (t2, w2) = bp.finalize();
tree2 = t2;
ws2 = w2;
}
{
let mut bp = BlockProcessor::new(&tree2, &ws2);
let mut nodes = make_nodes(n1, n2);
update_witnesses1(&mut tree1, &mut ws1, &nodes);
bp.add_nodes(&mut nodes, &make_witnesses(n1, n2));
let (_t2, w2) = bp.finalize();
ws2 = w2;
}
for (i, (w1, w2)) in ws1.iter().zip(ws2.iter()).enumerate() {
println!("Compare {}", i);
compare_witness(w1, w2);
}
}
}
}
#[test]
fn test_advance_tree_equal_blocks() {
for num_nodes in 1..=10 {
@ -380,8 +503,8 @@ mod tests {
#[test]
fn test_advance_tree_unequal_blocks() {
for num_nodes1 in 1..=30 {
for num_nodes2 in 1..=30 {
for num_nodes1 in 0..=30 {
for num_nodes2 in 0..=30 {
println!("TESTING {} {}", num_nodes1, num_nodes2);
let (t, ws) = test_advance_tree_helper(num_nodes1, 1, 100.0, None);
test_advance_tree_helper(num_nodes2, 1, 100.0, Some((t, ws)));
@ -390,8 +513,16 @@ mod tests {
}
#[test]
fn test_advance_tree() {
test_advance_tree_helper(100, 50, 1.0, None);
fn test_small_blocks() {
for num_nodes1 in 1..=30 {
println!("TESTING {}", num_nodes1);
test_advance_tree_helper(num_nodes1, 1, 100.0, None);
}
}
#[test]
fn test_tree() {
test_advance_tree_helper(4, 1, 100.0, None);
// test_advance_tree_helper(2, 10, 100.0);
// test_advance_tree_helper(1, 40, 100.0);

View File

@ -19,10 +19,11 @@ use zcash_primitives::transaction::components::sapling::CompactOutputDescription
use zcash_primitives::zip32::ExtendedFullViewingKey;
const MAX_CHUNK: u32 = 50000;
pub const LWD_URL: &str = "https://mainnet.lightwalletd.com:9067";
// pub const LWD_URL: &str = "https://testnet.lightwalletd.com:9067";
// pub const LWD_URL: &str = "http://lwd.hanh.me:9067";
// pub const LWD_URL: &str = "https://lwdv3.zecwallet.co";
pub const LWD_URL: &str = "http://127.0.0.1:9067";
// pub const LWD_URL: &str = "http://127.0.0.1:9067";
pub async fn get_latest_height(
client: &mut CompactTxStreamerClient<Channel>,
@ -211,7 +212,13 @@ pub async fn send_transaction(
.send_transaction(Request::new(raw_tx))
.await?
.into_inner();
Ok(rep.error_message)
let code = rep.error_code;
if code == 0 {
Ok(rep.error_message)
}
else {
Err(anyhow::anyhow!(rep.error_message))
}
}
/* Using the IncrementalWitness */

View File

@ -98,19 +98,20 @@ impl Witness {
}
}
pub fn read<R: Read>(position: usize, id_note: u32, mut reader: R) -> std::io::Result<Self> {
pub fn read<R: Read>(id_note: u32, mut reader: R) -> std::io::Result<Self> {
let tree = CTree::read(&mut reader)?;
let filled = Vector::read(&mut reader, |r| Node::read(r))?;
let cursor = Optional::read(&mut reader, |r| CTree::read(r))?;
let witness = Witness {
position,
let mut witness = Witness {
position: 0,
id_note,
tree,
filled,
cursor: cursor.unwrap_or_else(CTree::new),
note: None,
};
witness.position = witness.tree.get_position() - 1;
Ok(witness)
}

View File

@ -11,7 +11,7 @@ use zcash_primitives::zip32::ExtendedFullViewingKey;
pub const DEFAULT_DB_PATH: &str = "zec.db";
pub struct DbAdapter {
connection: Connection,
pub connection: Connection,
}
pub struct ReceivedNote {
@ -25,6 +25,7 @@ pub struct ReceivedNote {
}
pub struct SpendableNote {
pub id: u32,
pub note: Note,
pub diversifier: Diversifier,
pub witness: IncrementalWitness<Node>,
@ -40,6 +41,7 @@ impl DbAdapter {
self.connection.execute(
"CREATE TABLE IF NOT EXISTS accounts (
id_account INTEGER PRIMARY KEY,
seed TEXT NOT NULL,
sk TEXT NOT NULL UNIQUE,
ivk TEXT NOT NULL,
address TEXT NOT NULL)",
@ -95,11 +97,11 @@ impl DbAdapter {
Ok(())
}
pub fn store_account(&self, sk: &str, ivk: &str, address: &str) -> anyhow::Result<()> {
pub fn store_account(&self, seed: &str, sk: &str, ivk: &str, address: &str) -> anyhow::Result<()> {
self.connection.execute(
"INSERT INTO accounts(sk, ivk, address) VALUES (?1, ?2, ?3)
"INSERT INTO accounts(seed, sk, ivk, address) VALUES (?1, ?2, ?3, ?4)
ON CONFLICT DO NOTHING",
params![sk, ivk, address],
params![seed, sk, ivk, address],
)?;
Ok(())
}
@ -235,7 +237,7 @@ impl DbAdapter {
pub fn get_balance(&self) -> anyhow::Result<u64> {
let balance: Option<i64> = self.connection.query_row(
"SELECT SUM(value) FROM received_notes WHERE spent IS NULL",
"SELECT SUM(value) FROM received_notes WHERE spent IS NULL OR spent = 0",
NO_PARAMS,
|row| row.get(0),
)?;
@ -291,12 +293,11 @@ impl DbAdapter {
Some((height, tree)) => {
let tree = CTree::read(&*tree)?;
let mut statement = self.connection.prepare(
"SELECT id_note, position, witness FROM sapling_witnesses w, received_notes n WHERE w.height = ?1 AND w.note = n.id_note AND n.spent IS NULL")?;
"SELECT id_note, witness FROM sapling_witnesses w, received_notes n WHERE w.height = ?1 AND w.note = n.id_note AND (n.spent IS NULL OR n.spent = 0)")?;
let ws = statement.query_map(params![height], |row| {
let id_note: u32 = row.get(0)?;
let position: u32 = row.get(1)?;
let witness: Vec<u8> = row.get(2)?;
Ok(Witness::read(position as usize, id_note, &*witness).unwrap())
let witness: Vec<u8> = row.get(1)?;
Ok(Witness::read(id_note, &*witness).unwrap())
})?;
let mut witnesses: Vec<Witness> = vec![];
for w in ws {
@ -311,7 +312,7 @@ impl DbAdapter {
pub fn get_nullifiers(&self) -> anyhow::Result<HashMap<Nf, u32>> {
let mut statement = self
.connection
.prepare("SELECT id_note, nf FROM received_notes WHERE spent IS NULL")?;
.prepare("SELECT id_note, nf FROM received_notes WHERE spent IS NULL OR spent = 0")?;
let nfs_res = statement.query_map(NO_PARAMS, |row| {
let id_note: u32 = row.get(0)?;
let nf_vec: Vec<u8> = row.get(1)?;
@ -331,7 +332,7 @@ impl DbAdapter {
pub fn get_nullifier_amounts(&self) -> anyhow::Result<HashMap<Vec<u8>, u64>> {
let mut statement = self
.connection
.prepare("SELECT value, nf FROM received_notes WHERE spent IS NULL")?;
.prepare("SELECT value, nf FROM received_notes WHERE spent IS NULL OR spent = 0")?;
let nfs_res = statement.query_map(NO_PARAMS, |row| {
let amount: i64 = row.get(0)?;
let nf: Vec<u8> = row.get(1)?;
@ -352,15 +353,17 @@ impl DbAdapter {
fvk: &ExtendedFullViewingKey,
) -> anyhow::Result<Vec<SpendableNote>> {
let mut statement = self.connection.prepare(
"SELECT diversifier, value, rcm, witness FROM received_notes r, sapling_witnesses w WHERE spent IS NULL
"SELECT id_note, diversifier, value, rcm, witness FROM received_notes r, sapling_witnesses w WHERE spent IS NULL
AND w.height = (
SELECT MAX(height) FROM sapling_witnesses WHERE height <= ?1
) AND r.id_note = w.note")?;
let notes = statement.query_map(params![anchor_height], |row| {
let diversifier: Vec<u8> = row.get(0)?;
let value: i64 = row.get(1)?;
let rcm: Vec<u8> = row.get(2)?;
let witness: Vec<u8> = row.get(3)?;
let id_note: u32 = row.get(0)?;
let diversifier: Vec<u8> = row.get(1)?;
let value: i64 = row.get(2)?;
let rcm: Vec<u8> = row.get(3)?;
let witness: Vec<u8> = row.get(4)?;
let mut diversifer_bytes = [0u8; 11];
diversifer_bytes.copy_from_slice(&diversifier);
@ -374,6 +377,7 @@ impl DbAdapter {
let pa = fvk.fvk.vk.to_payment_address(diversifier).unwrap();
let note = pa.create_note(value as u64, rseed).unwrap();
Ok(SpendableNote {
id: id_note,
note,
diversifier,
witness,
@ -397,6 +401,20 @@ impl DbAdapter {
Ok(())
}
pub fn get_seed(&self, account: u32) -> anyhow::Result<String> {
log::debug!("+get_seed");
let ivk = self.connection.query_row(
"SELECT seed FROM accounts WHERE id_account = ?1",
params![account],
|row| {
let ivk: String = row.get(0)?;
Ok(ivk)
},
)?;
log::debug!("-get_seed");
Ok(ivk)
}
pub fn get_sk(&self, account: u32) -> anyhow::Result<String> {
log::debug!("+get_sk");
let ivk = self.connection.query_row(

View File

@ -3,7 +3,7 @@ use zcash_primitives::consensus::Network;
#[path = "generated/cash.z.wallet.sdk.rpc.rs"]
pub mod lw_rpc;
pub const NETWORK: Network = Network::TestNetwork;
pub const NETWORK: Network = Network::MainNetwork;
mod builder;
mod chain;
@ -28,3 +28,4 @@ pub use crate::lw_rpc::*;
pub use crate::mempool::MemPool;
pub use crate::scan::{latest_height, scan_all, sync_async};
pub use crate::wallet::{Wallet, WalletBalance, DEFAULT_ACCOUNT};
pub use crate::print::*;

View File

@ -1,7 +1,8 @@
use bip39::{Language, Mnemonic};
use rand::rngs::OsRng;
use rand::RngCore;
use sync::{DbAdapter, Wallet, DEFAULT_ACCOUNT, ChainError};
use sync::{DbAdapter, Wallet, DEFAULT_ACCOUNT, ChainError, Witness, print_witness2};
use rusqlite::NO_PARAMS;
const DB_NAME: &str = "zec.db";
@ -29,11 +30,11 @@ async fn test() -> anyhow::Result<()> {
println!("REORG");
}
}
let tx_id = wallet
.send_payment(DEFAULT_ACCOUNT, &address, 50000)
.await
.unwrap();
println!("TXID = {}", tx_id);
// let tx_id = wallet
// .send_payment(DEFAULT_ACCOUNT, &address, 50000)
// .await
// .unwrap();
// println!("TXID = {}", tx_id);
Ok(())
}
@ -50,18 +51,53 @@ fn test_make_wallet() {
#[allow(dead_code)]
fn test_rewind() {
let mut db = DbAdapter::new(DB_NAME).unwrap();
db.trim_to_height(1460000).unwrap();
db.trim_to_height(1466520).unwrap();
}
#[allow(dead_code)]
fn test_get_balance() {
let db = DbAdapter::new(DB_NAME).unwrap();
let balance = db.get_balance().unwrap();
println!("Balance = {}", (balance as f64) / 100_000_000.0);
}
#[allow(dead_code)]
fn test_invalid_witness() {
dotenv::dotenv().unwrap();
env_logger::init();
println!("BAD");
let witness = dotenv::var("WITNESS").unwrap();
let w = Witness::read(0, &*hex::decode(&witness).unwrap()).unwrap();
print_witness2(&w);
println!("GOOD");
let witness = dotenv::var("WITNESS2").unwrap();
let w = Witness::read(0, &*hex::decode(&witness).unwrap()).unwrap();
print_witness2(&w);
}
fn w() {
let db = DbAdapter::new("zec.db").unwrap();
// let w_b: Vec<u8> = db.connection.query_row("SELECT witness FROM sapling_witnesses WHERE note = 66 AND height = 1466097", NO_PARAMS, |row| row.get(0)).unwrap();
// let w = Witness::read(0, &*w_b).unwrap();
// print_witness2(&w);
//
let w_b: Vec<u8> = db.connection.query_row("SELECT witness FROM sapling_witnesses WHERE note = 66 AND height = 1466200", NO_PARAMS, |row| row.get(0)).unwrap();
let w = Witness::read(0, &*w_b).unwrap();
print_witness2(&w);
println!("GOOD");
let witness = dotenv::var("WITNESS2").unwrap();
let w = Witness::read(0, &*hex::decode(&witness).unwrap()).unwrap();
print_witness2(&w);
}
fn main() {
init();
// test_invalid_witness()
// test_rewind();
test().unwrap();
test_get_balance();
// test_get_balance();
// w();
}

View File

@ -17,6 +17,7 @@ use zcash_client_backend::encoding::decode_extended_full_viewing_key;
use zcash_primitives::consensus::{NetworkUpgrade, Parameters};
use zcash_primitives::sapling::Node;
use zcash_primitives::zip32::ExtendedFullViewingKey;
use std::panic;
pub async fn scan_all(fvks: &[ExtendedFullViewingKey]) -> anyhow::Result<()> {
let decrypter = DecryptNode::new(fvks.to_vec());
@ -214,6 +215,7 @@ pub async fn sync_async(
if !nodes.is_empty() {
bp.add_nodes(&mut nodes, &witnesses);
}
// println!("NOTES = {}", nodes.len());
if let Some(block) = blocks.0.last() {
let mut hash = [0u8; 32];
@ -276,6 +278,9 @@ pub async fn sync_async(
}
Err(err) => {
log::info!("Sync error = {}", err);
if err.is_panic() {
panic::resume_unwind(err.into_panic());
}
anyhow::bail!("Join Error");
},
}

View File

@ -76,6 +76,10 @@ impl Wallet {
Ok(())
}
pub fn get_seed(&self, account: u32) -> anyhow::Result<String> {
self.db.get_seed(account)
}
pub fn has_account(&self, account: u32) -> anyhow::Result<bool> {
self.db.has_account(account)
}
@ -84,7 +88,7 @@ impl Wallet {
let sk = get_secret_key(&seed).unwrap();
let vk = get_viewing_key(&sk).unwrap();
let pa = get_address(&vk).unwrap();
self.db.store_account(&sk, &vk, &pa)?;
self.db.store_account(seed, &sk, &vk, &pa)?;
Ok(())
}
@ -195,7 +199,8 @@ impl Wallet {
let mut amount = target_amount;
amount += DEFAULT_FEE;
for n in notes {
let mut selected_note: Vec<u32> = vec![];
for n in notes.iter() {
if amount.is_positive() {
let a = amount.min(
Amount::from_u64(n.note.value)
@ -203,12 +208,15 @@ impl Wallet {
);
amount -= a;
let merkle_path = n.witness.path().context("Invalid Merkle Path")?;
let mut witness_bytes: Vec<u8> = vec![];
n.witness.write(&mut witness_bytes)?;
builder.add_sapling_spend(
skey.clone(),
n.diversifier,
n.note.clone(),
merkle_path,
)?;
selected_note.push(n.id);
}
}
if amount.is_positive() {
@ -237,6 +245,10 @@ impl Wallet {
let mut client = connect_lightwalletd().await?;
let tx_id = send_transaction(&mut client, &raw_tx, last_height).await?;
log::info!("Tx ID = {}", tx_id);
for id_note in selected_note.iter() {
self.db.mark_spent(*id_note, 0)?;
}
Ok(tx_id)
}

Binary file not shown.