From 1b45874191f7faf6544693ecb86b5feb67682ade Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Tue, 13 Apr 2021 19:58:31 -0400 Subject: [PATCH] Compiling with tonic. --- Cargo.toml | 6 ++ build.rs | 7 ++ proto/.keep | 0 proto/compact_formats.proto | 48 ++++++++++ proto/service.proto | 177 ++++++++++++++++++++++++++++++++++++ src/main.rs | 94 +++++++++++++++++-- 6 files changed, 325 insertions(+), 7 deletions(-) create mode 100644 build.rs create mode 100644 proto/.keep create mode 100644 proto/compact_formats.proto create mode 100644 proto/service.proto diff --git a/Cargo.toml b/Cargo.toml index 3d13fdc..adfe8ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,11 +8,17 @@ edition = "2018" [dependencies] failure = "0.1" + +tonic = { version = "0.4.2", features = ["tls", "tls-roots"] } +prost = "0.7" + tiny-bip39 = "0.8.0" zcash_primitives = "0.5" zcash_client_sqlite = "0.3" zcash_client_backend = "0.5" +[build-dependencies] +tonic-build = "0.4" [patch.crates-io] zcash_primitives = { path = '../../clones/librustzcash/zcash_primitives' } diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..7263ca7 --- /dev/null +++ b/build.rs @@ -0,0 +1,7 @@ + +fn main() -> Result<(), Box> { + tonic_build::configure() + .build_server(false) + .compile(&["proto/compact_formats.proto", "proto/service.proto"], &["proto"])?; + Ok(()) +} diff --git a/proto/.keep b/proto/.keep new file mode 100644 index 0000000..e69de29 diff --git a/proto/compact_formats.proto b/proto/compact_formats.proto new file mode 100644 index 0000000..e20c05a --- /dev/null +++ b/proto/compact_formats.proto @@ -0,0 +1,48 @@ +syntax = "proto3"; +package cash.z.wallet.sdk.rpc; +option go_package = "walletrpc"; +option swift_prefix = ""; +// Remember that proto3 fields are all optional. A field that is not present will be set to its zero value. +// bytes fields of hashes are in canonical little-endian format. + +// CompactBlock is a packaging of ONLY the data from a block that's needed to: +// 1. Detect a payment to your shielded Sapling address +// 2. Detect a spend of your shielded Sapling notes +// 3. Update your witnesses to generate new Sapling spend proofs. +message CompactBlock { + uint32 protoVersion = 1; // the version of this wire format, for storage + uint64 height = 2; // the height of this block + bytes hash = 3; + bytes prevHash = 4; + uint32 time = 5; + bytes header = 6; // (hash, prevHash, and time) OR (full header) + repeated CompactTx vtx = 7; // compact transactions from this block +} + +message CompactTx { + // Index and hash will allow the receiver to call out to chain + // explorers or other data structures to retrieve more information + // about this transaction. + uint64 index = 1; + bytes hash = 2; + + // The transaction fee: present if server can provide. In the case of a + // stateless server and a transaction with transparent inputs, this will be + // unset because the calculation requires reference to prior transactions. + // in a pure-Sapling context, the fee will be calculable as: + // valueBalance + (sum(vPubNew) - sum(vPubOld) - sum(tOut)) + uint32 fee = 3; + + repeated CompactSpend spends = 4; + repeated CompactOutput outputs = 5; +} + +message CompactSpend { + bytes nf = 1; +} + +message CompactOutput { + bytes cmu = 1; + bytes epk = 2; + bytes ciphertext = 3; +} diff --git a/proto/service.proto b/proto/service.proto new file mode 100644 index 0000000..d0a7085 --- /dev/null +++ b/proto/service.proto @@ -0,0 +1,177 @@ +// Copyright (c) 2019-2020 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +syntax = "proto3"; +package cash.z.wallet.sdk.rpc; +option go_package = ".;walletrpc"; +option swift_prefix = ""; +import "compact_formats.proto"; + +// A BlockID message contains identifiers to select a block: a height or a +// hash. Specification by hash is not implemented, but may be in the future. +message BlockID { + uint64 height = 1; + bytes hash = 2; +} + +// BlockRange specifies a series of blocks from start to end inclusive. +// Both BlockIDs must be heights; specification by hash is not yet supported. +message BlockRange { + BlockID start = 1; + BlockID end = 2; +} + +// A TxFilter contains the information needed to identify a particular +// transaction: either a block and an index, or a direct transaction hash. +// Currently, only specification by hash is supported. +message TxFilter { + BlockID block = 1; // block identifier, height or hash + uint64 index = 2; // index within the block + bytes hash = 3; // transaction ID (hash, txid) +} + +// RawTransaction contains the complete transaction data. It also optionally includes +// the block height in which the transaction was included. +message RawTransaction { + bytes data = 1; // exact data returned by Zcash 'getrawtransaction' + uint64 height = 2; // height that the transaction was mined (or -1) +} + +// A SendResponse encodes an error code and a string. It is currently used +// only by SendTransaction(). If error code is zero, the operation was +// successful; if non-zero, it and the message specify the failure. +message SendResponse { + int32 errorCode = 1; + string errorMessage = 2; +} + +// Chainspec is a placeholder to allow specification of a particular chain fork. +message ChainSpec {} + +// Empty is for gRPCs that take no arguments, currently only GetLightdInfo. +message Empty {} + +// LightdInfo returns various information about this lightwalletd instance +// and the state of the blockchain. +message LightdInfo { + string version = 1; + string vendor = 2; + bool taddrSupport = 3; // true + string chainName = 4; // either "main" or "test" + uint64 saplingActivationHeight = 5; // depends on mainnet or testnet + string consensusBranchId = 6; // protocol identifier, see consensus/upgrades.cpp + uint64 blockHeight = 7; // latest block on the best chain + string gitCommit = 8; + string branch = 9; + string buildDate = 10; + string buildUser = 11; + uint64 estimatedHeight = 12; // less than tip height if zcashd is syncing + string zcashdBuild = 13; // example: "v4.1.1-877212414" + string zcashdSubversion = 14; // example: "/MagicBean:4.1.1/" +} + +// TransparentAddressBlockFilter restricts the results to the given address +// or block range. +message TransparentAddressBlockFilter { + string address = 1; // t-address + BlockRange range = 2; // start, end heights +} + +// Duration is currently used only for testing, so that the Ping rpc +// can simulate a delay, to create many simultaneous connections. Units +// are microseconds. +message Duration { + int64 intervalUs = 1; +} + +// PingResponse is used to indicate concurrency, how many Ping rpcs +// are executing upon entry and upon exit (after the delay). +// This rpc is used for testing only. +message PingResponse { + int64 entry = 1; + int64 exit = 2; +} + +message Address { + string address = 1; +} +message AddressList { + repeated string addresses = 1; +} +message Balance { + int64 valueZat = 1; +} + +message Exclude { + repeated bytes txid = 1; +} + +// The TreeState is derived from the Zcash z_gettreestate rpc. +message TreeState { + string network = 1; // "main" or "test" + uint64 height = 2; + string hash = 3; // block id + uint32 time = 4; // Unix epoch time when the block was mined + string tree = 5; // sapling commitment tree state +} + +message GetAddressUtxosArg { + string address = 1; + uint64 startHeight = 2; + uint32 maxEntries = 3; // zero means unlimited +} +message GetAddressUtxosReply { + bytes txid = 1; + int32 index = 2; + bytes script = 3; + int64 valueZat = 4; + uint64 height = 5; +} +message GetAddressUtxosReplyList { + repeated GetAddressUtxosReply addressUtxos = 1; +} + +service CompactTxStreamer { + // Return the height of the tip of the best chain + rpc GetLatestBlock(ChainSpec) returns (BlockID) {} + // Return the compact block corresponding to the given block identifier + rpc GetBlock(BlockID) returns (CompactBlock) {} + // Return a list of consecutive compact blocks + rpc GetBlockRange(BlockRange) returns (stream CompactBlock) {} + + // Return the requested full (not compact) transaction (as from zcashd) + rpc GetTransaction(TxFilter) returns (RawTransaction) {} + // Submit the given transaction to the Zcash network + rpc SendTransaction(RawTransaction) returns (SendResponse) {} + + // Return the txids corresponding to the given t-address within the given block range + rpc GetTaddressTxids(TransparentAddressBlockFilter) returns (stream RawTransaction) {} + rpc GetTaddressBalance(AddressList) returns (Balance) {} + rpc GetTaddressBalanceStream(stream Address) returns (Balance) {} + + // Return the compact transactions currently in the mempool; the results + // can be a few seconds out of date. If the Exclude list is empty, return + // all transactions; otherwise return all *except* those in the Exclude list + // (if any); this allows the client to avoid receiving transactions that it + // already has (from an earlier call to this rpc). The transaction IDs in the + // Exclude list can be shortened to any number of bytes to make the request + // more bandwidth-efficient; if two or more transactions in the mempool + // match a shortened txid, they are all sent (none is excluded). Transactions + // in the exclude list that don't exist in the mempool are ignored. + rpc GetMempoolTx(Exclude) returns (stream CompactTx) {} + + // GetTreeState returns the note commitment tree state corresponding to the given block. + // See section 3.7 of the Zcash protocol specification. It returns several other useful + // values also (even though they can be obtained using GetBlock). + // The block can be specified by either height or hash. + rpc GetTreeState(BlockID) returns (TreeState) {} + + rpc GetAddressUtxos(GetAddressUtxosArg) returns (GetAddressUtxosReplyList) {} + rpc GetAddressUtxosStream(GetAddressUtxosArg) returns (stream GetAddressUtxosReply) {} + + // Return information about this lightwalletd instance and the blockchain + rpc GetLightdInfo(Empty) returns (LightdInfo) {} + // Testing-only + rpc Ping(Duration) returns (PingResponse) {} +} diff --git a/src/main.rs b/src/main.rs index b03c273..e77026f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,20 +3,35 @@ use bip39::{Mnemonic, Language, Seed}; use failure::format_err; use std::path::Path; -use zcash_primitives::{consensus::{MainNetwork, Parameters}, transaction::Transaction}; -use zcash_client_backend::data_api::WalletRead; +use zcash_primitives::{ + consensus::{ + MainNetwork, Parameters, NetworkUpgrade + }, + transaction::Transaction +}; +use zcash_client_backend::{data_api::{DecryptedTransaction, WalletRead, WalletWrite, error::Error}, decrypt_transaction}; use zcash_client_sqlite::WalletDB; fn main() { // TODO: get this path from CLI args + let network = MainNetwork; let db_path = "/home/gmale/kg/work/clones/librustzcash/ZcashSdk_mainnet_Data.db"; - let db_data = wallet_db(db_path, MainNetwork).unwrap(); + let db_path_tmp = "/home/gmale/kg/work/clones/librustzcash/temp.db"; + + let db_data = wallet_db(db_path, network).unwrap(); + let db_tmp = wallet_db(db_path_tmp, network).unwrap(); let phrase = "chat error pigeon main parade window scene breeze scene frog inherit enforce wise resist rotate van pistol coral tide faint arm elegant velvet anxiety"; show_seed(phrase); - let tx = load_tx(&db_data, 3); - println!("loaded tx: {:?}", &tx.unwrap()); + let tx = load_tx(&db_data, 25); + let t = tx.unwrap(); + println!("loaded tx: {:?}", &t); + println!("tx.vout: {} tx.vin: {} tx.shout: {} tx.shin: {}", t.vout.len(), t.vin.len(), t.shielded_outputs.len(), t.shielded_spends.len()); + + let mut db_tmp = db_tmp.get_update_ops().unwrap(); + println!("decrypting transaction into temp DB..."); + decrypt_tx_to(&db_data, &mut db_tmp, &network, 3).unwrap() } fn wallet_db(db_path: &str, params: P) -> Result, failure::Error> { @@ -28,10 +43,49 @@ fn wallet_db(db_path: &str, params: P) -> Result, fai } } -fn load_tx(db_data: &WalletDB, id_tx: i64) -> Result { +// fn fetch_tx(db_data: &WalletDB

