[WIP] shield funds PoC
This commit is contained in:
parent
0c9f3cb5ef
commit
d75d5cb3ee
|
@ -49,9 +49,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6789e291be47ace86a60303502173d84af8327e3627ecf334356ee0f87a164c"
|
||||
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
|
@ -340,9 +340,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab"
|
||||
checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
|
@ -429,7 +429,7 @@ checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
|||
[[package]]
|
||||
name = "equihash"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nuttycom/librustzcash?branch=data_access_api#e96578195adfe0a80bc4be897e72d89813ea7387"
|
||||
source = "git+https://github.com/pacu/librustzcash?branch=autoshield-poc#dc99e1e130e859e5f17dbdbb55cb0f505ffb37e9"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"byteorder",
|
||||
|
@ -597,14 +597,14 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"rand",
|
||||
"ring",
|
||||
"secp256k1",
|
||||
"secp256k1 0.17.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||
checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
@ -674,9 +674,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.80"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
|
||||
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
|
@ -701,8 +701,9 @@ dependencies = [
|
|||
"hdwallet",
|
||||
"hex",
|
||||
"ripemd160",
|
||||
"secp256k1",
|
||||
"secp256k1 0.17.2",
|
||||
"sha2 0.9.2",
|
||||
"time",
|
||||
"zcash_client_backend",
|
||||
"zcash_client_sqlite",
|
||||
"zcash_primitives",
|
||||
|
@ -863,9 +864,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -971,9 +972,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.24.1"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e3d4791ab5517217f51216a84a688b53c1ebf7988736469c538d02f46ddba68"
|
||||
checksum = "d5f38ee71cbab2c827ec0ac24e76f82eca723cee92c509a65f67dee393c25112"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fallible-iterator",
|
||||
|
@ -1024,7 +1025,16 @@ version = "0.17.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2932dc07acd2066ff2e3921a4419606b220ba6cd03a9935123856cc534877056"
|
||||
dependencies = [
|
||||
"secp256k1-sys",
|
||||
"secp256k1-sys 0.1.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6179428c22c73ac0fbb7b5579a56353ce78ba29759b3b8575183336ea74cdfb"
|
||||
dependencies = [
|
||||
"secp256k1-sys 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1036,6 +1046,15 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1-sys"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11553d210db090930f4432bea123b31f70bbf693ace14504ea2a35e796c28dd2"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
|
@ -1053,18 +1072,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.117"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
|
||||
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.117"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
|
||||
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1115,9 +1134,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85"
|
||||
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
|
@ -1197,15 +1216,15 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
|||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd"
|
||||
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.53"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8833e20724c24de12bbaba5ad230ea61c3eafb05b881c7c9d3cfe8638b187e68"
|
||||
checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1287,9 +1306,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.7"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -1326,9 +1345,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
|||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.10"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
|
||||
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
|
@ -1443,7 +1462,7 @@ checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
|||
[[package]]
|
||||
name = "zcash_client_backend"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/nuttycom/librustzcash?branch=data_access_api#e96578195adfe0a80bc4be897e72d89813ea7387"
|
||||
source = "git+https://github.com/pacu/librustzcash?branch=autoshield-poc#dc99e1e130e859e5f17dbdbb55cb0f505ffb37e9"
|
||||
dependencies = [
|
||||
"base64 0.12.3",
|
||||
"bech32",
|
||||
|
@ -1466,7 +1485,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zcash_client_sqlite"
|
||||
version = "0.2.1"
|
||||
source = "git+https://github.com/nuttycom/librustzcash?branch=data_access_api#e96578195adfe0a80bc4be897e72d89813ea7387"
|
||||
source = "git+https://github.com/pacu/librustzcash?branch=autoshield-poc#dc99e1e130e859e5f17dbdbb55cb0f505ffb37e9"
|
||||
dependencies = [
|
||||
"bech32",
|
||||
"bs58",
|
||||
|
@ -1484,7 +1503,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zcash_primitives"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/nuttycom/librustzcash?branch=data_access_api#e96578195adfe0a80bc4be897e72d89813ea7387"
|
||||
source = "git+https://github.com/pacu/librustzcash?branch=autoshield-poc#dc99e1e130e859e5f17dbdbb55cb0f505ffb37e9"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"bitvec",
|
||||
|
@ -1503,6 +1522,8 @@ dependencies = [
|
|||
"log",
|
||||
"rand",
|
||||
"rand_core",
|
||||
"ripemd160",
|
||||
"secp256k1 0.19.0",
|
||||
"sha2 0.9.2",
|
||||
"subtle",
|
||||
]
|
||||
|
@ -1510,7 +1531,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zcash_proofs"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/nuttycom/librustzcash?branch=data_access_api#e96578195adfe0a80bc4be897e72d89813ea7387"
|
||||
source = "git+https://github.com/pacu/librustzcash?branch=autoshield-poc#dc99e1e130e859e5f17dbdbb55cb0f505ffb37e9"
|
||||
dependencies = [
|
||||
"bellman",
|
||||
"blake2b_simd",
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -13,7 +13,7 @@ ffi_helpers = "0.2"
|
|||
hex = "0.4"
|
||||
zcash_client_backend = "0.4"
|
||||
zcash_client_sqlite = "0.2.1"
|
||||
zcash_primitives = "0.4"
|
||||
zcash_primitives = { version = "0.4", features = ["transparent-inputs"] }
|
||||
|
||||
#### Temporary additions: ####################################
|
||||
base58 = "0.1.0"
|
||||
|
@ -22,8 +22,10 @@ bs58 = { version = "0.3", features = ["check"] }
|
|||
hdwallet = "0.2.2"
|
||||
ripemd160 = "0.9"
|
||||
secp256k1 = "0.17.2"
|
||||
time = "0.2"
|
||||
##############################################################
|
||||
|
||||
|
||||
[dependencies.zcash_proofs]
|
||||
version = "0.4"
|
||||
default-features = false
|
||||
|
@ -41,10 +43,10 @@ crate-type = ["staticlib"]
|
|||
lto = true
|
||||
|
||||
[patch.crates-io]
|
||||
zcash_client_backend = {git = "https://github.com/nuttycom/librustzcash", branch = "data_access_api"}
|
||||
zcash_client_sqlite = {git = "https://github.com/nuttycom/librustzcash", branch = "data_access_api"}
|
||||
zcash_primitives = {git = "https://github.com/nuttycom/librustzcash", branch = "data_access_api"}
|
||||
zcash_proofs = {git = "https://github.com/nuttycom/librustzcash", branch = "data_access_api"}
|
||||
zcash_client_backend = {git = "https://github.com/pacu/librustzcash", branch = "autoshield-poc" }
|
||||
zcash_client_sqlite = {git = "https://github.com/pacu/librustzcash", branch = "autoshield-poc" }
|
||||
zcash_primitives = {git = "https://github.com/pacu/librustzcash", branch = "autoshield-poc" }
|
||||
zcash_proofs = {git = "https://github.com/pacu/librustzcash", branch = "autoshield-poc" }
|
||||
|
||||
[features]
|
||||
mainnet = ["zcash_client_sqlite/mainnet"]
|
||||
|
|
|
@ -3,6 +3,31 @@
|
|||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
*
|
||||
* psst, hey I have a Major in Social Sciences. Consider using something else
|
||||
* Creates a transaction paying the specified address from the given account.
|
||||
*
|
||||
* Returns the row index of the newly-created transaction in the `transactions` table
|
||||
* within the data database. The caller can read the raw transaction bytes from the `raw`
|
||||
* column in order to broadcast the transaction to the network.
|
||||
*
|
||||
* Do not call this multiple times in parallel, or you will generate transactions that
|
||||
* double-spend the same notes.
|
||||
*/
|
||||
int64_t zcashlc_autoshield_funds(const uint8_t *db_data,
|
||||
uintptr_t db_data_len,
|
||||
const uint8_t *db_cache,
|
||||
uintptr_t db_cache_len,
|
||||
int32_t account,
|
||||
const char *tsk,
|
||||
const char *extsk,
|
||||
const char *memo,
|
||||
const uint8_t *spend_params,
|
||||
uintptr_t spend_params_len,
|
||||
const uint8_t *output_params,
|
||||
uintptr_t output_params_len);
|
||||
|
||||
int32_t zcashlc_branch_id_for_height(int32_t height);
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,10 +5,11 @@ use std::env;
|
|||
fn main() {
|
||||
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
|
||||
cbindgen::Builder::new()
|
||||
if let Ok(b) = cbindgen::Builder::new()
|
||||
.with_crate(crate_dir)
|
||||
.with_language(cbindgen::Language::C)
|
||||
.generate()
|
||||
.expect("Unable to generate bindings")
|
||||
.write_to_file("ZcashLightClientKit/zcashlc/zcashlc.h");
|
||||
.generate() {
|
||||
b.write_to_file("ZcashLightClientKit/zcashlc/zcashlc.h");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
312
rust/src/lib.rs
312
rust/src/lib.rs
|
@ -25,13 +25,22 @@ use zcash_client_backend::{
|
|||
use zcash_client_sqlite::{
|
||||
wallet::init::{init_accounts_table, init_blocks_table, init_data_database},
|
||||
BlockDB, NoteId, WalletDB,
|
||||
wallet::{UnspentTransactionOutput, get_utxos},
|
||||
};
|
||||
use zcash_primitives::{
|
||||
block::BlockHash,
|
||||
consensus::{BlockHeight, BranchId, Parameters},
|
||||
note_encryption::Memo,
|
||||
transaction::{components::Amount, Transaction},
|
||||
zip32::ExtendedFullViewingKey,
|
||||
transaction::components::{
|
||||
amount::DEFAULT_FEE,
|
||||
Amount,
|
||||
OutPoint,
|
||||
TxOut
|
||||
},
|
||||
legacy::Script,
|
||||
transaction::builder::Builder,
|
||||
transaction::Transaction,
|
||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||
};
|
||||
|
||||
#[cfg(feature = "mainnet")]
|
||||
|
@ -49,7 +58,10 @@ use base58::ToBase58;
|
|||
use sha2::{Digest, Sha256};
|
||||
// use zcash_primitives::legacy::TransparentAddress;
|
||||
use hdwallet::{ExtendedPrivKey, KeyIndex};
|
||||
use secp256k1::{PublicKey, Secp256k1};
|
||||
use secp256k1::{
|
||||
Secp256k1,
|
||||
key::{PublicKey, SecretKey},
|
||||
};
|
||||
|
||||
|
||||
// use crate::extended_key::{key_index::KeyIndex, ExtendedPrivKey, ExtendedPubKey, KeySeed};
|
||||
|
@ -314,15 +326,25 @@ pub unsafe extern "C" fn zcashlc_derive_shielded_address_from_seed(
|
|||
} else {
|
||||
return Err(format_err!("accounts argument must be greater than zero"));
|
||||
};
|
||||
let address = spending_key(&seed, NETWORK.coin_type(), account_index)
|
||||
.default_address()
|
||||
.unwrap()
|
||||
.1;
|
||||
let address_str = encode_payment_address(NETWORK.hrp_sapling_payment_address(), &address);
|
||||
let address_str = deriveShieldedAddressFromSeed(&NETWORK, &seed, account_index);
|
||||
Ok(CString::new(address_str).unwrap().into_raw())
|
||||
});
|
||||
unwrap_exc_or_null(res)
|
||||
}
|
||||
|
||||
fn deriveShieldedAddressFromSeed<P: Parameters>(params: &P, seed: &[u8], account_index: u32) -> String {
|
||||
let address = spending_key(&seed, NETWORK.coin_type(), account_index)
|
||||
.default_address()
|
||||
.unwrap()
|
||||
.1;
|
||||
encode_payment_address(params.hrp_sapling_payment_address(), &address)
|
||||
}
|
||||
|
||||
fn deriveShieldedAddressFromSpendingKey<P: Parameters>(params: &P, extsk: &ExtendedSpendingKey) -> String {
|
||||
let address = extsk.default_address().unwrap().1;
|
||||
encode_payment_address(params.hrp_sapling_payment_address(), &address)
|
||||
}
|
||||
|
||||
/// derives a shielded address from the given viewing key.
|
||||
/// call zcashlc_string_free with the returned pointer when done using it
|
||||
#[no_mangle]
|
||||
|
@ -366,17 +388,16 @@ pub unsafe extern "C" fn zcashlc_derive_extended_full_viewing_key(
|
|||
Ok(Some(extsk)) => ExtendedFullViewingKey::from(&extsk),
|
||||
Ok(None) => {
|
||||
return Err(format_err!("Deriving viewing key from spending key returned no results. Encoding was valid but type was incorrect."));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(format_err!(
|
||||
"Error while deriving viewing key from spending key: {}",
|
||||
e
|
||||
));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let encoded =
|
||||
encode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &extfvk);
|
||||
let encoded = encode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &extfvk);
|
||||
|
||||
Ok(CString::new(encoded).unwrap().into_raw())
|
||||
});
|
||||
|
@ -456,32 +477,28 @@ pub extern "C" fn zcashlc_get_address(
|
|||
pub unsafe extern "C" fn zcashlc_is_valid_shielded_address(address: *const c_char) -> bool {
|
||||
let res = catch_panic(|| {
|
||||
let addr = CStr::from_ptr(address).to_str()?;
|
||||
|
||||
match RecipientAddress::decode(&NETWORK, &addr) {
|
||||
Some(addr) => match addr {
|
||||
RecipientAddress::Shielded(_) => Ok(true),
|
||||
RecipientAddress::Transparent(_) => Ok(false),
|
||||
},
|
||||
None => Err(format_err!("Address is for the wrong network")),
|
||||
}
|
||||
Ok(is_valid_shielded_address(&addr))
|
||||
});
|
||||
unwrap_exc_or(res, false)
|
||||
}
|
||||
|
||||
fn is_valid_shielded_address(address: &str) -> bool {
|
||||
match RecipientAddress::decode(&NETWORK, &address) {
|
||||
Some(addr) => match addr {
|
||||
RecipientAddress::Shielded(_) => true,
|
||||
RecipientAddress::Transparent(_) => false,
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true when the address is valid and transparent.
|
||||
/// Returns false in any other case
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn zcashlc_is_valid_transparent_address(address: *const c_char) -> bool {
|
||||
let res = catch_panic(|| {
|
||||
let addr = CStr::from_ptr(address).to_str()?;
|
||||
|
||||
match RecipientAddress::decode(&NETWORK, &addr) {
|
||||
Some(addr) => match addr {
|
||||
RecipientAddress::Shielded(_) => Ok(false),
|
||||
RecipientAddress::Transparent(_) => Ok(true),
|
||||
},
|
||||
None => Err(format_err!("Address is for the wrong network")),
|
||||
}
|
||||
Ok(is_valid_transparent_address(&addr))
|
||||
});
|
||||
unwrap_exc_or(res, false)
|
||||
}
|
||||
|
@ -501,6 +518,16 @@ pub unsafe extern "C" fn zcashlc_is_valid_viewing_key(key: *const c_char) -> boo
|
|||
});
|
||||
unwrap_exc_or(res, false)
|
||||
}
|
||||
fn is_valid_transparent_address(address: &str) -> bool {
|
||||
match RecipientAddress::decode(&NETWORK, &address) {
|
||||
Some(addr) => match addr {
|
||||
RecipientAddress::Shielded(_) => false,
|
||||
RecipientAddress::Transparent(_) => true,
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the balance for the account, including all unspent notes that we know about.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn zcashlc_get_balance(db_data: *const u8, db_data_len: usize, account: i32) -> i64 {
|
||||
|
@ -785,17 +812,17 @@ pub extern "C" fn zcashlc_create_to_address(
|
|||
Ok(Some(extsk)) => extsk,
|
||||
Ok(None) => {
|
||||
return Err(format_err!("ExtendedSpendingKey is for the wrong network"));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(format_err!("Invalid ExtendedSpendingKey: {}", e));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let to = match RecipientAddress::decode(&NETWORK, &to) {
|
||||
Some(to) => to,
|
||||
None => {
|
||||
return Err(format_err!("PaymentAddress is for the wrong network"));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let memo = Memo::from_str(&memo).map_err(|_| format_err!("Invalid memo"))?;
|
||||
|
@ -931,6 +958,17 @@ pub unsafe extern "C" fn zcashlc_derive_transparent_address_from_seed(
|
|||
unwrap_exc_or_null(res)
|
||||
}
|
||||
|
||||
|
||||
fn derive_transparent_address_from_secret_key(secret_key: SecretKey) -> String {
|
||||
let secp = Secp256k1::new();
|
||||
let pk = PublicKey::from_secret_key(&secp, &secret_key);
|
||||
let mut hash160 = ripemd160::Ripemd160::new();
|
||||
hash160.update(Sha256::digest(&pk.serialize()[..].to_vec()));
|
||||
hash160
|
||||
.finalize()
|
||||
.to_base58check(&NETWORK.b58_pubkey_address_prefix(), &[])
|
||||
}
|
||||
|
||||
//
|
||||
// Helper code from: https://github.com/adityapk00/zecwallet-light-cli/blob/master/lib/src/lightwallet.rs
|
||||
//
|
||||
|
@ -959,4 +997,216 @@ pub fn double_sha256(payload: &[u8]) -> Vec<u8> {
|
|||
let h1 = Sha256::digest(&payload);
|
||||
let h2 = Sha256::digest(&h1);
|
||||
h2.to_vec()
|
||||
}
|
||||
|
||||
|
||||
|
||||
//// Extremely Experimental: I'm not even a rust developer
|
||||
///
|
||||
/// psst, hey I have a Major in Social Sciences. Consider using something else
|
||||
|
||||
/// Creates a transaction paying the specified address from the given account.
|
||||
///
|
||||
/// Returns the row index of the newly-created transaction in the `transactions` table
|
||||
/// within the data database. The caller can read the raw transaction bytes from the `raw`
|
||||
/// column in order to broadcast the transaction to the network.
|
||||
///
|
||||
/// Do not call this multiple times in parallel, or you will generate transactions that
|
||||
/// double-spend the same notes.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn zcashlc_autoshield_funds(
|
||||
db_data: *const u8,
|
||||
db_data_len: usize,
|
||||
db_cache: *const u8,
|
||||
db_cache_len: usize,
|
||||
account: i32,
|
||||
tsk: *const c_char,
|
||||
extsk: *const c_char,
|
||||
memo: *const c_char,
|
||||
spend_params: *const u8,
|
||||
spend_params_len: usize,
|
||||
output_params: *const u8,
|
||||
output_params_len: usize,
|
||||
) -> i64 {
|
||||
let res = catch_panic(|| {
|
||||
|
||||
let db_data = wallet_db(db_data, db_data_len)?;
|
||||
let db_cache = block_db(db_cache, db_cache_len)?;
|
||||
let account = if account >= 0 {
|
||||
account as u32
|
||||
} else {
|
||||
return Err(format_err!("account argument must be positive"));
|
||||
};
|
||||
let tsk = unsafe { CStr::from_ptr(tsk) }.to_str()?;
|
||||
let extsk = unsafe { CStr::from_ptr(extsk) }.to_str()?;
|
||||
let memo = unsafe { CStr::from_ptr(memo) }.to_str()?;
|
||||
let spend_params = Path::new(OsStr::from_bytes(unsafe {
|
||||
slice::from_raw_parts(spend_params, spend_params_len)
|
||||
}));
|
||||
let output_params = Path::new(OsStr::from_bytes(unsafe {
|
||||
slice::from_raw_parts(output_params, output_params_len)
|
||||
}));
|
||||
|
||||
let anchor_and_height = match (&db_data).get_target_and_anchor_heights() {
|
||||
Ok(Some(h)) => h,
|
||||
Err(e) => {
|
||||
return Err(format_err!("Error fetching anchor and target heights: {}", e));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// grab secret private key for t-funds
|
||||
let sk = match secp256k1::key::SecretKey::from_str(&tsk) {
|
||||
Ok(sk) => sk,
|
||||
Err(e) => {
|
||||
return Err(format_err!("Invalid Transparent Secret key: {}", e));
|
||||
},
|
||||
};
|
||||
|
||||
// derive the corresponding t-address
|
||||
let t_addr_str = derive_transparent_address_from_secret_key(sk);
|
||||
|
||||
let t_addr = match RecipientAddress::decode(&NETWORK, &t_addr_str) {
|
||||
Some(to) => to,
|
||||
None => {
|
||||
return Err(format_err!("PaymentAddress is for the wrong network"));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
let extsk =
|
||||
match decode_extended_spending_key(NETWORK.hrp_sapling_extended_spending_key(), &extsk)
|
||||
{
|
||||
Ok(Some(extsk)) => extsk,
|
||||
Ok(None) => {
|
||||
return Err(format_err!("ExtendedSpendingKey is for the wrong network"));
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(format_err!("Invalid ExtendedSpendingKey: {}", e));
|
||||
},
|
||||
};
|
||||
|
||||
// derive own shielded address from the provided extended spending key
|
||||
let z_address = extsk.default_address().unwrap().1;
|
||||
|
||||
let exfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
|
||||
let ovk = exfvk.fvk.ovk;
|
||||
|
||||
let memo = match Memo::from_str(&memo) {
|
||||
Ok(memo) => memo,
|
||||
};
|
||||
|
||||
// get UTXOs from DB
|
||||
let utxos = match get_utxos(&NETWORK, &db_cache) {
|
||||
Ok(u) => u
|
||||
};
|
||||
|
||||
// verify that the addresses of the UTXOs are correspond to the given t-address
|
||||
let distinctAddresses: Vec<UnspentTransactionOutput> = utxos.into_iter().filter(|utxo| utxo.address != t_addr).collect();
|
||||
|
||||
if distinctAddresses.len() > 0 {
|
||||
return Err(format_err!("one or more UTXOs correspond to other addresses that don't match the provided SecretKey"));
|
||||
}
|
||||
|
||||
// check that the utxos are confirmed
|
||||
|
||||
let latest_scanned_height = anchor_and_height.1;
|
||||
let latest_anchor = anchor_and_height.0;
|
||||
let unconfirmed_funds: Vec<BlockHeight> = utxos.iter().map(|u| u.height).filter(|h| h > &latest_anchor).collect();
|
||||
|
||||
if unconfirmed_funds.len() > 0 {
|
||||
return Err(format_err!("one or more UTXOs are unconfirmed "));
|
||||
}
|
||||
|
||||
let total_amount = utxos.iter().map(|u| u.value).sum();
|
||||
let fee = DEFAULT_FEE;
|
||||
let target_value = fee + total_amount;
|
||||
if fee >= total_amount {
|
||||
return Err(format_err!("Insufficient verified funds (have {}, need {:?}). NOTE: funds need {} confirmations before they can be spent.",
|
||||
u64::from(total_amount), target_value, anchor_and_height.0 + 1));
|
||||
}
|
||||
let amount_to_shield = total_amount - fee;
|
||||
|
||||
let prover = LocalTxProver::new(spend_params, output_params);
|
||||
|
||||
let mut builder = Builder::new(NETWORK, latest_scanned_height);
|
||||
|
||||
utxos.iter().map(|utxo| {
|
||||
let outpoint = OutPoint::new(
|
||||
utxo.txid.0,
|
||||
utxo.index as u32
|
||||
);
|
||||
|
||||
let coin = TxOut {
|
||||
value: utxo.value.clone(),
|
||||
script_pubkey: utxo.script.clone(),
|
||||
};
|
||||
builder.add_transparent_input(sk.clone(), outpoint.clone(), coin.clone())
|
||||
})
|
||||
.collect()
|
||||
.map_err(|e| format_err!("Error adding transparent output {}",e));
|
||||
|
||||
// there are no sapling notes so we set the change manually
|
||||
builder.send_change_to(ovk, z_address);
|
||||
|
||||
let recipient = RecipientAddress::Shielded(z_address);
|
||||
|
||||
// add the sapling output to shield the funds
|
||||
builder.add_sapling_output(Some(ovk), z_address.clone(), amount_to_shield, Some(memo));
|
||||
let consensus_branch_id = BranchId::for_height(&NETWORK, anchor_and_height.1);
|
||||
|
||||
|
||||
let (tx, tx_metadata) = match builder
|
||||
.build(consensus_branch_id, &prover) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return Err(format_err!("Error building transaction {}",e));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// We only called add_sapling_output() once.
|
||||
let output_index = match tx_metadata.output_index(0) {
|
||||
Some(idx) => idx as i64,
|
||||
None => {
|
||||
return Err(format_err!("Output 0 should exist in the transaction"));
|
||||
},
|
||||
};
|
||||
|
||||
// Update the database atomically, to ensure the result is internally consistent.
|
||||
let mut db_update = (&db_data).get_update_ops().map_err(|e| e.into())?;
|
||||
db_update
|
||||
.transactionally(|up| {
|
||||
let created = time::OffsetDateTime::now_utc();
|
||||
let tx_ref = up.put_tx_data(&tx, Some(created))?;
|
||||
|
||||
// Mark notes as spent.
|
||||
//
|
||||
// This locks the notes so they aren't selected again by a subsequent call to
|
||||
// create_spend_to_address() before this transaction has been mined (at which point the notes
|
||||
// get re-marked as spent).
|
||||
//
|
||||
// Assumes that create_spend_to_address() will never be called in parallel, which is a
|
||||
// reasonable assumption for a light client such as a mobile phone.
|
||||
for spend in &tx.shielded_spends {
|
||||
up.mark_spent(tx_ref, &spend.nullifier)?;
|
||||
}
|
||||
|
||||
up.insert_sent_note(
|
||||
&NETWORK,
|
||||
tx_ref,
|
||||
output_index as usize,
|
||||
AccountId(account),
|
||||
&RecipientAddress::from(z_address),
|
||||
amount_to_shield,
|
||||
Some(memo),
|
||||
)?;
|
||||
|
||||
// Return the row number of the transaction, so the caller can fetch it for sending.
|
||||
Ok(tx_ref)
|
||||
})
|
||||
.map_err(|e| format_err!("error building updating data_db {}",e))
|
||||
});
|
||||
unwrap_exc_or(res, -1)
|
||||
}
|
Loading…
Reference in New Issue