├── test_binaries └── bins │ ├── .gitinclude │ └── README.md ├── darkside-tests ├── lightwalletd_bin │ └── .gitinclude ├── src │ ├── lib.rs │ └── darkside_connector.rs ├── proptest-regressions │ ├── chain_generic_tests.txt │ └── chain_generics.txt ├── build.rs ├── tests │ └── data │ │ └── wallets │ │ └── advanced_reorg_tests │ │ └── tree_states │ │ ├── 210.json │ │ ├── 215.json │ │ ├── 216.json │ │ ├── 201.json │ │ ├── 202.json │ │ ├── 203.json │ │ ├── 204.json │ │ ├── 206.json │ │ ├── 207.json │ │ ├── 208.json │ │ ├── 220.json │ │ ├── 222.json │ │ ├── 209.json │ │ ├── 211.json │ │ ├── 213.json │ │ ├── 217.json │ │ ├── 218.json │ │ ├── 219.json │ │ ├── 221.json │ │ ├── 205.json │ │ ├── 212.json │ │ └── 214.json └── Cargo.toml ├── libtonode-tests ├── tests │ ├── times │ │ └── .gitinclude │ ├── shield_transparent.rs │ └── scripts │ │ └── start_darksidewalletd.sh ├── src │ ├── lib.rs │ └── chain_generics.rs └── Cargo.toml ├── zingolib ├── src │ ├── testutils │ │ ├── regtest │ │ │ ├── bin │ │ │ │ ├── .gitkeep │ │ │ │ └── .gitignore │ │ │ ├── logs │ │ │ │ ├── .gitkeep │ │ │ │ └── .gitignore │ │ │ ├── data │ │ │ │ ├── zcashd │ │ │ │ │ └── .gitkeep │ │ │ │ ├── lightwalletd │ │ │ │ │ └── .gitkeep │ │ │ │ ├── regtestvectors │ │ │ │ │ └── regtest │ │ │ │ │ │ ├── .lock │ │ │ │ │ │ ├── db.log │ │ │ │ │ │ ├── blocks │ │ │ │ │ │ ├── index │ │ │ │ │ │ │ ├── LOCK │ │ │ │ │ │ │ ├── CURRENT │ │ │ │ │ │ │ ├── 000005.ldb │ │ │ │ │ │ │ ├── 000008.ldb │ │ │ │ │ │ │ ├── 000009.log │ │ │ │ │ │ │ ├── MANIFEST-000007 │ │ │ │ │ │ │ ├── LOG │ │ │ │ │ │ │ └── LOG.old │ │ │ │ │ │ ├── blk00000.dat │ │ │ │ │ │ └── rev00000.dat │ │ │ │ │ │ ├── chainstate │ │ │ │ │ │ ├── LOCK │ │ │ │ │ │ ├── CURRENT │ │ │ │ │ │ ├── 000005.ldb │ │ │ │ │ │ ├── 000008.ldb │ │ │ │ │ │ ├── 000009.log │ │ │ │ │ │ ├── MANIFEST-000007 │ │ │ │ │ │ ├── LOG │ │ │ │ │ │ └── LOG.old │ │ │ │ │ │ ├── peers.dat │ │ │ │ │ │ ├── wallet.dat │ │ │ │ │ │ ├── banlist.dat │ │ │ │ │ │ ├── fee_estimates.dat │ │ │ │ │ │ └── database │ │ │ │ │ │ └── log.0000000001 │ │ │ │ └── chain_cache │ │ │ │ │ └── blocks_1153 │ │ │ │ │ └── zcashd │ │ │ │ │ └── regtest │ │ │ │ │ ├── .lock │ │ │ │ │ ├── db.log │ │ │ │ │ ├── blocks │ │ │ │ │ ├── index │ │ │ │ │ │ ├── LOCK │ │ │ │ │ │ ├── CURRENT │ │ │ │ │ │ ├── 000005.ldb │ │ │ │ │ │ ├── 000006.log │ │ │ │ │ │ └── MANIFEST-000004 │ │ │ │ │ ├── blk00000.dat │ │ │ │ │ └── rev00000.dat │ │ │ │ │ ├── chainstate │ │ │ │ │ ├── LOCK │ │ │ │ │ ├── CURRENT │ │ │ │ │ ├── 000005.ldb │ │ │ │ │ ├── 000006.log │ │ │ │ │ └── MANIFEST-000004 │ │ │ │ │ ├── peers.dat │ │ │ │ │ ├── banlist.dat │ │ │ │ │ ├── wallet.dat │ │ │ │ │ └── fee_estimates.dat │ │ │ └── conf │ │ │ │ ├── lightwalletd.yml │ │ │ │ └── zcash.conf │ │ ├── paths.rs │ │ ├── chain_generics.rs │ │ ├── chain_generics │ │ │ ├── networked.rs │ │ │ └── conduct_chain.rs │ │ ├── fee_tables.rs │ │ ├── macros.rs │ │ ├── assertions.rs │ │ └── lightclient.rs │ ├── testvectors │ │ ├── old_wallet_reorg_test_wallet │ │ │ └── zcashd │ │ │ │ └── regtest │ │ │ │ ├── .lock │ │ │ │ ├── db.log │ │ │ │ ├── blocks │ │ │ │ ├── index │ │ │ │ │ ├── LOCK │ │ │ │ │ ├── CURRENT │ │ │ │ │ ├── LOG.old │ │ │ │ │ ├── 000005.ldb │ │ │ │ │ ├── 000006.log │ │ │ │ │ ├── MANIFEST-000004 │ │ │ │ │ └── LOG │ │ │ │ ├── blk00000.dat │ │ │ │ └── rev00000.dat │ │ │ │ ├── chainstate │ │ │ │ ├── LOCK │ │ │ │ ├── CURRENT │ │ │ │ ├── LOG.old │ │ │ │ ├── 000005.ldb │ │ │ │ ├── 000006.log │ │ │ │ ├── MANIFEST-000004 │ │ │ │ └── LOG │ │ │ │ ├── peers.dat │ │ │ │ ├── wallet.dat │ │ │ │ ├── banlist.dat │ │ │ │ └── fee_estimates.dat │ │ └── CHANGELOG.md │ ├── wallet │ │ ├── disk │ │ │ ├── testing │ │ │ │ └── examples │ │ │ │ │ ├── testnet │ │ │ │ │ ├── glory_goddess │ │ │ │ │ │ └── latest │ │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ ├── cbbhrwiilgbrababsshsmtpr │ │ │ │ │ │ ├── v26 │ │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ │ ├── v27 │ │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ │ ├── v28 │ │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ │ └── latest │ │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ └── mskmgdbhotbpetcjwcspgopp │ │ │ │ │ │ ├── latest │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ │ ├── G93738061a │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ │ └── Gab72a38b │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ ├── mainnet │ │ │ │ │ ├── vtfcorfbcbpctcfupmegmwbp │ │ │ │ │ │ └── v28 │ │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ └── hhcclaltpcckcsslpcnetblr │ │ │ │ │ │ ├── latest │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ │ └── gf0aaf9347 │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ └── regtest │ │ │ │ │ ├── aaaaaaaaaaaaaaaaaaaaaaaa │ │ │ │ │ └── v26 │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ ├── hmvasmuvwmssvichcarbpoct │ │ │ │ │ └── v27 │ │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ └── aadaalacaadaalacaadaalac │ │ │ │ │ ├── orch_only │ │ │ │ │ └── zingo-wallet.dat │ │ │ │ │ └── orch_and_sapl │ │ │ │ │ └── zingo-wallet.dat │ │ │ └── testing.rs │ │ ├── traits.rs │ │ └── utils.rs │ ├── lib.rs │ ├── utils.rs │ ├── utils │ │ ├── conversion.rs │ │ └── error.rs │ ├── lightclient │ │ ├── error.rs │ │ └── save.rs │ ├── data.rs │ ├── grpc_connector.rs │ ├── data │ │ └── proposal.rs │ ├── grpc_client.rs │ └── error.rs ├── zcash-params │ └── .gitignore ├── proptest-regressions │ └── wallet │ │ └── transaction_records_by_id │ │ └── input_source.txt ├── CHANGELOG.md ├── build.rs └── Cargo.toml ├── rustfmt.toml ├── zingolib_testutils ├── src │ └── lib.rs └── Cargo.toml ├── rust-toolchain.toml ├── .vscode ├── settings.json ├── extensions.json ├── tasks.json └── launch.json ├── renovate.json ├── zingo-cli ├── src │ ├── version.rs │ ├── main.rs │ └── commands │ │ └── error.rs ├── CHANGELOG.md ├── Cargo.toml └── README.md ├── zingo-status ├── src │ └── lib.rs ├── CHANGELOG.md └── Cargo.toml ├── contrib └── hooks │ └── pre-push ├── .github ├── dependabot.yml └── workflows │ ├── cargo-checkmate.yaml │ ├── coverage.yaml │ ├── ci-pr.yaml │ ├── test.yaml │ └── ci-nightly.yaml ├── .gitignore ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── githooks └── pre-commit ├── .config └── nextest.toml ├── .gitattributes ├── .editorconfig ├── pepper-sync ├── diagrams │ ├── verification.mmd │ ├── scan_worker.mmd │ ├── initialization.mmd │ ├── sync.mmd │ └── process_scan_results.mmd ├── LICENSE-MIT ├── Cargo.toml └── src │ ├── witness.rs │ └── keys │ └── transparent.rs ├── zingo-memo ├── CHANGELOG.md └── Cargo.toml ├── zingo-testutils ├── CHANGELOG.md ├── Cargo.toml └── proto │ └── compact_formats.proto ├── docs └── testing │ └── testnet.md ├── utils ├── git-hook-pre-commit.sh ├── check_package.sh ├── gen_branch_wallet.sh ├── git-require-clean-worktree.sh └── trailing-whitespace.sh ├── docker-ci ├── README.txt └── Dockerfile ├── zingo-price └── Cargo.toml ├── LICENSE ├── docker └── Dockerfile ├── app_description.txt ├── Cargo.toml ├── mkrelease.sh └── README.md /test_binaries/bins/.gitinclude: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /darkside-tests/lightwalletd_bin/.gitinclude: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libtonode-tests/tests/times/.gitinclude: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/bin/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/logs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2024" 2 | max_width = 100 3 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/zcashd/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib_testutils/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod scenarios; 2 | -------------------------------------------------------------------------------- /libtonode-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod chain_generics; 2 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/lightwalletd/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/.lock: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/db.log: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test_binaries/bins/README.md: -------------------------------------------------------------------------------- 1 | This repo exists to stash test binaries in. 2 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/LOCK: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/LOCK: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/.lock: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/db.log: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitkeep 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/.lock: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/db.log: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitkeep 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/index/LOCK: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/chainstate/LOCK: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/index/LOCK: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/chainstate/LOCK: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.90" 3 | components = [ "clippy", "rustfmt" ] 4 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/CURRENT: -------------------------------------------------------------------------------- 1 | MANIFEST-000007 2 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/CURRENT: -------------------------------------------------------------------------------- 1 | MANIFEST-000007 2 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/chainstate/CURRENT: -------------------------------------------------------------------------------- 1 | MANIFEST-000004 2 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/chainstate/CURRENT: -------------------------------------------------------------------------------- 1 | MANIFEST-000004 2 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/index/CURRENT: -------------------------------------------------------------------------------- 1 | MANIFEST-000004 2 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/index/CURRENT: -------------------------------------------------------------------------------- 1 | MANIFEST-000004 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.diagnostics.disabled": [ 3 | "unresolved-macro-call" 4 | ] 5 | } -------------------------------------------------------------------------------- /zingolib/zcash-params/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/index/LOG.old: -------------------------------------------------------------------------------- 1 | 2023/08/16-15:57:34.899534 140342570898240 Delete type=3 #1 2 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/chainstate/LOG.old: -------------------------------------------------------------------------------- 1 | 2023/08/16-15:57:34.899625 140342570898240 Delete type=3 #1 2 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /zingo-cli/src/version.rs: -------------------------------------------------------------------------------- 1 | //! Version 2 | //! TODO: Add Mod Description Here! 3 | 4 | /// TODO: Add Doc Comment Here! 5 | pub const VERSION: &str = "0.1.1"; 6 | -------------------------------------------------------------------------------- /zingo-status/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Zingo-Status 2 | //! TODO: Add Crate Description Here! 3 | 4 | #![warn(missing_docs)] 5 | #[forbid(unsafe_code)] 6 | pub mod confirmation_status; 7 | -------------------------------------------------------------------------------- /contrib/hooks/pre-push: -------------------------------------------------------------------------------- 1 | RUSTFLAGS="-D warnings" cargo check && cargo clippy -- --deny warnings && cargo fmt && ./utils/trailing-whitespace.sh reject && RUSTFLAGS="-D warnings" cargo check --tests -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/peers.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/peers.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/wallet.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/banlist.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/banlist.dat -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | allow: 8 | - dependency-type: direct 9 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/fee_estimates.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/fee_estimates.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/blk00000.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/blk00000.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/rev00000.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/rev00000.dat -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/peers.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/peers.dat -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/wallet.dat -------------------------------------------------------------------------------- /darkside-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod constants; 2 | pub mod darkside_connector; 3 | pub mod utils; 4 | pub mod darkside_types { 5 | tonic::include_proto!("cash.z.wallet.sdk.rpc"); 6 | } 7 | pub mod chain_generics; 8 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/000005.ldb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/000005.ldb -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/000008.ldb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/000008.ldb -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/000009.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/000009.log -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/000005.ldb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/000005.ldb -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/000008.ldb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/000008.ldb -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/000009.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/000009.log -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/database/log.0000000001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/database/log.0000000001 -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/banlist.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/banlist.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/peers.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/peers.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/banlist.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/banlist.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/wallet.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/MANIFEST-000007: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/MANIFEST-000007 -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/MANIFEST-000007: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/MANIFEST-000007 -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/fee_estimates.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/fee_estimates.dat -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/testnet/glory_goddess/latest/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/testnet/glory_goddess/latest/zingo-wallet.dat -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .helix 2 | cobertura.xml 3 | target 4 | wallets/* 5 | zingolib/proptest-regressions 6 | test_binaries/bins/* 7 | libtonode-tests/tests/chain_generics.proptest-regressions 8 | libtonode-tests/store_all_checkpoints_test 9 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/blk00000.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/blk00000.dat -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/rev00000.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/rev00000.dat -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "EditorConfig.EditorConfig", 4 | "github.vscode-github-actions", 5 | "Swellaby.rust-pack", 6 | "pflannery.vscode-versionlens" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/fee_estimates.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/fee_estimates.dat -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/index/000005.ldb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/index/000005.ldb -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/index/000006.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/index/000006.log -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/chainstate/000005.ldb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/chainstate/000005.ldb -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/chainstate/000006.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/chainstate/000006.log -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/blk00000.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/blk00000.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/rev00000.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/rev00000.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/chainstate/000005.ldb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/chainstate/000005.ldb -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/chainstate/000006.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/chainstate/000006.log -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/index/MANIFEST-000004: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/index/MANIFEST-000004 -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/chainstate/MANIFEST-000004: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/chainstate/MANIFEST-000004 -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/mainnet/vtfcorfbcbpctcfupmegmwbp/v28/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/mainnet/vtfcorfbcbpctcfupmegmwbp/v28/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/regtest/aaaaaaaaaaaaaaaaaaaaaaaa/v26/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/regtest/aaaaaaaaaaaaaaaaaaaaaaaa/v26/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/regtest/hmvasmuvwmssvichcarbpoct/v27/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/regtest/hmvasmuvwmssvichcarbpoct/v27/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/testnet/cbbhrwiilgbrababsshsmtpr/v26/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/testnet/cbbhrwiilgbrababsshsmtpr/v26/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/testnet/cbbhrwiilgbrababsshsmtpr/v27/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/testnet/cbbhrwiilgbrababsshsmtpr/v27/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/testnet/cbbhrwiilgbrababsshsmtpr/v28/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/testnet/cbbhrwiilgbrababsshsmtpr/v28/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/index/000005.ldb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/index/000005.ldb -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/index/000006.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/index/000006.log -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/mainnet/hhcclaltpcckcsslpcnetblr/latest/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/mainnet/hhcclaltpcckcsslpcnetblr/latest/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/testnet/cbbhrwiilgbrababsshsmtpr/latest/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/testnet/cbbhrwiilgbrababsshsmtpr/latest/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/testnet/mskmgdbhotbpetcjwcspgopp/latest/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/testnet/mskmgdbhotbpetcjwcspgopp/latest/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/chainstate/MANIFEST-000004: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/chainstate/MANIFEST-000004 -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/mainnet/hhcclaltpcckcsslpcnetblr/gf0aaf9347/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/mainnet/hhcclaltpcckcsslpcnetblr/gf0aaf9347/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/regtest/aadaalacaadaalacaadaalac/orch_only/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/regtest/aadaalacaadaalacaadaalac/orch_only/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/testnet/mskmgdbhotbpetcjwcspgopp/G93738061a/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/testnet/mskmgdbhotbpetcjwcspgopp/G93738061a/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/testnet/mskmgdbhotbpetcjwcspgopp/Gab72a38b/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/testnet/mskmgdbhotbpetcjwcspgopp/Gab72a38b/zingo-wallet.dat -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/index/MANIFEST-000004: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/testutils/regtest/data/chain_cache/blocks_1153/zcashd/regtest/blocks/index/MANIFEST-000004 -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing/examples/regtest/aadaalacaadaalacaadaalac/orch_and_sapl/zingo-wallet.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zingolabs/zingolib/HEAD/zingolib/src/wallet/disk/testing/examples/regtest/aadaalacaadaalacaadaalac/orch_and_sapl/zingo-wallet.dat -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Base on the same image that we use in CI 2 | FROM zingodevops/ci-build:002 3 | 4 | # Install devcontainer-specific tools 5 | RUN apt-get update \ 6 | && apt-get install -y htop \ 7 | && apt-get clean \ 8 | && rm -rf /var/lib/apt/lists/* 9 | -------------------------------------------------------------------------------- /githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Run cargo clippy 3 | cargo clippy -- -D warnings 4 | 5 | # If clippy fails, exit with the status code 6 | if [ $? != 0 ]; then 7 | echo "Clippy found errors, aborting commit." 8 | exit 1 9 | fi 10 | 11 | # Otherwise, continue 12 | exit 0 13 | 14 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cargo", 6 | "command": "run", 7 | "problemMatcher": [ 8 | "$rustc" 9 | ], 10 | "label": "rust: cargo run", 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | } 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.config/nextest.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | fail-fast = false 3 | slow-timeout = { period = "60s", terminate-after = 10, grace-period = "30s" } 4 | 5 | [profile.ci] 6 | failure-output = "immediate-final" 7 | fail-fast = false 8 | slow-timeout = { period = "60s", terminate-after = 10, grace-period = "30s" } 9 | 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | # Ensure shell scripts use LF line endings (linux only accepts LF) 7 | *.sh eol=lf 8 | *.ps1 eol=lf 9 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/LOG: -------------------------------------------------------------------------------- 1 | 2022/08/26-00:13:45.165334 7f00d4cece80 Recovering log #6 2 | 2022/08/26-00:13:45.165346 7f00d4cece80 Level-0 table #8: started 3 | 2022/08/26-00:13:45.170697 7f00d4cece80 Level-0 table #8: 251 bytes OK 4 | 2022/08/26-00:13:45.192105 7f00d4cece80 Delete type=0 #6 5 | 2022/08/26-00:13:45.192123 7f00d4cece80 Delete type=3 #4 6 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/LOG: -------------------------------------------------------------------------------- 1 | 2022/08/26-00:13:45.135344 7f00d4cece80 Recovering log #6 2 | 2022/08/26-00:13:45.135361 7f00d4cece80 Level-0 table #8: started 3 | 2022/08/26-00:13:45.149354 7f00d4cece80 Level-0 table #8: 179 bytes OK 4 | 2022/08/26-00:13:45.165218 7f00d4cece80 Delete type=0 #6 5 | 2022/08/26-00:13:45.165235 7f00d4cece80 Delete type=3 #4 6 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/chainstate/LOG.old: -------------------------------------------------------------------------------- 1 | 2022/08/25-23:59:30.890923 7f9e23d55e80 Recovering log #3 2 | 2022/08/25-23:59:30.890937 7f9e23d55e80 Level-0 table #5: started 3 | 2022/08/25-23:59:30.895721 7f9e23d55e80 Level-0 table #5: 843 bytes OK 4 | 2022/08/25-23:59:30.904208 7f9e23d55e80 Delete type=0 #3 5 | 2022/08/25-23:59:30.904223 7f9e23d55e80 Delete type=3 #2 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome:http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.rs] 11 | indent_size = 4 12 | indent_style = space 13 | insert_final_newline = true 14 | trim_trailing_whitespace = true 15 | 16 | [*.json] 17 | indent_size = 4 18 | indent_style = space 19 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/data/regtestvectors/regtest/blocks/index/LOG.old: -------------------------------------------------------------------------------- 1 | 2022/08/25-23:59:30.876434 7f9e23d55e80 Recovering log #3 2 | 2022/08/25-23:59:30.876453 7f9e23d55e80 Level-0 table #5: started 3 | 2022/08/25-23:59:30.884850 7f9e23d55e80 Level-0 table #5: 1516 bytes OK 4 | 2022/08/25-23:59:30.890815 7f9e23d55e80 Delete type=0 #3 5 | 2022/08/25-23:59:30.890834 7f9e23d55e80 Delete type=3 #2 6 | -------------------------------------------------------------------------------- /pepper-sync/diagrams/verification.mmd: -------------------------------------------------------------------------------- 1 | flowchart LR 2 | %% VERIFICATION 3 | SV([Start verification]) --> V1{Continuity check fails?} 4 | V1 -- Yes --> V2["Set 10 blocks below current 'verify' range -> 'verify' and truncate wallet data"] --> V3{Exceeded max verification window?} 5 | V3 -- Yes --> V4([Return error]) 6 | V3 -- No --> SV 7 | V1 -- No --> V5([Proceed to scanning lower priority scan ranges]) 8 | 9 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/chainstate/LOG: -------------------------------------------------------------------------------- 1 | 2023/08/16-15:58:47.269655 140041836073792 Recovering log #3 2 | 2023/08/16-15:58:47.269673 140041836073792 Level-0 table #5: started 3 | 2023/08/16-15:58:47.269708 140041836073792 Level-0 table #5: 6254 bytes OK 4 | 2023/08/16-15:58:47.269740 140041836073792 Delete type=3 #2 5 | 2023/08/16-15:58:47.269742 140041836073792 Delete type=0 #3 6 | -------------------------------------------------------------------------------- /zingo-cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Deprecated 11 | 12 | ### Added 13 | 14 | ### Changed 15 | 16 | ### Removed 17 | 18 | -------------------------------------------------------------------------------- /zingo-memo/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Deprecated 11 | 12 | ### Added 13 | 14 | ### Changed 15 | 16 | ### Removed 17 | 18 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/old_wallet_reorg_test_wallet/zcashd/regtest/blocks/index/LOG: -------------------------------------------------------------------------------- 1 | 2023/08/16-15:58:47.269496 140041836073792 Recovering log #3 2 | 2023/08/16-15:58:47.269524 140041836073792 Level-0 table #5: started 3 | 2023/08/16-15:58:47.269565 140041836073792 Level-0 table #5: 5598 bytes OK 4 | 2023/08/16-15:58:47.269602 140041836073792 Delete type=3 #2 5 | 2023/08/16-15:58:47.269604 140041836073792 Delete type=0 #3 6 | -------------------------------------------------------------------------------- /zingo-status/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Deprecated 11 | 12 | ### Added 13 | 14 | ### Changed 15 | 16 | ### Removed 17 | 18 | -------------------------------------------------------------------------------- /zingo-testutils/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Deprecated 11 | 12 | ### Added 13 | 14 | ### Changed 15 | 16 | ### Removed 17 | 18 | -------------------------------------------------------------------------------- /zingolib/src/testvectors/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Deprecated 11 | 12 | ### Added 13 | 14 | ### Changed 15 | 16 | ### Removed 17 | 18 | -------------------------------------------------------------------------------- /darkside-tests/proptest-regressions/chain_generic_tests.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 70985695b2b442b21dbf8cb2ef9080cb38fc1fcdd003b86c2ba2da332e628fb6 # shrinks to value = 0 8 | -------------------------------------------------------------------------------- /zingo-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | 3 | use tracing_subscriber::EnvFilter; 4 | pub fn main() { 5 | tracing_subscriber::fmt() 6 | .with_env_filter(EnvFilter::from_default_env()) 7 | .init(); 8 | // install default crypto provider (ring) 9 | if let Err(e) = rustls::crypto::ring::default_provider().install_default() { 10 | eprintln!("Error installing crypto provider: {e:?}"); 11 | } 12 | zingo_cli::run_cli(); 13 | } 14 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/conf/lightwalletd.yml: -------------------------------------------------------------------------------- 1 | # # Default zingo lib lightwalletd conf YAML for regtest mode # # 2 | # # Log file and conf path are explicitly passed in regtest mode # # 3 | bind-addr: 127.0.0.1:9067 4 | cache-size: 10 5 | # log-file: ../logs/lwd.log 6 | log-level: 10 7 | # zcash-conf-path: ../conf/zcash.conf 8 | 9 | # example config for TLS 10 | #tls-cert: /secrets/lightwallted/example-only-cert.pem 11 | #tls-key: /secrets/lightwallted/example-only-cert.key 12 | -------------------------------------------------------------------------------- /darkside-tests/build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), Box> { 2 | tonic_build::configure().build_server(true).compile_protos( 3 | &[ 4 | "../zingo-testutils/proto/compact_formats.proto", 5 | "proto/darkside.proto", 6 | "../zingo-testutils/proto/service.proto", 7 | ], 8 | &["proto", "../zingo-testutils/proto"], 9 | )?; 10 | println!("cargo:rerun-if-changed=proto/darkside.proto"); 11 | Ok(()) 12 | } 13 | -------------------------------------------------------------------------------- /zingo-status/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zingo-status" 3 | description = "A Rust library for representing and working with transaction confirmation status in ZCash." 4 | authors = [""] 5 | version = "0.0.1" 6 | edition = "2024" 7 | repository = "https://github.com/zingolabs/zingolib" 8 | homepage = "https://github.com/zingolabs/zingolib" 9 | license = "MIT" 10 | 11 | [dependencies] 12 | zcash_primitives = { workspace = true } 13 | byteorder = { workspace = true } 14 | -------------------------------------------------------------------------------- /zingolib/proptest-regressions/wallet/transaction_records_by_id/input_source.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc e6e979e736900f39b24d9f11f81ecb61b9200b14dcdfe732138867d6b9e2d97f # shrinks to spent_val = 0, unspent_val = 0, unconf_spent_val = 0 8 | -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/210.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 210, 4 | "hash": "06f1bb264c89b9ab45d70625ca3fddcc3a050671b0157a609aa5bb2b85a80664", 5 | "time": 1694454365, 6 | "saplingTree": "000000", 7 | "orchardTree": "0112bf60643ca551d65f044cef5166559335ad085cb8061a3c06fdaccb2e305e2c001f0000000164ba86d24008a0397ad8a60bb80b9236813b36a58f4753d171c9c812d2a5f616010e83a85121631d3ba505124700ad7da4b8793f291a66ef28728abf94fce4aa3c0000000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/215.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 215, 4 | "hash": "0aae6e3626e74be4d4e1adaf7574278b352a1635c8a8a2577eb33130b8fd68a8", 5 | "time": 1694454448, 6 | "saplingTree": "000000", 7 | "orchardTree": "01f0f2ee2a80ff8b89bc4c58f1964e1d8e5f37b8921b1954ae34736159aeb36823019a1e5e0ee6f987783a5411e003cef274c03f577c18418b5eb6e1bd83690043251f0000000000015fe60f3e71ba24797be5421c6c702e0a50c3a2178291a7d3dbd9543f5815cb0400000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/216.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 216, 4 | "hash": "0070b51547a503e5be68ce40c7b32d95d597b072cda0973cefcf3e229410d36e", 5 | "time": 1694454464, 6 | "saplingTree": "000000", 7 | "orchardTree": "016ec4d7b8cc7cc32ab233af76cee0ccd47f87503c5671bac917ade998a55e5c01001f00018d8dd8f58e3dccccaf2fb85f22bee4b755c311157d31b6d4e58ff33046b4cf2a000000015fe60f3e71ba24797be5421c6c702e0a50c3a2178291a7d3dbd9543f5815cb0400000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/201.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 201, 4 | "hash": "00458a48c7dd2114b4bed3d010b32abf5e5f3c956e10c99950b267aa1624e23e", 5 | "time": 1694454056, 6 | "saplingTree": "000000", 7 | "orchardTree": "01f00aa280d1ab9c9904b50ca8bd783bd94ed2e16ea0572181d28e2f83aef055010124e97895b05b4dbb88ca48cc09bb6c849c3e119f96c28ae245be98d030dc3a311f0001814852d7f0924c499678ca5ffd914e6b3eab25c5968d4136c68bfb6c1ee8343600016be8aefe4f98825b5539a2b47b90a8057e52c1e1badc725d67c06b4cc2a32e24000000000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/202.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 202, 4 | "hash": "05b1594508675360d50ce02f922542f79e75e4bf7debfed101ed3fbfce2a17d1", 5 | "time": 1694454056, 6 | "saplingTree": "000000", 7 | "orchardTree": "01f00aa280d1ab9c9904b50ca8bd783bd94ed2e16ea0572181d28e2f83aef055010124e97895b05b4dbb88ca48cc09bb6c849c3e119f96c28ae245be98d030dc3a311f0001814852d7f0924c499678ca5ffd914e6b3eab25c5968d4136c68bfb6c1ee8343600016be8aefe4f98825b5539a2b47b90a8057e52c1e1badc725d67c06b4cc2a32e24000000000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/203.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 203, 4 | "hash": "016da97020ab191559f34f1d3f992ce2ec7c609cb0e5b932c45f1693eeb2192f", 5 | "time": 1694454196, 6 | "saplingTree": "000000", 7 | "orchardTree": "01136febe0db97210efb679e378d3b3a49d6ac72d0161ae478b1faaa9bd26a2118012246dd85ba2d9510caa03c40f0b75f7b02cb0cfac88ec1c4b9193d58bb6d44201f000001f0328e13a28669f9a5bd2a1c5301549ea28ccb7237347b9c76c05276952ad135016be8aefe4f98825b5539a2b47b90a8057e52c1e1badc725d67c06b4cc2a32e24000000000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/204.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 204, 4 | "hash": "0ef1dc1c5f8f3cfe7472c68277bdedf7b7373d7cf69af4089a671682806fbf8a", 5 | "time": 1694454212, 6 | "saplingTree": "000000", 7 | "orchardTree": "011d12a1807e01004954ab90bc824d1d0a51efcfda26271044da791654dacdcb3c001f0001ceed993f9b15320ee7183914ae3eb973e79d93e332a2e22c8620d70b651b523001f0328e13a28669f9a5bd2a1c5301549ea28ccb7237347b9c76c05276952ad135016be8aefe4f98825b5539a2b47b90a8057e52c1e1badc725d67c06b4cc2a32e24000000000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/206.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 206, 4 | "hash": "09ff44897dbafc4d861d40bef68d5dcb669603846c662209e5d3005889e6cafb", 5 | "time": 1694454297, 6 | "saplingTree": "000000", 7 | "orchardTree": "015375285b360d60cb5b80ccff3bac73563b1adcdbc5f3aa26409e6a3235e2a03c01200b6d8bd705608ab5ea14ee44be30c5102e2c978d100671e3261c6ea64d01161f01527f37e6a6ebbd98e002a7bd0a268df78aa2f1dd140ed2559695251010977038000000010e83a85121631d3ba505124700ad7da4b8793f291a66ef28728abf94fce4aa3c0000000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/207.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 207, 4 | "hash": "000cecf2924b0f31b62e848874d7dff1d9d7e4baf6479ced3a78365f5df9f43f", 5 | "time": 1694454313, 6 | "saplingTree": "000000", 7 | "orchardTree": "01f5d47c239b5d80c529cabc58972085eb719c35fb0ae3986da5000b7ed596d22f001f0107b4d9e422d16ac60cfab54a05c93c6087c2e41172e0f5f58df63bee96b30f18011ef0c234b33686b8c172c0fc2464bf57eb4222b564b36800866b787306efdc280000010e83a85121631d3ba505124700ad7da4b8793f291a66ef28728abf94fce4aa3c0000000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/208.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 208, 4 | "hash": "0015671c9305a525a84bfd4e36e957f7886fcdb129c9bcf2a18c8cf4362c772f", 5 | "time": 1694454329, 6 | "saplingTree": "000000", 7 | "orchardTree": "014c60c91a9291c89e89825a4d44ce52416c796c6655e182882dd5881865d1c40f01f73130170625910b793ecabebb3d79ef252bf4722a8e24cb3ef21060b6da4e351f000001716a6514a735bedcae036f43036bd51aa25d39c3f1b0d6550bf18d2ae9e7683700010e83a85121631d3ba505124700ad7da4b8793f291a66ef28728abf94fce4aa3c0000000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/220.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 220, 4 | "hash": "0476a0a9ddd5cf9d2ac15087a8d51b0bd28f125c5a44cbf18a16317864f5eec6", 5 | "time": 1694454528, 6 | "saplingTree": "000000", 7 | "orchardTree": "01075a8cf172ecf0deb634b8cb471ea41435e977d1c9b12fb0d4264e5cac7cee1a010cbea84f5195a4758f15bdcbcd264fc9494f61403572b365f277307e8ddbfa3c1f00000001085516881012d2729492ba29b11522d3a45f0b70e2a7ab62a4243ec9a67c2a1100015fe60f3e71ba24797be5421c6c702e0a50c3a2178291a7d3dbd9543f5815cb0400000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/222.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 222, 4 | "hash": "015265800472a8aaf96c7891cf7bd63ee1468bb6f3747714a6bd76c40ec9298b", 5 | "time": 1694454562, 6 | "saplingTree": "000000", 7 | "orchardTree": "01532c96c5d6a36ae79a9cad00ef7053e11b738c84c1022f80d8b0afcd2aedea23001f000001346fd8af3d66b14feaa60685fa189ca55cbd7f952fc25cdc971c310122b2402a01085516881012d2729492ba29b11522d3a45f0b70e2a7ab62a4243ec9a67c2a1100015fe60f3e71ba24797be5421c6c702e0a50c3a2178291a7d3dbd9543f5815cb0400000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /docs/testing/testnet.md: -------------------------------------------------------------------------------- 1 | run these SEPERATELY to avoid {possible?} collisions 2 | 3 | // Passes after 271 seconds on my laptop (on NU6.1 upgraded zl) 4 | cargo nextest run --run-ignored=all orchard_glory_goddess 5 | // Fails complaining about missing sapling in UAs 6 | cargo nextest run --run-ignored=all sapling_glory_goddess 7 | // Fails after > 20 minutes 8 | cargo nextest run --run-ignored=all shield_glory_goddess 9 | 10 | to update the testnet wallet run 11 | cargo run -- -c=testnet --server="https://testnet.zec.rocks:9067" --data-dir=zingolib/src/wallet/disk/testing/examples/testnet/glory_goddess/latest 12 | -------------------------------------------------------------------------------- /utils/git-hook-pre-commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # zingolib pre-commit hook 4 | # 5 | # zingolib devs can ensure consistent commits by using this pre-commit 6 | # hook. Install with: 7 | # 8 | # $ ln -s ../../utils/git-hook-pre-commit.sh .git/hooks/pre-commit 9 | 10 | set -efuo pipefail 11 | 12 | echo 'Running zingolib pre-commit checks.' 13 | 14 | cd "$(git rev-parse --show-toplevel)" 15 | 16 | ./utils/git-require-clean-worktree.sh 17 | 18 | set -x 19 | ./utils/trailing-whitespace.sh reject 20 | cargo fmt -- --check 21 | cargo check 22 | cargo test --bins --lib 23 | #cargo test --doc # No doc tests yet. 24 | cargo clippy 25 | -------------------------------------------------------------------------------- /zingo-memo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zingo-memo" 3 | description = "Utilities for procedural creation and parsing of the Memo field for ZCash." 4 | authors = [""] 5 | version = "0.0.1" 6 | edition = "2024" 7 | repository = "https://github.com/zingolabs/zingolib" 8 | homepage = "https://github.com/zingolabs/zingolib" 9 | license = "MIT" 10 | 11 | 12 | [dependencies] 13 | zcash_address.workspace = true 14 | zcash_client_backend.workspace = true 15 | zcash_encoding.workspace = true 16 | zcash_keys.workspace = true 17 | zcash_primitives.workspace = true 18 | 19 | [dev-dependencies] 20 | rand.workspace = true 21 | -------------------------------------------------------------------------------- /zingolib/src/testutils/regtest/conf/zcash.conf: -------------------------------------------------------------------------------- 1 | ## Default zingolib zcash.conf for regtest mode ## 2 | regtest=1 3 | nuparams=5ba81b19:1 # Overwinter 4 | nuparams=76b809bb:1 # Sapling 5 | nuparams=2bb40e60:1 # Blossom 6 | nuparams=f5b9230b:1 # Heartwood 7 | nuparams=e9ff75a6:1 # Canopy 8 | nuparams=c2d6d0b4:1 # NU5 9 | nuparams=c8e71055:1 # NU6 10 | txindex=1 11 | insightexplorer=1 12 | experimentalfeatures=1 13 | rpcuser=xxxxxx 14 | rpcpassword=xxxxxx 15 | rpcport=18232 16 | rpcallowip=127.0.0.1 17 | 18 | minetolocalwallet=0 19 | mineraddress=zregtestsapling1fmq2ufux3gm0v8qf7x585wj56le4wjfsqsj27zprjghntrerntggg507hxh2ydcdkn7sx8kya7p 20 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "dockerFile": "Dockerfile", 3 | "hostRequirements": { 4 | "cpus": 4, 5 | "memory": "16gb", 6 | "storage": "32gb" 7 | }, 8 | "postCreateCommand": "ln -s /usr/bin/lightwalletd /usr/bin/zcashd /usr/bin/zcash-cli ./zingocli/regtest/bin/", 9 | "customizations": { 10 | "vscode": { 11 | "extensions": [ 12 | "EditorConfig.EditorConfig", 13 | "github.vscode-github-actions", 14 | "Swellaby.rust-pack", 15 | "pflannery.vscode-versionlens" 16 | ] 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /darkside-tests/proptest-regressions/chain_generics.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc eef3deec08ec4d278ea80acf3351dba273deacb3d9544ebf173c6ce8d5d3f79d # shrinks to value = 0 8 | cc 58a549bf09db2b84ee36488c0bda1295ca5068fa7ec753dae3ca0b955bedd613 # shrinks to value = 0 9 | cc e2b75ab014d34f914e99dd8b15e75371a3da8e4d14896d8d65dc7199990de081 # shrinks to value = 34 10 | -------------------------------------------------------------------------------- /pepper-sync/diagrams/scan_worker.mmd: -------------------------------------------------------------------------------- 1 | flowchart TD 2 | %% SCAN WORKER 3 | START([Receive scan task]) --> WS1["Check block hash and height continuity"] 4 | WS1 --> WS2["Collect inputs -> build nullifier and outpoint maps"] 5 | WS2 --> WS3["Trial decrypt all notes in each compact block"] 6 | WS3 --> WS4["Fetch and scan full transactions for successful decrypts -> create wallet transactions"] 7 | WS4 --> WS5["Derive nullifier and position for each decrypted note"] 8 | WS5 --> WS6["Compute note commitment leaf and shard tree retention"] 9 | WS6 --> WS8["Create wallet blocks from compact blocks"] 10 | WS8 --> WS7([Return scan results]) 11 | -------------------------------------------------------------------------------- /zingolib/src/testutils/paths.rs: -------------------------------------------------------------------------------- 1 | //! TODO 2 | 3 | use std::path::PathBuf; 4 | 5 | /// TODO: Add Doc Comment Here! 6 | #[must_use] 7 | pub fn get_cargo_manifest_dir() -> PathBuf { 8 | PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("To be inside a manifested space.")) 9 | } 10 | 11 | /// TODO: Add Doc Comment Here! 12 | #[must_use] 13 | pub fn get_regtest_dir() -> PathBuf { 14 | get_cargo_manifest_dir().join("regtest") 15 | } 16 | 17 | /// TODO: Add Doc Comment Here! 18 | #[must_use] 19 | pub fn get_bin_dir() -> PathBuf { 20 | let mut dir = get_cargo_manifest_dir(); 21 | dir.pop(); 22 | dir.join("test_binaries").join("bins") 23 | } 24 | -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/209.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 209, 4 | "hash": "03e3820783cfcff52b7678a01e427cddfa80e0e2798b0fef89201f8f2cd21e75", 5 | "time": 1694454349, 6 | "saplingTree": "000000", 7 | "orchardTree": "01c3bd1bf05b48d6414a3260cc4c6a5cbce4ff4aa8f38addc8fe71e99abf75ec0801c9a1800efc18393396ea59ad6c0ed2c8fb0ce1a2ce8f10676ce76cb4a6f3cb131f00016d3c37cac01e1d7f35098320b49b24e8248245cd60633cac7969e752a3f5781801716a6514a735bedcae036f43036bd51aa25d39c3f1b0d6550bf18d2ae9e7683700010e83a85121631d3ba505124700ad7da4b8793f291a66ef28728abf94fce4aa3c0000000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/211.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 211, 4 | "hash": "042ce5b891276b0854573d1aa5b0f4fa747b4f2f00ff7147a46471365073d9f4", 5 | "time": 1694454381, 6 | "saplingTree": "000000", 7 | "orchardTree": "01aa59b4674f6ca4e5ab6e87983c5c117d94502316379cdae973a62aff8dfdce0601bbe30e62c55cd3b1911df02ab2a818bd65d21b73f2ea7fc011acf3a7292ee60b1f01c5c19f714b11c4c90f369d90ff31de9216fd722547594cab61b2d97cad1a743400000164ba86d24008a0397ad8a60bb80b9236813b36a58f4753d171c9c812d2a5f616010e83a85121631d3ba505124700ad7da4b8793f291a66ef28728abf94fce4aa3c0000000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/213.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 213, 4 | "hash": "014b85f0ba93e830524b6f7ef0f3c46fd218c2ab84cd559aeec82a0861e1e112", 5 | "time": 1694454415, 6 | "saplingTree": "000000", 7 | "orchardTree": "01effa18dff94cb0990d232fbdaae06ff982fd22ec8d5f21dc7fc4590e2392101a001f011af41fac18a83ca4ae4abda96976040d450c060d473264baf1da5e06903e44100001b5c1786ff372dbd5a953b3c5ba93f28e4c302a0e76c155d41491cd4be2ee5c170164ba86d24008a0397ad8a60bb80b9236813b36a58f4753d171c9c812d2a5f616010e83a85121631d3ba505124700ad7da4b8793f291a66ef28728abf94fce4aa3c0000000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/217.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 217, 4 | "hash": "098c25ea7d9a9a31b1efd62659c71001a563e1b969b557102a80799a27b6d178", 5 | "time": 1694454479, 6 | "saplingTree": "000000", 7 | "orchardTree": "0187474767b30ca0bc73b4bc575d8d112cd0a090d4dcede9d8871b667ab54d1c3e010574ff78b147cdc4f9df34a4e9475f5497ab37beae7869bfc6d23faffae68b331f01bf43fa83e167ea0170f13a2f94eb9cee7f67d1545aaf12acc16bf587d12ac936018d8dd8f58e3dccccaf2fb85f22bee4b755c311157d31b6d4e58ff33046b4cf2a000000015fe60f3e71ba24797be5421c6c702e0a50c3a2178291a7d3dbd9543f5815cb0400000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/218.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 218, 4 | "hash": "097e67374dcd1cb40b15f281baf067944096688c54e9b8258aaddfce3d872c64", 5 | "time": 1694454498, 6 | "saplingTree": "000000", 7 | "orchardTree": "01ff157a36bfdfdf400a3b01813bf1b748c73f33045aaaf362e776b0a93aacc82d01d1ae8b0353347eda03e7f9a4e35153a6a683669c4af05ef0b2d09ad85f69843f1f0118f8307faeba6474d6539bd12c76e328f189b1fcf4e4e9ce911a04b264fd7604000165a945a3a68e32b76ac9428b0bc3f816bf474e1ead11556f6a8659ed86f912240000015fe60f3e71ba24797be5421c6c702e0a50c3a2178291a7d3dbd9543f5815cb0400000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/219.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 219, 4 | "hash": "08ac173cdde155e0fea6d5fdd16d3cc67dcd8a11d4c52e0854f4fcda72b68f29", 5 | "time": 1694454513, 6 | "saplingTree": "000000", 7 | "orchardTree": "01eff34a8547ae7734bbb5ad1398ffdc79ed4f067c59edaeacabc762786c76d92b001f01ef15a08d1b75c5fcd8f449df58dd9f20089ce512bf1041267b5bd23fd267bb020130f5880d893e99cf7aef7ad824abdf42a2cb08eac300765aee7ccab226c375260165a945a3a68e32b76ac9428b0bc3f816bf474e1ead11556f6a8659ed86f912240000015fe60f3e71ba24797be5421c6c702e0a50c3a2178291a7d3dbd9543f5815cb0400000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /darkside-tests/tests/data/wallets/advanced_reorg_tests/tree_states/221.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": "regtest", 3 | "height": 221, 4 | "hash": "02bc192845f5b1ee325d46edf5a7ddc6e067f76c512285ee21c81823b7c96251", 5 | "time": 1694454547, 6 | "saplingTree": "000000", 7 | "orchardTree": "014d05142bf23d886c7f1f9d4fc6af466954763d9661b9749f2288355cc433771a01cf2746dbbecd066abcc3d9f229e9a5f01719361885ba8a0b8b6373cab3ba40361f000108f1ec43605d2424ba1800a55119d0496a213bb6f2b9c7ae6dc6546e1f6b34330001085516881012d2729492ba29b11522d3a45f0b70e2a7ab62a4243ec9a67c2a1100015fe60f3e71ba24797be5421c6c702e0a50c3a2178291a7d3dbd9543f5815cb0400000000000000000000000000000000000000000000000000" 8 | } -------------------------------------------------------------------------------- /docker-ci/README.txt: -------------------------------------------------------------------------------- 1 | The following is required to successfully update the docker container for Github CI workflows: 2 | - update the lightwalletd and zcash git tags to the latest versions in 'docker-ci/Dockerfile' 3 | - change to the `docker-ci` directory 4 | - run 'docker build -t zingodevops/ci-build: .' to build the image locally 5 | - run 'docker login' and fill in the credentials for DockerHub 6 | - run 'docker push zingodevops/ci-build:' to push to DockerHub 7 | - update github workflow files to the new image version number 8 | 9 | NOTE: if `sudo` is necessary use `sudo` with all commands including login. 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(Windows) Launch", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/target/debug/zingo-cli.exe", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${fileDirname}", 15 | "environment": [], 16 | "console": "externalTerminal" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /utils/check_package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check if packge name is provided 4 | if [ $# -eq 0 ]; then 5 | echo "Usage: $0 " 6 | echo "Example: $0 zaino-state" 7 | exit 1 8 | fi 9 | 10 | PACKAGE_NAME="$1" 11 | 12 | # Run all cargo commands for the specified packge 13 | set -e # Exit on first error 14 | 15 | echo "Running checks for packge: $PACKAGE_NAME" 16 | 17 | cargo check -p "$PACKAGE_NAME" && \ 18 | cargo check --all-features -p "$PACKAGE_NAME" && \ 19 | cargo check --tests -p "$PACKAGE_NAME" && \ 20 | cargo check --tests --all-features -p "$PACKAGE_NAME" && \ 21 | cargo fmt -p "$PACKAGE_NAME" && \ 22 | cargo clippy -p "$PACKAGE_NAME" #&& \ 23 | cargo nextest run -p "$PACKAGE_NAME" 24 | -------------------------------------------------------------------------------- /utils/gen_branch_wallet.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env sh 2 | 3 | set -ex 4 | 5 | GITBRANCH="$1" 6 | CHAIN="$2" 7 | REPOROOT=`git rev-parse --show-toplevel` 8 | WALLETDIR=$REPOROOT/zingocli/tests/data/wallets/v26/${GITBRANCH}/${CHAIN} 9 | 10 | if [[ "$CHAIN" = "regtest" ]] 11 | then 12 | COMMANDARGS="--data-dir=$WALLETDIR save --regtest" 13 | elif [[ "$CHAIN" = "mainnet" ]] 14 | then 15 | COMMANDARGS="--data-dir=$WALLETDIR save" 16 | else 17 | exit 99 18 | fi 19 | 20 | echo $COMMANDARGS 21 | generate_wallet () { 22 | git checkout $GITBRANCH && \ 23 | mkdir -p $WALLETDIR && \ 24 | cargo clean && \ 25 | cargo build --release && \ 26 | $REPOROOT/target/release/zingo-cli $COMMANDARGS 27 | } 28 | 29 | generate_wallet 30 | -------------------------------------------------------------------------------- /utils/git-require-clean-worktree.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -efuo pipefail 3 | 4 | echo 'Checking for clean git working tree:' 5 | 6 | if ! git status --porcelain | grep '^.[^ ]' 7 | then 8 | echo 'Clean git working tree.' 9 | exit 0 10 | fi 11 | 12 | cat <) -> std::io::Result<()> { 12 | let mut file = tokio::fs::File::create(path).await?; 13 | file.write_all(&bytes).await 14 | } 15 | 16 | /// Returns number of seconds since unix epoch. 17 | pub(crate) fn now() -> u32 { 18 | SystemTime::now() 19 | .duration_since(SystemTime::UNIX_EPOCH) 20 | .expect("should never fail when comparing with an instant so far in the past") 21 | .as_secs() as u32 22 | } 23 | -------------------------------------------------------------------------------- /zingolib_testutils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zingolib_testutils" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [features] 7 | test_zainod_zcashd = [] 8 | test_lwd_zebrad = [] 9 | test_lwd_zcashd = [] 10 | 11 | [dependencies] 12 | 13 | # zingolib 14 | zingolib = { workspace = true, features = ["testutils"] } 15 | pepper-sync.workspace = true 16 | zingo_common_components.workspace = true 17 | 18 | # zingo infrastructure 19 | zcash_local_net = { git = "https://github.com/zingolabs/infrastructure.git", branch = "dev" } 20 | zingo_test_vectors = { git = "https://github.com/zingolabs/infrastructure.git", branch = "dev" } 21 | 22 | # zcash 23 | zebra-chain.workspace = true 24 | zcash_protocol.workspace = true 25 | 26 | # bitcoin 27 | 28 | bip0039.workspace = true 29 | 30 | # utilties 31 | 32 | portpicker.workspace = true 33 | tempfile.workspace = true 34 | http.workspace = true 35 | zip32.workspace = true 36 | -------------------------------------------------------------------------------- /pepper-sync/diagrams/initialization.mmd: -------------------------------------------------------------------------------- 1 | flowchart LR 2 | %% INITIALIZATION 3 | START([Start initialization]) --> I1["Launch mempool monitor"] 4 | I1 --> I2["Transparent address discovery"] 5 | I2 --> I3["Fetch subtree metadata for shard-range scanning and add initial frontiers to wallet's shard trees"] 6 | I3 --> I4{First-time sync?} 7 | I4 -- Yes --> I4Y["Set wallet height = [wallet birthday - 1]"] --> I5 8 | I4 -- No --> I4N["Keep existing wallet height"] --> I5 9 | I5["Create scan range: [wallet height + 1] -> chain height"] --> I6["Update wallet height -> chain height"] 10 | I6 --> I7["Set 'found note' priority ranges (transparent discovery + targeted scanning)"] 11 | I7 --> I8["Find latest orchard and sapling shard upper bounds; split at the lowest height; set upper split to 'chain tip' priority"] 12 | I8 --> I9["Set first 10 blocks after highest previously scanned -> 'verify' (re-org check)"] 13 | I9 --> END([Proceed to verification]) 14 | -------------------------------------------------------------------------------- /zingolib/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this crate will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [v1.9.0] 9 | 10 | ### Added 11 | 12 | - `commands`: 13 | - `MessagesFilterCommand`: For listing `ValueTransfers` containing memos related to an optional `sender` parameter. Returns a `JsonValue` object. 14 | - `ValueTransfersCommand`: takes optional reverse sort argument 15 | - `lightclient::messages_containing`: used by `MessagesFilterCommand`. 16 | - `lightclient::sorted_value_transfers` 17 | - `tests`: 18 | - `message_thread` test. 19 | 20 | ### Changed 21 | 22 | - `LightClient::value_transfers::create_send_value_transfers` (in-function definition) -> `ValueTransfers::create_send_value_transfers` 23 | 24 | ### Removed 25 | 26 | - `lightclient.do_list_notes` is deprecated 27 | -------------------------------------------------------------------------------- /zingo-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zingo-cli" 3 | version = "0.2.0" 4 | edition = "2024" 5 | 6 | [features] 7 | regtest = [ "zingolib/regtest", "zingo_common_components/for_test" ] 8 | 9 | [dependencies] 10 | pepper-sync = { workspace = true } 11 | zingo_common_components = { workspace = true, optional = true } 12 | zingolib = { workspace = true } 13 | 14 | zcash_address = { workspace = true } 15 | zcash_client_backend = { workspace = true } 16 | zcash_keys = { workspace = true } 17 | zcash_primitives = { workspace = true } 18 | zcash_protocol = { workspace = true } 19 | 20 | bip0039 = { workspace = true } 21 | clap = { workspace = true } 22 | http = { workspace = true } 23 | indoc = { workspace = true } 24 | json = { workspace = true } 25 | log = { workspace = true } 26 | rustls = { workspace = true } 27 | rustyline = { workspace = true } 28 | shellwords = { workspace = true } 29 | tokio = { workspace = true } 30 | tracing-subscriber = { workspace = true } 31 | zip32 = { workspace = true } 32 | -------------------------------------------------------------------------------- /zingo-price/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zingo-price" 3 | description = "Crate for fetching ZEC prices." 4 | authors = [""] 5 | version = "0.0.1" 6 | edition = "2024" 7 | repository = "https://github.com/zingolabs/zingolib" 8 | homepage = "https://github.com/zingolabs/zingolib" 9 | license = "MIT" 10 | 11 | [dependencies] 12 | zcash_encoding = { workspace = true } 13 | zcash_client_backend = { workspace = true } 14 | 15 | byteorder = { workspace = true } 16 | reqwest = { workspace = true, default-features = false, features = [ 17 | "cookies", 18 | "rustls-tls", 19 | "json", 20 | ] } 21 | rust_decimal = { workspace = true } 22 | serde = { workspace = true, features = ["derive"] } 23 | serde_json = { workspace = true } 24 | thiserror = { workspace = true } 25 | rusqlite = { workspace = true, features = ["bundled"] } 26 | 27 | [dev-dependencies] 28 | tokio = { workspace = true, features = ["full"] } 29 | 30 | [package.metadata.cargo-machete] 31 | ignored = ["rusqlite"] 32 | -------------------------------------------------------------------------------- /pepper-sync/diagrams/sync.mmd: -------------------------------------------------------------------------------- 1 | 2 | flowchart LR 3 | %% SYNC 4 | START([Start sync]) --> INIT["Initialization"] 5 | INIT --> S0 6 | S0{Batcher idle?} 7 | S0 -- No --> S6 8 | S0 -- Yes --> S1{All ranges in wallet state are 'scanned'?} 9 | S1 -- Yes --> SHUTDOWN([Shutdown sync]) 10 | S1 -- No --> S2["Pick highest-priority scan range"] 11 | S2 --> S3{Priority is 'historic'?} 12 | S3 -- Yes --> S4["Split orchard shard range off lower end"] --> S5["Set range to 'scanning' and send to batcher"] 13 | S3 -- No --> S5 14 | S5 --> S6 15 | S6["Batcher: stream compact blocks until fixed output threshold; store batch; when entire scan range batched, set batcher idle"] 16 | subgraph WORKERS[Scan workers] 17 | direction LR 18 | W1["Scan Worker 1"] 19 | W2["Scan Worker 2"] 20 | Wn["Scan Worker N"] 21 | end 22 | S6 --> W1 23 | S6 --> W2 24 | S6 --> Wn 25 | W1 --> PROC_RES["Process scan results"] 26 | W2 --> PROC_RES 27 | Wn --> PROC_RES 28 | 29 | PROC_RES --> S7["Scan mempool transactions"] 30 | S7 --> S0 31 | -------------------------------------------------------------------------------- /.github/workflows/cargo-checkmate.yaml: -------------------------------------------------------------------------------- 1 | name: Cargo Checkmate 2 | 3 | on: 4 | workflow_call: 5 | 6 | jobs: 7 | cargo-checkmate: 8 | name: Cargo Checkmate 9 | runs-on: ubuntu-24.04 10 | strategy: 11 | matrix: 12 | phase: [build, check, clippy, doc, format] 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Setup Rust toolchain 18 | uses: actions-rust-lang/setup-rust-toolchain@v1 19 | with: 20 | toolchain: ${{ inputs.toolchain }} 21 | 22 | - name: Cache cargo 23 | uses: Swatinem/rust-cache@v2 24 | 25 | - name: Install protoc 26 | run: | 27 | if ! command -v protoc; then 28 | sudo apt-get update 29 | sudo apt-get install -y protobuf-compiler 30 | fi 31 | 32 | - name: Run cargo-checkmate 33 | uses: zingolabs/infrastructure/.github/actions/cargo-checkmate@20485fed7a080e381130ed8120419dc81acae641 34 | with: 35 | phase: ${{ matrix.phase }} 36 | -------------------------------------------------------------------------------- /pepper-sync/diagrams/process_scan_results.mmd: -------------------------------------------------------------------------------- 1 | flowchart TD 2 | %% PROCESS SCAN RESULTS 3 | START([Start]) --> P1["Set surrounding shard ranges of incoming notes
-> 'found note'"] 4 | P1 --> P2["Discover unified addresses in use on-chain"] 5 | P2 --> P3["Add wallet transactions (including notes and coins)"] 6 | P3 --> P4["Add nullifiers to wallet's nullifier map"] 7 | P4 --> P5["Add outpoints to wallet's outpoint map"] 8 | P5 --> P6["Update wallet's shard trees"] 9 | P6 --> P7["Check coins output ids vs outpoint map; if spent: mark coin and set surrounding narrow block range -> 'found note'"] 10 | P7 --> P8["Check notes derived nullifiers vs nullifier map; if spent: mark note and set surrounding shard range -> 'found note'"] 11 | P8 --> P9["Add wallet blocks; retain only: range bounds, blocks with relevant txs, and within max verification window of highest scanned block"] 12 | P9 --> P10["Mark scan range containing this batch -> 'scanned'"] 13 | P10 --> P11["Merge adjacent 'scanned' ranges"] 14 | P11 --> P12["Clean wallet data no longer relevant"] 15 | P12 --> END([End]) 16 | -------------------------------------------------------------------------------- /pepper-sync/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 ZingoLabs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022-present Zingolabs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | -------------------------------------------------------------------------------- /zingolib/src/utils/conversion.rs: -------------------------------------------------------------------------------- 1 | //! Conversion specific utilities 2 | 3 | use zcash_address::ZcashAddress; 4 | use zcash_protocol::{TxId, value::Zatoshis}; 5 | 6 | use super::error::ConversionError; 7 | 8 | /// Converts txid from hex-encoded `&str` to `zcash_primitives::transaction::TxId`. 9 | /// 10 | /// `TxId` byte order is displayed in the reverse order to how it's encoded. 11 | pub fn txid_from_hex_encoded_str(txid: &str) -> Result { 12 | let txid_bytes = hex::decode(txid).map_err(ConversionError::DecodeHexFailed)?; 13 | let mut txid_bytes = <[u8; 32]>::try_from(txid_bytes) 14 | .map_err(|e| ConversionError::InvalidTxidLength(e.len()))?; 15 | txid_bytes.reverse(); 16 | Ok(TxId::from_bytes(txid_bytes)) 17 | } 18 | 19 | /// Convert a &str to a `ZcashAddress` 20 | pub fn address_from_str(address: &str) -> Result { 21 | Ok(ZcashAddress::try_from_encoded(address)?) 22 | } 23 | 24 | /// Convert a valid u64 into Zatoshis. 25 | pub fn zatoshis_from_u64(amount: u64) -> Result { 26 | Zatoshis::from_u64(amount).map_err(|_e| ConversionError::OutsideValidRange) 27 | } 28 | -------------------------------------------------------------------------------- /zingo-testutils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zingo-testutils" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [features] 7 | grpc-proxy = ["tonic"] 8 | default = ["grpc-proxy"] 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | zingolib = { workspace = true, features = ["testutils"] } 14 | zingo_netutils = { workspace = true, features = ["test-features"] } 15 | zingo-testvectors = { workspace = true } 16 | zingo-status = { workspace = true } 17 | 18 | zcash_client_backend = { workspace = true, features = [ 19 | "lightwalletd-tonic-transport", 20 | ] } 21 | zcash_primitives = { workspace = true } 22 | zcash_address = { workspace = true } 23 | orchard = { workspace = true } 24 | portpicker = { workspace = true } 25 | tempfile = { workspace = true } 26 | incrementalmerkletree = { workspace = true } 27 | 28 | json = { workspace = true } 29 | log = { workspace = true } 30 | tokio = { workspace = true } 31 | http.workspace = true 32 | tonic = { workspace = true, optional = true } 33 | nonempty.workspace = true 34 | 35 | [dev-dependencies] 36 | zingolib = { workspace = true, features = ["testutils"] } 37 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yaml: -------------------------------------------------------------------------------- 1 | name: Coverage (Weekly) 2 | 3 | on: 4 | schedule: 5 | - cron: "30 8 * * 0" 6 | workflow_dispatch: 7 | 8 | jobs: 9 | coverage: 10 | name: Coverage 11 | runs-on: ubuntu-24.04 12 | env: 13 | RUSTFLAGS: -D warnings 14 | container: 15 | image: zingodevops/ci-build:009 16 | options: --security-opt seccomp=unconfined 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Symlink lightwalletd and zcash binaries 22 | run: ln -s /usr/bin/lightwalletd /usr/bin/zcashd /usr/bin/zcash-cli ./libtonode-tests/regtest/bin/ 23 | 24 | - name: Cargo cache 25 | uses: Swatinem/rust-cache@v2 26 | 27 | - name: Generate code coverage 28 | uses: actions-rs/cargo@v1 29 | with: 30 | command: tarpaulin 31 | args: --verbose --workspace --avoid-cfg-tarpaulin --skip-clean --release --timeout 3000 --out xml 32 | 33 | - name: Upload to codecov.io 34 | uses: codecov/codecov-action@v3 35 | with: 36 | token: ${{ secrets.CODECOV_TOKEN }} 37 | files: ./cobertura.xml 38 | fail_ci_if_error: true 39 | -------------------------------------------------------------------------------- /zingolib/src/utils/error.rs: -------------------------------------------------------------------------------- 1 | //! Error sub-module for utils module. 2 | 3 | use std::fmt; 4 | 5 | /// The error type for conversion errors. 6 | #[derive(thiserror::Error, Debug, PartialEq)] 7 | pub enum ConversionError { 8 | /// Failed to decode hex 9 | DecodeHexFailed(hex::FromHexError), 10 | /// Invalid string length 11 | InvalidTxidLength(usize), 12 | /// Invalid recipient address 13 | InvalidAddress(#[from] zcash_address::ParseError), 14 | /// Amount is outside the valid range of zatoshis 15 | OutsideValidRange, 16 | } 17 | 18 | impl fmt::Display for ConversionError { 19 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 20 | match self { 21 | ConversionError::DecodeHexFailed(e) => write!(f, "failed to decode hex. {e}"), 22 | ConversionError::InvalidTxidLength(len) => { 23 | write!(f, "invalid txid length. should be 32 bytes. length: {len}") 24 | } 25 | ConversionError::InvalidAddress(e) => { 26 | write!(f, "invalid recipient address. {e}") 27 | } 28 | ConversionError::OutsideValidRange => { 29 | write!(f, "amount is outside the valid range of zatoshis") 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /utils/trailing-whitespace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -efuo pipefail 3 | 4 | function main 5 | { 6 | [ $# -eq 1 ] || usage-error 'expected a single arg' 7 | 8 | # cd to repo dir: 9 | cd "$(git rev-parse --show-toplevel)" 10 | 11 | case "$1" in 12 | fix) fix ;; 13 | reject) reject ;; 14 | *) usage-error "unknown command: $1" ;; 15 | esac 16 | } 17 | 18 | function fix 19 | { 20 | process-well-known-text-files sed -i 's/ *$//' 21 | } 22 | 23 | function reject 24 | { 25 | local F="$(mktemp --tmpdir zingolib-trailing-whitespace.XXX)" 26 | 27 | process-well-known-text-files grep -E --with-filename ' +$' \ 28 | | sed 's/$/\\n/' \ 29 | | tee "$F" 30 | 31 | local NOISE="$(cat "$F" | wc -l)" 32 | rm "$F" 33 | 34 | if [ "$NOISE" -eq 0 ] 35 | then 36 | echo 'No trailing whitespace detected.' 37 | else 38 | echo -e '\nRejecting trailing whitespace above.' 39 | exit 1 40 | fi 41 | } 42 | 43 | function process-well-known-text-files 44 | { 45 | find . \ 46 | \( -type d \ 47 | \( \ 48 | -name '.git' \ 49 | -o -name 'target' \ 50 | \) \ 51 | -prune \ 52 | \) \ 53 | -o \( \ 54 | -type f \ 55 | \( \ 56 | -name '*.rs' \ 57 | -o -name '*.md' \ 58 | -o -name '*.toml' \ 59 | -o -name '*.yaml' \ 60 | \) \ 61 | -exec "$@" '{}' \; \ 62 | \) 63 | } 64 | 65 | function usage-error 66 | { 67 | echo "usage error: $*" 68 | echo 69 | echo "usage: $0 ( fix | reject )" 70 | exit 1 71 | } 72 | 73 | main "$@" 74 | -------------------------------------------------------------------------------- /darkside-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "darkside-tests" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [features] 8 | chain_generic_tests = [] 9 | 10 | [dependencies] 11 | zingolib = { workspace = true, features = ["darkside_tests", "testutils"] } 12 | zingo_netutils = { workspace = true } 13 | zingo_common_components = { workspace = true, features = ["for_test"] } 14 | 15 | zingolib_testutils = { version = "0.1.0", path = "../zingolib_testutils" } 16 | zcash_local_net = { git = "https://github.com/zingolabs/infrastructure.git", branch = "dev" } 17 | zingo_test_vectors = { git = "https://github.com/zingolabs/infrastructure.git", branch = "dev" } 18 | 19 | zebra-chain = { workspace = true } 20 | 21 | orchard = { workspace = true } 22 | sapling-crypto = { workspace = true } 23 | incrementalmerkletree = { workspace = true } 24 | zcash_primitives = { workspace = true } 25 | zcash_protocol = { workspace = true } 26 | bip0039.workspace = true 27 | zip32.workspace = true 28 | 29 | tokio = { workspace = true, features = ["full"] } 30 | http = { workspace = true } 31 | hyper = { workspace = true } 32 | hyper-util = { workspace = true } 33 | tonic = { workspace = true } 34 | prost = { workspace = true } 35 | tower = { workspace = true } 36 | hex = { workspace = true } 37 | futures-util = { workspace = true } 38 | serde_json = { workspace = true } 39 | proptest = { workspace = true } 40 | 41 | # Logging 42 | tracing.workspace = true 43 | tracing-subscriber.workspace = true 44 | 45 | [build-dependencies] 46 | tonic-build = { workspace = true } 47 | 48 | [package.metadata.cargo-machete] 49 | ignored = ["prost"] 50 | -------------------------------------------------------------------------------- /libtonode-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtonode-tests" 3 | version = "0.2.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [features] 8 | default = ["chain_generic_tests", "proptests"] 9 | proptests = ["proptest"] 10 | chain_generic_tests = [] 11 | ci = ["zingolib/ci"] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | [dependencies] 15 | zingolib = { workspace = true, features = ["darkside_tests", "testutils"] } 16 | zingo_netutils = { workspace = true } 17 | zingo_common_components = { workspace = true, features = ["for_test"] } 18 | pepper-sync = { workspace = true } 19 | zingo-status = { workspace = true } 20 | 21 | zingolib_testutils = { version = "0.1.0", path = "../zingolib_testutils", features = ["test_lwd_zcashd"]} 22 | zcash_local_net = { git = "https://github.com/zingolabs/infrastructure.git", branch = "dev" } 23 | zingo_test_vectors = { git = "https://github.com/zingolabs/infrastructure.git", branch = "dev" } 24 | 25 | zip32 = { workspace = true } 26 | zcash_address = { workspace = true, features = ["test-dependencies"] } 27 | zcash_client_backend = { workspace = true } 28 | zcash_primitives = { workspace = true } 29 | zcash_protocol = { workspace = true } 30 | shardtree.workspace = true 31 | bip0039.workspace = true 32 | zebra-chain = { workspace = true } 33 | 34 | tokio = { workspace = true, features = ["full"] } 35 | json = { workspace = true } 36 | itertools = { workspace = true } 37 | proptest = { workspace = true, optional = true } 38 | tracing.workspace = true 39 | tracing-subscriber.workspace = true 40 | 41 | http = { workspace = true } 42 | 43 | [dev-dependencies] 44 | rustls.workspace = true 45 | test-log.workspace = true 46 | -------------------------------------------------------------------------------- /zingolib/src/wallet/traits.rs: -------------------------------------------------------------------------------- 1 | //! Provides unifying interfaces for transaction management across Sapling and Orchard 2 | use std::io::{self, Read, Write}; 3 | 4 | use byteorder::ReadBytesExt; 5 | use tracing::{Level, event, instrument}; 6 | 7 | /// TODO: Add Doc Comment Here! 8 | pub trait ReadableWriteable: Sized { 9 | /// TODO: Add Doc Comment Here! 10 | const VERSION: u8; 11 | 12 | /// TODO: Add Doc Comment Here! 13 | fn read(reader: R, input: ReadInput) -> io::Result; 14 | 15 | /// TODO: Add Doc Comment Here! 16 | fn write(&self, writer: W, input: WriteInput) -> io::Result<()>; 17 | 18 | /// Reads a serialized version of the struct from `reader`, and returns the 19 | /// struct version. Else, returns an `io::Error` with `io::ErrorKind::InvalidData`. 20 | #[instrument(level = "info", skip(reader))] 21 | fn get_version(mut reader: R) -> io::Result { 22 | let external_version = reader.read_u8()?; 23 | if external_version > Self::VERSION { 24 | event!( 25 | Level::ERROR, 26 | where = std::any::type_name::(), 27 | got_version = external_version, 28 | expected_version = Self::VERSION, 29 | kind = ?io::ErrorKind::InvalidData, 30 | msg = "Struct version is from a future version of zingo" 31 | ); 32 | Err(io::Error::new( 33 | io::ErrorKind::InvalidData, 34 | format!("Struct version \"{external_version}\" is from future version of zingo",), 35 | )) 36 | } else { 37 | Ok(external_version) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | LABEL Description="Rust compile env for Linux + Windows (cross)" 3 | 4 | RUN apt update 5 | RUN apt install -y build-essential mingw-w64 gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf curl vim wget git 6 | 7 | # Get Rust 8 | RUN curl https://sh.rustup.rs -sSf | bash -s -- --default-toolchain none -y 9 | ENV PATH="/root/.cargo/bin:${PATH}" 10 | 11 | RUN rustup toolchain install stable 12 | 13 | RUN rustup target add x86_64-pc-windows-gnu 14 | RUN rustup target add aarch64-unknown-linux-gnu 15 | RUN rustup target add armv7-unknown-linux-gnueabihf 16 | 17 | # Append the linker to the cargo config for Windows cross compile 18 | RUN echo "[target.x86_64-pc-windows-gnu]" >> /root/.cargo/config && \ 19 | echo "linker = '/usr/bin/x86_64-w64-mingw32-gcc'" >> /root/.cargo/config 20 | 21 | RUN echo "[target.aarch64-unknown-linux-gnu]" >> /root/.cargo/config && \ 22 | echo "linker = '/usr/bin/aarch64-linux-gnu-gcc'" >> /root/.cargo/config 23 | 24 | RUN echo "[target.armv7-unknown-linux-gnueabihf]" >> /root/.cargo/config && \ 25 | echo "linker = '/usr/bin/arm-linux-gnueabihf-gcc'" >> /root/.cargo/config 26 | 27 | ENV CC_x86_64_unknown_linux_musl="gcc" 28 | ENV CC_aarch64_unknown_linux_gnu="aarch64-linux-gnu-gcc" 29 | ENV CC_armv7_unknown_linux_gnueabhihf="arm-linux-gnueabihf-gcc" 30 | 31 | # For windows cross compilation, use a pre-build binary. Remember to set the 32 | # SODIUM_LIB_DIR for windows cross compilation 33 | RUN cd /opt && wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.17-mingw.tar.gz && \ 34 | tar xvf libsodium-1.0.17-mingw.tar.gz 35 | 36 | # Cargo fetch the dependencies so we don't download them over and over again 37 | RUN cd /tmp && git clone --depth=1 https://github.com/zingolabs/zingolib.git && \ 38 | cd zingolib && \ 39 | cargo fetch && \ 40 | cd /tmp && rm -rf zingolib 41 | 42 | # This is a bug fix for the windows cross compiler for Rust. 43 | # RUN cp /usr/x86_64-w64-mingw32/lib/crt2.o /root/.rustup/toolchains/1.49.0-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/crt2.o 44 | 45 | -------------------------------------------------------------------------------- /.github/workflows/ci-pr.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | pull_request: 7 | types: [opened, synchronize, reopened, ready_for_review] 8 | push: 9 | branches: [stable, dev] 10 | 11 | jobs: 12 | reject-trailing-whitespace: 13 | uses: zingolabs/infrastructure/.github/workflows/trailing-whitespace.yaml@20485fed7a080e381130ed8120419dc81acae641 14 | 15 | cargo-checkmate: 16 | uses: ./.github/workflows/cargo-checkmate.yaml 17 | 18 | cargo-hack-check: 19 | name: Cargo Hack Check 20 | runs-on: ubuntu-24.04 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Setup Rust toolchain 26 | uses: actions-rust-lang/setup-rust-toolchain@v1 27 | with: 28 | toolchain: ${{ inputs.toolchain }} 29 | 30 | - name: Cache cargo 31 | uses: Swatinem/rust-cache@v2 32 | 33 | - name: Install protoc 34 | run: | 35 | if ! command -v protoc; then 36 | sudo apt-get update 37 | sudo apt-get install -y protobuf-compiler 38 | fi 39 | 40 | - name: Run cargo-hack 41 | uses: zingolabs/infrastructure/.github/actions/cargo-hack@20485fed7a080e381130ed8120419dc81acae641 42 | with: 43 | hack-subcommand: check 44 | 45 | run-doc-tests: 46 | name: Run doc tests 47 | runs-on: ubuntu-24.04 48 | if: github.event.pull_request.draft == false 49 | env: 50 | RUSTFLAGS: -D warnings 51 | steps: 52 | - name: Checkout repository 53 | uses: actions/checkout@v4 54 | 55 | - uses: actions-rust-lang/setup-rust-toolchain@v1 56 | 57 | - name: Install protoc 58 | run: | 59 | if ! command -v protoc; then 60 | sudo apt-get install -y protobuf-compiler 61 | fi 62 | 63 | - name: Cargo cache 64 | uses: Swatinem/rust-cache@v2 65 | 66 | - name: Run doc tests 67 | run: cargo test --doc 68 | 69 | test: 70 | uses: ./.github/workflows/test.yaml 71 | with: 72 | nextest-flags: "-E 'not test(slow)'" 73 | -------------------------------------------------------------------------------- /pepper-sync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pepper-sync" 3 | description = "Pepper-sync is a crate providing a sync engine for the zcash network." 4 | authors = [""] 5 | version = "0.0.1" 6 | edition = "2024" 7 | repository = "https://github.com/zingolabs/zingolib" 8 | homepage = "https://github.com/zingolabs/zingolib" 9 | license = "MIT" 10 | 11 | [badges] 12 | maintenance = { status = "actively-developed" } 13 | 14 | [features] 15 | default = ["wallet_essentials"] 16 | # utility not necessary for the sync engine but essential for wallets using pepper_sync::primitives for wallet functionality 17 | wallet_essentials = ["dep:byteorder", "dep:zcash_encoding"] 18 | # removes failing calls to `GetSubTreeRoots` in darkside testing 19 | darkside_test = [] 20 | 21 | [dependencies] 22 | # Zingo 23 | zingo-memo.workspace = true 24 | zingo-status.workspace = true 25 | zingo_netutils.workspace = true 26 | 27 | # Zcash 28 | zcash_address.workspace = true 29 | zcash_encoding = { workspace = true, optional = true } 30 | zcash_client_backend = { workspace = true, features = [ 31 | "unstable-serialization", 32 | ] } 33 | zcash_primitives.workspace = true 34 | zcash_note_encryption.workspace = true 35 | zcash_keys.workspace = true 36 | zcash_protocol.workspace = true 37 | zcash_transparent.workspace = true 38 | sapling-crypto.workspace = true 39 | orchard.workspace = true 40 | incrementalmerkletree.workspace = true 41 | shardtree.workspace = true 42 | zip32.workspace = true 43 | 44 | # Transparent 45 | bip32.workspace = true 46 | 47 | # Cryptography 48 | jubjub.workspace = true 49 | subtle.workspace = true 50 | 51 | # Async 52 | futures.workspace = true 53 | tokio.workspace = true 54 | 55 | # Client 56 | tonic.workspace = true 57 | 58 | # Logging 59 | tracing.workspace = true 60 | 61 | # Metrics 62 | memuse.workspace = true 63 | 64 | # Parallel processing 65 | crossbeam-channel.workspace = true 66 | rayon.workspace = true 67 | 68 | # Error handling 69 | thiserror.workspace = true 70 | 71 | # JSON 72 | json.workspace = true 73 | 74 | # Serialization 75 | byteorder = { workspace = true, optional = true } 76 | 77 | # Documentation 78 | simple-mermaid.workspace = true 79 | -------------------------------------------------------------------------------- /zingolib/src/wallet/disk/testing.rs: -------------------------------------------------------------------------------- 1 | //! functionality for testing the save and load functions of `LightWallet`. 2 | //! do not compile testutils feature for production. 3 | 4 | use bip0039::Mnemonic; 5 | use zcash_keys::keys::{Era, UnifiedSpendingKey}; 6 | 7 | use crate::wallet::keys::unified::UnifiedKeyStore; 8 | 9 | use super::LightWallet; 10 | 11 | /// example wallets 12 | /// including from different versions of the software. 13 | pub mod examples; 14 | 15 | /// tests 16 | #[cfg(test)] 17 | pub mod tests; 18 | 19 | // test helper functions 20 | 21 | /// asserts that a fresh capability generated with the seed matches the extant capability, which also can export the seed 22 | pub async fn assert_wallet_capability_matches_seed( 23 | wallet: &LightWallet, 24 | expected_seed_phrase: String, 25 | ) { 26 | let actual_seed_phrase = wallet.mnemonic_phrase().unwrap(); 27 | assert_eq!(expected_seed_phrase, actual_seed_phrase); 28 | 29 | let expected_mnemonic = 30 | Mnemonic::::from_phrase(expected_seed_phrase).unwrap(); 31 | 32 | let expected_keys = crate::wallet::keys::unified::UnifiedKeyStore::new_from_mnemonic( 33 | &wallet.network, 34 | &expected_mnemonic, 35 | zip32::AccountId::ZERO, 36 | ) 37 | .unwrap(); 38 | 39 | // Compare USK 40 | let UnifiedKeyStore::Spend(usk) = &wallet 41 | .unified_key_store 42 | .get(&zip32::AccountId::ZERO) 43 | .unwrap() 44 | else { 45 | panic!("Expected Unified Spending Key"); 46 | }; 47 | assert_eq!( 48 | usk.to_bytes(Era::Orchard), 49 | UnifiedSpendingKey::try_from(&expected_keys) 50 | .unwrap() 51 | .to_bytes(Era::Orchard) 52 | ); 53 | } 54 | 55 | /// basically does what it says on the tin 56 | pub async fn assert_wallet_capability_contains_n_triple_pool_receivers( 57 | wallet: &LightWallet, 58 | expected_num_addresses: usize, 59 | ) { 60 | assert_eq!(wallet.unified_addresses.len(), expected_num_addresses); 61 | for addr in wallet.unified_addresses.values() { 62 | assert!(addr.orchard().is_some()); 63 | assert!(addr.sapling().is_some()); 64 | assert!(addr.transparent().is_some()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /docker-ci/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:trixie-slim AS builder 2 | 3 | WORKDIR /usr/src 4 | 5 | # Install dependencies and update ca certificates 6 | RUN apt update \ 7 | && apt install -y --no-install-recommends --no-install-suggests \ 8 | build-essential \ 9 | pkg-config \ 10 | libc6-dev \ 11 | m4 \ 12 | g++-multilib \ 13 | autoconf \ 14 | libtool \ 15 | ncurses-dev \ 16 | unzip \ 17 | git \ 18 | python3 \ 19 | python3-zmq \ 20 | zlib1g-dev \ 21 | curl \ 22 | bsdmainutils \ 23 | automake \ 24 | golang \ 25 | ca-certificates \ 26 | && update-ca-certificates 27 | 28 | # Build lightwalletd 29 | RUN git clone https://github.com/zcash/lightwalletd \ 30 | && cd lightwalletd \ 31 | && git checkout v0.4.18 \ 32 | && make 33 | 34 | # Build zcashd and fetch params 35 | RUN git clone https://github.com/zcash/zcash.git \ 36 | && cd zcash/ \ 37 | && git checkout v6.3.0 \ 38 | && ./zcutil/build.sh -j$(nproc) 39 | 40 | FROM debian:trixie-slim AS runner 41 | 42 | # Copy regtest binaries and zcash params from builder 43 | COPY --from=builder /usr/src/lightwalletd/lightwalletd /usr/bin/ 44 | COPY --from=builder /usr/src/zcash/src/zcashd /usr/bin/ 45 | COPY --from=builder /usr/src/zcash/src/zcash-cli /usr/bin/ 46 | 47 | # Install dependencies and update ca certificates 48 | RUN apt update \ 49 | && apt install -y --no-install-recommends --no-install-suggests \ 50 | python3 \ 51 | git \ 52 | gcc \ 53 | curl \ 54 | pkg-config \ 55 | libssl-dev \ 56 | build-essential \ 57 | protobuf-compiler \ 58 | libclang-dev \ 59 | ca-certificates \ 60 | && update-ca-certificates 61 | 62 | # Install rust and cargo tarpaulin 63 | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 64 | ENV HOME=/root 65 | ENV CARGO_HOME=$HOME/.cargo 66 | ENV RUSTUP_HOME=$HOME/.rustup 67 | ENV PATH=$PATH:$CARGO_HOME/bin 68 | RUN rustup toolchain install stable --profile minimal --component clippy \ 69 | && rustup update \ 70 | && rustup default stable 71 | RUN cargo install cargo-nextest --locked 72 | RUN cargo install cargo-tarpaulin 73 | 74 | # Apt clean up 75 | RUN apt autoremove -y \ 76 | && apt clean \ 77 | && rm -rf /var/lib/apt/lists/* 78 | 79 | -------------------------------------------------------------------------------- /zingolib/src/testutils/chain_generics/networked.rs: -------------------------------------------------------------------------------- 1 | //! implementation of conduct chain for live chains 2 | 3 | use http::Uri; 4 | use zcash_protocol::consensus::BlockHeight; 5 | 6 | use crate::{config::DEFAULT_TESTNET_LIGHTWALLETD_SERVER, lightclient::LightClient}; 7 | 8 | use super::conduct_chain::ConductChain; 9 | 10 | /// this is essentially a placeholder. 11 | /// allows using existing `ChainGeneric` functions with `TestNet` wallets 12 | pub struct NetworkedTestEnvironment { 13 | indexer_uri: Uri, 14 | latest_known_server_height: Option, 15 | } 16 | 17 | impl NetworkedTestEnvironment { 18 | async fn update_server_height(&mut self) { 19 | let latest = crate::grpc_connector::get_latest_block(self.lightserver_uri().unwrap()) 20 | .await 21 | .unwrap() 22 | .height as u32; 23 | self.latest_known_server_height = Some(BlockHeight::from(latest)); 24 | crate::testutils::timestamped_test_log( 25 | format!("Networked Test Chain is now at height {latest}").as_str(), 26 | ); 27 | } 28 | } 29 | 30 | impl ConductChain for NetworkedTestEnvironment { 31 | async fn setup() -> Self { 32 | Self { 33 | indexer_uri: ::from_str(DEFAULT_TESTNET_LIGHTWALLETD_SERVER) 34 | .unwrap(), 35 | latest_known_server_height: None, 36 | } 37 | } 38 | 39 | async fn create_faucet(&mut self) -> LightClient { 40 | unimplemented!() 41 | } 42 | 43 | async fn zingo_config(&mut self) -> crate::config::ZingoConfig { 44 | unimplemented!() 45 | } 46 | 47 | async fn increase_chain_height(&mut self) { 48 | let before_height = self.latest_known_server_height; 49 | // loop until the server height increases 50 | loop { 51 | tokio::time::sleep(std::time::Duration::from_secs(1)).await; 52 | self.update_server_height().await; 53 | if self.latest_known_server_height != before_height { 54 | break; 55 | } 56 | } 57 | } 58 | 59 | fn lightserver_uri(&self) -> Option { 60 | Some(self.indexer_uri.clone()) 61 | } 62 | 63 | fn confirmation_patience_blocks(&self) -> usize { 64 | 10 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | nextest-flags: 7 | required: false 8 | type: string 9 | 10 | env: 11 | NEXTEST-FLAGS: ${{ inputs.nextest-flags }} 12 | 13 | jobs: 14 | build-test-artifacts: 15 | name: Build test artifacts 16 | container: 17 | image: zingodevops/ci-build:009 18 | runs-on: ubuntu-24.04 19 | if: github.event.pull_request.draft == false 20 | env: 21 | RUSTFLAGS: -D warnings 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | 26 | - uses: actions-rust-lang/setup-rust-toolchain@v1 27 | 28 | - name: Install nextest 29 | uses: taiki-e/install-action@v2 30 | with: 31 | tool: nextest@0.9.78 32 | 33 | - name: Cargo cache 34 | uses: Swatinem/rust-cache@v2 35 | 36 | - name: Build and archive tests 37 | run: cargo nextest archive --verbose --workspace --archive-file nextest-archive.tar.zst 38 | 39 | - name: Upload archive 40 | uses: actions/upload-artifact@v4 41 | with: 42 | name: nextest-archive 43 | path: nextest-archive.tar.zst 44 | overwrite: true 45 | 46 | run-tests: 47 | name: Run tests 48 | runs-on: ubuntu-24.04 49 | if: github.event.pull_request.draft == false 50 | needs: build-test-artifacts 51 | env: 52 | RUSTFLAGS: -D warnings 53 | container: 54 | image: zingodevops/ci-build:009 55 | options: --security-opt seccomp=unconfined 56 | strategy: 57 | matrix: 58 | partition: [1, 2, 3, 4, 5, 6, 7, 8] 59 | steps: 60 | - name: Checkout repository 61 | uses: actions/checkout@v4 62 | 63 | - name: create binaries dir 64 | run: mkdir -p ./test_binaries/bins 65 | 66 | - name: Symlink lightwalletd and zcash binaries 67 | run: ln -s /usr/bin/lightwalletd /usr/bin/zcashd /usr/bin/zcash-cli ./test_binaries/bins/ 68 | 69 | - name: Download archive 70 | uses: actions/download-artifact@v4 71 | with: 72 | name: nextest-archive 73 | 74 | - name: Run tests 75 | run: | 76 | cargo nextest run --verbose --profile ci --retries 2 --archive-file nextest-archive.tar.zst \ 77 | --workspace-remap ./ --partition count:${{ matrix.partition }}/8 ${{ env.NEXTEST-FLAGS }} 78 | -------------------------------------------------------------------------------- /zingolib/src/testutils/fee_tables.rs: -------------------------------------------------------------------------------- 1 | //! zip317 specifications 2 | 3 | use std::cmp::max; 4 | 5 | use zcash_primitives::transaction::fees::zip317::{GRACE_ACTIONS, MARGINAL_FEE}; 6 | use zcash_protocol::{PoolType, ShieldedProtocol}; 7 | 8 | /// estimates a fee based on the zip317 protocol rules 9 | /// 10 | #[must_use] 11 | pub fn one_to_one( 12 | source_protocol: Option, 13 | target_pool: PoolType, 14 | mut change: bool, 15 | ) -> u64 { 16 | if source_protocol.is_none() && target_pool == PoolType::TRANSPARENT { 17 | change = false; 18 | } 19 | 20 | let transparent_inputs = 0; 21 | let mut transparent_outputs = 0; 22 | let mut sapling_inputs = 0; 23 | let mut sapling_outputs = 0; 24 | let mut orchard_inputs = 0; 25 | let mut orchard_outputs = 0; 26 | match source_protocol { 27 | Some(ShieldedProtocol::Sapling) => sapling_inputs += 1, 28 | Some(ShieldedProtocol::Orchard) => orchard_inputs += 1, 29 | _ => {} 30 | } 31 | match target_pool { 32 | PoolType::Transparent => transparent_outputs += 1, 33 | PoolType::Shielded(ShieldedProtocol::Sapling) => sapling_outputs += 1, 34 | PoolType::Shielded(ShieldedProtocol::Orchard) => orchard_outputs += 1, 35 | } 36 | if change { 37 | if orchard_inputs + orchard_outputs == 0 { 38 | // sapling change 39 | sapling_outputs += 1; 40 | } else { 41 | //orchard change 42 | orchard_outputs += 1; 43 | } 44 | } 45 | if sapling_outputs > 0 || sapling_inputs > 0 { 46 | sapling_outputs = max(sapling_outputs, 2); //MIN_SHIELDED_OUTPUTS; 47 | } 48 | let mut orchard_actions = max(orchard_inputs, orchard_outputs); 49 | if orchard_actions > 0 { 50 | orchard_actions = max(orchard_actions, 2); //MIN_SHIELDED_OUTPUTS; 51 | } 52 | let contribution_transparent = max(transparent_outputs, transparent_inputs); 53 | let contribution_sapling = max(sapling_outputs, sapling_inputs); 54 | let contribution_orchard = orchard_actions; 55 | let total_fee = MARGINAL_FEE 56 | * max( 57 | contribution_transparent + contribution_sapling + contribution_orchard, 58 | GRACE_ACTIONS, 59 | ); 60 | total_fee 61 | .expect("actions expected to be in numerical range") 62 | .into_u64() 63 | } 64 | -------------------------------------------------------------------------------- /zingolib/src/lightclient/error.rs: -------------------------------------------------------------------------------- 1 | //! Errors assoicated with [`crate::lightclient::LightClient`]. 2 | 3 | use std::convert::Infallible; 4 | 5 | use pepper_sync::error::SyncModeError; 6 | 7 | use crate::wallet::{ 8 | error::{ 9 | CalculateTransactionError, ProposeSendError, ProposeShieldError, TransmissionError, 10 | WalletError, 11 | }, 12 | output::OutputRef, 13 | }; 14 | 15 | #[derive(Debug, thiserror::Error)] 16 | pub enum LightClientError { 17 | /// Sync not running. 18 | #[error("No sync handle. Sync is not running.")] 19 | SyncNotRunning, 20 | /// Sync error. 21 | #[error("Sync error. {0}")] 22 | SyncError(#[from] pepper_sync::error::SyncError), 23 | /// Sync mode error. 24 | #[error("sync mode error. {0}")] 25 | SyncModeError(#[from] SyncModeError), 26 | /// gPRC client error 27 | #[error("gRPC client error. {0}")] 28 | ClientError(#[from] zingo_netutils::GetClientError), 29 | /// File error 30 | #[error("File error. {0}")] 31 | FileError(#[from] std::io::Error), 32 | /// Wallet error 33 | #[error("Wallet error. {0}")] 34 | WalletError(#[from] WalletError), 35 | /// Tor client error. 36 | #[error("Tor client error. {0}")] 37 | TorClientError(#[from] zcash_client_backend::tor::Error), 38 | } 39 | 40 | #[allow(missing_docs)] // error types document themselves 41 | #[derive(Debug, thiserror::Error)] 42 | pub enum SendError { 43 | #[error("The sending transaction could not be calculated. {0}")] 44 | CalculateSendError(CalculateTransactionError), 45 | #[error("The shieldng transaction could not be calculated. {0}")] 46 | CalculateShieldError(CalculateTransactionError), 47 | #[error("Transmission failed. {0}")] 48 | TransmissionError(#[from] TransmissionError), 49 | #[error("No proposal found.")] 50 | NoStoredProposal, 51 | } 52 | 53 | #[allow(missing_docs)] // error types document themselves 54 | #[derive(Debug, thiserror::Error)] 55 | pub enum QuickSendError { 56 | #[error("proposal failed. {0}")] 57 | ProposalError(#[from] ProposeSendError), 58 | #[error("send failed. {0}")] 59 | SendError(#[from] SendError), 60 | } 61 | 62 | #[allow(missing_docs)] // error types document themselves 63 | #[derive(Debug, thiserror::Error)] 64 | pub enum QuickShieldError { 65 | #[error("proposal failed. {0}")] 66 | ProposalError(#[from] ProposeShieldError), 67 | #[error("send failed. {0}")] 68 | SendError(#[from] SendError), 69 | } 70 | -------------------------------------------------------------------------------- /libtonode-tests/src/chain_generics.rs: -------------------------------------------------------------------------------- 1 | //! libtonode tests use zcashd regtest mode to mock a chain 2 | 3 | use zcash_local_net::LocalNet; 4 | use zcash_local_net::indexer::Indexer; 5 | use zcash_local_net::network::localhost_uri; 6 | use zcash_local_net::validator::Validator; 7 | 8 | use zingolib::lightclient::LightClient; 9 | use zingolib::testutils::chain_generics::conduct_chain::ConductChain; 10 | use zingolib::testutils::timestamped_test_log; 11 | 12 | use zingolib_testutils::scenarios::ClientBuilder; 13 | use zingolib_testutils::scenarios::custom_clients_default; 14 | use zingolib_testutils::scenarios::network_combo::{DefaultIndexer, DefaultValidator}; 15 | 16 | /// includes utilities for connecting to zcashd regtest 17 | pub struct LibtonodeEnvironment { 18 | /// Local network 19 | pub local_net: LocalNet, 20 | /// Client builder 21 | pub client_builder: ClientBuilder, 22 | } 23 | 24 | /// known issues include --slow 25 | /// these tests cannot portray the full range of network weather. 26 | impl ConductChain for LibtonodeEnvironment { 27 | async fn setup() -> Self { 28 | timestamped_test_log("starting mock libtonode network"); 29 | let (local_net, client_builder) = custom_clients_default().await; 30 | 31 | LibtonodeEnvironment { 32 | local_net, 33 | client_builder, 34 | } 35 | } 36 | 37 | async fn create_faucet(&mut self) -> LightClient { 38 | self.client_builder.build_faucet( 39 | false, 40 | self.local_net.validator().get_activation_heights().await, 41 | ) 42 | } 43 | 44 | async fn zingo_config(&mut self) -> zingolib::config::ZingoConfig { 45 | self.client_builder.make_unique_data_dir_and_load_config( 46 | self.local_net.validator().get_activation_heights().await, 47 | ) 48 | } 49 | 50 | async fn increase_chain_height(&mut self) { 51 | let start_height = self.local_net.validator().get_chain_height().await; 52 | self.local_net 53 | .validator() 54 | .generate_blocks(1) 55 | .await 56 | .expect("Called for side effect, failed!"); 57 | assert_eq!( 58 | self.local_net.validator().get_chain_height().await, 59 | start_height + 1 60 | ); 61 | } 62 | 63 | fn lightserver_uri(&self) -> Option { 64 | Some(localhost_uri(self.local_net.indexer().listen_port())) 65 | } 66 | 67 | fn confirmation_patience_blocks(&self) -> usize { 68 | 1 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /zingolib/src/wallet/utils.rs: -------------------------------------------------------------------------------- 1 | //! TODO: Add Mod Description Here! 2 | use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; 3 | use std::io::{self, Read, Write}; 4 | use zcash_primitives::{memo::MemoBytes, transaction::TxId}; 5 | 6 | /// TODO: Add Doc Comment Here! 7 | pub fn read_string(mut reader: R) -> io::Result { 8 | // Strings are written as len + bytes 9 | let str_len = reader.read_u64::()?; 10 | let mut str_bytes = vec![0; str_len as usize]; 11 | reader.read_exact(&mut str_bytes)?; 12 | 13 | let str = String::from_utf8(str_bytes) 14 | .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?; 15 | 16 | Ok(str) 17 | } 18 | 19 | /// TODO: Add Doc Comment Here! 20 | pub fn write_string(mut writer: W, s: &String) -> io::Result<()> { 21 | // Strings are written as len + utf8 22 | writer.write_u64::(s.len() as u64)?; 23 | writer.write_all(s.as_bytes()) 24 | } 25 | 26 | /// Interpret a string or hex-encoded memo, and return a Memo object 27 | pub fn interpret_memo_string(memo_str: String) -> Result { 28 | // If the string starts with an "0x", and contains only hex chars ([a-f0-9]+) then 29 | // interpret it as a hex 30 | let s_bytes = if memo_str.to_lowercase().starts_with("0x") { 31 | match hex::decode(&memo_str[2..memo_str.len()]) { 32 | Ok(data) => data, 33 | Err(_) => Vec::from(memo_str.as_bytes()), 34 | } 35 | } else { 36 | Vec::from(memo_str.as_bytes()) 37 | }; 38 | 39 | MemoBytes::from_bytes(&s_bytes) 40 | .map_err(|_| format!("Error creating output. Memo '{memo_str:?}' is too long")) 41 | } 42 | 43 | /// TODO: Add Doc Comment Here! 44 | #[must_use] 45 | pub fn txid_from_slice(txid: &[u8]) -> TxId { 46 | let mut txid_bytes = [0u8; 32]; 47 | txid_bytes.copy_from_slice(txid); 48 | TxId::from_bytes(txid_bytes) 49 | } 50 | 51 | /// Returns the downloaded Sapling parameters as bytes. 52 | pub(crate) fn read_sapling_params() -> Result<(Vec, Vec), String> { 53 | use crate::SaplingParams; 54 | let mut sapling_output = vec![]; 55 | sapling_output.extend_from_slice( 56 | SaplingParams::get("sapling-output.params") 57 | .unwrap() 58 | .data 59 | .as_ref(), 60 | ); 61 | 62 | let mut sapling_spend = vec![]; 63 | sapling_spend.extend_from_slice( 64 | SaplingParams::get("sapling-spend.params") 65 | .unwrap() 66 | .data 67 | .as_ref(), 68 | ); 69 | Ok((sapling_output, sapling_spend)) 70 | } 71 | -------------------------------------------------------------------------------- /zingolib/build.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | use std::{env, fs::File, path::Path, process::Command}; 3 | 4 | fn git_description() { 5 | let _fetch = Command::new("git") 6 | .args(["fetch", "--tags", "https://github.com/zingolabs/zingolib"]) 7 | .output() 8 | .expect("Failed to execute git command"); 9 | let output = Command::new("git") 10 | .args(["describe", "--dirty", "--always", "--long"]) 11 | .output() 12 | .expect("Failed to execute git command"); 13 | 14 | eprintln!("Git command output: {output:?}"); 15 | println!("Git command output: {output:?}"); 16 | 17 | let git_description = String::from_utf8(output.stdout) 18 | .unwrap() 19 | .trim_end() 20 | .to_string(); 21 | 22 | // Write the git description to a file which will be included in the crate 23 | let out_dir = env::var("OUT_DIR").unwrap(); 24 | let dest_path = Path::new(&out_dir).join("git_description.rs"); 25 | let mut f = File::create(dest_path).unwrap(); 26 | writeln!( 27 | f, 28 | "/// The result of running 'git describe' at compile time:\n\ 29 | /// The most recent tag name, the number\n\ 30 | /// of commits above it, and the hash of\n\ 31 | /// the most recent commit\n\ 32 | pub fn git_description() -> &'static str {{\"{git_description}\"}}" 33 | ) 34 | .unwrap(); 35 | } 36 | 37 | /// Checks if zcash params are available and downloads them if not. 38 | /// Also copies them to an internal location for use by mobile platforms. 39 | fn get_zcash_params() { 40 | println!("Checking if params are available..."); 41 | 42 | let params_path = match zcash_proofs::download_sapling_parameters(Some(400)) { 43 | Ok(p) => { 44 | println!("Params downloaded!"); 45 | println!("Spend path: {}", p.spend.to_str().unwrap()); 46 | println!("Output path: {}", p.output.to_str().unwrap()); 47 | p 48 | } 49 | Err(e) => { 50 | println!("Error downloading params: {e}"); 51 | panic!(); 52 | } 53 | }; 54 | 55 | // Copy the params to the internal location. 56 | let internal_params_path = Path::new("zcash-params"); 57 | std::fs::create_dir_all(internal_params_path).unwrap(); 58 | std::fs::copy( 59 | params_path.spend, 60 | internal_params_path.join("sapling-spend.params"), 61 | ) 62 | .unwrap(); 63 | 64 | std::fs::copy( 65 | params_path.output, 66 | internal_params_path.join("sapling-output.params"), 67 | ) 68 | .unwrap(); 69 | } 70 | 71 | fn main() -> Result<(), Box> { 72 | get_zcash_params(); 73 | git_description(); 74 | Ok(()) 75 | } 76 | -------------------------------------------------------------------------------- /zingolib/src/data.rs: -------------------------------------------------------------------------------- 1 | //! This is a mod for data structs that will be used across all sections of zingolib. 2 | 3 | pub mod proposal; 4 | 5 | /// Return type for fns that poll the status of task handles. 6 | pub enum PollReport { 7 | /// Task has not been launched. 8 | NoHandle, 9 | /// Task is not complete. 10 | NotReady, 11 | /// Task has completed successfully or failed. 12 | Ready(Result), 13 | } 14 | 15 | /// transforming data related to the destination of a send. 16 | pub mod receivers { 17 | use zcash_address::ZcashAddress; 18 | use zcash_client_backend::zip321::Payment; 19 | use zcash_client_backend::zip321::TransactionRequest; 20 | use zcash_client_backend::zip321::Zip321Error; 21 | use zcash_primitives::memo::MemoBytes; 22 | use zcash_protocol::value::Zatoshis; 23 | 24 | /// A list of Receivers 25 | pub type Receivers = Vec; 26 | 27 | /// The superficial representation of the the consumer's intended receiver 28 | #[derive(Clone, Debug, PartialEq)] 29 | pub struct Receiver { 30 | pub recipient_address: ZcashAddress, 31 | pub amount: Zatoshis, 32 | pub memo: Option, 33 | } 34 | impl Receiver { 35 | /// Create a new Receiver 36 | pub(crate) fn new( 37 | recipient_address: ZcashAddress, 38 | amount: Zatoshis, 39 | memo: Option, 40 | ) -> Self { 41 | Self { 42 | recipient_address, 43 | amount, 44 | memo, 45 | } 46 | } 47 | } 48 | impl From for Payment { 49 | fn from(receiver: Receiver) -> Self { 50 | Payment::new( 51 | receiver.recipient_address, 52 | receiver.amount, 53 | receiver.memo, 54 | None, 55 | None, 56 | vec![], 57 | ) 58 | .expect("memo compatibility checked in 'parse_send_args'") 59 | } 60 | } 61 | 62 | /// Creates a [`zcash_client_backend::zip321::TransactionRequest`] from receivers. 63 | /// Note this fn is called to calculate the `spendable_shielded` balance 64 | /// shielding and TEX should be handled mutually exclusively 65 | pub fn transaction_request_from_receivers( 66 | receivers: Receivers, 67 | ) -> Result { 68 | // If this succeeds: 69 | // * zingolib learns whether there is a TEX address 70 | // * if there's a TEX address it's readable. 71 | let payments = receivers 72 | .into_iter() 73 | .map(std::convert::Into::into) 74 | .collect(); 75 | 76 | TransactionRequest::new(payments) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /libtonode-tests/tests/shield_transparent.rs: -------------------------------------------------------------------------------- 1 | use zingolib::get_base_address_macro; 2 | use zingolib::testutils::lightclient::from_inputs; 3 | use zingolib_testutils::scenarios::{ 4 | faucet_recipient_default, increase_height_and_wait_for_client, 5 | }; 6 | 7 | #[tokio::test] 8 | #[ignore] 9 | async fn shield_transparent() { 10 | let (local_net, mut faucet, mut recipient) = faucet_recipient_default().await; 11 | let transparent_funds = 100_000; 12 | 13 | tracing::info!( 14 | "scenario initial 15 | faucet: {} 16 | recipient: {}", 17 | &faucet 18 | .account_balance(zip32::AccountId::ZERO) 19 | .await 20 | .unwrap(), 21 | &recipient 22 | .account_balance(zip32::AccountId::ZERO) 23 | .await 24 | .unwrap(), 25 | ); 26 | let proposal = from_inputs::quick_send( 27 | &mut faucet, 28 | vec![( 29 | &get_base_address_macro!(recipient, "transparent"), 30 | transparent_funds, 31 | None, 32 | )], 33 | ) 34 | .await 35 | .unwrap(); 36 | 37 | tracing::info!( 38 | "sent to recipient 39 | faucet: {} 40 | recipient: {}", 41 | &faucet 42 | .account_balance(zip32::AccountId::ZERO) 43 | .await 44 | .unwrap(), 45 | &recipient 46 | .account_balance(zip32::AccountId::ZERO) 47 | .await 48 | .unwrap(), 49 | ); 50 | increase_height_and_wait_for_client(&local_net, &mut recipient, 1) 51 | .await 52 | .unwrap(); 53 | 54 | tracing::info!( 55 | "synced recipient 56 | faucet: {} 57 | recipient: {}", 58 | &faucet 59 | .account_balance(zip32::AccountId::ZERO) 60 | .await 61 | .unwrap(), 62 | &recipient 63 | .account_balance(zip32::AccountId::ZERO) 64 | .await 65 | .unwrap(), 66 | ); 67 | 68 | let shielding_proposal = recipient 69 | .propose_shield(zip32::AccountId::ZERO) 70 | .await 71 | .unwrap(); 72 | 73 | tracing::info!("Initial proposal {proposal:?}"); 74 | tracing::info!("Shielding proposal {shielding_proposal:?}"); 75 | 76 | recipient.send_stored_proposal(true).await.unwrap(); 77 | increase_height_and_wait_for_client(&local_net, &mut recipient, 1) 78 | .await 79 | .unwrap(); 80 | 81 | tracing::info!( 82 | "post-shield recipient 83 | faucet: {} 84 | recipient: {}", 85 | &faucet 86 | .account_balance(zip32::AccountId::ZERO) 87 | .await 88 | .unwrap(), 89 | &recipient 90 | .account_balance(zip32::AccountId::ZERO) 91 | .await 92 | .unwrap(), 93 | ); 94 | } 95 | -------------------------------------------------------------------------------- /zingo-cli/src/commands/error.rs: -------------------------------------------------------------------------------- 1 | //! Errors associated with the commands interface 2 | 3 | use std::fmt; 4 | 5 | #[derive(Debug)] 6 | pub(crate) enum CommandError { 7 | ArgsNotJson(json::Error), 8 | ArgNotJsonOrValidAddress, 9 | SingleArgNotJsonArray(String), 10 | JsonArrayNotObj(String), 11 | EmptyJsonArray, 12 | ParseIntFromString(std::num::ParseIntError), 13 | UnexpectedType(String), 14 | MissingKey(String), 15 | InvalidArguments, 16 | IncompatibleMemo, 17 | InvalidMemo(String), 18 | NonJsonNumberForAmount(String), 19 | ConversionFailed(zingolib::utils::error::ConversionError), 20 | MissingZenniesForZingoFlag, 21 | ZenniesFlagNonBool(String), 22 | } 23 | 24 | impl fmt::Display for CommandError { 25 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 26 | use CommandError::{ 27 | ArgNotJsonOrValidAddress, ArgsNotJson, ConversionFailed, EmptyJsonArray, 28 | IncompatibleMemo, InvalidArguments, InvalidMemo, JsonArrayNotObj, MissingKey, 29 | MissingZenniesForZingoFlag, NonJsonNumberForAmount, ParseIntFromString, 30 | SingleArgNotJsonArray, UnexpectedType, ZenniesFlagNonBool, 31 | }; 32 | 33 | match self { 34 | ArgsNotJson(e) => write!(f, "failed to parse argument. {e}"), 35 | ArgNotJsonOrValidAddress => write!( 36 | f, 37 | "argument cannot be converted to a valid address or parsed as json." 38 | ), 39 | SingleArgNotJsonArray(e) => { 40 | write!(f, "argument cannot be parsed to a json array. {e}") 41 | } 42 | JsonArrayNotObj(e) => { 43 | write!(f, "argument cannot be a json array. {e}") 44 | } 45 | ZenniesFlagNonBool(e) => { 46 | write!(f, "Argument must be a JSON bool. {e}") 47 | } 48 | EmptyJsonArray => write!(f, "json array has no arguments"), 49 | ParseIntFromString(e) => write!(f, "failed to parse argument. {e}"), 50 | UnexpectedType(e) => write!(f, "arguments cannot be parsed to expected type. {e}"), 51 | MissingKey(key) => write!(f, "json array is missing \"{key}\" key."), 52 | InvalidArguments => write!(f, "arguments given are invalid."), 53 | IncompatibleMemo => { 54 | write!(f, "memo's cannot be sent to transparent addresses.") 55 | } 56 | InvalidMemo(e) => write!(f, "failed to interpret memo. {e}"), 57 | NonJsonNumberForAmount(e) => write!(f, "invalid argument. expected a number. {e}"), 58 | ConversionFailed(e) => write!(f, "conversion failed. {e}"), 59 | MissingZenniesForZingoFlag => { 60 | write!(f, "Zennies flag must be set to 'true' or 'false'.") 61 | } 62 | } 63 | } 64 | } 65 | 66 | impl std::error::Error for CommandError {} 67 | -------------------------------------------------------------------------------- /zingolib/src/grpc_connector.rs: -------------------------------------------------------------------------------- 1 | //! TODO: Add Mod Description Here! 2 | 3 | use tonic::Request; 4 | 5 | use zcash_client_backend::proto::service::{BlockId, ChainSpec, Empty, LightdInfo, RawTransaction}; 6 | 7 | #[cfg(feature = "testutils")] 8 | use zcash_client_backend::proto::service::TreeState; 9 | 10 | /// Get server info. 11 | pub async fn get_info(uri: http::Uri) -> Result { 12 | let mut client = crate::grpc_client::get_zcb_client(uri.clone()) 13 | .await 14 | .map_err(|e| format!("Error getting client: {e:?}"))?; 15 | 16 | let request = Request::new(Empty {}); 17 | 18 | let response = client 19 | .get_lightd_info(request) 20 | .await 21 | .map_err(|e| format!("Error with get_lightd_info response at {uri}: {e:?}"))?; 22 | Ok(response.into_inner()) 23 | } 24 | 25 | /// TODO: Add Doc Comment Here! 26 | #[cfg(feature = "testutils")] 27 | pub async fn get_trees(uri: http::Uri, height: u64) -> Result { 28 | let mut client = crate::grpc_client::get_zcb_client(uri.clone()) 29 | .await 30 | .map_err(|e| format!("Error getting client: {e:?}"))?; 31 | 32 | let b = BlockId { 33 | height, 34 | hash: vec![], 35 | }; 36 | let response = client 37 | .get_tree_state(Request::new(b)) 38 | .await 39 | .map_err(|e| format!("Error with get_tree_state response at {uri}: {e:?}"))?; 40 | 41 | Ok(response.into_inner()) 42 | } 43 | 44 | /// `get_latest_block` GRPC call 45 | pub async fn get_latest_block(uri: http::Uri) -> Result { 46 | let mut client = crate::grpc_client::get_zcb_client(uri.clone()) 47 | .await 48 | .map_err(|e| format!("Error getting client: {e:?}"))?; 49 | 50 | let request = Request::new(ChainSpec {}); 51 | 52 | let response = client 53 | .get_latest_block(request) 54 | .await 55 | .map_err(|e| format!("Error with get_latest_block response at {uri}: {e:?}"))?; 56 | 57 | Ok(response.into_inner()) 58 | } 59 | 60 | /// TODO: Add Doc Comment Here! 61 | pub(crate) async fn send_transaction( 62 | uri: http::Uri, 63 | transaction_bytes: Box<[u8]>, 64 | ) -> Result { 65 | let mut client = crate::grpc_client::get_zcb_client(uri) 66 | .await 67 | .map_err(|e| format!("Error getting client: {e:?}"))?; 68 | 69 | let request = Request::new(RawTransaction { 70 | data: transaction_bytes.to_vec(), 71 | height: 0, 72 | }); 73 | 74 | let response = client 75 | .send_transaction(request) 76 | .await 77 | .map_err(|e| format!("Send Error: {e}"))?; 78 | 79 | let sendresponse = response.into_inner(); 80 | if sendresponse.error_code == 0 { 81 | let mut transaction_id = sendresponse.error_message; 82 | if transaction_id.starts_with('\"') && transaction_id.ends_with('\"') { 83 | transaction_id = transaction_id[1..transaction_id.len() - 1].to_string(); 84 | } 85 | 86 | Ok(transaction_id) 87 | } else { 88 | Err(format!("Error: {sendresponse:?}")) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /.github/workflows/ci-nightly.yaml: -------------------------------------------------------------------------------- 1 | name: CI (Nightly) 2 | 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | schedule: 7 | - cron: '30 3 * * *' 8 | 9 | jobs: 10 | 11 | pr-checks: 12 | needs: [ create-cache-key ] 13 | uses: ./.github/workflows/ci-pr.yaml 14 | 15 | cargo-hack-build: 16 | name: Cargo Hack Build 17 | runs-on: ubuntu-24.04 18 | if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }} 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Setup Rust toolchain 24 | uses: actions-rust-lang/setup-rust-toolchain@v1 25 | with: 26 | toolchain: ${{ inputs.toolchain }} 27 | 28 | - name: Cache cargo 29 | uses: Swatinem/rust-cache@v2 30 | 31 | - name: Install protoc 32 | run: | 33 | if ! command -v protoc; then 34 | sudo apt-get update 35 | sudo apt-get install -y protobuf-compiler 36 | fi 37 | 38 | - name: Run cargo-hack 39 | uses: zingolabs/infrastructure/.github/actions/cargo-hack@20485fed7a080e381130ed8120419dc81acae641 40 | with: 41 | hack-subcommand: build 42 | 43 | create-timestamp: 44 | uses: zingolabs/zingo-mobile/.github/workflows/create-timestamp.yaml@dev 45 | 46 | create-cache-key: 47 | uses: zingolabs/zingo-mobile/.github/workflows/create-cache-key.yaml@dev 48 | 49 | android-build: 50 | strategy: 51 | matrix: 52 | arch: [ x86_64, x86, arm64-v8a, armeabi-v7a ] 53 | fail-fast: false 54 | uses: zingolabs/zingo-mobile/.github/workflows/android-build.yaml@dev 55 | needs: create-cache-key 56 | with: 57 | cache-key: ${{ needs.create-cache-key.outputs.cache-key }} 58 | arch: ${{ matrix.arch }} 59 | 60 | android-ubuntu-integration-test-ci: 61 | strategy: 62 | matrix: 63 | config: 64 | - { abi: x86_64, api-level: 34, target: default } 65 | - { abi: x86, api-level: 29, target: default } 66 | fail-fast: false 67 | uses: zingolabs/zingo-mobile/.github/workflows/android-ubuntu-integration-test-ci.yaml@dev 68 | needs: [create-timestamp, create-cache-key, android-build] 69 | with: 70 | timestamp: ${{ needs.create-timestamp.outputs.timestamp }} 71 | cache-key: ${{ needs.create-cache-key.outputs.cache-key }} 72 | abi: ${{ matrix.config.abi }} 73 | api-level: ${{ matrix.config['api-level'] }} 74 | target: ${{ matrix.config.target }} 75 | 76 | ios-build: 77 | strategy: 78 | fail-fast: false 79 | uses: zingolabs/zingo-mobile/.github/workflows/ios-build.yaml@dev 80 | needs: create-cache-key 81 | with: 82 | cache-key: ${{ needs.create-cache-key.outputs.cache-key }} 83 | 84 | ios-integration-test: 85 | strategy: 86 | fail-fast: false 87 | uses: zingolabs/zingo-mobile/.github/workflows/ios-integration-test.yaml@dev 88 | needs: [ create-cache-key, ios-build ] 89 | with: 90 | cache-key: ${{ needs.create-cache-key.outputs.cache-key }} 91 | 92 | test: 93 | uses: ./.github/workflows/test.yaml 94 | -------------------------------------------------------------------------------- /zingolib/src/data/proposal.rs: -------------------------------------------------------------------------------- 1 | //! The types of transaction Proposal that Zingo! uses. 2 | 3 | use std::convert::Infallible; 4 | 5 | use zcash_client_backend::proposal::Proposal; 6 | use zcash_primitives::transaction::fees::zip317; 7 | use zcash_protocol::value::{BalanceError, Zatoshis}; 8 | 9 | use crate::wallet::output::OutputRef; 10 | 11 | /// A proposed send to addresses. 12 | /// Identifies the notes to spend by txid, pool, and `output_index`. 13 | /// This type alias, specifies the ZIP317 "Proportional Transfer Fee Mechanism" structure 14 | /// 15 | /// as the fee structure for a transaction series. This innovation was created in response 16 | /// "Binance Constraint" that t-addresses that only receive from t-addresses be supported. 17 | /// 18 | pub(crate) type ProportionalFeeProposal = Proposal; 19 | 20 | /// A proposed shielding. 21 | /// The `zcash_client_backend` Proposal type exposes a "`NoteRef`" generic 22 | /// parameter to track Shielded inputs to the proposal these are 23 | /// disallowed in Zingo `ShieldedProposals` 24 | pub(crate) type ProportionalFeeShieldProposal = Proposal; 25 | 26 | /// The `LightClient` holds one proposal at a time while the user decides whether to accept the fee. 27 | #[derive(Debug, Clone)] 28 | pub(crate) enum ZingoProposal { 29 | /// Send proposal. 30 | Send { 31 | proposal: ProportionalFeeProposal, 32 | sending_account: zip32::AccountId, 33 | }, 34 | /// Shield proposal. 35 | Shield { 36 | proposal: ProportionalFeeShieldProposal, 37 | shielding_account: zip32::AccountId, 38 | }, 39 | } 40 | 41 | /// total sum of all transaction request payment amounts in a proposal 42 | pub fn total_payment_amount(proposal: &ProportionalFeeProposal) -> Result { 43 | proposal 44 | .steps() 45 | .iter() 46 | .map(zcash_client_backend::proposal::Step::transaction_request) 47 | .try_fold(Zatoshis::ZERO, |acc, request| { 48 | (acc + request.total()?).ok_or(BalanceError::Overflow) 49 | }) 50 | } 51 | 52 | /// total sum of all fees in a proposal 53 | pub fn total_fee(proposal: &ProportionalFeeProposal) -> Result { 54 | proposal 55 | .steps() 56 | .iter() 57 | .map(|step| step.balance().fee_required()) 58 | .try_fold(Zatoshis::ZERO, |acc, fee| { 59 | (acc + fee).ok_or(BalanceError::Overflow) 60 | }) 61 | } 62 | 63 | #[cfg(test)] 64 | mod tests { 65 | use zcash_protocol::value::Zatoshis; 66 | 67 | use crate::mocks; 68 | 69 | #[test] 70 | fn total_payment_amount() { 71 | let proposal = mocks::proposal::ProposalBuilder::default().build(); 72 | assert_eq!( 73 | super::total_payment_amount(&proposal).unwrap(), 74 | Zatoshis::from_u64(100_000).unwrap() 75 | ); 76 | } 77 | #[test] 78 | fn total_fee() { 79 | let proposal = mocks::proposal::ProposalBuilder::default().build(); 80 | assert_eq!( 81 | super::total_fee(&proposal).unwrap(), 82 | Zatoshis::from_u64(20_000).unwrap() 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /zingolib/src/testutils/macros.rs: -------------------------------------------------------------------------------- 1 | //! TODO 2 | 3 | /// Note that `do_addresses` returns an array, each element is a JSON representation 4 | /// of a UA. Legacy addresses can be extracted from the receivers, per: 5 | /// 6 | // TODO: is this needed as a macro? 7 | // TODO: change unified to orchard as both are unified with orchard-only or sapling-only receiver selections 8 | #[macro_export] 9 | macro_rules! get_base_address_macro { 10 | ($client:expr, $address_protocol:expr) => { 11 | match $address_protocol { 12 | "unified" => { 13 | assert_eq!( 14 | $client.unified_addresses_json().await[0]["has_orchard"] 15 | .as_bool() 16 | .unwrap(), 17 | true 18 | ); 19 | $client.unified_addresses_json().await[0]["encoded_address"] 20 | .clone() 21 | .to_string() 22 | } 23 | "sapling" => { 24 | assert_eq!( 25 | $client.unified_addresses_json().await[1]["has_orchard"] 26 | .as_bool() 27 | .unwrap(), 28 | false 29 | ); 30 | assert_eq!( 31 | $client.unified_addresses_json().await[1]["has_sapling"] 32 | .as_bool() 33 | .unwrap(), 34 | true 35 | ); 36 | $client.unified_addresses_json().await[1]["encoded_address"] 37 | .clone() 38 | .to_string() 39 | } 40 | "transparent" => $client.transparent_addresses_json().await[0]["encoded_address"] 41 | .clone() 42 | .to_string(), 43 | _ => "ERROR".to_string(), 44 | } 45 | }; 46 | } 47 | 48 | /// First check that each pools' balance matches an expectation 49 | /// then check that the overall balance as calculated by 50 | /// summing the amounts listed in `tx_summaries` matches the 51 | /// sum of the balances. 52 | #[macro_export] 53 | macro_rules! check_client_balances { 54 | ($client:ident, o: $orchard:tt s: $sapling:tt t: $transparent:tt) => { 55 | let balance = $client 56 | .account_balance(zip32::AccountId::ZERO) 57 | .await 58 | .unwrap(); 59 | assert_eq!( 60 | balance.total_orchard_balance.unwrap().into_u64(), 61 | $orchard, 62 | "\no_balance: {} expectation: {} ", 63 | balance.total_orchard_balance.unwrap().into_u64(), 64 | $orchard 65 | ); 66 | assert_eq!( 67 | balance.total_sapling_balance.unwrap().into_u64(), 68 | $sapling, 69 | "\ns_balance: {} expectation: {} ", 70 | balance.total_sapling_balance.unwrap().into_u64(), 71 | $sapling 72 | ); 73 | assert_eq!( 74 | balance.confirmed_transparent_balance.unwrap().into_u64(), 75 | $transparent, 76 | "\nt_balance: {} expectation: {} ", 77 | balance.confirmed_transparent_balance.unwrap().into_u64(), 78 | $transparent 79 | ); 80 | }; 81 | } 82 | -------------------------------------------------------------------------------- /app_description.txt: -------------------------------------------------------------------------------- 1 | Zingo! A Zcash Application 2 | 3 | Use zero knowledge cryptography to transact, and to manage your transaction history, in a private and mindful way. 4 | 5 | Take exclusive and unique control of your financial history. Use this unique insight into your 6 | own financial behavior, to introspect on how you spend money. Share insights on your terms. 7 | Establish healthy relationships with diverse economic communities. 8 | 9 | Only you have the power to share your information! Your perspective matters. You matter. 10 | 11 | When you send Zcash you can write a memo containing anything you like. When you receive 12 | Zcash you also receive the memo that the Sender wrote. As with most cryptocurrencies, wallets 13 | routinely send transactions to themselves to recover "change" from the transaction. In this case, where 14 | the application is both the Sender and the Receiver, Zingo uses the memo system to write 15 | notes to itself about the transactions it builds for the User. 16 | 17 | Zingo extracts that data to provide insights exclusively to you. In our initial release we 18 | already use this introspective mechanism to "remember" which full Unified Addresses you've previously sent Zcash. 19 | In other words, Zingo keeps track of who you've sent Zcash to, on the blockchain. This "Address Book" 20 | of people you've "Sent To" in the past is completely private and, as long as you properly protect your 21 | seed-phrase, will be fully recovered even if you lose your device. 22 | 23 | We're currently implementing another use of these on-chain memos, that will allow you to keep track (even if 24 | you lose your device) of which addresses you previously produced to receive funds at. That is, you'll always 25 | have a full list of all the addresses you asked people to use to send you zcash. 26 | 27 | Taken together these two instrospective mechanisms provide you with insight into who you've sent 28 | funds to, and who you've asked for funds from. This is just the beginning! We intend to implement 29 | mechanisms to allow users to share their insights in an anonymous, and economically viable way that 30 | will allow communities to aggregate around marketplaces of interesting insights! 31 | 32 | Your on-chain transaction details are never exposed to any third party. By providing you with private insight into 33 | your own economic behavior, we are enabling everyone to make healthier financial decisions. 34 | We anticipate diverse communities that form around different kinds of insights, to collaborate 35 | on shared values. 36 | 37 | Technical Detail of UA Caching: 38 | 39 | Zcash supports a multi-protocol Unified Address that enables the protocol to upgrade to newer versions 40 | without breaking backwards compatibility with earlier versions. This Unified Address system is 41 | implemented by clients off-chain. Clients share Unified Addresses with each other, the Unified 42 | Addresses contain Protocol Version Specific Addresses that the client can unpack, and publish to the 43 | blockchain as part of the transaction that it builds. This means that the on-chain address associated 44 | with a transaction does not contain all of the information the Sending-Client received as a "Unified Address" 45 | from the Recipient-Client, off-chain. If the Sending-Client simply recorded the UA off-chain then, in the 46 | case of Re-Sprouting the Client from its SeedPhrase, there'd be no way to recover that relationship data. 47 | Zingo solves this issue by recording the original recipient provided UA in the memo field of change from the 48 | transaction. 49 | -------------------------------------------------------------------------------- /zingolib/src/testutils/chain_generics/conduct_chain.rs: -------------------------------------------------------------------------------- 1 | //! A Lightclient test may involve hosting a server to send data to the LightClient. This trait can be asked to set simple scenarios where a mock LightServer sends data showing a note to a LightClient, the LightClient updates and responds by sending the note, and the Lightserver accepts the transaction and rebroadcasts it... 2 | //! The initial two implementors are 3 | //! lib-to-node, which links a lightserver to a zcashd in regtest mode. see `impl ConductChain for LibtoNode 4 | //! darkside, a mode for the lightserver which mocks zcashd. search 'impl ConductChain for DarksideScenario 5 | 6 | use crate::config::ZingoConfig; 7 | use crate::get_base_address_macro; 8 | use crate::testutils::lightclient::from_inputs; 9 | use crate::wallet::keys::unified::ReceiverSelection; 10 | use crate::{lightclient::LightClient, wallet::LightWallet}; 11 | 12 | #[allow(async_fn_in_trait)] 13 | #[allow(opaque_hidden_inferred_bound)] 14 | /// a trait (capability) for operating a server. 15 | /// delegates client setup, because different mock servers require different client configuration 16 | /// currently, the server conductor is limited to adding to the mock blockchain linearly (bump chain) 17 | pub trait ConductChain { 18 | /// set up the test chain 19 | async fn setup() -> Self; 20 | 21 | /// used to connect to server via grpc 22 | fn lightserver_uri(&self) -> Option; 23 | 24 | /// builds a faucet (funded from mining) 25 | async fn create_faucet(&mut self) -> LightClient; 26 | 27 | /// the server communicates some parameters (asyncronously) 28 | /// that are here compiled into an appropriate wallet configuration 29 | // super awful that this function has to exist, because the wallet should be able to communicate without 'test-only helpers' 30 | async fn zingo_config(&mut self) -> crate::config::ZingoConfig; 31 | 32 | /// builds an empty client 33 | async fn create_client(&mut self) -> LightClient { 34 | let config = self.zingo_config().await; 35 | let mut lightclient = LightClient::new(config, 0.into(), false).unwrap(); 36 | lightclient 37 | .generate_unified_address(ReceiverSelection::sapling_only(), zip32::AccountId::ZERO) 38 | .await 39 | .unwrap(); 40 | 41 | lightclient 42 | } 43 | 44 | /// loads a client from bytes 45 | fn load_client(&mut self, config: ZingoConfig, data: &[u8]) -> LightClient { 46 | LightClient::create_from_wallet( 47 | LightWallet::read(data, config.chain).unwrap(), 48 | config, 49 | false, 50 | ) 51 | .unwrap() 52 | } 53 | 54 | /// moves the chain tip forward, creating 1 new block 55 | /// and confirming transactions that were received by the server 56 | async fn increase_chain_height(&mut self); 57 | 58 | /// builds a client and funds it in orchard and syncs it 59 | async fn fund_client_orchard(&mut self, value: u64) -> LightClient { 60 | let mut faucet = self.create_faucet().await; 61 | let mut recipient = self.create_client().await; 62 | 63 | self.increase_chain_height().await; 64 | faucet.sync_and_await().await.unwrap(); 65 | 66 | from_inputs::quick_send( 67 | &mut faucet, 68 | vec![( 69 | (get_base_address_macro!(recipient, "unified")).as_str(), 70 | value, 71 | None, 72 | )], 73 | ) 74 | .await 75 | .unwrap(); 76 | 77 | self.increase_chain_height().await; 78 | 79 | recipient.sync_and_await().await.unwrap(); 80 | 81 | recipient 82 | } 83 | 84 | /// how many blocks of leeway to allow the chain to Confirm a transaction 85 | fn confirmation_patience_blocks(&self) -> usize; 86 | } 87 | -------------------------------------------------------------------------------- /zingolib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zingolib" 3 | description = "Zingo backend library." 4 | version = "0.0.1" 5 | authors = [""] 6 | edition = "2024" 7 | repository = "https://github.com/zingolabs/zingolib" 8 | homepage = "https://github.com/zingolabs/zingolib" 9 | license = "MIT" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [features] 14 | default = [] 15 | ci = [] 16 | darkside_tests = ["pepper-sync/darkside_test"] 17 | testutils = ["portpicker", "zingo_test_vectors", "tempfile", "zingo_common_components/for_test"] 18 | regtest = [ "zingo_common_components/for_test" ] 19 | 20 | [dependencies] 21 | 22 | # zingolib 23 | zingo-memo = { workspace = true } 24 | zingo-status = { workspace = true } 25 | pepper-sync = { workspace = true } 26 | zingo-price = { workspace = true } 27 | 28 | # zingo 29 | zingo_common_components = { workspace = true } 30 | zingo_netutils = { workspace = true } 31 | 32 | # testutils feature 33 | zingo_test_vectors = { git = "https://github.com/zingolabs/infrastructure.git", branch = "dev", optional = true} 34 | 35 | # blockchain 36 | zebra-chain = { workspace = true } 37 | orchard = { workspace = true } 38 | shardtree = { workspace = true, features = ["legacy-api"] } 39 | incrementalmerkletree = { workspace = true } 40 | zcash_address = { workspace = true } 41 | zcash_client_backend = { workspace = true, features = [ 42 | "unstable", 43 | "transparent-inputs", 44 | "unstable-serialization", 45 | "unstable-spanning-tree", 46 | ] } 47 | zcash_encoding = { workspace = true } 48 | zcash_keys = { workspace = true } 49 | zcash_primitives = { workspace = true } 50 | zcash_proofs = { workspace = true, features = ["download-params"] } 51 | zcash_protocol = { workspace = true } 52 | zcash_transparent = { workspace = true } 53 | zip32.workspace = true 54 | bip0039.workspace = true 55 | bip32 = { workspace = true, features = ["secp256k1-ffi"] } 56 | bs58 = { workspace = true, features = ["check"] } 57 | sapling-crypto.workspace = true 58 | 59 | # network 60 | hyper-rustls = { workspace = true } 61 | hyper-util = { workspace = true } 62 | tokio-rustls = { workspace = true } 63 | webpki-roots = { workspace = true } 64 | tower = { workspace = true } 65 | 66 | # other 67 | 68 | append-only-vec = { workspace = true } 69 | byteorder = { workspace = true } 70 | bytes = { workspace = true } 71 | chrono = { workspace = true } 72 | dirs.workspace = true 73 | futures = { workspace = true } 74 | hex = { workspace = true } 75 | http.workspace = true 76 | json = { workspace = true } 77 | jubjub = { workspace = true } 78 | log = { workspace = true } 79 | log4rs.workspace = true 80 | nonempty.workspace = true 81 | portpicker = { workspace = true, optional = true } 82 | prost = { workspace = true } 83 | rand = { workspace = true } 84 | ring = { workspace = true } 85 | rustls.workspace = true 86 | secp256k1 = { workspace = true } 87 | secrecy = { workspace = true } 88 | serde = { workspace = true, features = ["derive"] } 89 | serde_json = { workspace = true } 90 | tempfile = { workspace = true, optional = true } 91 | thiserror = { workspace = true } 92 | tokio = { workspace = true, features = ["full"] } 93 | tonic = { workspace = true } 94 | tracing-subscriber = { workspace = true } 95 | bech32 = { workspace = true } 96 | rust-embed = { workspace = true, features = ["debug-embed"] } 97 | tracing.workspace = true 98 | 99 | [dev-dependencies] 100 | zingolib = { workspace = true, features = ["testutils"] } 101 | zcash_local_net = { git = "https://github.com/zingolabs/infrastructure.git", branch = "dev" } 102 | portpicker = { workspace = true } 103 | concat-idents = { workspace = true } 104 | tempfile = { workspace = true } 105 | 106 | [build-dependencies] 107 | zcash_proofs = { workspace = true, features = ["download-params"] } 108 | -------------------------------------------------------------------------------- /zingolib/src/testutils/assertions.rs: -------------------------------------------------------------------------------- 1 | //! contains functions that compare structs to see if they match 2 | 3 | use nonempty::NonEmpty; 4 | 5 | use pepper_sync::wallet::WalletTransaction; 6 | use zcash_client_backend::proposal::{Proposal, Step}; 7 | use zcash_primitives::transaction::TxId; 8 | use zcash_protocol::value::Zatoshis; 9 | 10 | use crate::{lightclient::LightClient, wallet::LightWallet}; 11 | 12 | #[allow(missing_docs)] // error types document themselves 13 | #[derive(Debug, thiserror::Error)] 14 | pub enum ProposalToTransactionRecordComparisonError { 15 | #[error("{0:?}")] 16 | LookupError(#[from] LookupRecordsPairStepsError), 17 | #[error("Mismatch: Recorded fee: {0:?} ; Expected fee: {1:?}")] 18 | Mismatch(Result, Zatoshis), 19 | } 20 | 21 | /// compares a proposal with a fulfilled record and returns the agreed fee 22 | pub fn compare_fee( 23 | wallet: &LightWallet, 24 | transaction: &WalletTransaction, 25 | step: &Step, 26 | ) -> Result { 27 | let recorded_fee_result = wallet.calculate_transaction_fee(transaction); 28 | let proposed_fee = step.balance().fee_required(); 29 | if let Ok(recorded_fee) = recorded_fee_result 30 | && recorded_fee == proposed_fee 31 | { 32 | return Ok(recorded_fee); 33 | } 34 | Err(ProposalToTransactionRecordComparisonError::Mismatch( 35 | recorded_fee_result, 36 | proposed_fee, 37 | )) 38 | } 39 | 40 | /// currently checks: 41 | /// 1. len of txids == num steps 42 | /// 2. the txid is stored in the `records_by_ids` database 43 | /// 3. if the fee from the `calculate_transaction_fee` matches the sum of the per-step fees 44 | /// 45 | /// if any of these checks fail, rather than panic immediately, this function will include an error enum in its output. make sure to expect this. 46 | pub async fn lookup_fees_with_proposal_check( 47 | client: &LightClient, 48 | proposal: &Proposal, 49 | txids: &NonEmpty, 50 | ) -> Vec> { 51 | for_each_proposed_transaction(client, proposal, txids, |records, record, step| { 52 | compare_fee(records, record, step) 53 | }) 54 | .await 55 | .into_iter() 56 | .map(|stepwise_result| { 57 | stepwise_result 58 | .map_err(ProposalToTransactionRecordComparisonError::LookupError) 59 | .and_then(|fee_comparison_result| fee_comparison_result) 60 | }) 61 | .collect() 62 | } 63 | 64 | #[allow(missing_docs)] // error types document themselves 65 | #[derive(Debug, thiserror::Error)] 66 | pub enum LookupRecordsPairStepsError { 67 | #[error("TxId missing from broadcast.")] 68 | MissingFromBroadcast, 69 | #[error("Could not look up TransactionRecord with txid {0:?}.")] 70 | MissingRecord(TxId), 71 | } 72 | 73 | /// checks the client for record of each of the expected transactions, and does anything to them. 74 | pub async fn for_each_proposed_transaction( 75 | client: &LightClient, 76 | proposal: &Proposal, 77 | txids: &NonEmpty, 78 | f: fn(&LightWallet, &WalletTransaction, &Step) -> Res, 79 | ) -> Vec> { 80 | let wallet = client.wallet.read().await; 81 | 82 | let mut step_results = vec![]; 83 | for (step_number, step) in proposal.steps().iter().enumerate() { 84 | step_results.push({ 85 | if let Some(txid) = txids.get(step_number) { 86 | if let Some(transaction) = wallet.wallet_transactions.get(txid) { 87 | Ok(f(&wallet, transaction, step)) 88 | } else { 89 | Err(LookupRecordsPairStepsError::MissingRecord(*txid)) 90 | } 91 | } else { 92 | Err(LookupRecordsPairStepsError::MissingFromBroadcast) 93 | } 94 | }); 95 | } 96 | step_results 97 | } 98 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # The order of the members reflects a dimension of the dependency relation, the first four depend on 2 | # * zingolib 3 | # which depends in turn, on the bottom 3. 4 | # This doesn't account for all dependency relations, for example, zingocli depends on zingoconfig directly (for now). 5 | [workspace] 6 | members = [ 7 | "libtonode-tests", 8 | "darkside-tests", 9 | "zingo-cli", 10 | "zingolib", 11 | "zingo-memo", 12 | "pepper-sync", 13 | "zingo-price", "zingolib_testutils", 14 | ] 15 | resolver = "2" 16 | 17 | [workspace.dependencies] 18 | bip0039 = { version = "0.12", features = ["rand"] } 19 | bip32 = { version = "0.6.0-pre.1", default-features = false } 20 | 21 | zip32 = "0.2.0" 22 | orchard = "0.11.0" 23 | sapling-crypto = "0.5.0" 24 | incrementalmerkletree = "0.8.2" 25 | shardtree = "0.6.1" 26 | zcash_address = { version = "0.10" } 27 | zcash_client_backend = { version = "0.20", features = [ 28 | "lightwalletd-tonic", 29 | "orchard", 30 | "transparent-inputs", 31 | "tor", 32 | ] } 33 | zcash_encoding = { version = "0.3" } 34 | zcash_keys = { version = "0.12", features = [ 35 | "transparent-inputs", 36 | "sapling", 37 | "orchard", 38 | ] } 39 | zcash_note_encryption = "0.4" 40 | zcash_primitives = { version = "0.26" } 41 | zcash_proofs = { version = "0.26" } 42 | zcash_protocol = { version = "0.7" } 43 | zcash_transparent = { version = "0.6" } 44 | 45 | zebra-chain = { version = "3.0" } 46 | 47 | zingo_netutils = { git = "https://github.com/zingolabs/zingo-common.git", branch = "dev" } 48 | zingo_common_components = { git = "https://github.com/zingolabs/zingo-common.git", branch = "dev" } 49 | 50 | append-only-vec = "0.1.7" 51 | bech32 = "0.11.0" 52 | bs58 = "0.5" 53 | byteorder = "1" 54 | bytes = "1" 55 | chrono = "0.4" 56 | clap = "4" 57 | concat-idents = "1" 58 | dirs = "6" 59 | futures = "0.3" 60 | futures-util = "0.3" 61 | hex = "0.4" 62 | http = "1" 63 | http-body-util = "0.1" 64 | http-body = "1" 65 | hyper-util = "0.1" 66 | hyper = { version = "1", features = ["full"] } 67 | hyper-rustls = { version = "0.27", features = ["http2"] } 68 | indoc = "2" 69 | itertools = "0.14" 70 | json = "0.12" 71 | jubjub = "0.10" 72 | log = "0.4" 73 | log4rs = "1" 74 | memuse = "0.2" 75 | nonempty = "0.11.0" 76 | portpicker = "0.1" 77 | proptest = "1.6.0" 78 | prost = "0.13" 79 | rand = "0.8" 80 | reqwest = { version = "0.12.15", default-features = false } 81 | ring = "0.17.14" 82 | rusqlite = "0.32" 83 | rust_decimal = "1.37.2" 84 | rust-embed = "6" 85 | rustls = { version = "0.23", features = ["ring"] } 86 | rustls-pemfile = "2" 87 | rustyline = "11" 88 | secp256k1 = "0.29.1" 89 | secrecy = "0.8" 90 | serde = "1" 91 | serde_json = "1" 92 | shellwords = "1" 93 | subtle = "2" 94 | tempfile = "3" 95 | thiserror = "2" 96 | tokio = "1" 97 | tokio-rustls = "0.26" 98 | tonic = { version = "0.13", features = ["tls-webpki-roots"] } 99 | tonic-build = "0.13" 100 | tower = { version = "0.5" } 101 | tracing = "0.1" 102 | tracing-subscriber = "0.3" 103 | webpki-roots = "0.25" 104 | 105 | # Parallel processing 106 | crossbeam-channel = "0.5" 107 | rayon = "1" 108 | 109 | # Documentation 110 | simple-mermaid = "0.2.0" 111 | 112 | # Workspace 113 | zingo-memo = { path = "zingo-memo" } 114 | zingo-price = { path = "zingo-price" } 115 | zingo-status = { path = "zingo-status" } 116 | pepper-sync = { path = "pepper-sync" } 117 | zingolib = { path = "zingolib" } 118 | 119 | # Test Attributes 120 | test-log = "0.2" 121 | 122 | [patch.crates-io] 123 | zcash_client_backend = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"} 124 | zcash_address = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"} 125 | zcash_keys = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"} 126 | zcash_primitives = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"} 127 | zcash_protocol = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"} 128 | zcash_transparent = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"} 129 | 130 | [profile.test] 131 | opt-level = 3 132 | -------------------------------------------------------------------------------- /mkrelease.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script depends on a docker image already being built 3 | # To build it, 4 | # cd docker 5 | # docker build --tag rustbuild:latest . 6 | 7 | POSITIONAL=() 8 | while [[ $# -gt 0 ]] 9 | do 10 | key="$1" 11 | 12 | case $key in 13 | -v|--version) 14 | APP_VERSION="$2" 15 | shift # past argument 16 | shift # past value 17 | ;; 18 | *) # unknown option 19 | POSITIONAL+=("$1") # save it in an array for later 20 | shift # past argument 21 | ;; 22 | esac 23 | done 24 | set -- "${POSITIONAL[@]}" # restore positional parameters 25 | 26 | if [ -z $APP_VERSION ]; then echo "APP_VERSION is not set"; exit 1; fi 27 | 28 | # Write the version file 29 | echo "pub const VERSION: &str = \"$APP_VERSION\";" > cli/src/version.rs 30 | 31 | # First, do the tests 32 | cd lib && cargo test --release 33 | retVal=$? 34 | if [ $retVal -ne 0 ]; then 35 | echo "Error" 36 | exit $retVal 37 | fi 38 | cd .. 39 | 40 | # Compile for mac directly 41 | cargo build --release 42 | 43 | #macOS 44 | codesign -f -s "Apple Distribution: Concision Systems LLC (5N76B7JDDT)" target/release/zingo-cli --deep 45 | rm -rf target/macOS-zingo-cli-v$APP_VERSION 46 | mkdir -p target/macOS-zingo-cli-v$APP_VERSION 47 | cp target/release/zingo-cli target/macOS-zingo-cli-v$APP_VERSION/ 48 | 49 | # Now sign and zip the binaries 50 | # macOS 51 | gpg --batch --output target/macOS-zingo-cli-v$APP_VERSION/zingo-cli.sig --detach-sig target/macOS-zingo-cli-v$APP_VERSION/zingo-cli 52 | cd target 53 | cd macOS-zingo-cli-v$APP_VERSION 54 | gsha256sum zingo-cli > sha256sum.txt 55 | cd .. 56 | zip -r macOS-zingo-cli-v$APP_VERSION.zip macOS-zingo-cli-v$APP_VERSION 57 | cd .. 58 | 59 | # For Windows and Linux, build via docker 60 | docker run --rm -v $(pwd)/:/opt/zingolib rustbuild:latest bash -c "cd /opt/zingolib && cargo build --release && cargo build --release --target armv7-unknown-linux-gnueabihf && cargo build --release --target aarch64-unknown-linux-gnu && SODIUM_LIB_DIR='/opt/libsodium-win64/lib/' cargo build --release --target x86_64-pc-windows-gnu" 61 | 62 | #Linux 63 | rm -rf target/linux-zingo-cli-v$APP_VERSION 64 | mkdir -p target/linux-zingo-cli-v$APP_VERSION 65 | cp target/release/zingo-cli target/linux-zingo-cli-v$APP_VERSION/ 66 | gpg --batch --output target/linux-zingo-cli-v$APP_VERSION/zingo-cli.sig --detach-sig target/linux-zingo-cli-v$APP_VERSION/zingo-cli 67 | cd target 68 | cd linux-zingo-cli-v$APP_VERSION 69 | gsha256sum zingo-cli > sha256sum.txt 70 | cd .. 71 | zip -r linux-zingo-cli-v$APP_VERSION.zip linux-zingo-cli-v$APP_VERSION 72 | cd .. 73 | 74 | 75 | #Windows 76 | rm -rf target/Windows-zingo-cli-v$APP_VERSION 77 | mkdir -p target/Windows-zingo-cli-v$APP_VERSION 78 | cp target/x86_64-pc-windows-gnu/release/zingo-cli.exe target/Windows-zingo-cli-v$APP_VERSION/ 79 | gpg --batch --output target/Windows-zingo-cli-v$APP_VERSION/zingo-cli.sig --detach-sig target/Windows-zingo-cli-v$APP_VERSION/zingo-cli.exe 80 | cd target 81 | cd Windows-zingo-cli-v$APP_VERSION 82 | gsha256sum zingo-cli.exe > sha256sum.txt 83 | cd .. 84 | zip -r Windows-zingo-cli-v$APP_VERSION.zip Windows-zingo-cli-v$APP_VERSION 85 | cd .. 86 | 87 | 88 | #Armv7 89 | rm -rf target/Armv7-zingo-cli-v$APP_VERSION 90 | mkdir -p target/Armv7-zingo-cli-v$APP_VERSION 91 | cp target/armv7-unknown-linux-gnueabihf/release/zingo-cli target/Armv7-zingo-cli-v$APP_VERSION/ 92 | gpg --batch --output target/Armv7-zingo-cli-v$APP_VERSION/zingo-cli.sig --detach-sig target/Armv7-zingo-cli-v$APP_VERSION/zingo-cli 93 | cd target 94 | cd Armv7-zingo-cli-v$APP_VERSION 95 | gsha256sum zingo-cli > sha256sum.txt 96 | cd .. 97 | zip -r Armv7-zingo-cli-v$APP_VERSION.zip Armv7-zingo-cli-v$APP_VERSION 98 | cd .. 99 | 100 | 101 | #AARCH64 102 | rm -rf target/aarch64-zingo-cli-v$APP_VERSION 103 | mkdir -p target/aarch64-zingo-cli-v$APP_VERSION 104 | cp target/aarch64-unknown-linux-gnu/release/zingo-cli target/aarch64-zingo-cli-v$APP_VERSION/ 105 | gpg --batch --output target/aarch64-zingo-cli-v$APP_VERSION/zingo-cli.sig --detach-sig target/aarch64-zingo-cli-v$APP_VERSION/zingo-cli 106 | cd target 107 | cd aarch64-zingo-cli-v$APP_VERSION 108 | gsha256sum zingo-cli > sha256sum.txt 109 | cd .. 110 | zip -r aarch64-zingo-cli-v$APP_VERSION.zip aarch64-zingo-cli-v$APP_VERSION 111 | cd .. 112 | -------------------------------------------------------------------------------- /zingolib/src/grpc_client.rs: -------------------------------------------------------------------------------- 1 | //! Module for creating GRPC clients compatible with `zcash_client_backend` 2 | 3 | use http::{Uri, uri::PathAndQuery}; 4 | use hyper_util::client::legacy::connect::HttpConnector; 5 | use std::sync::Arc; 6 | use tokio_rustls::rustls::pki_types::{Der, TrustAnchor}; 7 | use tokio_rustls::rustls::{ClientConfig, RootCertStore}; 8 | use tower::ServiceExt; 9 | use zcash_client_backend::proto::service::compact_tx_streamer_client::CompactTxStreamerClient; 10 | use zingo_netutils::UnderlyingService; 11 | 12 | /// Creates a `zcash_client_backend` compatible GRPC client from a URI 13 | /// This duplicates the connection logic from `zingo_netutils` but creates a `zcash_client_backend` client 14 | pub async fn get_zcb_client( 15 | uri: Uri, 16 | ) -> Result, zingo_netutils::GetClientError> { 17 | let uri = Arc::new(uri); 18 | let mut http_connector = HttpConnector::new(); 19 | http_connector.enforce_http(false); 20 | let scheme = uri 21 | .scheme() 22 | .ok_or(zingo_netutils::GetClientError::InvalidScheme)? 23 | .clone(); 24 | let authority = uri 25 | .authority() 26 | .ok_or(zingo_netutils::GetClientError::InvalidAuthority)? 27 | .clone(); 28 | 29 | if uri.scheme_str() == Some("https") { 30 | let mut root_store = RootCertStore::empty(); 31 | root_store.extend( 32 | webpki_roots::TLS_SERVER_ROOTS 33 | .iter() 34 | .map(|anchor_ref| TrustAnchor { 35 | subject: Der::from_slice(anchor_ref.subject), 36 | subject_public_key_info: Der::from_slice(anchor_ref.spki), 37 | name_constraints: anchor_ref.name_constraints.map(Der::from_slice), 38 | }), 39 | ); 40 | 41 | let config = ClientConfig::builder() 42 | .with_root_certificates(root_store) 43 | .with_no_client_auth(); 44 | 45 | let connector = tower::ServiceBuilder::new() 46 | .layer_fn(move |s| { 47 | let tls = config.clone(); 48 | 49 | hyper_rustls::HttpsConnectorBuilder::new() 50 | .with_tls_config(tls) 51 | .https_or_http() 52 | .enable_http2() 53 | .wrap_connector(s) 54 | }) 55 | .service(http_connector); 56 | 57 | let client = zingo_netutils::client::client_from_connector(connector, false); 58 | let svc = tower::ServiceBuilder::new() 59 | .map_request(move |mut request: http::Request<_>| { 60 | let path_and_query = request 61 | .uri() 62 | .path_and_query() 63 | .cloned() 64 | .unwrap_or(PathAndQuery::from_static("/")); 65 | let uri = Uri::builder() 66 | .scheme(scheme.clone()) 67 | .authority(authority.clone()) 68 | .path_and_query(path_and_query) 69 | .build() 70 | .unwrap(); 71 | 72 | *request.uri_mut() = uri; 73 | request 74 | }) 75 | .service(client); 76 | 77 | Ok(CompactTxStreamerClient::new(svc.boxed_clone())) 78 | } else { 79 | let connector = tower::ServiceBuilder::new().service(http_connector); 80 | let client = zingo_netutils::client::client_from_connector(connector, true); 81 | let svc = tower::ServiceBuilder::new() 82 | .map_request(move |mut request: http::Request<_>| { 83 | let path_and_query = request 84 | .uri() 85 | .path_and_query() 86 | .cloned() 87 | .unwrap_or(PathAndQuery::from_static("/")); 88 | let uri = Uri::builder() 89 | .scheme(scheme.clone()) 90 | .authority(authority.clone()) 91 | .path_and_query(path_and_query) 92 | .build() 93 | .unwrap(); 94 | 95 | *request.uri_mut() = uri; 96 | request 97 | }) 98 | .service(client); 99 | 100 | Ok(CompactTxStreamerClient::new(svc.boxed_clone())) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /zingo-testutils/proto/compact_formats.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 The Zcash developers 2 | // Distributed under the MIT software license, see the accompanying 3 | // file COPYING or https://www.opensource.org/licenses/mit-license.php . 4 | 5 | syntax = "proto3"; 6 | package cash.z.wallet.sdk.rpc; 7 | option go_package = "walletrpc"; 8 | option swift_prefix = ""; 9 | 10 | // Remember that proto3 fields are all optional. A field that is not present will be set to its zero value. 11 | // bytes fields of hashes are in canonical little-endian format. 12 | 13 | // Information about the state of the chain as of a given block. 14 | message ChainMetadata { 15 | uint32 saplingCommitmentTreeSize = 1; // the size of the Sapling note commitment tree as of the end of this block 16 | uint32 orchardCommitmentTreeSize = 2; // the size of the Orchard note commitment tree as of the end of this block 17 | } 18 | 19 | // A compact representation of the shielded data in a Zcash block. 20 | // 21 | // CompactBlock is a packaging of ONLY the data from a block that's needed to: 22 | // 1. Detect a payment to your shielded Sapling address 23 | // 2. Detect a spend of your shielded Sapling notes 24 | // 3. Update your witnesses to generate new Sapling spend proofs. 25 | message CompactBlock { 26 | uint32 protoVersion = 1; // the version of this wire format, for storage 27 | uint64 height = 2; // the height of this block 28 | bytes hash = 3; // the ID (hash) of this block, same as in block explorers 29 | bytes prevHash = 4; // the ID (hash) of this block's predecessor 30 | uint32 time = 5; // Unix epoch time when the block was mined 31 | bytes header = 6; // (hash, prevHash, and time) OR (full header) 32 | repeated CompactTx vtx = 7; // zero or more compact transactions from this block 33 | ChainMetadata chainMetadata = 8; // information about the state of the chain as of this block 34 | } 35 | 36 | // A compact representation of the shielded data in a Zcash transaction. 37 | // 38 | // CompactTx contains the minimum information for a wallet to know if this transaction 39 | // is relevant to it (either pays to it or spends from it) via shielded elements 40 | // only. This message will not encode a transparent-to-transparent transaction. 41 | message CompactTx { 42 | // Index and hash will allow the receiver to call out to chain 43 | // explorers or other data structures to retrieve more information 44 | // about this transaction. 45 | uint64 index = 1; // the index within the full block 46 | bytes hash = 2; // the ID (hash) of this transaction, same as in block explorers 47 | 48 | // The transaction fee: present if server can provide. In the case of a 49 | // stateless server and a transaction with transparent inputs, this will be 50 | // unset because the calculation requires reference to prior transactions. 51 | // If there are no transparent inputs, the fee will be calculable as: 52 | // valueBalanceSapling + valueBalanceOrchard + sum(vPubNew) - sum(vPubOld) - sum(tOut) 53 | uint32 fee = 3; 54 | 55 | repeated CompactSaplingSpend spends = 4; 56 | repeated CompactSaplingOutput outputs = 5; 57 | repeated CompactOrchardAction actions = 6; 58 | } 59 | 60 | // A compact representation of a [Sapling Spend](https://zips.z.cash/protocol/protocol.pdf#spendencodingandconsensus). 61 | // 62 | // CompactSaplingSpend is a Sapling Spend Description as described in 7.3 of the Zcash 63 | // protocol specification. 64 | message CompactSaplingSpend { 65 | bytes nf = 1; // Nullifier (see the Zcash protocol specification) 66 | } 67 | 68 | // A compact representation of a [Sapling Output](https://zips.z.cash/protocol/protocol.pdf#outputencodingandconsensus). 69 | // 70 | // It encodes the `cmu` field, `ephemeralKey` field, and a 52-byte prefix of the 71 | // `encCiphertext` field of a Sapling Output Description. Total size is 116 bytes. 72 | message CompactSaplingOutput { 73 | bytes cmu = 1; // Note commitment u-coordinate. 74 | bytes ephemeralKey = 2; // Ephemeral public key. 75 | bytes ciphertext = 3; // First 52 bytes of ciphertext. 76 | } 77 | 78 | // A compact representation of an [Orchard Action](https://zips.z.cash/protocol/protocol.pdf#actionencodingandconsensus). 79 | message CompactOrchardAction { 80 | bytes nullifier = 1; // [32] The nullifier of the input note 81 | bytes cmx = 2; // [32] The x-coordinate of the note commitment for the output note 82 | bytes ephemeralKey = 3; // [32] An encoding of an ephemeral Pallas public key 83 | bytes ciphertext = 4; // [52] The first 52 bytes of the encCiphertext field 84 | } 85 | -------------------------------------------------------------------------------- /zingolib/src/lightclient/save.rs: -------------------------------------------------------------------------------- 1 | //! `LightClient` saves internally when it gets to a checkpoint. If has filesystem access, it saves to file at those points. otherwise, it passes the save buffer to the FFI. 2 | 3 | use futures::FutureExt as _; 4 | use log::error; 5 | 6 | use std::{borrow::BorrowMut as _, fs::remove_file, sync::atomic}; 7 | 8 | use super::LightClient; 9 | use crate::{data::PollReport, utils}; 10 | 11 | impl LightClient { 12 | /// Launches a task for saving the wallet data to persistance when the wallet's `save_required` flag is set. 13 | pub async fn save_task(&mut self) { 14 | if self.save_active.load(atomic::Ordering::Acquire) { 15 | return; 16 | } 17 | 18 | self.save_active.store(true, atomic::Ordering::Release); 19 | let save_active = self.save_active.clone(); 20 | let wallet = self.wallet.clone(); 21 | let wallet_path = self.config.get_wallet_path(); 22 | let mut interval = tokio::time::interval(std::time::Duration::from_secs(1)); 23 | interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay); 24 | let save_handle = tokio::spawn(async move { 25 | loop { 26 | interval.tick().await; 27 | if let Some(wallet_bytes) = wallet.write().await.save()? { 28 | utils::write_to_path(&wallet_path, wallet_bytes).await?; 29 | } 30 | if !save_active.load(atomic::Ordering::Acquire) { 31 | return Ok(()); 32 | } 33 | } 34 | }); 35 | self.save_handle = Some(save_handle); 36 | } 37 | 38 | /// Wait until the wallet's `save_required` flag is not set. 39 | pub async fn wait_for_save(&self) { 40 | let mut interval = tokio::time::interval(std::time::Duration::from_secs(1)); 41 | interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay); 42 | loop { 43 | interval.tick().await; 44 | if !self.wallet.read().await.save_required { 45 | return; 46 | } 47 | } 48 | } 49 | 50 | /// Polls the save task, returning [`self::PollReport`]. 51 | fn poll_save_task(&mut self) -> PollReport<(), std::io::Error> { 52 | if let Some(mut save_handle) = self.save_handle.take() { 53 | if let Some(save_result) = save_handle.borrow_mut().now_or_never() { 54 | PollReport::Ready(save_result.expect("task panicked")) 55 | } else { 56 | self.save_handle = Some(save_handle); 57 | PollReport::NotReady 58 | } 59 | } else { 60 | PollReport::NoHandle 61 | } 62 | } 63 | 64 | /// Checks the save task handle in case of failure. 65 | /// On save task failure, restarts the save task and returns the error. 66 | pub async fn check_save_error(&mut self) -> std::io::Result<()> { 67 | match self.poll_save_task() { 68 | PollReport::Ready(save_result) => { 69 | if save_result.is_err() { 70 | self.save_task().await; 71 | } 72 | save_result 73 | } 74 | _ => Ok(()), 75 | } 76 | } 77 | 78 | pub async fn shutdown_save_task(&mut self) -> std::io::Result<()> { 79 | self.save_active.store(false, atomic::Ordering::Release); 80 | if let Some(save_handle) = self.save_handle.take() { 81 | save_handle.await.expect("task panicked") 82 | } else { 83 | Ok(()) 84 | } 85 | } 86 | 87 | /// Only relevant in non-mobile, this function removes the save file. 88 | // TodO: can we shred it? 89 | pub async fn do_delete(&self) -> Result<(), String> { 90 | // Check if the file exists before attempting to delete 91 | if self.config.wallet_path_exists() { 92 | match remove_file(self.config.get_wallet_path()) { 93 | Ok(()) => { 94 | log::debug!("File deleted successfully!"); 95 | Ok(()) 96 | } 97 | Err(e) => { 98 | let err = format!("ERR: {e}"); 99 | error!("{err}"); 100 | log::debug!("DELETE FAIL ON FILE!"); 101 | Err(e.to_string()) 102 | } 103 | } 104 | } else { 105 | let err = "Error: File does not exist, nothing to delete.".to_string(); 106 | error!("{err}"); 107 | log::debug!("File does not exist, nothing to delete."); 108 | Err(err) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /darkside-tests/src/darkside_connector.rs: -------------------------------------------------------------------------------- 1 | use super::darkside_types::{ 2 | DarksideBlock, DarksideBlocksUrl, DarksideEmptyBlocks, DarksideHeight, DarksideMetaState, 3 | Empty, RawTransaction, TreeState, darkside_streamer_client::DarksideStreamerClient, 4 | }; 5 | 6 | use hyper::Uri; 7 | use hyper_util::client::legacy::connect::HttpConnector; 8 | use std::sync::Arc; 9 | use tower::ServiceExt; 10 | use zingo_netutils::UnderlyingService; 11 | 12 | macro_rules! define_darkside_connector_methods( 13 | ($($name:ident (&$self:ident $(,$param:ident: $param_type:ty)*$(,)?) -> $return:ty {$param_packing:expr}),*) => {$( 14 | #[allow(unused)] 15 | pub async fn $name(&$self, $($param: $param_type),*) -> ::std::result::Result<$return, String> { 16 | let request = ::tonic::Request::new($param_packing); 17 | 18 | let mut client = $self.get_client().await.map_err(|e| format!("{e}"))?; 19 | 20 | client 21 | .$name(request) 22 | .await 23 | .map_err(|e| format!("{}", e)) 24 | .map(|resp| resp.into_inner()) 25 | })*} 26 | ); 27 | 28 | #[derive(Clone)] 29 | pub struct DarksideConnector(pub http::Uri); 30 | 31 | impl DarksideConnector { 32 | pub fn get_client( 33 | &self, 34 | ) -> impl std::future::Future< 35 | Output = Result, Box>, 36 | > { 37 | let uri = Arc::new(self.0.clone()); 38 | async move { 39 | let mut http_connector = HttpConnector::new(); 40 | http_connector.enforce_http(false); 41 | let connector = tower::ServiceBuilder::new().service(http_connector); 42 | let client = zingo_netutils::client::client_from_connector(connector, true); 43 | let uri = uri.clone(); 44 | let svc = tower::ServiceBuilder::new() 45 | //Here, we take all the pieces of our uri, and add in the path from the Requests's uri 46 | .map_request(move |mut req: http::Request| { 47 | let uri = Uri::builder() 48 | .scheme(uri.scheme().unwrap().clone()) 49 | .authority(uri.authority().unwrap().clone()) 50 | //here. The Request's uri contains the path to the GRPC server and 51 | //the method being called 52 | .path_and_query(req.uri().path_and_query().unwrap().clone()) 53 | .build() 54 | .unwrap(); 55 | 56 | *req.uri_mut() = uri; 57 | req 58 | }) 59 | .service(client); 60 | 61 | Ok(DarksideStreamerClient::new(svc.boxed_clone())) 62 | } 63 | } 64 | 65 | define_darkside_connector_methods!( 66 | apply_staged(&self, height: i32) -> Empty { DarksideHeight { height } }, 67 | add_tree_state(&self, tree_state: TreeState) -> Empty { tree_state }, 68 | reset( 69 | &self, 70 | sapling_activation: i32, 71 | branch_id: String, 72 | chain_name: String, 73 | ) -> Empty { 74 | DarksideMetaState { 75 | sapling_activation, 76 | branch_id, 77 | chain_name, 78 | } 79 | }, 80 | stage_blocks(&self, url: String) -> Empty { DarksideBlocksUrl { url } }, 81 | stage_blocks_create( 82 | &self, 83 | height: i32, 84 | count: i32, 85 | nonce: i32 86 | ) -> Empty { 87 | DarksideEmptyBlocks { 88 | height, 89 | nonce, 90 | count 91 | } 92 | }, 93 | 94 | stage_blocks_stream(&self, blocks: Vec) -> Empty { 95 | ::futures_util::stream::iter( 96 | blocks.into_iter().map(|block| DarksideBlock { block }) 97 | ) 98 | }, 99 | stage_transactions_stream(&self, transactions: Vec<(Vec, u64)>) -> Empty { 100 | ::futures_util::stream::iter( 101 | transactions.into_iter().map(|transaction| { 102 | RawTransaction { 103 | data: transaction.0, 104 | height: transaction.1 105 | } 106 | }) 107 | ) 108 | }, 109 | get_incoming_transactions(&self) -> ::tonic::Streaming { 110 | Empty {} 111 | }, 112 | clear_incoming_transactions(&self) -> Empty { 113 | Empty {} 114 | } 115 | ); 116 | } 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Zingolib 2 | [![license](https://img.shields.io/github/license/zingolabs/zingolib)](LICENSE) [![codecov](https://codecov.io/gh/zingolabs/zingolib/branch/dev/graph/badge.svg?token=WMKTJMQY28)](https://codecov.io/gh/zingolabs/zingolib) 3 | This repo provides both a library for zingo-mobile, as well as an included cli application to interact with zcashd via lightwalletd. 4 | 5 | # Security Vulnerability Disclosure 6 | 7 | If you believe you have discovered a security issue, please contact us at: 8 | 9 | zingodisclosure@proton.me 10 | 11 | ## Zingo CLI 12 | `zingo-cli` is a command line lightwalletd-proxy client. To use it, see "compiling from source" below. Releases are currently only provisional, we will update the README as releases come out. 13 | 14 | ## Privacy 15 | * While all the keys and transaction detection happens on the client, the server can learn what blocks contain your shielded transactions. 16 | * The server also learns other metadata about you like your ip address etc... 17 | * Also remember that t-addresses are publicly visible on the blockchain. 18 | * Price information is retrieved from Gemini exchange. 19 | 20 | ### Note Management 21 | Zingo-CLI does automatic note and utxo management, which means it doesn't allow you to manually select which address to send outgoing transactions from. It follows these principles: 22 | * Defaults to sending shielded transactions, even if you're sending to a transparent address 23 | * Can select funds from multiple shielded addresses in the same transaction 24 | * Will automatically shield your sapling funds at the first opportunity 25 | * When sending an outgoing transaction to a shielded address, Zingo-CLI can decide to use the transaction to additionally shield your sapling funds (i.e., send your sapling funds to your own orchard address in the same transaction) 26 | * Transparent funds are only spent via explicit shield operations 27 | 28 | ## Compiling from source 29 | 30 | #### Pre-requisites 31 | * Rust v1.85 or higher. 32 | * Run `rustup update` to get the latest version of Rust if you already have it installed 33 | * Rustfmt 34 | * Run `rustup component add rustfmt` to add rustfmt 35 | * Build Tools 36 | * Please install the build tools for your platform. On Ubuntu `sudo apt install build-essential gcc` 37 | * Protobuf Compiler 38 | * Please install the protobuf compiler for your platform. On Ubuntu `sudo apt install protobuf-compiler` 39 | ``` 40 | git clone https://github.com/zingolabs/zingolib.git 41 | cd zingolib 42 | cargo build --release --package zingo-cli 43 | ./target/release/zingo-cli --data-dir /path/to/data_directory/ 44 | ``` 45 | 46 | This will launch the interactive prompt. Type `help` to get a list of commands. 47 | 48 | ## Notes: 49 | * If you want to run your own server, please see [zingo lightwalletd](https://github.com/zingolabs/lightwalletd), and then run `./zingo-cli --server http://127.0.0.1:9067` 50 | * The default log file is in `~/.zcash/zingo-wallet.debug.log`. A default wallet is stored in `~/.zcash/zingo-wallet.dat` 51 | * If a server is not specified, the default indexer/lightwallet server is "https://zec.rocks:443". 52 | 53 | ## Running in non-interactive mode: 54 | You can also run `zingo-cli` in non-interactive mode by passing the command you want to run as an argument. For example, `zingo-cli addresses` will list all wallet addresses and exit. 55 | If you need to sync the wallet first before running the command, use --waitsync argument. This is useful for example for `zingo-cli balance`. 56 | Run `zingo-cli help` to see a list of all commands. 57 | 58 | ## Options 59 | Here are some CLI arguments you can pass to `zingo-cli`. Please run `zingo-cli --help` for the full list. 60 | 61 | * `--data-dir`: uses the specified path as data directory. This is required when not using the `--regtest` option. 62 | * Example: `./zingo-cli --data-dir /path/to/data_directory/` will use the provided directory to store `zingo-wallet.dat` and logs. If the provided directory does not exist, it will create it. 63 | * `--waitsync`: Wait for sync before running a command in non-interactive mode 64 | * Example: `./zingo-cli --data-dir /path/to/data_directory/ --waitsync balance` 65 | * `--server`: Connect to a custom zcash lightwalletd server. 66 | * Example: `./zingo-cli --data-dir /path/to/data_directory/ --server 127.0.0.1:9067` 67 | * `--seed`: Restore a wallet from a seed phrase. Note that this will fail if there is an existing wallet. Delete (or move) any existing wallet to restore from the 24-word seed phrase 68 | * `--birthday`: Specify wallet birthday when restoring from seed. This is the earliest block height where the wallet has a transaction. 69 | * Example: `./zingo-cli --data-dir /path/to/data_directory/ --seed "twenty four words seed phrase" --birthday 1234567` 70 | * `--recover`: Attempt to recover the seed phrase from a corrupted wallet 71 | 72 | ## Regtest 73 | Please see `zingo-cli/README.md` for details of running zingo-cli in regtest mode with a local network. 74 | -------------------------------------------------------------------------------- /zingolib/src/error.rs: -------------------------------------------------------------------------------- 1 | //! TODO: Add Mod Description Here! 2 | 3 | use std::error::Error; 4 | use std::fmt; 5 | 6 | use zcash_primitives::transaction::TxId; 7 | 8 | /// Holds zingolib error types 9 | #[derive(Debug)] 10 | pub enum ZingoLibError { 11 | /// TODO: Add Doc Comment Here! 12 | UnknownError, 13 | /// TODO: Add Doc Comment Here! 14 | Error(String), 15 | /// TODO: Add Doc Comment Here! 16 | NoWalletLocation, 17 | /// TODO: Add Doc Comment Here! 18 | MetadataUnderflow(String), 19 | /// TODO: Add Doc Comment Here! 20 | InternalWriteBufferError(std::io::Error), 21 | /// TODO: Add Doc Comment Here! 22 | WriteFileError(std::io::Error), 23 | /// TODO: Add Doc Comment Here! 24 | EmptySaveBuffer, 25 | /// TODO: Add Doc Comment Here! 26 | CantReadWallet(std::io::Error), 27 | /// TODO: Add Doc Comment Here! 28 | NoSuchTxId(TxId), 29 | /// TODO: Add Doc Comment Here! 30 | NoSuchSaplingOutputInTx(TxId, u32), 31 | /// TODO: Add Doc Comment Here! 32 | NoSuchOrchardOutputInTx(TxId, u32), 33 | /// TODO: Add Doc Comment Here! 34 | NoSuchNullifierInTx(TxId), 35 | /// TODO: Add Doc Comment Here! 36 | MissingOutputIndex(TxId), 37 | /// TODO: Add Doc Comment Here! 38 | CouldNotDecodeMemo(std::io::Error), 39 | /// server error 40 | Lightwalletd(String), 41 | } 42 | 43 | /// TODO: Add Doc Comment Here! 44 | pub type ZingoLibResult = Result; 45 | 46 | impl ZingoLibError { 47 | /// TODO: Add Doc Comment Here! 48 | pub fn handle(self) -> ZingoLibResult { 49 | log::error!("{self}"); 50 | Err(self) 51 | } 52 | } 53 | 54 | impl std::fmt::Display for ZingoLibError { 55 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 56 | use ZingoLibError::{ 57 | CantReadWallet, CouldNotDecodeMemo, EmptySaveBuffer, Error, InternalWriteBufferError, 58 | Lightwalletd, MetadataUnderflow, MissingOutputIndex, NoSuchNullifierInTx, 59 | NoSuchOrchardOutputInTx, NoSuchSaplingOutputInTx, NoSuchTxId, NoWalletLocation, 60 | UnknownError, WriteFileError, 61 | }; 62 | match self { 63 | UnknownError => write!(f, "UnknownError",), 64 | Error(string) => write!(f, "Error: {string}",), 65 | NoWalletLocation => write!( 66 | f, 67 | "No wallet location! (compiled for native rust, wallet location expected)" 68 | ), 69 | MetadataUnderflow(explanation) => write!( 70 | f, 71 | "Metadata underflow! Recorded metadata shows greater output than input value. This may be because input notes are prebirthday. {explanation}", 72 | ), 73 | InternalWriteBufferError(err) => write!(f, "Internal save error! {err} ",), 74 | WriteFileError(err) => write!( 75 | f, 76 | "Could not write to wallet save file. Was this erroneously attempted in mobile?, instead of native save buffer handling? Is there a permission issue? {err} ", 77 | ), 78 | EmptySaveBuffer => write!( 79 | f, 80 | "Empty save buffer. probably save_external was called before save_internal_rust. this is handled by save_external." 81 | ), 82 | CantReadWallet(err) => write!( 83 | f, 84 | "Cant read wallet. Corrupt file. Or maybe a backwards version issue? {err}", 85 | ), 86 | NoSuchTxId(txid) => write!(f, "Cant find TxId {txid}!",), 87 | NoSuchSaplingOutputInTx(txid, output_index) => write!( 88 | f, 89 | "Cant find note with sapling output_index {output_index} in TxId {txid}", 90 | ), 91 | NoSuchOrchardOutputInTx(txid, output_index) => write!( 92 | f, 93 | "Cant find note with orchard output_index {output_index} in TxId {txid}", 94 | ), 95 | NoSuchNullifierInTx(txid) => write!(f, "Cant find that Nullifier in TxId {txid}",), 96 | CouldNotDecodeMemo(err) => write!( 97 | f, 98 | "Could not decode memo. Zingo plans to support foreign memo formats soon. {err}", 99 | ), 100 | MissingOutputIndex(txid) => write!( 101 | f, 102 | "{txid} is missing output_index for note, cannot mark change" 103 | ), 104 | Lightwalletd(string) => write!(f, "{string}"), 105 | } 106 | } 107 | } 108 | 109 | impl From for String { 110 | fn from(value: ZingoLibError) -> Self { 111 | format!("{value}") 112 | } 113 | } 114 | 115 | impl Error for ZingoLibError {} 116 | 117 | /// Errors associated with builder patterns in production code 118 | #[derive(thiserror::Error, Debug)] 119 | pub enum BuildError { 120 | /// build failed. missing field. 121 | #[error("build failed. missing field {0}")] 122 | MissingField(String), 123 | } 124 | -------------------------------------------------------------------------------- /pepper-sync/src/witness.rs: -------------------------------------------------------------------------------- 1 | //! Module for structs and types associated with witness construction 2 | 3 | use std::collections::BTreeMap; 4 | 5 | use incrementalmerkletree::{Position, Retention}; 6 | use orchard::tree::MerkleHashOrchard; 7 | use shardtree::LocatedPrunableTree; 8 | use zcash_primitives::consensus::BlockHeight; 9 | 10 | #[cfg(not(feature = "darkside_test"))] 11 | use { 12 | crate::error::ServerError, shardtree::store::ShardStore, subtle::CtOption, 13 | zcash_client_backend::proto::service::SubtreeRoot, 14 | }; 15 | 16 | pub(crate) const SHARD_HEIGHT: u8 = 16; 17 | 18 | /// Required data for updating [`shardtree::ShardTree`] 19 | #[derive(Debug)] 20 | pub(crate) struct WitnessData { 21 | pub(crate) sapling_initial_position: Position, 22 | pub(crate) orchard_initial_position: Position, 23 | pub(crate) sapling_leaves_and_retentions: Vec<(sapling_crypto::Node, Retention)>, 24 | pub(crate) orchard_leaves_and_retentions: Vec<(MerkleHashOrchard, Retention)>, 25 | } 26 | 27 | impl WitnessData { 28 | /// Creates new `ShardTreeData` 29 | pub(crate) fn new( 30 | sapling_initial_position: Position, 31 | orchard_initial_position: Position, 32 | ) -> Self { 33 | WitnessData { 34 | sapling_initial_position, 35 | orchard_initial_position, 36 | sapling_leaves_and_retentions: Vec::new(), 37 | orchard_leaves_and_retentions: Vec::new(), 38 | } 39 | } 40 | } 41 | 42 | /// Located prunable tree data built from nodes and retentions during scanning for insertion into the shard store. 43 | #[derive(Debug)] 44 | pub struct LocatedTreeData { 45 | /// Located prunable tree 46 | pub(crate) subtree: LocatedPrunableTree, 47 | /// Checkpoints 48 | pub(crate) checkpoints: BTreeMap, 49 | } 50 | 51 | pub(crate) fn build_located_trees( 52 | initial_position: Position, 53 | leaves_and_retentions: Vec<(H, Retention)>, 54 | located_tree_size: usize, 55 | ) -> Vec> 56 | where 57 | H: Copy + PartialEq + incrementalmerkletree::Hashable + Sync + Send, 58 | { 59 | let (sender, receiver) = crossbeam_channel::unbounded(); 60 | rayon::scope_fifo(|scope| { 61 | for (i, chunk) in leaves_and_retentions.chunks(located_tree_size).enumerate() { 62 | let sender = sender.clone(); 63 | scope.spawn_fifo(move |_scope| { 64 | let start_position = initial_position + ((i * located_tree_size) as u64); 65 | let tree = LocatedPrunableTree::from_iter( 66 | start_position..(start_position + chunk.len() as u64), 67 | incrementalmerkletree::Level::from(SHARD_HEIGHT), 68 | chunk.iter().copied(), 69 | ); 70 | let _ignore_error = sender.send(tree); 71 | }); 72 | } 73 | }); 74 | drop(sender); 75 | 76 | let mut located_tree_data = Vec::new(); 77 | for tree in receiver.iter().flatten() { 78 | located_tree_data.push(LocatedTreeData { 79 | subtree: tree.subtree, 80 | checkpoints: tree.checkpoints, 81 | }); 82 | } 83 | 84 | located_tree_data 85 | } 86 | 87 | #[cfg(not(feature = "darkside_test"))] 88 | pub(crate) fn add_subtree_roots( 89 | subtree_roots: Vec, 90 | shard_tree: &mut shardtree::ShardTree, 91 | ) -> Result<(), ServerError> 92 | where 93 | S: ShardStore< 94 | H: incrementalmerkletree::Hashable + Clone + PartialEq + FromBytes, 95 | CheckpointId: Clone + Ord + std::fmt::Debug, 96 | Error = std::convert::Infallible, 97 | >, 98 | { 99 | for (index, tree_root) in subtree_roots.into_iter().enumerate() { 100 | let node = ::from_bytes( 101 | tree_root 102 | .root_hash 103 | .try_into() 104 | .map_err(|_| ServerError::InvalidSubtreeRoot)?, 105 | ) 106 | .into_option() 107 | .ok_or(ServerError::InvalidSubtreeRoot)?; 108 | let shard = LocatedPrunableTree::with_root_value( 109 | incrementalmerkletree::Address::from_parts( 110 | incrementalmerkletree::Level::new(SHARD_HEIGHT), 111 | index as u64, 112 | ), 113 | (node, shardtree::RetentionFlags::EPHEMERAL), 114 | ); 115 | shard_tree.store_mut().put_shard(shard).expect("infallible"); 116 | } 117 | 118 | Ok(()) 119 | } 120 | 121 | /// Allows generic construction of a shardtree node from raw byte representation 122 | #[cfg(not(feature = "darkside_test"))] 123 | pub(crate) trait FromBytes 124 | where 125 | Self: Sized, 126 | { 127 | fn from_bytes(array: [u8; 32]) -> CtOption; 128 | } 129 | 130 | #[cfg(not(feature = "darkside_test"))] 131 | impl FromBytes for orchard::tree::MerkleHashOrchard { 132 | fn from_bytes(array: [u8; 32]) -> CtOption { 133 | Self::from_bytes(&array) 134 | } 135 | } 136 | 137 | #[cfg(not(feature = "darkside_test"))] 138 | impl FromBytes for sapling_crypto::Node { 139 | fn from_bytes(array: [u8; 32]) -> CtOption { 140 | Self::from_bytes(array) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /pepper-sync/src/keys/transparent.rs: -------------------------------------------------------------------------------- 1 | //! Transparent keys and addresses 2 | 3 | use zcash_address::{ToAddress as _, ZcashAddress}; 4 | use zcash_primitives::{ 5 | legacy::{ 6 | TransparentAddress, 7 | keys::{ 8 | AccountPubKey, IncomingViewingKey as _, NonHardenedChildIndex, TransparentKeyScope, 9 | }, 10 | }, 11 | zip32::AccountId, 12 | }; 13 | use zcash_protocol::consensus; 14 | 15 | use crate::wallet::KeyIdInterface; 16 | 17 | /// Unique ID for transparent addresses. 18 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 19 | pub struct TransparentAddressId { 20 | account_id: AccountId, 21 | scope: TransparentScope, 22 | address_index: NonHardenedChildIndex, 23 | } 24 | 25 | impl TransparentAddressId { 26 | /// Construct from parts 27 | #[must_use] 28 | pub fn new( 29 | account_id: zcash_primitives::zip32::AccountId, 30 | scope: TransparentScope, 31 | address_index: NonHardenedChildIndex, 32 | ) -> Self { 33 | Self { 34 | account_id, 35 | scope, 36 | address_index, 37 | } 38 | } 39 | 40 | /// Gets address index 41 | #[must_use] 42 | pub fn address_index(&self) -> NonHardenedChildIndex { 43 | self.address_index 44 | } 45 | 46 | /// Gets address scope 47 | #[must_use] 48 | pub fn scope(&self) -> TransparentScope { 49 | self.scope 50 | } 51 | } 52 | 53 | impl KeyIdInterface for TransparentAddressId { 54 | fn account_id(&self) -> AccountId { 55 | self.account_id 56 | } 57 | } 58 | 59 | /// Child index for the `change` path level in the BIP44 hierarchy (a.k.a. scope/chain). 60 | #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] 61 | pub enum TransparentScope { 62 | /// External scope 63 | External, 64 | /// Internal scope (a.k.a. change) 65 | Internal, 66 | /// Refund scope (a.k.a. ephemeral) 67 | Refund, 68 | } 69 | 70 | impl std::fmt::Display for TransparentScope { 71 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 72 | write!( 73 | f, 74 | "{}", 75 | match self { 76 | TransparentScope::External => "external", 77 | TransparentScope::Internal => "internal", 78 | TransparentScope::Refund => "refund", 79 | } 80 | ) 81 | } 82 | } 83 | 84 | impl From for TransparentKeyScope { 85 | fn from(value: TransparentScope) -> Self { 86 | match value { 87 | TransparentScope::External => TransparentKeyScope::EXTERNAL, 88 | TransparentScope::Internal => TransparentKeyScope::INTERNAL, 89 | TransparentScope::Refund => TransparentKeyScope::EPHEMERAL, 90 | } 91 | } 92 | } 93 | 94 | impl TryFrom for TransparentScope { 95 | type Error = std::io::Error; 96 | 97 | fn try_from(value: u8) -> std::io::Result { 98 | match value { 99 | 0 => Ok(TransparentScope::External), 100 | 1 => Ok(TransparentScope::Internal), 101 | 2 => Ok(TransparentScope::Refund), 102 | _ => Err(std::io::Error::new( 103 | std::io::ErrorKind::InvalidData, 104 | "invalid scope value", 105 | )), 106 | } 107 | } 108 | } 109 | 110 | pub(crate) fn derive_address( 111 | consensus_parameters: &impl consensus::Parameters, 112 | account_pubkey: &AccountPubKey, 113 | address_id: TransparentAddressId, 114 | ) -> Result { 115 | let address = match address_id.scope() { 116 | TransparentScope::External => { 117 | derive_external_address(account_pubkey, address_id.address_index())? 118 | } 119 | TransparentScope::Internal => { 120 | derive_internal_address(account_pubkey, address_id.address_index())? 121 | } 122 | TransparentScope::Refund => { 123 | derive_refund_address(account_pubkey, address_id.address_index())? 124 | } 125 | }; 126 | 127 | Ok(encode_address(consensus_parameters, address)) 128 | } 129 | 130 | fn derive_external_address( 131 | account_pubkey: &AccountPubKey, 132 | address_index: NonHardenedChildIndex, 133 | ) -> Result { 134 | account_pubkey 135 | .derive_external_ivk()? 136 | .derive_address(address_index) 137 | } 138 | 139 | fn derive_internal_address( 140 | account_pubkey: &AccountPubKey, 141 | address_index: NonHardenedChildIndex, 142 | ) -> Result { 143 | account_pubkey 144 | .derive_internal_ivk()? 145 | .derive_address(address_index) 146 | } 147 | 148 | fn derive_refund_address( 149 | account_pubkey: &AccountPubKey, 150 | address_index: NonHardenedChildIndex, 151 | ) -> Result { 152 | account_pubkey 153 | .derive_ephemeral_ivk()? 154 | .derive_ephemeral_address(address_index) 155 | } 156 | 157 | /// Encodes transparent address 158 | pub fn encode_address( 159 | consensus_parameters: &impl consensus::Parameters, 160 | address: TransparentAddress, 161 | ) -> String { 162 | let zcash_address = match address { 163 | TransparentAddress::PublicKeyHash(data) => { 164 | ZcashAddress::from_transparent_p2pkh(consensus_parameters.network_type(), data) 165 | } 166 | TransparentAddress::ScriptHash(data) => { 167 | ZcashAddress::from_transparent_p2sh(consensus_parameters.network_type(), data) 168 | } 169 | }; 170 | zcash_address.to_string() 171 | } 172 | -------------------------------------------------------------------------------- /zingo-cli/README.md: -------------------------------------------------------------------------------- 1 | # Zingo CLI 2 | 3 | A command-line interface for the Zingo wallet. 4 | 5 | ## Building 6 | 7 | ### Default Build (Mainnet/Testnet) 8 | 9 | To build the standard zingo-cli binary that works with mainnet and testnet: 10 | 11 | ```bash 12 | cargo build --release 13 | ``` 14 | 15 | The binary will be available at `target/release/zingo-cli`. 16 | 17 | ### Build with Regtest Support 18 | 19 | To build zingo-cli with regtest support in addition to mainnet and testnet: 20 | 21 | ```bash 22 | cargo build --release --features regtest 23 | ``` 24 | 25 | The binary will be available at `target/release/zingo-cli`. 26 | 27 | ## Running 28 | 29 | By default, zingo-cli stores wallet data in a `wallets/` directory in the current working directory. 30 | 31 | The `--chain` argument allows you to select which network to connect to. If not specified, it defaults to mainnet. 32 | 33 | ### Mainnet 34 | 35 | To connect to mainnet (default): 36 | 37 | ```bash 38 | # Uses default wallet location: ./wallets/ 39 | ./target/release/zingo-cli 40 | 41 | # Or explicitly specify mainnet: 42 | ./target/release/zingo-cli --chain mainnet 43 | 44 | # Or specify a custom data directory: 45 | ./target/release/zingo-cli --data-dir /path/to/mainnet-wallet 46 | ``` 47 | 48 | ### Testnet 49 | 50 | To connect to testnet: 51 | 52 | ```bash 53 | # Uses default wallet location: ./wallets/ 54 | ./target/release/zingo-cli --chain testnet 55 | 56 | # Or specify a custom data directory: 57 | ./target/release/zingo-cli --chain testnet --data-dir /path/to/testnet-wallet 58 | ``` 59 | 60 | ### Regtest Mode 61 | 62 | To run in regtest mode: 63 | 1. Build the zingo-cli binary with the `regtest` feature flag enabled 64 | ```bash 65 | cargo build --release -p zingo-cli --features regtest 66 | ``` 67 | 2. Launch a validator, see details below for an example of launching zcashd and generating blocks with zcash-cli. 68 | 3. Launch an indexer/lightserver, see details below for an example of launching lightwalletd. 69 | 4. Create a wallet directory (data-dir) and run zingo-cli, 70 | ```bash 71 | ./target/release/zingo-cli --chain regtest --server 127.0.0.1:9067 --data-dir ~/tmp/regtest_temp 72 | ``` 73 | 74 | **Note:** The zcash_local_net crate will soon offer a binary for simplifying the process of launching and interacting with the local network. 75 | https://github.com/zingolabs/infrastructure/tree/dev/zcash_local_net 76 | 77 | #### Example: Launching a Local Network 78 | 79 | 1. Create a directory for zcashd with a `data` directory inside. 80 | 2. Add a `zcash.conf` config file to the main zcashd directory, see below for an example config. 81 | 3. Run zcashd: 82 | ```bash 83 | zcashd --printtoconsole --conf=/home/user/tmp/zcashd_regtest/zcash.conf --datadir=/home/user/tmp/zcashd_regtest/data -debug=1 84 | ``` 85 | 4. Create a directory for lightwalletd with a `data` and `logs` directory inside. 86 | 5. Create a `lwd.log` file inside the `logs` directory. 87 | 6. Add a `lightwalletd.yml` config file to the main lightwalletd directory, see below for an example config. 88 | 7. In a new command prompt, run lightwalletd: 89 | ```bash 90 | lightwalletd --no-tls-very-insecure --data-dir /home/user/tmp/lwd_regtest/data/ --log-file /home/user/tmp/lwd_regtest/logs/lwd.log --zcash-conf-path /home/user/tmp/zcashd_regtest/zcash.conf --config /home/user/tmp/lwd_regtest/lightwalletd.yml 91 | ``` 92 | 8. In a new command prompt, generate blocks: 93 | ```bash 94 | zcash-cli -conf=/home/user/tmp/zcashd_regtest/zcash.conf generate 1 95 | ``` 96 | 97 | ### Example: Zcashd Config File 98 | 99 | ``` 100 | ### Blockchain Configuration 101 | regtest=1 102 | nuparams=5ba81b19:1 # Overwinter 103 | nuparams=76b809bb:1 # Sapling 104 | nuparams=2bb40e60:1 # Blossom 105 | nuparams=f5b9230b:1 # Heartwood 106 | nuparams=e9ff75a6:1 # Canopy 107 | nuparams=c2d6d0b4:1 # NU5 (Orchard) 108 | nuparams=c8e71055:1 # NU6 109 | nuparams=4dec4df0:1 # NU6_1 https://zips.z.cash/zip-0255#nu6.1deployment 110 | 111 | ### MetaData Storage and Retrieval 112 | # txindex: 113 | # https://zcash.readthedocs.io/en/latest/rtd_pages/zcash_conf_guide.html#miscellaneous-options 114 | txindex=1 115 | # insightexplorer: 116 | # https://zcash.readthedocs.io/en/latest/rtd_pages/insight_explorer.html?highlight=insightexplorer#additional-getrawtransaction-fields 117 | insightexplorer=1 118 | experimentalfeatures=1 119 | lightwalletd=1 120 | 121 | ### RPC Server Interface Options: 122 | # https://zcash.readthedocs.io/en/latest/rtd_pages/zcash_conf_guide.html#json-rpc-options 123 | rpcuser=xxxxxx 124 | rpcpassword=xxxxxx 125 | rpcport=8232 126 | rpcallowip=127.0.0.1 127 | 128 | # Buried config option to allow non-canonical RPC-PORT: 129 | # https://zcash.readthedocs.io/en/latest/rtd_pages/zcash_conf_guide.html#zcash-conf-guide 130 | listen=0 131 | 132 | i-am-aware-zcashd-will-be-replaced-by-zebrad-and-zallet-in-2025=1 133 | 134 | ### Zcashd Help provides documentation of the following: 135 | mineraddress=uregtest1zkuzfv5m3yhv2j4fmvq5rjurkxenxyq8r7h4daun2zkznrjaa8ra8asgdm8wwgwjvlwwrxx7347r8w0ee6dqyw4rufw4wg9djwcr6frzkezmdw6dud3wsm99eany5r8wgsctlxquu009nzd6hsme2tcsk0v3sgjvxa70er7h27z5epr67p5q767s2z5gt88paru56mxpm6pwz0cu35m 136 | minetolocalwallet=0 # This is set to false so that we can mine to a wallet, other than the zcashd wallet. 137 | ``` 138 | 139 | ### Example: Zcashd Config File 140 | 141 | ``` 142 | grpc-bind-addr: 127.0.0.1:9067 143 | cache-size: 10 144 | log-file: /home/user/tmp/lwd_regtest/logs/lwd.log 145 | log-level: 10 146 | zcash-conf-path: /home/user/tmp/zcashd_regtest/zcash.conf 147 | ``` 148 | 149 | ## Exiting the CLI 150 | 151 | To quit the Zingo CLI, use the `quit` command (not `exit`). 152 | 153 | **Note:** Each network (mainnet, testnet, regtest) requires its own wallet data. If you get an error about wallet chain name mismatch, ensure you're using the correct data directory for your chosen network. 154 | -------------------------------------------------------------------------------- /libtonode-tests/tests/scripts/start_darksidewalletd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | grpcurl -plaintext -d '{ "saplingActivation" : 663150, "branchID" : "2bb40e60", "chainName" : "main"}' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/Reset 4 | 5 | grpcurl -plaintext -d '{ "url" : "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-height-reorg/before-reorg.txt" }' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/StageBlocks 6 | ## Initialize dataset 7 | sleep 1 8 | 9 | grpcurl -plaintext -d '{ 10 | "network": "main", 11 | "height": "663150", 12 | "hash": "0000000002fd3be4c24c437bd22620901617125ec2a3a6c902ec9a6c06f734fc", 13 | "time": 1576821833, 14 | "saplingTree": "01ec6278a1bed9e1b080fd60ef50eb17411645e3746ff129283712bc4757ecc833001001b4e1d4a26ac4a2810b57a14f4ffb69395f55dde5674ecd2462af96f9126e054701a36afb68534f640938bdffd80dfcb3f4d5e232488abbf67d049b33a761e7ed6901a16e35205fb7fe626a9b13fc43e1d2b98a9c241f99f93d5e93a735454073025401f5b9bcbf3d0e3c83f95ee79299e8aeadf30af07717bda15ffb7a3d00243b58570001fa6d4c2390e205f81d86b85ace0b48f3ce0afb78eeef3e14c70bcfd7c5f0191c0000011bc9521263584de20822f9483e7edb5af54150c4823c775b2efc6a1eded9625501a6030f8d4b588681eddb66cad63f09c5c7519db49500fc56ebd481ce5e903c22000163f4eec5a2fe00a5f45e71e1542ff01e937d2210c99f03addcce5314a5278b2d0163ab01f46a3bb6ea46f5a19d5bdd59eb3f81e19cfa6d10ab0fd5566c7a16992601fa6980c053d84f809b6abcf35690f03a11f87b28e3240828e32e3f57af41e54e01319312241b0031e3a255b0d708750b4cb3f3fe79e3503fe488cc8db1dd00753801754bb593ea42d231a7ddf367640f09bbf59dc00f2c1d2003cc340e0c016b5b13" 15 | }' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/AddTreeState 16 | 17 | grpcurl -plaintext -d '{ 18 | "network": "main", 19 | "height": "663173", 20 | "hash": "0000000001b80f69c0b4a02228b59f2793ba9a4c3e8742fe56b9c9e331a6005d", 21 | "time": 1576823459, 22 | "saplingTree": "018310f940d4b52399e656d8a090bd87f64a4abe80fffb2b4a882675def41f943c0114dd70a8a8a22869785b86b1dcad5e9508f419aad84ef8e83d50ec061117022310000199517be06af7c07c2d393c590c62add4dbcd9cc3402167521786f91a5d01d538000001989561014441f9f9043e11c01e220730df2219c090fa02f58d278fb7f447271601fa6d4c2390e205f81d86b85ace0b48f3ce0afb78eeef3e14c70bcfd7c5f0191c0000011bc9521263584de20822f9483e7edb5af54150c4823c775b2efc6a1eded9625501a6030f8d4b588681eddb66cad63f09c5c7519db49500fc56ebd481ce5e903c22000163f4eec5a2fe00a5f45e71e1542ff01e937d2210c99f03addcce5314a5278b2d0163ab01f46a3bb6ea46f5a19d5bdd59eb3f81e19cfa6d10ab0fd5566c7a16992601fa6980c053d84f809b6abcf35690f03a11f87b28e3240828e32e3f57af41e54e01319312241b0031e3a255b0d708750b4cb3f3fe79e3503fe488cc8db1dd00753801754bb593ea42d231a7ddf367640f09bbf59dc00f2c1d2003cc340e0c016b5b13" 23 | }' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/AddTreeState 24 | 25 | 26 | grpcurl -plaintext -d '{ 27 | "network": "main", 28 | "height": "663187", 29 | "hash": "00000000027fa4bfd6c012325a44eef7211a6162b5979507f07603333e9b3068", 30 | "time": 1576824317, 31 | "saplingTree": "018d5b2b12a89cabbeb6c98bde09fbea143d49ea8e4dbf1d612e4906e73e1af96b001000018b01c7e7b2b183d022fc35e351e6423aee8885debc899036d0bc3b389c9f161501dba0595ce728b41452a9c595341074cf01e1152abe401db2b30a9ab007ad006e0001989561014441f9f9043e11c01e220730df2219c090fa02f58d278fb7f447271601fa6d4c2390e205f81d86b85ace0b48f3ce0afb78eeef3e14c70bcfd7c5f0191c0000011bc9521263584de20822f9483e7edb5af54150c4823c775b2efc6a1eded9625501a6030f8d4b588681eddb66cad63f09c5c7519db49500fc56ebd481ce5e903c22000163f4eec5a2fe00a5f45e71e1542ff01e937d2210c99f03addcce5314a5278b2d0163ab01f46a3bb6ea46f5a19d5bdd59eb3f81e19cfa6d10ab0fd5566c7a16992601fa6980c053d84f809b6abcf35690f03a11f87b28e3240828e32e3f57af41e54e01319312241b0031e3a255b0d708750b4cb3f3fe79e3503fe488cc8db1dd00753801754bb593ea42d231a7ddf367640f09bbf59dc00f2c1d2003cc340e0c016b5b13" 32 | }' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/AddTreeState 33 | 34 | grpcurl -plaintext -d '{ 35 | "network": "main", 36 | "height": "663189", 37 | "hash": "00000000025a03b6b11364b9c01a5fc9a927eca98ae899bb88f0ddf2de77fda3", 38 | "time": 1576824596, 39 | "saplingTree": "01f1c5f08a96b8e0befe04ff061dd51ed96ffcc7fcd96d011dc06255692989731c001001ea6dbe4e95ec503600ee3260105ffcc2ceb63eb34ec323c794eebfc49b7beb2c018b01c7e7b2b183d022fc35e351e6423aee8885debc899036d0bc3b389c9f161501dba0595ce728b41452a9c595341074cf01e1152abe401db2b30a9ab007ad006e0001989561014441f9f9043e11c01e220730df2219c090fa02f58d278fb7f447271601fa6d4c2390e205f81d86b85ace0b48f3ce0afb78eeef3e14c70bcfd7c5f0191c0000011bc9521263584de20822f9483e7edb5af54150c4823c775b2efc6a1eded9625501a6030f8d4b588681eddb66cad63f09c5c7519db49500fc56ebd481ce5e903c22000163f4eec5a2fe00a5f45e71e1542ff01e937d2210c99f03addcce5314a5278b2d0163ab01f46a3bb6ea46f5a19d5bdd59eb3f81e19cfa6d10ab0fd5566c7a16992601fa6980c053d84f809b6abcf35690f03a11f87b28e3240828e32e3f57af41e54e01319312241b0031e3a255b0d708750b4cb3f3fe79e3503fe488cc8db1dd00753801754bb593ea42d231a7ddf367640f09bbf59dc00f2c1d2003cc340e0c016b5b13" 40 | }' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/AddTreeState 41 | 42 | 43 | grpcurl -plaintext -d '{ 44 | "network": "main", 45 | "height": "663194", 46 | "hash": "000000000096e4f59faae478c0ea5cb982c1cba223a92d15fbcf74795a0b4e5d", 47 | "time": 1576824759, 48 | "saplingTree": "01f1c5f08a96b8e0befe04ff061dd51ed96ffcc7fcd96d011dc06255692989731c001001ea6dbe4e95ec503600ee3260105ffcc2ceb63eb34ec323c794eebfc49b7beb2c018b01c7e7b2b183d022fc35e351e6423aee8885debc899036d0bc3b389c9f161501dba0595ce728b41452a9c595341074cf01e1152abe401db2b30a9ab007ad006e0001989561014441f9f9043e11c01e220730df2219c090fa02f58d278fb7f447271601fa6d4c2390e205f81d86b85ace0b48f3ce0afb78eeef3e14c70bcfd7c5f0191c0000011bc9521263584de20822f9483e7edb5af54150c4823c775b2efc6a1eded9625501a6030f8d4b588681eddb66cad63f09c5c7519db49500fc56ebd481ce5e903c22000163f4eec5a2fe00a5f45e71e1542ff01e937d2210c99f03addcce5314a5278b2d0163ab01f46a3bb6ea46f5a19d5bdd59eb3f81e19cfa6d10ab0fd5566c7a16992601fa6980c053d84f809b6abcf35690f03a11f87b28e3240828e32e3f57af41e54e01319312241b0031e3a255b0d708750b4cb3f3fe79e3503fe488cc8db1dd00753801754bb593ea42d231a7ddf367640f09bbf59dc00f2c1d2003cc340e0c016b5b13" 49 | }' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/AddTreeState 50 | 51 | grpcurl -plaintext -d '{ "height" : 663200 }' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/ApplyStaged 52 | 53 | -------------------------------------------------------------------------------- /zingolib/src/testutils/lightclient.rs: -------------------------------------------------------------------------------- 1 | //! This mod is mostly to take inputs, raw data amd convert it into lightclient actions 2 | //! (obviously) in a test environment. 3 | 4 | use zcash_primitives::transaction::TxId; 5 | use zcash_protocol::{PoolType, ShieldedProtocol}; 6 | 7 | use crate::{ 8 | lightclient::{LightClient, error::LightClientError}, 9 | wallet::LightWallet, 10 | }; 11 | 12 | /// Create a lightclient from the buffer of another 13 | pub async fn new_client_from_save_buffer( 14 | template_client: &mut LightClient, 15 | ) -> Result { 16 | let mut wallet_bytes: Vec = vec![]; 17 | template_client 18 | .wallet 19 | .write() 20 | .await 21 | .write(&mut wallet_bytes, &template_client.config.chain)?; 22 | 23 | LightClient::create_from_wallet( 24 | LightWallet::read(wallet_bytes.as_slice(), template_client.config.chain)?, 25 | template_client.config.clone(), 26 | false, 27 | ) 28 | } 29 | /// gets the first address that will allow a sender to send to a specific pool, as a string 30 | pub async fn get_base_address(client: &LightClient, pooltype: PoolType) -> String { 31 | match pooltype { 32 | PoolType::Shielded(ShieldedProtocol::Orchard) => { 33 | assert!( 34 | client.unified_addresses_json().await[0]["has_orchard"] 35 | .as_bool() 36 | .unwrap() 37 | ); 38 | client.unified_addresses_json().await[0]["encoded_address"] 39 | .clone() 40 | .to_string() 41 | } 42 | PoolType::Shielded(ShieldedProtocol::Sapling) => { 43 | assert!( 44 | !client.unified_addresses_json().await[1]["has_orchard"] 45 | .as_bool() 46 | .unwrap() 47 | ); 48 | assert!( 49 | client.unified_addresses_json().await[1]["has_sapling"] 50 | .as_bool() 51 | .unwrap() 52 | ); 53 | client.unified_addresses_json().await[1]["encoded_address"] 54 | .clone() 55 | .to_string() 56 | } 57 | PoolType::Transparent => client.transparent_addresses_json().await[0]["encoded_address"] 58 | .clone() 59 | .to_string(), 60 | } 61 | } 62 | /// Get the total fees paid by a given client (assumes 1 capability per client). 63 | pub async fn get_fees_paid_by_client(client: &LightClient) -> u64 { 64 | client 65 | .transaction_summaries(false) 66 | .await 67 | .unwrap() 68 | .paid_fees() 69 | } 70 | /// Helpers to provide `raw_receivers` to lightclients for send and shield, etc. 71 | pub mod from_inputs { 72 | 73 | use nonempty::NonEmpty; 74 | use zcash_primitives::transaction::TxId; 75 | 76 | use crate::{ 77 | lightclient::{LightClient, error::QuickSendError}, 78 | wallet::error::ProposeSendError, 79 | }; 80 | 81 | /// Panics if the address, amount or memo conversion fails. 82 | pub async fn quick_send( 83 | quick_sender: &mut crate::lightclient::LightClient, 84 | raw_receivers: Vec<(&str, u64, Option<&str>)>, 85 | ) -> Result, QuickSendError> { 86 | let request = transaction_request_from_send_inputs(raw_receivers) 87 | .expect("should be able to create a transaction request as receivers are valid."); 88 | quick_sender 89 | .quick_send(request, zip32::AccountId::ZERO, true) 90 | .await 91 | } 92 | 93 | /// Panics if the address, amount or memo conversion fails. 94 | pub(crate) fn receivers_from_send_inputs( 95 | raw_receivers: Vec<(&str, u64, Option<&str>)>, 96 | ) -> crate::data::receivers::Receivers { 97 | raw_receivers 98 | .into_iter() 99 | .map(|(address, amount, memo)| { 100 | let recipient_address = crate::utils::conversion::address_from_str(address) 101 | .expect("should be a valid address"); 102 | let amount = crate::utils::conversion::zatoshis_from_u64(amount) 103 | .expect("should be inside the range of valid zatoshis"); 104 | let memo = memo.map(|memo| { 105 | crate::wallet::utils::interpret_memo_string(memo.to_string()) 106 | .expect("should be able to interpret memo") 107 | }); 108 | 109 | crate::data::receivers::Receiver::new(recipient_address, amount, memo) 110 | }) 111 | .collect() 112 | } 113 | 114 | /// Creates a [`zcash_client_backend::zip321::TransactionRequest`] from rust primitives for simplified test writing. 115 | pub fn transaction_request_from_send_inputs( 116 | raw_receivers: Vec<(&str, u64, Option<&str>)>, 117 | ) -> Result< 118 | zcash_client_backend::zip321::TransactionRequest, 119 | zcash_client_backend::zip321::Zip321Error, 120 | > { 121 | let receivers = receivers_from_send_inputs(raw_receivers); 122 | crate::data::receivers::transaction_request_from_receivers(receivers) 123 | } 124 | 125 | /// Panics if the address, amount or memo conversion fails. 126 | pub async fn propose( 127 | proposer: &mut LightClient, 128 | raw_receivers: Vec<(&str, u64, Option<&str>)>, 129 | ) -> Result { 130 | let request = transaction_request_from_send_inputs(raw_receivers) 131 | .expect("should be able to create a transaction request as receivers are valid."); 132 | proposer.propose_send(request, zip32::AccountId::ZERO).await 133 | } 134 | } 135 | 136 | /// gets stati for a vec of txids 137 | pub async fn lookup_statuses( 138 | client: &LightClient, 139 | txids: nonempty::NonEmpty, 140 | ) -> nonempty::NonEmpty> { 141 | let wallet = client.wallet.read().await; 142 | 143 | txids.map(|txid| { 144 | wallet 145 | .wallet_transactions 146 | .get(&txid) 147 | .map(pepper_sync::wallet::WalletTransaction::status) 148 | }) 149 | } 150 | --------------------------------------------------------------------------------