, id_tx: i64) -> Result { +// let tx = load_tx(&db_data, id_tx); +// } + +fn load_tx(db_data: &WalletDB

, id_tx: i64) -> Result { return (&db_data).get_transaction(id_tx).map_err(|_| format_err!("Invalid amount, out of range")); } +/// Take a transaction out of one db, then decrypt it and store it in another db. +/// This is useful for exercising decrypt code to see what it discovers, +/// without contaminating the original data. +fn decrypt_tx_to(db_src: &WalletDB

, db_dest: &mut W, params: &P, id_tx: i64) -> Result<(), E> +where + E: From>, + P: Parameters, + W: WalletWrite +{ + let tx = &load_tx(&db_src, id_tx).unwrap(); + // Fetch the ExtendedFullViewingKeys we are tracking + let extfvks = db_src.get_extended_full_viewing_keys().unwrap(); + + // Height is block height for mined transactions, and the "mempool height" (chain height + 1) + // for mempool transactions. + let height = db_src + .get_tx_height(tx.txid()).unwrap() + .or(db_src + .block_height_extrema().unwrap() + .map(|(_, max_height)| max_height + 1)) + .or_else(|| params.activation_height(NetworkUpgrade::Sapling)) + .ok_or(Error::SaplingNotActive)?; + + let sapling_outputs = decrypt_transaction(params, height, tx, &extfvks); + let nullifiers = db_src.get_nullifiers().unwrap(); + + if !(sapling_outputs.is_empty() && tx.vout.is_empty()) { + db_dest.store_decrypted_tx( + &DecryptedTransaction { tx, sapling_outputs: &sapling_outputs, }, + &nullifiers, + )?; + } + + Ok(()) +} @@ -42,4 +96,30 @@ fn show_seed(phrase: &str) { let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap(); let seed = Seed::new(&mnemonic, ""); println!("{:X}", seed); -} \ No newline at end of file +} + + + + +// GRPC things + +// fn init_grpc() -> CompactTxStreamerClient { +// let tls = { +// let mut tls_connector = tls_api_rustls::TlsConnector::builder()?; + +// if tls_api_rustls::TlsConnector::supports_alpn() { +// tls_connector.set_alpn_protocols(&[b"h2"])?; +// } + +// let tls_connector = tls_connector.build()?; + +// let tls_connector = Arc::new(tls_connector); +// ClientTlsOption::Tls(LIGHTWALLETD_HOST.to_owned(), tls_connector) +// }; + +// return grpc::ClientBuilder::new(LIGHTWALLETD_HOST, LIGHTWALLETD_PORT) +// .explicit_tls(tls) +// .build() +// .map(|c| service_grpc::CompactTxStreamerClient::with_client(Arc::new(c)))?; + +// } \ No newline at end of file