From 1affc5072668368aaaad63af946d5f264458caac Mon Sep 17 00:00:00 2001 From: Francisco Gindre Date: Wed, 17 Feb 2021 20:02:25 -0300 Subject: [PATCH] [WIP] data access api compliance --- Cargo.lock | 142 +++-- Cargo.toml | 20 +- .../DemoAppConfig.swift | 6 +- .../DAO/UnspentTransactionOutputDao.swift | 4 +- .../UnspentTransactionOutputRepository.swift | 7 +- .../Rust/ZcashRustBackend.swift | 48 +- .../Rust/ZcashRustBackendWelding.swift | 18 +- ZcashLightClientKit/Synchronizer.swift | 14 +- ZcashLightClientKit/Tool/DerivationTool.swift | 18 +- .../WalletTransactionEncoder.swift | 3 - .../UIKit/Synchronizer/SDKSynchronizer.swift | 4 +- ZcashLightClientKit/zcashlc/zcashlc.h | 41 +- rust/src/lib.rs | 507 +++++++----------- 13 files changed, 407 insertions(+), 425 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e98e6ca..4d57b1b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,7 +98,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" dependencies = [ "addr2line", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide", "object", @@ -131,9 +131,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bech32" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcf67bb7ba7797a081cd19009948ab533af7c355d5caf1d08c777582d351e9c" +checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" [[package]] name = "bellman" @@ -307,15 +307,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.41" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dae9c4b8fedcae85592ba623c4fd08cfdab3e3b72d6df780c6ead964a69bfff" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" [[package]] name = "cfg-if" @@ -363,7 +357,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" dependencies = [ "autocfg", - "cfg-if 1.0.0", + "cfg-if", "lazy_static", ] @@ -546,7 +540,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -557,7 +551,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.10.2+wasi-snapshot-preview1", ] @@ -601,13 +595,24 @@ dependencies = [ [[package]] name = "hdwallet" version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b15cc3c181a2aace485d56c784568a4ae6e34322287a2499549d4cda7af3e1" +source = "git+https://github.com/nuttycom/hdwallet?rev=72f1f7a56c114eed484cefd6d402b7ef28158712#72f1f7a56c114eed484cefd6d402b7ef28158712" dependencies = [ "lazy_static", - "rand 0.7.3", + "rand_core 0.6.2", "ring", - "secp256k1 0.17.2", + "secp256k1", +] + +[[package]] +name = "hdwallet" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2033326faddc94885c26775db24683819bf3ae9461e016c45df18b910bdeedd7" +dependencies = [ + "lazy_static", + "rand_core 0.6.2", + "ring", + "secp256k1", ] [[package]] @@ -671,22 +676,22 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lexical-core" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" +checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374" dependencies = [ "arrayvec", "bitflags", - "cfg-if 0.1.10", + "cfg-if", "ryu", "static_assertions", ] [[package]] name = "libc" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" [[package]] name = "libsqlite3-sys" @@ -708,10 +713,11 @@ dependencies = [ "cbindgen", "failure", "ffi_helpers", - "hdwallet", + "funty", + "hdwallet 0.3.0", "hex", "ripemd160", - "secp256k1 0.19.0", + "secp256k1", "sha2 0.9.3", "time", "zcash_client_backend", @@ -726,7 +732,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -792,6 +798,12 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" +[[package]] +name = "once_cell" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" + [[package]] name = "opaque-debug" version = "0.2.3" @@ -849,24 +861,24 @@ dependencies = [ [[package]] name = "protobuf" -version = "2.20.0" +version = "2.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86473d5f16580f10b131a0bf0afb68f8e029d1835d33a00f37281b05694e5312" +checksum = "73f72884896d22e0da0e5b266cb9a780b791f6c3b2f5beab6368d6cd4f0dbb86" [[package]] name = "protobuf-codegen" -version = "2.20.0" +version = "2.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8b6ba4581fcd9c3ce3576f25e528467b0d3516e332884c0da6f2084fe59045f" +checksum = "e8217a1652dbc91d19c509c558234145faed729191a966896414e5889f62d543" dependencies = [ "protobuf", ] [[package]] name = "protobuf-codegen-pure" -version = "2.20.0" +version = "2.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cc5a64733bf127b466ca734a39ad1b123ac37bfe96c5a340fa6f5393dfd964" +checksum = "1f239d71417bdc5f8d83c07aeb265f911346e5540a1a6c4285f9c3d1966ed6e3" dependencies = [ "protobuf", "protobuf-codegen", @@ -874,9 +886,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ "proc-macro2", ] @@ -908,7 +920,7 @@ checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" dependencies = [ "libc", "rand_chacha 0.3.0", - "rand_core 0.6.1", + "rand_core 0.6.2", "rand_hc 0.3.0", ] @@ -929,7 +941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", - "rand_core 0.6.1", + "rand_core 0.6.2", ] [[package]] @@ -943,9 +955,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ "getrandom 0.2.2", ] @@ -965,7 +977,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ - "rand_core 0.6.1", + "rand_core 0.6.2", ] [[package]] @@ -976,9 +988,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_syscall" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" dependencies = [ "bitflags", ] @@ -1005,13 +1017,13 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.12" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", - "lazy_static", "libc", + "once_cell", "spin", "untrusted", "web-sys", @@ -1078,31 +1090,13 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -[[package]] -name = "secp256k1" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2932dc07acd2066ff2e3921a4419606b220ba6cd03a9935123856cc534877056" -dependencies = [ - "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]] -name = "secp256k1-sys" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab2c26f0d3552a0f12e639ae8a64afc2e3db9c52fe32f5fc6c289d38519f220" -dependencies = [ - "cc", + "secp256k1-sys", ] [[package]] @@ -1151,9 +1145,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" dependencies = [ "itoa", "ryu", @@ -1185,7 +1179,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if", "cpuid-bool", "digest 0.9.0", "opaque-debug 0.3.0", @@ -1205,9 +1199,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "standback" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66a8cff4fa24853fdf6b51f75c6d7f8206d7c75cab4e467bcd7f25c2b1febe0" +checksum = "a2beb4d1860a61f571530b3f855a1b538d0200f7871c63331ecd6f17b1f014f8" dependencies = [ "version_check", ] @@ -1308,10 +1302,10 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "rand 0.8.3", - "redox_syscall 0.2.4", + "redox_syscall 0.2.5", "remove_dir_all", "winapi", ] @@ -1438,7 +1432,7 @@ version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -1534,6 +1528,7 @@ dependencies = [ "bs58", "ff", "group", + "hdwallet 0.2.5", "hex", "jubjub", "nom", @@ -1541,6 +1536,9 @@ dependencies = [ "protobuf", "protobuf-codegen-pure", "rand_core 0.5.1", + "ripemd160", + "secp256k1", + "sha2 0.9.3", "subtle", "time", "zcash_primitives", @@ -1585,7 +1583,7 @@ dependencies = [ "rand 0.7.3", "rand_core 0.5.1", "ripemd160", - "secp256k1 0.19.0", + "secp256k1", "sha2 0.9.3", "subtle", ] diff --git a/Cargo.toml b/Cargo.toml index e76fbcf3..a52c6fc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,18 +11,19 @@ build = "rust/build.rs" failure = "0.1" ffi_helpers = "0.2" hex = "0.4" -zcash_client_backend = "0.4" -zcash_client_sqlite = "0.2.1" +zcash_client_backend = { version = "0.4", features = ["transparent-inputs"] } +zcash_client_sqlite = { version = "0.2.1", features = ["transparent-inputs"] } zcash_primitives = { version = "0.4", features = ["transparent-inputs"] } - + #### Temporary additions: #################################### base58 = "0.1.0" sha2 = "0.9" bs58 = { version = "0.3", features = ["check"] } -hdwallet = "0.2.2" +hdwallet = "0.3.0" ripemd160 = "0.9" secp256k1 = "0.19" time = "0.2" +funty = "=1.1.0" ############################################################## @@ -43,10 +44,10 @@ crate-type = ["staticlib"] lto = true [patch.crates-io] -#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" } +#zcash_client_backend = {git = "https://github.com/nuttycom/librustzcash", branch = "autoshield-poc-daa" } +#zcash_client_sqlite = {git = "https://github.com/nuttycom/librustzcash", branch = "autoshield-poc-daa" } +#zcash_primitives = {git = "https://github.com/nuttycom/librustzcash", branch = "autoshield-poc-daa" } +#zcash_proofs = {git = "https://github.com/nuttycom/librustzcash", branch = "autoshield-poc-daa" } zcash_client_backend = { path = "/Users/pacu/Repos/ECC/pacu-librustzcash/zcash_client_backend" } zcash_client_sqlite = { path = "/Users/pacu/Repos/ECC/pacu-librustzcash/zcash_client_sqlite" } @@ -54,4 +55,5 @@ zcash_primitives = { path = "/Users/pacu/Repos/ECC/pacu-librustzcash/zcash_primi zcash_proofs = { path = "/Users/pacu/Repos/ECC/pacu-librustzcash/zcash_proofs" } [features] -mainnet = ["zcash_client_sqlite/mainnet"] +mainnet = ["zcash_client_sqlite/mainnet", "zcash_client_sqlite/transparent-inputs", "zcash_client_backend/transparent-inputs", "zcash_primitives/transparent-inputs"] +testnet = ["zcash_client_backend/transparent-inputs", "zcash_primitives/transparent-inputs"] diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift index 71323484..a5e21610 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift @@ -10,11 +10,11 @@ import Foundation import ZcashLightClientKit import MnemonicSwift struct DemoAppConfig { - static var host = ZcashSDK.isMainnet ? "localhost" : "lightwalletd.testnet.electriccoin.co" + static var host = ZcashSDK.isMainnet ? "lightwalletd.electriccoin.co" : "lightwalletd.testnet.electriccoin.co" static var port: Int = 9067 - static var birthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 663174 : 620_000 + static var birthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 620_000 static var network = ZcashSDK.isMainnet ? ZcashNetwork.mainNet : ZcashNetwork.testNet - static var seed = ZcashSDK.isMainnet ? try! Mnemonic.deterministicSeedBytes(from: "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread") : Array("testreferencealicetestreferencealice".utf8) + static var seed = ZcashSDK.isMainnet ? try! Mnemonic.deterministicSeedBytes(from: "old wise letter cigar ready miracle settle crystal bag amateur dial index slot dune crisp ready chest advice camera always drive dentist sport smoke") : Array("testreferencealicetestreferencealice".utf8) static var address: String { "\(host):\(port)" } diff --git a/ZcashLightClientKit/DAO/UnspentTransactionOutputDao.swift b/ZcashLightClientKit/DAO/UnspentTransactionOutputDao.swift index 14c5d01f..9a08fec3 100644 --- a/ZcashLightClientKit/DAO/UnspentTransactionOutputDao.swift +++ b/ZcashLightClientKit/DAO/UnspentTransactionOutputDao.swift @@ -123,7 +123,7 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository { } } - func balance(address: String, latestHeight: BlockHeight) throws -> UnshieldedBalance { + func balance(address: String, latestHeight: BlockHeight) throws -> WalletBalance { do { let confirmed = try dbProvider.connection().scalar( @@ -142,7 +142,7 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository { } } -struct TransparentBalance: UnshieldedBalance { +struct TransparentBalance: WalletBalance { var confirmed: Int64 var unconfirmed: Int64 var address: String diff --git a/ZcashLightClientKit/Repository/UnspentTransactionOutputRepository.swift b/ZcashLightClientKit/Repository/UnspentTransactionOutputRepository.swift index 532fd044..7e8329b9 100644 --- a/ZcashLightClientKit/Repository/UnspentTransactionOutputRepository.swift +++ b/ZcashLightClientKit/Repository/UnspentTransactionOutputRepository.swift @@ -7,16 +7,11 @@ import Foundation -public protocol UnshieldedBalance { - var confirmed: Int64 { get set } - var unconfirmed: Int64 { get set } -} - protocol UnspentTransactionOutputRepository { func getAll(address: String?) throws -> [UnspentTransactionOutputEntity] - func balance(address: String, latestHeight: BlockHeight) throws -> UnshieldedBalance + func balance(address: String, latestHeight: BlockHeight) throws -> WalletBalance func store(utxos: [UnspentTransactionOutputEntity]) throws diff --git a/ZcashLightClientKit/Rust/ZcashRustBackend.swift b/ZcashLightClientKit/Rust/ZcashRustBackend.swift index 93d76ff5..c260c37c 100644 --- a/ZcashLightClientKit/Rust/ZcashRustBackend.swift +++ b/ZcashLightClientKit/Rust/ZcashRustBackend.swift @@ -9,6 +9,34 @@ import Foundation class ZcashRustBackend: ZcashRustBackendWelding { + static func putUnspentTransparentOutput(dbData: URL, address: String, txid: [UInt8], index: Int, script: [UInt8], value: Int64, height: BlockHeight) throws -> Bool { + + let dbData = dbData.osStr() + + guard !address.containsCStringNullBytesBeforeStringEnding() else { + throw RustWeldingError.malformedStringInput + } + + guard zcashlc_put_utxo(dbData.0, + dbData.1, + [CChar](address.utf8CString), + txid, + UInt(txid.count), + Int32(index), + script, + UInt(script.count), + value, + Int32(height)) else { + if let error = lastError() { + throw error + } + return false + + } + + return true + } + static func lastError() -> RustWeldingError? { guard let message = getLastError() else { return nil } @@ -207,7 +235,7 @@ class ZcashRustBackend: ZcashRustBackendWelding { return zcashlc_decrypt_and_store_transaction(dbData.0, dbData.1, tx, UInt(tx.count)) != 0 } - static func createToAddress(dbData: URL, account: Int32, extsk: String, consensusBranchId: Int32,to: String, value: Int64, memo: String?, spendParamsPath: String, outputParamsPath: String) -> Int64 { + static func createToAddress(dbData: URL, account: Int32, extsk: String, to: String, value: Int64, memo: String?, spendParamsPath: String, outputParamsPath: String) -> Int64 { let dbData = dbData.osStr() let memoBytes = memo ?? "" @@ -215,7 +243,6 @@ class ZcashRustBackend: ZcashRustBackendWelding { dbData.1, account, [CChar](extsk.utf8CString), - consensusBranchId, [CChar](to.utf8CString), value, [CChar](memoBytes.utf8CString), @@ -227,17 +254,14 @@ class ZcashRustBackend: ZcashRustBackendWelding { static func shieldFunds(dbCache: URL, dbData: URL, account: Int32, tsk: String, extsk: String, memo: String?, spendParamsPath: String, outputParamsPath: String) -> Int64 { let dbData = dbData.osStr() - let dbCache = dbCache.osStr() let memoBytes = memo ?? "" return zcashlc_shield_funds(dbData.0, dbData.1, - dbCache.0, - dbCache.1, account, - tsk, - extsk, - memoBytes, + [CChar](tsk.utf8CString), + [CChar](extsk.utf8CString), + [CChar](memoBytes.utf8CString), spendParamsPath, UInt(spendParamsPath.lengthOfBytes(using: .utf8)), outputParamsPath, @@ -329,9 +353,9 @@ class ZcashRustBackend: ZcashRustBackendWelding { return zAddr } - static func deriveTransparentAddressFromSeed(seed: [UInt8]) throws -> String? { + static func deriveTransparentAddressFromSeed(seed: [UInt8], account: Int, index: Int) throws -> String? { - guard let tAddrCStr = zcashlc_derive_transparent_address_from_seed(seed, UInt(seed.count)) else { + guard let tAddrCStr = zcashlc_derive_transparent_address_from_seed(seed, UInt(seed.count), Int32(account), Int32(index)) else { if let error = lastError() { throw error } @@ -343,8 +367,8 @@ class ZcashRustBackend: ZcashRustBackendWelding { return tAddr } - static func deriveTransparentPrivateKeyFromSeed(seed: [UInt8]) throws -> String? { - guard let skCStr = zcashlc_derive_transparent_private_key_from_seed(seed, UInt(seed.count)) else { + static func deriveTransparentPrivateKeyFromSeed(seed: [UInt8], account: Int, index: Int) throws -> String? { + guard let skCStr = zcashlc_derive_transparent_private_key_from_seed(seed, UInt(seed.count), Int32(account), Int32(index)) else { if let error = lastError() { throw error } diff --git a/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift b/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift index 8c810697..989a7a86 100644 --- a/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift +++ b/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift @@ -165,6 +165,18 @@ public protocol ZcashRustBackendWelding { */ static func scanBlocks(dbCache: URL, dbData: URL) -> Bool + /** + puts a UTXO into the data db database + - Parameters: + - dbData: location of the data db file + - address: the address of the UTXO + - txid: the txid bytes for the UTXO + - index: the index of the UTXO + - value: the value of the UTXO + - height: the mined height for the UTXO + - Returns: true if the operation succeded or false otherwise + */ + static func putUnspentTransparentOutput(dbData: URL, address: String, txid: [UInt8], index: Int, script: [UInt8], value: Int64, height: BlockHeight) throws -> Bool /** Scans a transaction for any information that can be decrypted by the accounts in the wallet, and saves it to the wallet. @@ -188,7 +200,7 @@ public protocol ZcashRustBackendWelding { - spendParamsPath: path escaped String for the filesystem locations where the spend parameters are located - outputParamsPath: path escaped String for the filesystem locations where the output parameters are located */ - static func createToAddress(dbData: URL, account: Int32, extsk: String, consensusBranchId: Int32, to: String, value: Int64, memo: String?, spendParamsPath: String, outputParamsPath: String) -> Int64 + static func createToAddress(dbData: URL, account: Int32, extsk: String, to: String, value: Int64, memo: String?, spendParamsPath: String, outputParamsPath: String) -> Int64 /** Creates a transaction to shield all found UTXOs in cache db. @@ -253,14 +265,14 @@ public protocol ZcashRustBackendWelding { - Returns: an optional String containing the transparent address - Throws: RustBackendError if fatal error occurs */ - static func deriveTransparentAddressFromSeed(seed: [UInt8]) throws -> String? + static func deriveTransparentAddressFromSeed(seed: [UInt8], account: Int, index: Int) throws -> String? /** Derives a transparent secret key from Seed - Parameter seed: an array of bytes containing the seed - Returns: an optional String containing the transparent secret (private) key */ - static func deriveTransparentPrivateKeyFromSeed(seed: [UInt8]) throws -> String? + static func deriveTransparentPrivateKeyFromSeed(seed: [UInt8], account: Int, index: Int) throws -> String? /** Derives a transparent address from a secret key diff --git a/ZcashLightClientKit/Synchronizer.swift b/ZcashLightClientKit/Synchronizer.swift index 9a0f7423..aedd9bb4 100644 --- a/ZcashLightClientKit/Synchronizer.swift +++ b/ZcashLightClientKit/Synchronizer.swift @@ -44,6 +44,10 @@ extension ShieldFundsError: LocalizedError { } +public protocol WalletBalance { + var confirmed: Int64 { get set } + var unconfirmed: Int64 { get set } +} /** Primary interface for interacting with the SDK. Defines the contract that specific @@ -170,6 +174,11 @@ public protocol Synchronizer { */ func latestUTXOs(address: String, result: @escaping (Result<[UnspentTransactionOutputEntity], Error>) -> Void) + /** + Gets the latests UTXOs for the given address from the specified height on + */ + func refreshUTXOs(address: String, from height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity],Error>) -> Void) + /** gets the latest cached UTXOs for the given t-address for the given address */ @@ -178,12 +187,13 @@ public protocol Synchronizer { /** gets the unshielded balance for the given address. */ - func latestUnshieldedBalance(address: String, result: @escaping (Result) -> Void) + func latestUnshieldedBalance(address: String, result: @escaping (Result) -> Void) /** gets the last stored unshielded balance */ - func getUnshieldedBalance(address: String) throws -> UnshieldedBalance + func getTransparentBalance(address: String) throws -> WalletBalance + } /** diff --git a/ZcashLightClientKit/Tool/DerivationTool.swift b/ZcashLightClientKit/Tool/DerivationTool.swift index 19547d12..5ce7017f 100644 --- a/ZcashLightClientKit/Tool/DerivationTool.swift +++ b/ZcashLightClientKit/Tool/DerivationTool.swift @@ -77,12 +77,12 @@ public protocol KeyDeriving { // WIP probably shouldn't be used just yet. Why? // - because we need the private key associated with this seed and this function doesn't return it. // - the underlying implementation needs to be split out into a few lower-level calls - func deriveTransparentAddress(seed: [UInt8]) throws -> String + func deriveTransparentAddress(seed: [UInt8], account: Int, index: Int) throws -> String /** Derives a SecretKey to spend transparent funds from the given seed */ - func deriveTransparentPrivateKey(seed: [UInt8]) throws -> String + func deriveTransparentPrivateKey(seed: [UInt8], account: Int, index: Int) throws -> String /** Derives a transparent address from the given transparent Secret Key @@ -215,9 +215,9 @@ public class DerivationTool: KeyDeriving { // WIP probably shouldn't be used just yet. Why? // - because we need the private key associated with this seed and this function doesn't return it. // - the underlying implementation needs to be split out into a few lower-level calls - public func deriveTransparentAddress(seed: [UInt8]) throws -> String { + public func deriveTransparentAddress(seed: [UInt8], account: Int = 0, index: Int = 0) throws -> String { do { - guard let zaddr = try rustwelding.deriveTransparentAddressFromSeed(seed: seed) else { + guard let zaddr = try rustwelding.deriveTransparentAddressFromSeed(seed: seed, account: account, index: index) else { throw KeyDerivationErrors.unableToDerive } return zaddr @@ -237,9 +237,9 @@ public class DerivationTool: KeyDeriving { - KeyDerivationErrors.derivationError with the underlying error when it fails - KeyDerivationErrors.unableToDerive when there's an unknown error */ - public func deriveTransparentPrivateKey(seed: [UInt8]) throws -> String { + public func deriveTransparentPrivateKey(seed: [UInt8], account: Int = 0, index: Int = 0) throws -> String { do { - guard let sk = try rustwelding.deriveTransparentPrivateKeyFromSeed(seed: seed) else { + guard let sk = try rustwelding.deriveTransparentPrivateKeyFromSeed(seed: seed, account: account, index: index) else { throw KeyDerivationErrors.unableToDerive } return sk @@ -276,6 +276,12 @@ extension DerivationTool: KeyValidation { } + /** + Derives the transparent address from a WIF Private Key + - Throws: + - KeyDerivationErrors.derivationError with the underlying error when it fails + - KeyDerivationErrors.unableToDerive when there's an unknown error + */ public func deriveTransparentAddressFromPrivateKey(_ tsk: String) throws -> String { do { guard let tAddr = try rustwelding.deriveTransparentAddressFromSecretKey(tsk) else { diff --git a/ZcashLightClientKit/Transaction/WalletTransactionEncoder.swift b/ZcashLightClientKit/Transaction/WalletTransactionEncoder.swift index 9ba7282a..6161ab7f 100644 --- a/ZcashLightClientKit/Transaction/WalletTransactionEncoder.swift +++ b/ZcashLightClientKit/Transaction/WalletTransactionEncoder.swift @@ -83,13 +83,10 @@ class WalletTransactionEncoder: TransactionEncoder { guard let latestHeight = Int32(exactly: scannedHeight) else { throw RustWeldingError.genericError(message: "could not convert \(scannedHeight)") } - - let consensusBranchId = try rustBackend.consensusBranchIdFor(height: latestHeight) let txId = rustBackend.createToAddress(dbData: self.dataDbURL, account: Int32(accountIndex), extsk: spendingKey, - consensusBranchId: consensusBranchId, to: address, value: Int64(zatoshi), memo: memo, diff --git a/ZcashLightClientKit/UIKit/Synchronizer/SDKSynchronizer.swift b/ZcashLightClientKit/UIKit/Synchronizer/SDKSynchronizer.swift index 65f0c6f2..e05315de 100644 --- a/ZcashLightClientKit/UIKit/Synchronizer/SDKSynchronizer.swift +++ b/ZcashLightClientKit/UIKit/Synchronizer/SDKSynchronizer.swift @@ -495,7 +495,7 @@ public class SDKSynchronizer: Synchronizer { /** gets the unshielded balance for the given address. */ - public func latestUnshieldedBalance(address: String, result: @escaping (Result) -> Void) { + public func latestUnshieldedBalance(address: String, result: @escaping (Result) -> Void) { latestUTXOs(address: address, result: { [weak self] (r) in guard let self = self else { return } @@ -515,7 +515,7 @@ public class SDKSynchronizer: Synchronizer { /** gets the last stored unshielded balance */ - public func getUnshieldedBalance(address: String) throws -> UnshieldedBalance { + public func getTransparentBalance(address: String) throws -> WalletBalance { do { let latestHeight = try self.latestDownloadedHeight() let cachedBalance = try utxoRepository.balance(address: address, latestHeight: latestHeight) diff --git a/ZcashLightClientKit/zcashlc/zcashlc.h b/ZcashLightClientKit/zcashlc/zcashlc.h index 2f10f163..cc7113d9 100644 --- a/ZcashLightClientKit/zcashlc/zcashlc.h +++ b/ZcashLightClientKit/zcashlc/zcashlc.h @@ -82,14 +82,17 @@ char *zcashlc_derive_shielded_address_from_seed(const uint8_t *seed, char *zcashlc_derive_shielded_address_from_viewing_key(const char *extfvk); /** - * Derives a transparent address from the given seed + * Derives a transparent address from the given secret key enconded as a WIF string */ char *zcashlc_derive_transparent_address_from_secret_key(const char *tsk); /** * Derives a transparent address from the given seed */ -char *zcashlc_derive_transparent_address_from_seed(const uint8_t *seed, uintptr_t seed_len); +char *zcashlc_derive_transparent_address_from_seed(const uint8_t *seed, + uintptr_t seed_len, + int32_t account, + int32_t index); /** * TEST TEST 123 TEST @@ -97,7 +100,10 @@ char *zcashlc_derive_transparent_address_from_seed(const uint8_t *seed, uintptr_ * * Derives a transparent private key from seed */ -char *zcashlc_derive_transparent_private_key_from_seed(const uint8_t *seed, uintptr_t seed_len); +char *zcashlc_derive_transparent_private_key_from_seed(const uint8_t *seed, + uintptr_t seed_len, + int32_t account, + int32_t index); /** * Copies the last error message into the provided allocated buffer. @@ -138,6 +144,14 @@ char *zcashlc_get_received_memo_as_utf8(const uint8_t *db_data, */ char *zcashlc_get_sent_memo_as_utf8(const uint8_t *db_data, uintptr_t db_data_len, int64_t id_note); +/** + * Returns the verified transparent balance for the address, which ignores utxos that have been + * received too recently and are not yet deemed spendable. + */ +int64_t zcashlc_get_total_transparent_balance(const uint8_t *db_data, + uintptr_t db_data_len, + const char *address); + /** * Returns the verified balance for the account, which ignores notes that have been * received too recently and are not yet deemed spendable. @@ -146,6 +160,14 @@ int64_t zcashlc_get_verified_balance(const uint8_t *db_data, uintptr_t db_data_len, int32_t account); +/** + * Returns the verified transparent balance for the address, which ignores utxos that have been + * received too recently and are not yet deemed spendable. + */ +int64_t zcashlc_get_verified_transparent_balance(const uint8_t *db_data, + uintptr_t db_data_len, + const char *address); + /** * Initialises the data database with the given number of accounts using the given seed. * @@ -208,6 +230,17 @@ bool zcashlc_is_valid_viewing_key(const char *key); */ int32_t zcashlc_last_error_length(void); +bool zcashlc_put_utxo(const uint8_t *db_data, + uintptr_t db_data_len, + const char *address_str, + const uint8_t *txid_bytes, + uintptr_t txid_bytes_len, + int32_t index, + const uint8_t *script_bytes, + uintptr_t script_bytes_len, + int64_t value, + int32_t height); + /** * Rewinds the data database to the given height. * @@ -239,8 +272,6 @@ int32_t zcashlc_scan_blocks(const uint8_t *db_cache, int64_t zcashlc_shield_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, diff --git a/rust/src/lib.rs b/rust/src/lib.rs index f833e604..85ceca5e 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -8,42 +8,47 @@ use std::slice; use std::str::FromStr; use zcash_client_backend::{ address::RecipientAddress, - wallet::AccountId, data_api::{ chain::{scan_cached_blocks, validate_chain}, error::Error, - wallet::{create_spend_to_address, decrypt_and_store_transaction}, + wallet::{create_spend_to_address, decrypt_and_store_transaction, shield_funds, ANCHOR_OFFSET}, WalletRead, WalletWrite, }, encoding::{ - decode_extended_full_viewing_key, decode_extended_spending_key, - encode_extended_full_viewing_key, encode_extended_spending_key, encode_payment_address, + AddressCodec, + decode_extended_full_viewing_key, + decode_extended_spending_key, + encode_extended_full_viewing_key, + encode_extended_spending_key, + encode_payment_address, }, - keys::spending_key, - wallet::OvkPolicy, + keys::{ + derive_secret_key_from_seed, + derive_transparent_address_from_secret_key, + spending_key, Wif, + }, + wallet::{AccountId, OvkPolicy, WalletTransparentOutput}, }; use zcash_client_sqlite::{ error::SqliteClientError, wallet::{ - init::{init_accounts_table, init_blocks_table, init_wallet_db} + put_received_transparent_utxo, + init::{init_accounts_table, init_blocks_table, init_wallet_db,} }, BlockDB, NoteId, WalletDB, - chain::get_confirmed_utxos_for_address, + }; use zcash_primitives::{ block::BlockHash, consensus::{self,BlockHeight, BranchId, Parameters}, note_encryption::Memo, - transaction::components::{ - amount::DEFAULT_FEE, - Amount, - OutPoint, - TxOut + transaction::{ + Transaction, + components::{Amount, OutPoint}, }, - legacy::Script, - transaction::builder::Builder, - transaction::Transaction, + zip32::{ExtendedFullViewingKey}, + legacy::TransparentAddress, }; #[cfg(feature = "mainnet")] @@ -52,23 +57,10 @@ use zcash_primitives::consensus::{MainNetwork, MAIN_NETWORK}; use zcash_primitives::consensus::{TestNetwork, TEST_NETWORK}; use zcash_proofs::prover::LocalTxProver; - -use std::convert::TryFrom; - -// ///////////////////////////////////////////////////////////////////////////////////////////////// -// Temporary Imports +use std::convert::{TryFrom, TryInto}; use base58::ToBase58; use sha2::{Digest, Sha256}; -// use zcash_primitives::legacy::TransparentAddress; -use hdwallet::{ExtendedPrivKey, KeyIndex}; -use secp256k1::{ - Secp256k1, - key::{PublicKey, SecretKey}, -}; - - -// use crate::extended_key::{key_index::KeyIndex, ExtendedPrivKey, ExtendedPubKey, KeySeed}; -// ///////////////////////////////////////////////////////////////////////////////////////////////// +use secp256k1::key::SecretKey; fn unwrap_exc_or(exc: Result, def: T) -> T { @@ -601,6 +593,74 @@ pub extern "C" fn zcashlc_get_verified_balance( unwrap_exc_or(res, -1) } +/// Returns the verified transparent balance for the address, which ignores utxos that have been +/// received too recently and are not yet deemed spendable. +#[no_mangle] +pub extern "C" fn zcashlc_get_verified_transparent_balance( + db_data: *const u8, + db_data_len: usize, + address: *const c_char, +) -> i64 { + let res = catch_panic(|| { + let db_data = wallet_db(NETWORK, db_data, db_data_len)?; + let addr = unsafe { CStr::from_ptr(address).to_str()? }; + let taddr = TransparentAddress::decode(&NETWORK, &addr).unwrap(); + let amount = (&db_data) + .get_target_and_anchor_heights() + .map_err(|e| format_err!("Error while fetching anchor height: {}", e)) + .and_then(|opt_anchor| { + opt_anchor + .map(|(h, _)| h) + .ok_or(format_err!("height not available; scan required.")) + }) + .and_then(|anchor| { + (&db_data) + .get_unspent_transparent_utxos(&taddr, anchor - 10) + .map_err(|e| format_err!("Error while fetching verified transparent balance: {}", e)) + })? + .iter() + .map(|utxo| utxo.value) + .sum::(); + + Ok(amount.into()) + }); + unwrap_exc_or(res, -1) +} + +/// Returns the verified transparent balance for the address, which ignores utxos that have been +/// received too recently and are not yet deemed spendable. +#[no_mangle] +pub extern "C" fn zcashlc_get_total_transparent_balance( + db_data: *const u8, + db_data_len: usize, + address: *const c_char, +) -> i64 { + let res = catch_panic(|| { + let db_data = wallet_db(NETWORK, db_data, db_data_len)?; + let addr = unsafe { CStr::from_ptr(address).to_str()? }; + let taddr = TransparentAddress::decode(&NETWORK, &addr).unwrap(); + let amount = (&db_data) + .get_target_and_anchor_heights() + .map_err(|e| format_err!("Error while fetching anchor height: {}", e)) + .and_then(|opt_anchor| { + opt_anchor + .map(|(h, _)| h) + .ok_or(format_err!("height not available; scan required.")) + }) + .and_then(|anchor| { + (&db_data) + .get_unspent_transparent_utxos(&taddr, anchor) + .map_err(|e| format_err!("Error while fetching total transparent balance: {}", e)) + })? + .iter() + .map(|utxo| utxo.value) + .sum::(); + + Ok(amount.into()) + }); + unwrap_exc_or(res, -1) +} + /// Returns the memo for a received note, if it is known and a valid UTF-8 string. /// /// The note is identified by its row index in the `received_notes` table within the data @@ -761,6 +821,49 @@ pub extern "C" fn zcashlc_scan_blocks( unwrap_exc_or_null(res) } +#[no_mangle] +pub extern "C" fn zcashlc_put_utxo( + db_data: *const u8, + db_data_len: usize, + address_str: *const c_char, + txid_bytes: *const u8, + txid_bytes_len: usize, + index: i32, + script_bytes: *const u8, + script_bytes_len: usize, + value: i64, + height: i32, +) -> bool { + let res = catch_panic(|| { + let db_data = wallet_db(NETWORK, db_data, db_data_len)?; + let mut db_data = db_data.get_update_ops()?; + + let addr = unsafe {CStr::from_ptr(address_str).to_str()? }; + let txid_bytes = unsafe { slice::from_raw_parts(txid_bytes, txid_bytes_len) }; + let mut txid = [0u8; 32]; + txid.copy_from_slice(&txid_bytes); + + let script_bytes = unsafe { slice::from_raw_parts(script_bytes, script_bytes_len) }; + let mut script = [0u8; 32]; + script.copy_from_slice(&script_bytes); + + let address = TransparentAddress::decode(&NETWORK, &addr).unwrap(); + + let output = WalletTransparentOutput { + address: address, + outpoint: OutPoint::new(txid, index as u32), + script: script.to_vec(), + value: Amount::from_i64(value).unwrap(), + height: BlockHeight::from(height as u32), + }; + match put_received_transparent_utxo(&mut db_data, &output) { + Ok(_) => Ok(true), + Err(e) => Err(format_err!("Error while inserting UTXO: {}", e)), + } + }); + unwrap_exc_or(res, false) +} + #[no_mangle] pub extern "C" fn zcashlc_decrypt_and_store_transaction( db_data: *const u8, @@ -911,31 +1014,27 @@ pub extern "C" fn zcashlc_vec_string_free(v: *mut *mut c_char, len: usize, capac pub unsafe extern "C" fn zcashlc_derive_transparent_private_key_from_seed( seed: *const u8, seed_len: usize, + account: i32, + index: i32, ) -> *mut c_char { let res = catch_panic(|| { let seed = slice::from_raw_parts(seed, seed_len); - - // modified from: https://github.com/adityapk00/zecwallet-light-cli/blob/master/lib/src/lightwallet.rs + let account = if account >= 0 { + account as u32 + } else { + return Err(format_err!("account argument must be positive")); + }; - let ext_t_key = ExtendedPrivKey::with_seed(&seed).unwrap(); - let address_sk = ext_t_key - .derive_private_key(KeyIndex::hardened_from_normalize_index(44).unwrap()) - .unwrap() - .derive_private_key( - KeyIndex::hardened_from_normalize_index(NETWORK.coin_type()).unwrap(), - ) - .unwrap() - .derive_private_key(KeyIndex::hardened_from_normalize_index(0).unwrap()) - .unwrap() - .derive_private_key(KeyIndex::Normal(0)) - .unwrap() - .derive_private_key(KeyIndex::Normal(0)) - .unwrap() - .private_key; - + let index = if index >= 0 { + index as u32 + } else { + return Err(format_err!("index argument must be positive")); + }; + let sk = derive_secret_key_from_seed(&NETWORK, &seed, AccountId(account), index).unwrap(); + let sk_wif = Wif::from_secret_key(&sk, true); - Ok(CString::new(address_sk.to_string()).unwrap().into_raw()) + Ok(CString::new(sk_wif.0.to_string()).unwrap().into_raw()) }); unwrap_exc_or_null(res) } @@ -945,81 +1044,51 @@ pub unsafe extern "C" fn zcashlc_derive_transparent_private_key_from_seed( pub unsafe extern "C" fn zcashlc_derive_transparent_address_from_seed( seed: *const u8, seed_len: usize, + account: i32, + index: i32, ) -> *mut c_char { let res = catch_panic(|| { let seed = slice::from_raw_parts(seed, seed_len); - - // modified from: https://github.com/adityapk00/zecwallet-light-cli/blob/master/lib/src/lightwallet.rs - - let ext_t_key = ExtendedPrivKey::with_seed(&seed).unwrap(); - let address_sk = ext_t_key - .derive_private_key(KeyIndex::hardened_from_normalize_index(44).unwrap()) - .unwrap() - .derive_private_key( - KeyIndex::hardened_from_normalize_index(NETWORK.coin_type()).unwrap(), - ) - .unwrap() - .derive_private_key(KeyIndex::hardened_from_normalize_index(0).unwrap()) - .unwrap() - .derive_private_key(KeyIndex::Normal(0)) - .unwrap() - .derive_private_key(KeyIndex::Normal(0)) - .unwrap() - .private_key; - - let private_key = match secp256k1::SecretKey::from_slice(&address_sk[..]) { - Ok(pk) => pk, - Err(e) => { - return Err(format_err!("error converting secret key {}",e)); - }, + let account = if account >= 0 { + account as u32 + } else { + return Err(format_err!("account argument must be positive")); }; - let secp = Secp256k1::new(); - let pk = PublicKey::from_secret_key(&secp, &private_key); - let mut hash160 = ripemd160::Ripemd160::new(); - hash160.update(Sha256::digest(&pk.serialize()[..].to_vec())); - let address_string = hash160 - .finalize() - .to_base58check(&NETWORK.b58_pubkey_address_prefix(), &[]); - Ok(CString::new(address_string).unwrap().into_raw()) + let index = if index >= 0 { + index as u32 + } else { + return Err(format_err!("index argument must be positive")); + }; + let sk = derive_secret_key_from_seed(&NETWORK, &seed, AccountId(account), index); + let taddr = derive_transparent_address_from_secret_key(sk.unwrap()) + .encode(&NETWORK); + + Ok(CString::new(taddr).unwrap().into_raw()) }); unwrap_exc_or_null(res) } -/// Derives a transparent address from the given seed +/// Derives a transparent address from the given secret key enconded as a WIF string #[no_mangle] pub unsafe extern "C" fn zcashlc_derive_transparent_address_from_secret_key( tsk: *const c_char, ) -> *mut c_char { let res = catch_panic(|| { - let tsk = CStr::from_ptr(tsk).to_str()?; + let tsk_wif = CStr::from_ptr(tsk).to_str()?; - // 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)); - }, - }; + let sk: SecretKey = (&Wif(tsk_wif.to_string())).try_into().expect("invalid private key WIF"); // derive the corresponding t-address - - Ok(CString::new(derive_transparent_address_from_secret_key(sk)).unwrap().into_raw()) + let taddr = + derive_transparent_address_from_secret_key(sk) + .encode(&NETWORK); + Ok(CString::new(taddr).unwrap().into_raw()) }); 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 @@ -1051,204 +1120,10 @@ pub fn double_sha256(payload: &[u8]) -> Vec { 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. -/// -/// - -fn shield_funds( - db_cache: &BlockDB, - db_data: &WalletDB

, - account: u32, - tsk: &str, - extsk: &str, - memo: &str, - spend_params: &Path, - output_params: &Path, -) -> Result { - let target_height_and_anchor = match (&db_data).get_target_and_anchor_heights() { - Ok(Some(h)) => h, - Ok(None) => { - return Err(format_err!("No anchor and target heights found")); - }, - 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 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, - Err(_) => { - return Err(format_err!("Invalid memo input")); - } - }; - - // get latest height and anchor - - let latest_scanned_height = target_height_and_anchor.0; - let latest_anchor = target_height_and_anchor.1; - - // get UTXOs from DB - let utxos = match get_confirmed_utxos_for_address(&NETWORK, &db_cache, latest_anchor, &t_addr_str) { - Ok(u) => u, - Err(e) => { - return Err(format_err!("Error getting UTXOs {}",e)); - }, - }; - - let total_amount = match Amount::from_i64(utxos.iter().map(|u| i64::from(u.value)).sum::()) { - Ok(a) => a, - _ => { - return Err(format_err!("error collecting total amount from UTXOs")); - }, - }; - 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, target_height_and_anchor.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); - - for utxo in utxos.iter() { - let outpoint = OutPoint::new( - utxo.txid.0, - utxo.index as u32 - ); - - let coin = TxOut { - value: utxo.value.clone(), - script_pubkey: Script { 0: utxo.script.clone() }, - }; - - match builder.add_transparent_input(sk.clone(), outpoint.clone(), coin.clone()) { - Ok(_) => (), - Err(e) => { - return Err(format_err!("error adding transparent input {}",e)); - }, - } - } - - // there are no sapling notes so we set the change manually - builder.send_change_to(ovk, z_address.clone()); - - // add the sapling output to shield the funds - match builder.add_sapling_output(Some(ovk), z_address.clone(), amount_to_shield, Some(memo.clone())) { - Ok(_) =>(), - Err(e) => { - return Err(format_err!("Failed to add sapling output {}", e)); - } - }; - let consensus_branch_id = BranchId::for_height(&NETWORK, target_height_and_anchor.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 = match (&db_data).get_update_ops() { - Ok(d) => d, - Err(e) => { - return Err(format_err!("error updating database with created tx {}",e)); - }, - }; - 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( - 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)) -} #[no_mangle] pub extern "C" fn zcashlc_shield_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, @@ -1261,13 +1136,16 @@ pub extern "C" fn zcashlc_shield_funds( let res = catch_panic(|| { let db_data = wallet_db(NETWORK, db_data, db_data_len)?; - let db_cache = block_db(db_cache, db_cache_len)?; + let mut update_ops = (&db_data) + .get_update_ops() + .map_err(|e| format_err!("Could not obtain a writable database connection: {}", e))?; + 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 tsk_wif = 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 { @@ -1277,7 +1155,36 @@ pub extern "C" fn zcashlc_shield_funds( slice::from_raw_parts(output_params, output_params_len) })); - shield_funds(&db_cache, &db_data, account, &tsk, &extsk, &memo, &spend_params, &output_params) + //grab secret private key for t-funds + let sk:SecretKey = (&Wif(tsk_wif.to_string())).try_into()?; + + 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)); + }, + }; + let memo = match Memo::from_str(&memo) { + Ok(memo) => memo, + Err(_) => { + return Err(format_err!("Invalid memo input")); + } + }; + // shield_funds(&db_cache, &db_data, account, &tsk, &extsk, &memo, &spend_params, &output_params) + shield_funds(&mut update_ops, + &NETWORK, + LocalTxProver::new(spend_params, output_params), + AccountId(account), + &sk, + &extsk, + &memo, + ANCHOR_OFFSET) + .map_err(|e| format_err!("Error while shielding transaction: {}", e)) }); unwrap_exc_or(res, -1) }