cleanup
This commit is contained in:
parent
6cbd30c959
commit
572195d8df
|
@ -3,3 +3,4 @@
|
||||||
.idea/
|
.idea/
|
||||||
src/generated/*.rs
|
src/generated/*.rs
|
||||||
docs/_site/
|
docs/_site/
|
||||||
|
*.db
|
||||||
|
|
|
@ -37,6 +37,12 @@ dependencies = [
|
||||||
"opaque-debug",
|
"opaque-debug",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.18"
|
version = "0.7.18"
|
||||||
|
@ -127,9 +133,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bech32"
|
name = "bech32"
|
||||||
version = "0.8.0"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c7f7096bc256f5e5cb960f60dfc4f4ef979ca65abe7fb9d5a4f77150d3783d4"
|
checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bellman"
|
name = "bellman"
|
||||||
|
@ -638,11 +644,24 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equihash"
|
name = "equihash"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/zcash/librustzcash.git?rev=d50bb12a97da768dc8f3ee39b81f84262103e6eb#d50bb12a97da768dc8f3ee39b81f84262103e6eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-iterator"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-streaming-iterator"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ff"
|
name = "ff"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -893,6 +912,18 @@ name = "hashbrown"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashlink"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
|
@ -1064,6 +1095,17 @@ version = "0.2.97"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6"
|
checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libsqlite3-sys"
|
||||||
|
version = "0.20.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64d31059f22935e6c31830db5249ba2b7ecd54fd73a9909286f0a67aa55c2fbd"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
|
@ -1294,6 +1336,12 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plotters"
|
name = "plotters"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -1577,6 +1625,22 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusqlite"
|
||||||
|
version = "0.24.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d5f38ee71cbab2c827ec0ac24e76f82eca723cee92c509a65f67dee393c25112"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"fallible-iterator",
|
||||||
|
"fallible-streaming-iterator",
|
||||||
|
"hashlink",
|
||||||
|
"libsqlite3-sys",
|
||||||
|
"memchr",
|
||||||
|
"smallvec",
|
||||||
|
"time 0.2.27",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
@ -1783,6 +1847,12 @@ version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -1907,6 +1977,7 @@ dependencies = [
|
||||||
"tonic",
|
"tonic",
|
||||||
"tonic-build",
|
"tonic-build",
|
||||||
"zcash_client_backend",
|
"zcash_client_backend",
|
||||||
|
"zcash_client_sqlite",
|
||||||
"zcash_primitives",
|
"zcash_primitives",
|
||||||
"zcash_proofs",
|
"zcash_proofs",
|
||||||
]
|
]
|
||||||
|
@ -2252,6 +2323,12 @@ version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
@ -2415,6 +2492,7 @@ checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_client_backend"
|
name = "zcash_client_backend"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
source = "git+https://github.com/zcash/librustzcash.git?rev=d50bb12a97da768dc8f3ee39b81f84262103e6eb#d50bb12a97da768dc8f3ee39b81f84262103e6eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bech32",
|
"bech32",
|
||||||
|
@ -2435,9 +2513,28 @@ dependencies = [
|
||||||
"zcash_primitives",
|
"zcash_primitives",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zcash_client_sqlite"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "git+https://github.com/zcash/librustzcash.git?rev=d50bb12a97da768dc8f3ee39b81f84262103e6eb#d50bb12a97da768dc8f3ee39b81f84262103e6eb"
|
||||||
|
dependencies = [
|
||||||
|
"bech32",
|
||||||
|
"bs58",
|
||||||
|
"ff",
|
||||||
|
"group",
|
||||||
|
"jubjub",
|
||||||
|
"protobuf",
|
||||||
|
"rand_core",
|
||||||
|
"rusqlite",
|
||||||
|
"time 0.2.27",
|
||||||
|
"zcash_client_backend",
|
||||||
|
"zcash_primitives",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_note_encryption"
|
name = "zcash_note_encryption"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
source = "git+https://github.com/zcash/librustzcash.git?rev=d50bb12a97da768dc8f3ee39b81f84262103e6eb#d50bb12a97da768dc8f3ee39b81f84262103e6eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -2451,6 +2548,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_primitives"
|
name = "zcash_primitives"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
source = "git+https://github.com/zcash/librustzcash.git?rev=d50bb12a97da768dc8f3ee39b81f84262103e6eb#d50bb12a97da768dc8f3ee39b81f84262103e6eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"bitvec 0.20.4",
|
"bitvec 0.20.4",
|
||||||
|
@ -2478,6 +2576,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_proofs"
|
name = "zcash_proofs"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
source = "git+https://github.com/zcash/librustzcash.git?rev=d50bb12a97da768dc8f3ee39b81f84262103e6eb#d50bb12a97da768dc8f3ee39b81f84262103e6eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bellman",
|
"bellman",
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -10,6 +10,10 @@ edition = "2018"
|
||||||
name = "scan_all"
|
name = "scan_all"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "warp-cli"
|
||||||
|
path = "src/main/warp_cli.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
env_logger = "0.8.4"
|
env_logger = "0.8.4"
|
||||||
|
@ -31,15 +35,21 @@ tonic = {version = "0.4.3", features = ["tls", "tls-roots"]}
|
||||||
prost = "0.7"
|
prost = "0.7"
|
||||||
rayon = "1.5.1"
|
rayon = "1.5.1"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
|
tiny-bip39 = "0.8"
|
||||||
|
rand = "0.8.4"
|
||||||
|
rusqlite = "^0.25.3"
|
||||||
|
|
||||||
[dependencies.zcash_client_backend]
|
[dependencies.zcash_client_backend]
|
||||||
path = "../librustzcash/zcash_client_backend"
|
git = "https://github.com/zcash/librustzcash.git"
|
||||||
|
rev = "d50bb12a97da768dc8f3ee39b81f84262103e6eb"
|
||||||
|
|
||||||
[dependencies.zcash_primitives]
|
[dependencies.zcash_primitives]
|
||||||
path = "../librustzcash/zcash_primitives"
|
git = "https://github.com/zcash/librustzcash.git"
|
||||||
|
rev = "d50bb12a97da768dc8f3ee39b81f84262103e6eb"
|
||||||
|
|
||||||
[dependencies.zcash_proofs]
|
[dependencies.zcash_proofs]
|
||||||
path = "../librustzcash/zcash_proofs"
|
git = "https://github.com/zcash/librustzcash.git"
|
||||||
|
rev = "d50bb12a97da768dc8f3ee39b81f84262103e6eb"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = "0.4.2"
|
tonic-build = "0.4.2"
|
||||||
|
|
|
@ -14,13 +14,12 @@ fn scan(c: &mut Criterion) {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let ivk = fvk.fvk.vk.ivk();
|
let fvks = &vec![fvk];
|
||||||
let ivks = &vec![ivk];
|
|
||||||
|
|
||||||
c.bench_function("scan all", |b| {
|
c.bench_function("scan all", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let r = Runtime::new().unwrap();
|
let r = Runtime::new().unwrap();
|
||||||
r.block_on(scan_all(ivks.clone().as_slice())).unwrap();
|
r.block_on(scan_all(fvks.clone().as_slice())).unwrap();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,16 +315,15 @@ pub fn advance_tree(
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::builder::advance_tree;
|
use crate::builder::advance_tree;
|
||||||
use crate::commitment::{CTree, Witness};
|
use crate::commitment::{CTree, Witness};
|
||||||
use crate::print::{print_tree, print_witness};
|
|
||||||
use zcash_primitives::merkle_tree::{CommitmentTree, IncrementalWitness};
|
use zcash_primitives::merkle_tree::{CommitmentTree, IncrementalWitness};
|
||||||
use zcash_primitives::sapling::Node;
|
use zcash_primitives::sapling::Node;
|
||||||
|
use crate::chain::DecryptedNote;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_advance_tree() {
|
fn test_advance_tree() {
|
||||||
const NUM_NODES: usize = 1000;
|
const NUM_NODES: usize = 1000;
|
||||||
const NUM_CHUNKS: usize = 50;
|
const NUM_CHUNKS: usize = 50;
|
||||||
const WITNESS_PERCENT: f64 = 1.0; // percentage of notes that are ours
|
const WITNESS_PERCENT: f64 = 1.0; // percentage of notes that are ours
|
||||||
const DEBUG_PRINT: bool = true;
|
|
||||||
let witness_freq = (100.0 / WITNESS_PERCENT) as usize;
|
let witness_freq = (100.0 / WITNESS_PERCENT) as usize;
|
||||||
|
|
||||||
let mut tree1: CommitmentTree<Node> = CommitmentTree::empty();
|
let mut tree1: CommitmentTree<Node> = CommitmentTree::empty();
|
||||||
|
@ -347,7 +346,7 @@ mod tests {
|
||||||
// if v == 499 {
|
// if v == 499 {
|
||||||
let w = IncrementalWitness::from_tree(&tree1);
|
let w = IncrementalWitness::from_tree(&tree1);
|
||||||
ws.push(w);
|
ws.push(w);
|
||||||
ws2.push(Witness::new(v));
|
ws2.push(Witness::new(v, 0, None));
|
||||||
}
|
}
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
}
|
}
|
||||||
|
@ -382,48 +381,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if DEBUG_PRINT && (failed_index.is_some() || !equal) {
|
assert!(equal && failed_index.is_none());
|
||||||
let i = failed_index.unwrap();
|
|
||||||
println!("FAILED AT {}", i);
|
|
||||||
print_witness(&ws[i]);
|
|
||||||
|
|
||||||
// println!("-----");
|
|
||||||
// println!("Final-----");
|
|
||||||
//
|
|
||||||
// println!("{:?}", tree2.left.map(|n| hex::encode(n.repr)));
|
|
||||||
// println!("{:?}", tree2.right.map(|n| hex::encode(n.repr)));
|
|
||||||
// for p in tree2.parents.iter() {
|
|
||||||
// println!("{:?}", p.map(|n| hex::encode(n.repr)));
|
|
||||||
// }
|
|
||||||
// println!("-----");
|
|
||||||
|
|
||||||
// println!("{:?}", tree1.left.map(|n| hex::encode(n.repr)));
|
|
||||||
// println!("{:?}", tree1.right.map(|n| hex::encode(n.repr)));
|
|
||||||
// for p in tree1.parents.iter() {
|
|
||||||
// println!("{:?}", p.map(|n| hex::encode(n.repr)));
|
|
||||||
// }
|
|
||||||
println!("----- {}", ws2[i].position);
|
|
||||||
let tree2 = &ws2[i].tree;
|
|
||||||
println!("{:?}", tree2.left.map(|n| hex::encode(n.repr)));
|
|
||||||
println!("{:?}", tree2.right.map(|n| hex::encode(n.repr)));
|
|
||||||
for p in tree2.parents.iter() {
|
|
||||||
println!("{:?}", p.map(|n| hex::encode(n.repr)));
|
|
||||||
}
|
|
||||||
println!("-----");
|
|
||||||
let filled2 = &ws2[i].filled;
|
|
||||||
println!("Filled");
|
|
||||||
for f in filled2.iter() {
|
|
||||||
println!("{:?}", hex::encode(f.repr));
|
|
||||||
}
|
|
||||||
println!("Cursor");
|
|
||||||
let cursor2 = &ws2[i].cursor;
|
|
||||||
println!("{:?}", cursor2.left.map(|n| hex::encode(n.repr)));
|
|
||||||
println!("{:?}", cursor2.right.map(|n| hex::encode(n.repr)));
|
|
||||||
for p in cursor2.parents.iter() {
|
|
||||||
println!("{:?}", p.map(|n| hex::encode(n.repr)));
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
|
||||||
|
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||||
|
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
|
||||||
|
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
|
||||||
|
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||||
|
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
|
||||||
|
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
|
||||||
|
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
|
||||||
|
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
|
||||||
|
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
|
||||||
|
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
|
||||||
|
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
|
||||||
|
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
|
||||||
|
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
|
||||||
|
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
|
||||||
|
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
|
||||||
|
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
|
||||||
|
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
|
||||||
|
-----END CERTIFICATE-----
|
126
src/chain.rs
126
src/chain.rs
|
@ -1,21 +1,26 @@
|
||||||
use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
||||||
use crate::lw_rpc::*;
|
use crate::lw_rpc::*;
|
||||||
use crate::NETWORK;
|
use crate::{NETWORK, advance_tree};
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
use group::GroupEncoding;
|
use group::GroupEncoding;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use tonic::transport::Channel;
|
use tonic::transport::{Channel, Certificate, ClientTlsConfig};
|
||||||
use tonic::Request;
|
use tonic::Request;
|
||||||
use zcash_primitives::consensus::BlockHeight;
|
use zcash_primitives::consensus::{BlockHeight, Parameters, NetworkUpgrade};
|
||||||
use zcash_primitives::merkle_tree::{CommitmentTree, IncrementalWitness};
|
use zcash_primitives::merkle_tree::{CommitmentTree, IncrementalWitness};
|
||||||
use zcash_primitives::sapling::note_encryption::try_sapling_compact_note_decryption;
|
use zcash_primitives::sapling::note_encryption::try_sapling_compact_note_decryption;
|
||||||
use zcash_primitives::sapling::{Node, Note, SaplingIvk};
|
use zcash_primitives::sapling::{Node, Note, PaymentAddress};
|
||||||
use zcash_primitives::transaction::components::sapling::CompactOutputDescription;
|
use zcash_primitives::transaction::components::sapling::CompactOutputDescription;
|
||||||
use crate::commitment::{CTree, Witness, NotePosition};
|
use crate::commitment::{CTree, Witness};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
|
||||||
|
use zcash_primitives::zip32::ExtendedFullViewingKey;
|
||||||
|
|
||||||
const MAX_CHUNK: u32 = 50000;
|
const MAX_CHUNK: u32 = 50000;
|
||||||
|
// pub const LWD_URL: &str = "https://mainnet.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(
|
pub async fn get_latest_height(
|
||||||
|
@ -60,7 +65,7 @@ pub async fn download_chain(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DecryptNode {
|
pub struct DecryptNode {
|
||||||
ivks: Vec<SaplingIvk>,
|
fvks: Vec<ExtendedFullViewingKey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DecryptedBlock {
|
pub struct DecryptedBlock {
|
||||||
|
@ -69,17 +74,25 @@ pub struct DecryptedBlock {
|
||||||
pub count_outputs: u32,
|
pub count_outputs: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct DecryptedNote {
|
pub struct DecryptedNote {
|
||||||
|
pub ivk: ExtendedFullViewingKey,
|
||||||
pub note: Note,
|
pub note: Note,
|
||||||
pub position: u32,
|
pub pa: PaymentAddress,
|
||||||
|
pub position: usize,
|
||||||
|
|
||||||
|
pub height: u32,
|
||||||
|
pub txid: Vec<u8>,
|
||||||
|
pub tx_index: usize,
|
||||||
|
pub output_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_notes(block: &CompactBlock, ivks: &[SaplingIvk]) -> DecryptedBlock {
|
fn decrypt_notes(block: &CompactBlock, fvks: &[ExtendedFullViewingKey]) -> DecryptedBlock {
|
||||||
let height = BlockHeight::from_u32(block.height as u32);
|
let height = BlockHeight::from_u32(block.height as u32);
|
||||||
let mut count_outputs = 0u32;
|
let mut count_outputs = 0u32;
|
||||||
let mut notes: Vec<DecryptedNote> = vec![];
|
let mut notes: Vec<DecryptedNote> = vec![];
|
||||||
for vtx in block.vtx.iter() {
|
for (tx_index, vtx) in block.vtx.iter().enumerate() {
|
||||||
for co in vtx.outputs.iter() {
|
for (output_index, co) in vtx.outputs.iter().enumerate() {
|
||||||
let mut cmu = [0u8; 32];
|
let mut cmu = [0u8; 32];
|
||||||
cmu.copy_from_slice(&co.cmu);
|
cmu.copy_from_slice(&co.cmu);
|
||||||
let cmu = bls12_381::Scalar::from_repr(cmu).unwrap();
|
let cmu = bls12_381::Scalar::from_repr(cmu).unwrap();
|
||||||
|
@ -91,13 +104,20 @@ fn decrypt_notes(block: &CompactBlock, ivks: &[SaplingIvk]) -> DecryptedBlock {
|
||||||
cmu,
|
cmu,
|
||||||
enc_ciphertext: co.ciphertext.to_vec(),
|
enc_ciphertext: co.ciphertext.to_vec(),
|
||||||
};
|
};
|
||||||
for ivk in ivks.iter() {
|
for fvk in fvks.iter() {
|
||||||
if let Some((note, _pa)) =
|
let ivk = &fvk.fvk.vk.ivk();
|
||||||
|
if let Some((note, pa)) =
|
||||||
try_sapling_compact_note_decryption(&NETWORK, height, ivk, &od)
|
try_sapling_compact_note_decryption(&NETWORK, height, ivk, &od)
|
||||||
{
|
{
|
||||||
notes.push(DecryptedNote {
|
notes.push(DecryptedNote {
|
||||||
|
ivk: fvk.clone(),
|
||||||
note,
|
note,
|
||||||
position: count_outputs,
|
pa,
|
||||||
|
position: count_outputs as usize,
|
||||||
|
height: block.height as u32,
|
||||||
|
tx_index,
|
||||||
|
txid: vtx.hash.clone(),
|
||||||
|
output_index,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,14 +132,14 @@ fn decrypt_notes(block: &CompactBlock, ivks: &[SaplingIvk]) -> DecryptedBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DecryptNode {
|
impl DecryptNode {
|
||||||
pub fn new(ivks: Vec<SaplingIvk>) -> DecryptNode {
|
pub fn new(fvks: Vec<ExtendedFullViewingKey>) -> DecryptNode {
|
||||||
DecryptNode { ivks }
|
DecryptNode { fvks }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decrypt_blocks(&self, blocks: &[CompactBlock]) -> Vec<DecryptedBlock> {
|
pub fn decrypt_blocks(&self, blocks: &[CompactBlock]) -> Vec<DecryptedBlock> {
|
||||||
let mut decrypted_blocks: Vec<DecryptedBlock> = blocks
|
let mut decrypted_blocks: Vec<DecryptedBlock> = blocks
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|b| decrypt_notes(b, &self.ivks))
|
.map(|b| decrypt_notes(b, &self.fvks))
|
||||||
.collect();
|
.collect();
|
||||||
decrypted_blocks.sort_by(|a, b| a.height.cmp(&b.height));
|
decrypted_blocks.sort_by(|a, b| a.height.cmp(&b.height));
|
||||||
decrypted_blocks
|
decrypted_blocks
|
||||||
|
@ -156,7 +176,7 @@ fn calculate_tree_state_v1(
|
||||||
} // skip before height
|
} // skip before height
|
||||||
let mut notes = block.notes.iter();
|
let mut notes = block.notes.iter();
|
||||||
let mut n = notes.next();
|
let mut n = notes.next();
|
||||||
let mut i = 0u32;
|
let mut i = 0usize;
|
||||||
for tx in cb.vtx.iter() {
|
for tx in cb.vtx.iter() {
|
||||||
for co in tx.outputs.iter() {
|
for co in tx.outputs.iter() {
|
||||||
let mut cmu = [0u8; 32];
|
let mut cmu = [0u8; 32];
|
||||||
|
@ -192,9 +212,12 @@ pub fn calculate_tree_state_v2(cbs: &[CompactBlock], blocks: &[DecryptedBlock])
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
for (cb, block) in cbs.iter().zip(blocks) {
|
for (cb, block) in cbs.iter().zip(blocks) {
|
||||||
assert_eq!(cb.height as u32, block.height);
|
assert_eq!(cb.height as u32, block.height);
|
||||||
|
if !block.notes.is_empty() {
|
||||||
|
println!("{} {}", block.height, block.notes.len());
|
||||||
|
}
|
||||||
let mut notes = block.notes.iter();
|
let mut notes = block.notes.iter();
|
||||||
let mut n = notes.next();
|
let mut n = notes.next();
|
||||||
let mut i = 0u32;
|
let mut i = 0usize;
|
||||||
for tx in cb.vtx.iter() {
|
for tx in cb.vtx.iter() {
|
||||||
for co in tx.outputs.iter() {
|
for co in tx.outputs.iter() {
|
||||||
let mut cmu = [0u8; 32];
|
let mut cmu = [0u8; 32];
|
||||||
|
@ -215,13 +238,57 @@ pub fn calculate_tree_state_v2(cbs: &[CompactBlock], blocks: &[DecryptedBlock])
|
||||||
}
|
}
|
||||||
info!("Build CMU list: {} ms - {} nodes", start.elapsed().as_millis(), nodes.len());
|
info!("Build CMU list: {} ms - {} nodes", start.elapsed().as_millis(), nodes.len());
|
||||||
|
|
||||||
let start = Instant::now();
|
let witnesses: Vec<_> = positions.iter().map(|p| Witness::new(*p, 0, None)).collect();
|
||||||
let n = nodes.len();
|
let (_, new_witnesses) = advance_tree(CTree::new(), &witnesses, &mut nodes);
|
||||||
let mut positions: Vec<_> = positions.iter().map(|&p| NotePosition::new(p, n)).collect();
|
|
||||||
let _frontier = CTree::calc_state(nodes, &mut positions, None);
|
|
||||||
let witnesses: Vec<_> = positions.iter().map(|p| p.witness.clone()).collect();
|
|
||||||
info!("Tree State & Witnesses: {} ms", start.elapsed().as_millis());
|
info!("Tree State & Witnesses: {} ms", start.elapsed().as_millis());
|
||||||
witnesses
|
new_witnesses
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn connect_lightwalletd() -> anyhow::Result<CompactTxStreamerClient<Channel>> {
|
||||||
|
let mut channel = tonic::transport::Channel::from_shared(LWD_URL)?;
|
||||||
|
if LWD_URL.starts_with("https") {
|
||||||
|
let pem = include_bytes!("ca.pem");
|
||||||
|
let ca = Certificate::from_pem(pem);
|
||||||
|
let tls = ClientTlsConfig::new().ca_certificate(ca);
|
||||||
|
channel = channel.tls_config(tls)?;
|
||||||
|
}
|
||||||
|
let client = CompactTxStreamerClient::connect(channel).await?;
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn sync(ivk: &str) -> anyhow::Result<()> {
|
||||||
|
let fvk =
|
||||||
|
decode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &ivk)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
let decrypter = DecryptNode::new(vec![fvk]);
|
||||||
|
let mut client = connect_lightwalletd().await?;
|
||||||
|
let start_height: u32 = crate::NETWORK
|
||||||
|
.activation_height(NetworkUpgrade::Sapling)
|
||||||
|
.unwrap()
|
||||||
|
.into();
|
||||||
|
let end_height = get_latest_height(&mut client).await?;
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let cbs = download_chain(&mut client, start_height, end_height).await?;
|
||||||
|
eprintln!("Download chain: {} ms", start.elapsed().as_millis());
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let blocks = decrypter.decrypt_blocks(&cbs);
|
||||||
|
eprintln!("Decrypt Notes: {} ms", start.elapsed().as_millis());
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let witnesses = calculate_tree_state_v2(&cbs, &blocks);
|
||||||
|
eprintln!("Tree State & Witnesses: {} ms", start.elapsed().as_millis());
|
||||||
|
|
||||||
|
eprintln!("# Witnesses {}", witnesses.len());
|
||||||
|
for w in witnesses.iter() {
|
||||||
|
let mut bb: Vec<u8> = vec![];
|
||||||
|
w.write(&mut bb).unwrap();
|
||||||
|
log::info!("{}", hex::encode(&bb));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -247,14 +314,13 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_download_chain() -> anyhow::Result<()> {
|
async fn test_download_chain() -> anyhow::Result<()> {
|
||||||
dotenv::dotenv().unwrap();
|
dotenv::dotenv().unwrap();
|
||||||
let ivk = dotenv::var("IVK").unwrap();
|
let fvk = dotenv::var("FVK").unwrap();
|
||||||
|
|
||||||
let fvk =
|
let fvk =
|
||||||
decode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &ivk)
|
decode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &fvk)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ivk = fvk.fvk.vk.ivk();
|
let decrypter = DecryptNode::new(vec![fvk]);
|
||||||
let decrypter = DecryptNode::new(vec![ivk]);
|
|
||||||
let mut client = CompactTxStreamerClient::connect(LWD_URL).await?;
|
let mut client = CompactTxStreamerClient::connect(LWD_URL).await?;
|
||||||
let start_height: u32 = crate::NETWORK
|
let start_height: u32 = crate::NETWORK
|
||||||
.activation_height(NetworkUpgrade::Sapling)
|
.activation_height(NetworkUpgrade::Sapling)
|
||||||
|
@ -282,13 +348,15 @@ mod tests {
|
||||||
|
|
||||||
// let witnesses = calculate_tree_state(&cbs, &blocks, 0, tree_state);
|
// let witnesses = calculate_tree_state(&cbs, &blocks, 0, tree_state);
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
let witnesses = calculate_tree_state_v2(&cbs, &blocks);
|
let witnesses = calculate_tree_state_v2(&cbs, &blocks);
|
||||||
|
eprintln!("Tree State & Witnesses: {} ms", start.elapsed().as_millis());
|
||||||
|
|
||||||
eprintln!("# Witnesses {}", witnesses.len());
|
eprintln!("# Witnesses {}", witnesses.len());
|
||||||
for w in witnesses.iter() {
|
for w in witnesses.iter() {
|
||||||
let mut bb: Vec<u8> = vec![];
|
let mut bb: Vec<u8> = vec![];
|
||||||
w.write(&mut bb).unwrap();
|
w.write(&mut bb).unwrap();
|
||||||
eprintln!("{}", hex::encode(&bb));
|
log::info!("{}", hex::encode(&bb));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use crate::path::MerklePath;
|
|
||||||
use byteorder::WriteBytesExt;
|
use byteorder::WriteBytesExt;
|
||||||
use rayon::prelude::*;
|
use std::io::{Write, Read};
|
||||||
use std::io::Write;
|
use zcash_primitives::merkle_tree::{Hashable, CommitmentTree};
|
||||||
use zcash_primitives::merkle_tree::Hashable;
|
|
||||||
use zcash_primitives::sapling::Node;
|
use zcash_primitives::sapling::Node;
|
||||||
use zcash_primitives::serialize::{Optional, Vector};
|
use zcash_primitives::serialize::{Optional, Vector};
|
||||||
|
use crate::chain::DecryptedNote;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Same behavior and structure as CommitmentTree<Node> from librustzcash
|
Same behavior and structure as CommitmentTree<Node> from librustzcash
|
||||||
|
@ -81,18 +80,41 @@ pub struct Witness {
|
||||||
pub tree: CTree, // commitment tree at the moment the witness is created: immutable
|
pub tree: CTree, // commitment tree at the moment the witness is created: immutable
|
||||||
pub filled: Vec<Node>, // as more nodes are added, levels get filled up: won't change anymore
|
pub filled: Vec<Node>, // as more nodes are added, levels get filled up: won't change anymore
|
||||||
pub cursor: CTree, // partial tree which still updates when nodes are added
|
pub cursor: CTree, // partial tree which still updates when nodes are added
|
||||||
|
|
||||||
|
// not used for decryption but identifies the witness
|
||||||
|
pub id_note: u32,
|
||||||
|
pub note: Option<DecryptedNote>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Witness {
|
impl Witness {
|
||||||
pub fn new(position: usize) -> Witness {
|
pub fn new(position: usize, id_note: u32, note: Option<DecryptedNote>) -> Witness {
|
||||||
Witness {
|
Witness {
|
||||||
position,
|
position,
|
||||||
|
id_note,
|
||||||
|
note,
|
||||||
tree: CTree::new(),
|
tree: CTree::new(),
|
||||||
filled: vec![],
|
filled: vec![],
|
||||||
cursor: CTree::new(),
|
cursor: CTree::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read<R: Read>(position: usize, 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,
|
||||||
|
id_note,
|
||||||
|
tree,
|
||||||
|
filled,
|
||||||
|
cursor: cursor.unwrap_or_else(CTree::new),
|
||||||
|
note: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(witness)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
|
pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
|
||||||
self.tree.write(&mut writer)?;
|
self.tree.write(&mut writer)?;
|
||||||
Vector::write(&mut writer, &self.filled, |w, n| n.write(w))?;
|
Vector::write(&mut writer, &self.filled, |w, n| n.write(w))?;
|
||||||
|
@ -106,191 +128,7 @@ impl Witness {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NotePosition {
|
|
||||||
p0: usize,
|
|
||||||
p: usize,
|
|
||||||
p2: usize,
|
|
||||||
c: usize,
|
|
||||||
pub witness: Witness,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect(
|
|
||||||
tree: &mut CTree,
|
|
||||||
mut p: usize,
|
|
||||||
depth: usize,
|
|
||||||
commitments: &[Node],
|
|
||||||
offset: usize,
|
|
||||||
cursor: bool,
|
|
||||||
) -> usize {
|
|
||||||
// println!("--> {} {} {}", depth, p, offset);
|
|
||||||
if p < offset {
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
if depth == 0 {
|
|
||||||
if p % 2 == 0 {
|
|
||||||
tree.left = Some(commitments[p - offset]);
|
|
||||||
} else {
|
|
||||||
tree.left = Some(commitments[p - 1 - offset]);
|
|
||||||
tree.right = Some(commitments[p - offset]);
|
|
||||||
p -= 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// the rest gets combined as a binary tree
|
|
||||||
if p % 2 != 0 {
|
|
||||||
tree.parents.push(Some(commitments[p - 1 - offset]));
|
|
||||||
} else if (cursor && p != offset) || !cursor && (p != 0 || offset != 0) {
|
|
||||||
tree.parents.push(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NotePosition {
|
|
||||||
pub fn new(position: usize, count: usize) -> NotePosition {
|
|
||||||
let c = cursor_start_position(position, count);
|
|
||||||
NotePosition {
|
|
||||||
p0: position,
|
|
||||||
p: position,
|
|
||||||
p2: count - 1,
|
|
||||||
c,
|
|
||||||
witness: Witness::new(position),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self, count: usize) {
|
|
||||||
let c = cursor_start_position(self.p0, count);
|
|
||||||
self.p = self.p0;
|
|
||||||
self.p2 = count - 1;
|
|
||||||
self.c = c;
|
|
||||||
self.witness.cursor = CTree::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect(&mut self, depth: usize, commitments: &[Node], offset: usize) {
|
|
||||||
let count = commitments.len();
|
|
||||||
let p = self.p;
|
|
||||||
|
|
||||||
self.p = collect(&mut self.witness.tree, p, depth, commitments, offset, false);
|
|
||||||
|
|
||||||
if p % 2 == 0 && p + 1 >= offset && p + 1 - offset < commitments.len() {
|
|
||||||
let filler = commitments[p + 1 - offset];
|
|
||||||
self.witness.filled.push(filler);
|
|
||||||
}
|
|
||||||
|
|
||||||
let c = self.c - offset;
|
|
||||||
let cursor_commitments = &commitments[c..count];
|
|
||||||
// println!("c> {} {} {}", c, count, depth);
|
|
||||||
// println!("> {} {}", self.p2, self.c);
|
|
||||||
if !cursor_commitments.is_empty() {
|
|
||||||
let p2 = collect(
|
|
||||||
&mut self.witness.cursor,
|
|
||||||
self.p2,
|
|
||||||
depth,
|
|
||||||
cursor_commitments,
|
|
||||||
offset + c,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
self.p2 = (p2 - self.c) / 2 + self.c / 2;
|
|
||||||
// println!("+ {} {}", self.p2, self.c);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.p /= 2;
|
|
||||||
self.c /= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cursor_start_position(mut position: usize, mut count: usize) -> usize {
|
|
||||||
assert!(position < count);
|
|
||||||
// same logic as filler
|
|
||||||
let mut depth = 0;
|
|
||||||
loop {
|
|
||||||
if position % 2 == 0 {
|
|
||||||
if position + 1 < count {
|
|
||||||
position += 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
position /= 2;
|
|
||||||
count /= 2;
|
|
||||||
depth += 1;
|
|
||||||
}
|
|
||||||
(position + 1) << depth
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CTree {
|
impl CTree {
|
||||||
pub fn calc_state(
|
|
||||||
mut commitments: Vec<Node>,
|
|
||||||
positions: &mut [NotePosition],
|
|
||||||
prev_frontier: Option<CTree>,
|
|
||||||
) -> CTree {
|
|
||||||
let mut n = commitments.len();
|
|
||||||
assert_ne!(n, 0);
|
|
||||||
|
|
||||||
let prev_count = prev_frontier
|
|
||||||
.as_ref()
|
|
||||||
.map(|f| f.get_position())
|
|
||||||
.unwrap_or(0);
|
|
||||||
let count = prev_count + n;
|
|
||||||
let mut last_path = prev_frontier
|
|
||||||
.as_ref()
|
|
||||||
.map(|f| MerklePath::new(f.left, f.right));
|
|
||||||
let mut frontier = NotePosition::new(count - 1, count);
|
|
||||||
let mut offset = prev_count;
|
|
||||||
|
|
||||||
for p in positions.iter_mut() {
|
|
||||||
p.reset(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut depth = 0usize;
|
|
||||||
while n + offset > 0 {
|
|
||||||
if offset % 2 == 1 {
|
|
||||||
// start is not aligned
|
|
||||||
let mut lp = last_path.take().unwrap();
|
|
||||||
let node = lp.get(); // prepend the last node from the previous run
|
|
||||||
if n > 0 {
|
|
||||||
lp.set(commitments[0]); // put the right node into the path
|
|
||||||
}
|
|
||||||
last_path = Some(lp);
|
|
||||||
commitments.insert(0, node);
|
|
||||||
n += 1;
|
|
||||||
offset -= 1;
|
|
||||||
}
|
|
||||||
let commitment_slice = &commitments[0..n];
|
|
||||||
frontier.collect(depth, commitment_slice, offset);
|
|
||||||
|
|
||||||
for p in positions.iter_mut() {
|
|
||||||
p.collect(depth, commitment_slice, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
let nn = n / 2;
|
|
||||||
let next_level: Vec<_> = (0..nn)
|
|
||||||
.into_par_iter()
|
|
||||||
.map(|i| Node::combine(depth, &commitments[2 * i], &commitments[2 * i + 1]))
|
|
||||||
.collect();
|
|
||||||
commitments[0..nn].copy_from_slice(&next_level);
|
|
||||||
|
|
||||||
if let Some(mut lp) = last_path.take() {
|
|
||||||
lp.up(
|
|
||||||
depth,
|
|
||||||
prev_frontier
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.parents
|
|
||||||
.get(depth)
|
|
||||||
.unwrap_or(&None),
|
|
||||||
);
|
|
||||||
last_path = Some(lp);
|
|
||||||
}
|
|
||||||
|
|
||||||
depth += 1;
|
|
||||||
n = nn;
|
|
||||||
offset /= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
frontier.witness.tree
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new() -> CTree {
|
pub fn new() -> CTree {
|
||||||
CTree {
|
CTree {
|
||||||
left: None,
|
left: None,
|
||||||
|
@ -299,11 +137,7 @@ impl CTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
|
||||||
self.left.is_none() && self.right.is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
|
|
||||||
Optional::write(&mut writer, &self.left, |w, n| n.write(w))?;
|
Optional::write(&mut writer, &self.left, |w, n| n.write(w))?;
|
||||||
Optional::write(&mut writer, &self.right, |w, n| n.write(w))?;
|
Optional::write(&mut writer, &self.right, |w, n| n.write(w))?;
|
||||||
Vector::write(&mut writer, &self.parents, |w, e| {
|
Vector::write(&mut writer, &self.parents, |w, e| {
|
||||||
|
@ -311,7 +145,19 @@ impl CTree {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_position(&self) -> usize {
|
pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
|
||||||
|
let left = Optional::read(&mut reader, |r| Node::read(r))?;
|
||||||
|
let right = Optional::read(&mut reader, |r| Node::read(r))?;
|
||||||
|
let parents = Vector::read(&mut reader, |r| Optional::read(r, |r| Node::read(r)))?;
|
||||||
|
|
||||||
|
Ok(CTree {
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
parents,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_position(&self) -> usize {
|
||||||
let mut p = 0usize;
|
let mut p = 0usize;
|
||||||
for parent in self.parents.iter().rev() {
|
for parent in self.parents.iter().rev() {
|
||||||
if parent.is_some() {
|
if parent.is_some() {
|
||||||
|
@ -327,137 +173,10 @@ impl CTree {
|
||||||
}
|
}
|
||||||
p
|
p
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
pub fn to_commitment_tree(&self) -> CommitmentTree<Node> {
|
||||||
mod tests {
|
let mut bb: Vec<u8> = vec![];
|
||||||
use crate::commitment::{cursor_start_position, CTree, NotePosition};
|
self.write(&mut bb).unwrap();
|
||||||
#[allow(unused_imports)]
|
CommitmentTree::<Node>::read(&*bb).unwrap()
|
||||||
use crate::print::{print_tree, print_witness};
|
|
||||||
use std::time::Instant;
|
|
||||||
use zcash_primitives::merkle_tree::{CommitmentTree, IncrementalWitness};
|
|
||||||
use zcash_primitives::sapling::Node;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Build incremental witnesses with both methods and compare their binary serialization
|
|
||||||
*/
|
|
||||||
#[test]
|
|
||||||
fn test_calc_witnesses() {
|
|
||||||
const NUM_CHUNKS: usize = 3;
|
|
||||||
const NUM_NODES: usize = 20; // number of notes
|
|
||||||
const WITNESS_PERCENT: usize = 1; // percentage of notes that are ours
|
|
||||||
const DEBUG_PRINT: bool = true;
|
|
||||||
|
|
||||||
let _witness_freq = 100000 / WITNESS_PERCENT;
|
|
||||||
let mut tree1: CommitmentTree<Node> = CommitmentTree::empty();
|
|
||||||
let mut tree2: Option<CTree> = None;
|
|
||||||
let mut witnesses: Vec<IncrementalWitness<Node>> = vec![];
|
|
||||||
let mut all_positions: Vec<NotePosition> = vec![];
|
|
||||||
|
|
||||||
for c in 0..NUM_CHUNKS {
|
|
||||||
let mut positions: Vec<usize> = vec![];
|
|
||||||
let mut nodes: Vec<Node> = vec![];
|
|
||||||
for i in 1..=NUM_NODES {
|
|
||||||
let mut bb = [0u8; 32];
|
|
||||||
bb[0..8].copy_from_slice(&i.to_be_bytes());
|
|
||||||
let node = Node::new(bb);
|
|
||||||
|
|
||||||
tree1.append(node).unwrap();
|
|
||||||
|
|
||||||
for w in witnesses.iter_mut() {
|
|
||||||
w.append(node).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if i % witness_freq == 0 {
|
|
||||||
if c == 0 && i == 1 {
|
|
||||||
let w = IncrementalWitness::<Node>::from_tree(&tree1);
|
|
||||||
witnesses.push(w);
|
|
||||||
positions.push((i - 1 + c * NUM_NODES) as usize);
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes.push(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
let start = Instant::now();
|
|
||||||
let n = nodes.len();
|
|
||||||
let mut positions: Vec<_> = positions
|
|
||||||
.iter()
|
|
||||||
.map(|&p| NotePosition::new(p, n + c * NUM_NODES))
|
|
||||||
.collect();
|
|
||||||
all_positions.append(&mut positions);
|
|
||||||
tree2 = Some(CTree::calc_state(nodes, &mut all_positions, tree2));
|
|
||||||
eprintln!(
|
|
||||||
"Update State & Witnesses: {} ms",
|
|
||||||
start.elapsed().as_millis()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let tree2 = tree2.unwrap();
|
|
||||||
|
|
||||||
println!("# witnesses = {}", all_positions.len());
|
|
||||||
|
|
||||||
for (_i, (w, p)) in witnesses.iter().zip(&all_positions).enumerate() {
|
|
||||||
let mut bb1: Vec<u8> = vec![];
|
|
||||||
w.write(&mut bb1).unwrap();
|
|
||||||
|
|
||||||
let mut bb2: Vec<u8> = vec![];
|
|
||||||
p.witness.write(&mut bb2).unwrap();
|
|
||||||
|
|
||||||
// assert_eq!(bb1.as_slice(), bb2.as_slice(), "failed at {}", i);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut bb1: Vec<u8> = vec![];
|
|
||||||
tree1.write(&mut bb1).unwrap();
|
|
||||||
|
|
||||||
let mut bb2: Vec<u8> = vec![];
|
|
||||||
tree2.write(&mut bb2).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(bb1.as_slice(), bb2.as_slice(), "tree states not equal");
|
|
||||||
|
|
||||||
if DEBUG_PRINT {
|
|
||||||
let slot = 0usize;
|
|
||||||
print_witness(&witnesses[slot]);
|
|
||||||
|
|
||||||
println!("+++++");
|
|
||||||
println!("Tree");
|
|
||||||
let t = &all_positions[slot].witness.tree;
|
|
||||||
println!("{:?}", t.left.map(|n| hex::encode(n.repr)));
|
|
||||||
println!("{:?}", t.right.map(|n| hex::encode(n.repr)));
|
|
||||||
for p in t.parents.iter() {
|
|
||||||
println!("{:?}", p.map(|n| hex::encode(n.repr)));
|
|
||||||
}
|
|
||||||
println!("Filled");
|
|
||||||
for f in all_positions[slot].witness.filled.iter() {
|
|
||||||
println!("{:?}", hex::encode(f.repr));
|
|
||||||
}
|
|
||||||
println!("Cursor");
|
|
||||||
let t = &all_positions[slot].witness.cursor;
|
|
||||||
println!("{:?}", t.left.map(|n| hex::encode(n.repr)));
|
|
||||||
println!("{:?}", t.right.map(|n| hex::encode(n.repr)));
|
|
||||||
for p in t.parents.iter() {
|
|
||||||
println!("{:?}", p.map(|n| hex::encode(n.repr)));
|
|
||||||
}
|
|
||||||
println!("====");
|
|
||||||
|
|
||||||
// println!("{:?}", tree1.left.map(|n| hex::encode(n.repr)));
|
|
||||||
// println!("{:?}", tree1.right.map(|n| hex::encode(n.repr)));
|
|
||||||
// for p in tree1.parents.iter() {
|
|
||||||
// println!("{:?}", p.map(|n| hex::encode(n.repr)));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// println!("-----");
|
|
||||||
//
|
|
||||||
// println!("{:?}", tree2.left.map(|n| hex::encode(n.repr)));
|
|
||||||
// println!("{:?}", tree2.right.map(|n| hex::encode(n.repr)));
|
|
||||||
// for p in tree2.parents.iter() {
|
|
||||||
// println!("{:?}", p.map(|n| hex::encode(n.repr)));
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cursor() {
|
|
||||||
// println!("{}", cursor_start_position(8, 14));
|
|
||||||
println!("{}", cursor_start_position(9, 14));
|
|
||||||
// println!("{}", cursor_start_position(10, 14));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
use rusqlite::{Connection, params, OptionalExtension};
|
||||||
|
use crate::{Witness, CTree};
|
||||||
|
|
||||||
|
pub struct DbAdapter {
|
||||||
|
connection: Connection,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ReceivedNote {
|
||||||
|
pub height: u32,
|
||||||
|
pub output_index: u32,
|
||||||
|
pub diversifier: Vec<u8>,
|
||||||
|
pub value: u64,
|
||||||
|
pub rcm: Vec<u8>,
|
||||||
|
pub nf: Vec<u8>,
|
||||||
|
pub is_change: bool,
|
||||||
|
pub memo: Vec<u8>,
|
||||||
|
pub spent: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DbAdapter {
|
||||||
|
pub fn new(db_path: &str) -> anyhow::Result<DbAdapter> {
|
||||||
|
let connection = Connection::open(db_path)?;
|
||||||
|
Ok(DbAdapter {
|
||||||
|
connection,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_db(&self) -> anyhow::Result<()> {
|
||||||
|
self.connection.execute("CREATE TABLE IF NOT EXISTS blocks (
|
||||||
|
height INTEGER PRIMARY KEY,
|
||||||
|
hash BLOB NOT NULL,
|
||||||
|
sapling_tree BLOB NOT NULL)", [])?;
|
||||||
|
|
||||||
|
self.connection.execute("CREATE TABLE IF NOT EXISTS transactions (
|
||||||
|
id_tx INTEGER PRIMARY KEY,
|
||||||
|
txid BLOB NOT NULL UNIQUE,
|
||||||
|
height INTEGER,
|
||||||
|
tx_index INTEGER)", [])?;
|
||||||
|
|
||||||
|
self.connection.execute("CREATE TABLE IF NOT EXISTS received_notes (
|
||||||
|
id_note INTEGER PRIMARY KEY,
|
||||||
|
position INTEGER NOT NULL,
|
||||||
|
tx INTEGER NOT NULL,
|
||||||
|
height INTEGER NOT NULL,
|
||||||
|
output_index INTEGER NOT NULL,
|
||||||
|
diversifier BLOB NOT NULL,
|
||||||
|
value INTEGER NOT NULL,
|
||||||
|
rcm BLOB NOT NULL,
|
||||||
|
nf BLOB NOT NULL UNIQUE,
|
||||||
|
is_change INTEGER NOT NULL,
|
||||||
|
memo BLOB,
|
||||||
|
spent INTEGER,
|
||||||
|
FOREIGN KEY (tx) REFERENCES transactions(id_tx),
|
||||||
|
FOREIGN KEY (spent) REFERENCES transactions(id_tx),
|
||||||
|
CONSTRAINT tx_output UNIQUE (tx, output_index))", [])?;
|
||||||
|
|
||||||
|
self.connection.execute("CREATE TABLE IF NOT EXISTS sapling_witnesses (
|
||||||
|
id_witness INTEGER PRIMARY KEY,
|
||||||
|
note INTEGER NOT NULL,
|
||||||
|
height INTEGER NOT NULL,
|
||||||
|
witness BLOB NOT NULL,
|
||||||
|
FOREIGN KEY (note) REFERENCES received_notes(id_note),
|
||||||
|
CONSTRAINT witness_height UNIQUE (note, height))", [])?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trim_to_height(&mut self, height: u32) -> anyhow::Result<()> {
|
||||||
|
let tx = self.connection.transaction()?;
|
||||||
|
tx.execute("DELETE FROM blocks WHERE height >= ?1", params![height])?;
|
||||||
|
tx.execute("DELETE FROM sapling_witnesses WHERE height >= ?1", params![height])?;
|
||||||
|
tx.execute("DELETE FROM received_notes WHERE height >= ?1", params![height])?;
|
||||||
|
tx.execute("DELETE FROM transactions WHERE height >= ?1", params![height])?;
|
||||||
|
tx.commit()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store_block(&self, height: u32, hash: &[u8], tree: &CTree) -> anyhow::Result<()> {
|
||||||
|
let mut bb: Vec<u8> = vec![];
|
||||||
|
tree.write(&mut bb)?;
|
||||||
|
self.connection.execute("INSERT INTO blocks(height, hash, sapling_tree)
|
||||||
|
VALUES (?1, ?2, ?3)
|
||||||
|
ON CONFLICT DO NOTHING", params![height, hash, &bb])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store_transaction(&self, txid: &[u8], height: u32, tx_index: u32) -> anyhow::Result<u32> {
|
||||||
|
self.connection.execute("INSERT INTO transactions(txid, height, tx_index)
|
||||||
|
VALUES (?1, ?2, ?3)
|
||||||
|
ON CONFLICT DO NOTHING", params![txid, height, tx_index])?;
|
||||||
|
let id_tx: u32 = self.connection.query_row("SELECT id_tx FROM transactions WHERE txid = ?1", params![txid], |row| row.get(0))?;
|
||||||
|
Ok(id_tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store_received_note(&self, note: &ReceivedNote, id_tx: u32, position: usize) -> anyhow::Result<u32> {
|
||||||
|
self.connection.execute("INSERT INTO received_notes(tx, height, position, output_index, diversifier, value, rcm, nf, is_change, memo, spent)
|
||||||
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)
|
||||||
|
ON CONFLICT DO NOTHING", params![id_tx, note.height, position, note.output_index, note.diversifier, note.value, note.rcm, note.nf, note.is_change, note.memo, note.spent])?;
|
||||||
|
let id_note: u32 = self.connection.query_row("SELECT id_note FROM received_notes WHERE tx = ?1 AND output_index = ?2", params![id_tx, note.output_index], |row| row.get(0))?;
|
||||||
|
Ok(id_note)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store_witnesses(&self, witness: &Witness, height: u32, id_note: u32) -> anyhow::Result<()> {
|
||||||
|
let mut bb: Vec<u8> = vec![];
|
||||||
|
witness.write(&mut bb)?;
|
||||||
|
println!("{} {}", height, id_note);
|
||||||
|
self.connection.execute("INSERT INTO sapling_witnesses(note, height, witness) VALUES (?1, ?2, ?3)
|
||||||
|
ON CONFLICT DO NOTHING", params![id_note, height, bb])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_balance(&self) -> anyhow::Result<u64> {
|
||||||
|
let balance: u64 = self.connection.query_row("SELECT SUM(value) FROM received_notes WHERE spent = 0", [], |row| row.get(0))?;
|
||||||
|
Ok(balance)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_last_height(&self) -> anyhow::Result<Option<u32>> {
|
||||||
|
let height: Option<u32> = self.connection.query_row("SELECT MAX(height) FROM blocks", [], |row| row.get(0)).optional()?;
|
||||||
|
Ok(height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_tree(&self) -> anyhow::Result<(CTree, Vec<Witness>)> {
|
||||||
|
let res = self.connection.query_row(
|
||||||
|
"SELECT height, sapling_tree FROM blocks WHERE height = (SELECT MAX(height) FROM blocks)",
|
||||||
|
[], |row| {
|
||||||
|
let height: u32 = row.get(0)?;
|
||||||
|
let tree: Vec<u8> = row.get(1)?;
|
||||||
|
Ok((height, tree))
|
||||||
|
}).optional()?;
|
||||||
|
Ok(match res {
|
||||||
|
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")?;
|
||||||
|
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 mut witnesses: Vec<Witness> = vec![];
|
||||||
|
for w in ws {
|
||||||
|
witnesses.push(w?);
|
||||||
|
}
|
||||||
|
(tree, witnesses)
|
||||||
|
},
|
||||||
|
None => (CTree::new(), vec![])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::db::{DbAdapter, ReceivedNote};
|
||||||
|
use crate::{Witness, CTree};
|
||||||
|
|
||||||
|
const DB_PATH: &str = "zec.db";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_db() {
|
||||||
|
let mut db = DbAdapter::new(DB_PATH).unwrap();
|
||||||
|
db.init_db().unwrap();
|
||||||
|
db.trim_to_height(0).unwrap();
|
||||||
|
|
||||||
|
db.store_block(1, &[0u8; 32], &CTree::new()).unwrap();
|
||||||
|
let id_tx = db.store_transaction(&[0; 32], 1, 20).unwrap();
|
||||||
|
db.store_received_note(&ReceivedNote {
|
||||||
|
height: 1,
|
||||||
|
output_index: 0,
|
||||||
|
diversifier: vec![],
|
||||||
|
value: 0,
|
||||||
|
rcm: vec![],
|
||||||
|
nf: vec![],
|
||||||
|
is_change: false,
|
||||||
|
memo: vec![],
|
||||||
|
spent: false
|
||||||
|
}, id_tx, 5).unwrap();
|
||||||
|
let witness = Witness {
|
||||||
|
position: 10,
|
||||||
|
id_note: 0,
|
||||||
|
note: None,
|
||||||
|
tree: CTree::new(),
|
||||||
|
filled: vec![],
|
||||||
|
cursor: CTree::new(),
|
||||||
|
};
|
||||||
|
db.store_witnesses(&witness, 1000, 1).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_balance() {
|
||||||
|
let db = DbAdapter::new(DB_PATH).unwrap();
|
||||||
|
let balance = db.get_balance().unwrap();
|
||||||
|
println!("{}", balance);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
use bip39::{Language, Mnemonic, Seed};
|
||||||
|
use zcash_primitives::zip32::{ExtendedSpendingKey, ExtendedFullViewingKey, ChildIndex};
|
||||||
|
use crate::NETWORK;
|
||||||
|
use zcash_primitives::consensus::Parameters;
|
||||||
|
use zcash_client_backend::encoding::{encode_extended_spending_key, encode_extended_full_viewing_key, encode_payment_address, decode_extended_spending_key, decode_extended_full_viewing_key};
|
||||||
|
use anyhow::anyhow;
|
||||||
|
|
||||||
|
pub fn get_secret_key(seed: &str) -> anyhow::Result<String> {
|
||||||
|
let mnemonic = Mnemonic::from_phrase(&seed, Language::English)?;
|
||||||
|
let seed = Seed::new(&mnemonic, "");
|
||||||
|
let master = ExtendedSpendingKey::master(seed.as_bytes());
|
||||||
|
let path = [
|
||||||
|
ChildIndex::Hardened(32),
|
||||||
|
ChildIndex::Hardened(NETWORK.coin_type()),
|
||||||
|
ChildIndex::Hardened(0),
|
||||||
|
];
|
||||||
|
let extsk = ExtendedSpendingKey::from_path(&master, &path);
|
||||||
|
let spending_key = encode_extended_spending_key(NETWORK.hrp_sapling_extended_spending_key(), &extsk);
|
||||||
|
|
||||||
|
Ok(spending_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_viewing_key(secret_key: &str) -> anyhow::Result<String> {
|
||||||
|
let extsk = decode_extended_spending_key(NETWORK.hrp_sapling_extended_spending_key(), secret_key)?.ok_or(anyhow!("Invalid Secret Key"))?;
|
||||||
|
let fvk = ExtendedFullViewingKey::from(&extsk);
|
||||||
|
let viewing_key = encode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &fvk);
|
||||||
|
Ok(viewing_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_address(viewing_key: &str) -> anyhow::Result<String> {
|
||||||
|
let fvk = decode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &viewing_key)?.ok_or(anyhow!("Invalid Viewing Key"))?;
|
||||||
|
let (_, payment_address) = fvk.default_address().unwrap();
|
||||||
|
let address = encode_payment_address(NETWORK.hrp_sapling_payment_address(), &payment_address);
|
||||||
|
Ok(address)
|
||||||
|
}
|
18
src/lib.rs
18
src/lib.rs
|
@ -5,16 +5,22 @@ pub mod lw_rpc;
|
||||||
|
|
||||||
pub const NETWORK: Network = Network::MainNetwork;
|
pub const NETWORK: Network = Network::MainNetwork;
|
||||||
|
|
||||||
mod print;
|
mod builder;
|
||||||
mod chain;
|
mod chain;
|
||||||
mod path;
|
|
||||||
mod commitment;
|
mod commitment;
|
||||||
mod scan;
|
mod scan;
|
||||||
mod builder;
|
mod key;
|
||||||
|
mod db;
|
||||||
|
mod wallet;
|
||||||
|
|
||||||
pub use crate::chain::{LWD_URL, get_latest_height, download_chain, calculate_tree_state_v2, DecryptNode};
|
|
||||||
pub use crate::commitment::{NotePosition, Witness, CTree};
|
|
||||||
pub use crate::builder::advance_tree;
|
pub use crate::builder::advance_tree;
|
||||||
|
pub use crate::chain::{
|
||||||
|
calculate_tree_state_v2, connect_lightwalletd, download_chain, get_latest_height, sync,
|
||||||
|
DecryptNode, LWD_URL,
|
||||||
|
};
|
||||||
|
pub use crate::commitment::{CTree, Witness};
|
||||||
pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
||||||
pub use crate::lw_rpc::*;
|
pub use crate::lw_rpc::*;
|
||||||
pub use crate::scan::scan_all;
|
pub use crate::scan::{scan_all, sync_async, latest_height};
|
||||||
|
pub use crate::key::{get_secret_key, get_address, get_viewing_key};
|
||||||
|
pub use crate::db::DbAdapter;
|
||||||
|
|
|
@ -16,9 +16,8 @@ async fn main_scan() {
|
||||||
decode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &ivk)
|
decode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &ivk)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ivk = fvk.fvk.vk.ivk();
|
|
||||||
|
|
||||||
scan_all(&vec![ivk]).await.unwrap();
|
scan_all(&vec![fvk]).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -48,7 +47,7 @@ fn test_increasing_notes() {
|
||||||
if v % witness_freq == 0 {
|
if v % witness_freq == 0 {
|
||||||
// let w = IncrementalWitness::from_tree(&tree1);
|
// let w = IncrementalWitness::from_tree(&tree1);
|
||||||
// ws.push(w);
|
// ws.push(w);
|
||||||
ws2.push(Witness::new(v));
|
ws2.push(Witness::new(v, 0, None));
|
||||||
}
|
}
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
}
|
}
|
||||||
|
@ -89,7 +88,7 @@ fn test_increasing_gap(run_normal: bool, run_warp: bool) {
|
||||||
let w = IncrementalWitness::from_tree(&tree1);
|
let w = IncrementalWitness::from_tree(&tree1);
|
||||||
ws.push(w);
|
ws.push(w);
|
||||||
}
|
}
|
||||||
ws2.push(Witness::new(pos));
|
ws2.push(Witness::new(pos, 0, None));
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
use sync::{sync_async, DbAdapter};
|
||||||
|
use bip39::{Language, Mnemonic};
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
use rand::RngCore;
|
||||||
|
|
||||||
|
const DB_NAME: &str = "zec.db";
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
async fn test() -> anyhow::Result<()> {
|
||||||
|
dotenv::dotenv().unwrap();
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let ivk = dotenv::var("IVK").unwrap();
|
||||||
|
{
|
||||||
|
let db = DbAdapter::new(DB_NAME)?;
|
||||||
|
db.init_db()?;
|
||||||
|
}
|
||||||
|
sync_async(&ivk, 50000, DB_NAME, |height| {
|
||||||
|
log::info!("Height = {}", height);
|
||||||
|
}).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn test_rewind() {
|
||||||
|
let mut db = DbAdapter::new(DB_NAME).unwrap();
|
||||||
|
db.trim_to_height(1_250_000).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// test_rewind();
|
||||||
|
test().unwrap();
|
||||||
|
// let mut entropy = [0u8; 32];
|
||||||
|
// OsRng.fill_bytes(&mut entropy);
|
||||||
|
// let mnemonic = Mnemonic::from_entropy(&entropy, Language::English).unwrap();
|
||||||
|
// let phrase = mnemonic.phrase();
|
||||||
|
// println!("Seed Phrase: {}", phrase);
|
||||||
|
}
|
37
src/path.rs
37
src/path.rs
|
@ -1,37 +0,0 @@
|
||||||
use zcash_primitives::merkle_tree::Hashable;
|
|
||||||
use zcash_primitives::sapling::Node;
|
|
||||||
|
|
||||||
pub struct MerklePath {
|
|
||||||
left: Option<Node>,
|
|
||||||
right: Option<Node>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MerklePath {
|
|
||||||
pub fn new(left: Option<Node>, right: Option<Node>) -> Self {
|
|
||||||
MerklePath { left, right }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self) -> Node {
|
|
||||||
self.left.unwrap() // shouldn't call if empty
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(&mut self, right: Node) {
|
|
||||||
assert!(self.left.is_some());
|
|
||||||
self.right = Some(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn up(&mut self, depth: usize, parent: &Option<Node>) {
|
|
||||||
let node = if self.left.is_some() && self.right.is_some() {
|
|
||||||
Some(Node::combine(depth, &self.left.unwrap(), &self.right.unwrap()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
if parent.is_some() {
|
|
||||||
self.left = *parent;
|
|
||||||
self.right = node;
|
|
||||||
} else {
|
|
||||||
self.left = node;
|
|
||||||
self.right = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
28
src/print.rs
28
src/print.rs
|
@ -1,28 +0,0 @@
|
||||||
use zcash_primitives::sapling::Node;
|
|
||||||
use zcash_primitives::merkle_tree::{CommitmentTree, IncrementalWitness};
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn print_node(n: &Node) {
|
|
||||||
println!("{:?}", hex::encode(n.repr));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn print_tree(t: &CommitmentTree<Node>) {
|
|
||||||
println!("{:?}", t.left.map(|n| hex::encode(n.repr)));
|
|
||||||
println!("{:?}", t.right.map(|n| hex::encode(n.repr)));
|
|
||||||
for p in t.parents.iter() {
|
|
||||||
println!("{:?}", p.map(|n| hex::encode(n.repr)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn print_witness(w: &IncrementalWitness<Node>) {
|
|
||||||
println!("Tree");
|
|
||||||
print_tree(&w.tree);
|
|
||||||
println!("Filled");
|
|
||||||
for n in w.filled.iter() {
|
|
||||||
print_node(n);
|
|
||||||
}
|
|
||||||
println!("Cursor");
|
|
||||||
w.cursor.as_ref().map(|c| print_tree(c));
|
|
||||||
}
|
|
158
src/scan.rs
158
src/scan.rs
|
@ -1,12 +1,18 @@
|
||||||
use zcash_primitives::sapling::SaplingIvk;
|
use zcash_primitives::sapling::Node;
|
||||||
use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
||||||
use crate::{DecryptNode, LWD_URL, get_latest_height, download_chain, calculate_tree_state_v2};
|
use crate::{DecryptNode, LWD_URL, get_latest_height, download_chain, calculate_tree_state_v2, CompactBlock, NETWORK, connect_lightwalletd, Witness, advance_tree};
|
||||||
use zcash_primitives::consensus::{NetworkUpgrade, Parameters};
|
use zcash_primitives::consensus::{NetworkUpgrade, Parameters};
|
||||||
|
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use std::ops::Range;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use crate::db::{DbAdapter, ReceivedNote};
|
||||||
|
use ff::PrimeField;
|
||||||
|
use zcash_primitives::zip32::ExtendedFullViewingKey;
|
||||||
|
|
||||||
pub async fn scan_all(ivks: &[SaplingIvk]) -> anyhow::Result<()> {
|
pub async fn scan_all(fvks: &[ExtendedFullViewingKey]) -> anyhow::Result<()> {
|
||||||
let decrypter = DecryptNode::new(ivks.to_vec());
|
let decrypter = DecryptNode::new(fvks.to_vec());
|
||||||
|
|
||||||
let total_start = Instant::now();
|
let total_start = Instant::now();
|
||||||
let mut client = CompactTxStreamerClient::connect(LWD_URL).await?;
|
let mut client = CompactTxStreamerClient::connect(LWD_URL).await?;
|
||||||
|
@ -38,3 +44,147 @@ pub async fn scan_all(ivks: &[SaplingIvk]) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Blocks(Vec<CompactBlock>);
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Blocks {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Blocks of len {}", self.0.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_db_height(db_path: &str) -> anyhow::Result<u32> {
|
||||||
|
let db = DbAdapter::new(db_path).unwrap();
|
||||||
|
let height: u32 = db.get_last_height()?.unwrap_or_else(|| {
|
||||||
|
crate::NETWORK
|
||||||
|
.activation_height(NetworkUpgrade::Sapling)
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
});
|
||||||
|
Ok(height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn sync_async(ivk: &str, chunk_size: u32, db_path: &str, progress_callback: impl Fn(u32) + Send + 'static) -> anyhow::Result<()> {
|
||||||
|
let db_path = db_path.to_string();
|
||||||
|
let fvk =
|
||||||
|
decode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &ivk)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
let decrypter = DecryptNode::new(vec![fvk]);
|
||||||
|
|
||||||
|
let mut client = connect_lightwalletd().await?;
|
||||||
|
let start_height = get_db_height(&db_path)?;
|
||||||
|
let end_height = get_latest_height(&mut client).await?;
|
||||||
|
|
||||||
|
let (downloader_tx, mut download_rx) = mpsc::channel::<Range<u32>>(2);
|
||||||
|
let (processor_tx, mut processor_rx) = mpsc::channel::<Blocks>(2);
|
||||||
|
let (completed_tx, mut completed_rx) = mpsc::channel::<()>(1);
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut client = connect_lightwalletd().await.unwrap();
|
||||||
|
while let Some(range) = download_rx.recv().await {
|
||||||
|
log::info!("{:?}", range);
|
||||||
|
let blocks = download_chain(&mut client, range.start, range.end).await.unwrap();
|
||||||
|
let b = Blocks(blocks);
|
||||||
|
processor_tx.send(b).await.unwrap();
|
||||||
|
}
|
||||||
|
log::info!("download completed");
|
||||||
|
drop(processor_tx);
|
||||||
|
|
||||||
|
// Ok::<_, anyhow::Error>(())
|
||||||
|
});
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let db = DbAdapter::new(&db_path).unwrap();
|
||||||
|
let (mut tree, mut witnesses) = db.get_tree().unwrap();
|
||||||
|
let mut pos = tree.get_position();
|
||||||
|
// let mut tree = CTree::new();
|
||||||
|
// let mut witnesses: Vec<Witness> = vec![];
|
||||||
|
while let Some(blocks) = processor_rx.recv().await {
|
||||||
|
log::info!("{:?}", blocks);
|
||||||
|
if blocks.0.is_empty() { continue }
|
||||||
|
|
||||||
|
let dec_blocks = decrypter.decrypt_blocks(&blocks.0);
|
||||||
|
for b in dec_blocks.iter() {
|
||||||
|
if !b.notes.is_empty() {
|
||||||
|
log::info!("{} {}", b.height, b.notes.len());
|
||||||
|
}
|
||||||
|
for n in b.notes.iter() {
|
||||||
|
let p = pos + n.position;
|
||||||
|
|
||||||
|
let note = &n.note;
|
||||||
|
let id_tx = db.store_transaction(&n.txid, n.height, n.tx_index as u32).unwrap();
|
||||||
|
let rcm = note.rcm().to_repr();
|
||||||
|
let nf = note.nf(&n.ivk.fvk.vk, n.position as u64);
|
||||||
|
|
||||||
|
let id_note = db.store_received_note(&ReceivedNote {
|
||||||
|
height: n.height,
|
||||||
|
output_index: n.output_index as u32,
|
||||||
|
diversifier: n.pa.diversifier().0.to_vec(),
|
||||||
|
value: note.value,
|
||||||
|
rcm: rcm.to_vec(),
|
||||||
|
nf: nf.0.to_vec(),
|
||||||
|
is_change: false, // TODO: it's change the ovk matches too
|
||||||
|
memo: vec![],
|
||||||
|
spent: false
|
||||||
|
}, id_tx, n.position).unwrap();
|
||||||
|
|
||||||
|
let w = Witness::new(p as usize, id_note, Some(n.clone()));
|
||||||
|
witnesses.push(w);
|
||||||
|
}
|
||||||
|
pos += b.count_outputs as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nodes: Vec<Node> = vec![];
|
||||||
|
for cb in blocks.0.iter() {
|
||||||
|
for tx in cb.vtx.iter() {
|
||||||
|
for co in tx.outputs.iter() {
|
||||||
|
let mut cmu = [0u8; 32];
|
||||||
|
cmu.copy_from_slice(&co.cmu);
|
||||||
|
let node = Node::new(cmu);
|
||||||
|
nodes.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (new_tree, new_witnesses) = advance_tree(tree, &witnesses, &mut nodes);
|
||||||
|
tree = new_tree;
|
||||||
|
witnesses = new_witnesses;
|
||||||
|
|
||||||
|
let last_block = blocks.0.last().unwrap();
|
||||||
|
let last_height = last_block.height as u32;
|
||||||
|
db.store_block(last_height, &last_block.hash, &tree).unwrap();
|
||||||
|
for w in witnesses.iter() {
|
||||||
|
db.store_witnesses(w, last_height, w.id_note).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
progress_callback(blocks.0[0].height as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
progress_callback(end_height);
|
||||||
|
log::info!("Witnesses {}", witnesses.len());
|
||||||
|
drop(completed_tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut height = start_height;
|
||||||
|
while height < end_height {
|
||||||
|
let s = height;
|
||||||
|
let e = (height + chunk_size).min(end_height);
|
||||||
|
let range = s..e;
|
||||||
|
|
||||||
|
downloader_tx.send(range).await?;
|
||||||
|
|
||||||
|
height = e;
|
||||||
|
}
|
||||||
|
drop(downloader_tx);
|
||||||
|
log::info!("req downloading completed");
|
||||||
|
|
||||||
|
completed_rx.recv().await;
|
||||||
|
log::info!("completed");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn latest_height() -> u32 {
|
||||||
|
let mut client = connect_lightwalletd().await.unwrap();
|
||||||
|
get_latest_height(&mut client).await.unwrap()
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue