[WIP] data access api compliance

This commit is contained in:
Francisco Gindre 2021-02-17 20:02:25 -03:00
parent 2d76c96b42
commit 1affc50726
13 changed files with 407 additions and 425 deletions

142
Cargo.lock generated
View File

@ -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",
]

View File

@ -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"]

View File

@ -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)"
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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<UnshieldedBalance,Error>) -> Void)
func latestUnshieldedBalance(address: String, result: @escaping (Result<WalletBalance,Error>) -> Void)
/**
gets the last stored unshielded balance
*/
func getUnshieldedBalance(address: String) throws -> UnshieldedBalance
func getTransparentBalance(address: String) throws -> WalletBalance
}
/**

View File

@ -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 {

View File

@ -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,

View File

@ -495,7 +495,7 @@ public class SDKSynchronizer: Synchronizer {
/**
gets the unshielded balance for the given address.
*/
public func latestUnshieldedBalance(address: String, result: @escaping (Result<UnshieldedBalance,Error>) -> Void) {
public func latestUnshieldedBalance(address: String, result: @escaping (Result<WalletBalance,Error>) -> 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)

View File

@ -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,

View File

@ -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<T>(exc: Result<T, ()>, 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::<Amount>();
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::<Amount>();
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<u8> {
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<P: consensus::Parameters>(
db_cache: &BlockDB,
db_data: &WalletDB<P>,
account: u32,
tsk: &str,
extsk: &str,
memo: &str,
spend_params: &Path,
output_params: &Path,
) -> Result<i64,failure::Error> {
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::<i64>()) {
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)
}