From b912dee85b0dfa11c3b3a7975f5448f544b35947 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 9 Apr 2024 21:14:02 +1000 Subject: [PATCH 01/10] store tip hash and height in tiny-cash struct rather than query state --- justfile | 3 - tiny-cash/src/write.rs | 162 ++++++++++++++++++++++------------------- 2 files changed, 86 insertions(+), 79 deletions(-) diff --git a/justfile b/justfile index ab653d9..86eb49a 100644 --- a/justfile +++ b/justfile @@ -9,9 +9,6 @@ run: run-local: ROLLUP_HTTP_SERVER_URL=http://127.0.0.1:8080/host-runner GRPC_SERVER_URL="[::1]:50051" cargo run -run-proxy: - CARTESI_NODE_URL="0.0.0.0:8080" cargo run -p cartezcash-proxy - sunodo-nobackend: sunodo run --no-backend diff --git a/tiny-cash/src/write.rs b/tiny-cash/src/write.rs index 2ef8b7c..efdc946 100644 --- a/tiny-cash/src/write.rs +++ b/tiny-cash/src/write.rs @@ -30,6 +30,9 @@ pub type BoxError = Box; pub struct TinyCashWriteService { state_service: S, tx_verifier_service: V, + + tip_height: Height, + tip_hash: Option, } impl TinyCashWriteService { @@ -37,6 +40,9 @@ impl TinyCashWriteService { Self { state_service, tx_verifier_service, + + tip_height: Height(0), + tip_hash: None, } } } @@ -98,89 +104,93 @@ where let mut state_service = self.state_service.clone(); let mut transaction_verifier = self.tx_verifier_service.clone(); - async move { - let (block, height, burns) = match req { - Request::Genesis => { - ( - Block::zcash_deserialize( - zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES.as_slice(), - ) - .unwrap(), - Height(0), - Vec::new(), - ) // here is one I prepared earlier :) - } - _ => { - let (tip_height, previous_block_hash) = match state_service - .ready() - .await? - .call(zebra_state::Request::Tip) - .await? - { - zebra_state::Response::Tip(Some(tip)) => tip, - _ => panic!("unexpected reponse for tip request"), - }; + let tip_height = self.tip_height; + let previous_block_hash = self.tip_hash.unwrap_or(Default::default()); - let height = (tip_height + 1).unwrap(); + let (block, height, burns) = match req { + Request::Genesis => { + ( + Block::zcash_deserialize( + zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES.as_slice(), + ) + .unwrap(), + Height(0), + Vec::new(), + ) // here is one I prepared earlier :) + } + _ => { + // let (tip_height, previous_block_hash) = match state_service + // .ready() + // .await? + // .call(zebra_state::Request::Tip) + // .await? + // { + // zebra_state::Response::Tip(Some(tip)) => tip, + // _ => panic!("unexpected reponse for tip request"), + // }; + let height = tip_height.next().unwrap(); - // Every block needs a coinbase transaction which records the height - // For a mint event this will also be used to mint new coins - // otherwise it is just an empty txn - // also keep track of if any coins were burned by sending to the mt doom script - let (transactions, burns) = match req { - Request::Genesis => { - unreachable!("genesis block is handled prior") - } - Request::Mint { amount, to } => { - let coinbase_tx = mint_coinbase_txn(amount, &to, height); - let burns = Vec::new(); - (vec![Arc::new(coinbase_tx)], burns) - } - Request::IncludeTransaction { transaction } => { - let coinbase_tx = empty_coinbase_txn(height); - // check for withdrawals here - let burns = transaction - .orchard_actions() - .filter_map(extract_burn_info) - .collect(); - (vec![Arc::new(coinbase_tx), Arc::new(transaction)], burns) - } - }; + // Every block needs a coinbase transaction which records the height + // For a mint event this will also be used to mint new coins + // otherwise it is just an empty txn + // also keep track of if any coins were burned by sending to the mt doom script + let (transactions, burns) = match req { + Request::Genesis => { + unreachable!("genesis block is handled prior") + } + Request::Mint { amount, to } => { + let coinbase_tx = mint_coinbase_txn(amount, &to, height); + let burns = Vec::new(); + (vec![Arc::new(coinbase_tx)], burns) + } + Request::IncludeTransaction { transaction } => { + let coinbase_tx = empty_coinbase_txn(height); + // check for withdrawals here + let burns = transaction + .orchard_actions() + .filter_map(extract_burn_info) + .collect(); + (vec![Arc::new(coinbase_tx), Arc::new(transaction)], burns) + } + }; - // build the block! - let block = Block { - header: Header { - version: 5, - previous_block_hash, - merkle_root: transactions.iter().collect(), - commitment_bytes: HexDebug::default(), - time: DateTime::::default(), - difficulty_threshold: CompactDifficulty::default(), - nonce: HexDebug::default(), - solution: Solution::default(), - } - .into(), - transactions: transactions.clone(), - }; - (block, height, burns) - } - }; + // build the block! + let block = Block { + header: Header { + version: 5, + previous_block_hash, + merkle_root: transactions.iter().collect(), + commitment_bytes: HexDebug::default(), + time: DateTime::::default(), + difficulty_threshold: CompactDifficulty::default(), + nonce: HexDebug::default(), + solution: Solution::default(), + } + .into(), + transactions: transactions.clone(), + }; + (block, height, burns) + } + }; - // the below checks are from the zebra-consensus block verifier - // this logic mostly taken from zebra-consensus block verifier - // https://github.com/ZcashFoundation/zebra/blob/main/zebra-consensus/src/block.rs + // the below checks are from the zebra-consensus block verifier + // this logic mostly taken from zebra-consensus block verifier + // https://github.com/ZcashFoundation/zebra/blob/main/zebra-consensus/src/block.rs - let ret = block.clone(); - let block_hash = block.hash(); - let transaction_hashes: Arc<[_]> = - block.transactions.iter().map(|t| t.hash()).collect(); - let transactions = block.transactions.clone(); - let known_utxos = Arc::new(transparent::new_ordered_outputs( - &block, - &transaction_hashes, - )); + let ret = block.clone(); + let block_hash = block.hash(); + let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|t| t.hash()).collect(); + let transactions = block.transactions.clone(); + let known_utxos = Arc::new(transparent::new_ordered_outputs( + &block, + &transaction_hashes, + )); + self.tip_hash = Some(block_hash); + self.tip_height = height; + + async move { if height > Height(0) { tracing::info!("Verifying transactions..... may take a while. Please wait before rescanning wallet"); From 0ad49e25fcd757f2de7673286cf2425303cef89d Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Wed, 10 Apr 2024 17:52:00 +1000 Subject: [PATCH 02/10] refactor tiny-cash write service --- Cargo.lock | 491 ++--------------------------------------- Cargo.toml | 11 +- justfile | 2 +- src/main.rs | 33 +-- tiny-cash/src/write.rs | 151 ++++++------- 5 files changed, 121 insertions(+), 567 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac51390..363875d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,59 +75,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" -[[package]] -name = "alloy-json-abi" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ed0f2a6c3a1c947b4508522a53a190dba8f94dcd4e3e1a5af945a498e78f2f" -dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-primitives" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" -dependencies = [ - "alloy-rlp", - "bytes", - "cfg-if", - "const-hex", - "derive_more", - "hex-literal", - "itoa", - "k256 0.13.3", - "keccak-asm", - "proptest", - "rand", - "ruint", - "serde", - "tiny-keccak", -] - -[[package]] -name = "alloy-rlp" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" -dependencies = [ - "arrayvec", - "bytes", -] - -[[package]] -name = "alloy-sol-type-parser" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0045cc89524e1451ccf33e8581355b6027ac7c6e494bb02959d4213ad0d8e91d" -dependencies = [ - "winnow 0.6.5", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -149,130 +96,6 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" -[[package]] -name = "ark-ff" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" -dependencies = [ - "ark-ff-asm 0.3.0", - "ark-ff-macros 0.3.0", - "ark-serialize 0.3.0", - "ark-std 0.3.0", - "derivative", - "num-bigint", - "num-traits", - "paste", - "rustc_version 0.3.3", - "zeroize", -] - -[[package]] -name = "ark-ff" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" -dependencies = [ - "ark-ff-asm 0.4.2", - "ark-ff-macros 0.4.2", - "ark-serialize 0.4.2", - "ark-std 0.4.0", - "derivative", - "digest 0.10.7", - "itertools 0.10.5", - "num-bigint", - "num-traits", - "paste", - "rustc_version 0.4.0", - "zeroize", -] - -[[package]] -name = "ark-ff-asm" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-asm" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" -dependencies = [ - "num-bigint", - "num-traits", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" -dependencies = [ - "num-bigint", - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-serialize" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" -dependencies = [ - "ark-std 0.3.0", - "digest 0.9.0", -] - -[[package]] -name = "ark-serialize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" -dependencies = [ - "ark-std 0.4.0", - "digest 0.10.7", - "num-bigint", -] - -[[package]] -name = "ark-std" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" -dependencies = [ - "num-traits", - "rand", -] - -[[package]] -name = "ark-std" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" -dependencies = [ - "num-traits", - "rand", -] - [[package]] name = "arrayref" version = "0.3.7" @@ -326,7 +149,7 @@ checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" dependencies = [ "futures", "pharos", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -424,12 +247,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base58" version = "0.1.0" @@ -786,7 +603,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.22", + "semver", "serde", "serde_json", "thiserror", @@ -796,14 +613,10 @@ dependencies = [ name = "cartezcash" version = "0.1.0" dependencies = [ - "alloy-json-abi", "anyhow", "base58check", - "base64-url", "cartezcash-lightwalletd", "chrono", - "ciborium", - "ethabi", "ethereum-types", "futures-util", "hex", @@ -993,7 +806,7 @@ dependencies = [ "digest 0.10.7", "getrandom", "hmac", - "k256 0.11.6", + "k256", "lazy_static", "serde", "sha2 0.10.8", @@ -1077,19 +890,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "const-hex" -version = "1.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" -dependencies = [ - "cfg-if", - "cpufeatures", - "hex", - "proptest", - "serde", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -1102,12 +902,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "convert_case" version = "0.6.0" @@ -1194,18 +988,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -1237,7 +1019,7 @@ dependencies = [ "digest 0.10.7", "fiat-crypto", "platforms", - "rustc_version 0.4.0", + "rustc_version", "serde", "subtle", "zeroize", @@ -1360,27 +1142,14 @@ dependencies = [ "serde", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive_more" version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case 0.4.0", "proc-macro2", "quote", - "rustc_version 0.4.0", "syn 1.0.109", ] @@ -1393,15 +1162,6 @@ dependencies = [ "generic-array 0.12.4", ] -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.7", -] - [[package]] name = "digest" version = "0.10.7" @@ -1409,7 +1169,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "const-oid", "crypto-common", "subtle", ] @@ -1468,25 +1227,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", + "elliptic-curve", + "rfc6979", "signature 1.6.4", ] -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der 0.7.8", - "digest 0.10.7", - "elliptic-curve 0.13.8", - "rfc6979 0.4.0", - "signature 2.2.0", - "spki 0.7.3", -] - [[package]] name = "ed25519" version = "2.2.3" @@ -1526,8 +1271,8 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", + "base16ct", + "crypto-bigint", "der 0.6.1", "digest 0.10.7", "ff 0.12.1", @@ -1535,26 +1280,7 @@ dependencies = [ "group 0.12.1", "pkcs8 0.9.0", "rand_core", - "sec1 0.3.0", - "subtle", - "zeroize", -] - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.5", - "digest 0.10.7", - "ff 0.13.0", - "generic-array 0.14.7", - "group 0.13.0", - "pkcs8 0.10.2", - "rand_core", - "sec1 0.7.3", + "sec1", "subtle", "zeroize", ] @@ -1766,12 +1492,12 @@ dependencies = [ "bytes", "cargo_metadata", "chrono", - "convert_case 0.6.0", - "elliptic-curve 0.12.3", + "convert_case", + "elliptic-curve", "ethabi", "generic-array 0.14.7", "hex", - "k256 0.11.6", + "k256", "once_cell", "open-fastrlp", "proc-macro2", @@ -1796,7 +1522,7 @@ dependencies = [ "ethers-core", "getrandom", "reqwest", - "semver 1.0.22", + "semver", "serde", "serde-aux", "serde_json", @@ -1874,7 +1600,7 @@ dependencies = [ "async-trait", "coins-bip32", "coins-bip39", - "elliptic-curve 0.12.3", + "elliptic-curve", "eth-keystore", "ethers-core", "hex", @@ -1914,17 +1640,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" -[[package]] -name = "fastrlp" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" -dependencies = [ - "arrayvec", - "auto_impl 1.2.0", - "bytes", -] - [[package]] name = "ff" version = "0.12.1" @@ -2142,7 +1857,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -2610,15 +2324,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.11.0" @@ -2688,26 +2393,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", + "ecdsa", + "elliptic-curve", "sha2 0.10.8", "sha3", ] -[[package]] -name = "k256" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" -dependencies = [ - "cfg-if", - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", - "once_cell", - "sha2 0.10.8", - "signature 2.2.0", -] - [[package]] name = "keccak" version = "0.1.5" @@ -2717,16 +2408,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "keccak-asm" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb8515fff80ed850aea4a1595f2e519c003e2a00a82fe168ebf5269196caf444" -dependencies = [ - "digest 0.10.7", - "sha3-asm", -] - [[package]] name = "known-folders" version = "1.1.0" @@ -3298,12 +2979,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - [[package]] name = "pbkdf2" version = "0.10.1" @@ -3332,17 +3007,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pest" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - [[package]] name = "pharos" version = "0.5.3" @@ -3350,7 +3014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ "futures", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -3825,21 +3489,11 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint 0.4.9", + "crypto-bigint", "hmac", "zeroize", ] -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - [[package]] name = "ring" version = "0.16.20" @@ -3917,36 +3571,6 @@ dependencies = [ "librocksdb-sys", ] -[[package]] -name = "ruint" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f308135fef9fc398342da5472ce7c484529df23743fb7c734e0f3d472971e62" -dependencies = [ - "alloy-rlp", - "ark-ff 0.3.0", - "ark-ff 0.4.2", - "bytes", - "fastrlp", - "num-bigint", - "num-traits", - "parity-scale-codec", - "primitive-types 0.12.2", - "proptest", - "rand", - "rlp", - "ruint-macro", - "serde", - "valuable", - "zeroize", -] - -[[package]] -name = "ruint-macro" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -3965,22 +3589,13 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.22", + "semver", ] [[package]] @@ -4159,7 +3774,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct 0.1.1", + "base16ct", "der 0.6.1", "generic-array 0.14.7", "pkcs8 0.9.0", @@ -4167,20 +3782,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct 0.2.0", - "der 0.7.8", - "generic-array 0.14.7", - "pkcs8 0.10.2", - "subtle", - "zeroize", -] - [[package]] name = "secp256k1" version = "0.26.0" @@ -4209,15 +3810,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.22" @@ -4227,15 +3819,6 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "send_wrapper" version = "0.6.0" @@ -4367,16 +3950,6 @@ dependencies = [ "keccak", ] -[[package]] -name = "sha3-asm" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac61da6b35ad76b195eb4771210f947734321a8d81d7738e1580d953bc7a15e" -dependencies = [ - "cc", - "cfg-if", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -4417,7 +3990,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest 0.10.7", "rand_core", ] @@ -4827,7 +4399,7 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.2.5", "toml_datetime", - "winnow 0.5.40", + "winnow", ] [[package]] @@ -4838,7 +4410,7 @@ checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ "indexmap 2.2.5", "toml_datetime", - "winnow 0.5.40", + "winnow", ] [[package]] @@ -5062,12 +4634,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - [[package]] name = "uint" version = "0.9.5" @@ -5554,15 +5120,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" -dependencies = [ - "memchr", -] - [[package]] name = "winreg" version = "0.50.0" @@ -5584,7 +5141,7 @@ dependencies = [ "js-sys", "log", "pharos", - "rustc_version 0.4.0", + "rustc_version", "send_wrapper", "thiserror", "wasm-bindgen", @@ -5986,7 +5543,7 @@ dependencies = [ "regex", "rlimit", "rocksdb", - "semver 1.0.22", + "semver", "serde", "tempfile", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 7de7ad9..d2325a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,24 +24,23 @@ ethereum-types = "0.14.1" futures-util = "0.3.30" tracing = "0.1.40" tracing-subscriber = "0.3.18" -ethabi = "18.0.0" -ciborium = "0.2.2" -base64-url = "2.0.2" -alloy-json-abi = "0.6.4" -tonic = "0.11" +tonic = { version = "0.11", optional = true} zebra-chain = { workspace = true } zebra-state = { workspace = true } zebra-consensus = { workspace = true } tiny-cash = { path = "tiny-cash" } -cartezcash-lightwalletd = { path = "cartezcash-lightwalletd" } +cartezcash-lightwalletd = { path = "cartezcash-lightwalletd", optional = true} tower-cartesi = { path = "tower-cartesi" } base58check = "0.1.0" zcash_address = "0.3.2" zcash_keys = { version = "0.2.0", features = ["orchard"] } zcash_primitives = "0.15.0" +[features] +default = [] +lightwalletd = ["dep:cartezcash-lightwalletd", "dep:tonic"] [workspace] members = [ "cartezcash-lightwalletd", "tiny-cash", "tower-cartesi"] diff --git a/justfile b/justfile index 86eb49a..692d738 100644 --- a/justfile +++ b/justfile @@ -7,7 +7,7 @@ run: sunodo run --epoch-duration=10 run-local: - ROLLUP_HTTP_SERVER_URL=http://127.0.0.1:8080/host-runner GRPC_SERVER_URL="[::1]:50051" cargo run + ROLLUP_HTTP_SERVER_URL=http://127.0.0.1:8080/host-runner GRPC_SERVER_URL="[::1]:50051" cargo run --features lightwalletd sunodo-nobackend: sunodo run --no-backend diff --git a/src/main.rs b/src/main.rs index 7f47d78..d05fe01 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,3 @@ -use cartezcash_lightwalletd::{ - proto::service::compact_tx_streamer_server::CompactTxStreamerServer, - service_impl::CompactTxStreamerImpl, -}; use service::{CarteZcashService, Request}; use zcash_keys::address::UnifiedAddress; use zcash_primitives::consensus::MAIN_NETWORK; @@ -19,6 +15,12 @@ use futures_util::future::FutureExt; use zebra_chain::{block, parameters::Network}; use zebra_consensus::transaction as tx; +#[cfg(feature = "lightwalletd")] +use cartezcash_lightwalletd::{ + proto::service::compact_tx_streamer_server::CompactTxStreamerServer, + service_impl::CompactTxStreamerImpl, +}; + type StateService = Buffer< BoxService, zebra_state::Request, @@ -39,7 +41,6 @@ async fn main() -> Result<(), anyhow::Error> { tracing::subscriber::set_global_default(subscriber)?; let server_addr = env::var("ROLLUP_HTTP_SERVER_URL")?; - let grpc_addr = env::var("GRPC_SERVER_URL")?; let network = Network::Mainnet; @@ -62,18 +63,22 @@ async fn main() -> Result<(), anyhow::Error> { 0, ); let state_service = Buffer::new(state_service, 30); - let state_read_service = Buffer::new(state_read_service.boxed(), 30); let mut cartezcash_app = CarteZcashApp::new(network, state_service).await; - let svc = CompactTxStreamerServer::new(CompactTxStreamerImpl { state_read_service }); - let addr = grpc_addr.parse()?; - let grpc_server = tonic::transport::Server::builder() - .trace_fn(|_| tracing::info_span!("cartezcash-grpc")) - .add_service(svc) - .serve(addr); - tokio::spawn(grpc_server); - tracing::info!("wallet GRPC server listening on {}", addr); + #[cfg(feature = "lightwalletd")] + { + let grpc_addr = env::var("GRPC_SERVER_URL")?; + let state_read_service = Buffer::new(state_read_service.boxed(), 30); + let svc = CompactTxStreamerServer::new(CompactTxStreamerImpl { state_read_service }); + let addr = grpc_addr.parse()?; + let grpc_server = tonic::transport::Server::builder() + .trace_fn(|_| tracing::info_span!("cartezcash-grpc")) + .add_service(svc) + .serve(addr); + tokio::spawn(grpc_server); + tracing::info!("wallet GRPC server listening on {}", addr); + } listen_http(&mut cartezcash_app, &server_addr) .await diff --git a/tiny-cash/src/write.rs b/tiny-cash/src/write.rs index efdc946..935292a 100644 --- a/tiny-cash/src/write.rs +++ b/tiny-cash/src/write.rs @@ -8,7 +8,6 @@ use std::{ }; use tower::{Service, ServiceExt}; -use zebra_chain::transparent; use zebra_chain::{ amount::{Amount, NonNegative}, block::{Block, Header, Height}, @@ -17,6 +16,7 @@ use zebra_chain::{ work::{difficulty::CompactDifficulty, equihash::Solution}, }; use zebra_chain::{block, serialization::ZcashDeserialize}; +use zebra_chain::{transaction::UnminedTxId, transparent}; use zebra_chain::{ transaction::{Memo, Transaction}, transparent::Script, @@ -31,7 +31,7 @@ pub struct TinyCashWriteService { state_service: S, tx_verifier_service: V, - tip_height: Height, + tip_height: Option, tip_hash: Option, } @@ -41,7 +41,7 @@ impl TinyCashWriteService { state_service, tx_verifier_service, - tip_height: Height(0), + tip_height: None, tip_hash: None, } } @@ -102,75 +102,27 @@ where fn call(&mut self, req: Request) -> Self::Future { let mut state_service = self.state_service.clone(); - let mut transaction_verifier = self.tx_verifier_service.clone(); + let mut tx_verifier_service = self.tx_verifier_service.clone(); let tip_height = self.tip_height; let previous_block_hash = self.tip_hash.unwrap_or(Default::default()); - let (block, height, burns) = match req { - Request::Genesis => { - ( - Block::zcash_deserialize( - zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES.as_slice(), - ) - .unwrap(), - Height(0), - Vec::new(), - ) // here is one I prepared earlier :) - } - _ => { - // let (tip_height, previous_block_hash) = match state_service - // .ready() - // .await? - // .call(zebra_state::Request::Tip) - // .await? - // { - // zebra_state::Response::Tip(Some(tip)) => tip, - // _ => panic!("unexpected reponse for tip request"), - // }; - - let height = tip_height.next().unwrap(); - - // Every block needs a coinbase transaction which records the height - // For a mint event this will also be used to mint new coins - // otherwise it is just an empty txn - // also keep track of if any coins were burned by sending to the mt doom script - let (transactions, burns) = match req { - Request::Genesis => { - unreachable!("genesis block is handled prior") - } - Request::Mint { amount, to } => { - let coinbase_tx = mint_coinbase_txn(amount, &to, height); - let burns = Vec::new(); - (vec![Arc::new(coinbase_tx)], burns) - } - Request::IncludeTransaction { transaction } => { - let coinbase_tx = empty_coinbase_txn(height); - // check for withdrawals here - let burns = transaction - .orchard_actions() - .filter_map(extract_burn_info) - .collect(); - (vec![Arc::new(coinbase_tx), Arc::new(transaction)], burns) - } - }; + let height = tip_height.map(|h| h.next().unwrap()).unwrap_or(Height(0)); - // build the block! - let block = Block { - header: Header { - version: 5, - previous_block_hash, - merkle_root: transactions.iter().collect(), - commitment_bytes: HexDebug::default(), - time: DateTime::::default(), - difficulty_threshold: CompactDifficulty::default(), - nonce: HexDebug::default(), - solution: Solution::default(), - } - .into(), - transactions: transactions.clone(), - }; - (block, height, burns) + let (block, burns, tx_to_verify) = match req { + Request::Genesis => (genesis_block(), Vec::new(), None), + Request::Mint { amount, to } => { + let block = build_mint_block(height, previous_block_hash, amount, to); + let burns = Vec::new(); + (block, burns, None) + } + Request::IncludeTransaction { transaction } => { + let burns = transaction + .orchard_actions() + .filter_map(extract_burn_info) + .collect(); + let block = build_transact_block(height, previous_block_hash, transaction.clone()); + (block, burns, Some(transaction)) } }; @@ -181,30 +133,27 @@ where let ret = block.clone(); let block_hash = block.hash(); let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|t| t.hash()).collect(); - let transactions = block.transactions.clone(); let known_utxos = Arc::new(transparent::new_ordered_outputs( &block, &transaction_hashes, )); self.tip_hash = Some(block_hash); - self.tip_height = height; - + self.tip_height = Some(height); + async move { if height > Height(0) { - - tracing::info!("Verifying transactions..... may take a while. Please wait before rescanning wallet"); - // verify the transactions - for transaction in &transactions { - transaction_verifier + // verify the transaction if required + if let Some(tx) = tx_to_verify { + tx_verifier_service .ready() .await .expect("transaction verifier is always ready") .call(tx::Request::Block { - transaction: transaction.clone(), - known_utxos: known_utxos.clone(), + transaction: tx.into(), + known_utxos: std::default::Default::default(), height, - time: block.header.time, + time: DateTime::::default(), }) .await?; } @@ -222,7 +171,8 @@ where tracing::info!( "Appending block: height: {:?}, hash: {:?}", - height, block_hash + height, + block_hash ); state_service @@ -251,6 +201,49 @@ where } } +fn build_mint_block( + height: Height, + previous_block_hash: block::Hash, + amount: Amount, + to: transparent::Script, +) -> Block { + let coinbase_tx = mint_coinbase_txn(amount, &to, height); + build_block(previous_block_hash, vec![Arc::new(coinbase_tx)]) +} + +fn build_transact_block( + height: Height, + previous_block_hash: block::Hash, + transaction: Transaction, +) -> Block { + let coinbase_tx = empty_coinbase_txn(height); + build_block( + previous_block_hash, + vec![Arc::new(coinbase_tx), Arc::new(transaction)], + ) +} + +fn genesis_block() -> Block { + Block::zcash_deserialize(zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES.as_slice()).unwrap() +} + +fn build_block(previous_block_hash: block::Hash, transactions: Vec>) -> Block { + Block { + header: Header { + version: 5, + previous_block_hash, + merkle_root: transactions.iter().collect(), + commitment_bytes: HexDebug::default(), + time: DateTime::::default(), + difficulty_threshold: CompactDifficulty::default(), + nonce: HexDebug::default(), + solution: Solution::default(), + } + .into(), + transactions, + } +} + // create a new transparent v5 coinbase transaction that mints the given amount and sends it to the given address fn mint_coinbase_txn( amount: Amount, From 0e1de3686ab63c1c3a60b308297c99b644a3d42e Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Wed, 10 Apr 2024 19:12:18 +1000 Subject: [PATCH 03/10] create transaction verifier function that doesnt use state --- Cargo.lock | 9 +---- Cargo.toml | 18 +++++---- tiny-cash/Cargo.toml | 2 + tiny-cash/src/write.rs | 70 +++++++++++++++++++++++++++++++++- tower-cartesi/examples/echo.rs | 2 +- 5 files changed, 83 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 363875d..086387d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4272,6 +4272,7 @@ dependencies = [ "zcash_note_encryption", "zebra-chain", "zebra-consensus", + "zebra-script", "zebra-state", "zebra-test", ] @@ -4463,7 +4464,6 @@ dependencies = [ [[package]] name = "tower-batch-control" version = "0.2.41-beta.11" -source = "git+https://github.com/willemolding/zebra?rev=185e404bed938e3aa916aee1ae6999a70efceddb#185e404bed938e3aa916aee1ae6999a70efceddb" dependencies = [ "futures", "futures-core", @@ -4497,7 +4497,6 @@ dependencies = [ [[package]] name = "tower-fallback" version = "0.2.41-beta.11" -source = "git+https://github.com/willemolding/zebra?rev=185e404bed938e3aa916aee1ae6999a70efceddb#185e404bed938e3aa916aee1ae6999a70efceddb" dependencies = [ "futures-core", "pin-project", @@ -5410,7 +5409,6 @@ dependencies = [ [[package]] name = "zebra-chain" version = "1.0.0-beta.35" -source = "git+https://github.com/willemolding/zebra?rev=185e404bed938e3aa916aee1ae6999a70efceddb#185e404bed938e3aa916aee1ae6999a70efceddb" dependencies = [ "bitflags 2.5.0", "bitflags-serde-legacy", @@ -5468,7 +5466,6 @@ dependencies = [ [[package]] name = "zebra-consensus" version = "1.0.0-beta.35" -source = "git+https://github.com/willemolding/zebra?rev=185e404bed938e3aa916aee1ae6999a70efceddb#185e404bed938e3aa916aee1ae6999a70efceddb" dependencies = [ "bellman", "blake2b_simd", @@ -5504,7 +5501,6 @@ dependencies = [ [[package]] name = "zebra-node-services" version = "1.0.0-beta.35" -source = "git+https://github.com/willemolding/zebra?rev=185e404bed938e3aa916aee1ae6999a70efceddb#185e404bed938e3aa916aee1ae6999a70efceddb" dependencies = [ "zebra-chain", ] @@ -5512,7 +5508,6 @@ dependencies = [ [[package]] name = "zebra-script" version = "1.0.0-beta.35" -source = "git+https://github.com/willemolding/zebra?rev=185e404bed938e3aa916aee1ae6999a70efceddb#185e404bed938e3aa916aee1ae6999a70efceddb" dependencies = [ "displaydoc", "thiserror", @@ -5523,7 +5518,6 @@ dependencies = [ [[package]] name = "zebra-state" version = "1.0.0-beta.35" -source = "git+https://github.com/willemolding/zebra?rev=185e404bed938e3aa916aee1ae6999a70efceddb#185e404bed938e3aa916aee1ae6999a70efceddb" dependencies = [ "bincode", "chrono", @@ -5557,7 +5551,6 @@ dependencies = [ [[package]] name = "zebra-test" version = "1.0.0-beta.35" -source = "git+https://github.com/willemolding/zebra?rev=185e404bed938e3aa916aee1ae6999a70efceddb#185e404bed938e3aa916aee1ae6999a70efceddb" dependencies = [ "color-eyre", "futures", diff --git a/Cargo.toml b/Cargo.toml index d2325a8..42e8c0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,16 +48,18 @@ members = [ "cartezcash-lightwalletd", "tiny-cash", "tower-cartesi"] [workspace.dependencies] # patched zebra crates to support TinyCash network and expose things we need -zebra-consensus = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } -zebra-state = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } -zebra-test = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } -zebra-chain = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } +# zebra-consensus = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } +# zebra-state = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } +# zebra-test = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } +# zebra-chain = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } +# zebra-script = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } # uncomment for local dev when modifying zebra -# zebra-consensus = { path = "../zebra/zebra-consensus", default-features = false } -# zebra-state = { path = "../zebra/zebra-state", default-features = false } -# zebra-test = { path = "../zebra/zebra-test", default-features = false } -# zebra-chain = { path = "../zebra/zebra-chain", default-features = false } +zebra-consensus = { path = "../zebra/zebra-consensus", default-features = false } +zebra-state = { path = "../zebra/zebra-state", default-features = false } +zebra-test = { path = "../zebra/zebra-test", default-features = false } +zebra-chain = { path = "../zebra/zebra-chain", default-features = false } +zebra-script = { path = "../zebra/zebra-script", default-features = false } [patch.crates-io] diff --git a/tiny-cash/Cargo.toml b/tiny-cash/Cargo.toml index 29794ab..f91e0e4 100644 --- a/tiny-cash/Cargo.toml +++ b/tiny-cash/Cargo.toml @@ -16,6 +16,8 @@ zebra-consensus = { workspace = true, default-features = false, features = [] } zebra-state = { workspace = true, default-features = false, features = ["proptest-impl"] } zebra-test = { workspace = true, default-features = false } zebra-chain = { workspace = true, default-features = false, features = ["proptest-impl"] } +zebra-script = { workspace = true } + lazy_static = "1.4.0" orchard = "0.8.0" zcash_note_encryption = "0.4.0" diff --git a/tiny-cash/src/write.rs b/tiny-cash/src/write.rs index 935292a..04b4900 100644 --- a/tiny-cash/src/write.rs +++ b/tiny-cash/src/write.rs @@ -1,6 +1,7 @@ use chrono::{DateTime, Utc}; use futures_util::future::FutureExt; use std::{ + collections::HashMap, future::Future, pin::Pin, sync::Arc, @@ -12,7 +13,8 @@ use zebra_chain::{ amount::{Amount, NonNegative}, block::{Block, Header, Height}, fmt::HexDebug, - parameters::Network, + parameters::{Network, NetworkUpgrade}, + transaction::HashType, work::{difficulty::CompactDifficulty, equihash::Solution}, }; use zebra_chain::{block, serialization::ZcashDeserialize}; @@ -21,7 +23,9 @@ use zebra_chain::{ transaction::{Memo, Transaction}, transparent::Script, }; +use zebra_consensus::script; use zebra_consensus::transaction as tx; +use zebra_consensus::transaction::Verifier as TxVerifier; use crate::extract_burn_info; @@ -201,6 +205,70 @@ where } } +impl TinyCashWriteService +where + S: Service< + zebra_state::Request, + Response = zebra_state::Response, + Error = zebra_state::BoxError, + > + + Send + + Clone + + 'static + + Clone, + S::Future: Send + 'static, +{ + async fn verify_transaction( + tx: Arc, + height: Height, + all_previous_outputs: &[transparent::Output], + ) -> Result<(), BoxError> { + let async_checks = match tx.as_ref() { + Transaction::V1 { .. } + | Transaction::V2 { .. } + | Transaction::V3 { .. } + | Transaction::V4 { .. } => { + panic!("Unsupported transaction version"); + } + Transaction::V5 { + sapling_shielded_data, + orchard_shielded_data, + .. + } => { + if sapling_shielded_data.is_some() { + panic!("Sapling shielded data is not supported"); + } + let shielded_sighash = tx.sighash( + NetworkUpgrade::Nu5, + HashType::ALL, + all_previous_outputs, + None, + ); + TxVerifier::::verify_transparent_inputs_and_outputs( + &tx::Request::Block { + transaction: tx.clone(), + known_utxos: HashMap::new().into(), + height, + time: DateTime::::default(), + }, + Network::Mainnet, + script::Verifier, // TODO: Maybe try and reuse this + zebra_script::CachedFfiTransaction::new( + tx.clone(), + all_previous_outputs.to_vec(), + ) + .into(), + )? + .and(TxVerifier::::verify_orchard_shielded_data( + &orchard_shielded_data, + &shielded_sighash, + )?) + } + }; + async_checks.check().await + } +} + fn build_mint_block( height: Height, previous_block_hash: block::Hash, diff --git a/tower-cartesi/examples/echo.rs b/tower-cartesi/examples/echo.rs index dcb2eb1..4d08020 100644 --- a/tower-cartesi/examples/echo.rs +++ b/tower-cartesi/examples/echo.rs @@ -1,9 +1,9 @@ +use futures_util::FutureExt; use std::env; use std::error::Error; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -use futures_util::FutureExt; use tower_cartesi::{listen_http, Request, Response}; use tower_service::{BoxError, Service}; From d6e40aaf55fedf33240c20e61aa44dbb56e7cbc5 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Wed, 10 Apr 2024 19:57:52 +1000 Subject: [PATCH 04/10] implement tx verification without service --- src/main.rs | 6 +-- tiny-cash/src/write.rs | 83 ++++++++++++++++++++++++------------------ 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/main.rs b/src/main.rs index d05fe01..77ab3d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,7 +64,7 @@ async fn main() -> Result<(), anyhow::Error> { ); let state_service = Buffer::new(state_service, 30); - let mut cartezcash_app = CarteZcashApp::new(network, state_service).await; + let mut cartezcash_app = CarteZcashApp::new(state_service).await; #[cfg(feature = "lightwalletd")] { @@ -93,13 +93,11 @@ struct CarteZcashApp { } impl CarteZcashApp { - pub async fn new(network: Network, state_service: StateService) -> Self { + pub async fn new(state_service: StateService) -> Self { // set up the services needed to run the rollup - let verifier_service = tx::Verifier::new(network, state_service.clone()); let mut tinycash = Buffer::new( BoxService::new(tiny_cash::write::TinyCashWriteService::new( state_service, - verifier_service, )), 10, ); diff --git a/tiny-cash/src/write.rs b/tiny-cash/src/write.rs index 04b4900..cdf5193 100644 --- a/tiny-cash/src/write.rs +++ b/tiny-cash/src/write.rs @@ -1,7 +1,7 @@ use chrono::{DateTime, Utc}; use futures_util::future::FutureExt; use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, future::Future, pin::Pin, sync::Arc, @@ -15,6 +15,7 @@ use zebra_chain::{ fmt::HexDebug, parameters::{Network, NetworkUpgrade}, transaction::HashType, + transparent::{OrderedUtxo, OutPoint, Utxo}, work::{difficulty::CompactDifficulty, equihash::Solution}, }; use zebra_chain::{block, serialization::ZcashDeserialize}; @@ -31,22 +32,26 @@ use crate::extract_burn_info; pub type BoxError = Box; -pub struct TinyCashWriteService { +pub struct TinyCashWriteService { state_service: S, - tx_verifier_service: V, tip_height: Option, tip_hash: Option, + + // Set of all unspent transparent outputs. + // This is expected to grow but very slowly since transparent outputs are only created + // by deposits and destroyed by subsequent spends. + utxos_set: HashMap, } -impl TinyCashWriteService { - pub fn new(state_service: S, tx_verifier_service: V) -> Self { +impl TinyCashWriteService { + pub fn new(state_service: S) -> Self { Self { state_service, - tx_verifier_service, tip_height: None, tip_hash: None, + utxos_set: HashMap::new(), } } } @@ -72,7 +77,7 @@ pub struct Response { pub burns: Vec<(Amount, Memo)>, } -impl tower::Service for TinyCashWriteService +impl tower::Service for TinyCashWriteService where S: Service< zebra_state::Request, @@ -84,16 +89,6 @@ where + 'static + Clone, S::Future: Send + 'static, - V: Service< - tx::Request, - Response = tx::Response, - Error = zebra_consensus::error::TransactionError, - > - + Send - + Clone - + 'static - + Clone, - V::Future: Send + 'static, { type Response = Response; type Error = BoxError; @@ -106,7 +101,6 @@ where fn call(&mut self, req: Request) -> Self::Future { let mut state_service = self.state_service.clone(); - let mut tx_verifier_service = self.tx_verifier_service.clone(); let tip_height = self.tip_height; let previous_block_hash = self.tip_hash.unwrap_or(Default::default()); @@ -136,35 +130,37 @@ where let ret = block.clone(); let block_hash = block.hash(); + + self.tip_hash = Some(block_hash); + self.tip_height = Some(height); + let outputs_spent = if let Some(tx) = tx_to_verify.as_ref() { + self.outputs_spent_by_transaction(tx.clone().into()) + } else { + Vec::new() + }; + + // build the set of new UTXOs this block creates and add to the global state let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|t| t.hash()).collect(); let known_utxos = Arc::new(transparent::new_ordered_outputs( &block, &transaction_hashes, )); - - self.tip_hash = Some(block_hash); - self.tip_height = Some(height); + let new_outputs = Arc::into_inner(known_utxos) + .expect("all verification tasks using known_utxos are complete"); + tracing::info!("Adding new UTXOs to the set: {:?}", new_outputs); + self.utxos_set + .extend(new_outputs.iter().map(|(k, v)| (k.clone(), v.clone()))); async move { if height > Height(0) { // verify the transaction if required if let Some(tx) = tx_to_verify { - tx_verifier_service - .ready() - .await - .expect("transaction verifier is always ready") - .call(tx::Request::Block { - transaction: tx.into(), - known_utxos: std::default::Default::default(), - height, - time: DateTime::::default(), - }) - .await?; + tracing::info!("Verifying transaction"); + Self::verify_transaction(tx.into(), height, outputs_spent.as_slice()).await?; + tracing::info!("Transaction passed!"); } // contextually verify and commit the block - let new_outputs = Arc::into_inner(known_utxos) - .expect("all verification tasks using known_utxos are complete"); let prepared_block = zebra_state::SemanticallyVerifiedBlock { block: block.into(), hash: block_hash, @@ -205,7 +201,24 @@ where } } -impl TinyCashWriteService +impl TinyCashWriteService { + pub fn outputs_spent_by_transaction(&self, tx: Arc) -> Vec { + tx.inputs() + .iter() + .filter_map(|input| { + if let Some(outpoint) = input.outpoint() { + self.utxos_set + .get(&outpoint) + .map(|utxo| utxo.as_ref().output.clone()) + } else { + None + } + }) + .collect() + } +} + +impl TinyCashWriteService where S: Service< zebra_state::Request, From a5243c28a0029aa04b78a42fbf6b26cc3e3c05a1 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Wed, 10 Apr 2024 19:59:04 +1000 Subject: [PATCH 05/10] fmt --- src/main.rs | 4 +--- tiny-cash/src/write.rs | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 77ab3d6..98b82e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -96,9 +96,7 @@ impl CarteZcashApp { pub async fn new(state_service: StateService) -> Self { // set up the services needed to run the rollup let mut tinycash = Buffer::new( - BoxService::new(tiny_cash::write::TinyCashWriteService::new( - state_service, - )), + BoxService::new(tiny_cash::write::TinyCashWriteService::new(state_service)), 10, ); diff --git a/tiny-cash/src/write.rs b/tiny-cash/src/write.rs index cdf5193..490dd97 100644 --- a/tiny-cash/src/write.rs +++ b/tiny-cash/src/write.rs @@ -1,7 +1,7 @@ use chrono::{DateTime, Utc}; use futures_util::future::FutureExt; use std::{ - collections::{HashMap, HashSet}, + collections::HashMap, future::Future, pin::Pin, sync::Arc, @@ -9,17 +9,17 @@ use std::{ }; use tower::{Service, ServiceExt}; +use zebra_chain::transparent; use zebra_chain::{ amount::{Amount, NonNegative}, block::{Block, Header, Height}, fmt::HexDebug, parameters::{Network, NetworkUpgrade}, transaction::HashType, - transparent::{OrderedUtxo, OutPoint, Utxo}, + transparent::{OrderedUtxo, OutPoint}, work::{difficulty::CompactDifficulty, equihash::Solution}, }; use zebra_chain::{block, serialization::ZcashDeserialize}; -use zebra_chain::{transaction::UnminedTxId, transparent}; use zebra_chain::{ transaction::{Memo, Transaction}, transparent::Script, From 62aadce7dc3ee9679b701bddd499844f67beae7c Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Wed, 10 Apr 2024 19:59:18 +1000 Subject: [PATCH 06/10] clippy fix --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 98b82e1..4d2c2d1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use tower_cartesi::{listen_http, Request as RollAppRequest, Response}; use futures_util::future::FutureExt; use zebra_chain::{block, parameters::Network}; -use zebra_consensus::transaction as tx; + #[cfg(feature = "lightwalletd")] use cartezcash_lightwalletd::{ @@ -56,7 +56,7 @@ async fn main() -> Result<(), anyhow::Error> { // tiny_cash::initialize_halo2(); // tracing::info!("Initializing Halo2 verifier key complete"); - let (state_service, state_read_service, _, _) = zebra_state::init( + let (state_service, _state_read_service, _, _) = zebra_state::init( zebra_state::Config::ephemeral(), network, block::Height::MAX, From 6f7f87cb22cd761c97aeb9d96a6e44a1c071a8f9 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Wed, 10 Apr 2024 20:40:06 +1000 Subject: [PATCH 07/10] fix tests --- tiny-cash/Cargo.toml | 1 + tiny-cash/src/test.rs | 10 +++------- tiny-cash/src/write.rs | 33 +++++++++++++++++++++++---------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/tiny-cash/Cargo.toml b/tiny-cash/Cargo.toml index f91e0e4..1cfec29 100644 --- a/tiny-cash/Cargo.toml +++ b/tiny-cash/Cargo.toml @@ -22,6 +22,7 @@ lazy_static = "1.4.0" orchard = "0.8.0" zcash_note_encryption = "0.4.0" base58check = "0.1.0" +incrementalmerkletree = "0.5.1" [dev-dependencies] diff --git a/tiny-cash/src/test.rs b/tiny-cash/src/test.rs index fc33fc8..9d0cf07 100644 --- a/tiny-cash/src/test.rs +++ b/tiny-cash/src/test.rs @@ -16,7 +16,6 @@ use zebra_chain::{ block, block::Height, }; -use zebra_consensus::transaction as tx; // anything sent to this script can be spent by anyway. Useful for testing fn accepting() -> Script { @@ -35,9 +34,8 @@ async fn test_genesis() { 0, ); let state_service = Buffer::new(state_service, 1); - let verifier_service = tx::Verifier::new(network, state_service.clone()); - let mut tinycash = BoxService::new(TinyCashWriteService::new(state_service, verifier_service)); + let mut tinycash = BoxService::new(TinyCashWriteService::new(state_service)); tinycash .call(Request::Genesis) @@ -57,9 +55,8 @@ async fn test_mint_txns_update_balance() { 0, ); let state_service = Buffer::new(state_service, 10); - let verifier_service = tx::Verifier::new(network, state_service.clone()); - let mut tinycash = BoxService::new(TinyCashWriteService::new(state_service, verifier_service)); + let mut tinycash = BoxService::new(TinyCashWriteService::new(state_service)); tinycash .ready() @@ -134,9 +131,8 @@ async fn test_include_transparent_transaction() { ); let state_service = Buffer::new(state_service, 10); - let verifier_service = tx::Verifier::new(network, state_service.clone()); - let mut tinycash = BoxService::new(TinyCashWriteService::new(state_service, verifier_service)); + let mut tinycash = BoxService::new(TinyCashWriteService::new(state_service)); tinycash .ready() diff --git a/tiny-cash/src/write.rs b/tiny-cash/src/write.rs index 490dd97..6a41d36 100644 --- a/tiny-cash/src/write.rs +++ b/tiny-cash/src/write.rs @@ -1,13 +1,13 @@ use chrono::{DateTime, Utc}; use futures_util::future::FutureExt; use std::{ - collections::HashMap, + collections::{HashMap, HashSet, VecDeque}, future::Future, pin::Pin, sync::Arc, task::{Context, Poll}, }; -use tower::{Service, ServiceExt}; +use tower::{BoxError, Service, ServiceExt}; use zebra_chain::transparent; use zebra_chain::{ @@ -27,14 +27,17 @@ use zebra_chain::{ use zebra_consensus::script; use zebra_consensus::transaction as tx; use zebra_consensus::transaction::Verifier as TxVerifier; +use incrementalmerkletree::frontier::Frontier; use crate::extract_burn_info; -pub type BoxError = Box; +pub type OrchardFrontier = + Frontier; pub struct TinyCashWriteService { state_service: S, + // Current tip of the chain tip_height: Option, tip_hash: Option, @@ -42,6 +45,21 @@ pub struct TinyCashWriteService { // This is expected to grow but very slowly since transparent outputs are only created // by deposits and destroyed by subsequent spends. utxos_set: HashMap, + + // The frontier of the commitment tree. This is the Merkle path of the last added commitment + // The frontier is all that is needed to update the root and produce a new frontier when new commitments are added + // So for a fixed depth this is constant size! + // commitment_tree_frontier: OrchardFrontier, + + // A FILO queue of orchard commitment tree roots. + // Only a fixed window are kept so we can have constant state. + // This means older witnesses become invalid after a period of time + // commitment_tree_roots: VecDeque, + + // A set of all nullifiers that have been seen. This prevents double spends. + // sadly this is not fixed size but it could be replaced with a Sparse Merkle Tree + // in the future. This would require updates to wallets though + // nullifier_set: HashSet, } impl TinyCashWriteService { @@ -237,12 +255,6 @@ where all_previous_outputs: &[transparent::Output], ) -> Result<(), BoxError> { let async_checks = match tx.as_ref() { - Transaction::V1 { .. } - | Transaction::V2 { .. } - | Transaction::V3 { .. } - | Transaction::V4 { .. } => { - panic!("Unsupported transaction version"); - } Transaction::V5 { sapling_shielded_data, orchard_shielded_data, @@ -276,7 +288,8 @@ where &orchard_shielded_data, &shielded_sighash, )?) - } + }, + _ => panic!("Only V5 transactions are supported"), }; async_checks.check().await } From 3df0896fd2d87f027a3f357f5cfe5b0641d56ed3 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Wed, 10 Apr 2024 20:40:17 +1000 Subject: [PATCH 08/10] update zebra refs --- Cargo.lock | 9 +++++++++ Cargo.toml | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 086387d..9f69eee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4263,6 +4263,7 @@ dependencies = [ "chrono", "futures-util", "hex", + "incrementalmerkletree", "lazy_static", "orchard 0.8.0", "tokio", @@ -4464,6 +4465,7 @@ dependencies = [ [[package]] name = "tower-batch-control" version = "0.2.41-beta.11" +source = "git+https://github.com/willemolding/zebra?rev=04615946f3bae4e39925edae8ed1e150b7cf531a#04615946f3bae4e39925edae8ed1e150b7cf531a" dependencies = [ "futures", "futures-core", @@ -4497,6 +4499,7 @@ dependencies = [ [[package]] name = "tower-fallback" version = "0.2.41-beta.11" +source = "git+https://github.com/willemolding/zebra?rev=04615946f3bae4e39925edae8ed1e150b7cf531a#04615946f3bae4e39925edae8ed1e150b7cf531a" dependencies = [ "futures-core", "pin-project", @@ -5409,6 +5412,7 @@ dependencies = [ [[package]] name = "zebra-chain" version = "1.0.0-beta.35" +source = "git+https://github.com/willemolding/zebra?rev=04615946f3bae4e39925edae8ed1e150b7cf531a#04615946f3bae4e39925edae8ed1e150b7cf531a" dependencies = [ "bitflags 2.5.0", "bitflags-serde-legacy", @@ -5466,6 +5470,7 @@ dependencies = [ [[package]] name = "zebra-consensus" version = "1.0.0-beta.35" +source = "git+https://github.com/willemolding/zebra?rev=04615946f3bae4e39925edae8ed1e150b7cf531a#04615946f3bae4e39925edae8ed1e150b7cf531a" dependencies = [ "bellman", "blake2b_simd", @@ -5501,6 +5506,7 @@ dependencies = [ [[package]] name = "zebra-node-services" version = "1.0.0-beta.35" +source = "git+https://github.com/willemolding/zebra?rev=04615946f3bae4e39925edae8ed1e150b7cf531a#04615946f3bae4e39925edae8ed1e150b7cf531a" dependencies = [ "zebra-chain", ] @@ -5508,6 +5514,7 @@ dependencies = [ [[package]] name = "zebra-script" version = "1.0.0-beta.35" +source = "git+https://github.com/willemolding/zebra?rev=04615946f3bae4e39925edae8ed1e150b7cf531a#04615946f3bae4e39925edae8ed1e150b7cf531a" dependencies = [ "displaydoc", "thiserror", @@ -5518,6 +5525,7 @@ dependencies = [ [[package]] name = "zebra-state" version = "1.0.0-beta.35" +source = "git+https://github.com/willemolding/zebra?rev=04615946f3bae4e39925edae8ed1e150b7cf531a#04615946f3bae4e39925edae8ed1e150b7cf531a" dependencies = [ "bincode", "chrono", @@ -5551,6 +5559,7 @@ dependencies = [ [[package]] name = "zebra-test" version = "1.0.0-beta.35" +source = "git+https://github.com/willemolding/zebra?rev=04615946f3bae4e39925edae8ed1e150b7cf531a#04615946f3bae4e39925edae8ed1e150b7cf531a" dependencies = [ "color-eyre", "futures", diff --git a/Cargo.toml b/Cargo.toml index 42e8c0f..a5c6e5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,18 +48,18 @@ members = [ "cartezcash-lightwalletd", "tiny-cash", "tower-cartesi"] [workspace.dependencies] # patched zebra crates to support TinyCash network and expose things we need -# zebra-consensus = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } -# zebra-state = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } -# zebra-test = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } -# zebra-chain = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } -# zebra-script = { git = "https://github.com/willemolding/zebra", rev = "185e404bed938e3aa916aee1ae6999a70efceddb", default-features = false } +zebra-consensus = { git = "https://github.com/willemolding/zebra", rev = "04615946f3bae4e39925edae8ed1e150b7cf531a", default-features = false } +zebra-state = { git = "https://github.com/willemolding/zebra", rev = "04615946f3bae4e39925edae8ed1e150b7cf531a", default-features = false } +zebra-test = { git = "https://github.com/willemolding/zebra", rev = "04615946f3bae4e39925edae8ed1e150b7cf531a", default-features = false } +zebra-chain = { git = "https://github.com/willemolding/zebra", rev = "04615946f3bae4e39925edae8ed1e150b7cf531a", default-features = false } +zebra-script = { git = "https://github.com/willemolding/zebra", rev = "04615946f3bae4e39925edae8ed1e150b7cf531a", default-features = false } # uncomment for local dev when modifying zebra -zebra-consensus = { path = "../zebra/zebra-consensus", default-features = false } -zebra-state = { path = "../zebra/zebra-state", default-features = false } -zebra-test = { path = "../zebra/zebra-test", default-features = false } -zebra-chain = { path = "../zebra/zebra-chain", default-features = false } -zebra-script = { path = "../zebra/zebra-script", default-features = false } +# zebra-consensus = { path = "../zebra/zebra-consensus", default-features = false } +# zebra-state = { path = "../zebra/zebra-state", default-features = false } +# zebra-test = { path = "../zebra/zebra-test", default-features = false } +# zebra-chain = { path = "../zebra/zebra-chain", default-features = false } +# zebra-script = { path = "../zebra/zebra-script", default-features = false } [patch.crates-io] From 717ca08401307bc8f60f7e9e275e0fad4e4d27f6 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Wed, 10 Apr 2024 21:31:58 +1000 Subject: [PATCH 09/10] add nullifier set logic --- src/main.rs | 4 ++-- tiny-cash/src/write.rs | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4d2c2d1..0d129d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,7 +56,7 @@ async fn main() -> Result<(), anyhow::Error> { // tiny_cash::initialize_halo2(); // tracing::info!("Initializing Halo2 verifier key complete"); - let (state_service, _state_read_service, _, _) = zebra_state::init( + let (state_service, state_read_service, _, _) = zebra_state::init( zebra_state::Config::ephemeral(), network, block::Height::MAX, @@ -151,7 +151,7 @@ where S: Service< tiny_cash::write::Request, Response = tiny_cash::write::Response, - Error = tiny_cash::write::BoxError, + Error = BoxError, > + Send + Clone + 'static, diff --git a/tiny-cash/src/write.rs b/tiny-cash/src/write.rs index 6a41d36..c09be08 100644 --- a/tiny-cash/src/write.rs +++ b/tiny-cash/src/write.rs @@ -1,5 +1,6 @@ use chrono::{DateTime, Utc}; use futures_util::future::FutureExt; +use zebra_state::DuplicateNullifierError; use std::{ collections::{HashMap, HashSet, VecDeque}, future::Future, @@ -59,7 +60,7 @@ pub struct TinyCashWriteService { // A set of all nullifiers that have been seen. This prevents double spends. // sadly this is not fixed size but it could be replaced with a Sparse Merkle Tree // in the future. This would require updates to wallets though - // nullifier_set: HashSet, + nullifier_set: HashSet, } impl TinyCashWriteService { @@ -70,6 +71,7 @@ impl TinyCashWriteService { tip_height: None, tip_hash: None, utxos_set: HashMap::new(), + nullifier_set: HashSet::new(), } } } @@ -169,6 +171,15 @@ where self.utxos_set .extend(new_outputs.iter().map(|(k, v)| (k.clone(), v.clone()))); + + // check this block doesn't reuse a known nullifier then add the block nullifiers to the state + for nullifier in block.clone().orchard_nullifiers() { + if self.nullifier_set.contains(nullifier) { + panic!("duplicate nullifier detected"); + } + } + self.nullifier_set.extend(block.orchard_nullifiers().cloned()); + async move { if height > Height(0) { // verify the transaction if required @@ -352,6 +363,8 @@ fn mint_coinbase_txn( ) } +// TODO: If this can avoid adding a spend this will slow state growth +// currently having it empty makes the block verifier freak out though fn empty_coinbase_txn(height: Height) -> Transaction { mint_coinbase_txn(Amount::zero(), &Script::new(&[0x0; 32]), height) } From fbbaa52a9af19c079ace14d60d328fa6b2e8d66e Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Wed, 10 Apr 2024 21:56:00 +1000 Subject: [PATCH 10/10] tidy up tracing outputs --- cartezcash-lightwalletd/src/service_impl.rs | 52 ++++++++++----------- src/main.rs | 8 +--- tiny-cash/src/write.rs | 37 +++++++++++---- 3 files changed, 57 insertions(+), 40 deletions(-) diff --git a/cartezcash-lightwalletd/src/service_impl.rs b/cartezcash-lightwalletd/src/service_impl.rs index 6755acb..6c6bb4f 100644 --- a/cartezcash-lightwalletd/src/service_impl.rs +++ b/cartezcash-lightwalletd/src/service_impl.rs @@ -55,7 +55,7 @@ where &self, request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("send_transaction called. Fowarding to InputBox contract"); + tracing::debug!("send_transaction called. Fowarding to InputBox contract"); let provider = Provider::::try_from("http://127.0.0.1:8545").unwrap(); let wallet: LocalWallet = @@ -91,7 +91,7 @@ where &self, _request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_latest_block called"); + tracing::debug!("get_latest_block called"); let res: zebra_state::ReadResponse = self .state_read_service @@ -104,13 +104,13 @@ where .unwrap(); if let ReadResponse::Tip(Some((height, hash))) = res { - tracing::info!("returning tip: {:?}", res); + tracing::debug!("returning tip: {:?}", res); Ok(tonic::Response::new(BlockId { hash: hash.0.to_vec(), height: height.0 as u64, })) } else { - tracing::info!("unexpected response"); + tracing::debug!("unexpected response"); Err(tonic::Status::not_found( "Could not find the latest block in the state store", )) @@ -122,7 +122,7 @@ where &self, request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_block_range called with: {:?} ", request); + tracing::debug!("get_block_range called with: {:?} ", request); let (tx, rx) = mpsc::channel(10); // these sometimes come in reverse order... @@ -141,7 +141,7 @@ where let mut state_read_service = self.state_read_service.clone(); for height in range { - tracing::info!("fetching block at height: {}", height); + tracing::debug!("fetching block at height: {}", height); let res: zebra_state::ReadResponse = state_read_service .ready() .await @@ -155,7 +155,7 @@ where let block = match res { ReadResponse::Block(Some(block)) => block, _ => { - tracing::info!("unexpected response"); + tracing::debug!("unexpected response"); return Err(tonic::Status::not_found( "Could not find the block in the state store", )); @@ -220,7 +220,7 @@ where &self, request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_transaction called"); + tracing::debug!("get_transaction called"); let mut state_read_service = self.state_read_service.clone(); let request = zebra_state::ReadRequest::Transaction( @@ -239,7 +239,7 @@ where height: transaction.height.0 as u64, })) } else { - tracing::info!("unexpected response"); + tracing::debug!("unexpected response"); Err(tonic::Status::not_found( "Could not find the transaction in the state store", )) @@ -251,7 +251,7 @@ where &self, request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_taddress_txids called with {:?}", request); + tracing::debug!("get_taddress_txids called with {:?}", request); let request = request.into_inner(); let address = transparent::Address::from_str(&request.address).unwrap(); @@ -277,7 +277,7 @@ where if let ReadResponse::AddressesTransactionIds(txns) = res { let (tx, rx) = mpsc::channel(10); - tracing::info!("{:?} transactions found", txns.len()); + tracing::debug!("{:?} transactions found", txns.len()); for (_location, tx_id) in txns.iter() { tracing::debug!("got txid: {:?}", tx_id); @@ -317,7 +317,7 @@ where &self, request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_tree_state called"); + tracing::debug!("get_tree_state called"); let mut read_service = self.state_read_service.clone(); let height: Height = Height(request.into_inner().height.try_into().unwrap()); @@ -370,7 +370,7 @@ where hash: hash.to_string(), time: block.header.time.timestamp() as u32, }; - tracing::info!("returning tree state: {:?}", tree_state); + tracing::debug!("returning tree state: {:?}", tree_state); Ok(tonic::Response::new(tree_state)) } @@ -379,7 +379,7 @@ where &self, _request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_lightd_info called"); + tracing::debug!("get_lightd_info called"); let block_height = 0; // TODO: fetch this from the store @@ -415,7 +415,7 @@ where &self, _request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_mempool_stream called"); + tracing::debug!("get_mempool_stream called"); // let (tx, rx) = mpsc::channel(4); // TODO: Send the txiods into the tx end of the channel Err(tonic::Status::unimplemented( @@ -432,7 +432,7 @@ where tonic::Response, tonic::Status, > { - tracing::info!("get_block called. Ignoring request"); + tracing::debug!("get_block called. Ignoring request"); Err(tonic::Status::unimplemented( "gRPC endpoint not supported for cartezcash", )) @@ -446,7 +446,7 @@ where tonic::Response, tonic::Status, > { - tracing::info!("get_block_nullifiers called. Ignoring request"); + tracing::debug!("get_block_nullifiers called. Ignoring request"); Err(tonic::Status::unimplemented( "gRPC endpoint not supported for cartezcash", )) @@ -461,7 +461,7 @@ where _request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_block_range_nullifiers called. Ignoring request"); + tracing::debug!("get_block_range_nullifiers called. Ignoring request"); Err(tonic::Status::unimplemented( "gRPC endpoint not supported for cartezcash", )) @@ -471,7 +471,7 @@ where &self, _request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_taddress_balance called. Ignoring request"); + tracing::debug!("get_taddress_balance called. Ignoring request"); Err(tonic::Status::unimplemented( "gRPC endpoint not supported for cartezcash", )) @@ -481,7 +481,7 @@ where &self, _request: tonic::Request>, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_taddress_balance_stream called. Ignoring request"); + tracing::debug!("get_taddress_balance_stream called. Ignoring request"); Err(tonic::Status::unimplemented( "gRPC endpoint not supported for cartezcash", )) @@ -503,7 +503,7 @@ where &self, _request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_mempool_tx called. Ignoring request"); + tracing::debug!("get_mempool_tx called. Ignoring request"); Err(tonic::Status::unimplemented( "gRPC endpoint not supported for cartezcash", )) @@ -513,7 +513,7 @@ where &self, _request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_latest_tree_state called. Ignoring request"); + tracing::debug!("get_latest_tree_state called. Ignoring request"); Err(tonic::Status::unimplemented( "gRPC endpoint not supported for cartezcash", )) @@ -527,7 +527,7 @@ where &self, _request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_subtree_roots called. Ignoring request"); + tracing::debug!("get_subtree_roots called. Ignoring request"); Err(tonic::Status::unimplemented( "gRPC endpoint not supported for cartezcash", )) @@ -536,7 +536,7 @@ where &self, _request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_address_utxos called. Ignoring request"); + tracing::debug!("get_address_utxos called. Ignoring request"); Err(tonic::Status::unimplemented( "gRPC endpoint not supported for cartezcash", )) @@ -549,7 +549,7 @@ where _request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("get_address_utxos_stream called. Ignoring request"); + tracing::debug!("get_address_utxos_stream called. Ignoring request"); Err(tonic::Status::unimplemented( "gRPC endpoint not supported for cartezcash", )) @@ -560,7 +560,7 @@ where &self, _request: tonic::Request, ) -> std::result::Result, tonic::Status> { - tracing::info!("ping called. Ignoring request"); + tracing::debug!("ping called. Ignoring request"); Err(tonic::Status::unimplemented( "gRPC endpoint not supported for cartezcash", )) diff --git a/src/main.rs b/src/main.rs index 0d129d3..83a740c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,6 @@ use futures_util::future::FutureExt; use zebra_chain::{block, parameters::Network}; - #[cfg(feature = "lightwalletd")] use cartezcash_lightwalletd::{ proto::service::compact_tx_streamer_server::CompactTxStreamerServer, @@ -148,11 +147,8 @@ impl Service for CarteZcashApp { async fn initialize_network(tinycash: &mut S) -> Result<(), BoxError> where - S: Service< - tiny_cash::write::Request, - Response = tiny_cash::write::Response, - Error = BoxError, - > + Send + S: Service + + Send + Clone + 'static, S::Future: Send + 'static, diff --git a/tiny-cash/src/write.rs b/tiny-cash/src/write.rs index c09be08..b1d511a 100644 --- a/tiny-cash/src/write.rs +++ b/tiny-cash/src/write.rs @@ -1,6 +1,5 @@ use chrono::{DateTime, Utc}; use futures_util::future::FutureExt; -use zebra_state::DuplicateNullifierError; use std::{ collections::{HashMap, HashSet, VecDeque}, future::Future, @@ -10,6 +9,8 @@ use std::{ }; use tower::{BoxError, Service, ServiceExt}; +use incrementalmerkletree::frontier::Frontier; +use zebra_chain::orchard::tree::NoteCommitmentTree; use zebra_chain::transparent; use zebra_chain::{ amount::{Amount, NonNegative}, @@ -28,7 +29,6 @@ use zebra_chain::{ use zebra_consensus::script; use zebra_consensus::transaction as tx; use zebra_consensus::transaction::Verifier as TxVerifier; -use incrementalmerkletree::frontier::Frontier; use crate::extract_burn_info; @@ -48,14 +48,15 @@ pub struct TinyCashWriteService { utxos_set: HashMap, // The frontier of the commitment tree. This is the Merkle path of the last added commitment - // The frontier is all that is needed to update the root and produce a new frontier when new commitments are added + // Internally this just stores the frontier which is the Merkle path of the most recently added note + // This is all that is needed to update the root and produce a new frontier when new commitments are added // So for a fixed depth this is constant size! - // commitment_tree_frontier: OrchardFrontier, + commitment_tree_frontier: NoteCommitmentTree, // A FILO queue of orchard commitment tree roots. // Only a fixed window are kept so we can have constant state. // This means older witnesses become invalid after a period of time - // commitment_tree_roots: VecDeque, + historical_tree_roots: VecDeque, // A set of all nullifiers that have been seen. This prevents double spends. // sadly this is not fixed size but it could be replaced with a Sparse Merkle Tree @@ -70,6 +71,8 @@ impl TinyCashWriteService { tip_height: None, tip_hash: None, + commitment_tree_frontier: NoteCommitmentTree::default(), + historical_tree_roots: VecDeque::new(), utxos_set: HashMap::new(), nullifier_set: HashSet::new(), } @@ -171,14 +174,32 @@ where self.utxos_set .extend(new_outputs.iter().map(|(k, v)| (k.clone(), v.clone()))); - // check this block doesn't reuse a known nullifier then add the block nullifiers to the state for nullifier in block.clone().orchard_nullifiers() { if self.nullifier_set.contains(nullifier) { panic!("duplicate nullifier detected"); } } - self.nullifier_set.extend(block.orchard_nullifiers().cloned()); + self.nullifier_set + .extend(block.orchard_nullifiers().cloned()); + + // update the commitment tree frontier and add the new root to the queue + for commitment in block.orchard_note_commitments() { + self.commitment_tree_frontier + .append(*commitment) + .expect("failed to append to tree"); + } + self.historical_tree_roots + .push_back(self.commitment_tree_frontier.root()); + + // ensure the anchors/tree-roots referenced by the transaction are in the state + if let Some(tx) = tx_to_verify.as_ref() { + if let Some(data) = tx.orchard_shielded_data() { + if !self.historical_tree_roots.contains(&data.shared_anchor) { + panic!("Tree root not found in state. Witness may be invalid or too old"); + } + } + } async move { if height > Height(0) { @@ -299,7 +320,7 @@ where &orchard_shielded_data, &shielded_sighash, )?) - }, + } _ => panic!("Only V5 transactions are supported"), }; async_checks.check().await