Data Access Api integration (#227)

* add cargo dependencies for data access api

* Imports + init_accounts_table

* ported init_accounts_table and init_accounts_table_with_keys

* ported zcashlc_derive_extended_spending_keys

* ported zcashlc_derive_extended_full_viewing_keys

* ported zcashlc_derive_shielded_address_from_seed

* ported zcashlc_derive_shielded_address_from_viewing_key

* ported zcashlc_derive_extended_full_viewing_key

* ported zcashlc_init_blocks_table

* port zcashlc_get_address

* port zcashlc_is_valid_transparent_address

* port zcashlc_get_balance

* fix u32 try_into error

* ported zcashlc_get_received_memo_as_utf8

* fixed zcashlc_init_blocks_table, ported zcashlc_get_sent_memo_as_utf8

* ported zcashlc_validate_combined_chain

* ported zcashlc_rewind_to_height

* ported zcashlc_rewind_to_height

* ported zcashlc_decrypt_and_store_transaction

* ported zcashlc_branch_id_for_height

* ported zcashlc_derive_transparent_address_from_seed

* Compiled lib.rs

* lib.rs compiles

* fix AccountId import

* Fix: send fails with missing params. cargo update

* Fix: demo app fails to initialize

* fix AdvancedReOrgTests

* deprecate default FEE

* Add fee for height helper for UI purposes

* default fee for heightwq

* update proto files and compiled files

* Update to librustzcash master.

* add latests changes from rust on master

* merge with master

* fix merge error

Co-authored-by: Kris Nuttycombe <kris@electriccoin.co>
This commit is contained in:
Francisco Gindre 2021-03-11 18:41:57 -03:00 committed by GitHub
parent ca722571d0
commit c69b54db9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1404 additions and 510 deletions

459
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -16,12 +16,16 @@ zcash_client_sqlite = "0.2.1"
zcash_primitives = "0.4"
#### Temporary additions: ####################################
bitvec = "0.18"
base58 = "0.1.0"
sha2 = "0.9"
bs58 = { version = "0.3", features = ["check"] }
hdwallet = "0.2.2"
ripemd160 = "0.9"
secp256k1 = "0.17.2"
# Temporary workaround for https://github.com/myrrlyn/funty/issues/3
funty = "=1.1.0"
##############################################################
[dependencies.zcash_proofs]
@ -40,11 +44,11 @@ crate-type = ["staticlib"]
[profile.release]
lto = true
[patch.crates-io]
zcash_client_backend = {git = "https://github.com/zcash/librustzcash", branch = "master"}
zcash_client_sqlite = {git = "https://github.com/zcash/librustzcash", branch = "master"}
zcash_primitives = {git = "https://github.com/zcash/librustzcash", branch = "master"}
zcash_proofs = {git = "https://github.com/zcash/librustzcash", branch = "master"}
[features]
mainnet = ["zcash_client_sqlite/mainnet"]
[patch.crates-io]
zcash_client_backend = { git = 'https://github.com/zcash/librustzcash', rev = 'c289cf9d4b46d330c265006e5f796543f2744fe5' }
zcash_client_sqlite = { git = 'https://github.com/zcash/librustzcash', rev = 'c289cf9d4b46d330c265006e5f796543f2744fe5' }
zcash_primitives = { git = 'https://github.com/zcash/librustzcash', rev = 'c289cf9d4b46d330c265006e5f796543f2744fe5' }
zcash_proofs = { git = 'https://github.com/zcash/librustzcash', rev = 'c289cf9d4b46d330c265006e5f796543f2744fe5' }

View File

@ -66,10 +66,10 @@ PODS:
- SwiftNIOFoundationCompat (< 3, >= 2.22.0)
- SwiftNIOTLS (< 3, >= 2.22.0)
- SwiftProtobuf (1.12.0)
- ZcashLightClientKit (0.9.4):
- ZcashLightClientKit (0.10.0):
- gRPC-Swift (= 1.0.0-alpha.19)
- SQLite.swift (~> 0.12.2)
- ZcashLightClientKit/Tests (0.9.4):
- ZcashLightClientKit/Tests (0.10.0):
- gRPC-Swift (= 1.0.0-alpha.19)
- SQLite.swift (~> 0.12.2)
@ -145,7 +145,7 @@ SPEC CHECKSUMS:
SwiftNIOTLS: 46bb3a0ff37d6b52ae6baf5207ec3cd411da327c
SwiftNIOTransportServices: 801923921fbecdcde1e1c1ff38e812167d01ead1
SwiftProtobuf: 4ef85479c18ca85b5482b343df9c319c62bda699
ZcashLightClientKit: 7f144177deece40fb9075bf6024028026255f75b
ZcashLightClientKit: a1a36be74ca95e2802ba3d6bcc40e9b2753eb0da
PODFILE CHECKSUM: 7d5095283dc02470f40ab06564d94076ba16d570

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'ZcashLightClientKit'
s.version = '0.9.4'
s.version = '0.10.0'
s.summary = 'Zcash Light Client wallet SDK for iOS'
s.description = <<-DESC

View File

@ -198,7 +198,13 @@ public class Initializer {
transactionRepository: transactionRepository,
backend: rustBackend)
guard try rustBackend.initAccountsTable(dbData: dataDbURL, exfvks: viewingKeys) else {
do {
guard try rustBackend.initAccountsTable(dbData: dataDbURL, exfvks: viewingKeys) else {
throw rustBackend.lastError() ?? InitializerError.accountInitFailed
}
} catch RustWeldingError.dataDbNotEmpty {
// this is fine
}catch {
throw rustBackend.lastError() ?? InitializerError.accountInitFailed
}

View File

@ -215,7 +215,6 @@ class ZcashRustBackend: ZcashRustBackendWelding {
dbData.1,
account,
[CChar](extsk.utf8CString),
consensusBranchId,
[CChar](to.utf8CString),
value,
[CChar](memoBytes.utf8CString),

View File

@ -62,6 +62,10 @@ message LightdInfo {
uint64 saplingActivationHeight = 5; // depends on mainnet or testnet
string consensusBranchId = 6; // protocol identifier, see consensus/upgrades.cpp
uint64 blockHeight = 7; // latest block on the best chain
string gitCommit = 8;
string branch = 9;
string buildDate = 10;
string buildUser = 11;
}
// TransparentAddressBlockFilter restricts the results to the given address
@ -86,6 +90,45 @@ message PingResponse {
int64 exit = 2;
}
message Address {
string address = 1;
}
message AddressList {
repeated string addresses = 1;
}
message Balance {
int64 valueZat = 1;
}
message Exclude {
repeated bytes txid = 1;
}
// The TreeState is derived from the zcash z_gettreestate rpc.
message TreeState {
string network = 1; // "main" or "test"
uint64 height = 2;
string hash = 3; // block id
uint32 time = 4; // Unix epoch time when the block was mined
string tree = 5; // sapling commitment tree state
}
message GetAddressUtxosArg {
string address = 1;
uint64 startHeight = 2;
uint32 maxEntries = 3; // zero means unlimited
}
message GetAddressUtxosReply {
bytes txid = 1;
int32 index = 2;
bytes script = 3;
int64 valueZat = 4;
uint64 height = 5;
}
message GetAddressUtxosReplyList {
repeated GetAddressUtxosReply addressUtxos = 1;
}
service CompactTxStreamer {
// Return the height of the tip of the best chain
rpc GetLatestBlock(ChainSpec) returns (BlockID) {}
@ -100,7 +143,29 @@ service CompactTxStreamer {
rpc SendTransaction(RawTransaction) returns (SendResponse) {}
// Return the txids corresponding to the given t-address within the given block range
rpc GetAddressTxids(TransparentAddressBlockFilter) returns (stream RawTransaction) {}
rpc GetTaddressTxids(TransparentAddressBlockFilter) returns (stream RawTransaction) {}
rpc GetTaddressBalance(AddressList) returns (Balance) {}
rpc GetTaddressBalanceStream(stream Address) returns (Balance) {}
// Return the compact transactions currently in the mempool; the results
// can be a few seconds out of date. If the Exclude list is empty, return
// all transactions; otherwise return all *except* those in the Exclude list
// (if any); this allows the client to avoid receiving transactions that it
// already has (from an earlier call to this rpc). The transaction IDs in the
// Exclude list can be shortened to any number of bytes to make the request
// more bandwidth-efficient; if two or more transactions in the mempool
// match a shortened txid, they are all sent (none is excluded). Transactions
// in the exclude list that don't exist in the mempool are ignored.
rpc GetMempoolTx(Exclude) returns (stream CompactTx) {}
// GetTreeState returns the note commitment tree state corresponding to the given block.
// See section 3.7 of the zcash protocol specification. It returns several other useful
// values also (even though they can be obtained using GetBlock).
// The block can be specified by either height or hash.
rpc GetTreeState(BlockID) returns (TreeState) {}
rpc GetAddressUtxos(GetAddressUtxosArg) returns (GetAddressUtxosReplyList) {}
rpc GetAddressUtxosStream(GetAddressUtxosArg) returns (stream GetAddressUtxosReply) {}
// Return information about this lightwalletd instance and the blockchain
rpc GetLightdInfo(Empty) returns (LightdInfo) {}

View File

@ -53,12 +53,43 @@ internal protocol CompactTxStreamerClientProtocol: GRPCClient {
callOptions: CallOptions?
) -> UnaryCall<RawTransaction, SendResponse>
func getAddressTxids(
func getTaddressTxids(
_ request: TransparentAddressBlockFilter,
callOptions: CallOptions?,
handler: @escaping (RawTransaction) -> Void
) -> ServerStreamingCall<TransparentAddressBlockFilter, RawTransaction>
func getTaddressBalance(
_ request: AddressList,
callOptions: CallOptions?
) -> UnaryCall<AddressList, Balance>
func getTaddressBalanceStream(
callOptions: CallOptions?
) -> ClientStreamingCall<Address, Balance>
func getMempoolTx(
_ request: Exclude,
callOptions: CallOptions?,
handler: @escaping (CompactTx) -> Void
) -> ServerStreamingCall<Exclude, CompactTx>
func getTreeState(
_ request: BlockID,
callOptions: CallOptions?
) -> UnaryCall<BlockID, TreeState>
func getAddressUtxos(
_ request: GetAddressUtxosArg,
callOptions: CallOptions?
) -> UnaryCall<GetAddressUtxosArg, GetAddressUtxosReplyList>
func getAddressUtxosStream(
_ request: GetAddressUtxosArg,
callOptions: CallOptions?,
handler: @escaping (GetAddressUtxosReply) -> Void
) -> ServerStreamingCall<GetAddressUtxosArg, GetAddressUtxosReply>
func getLightdInfo(
_ request: Empty,
callOptions: CallOptions?
@ -164,17 +195,136 @@ extension CompactTxStreamerClientProtocol {
/// Return the txids corresponding to the given t-address within the given block range
///
/// - Parameters:
/// - request: Request to send to GetAddressTxids.
/// - request: Request to send to GetTaddressTxids.
/// - callOptions: Call options.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.
internal func getAddressTxids(
internal func getTaddressTxids(
_ request: TransparentAddressBlockFilter,
callOptions: CallOptions? = nil,
handler: @escaping (RawTransaction) -> Void
) -> ServerStreamingCall<TransparentAddressBlockFilter, RawTransaction> {
return self.makeServerStreamingCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressTxids",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressTxids",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
handler: handler
)
}
/// Unary call to GetTaddressBalance
///
/// - Parameters:
/// - request: Request to send to GetTaddressBalance.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
internal func getTaddressBalance(
_ request: AddressList,
callOptions: CallOptions? = nil
) -> UnaryCall<AddressList, Balance> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressBalance",
request: request,
callOptions: callOptions ?? self.defaultCallOptions
)
}
/// Client streaming call to GetTaddressBalanceStream
///
/// Callers should use the `send` method on the returned object to send messages
/// to the server. The caller should send an `.end` after the final message has been sent.
///
/// - Parameters:
/// - callOptions: Call options.
/// - Returns: A `ClientStreamingCall` with futures for the metadata, status and response.
internal func getTaddressBalanceStream(
callOptions: CallOptions? = nil
) -> ClientStreamingCall<Address, Balance> {
return self.makeClientStreamingCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressBalanceStream",
callOptions: callOptions ?? self.defaultCallOptions
)
}
/// Return the compact transactions currently in the mempool; the results
/// can be a few seconds out of date. If the Exclude list is empty, return
/// all transactions; otherwise return all *except* those in the Exclude list
/// (if any); this allows the client to avoid receiving transactions that it
/// already has (from an earlier call to this rpc). The transaction IDs in the
/// Exclude list can be shortened to any number of bytes to make the request
/// more bandwidth-efficient; if two or more transactions in the mempool
/// match a shortened txid, they are all sent (none is excluded). Transactions
/// in the exclude list that don't exist in the mempool are ignored.
///
/// - Parameters:
/// - request: Request to send to GetMempoolTx.
/// - callOptions: Call options.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.
internal func getMempoolTx(
_ request: Exclude,
callOptions: CallOptions? = nil,
handler: @escaping (CompactTx) -> Void
) -> ServerStreamingCall<Exclude, CompactTx> {
return self.makeServerStreamingCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolTx",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
handler: handler
)
}
/// GetTreeState returns the note commitment tree state corresponding to the given block.
/// See section 3.7 of the zcash protocol specification. It returns several other useful
/// values also (even though they can be obtained using GetBlock).
/// The block can be specified by either height or hash.
///
/// - Parameters:
/// - request: Request to send to GetTreeState.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
internal func getTreeState(
_ request: BlockID,
callOptions: CallOptions? = nil
) -> UnaryCall<BlockID, TreeState> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTreeState",
request: request,
callOptions: callOptions ?? self.defaultCallOptions
)
}
/// Unary call to GetAddressUtxos
///
/// - Parameters:
/// - request: Request to send to GetAddressUtxos.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
internal func getAddressUtxos(
_ request: GetAddressUtxosArg,
callOptions: CallOptions? = nil
) -> UnaryCall<GetAddressUtxosArg, GetAddressUtxosReplyList> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxos",
request: request,
callOptions: callOptions ?? self.defaultCallOptions
)
}
/// Server streaming call to GetAddressUtxosStream
///
/// - Parameters:
/// - request: Request to send to GetAddressUtxosStream.
/// - callOptions: Call options.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.
internal func getAddressUtxosStream(
_ request: GetAddressUtxosArg,
callOptions: CallOptions? = nil,
handler: @escaping (GetAddressUtxosReply) -> Void
) -> ServerStreamingCall<GetAddressUtxosArg, GetAddressUtxosReply> {
return self.makeServerStreamingCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxosStream",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
handler: handler

View File

@ -187,6 +187,14 @@ struct LightdInfo {
/// latest block on the best chain
var blockHeight: UInt64 = 0
var gitCommit: String = String()
var branch: String = String()
var buildDate: String = String()
var buildUser: String = String()
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
@ -251,6 +259,128 @@ struct PingResponse {
init() {}
}
struct Address {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var address: String = String()
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
struct AddressList {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var addresses: [String] = []
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
struct Balance {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var valueZat: Int64 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
struct Exclude {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var txid: [Data] = []
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
/// The TreeState is derived from the zcash z_gettreestate rpc.
struct TreeState {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// "main" or "test"
var network: String = String()
var height: UInt64 = 0
/// block id
var hash: String = String()
/// Unix epoch time when the block was mined
var time: UInt32 = 0
/// sapling commitment tree state
var tree: String = String()
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
struct GetAddressUtxosArg {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var address: String = String()
var startHeight: UInt64 = 0
/// zero means unlimited
var maxEntries: UInt32 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
struct GetAddressUtxosReply {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var txid: Data = SwiftProtobuf.Internal.emptyData
var index: Int32 = 0
var script: Data = SwiftProtobuf.Internal.emptyData
var valueZat: Int64 = 0
var height: UInt64 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
struct GetAddressUtxosReplyList {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var addressUtxos: [GetAddressUtxosReply] = []
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "cash.z.wallet.sdk.rpc"
@ -484,6 +614,10 @@ extension LightdInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
5: .same(proto: "saplingActivationHeight"),
6: .same(proto: "consensusBranchId"),
7: .same(proto: "blockHeight"),
8: .same(proto: "gitCommit"),
9: .same(proto: "branch"),
10: .same(proto: "buildDate"),
11: .same(proto: "buildUser"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -496,6 +630,10 @@ extension LightdInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
case 5: try decoder.decodeSingularUInt64Field(value: &self.saplingActivationHeight)
case 6: try decoder.decodeSingularStringField(value: &self.consensusBranchID)
case 7: try decoder.decodeSingularUInt64Field(value: &self.blockHeight)
case 8: try decoder.decodeSingularStringField(value: &self.gitCommit)
case 9: try decoder.decodeSingularStringField(value: &self.branch)
case 10: try decoder.decodeSingularStringField(value: &self.buildDate)
case 11: try decoder.decodeSingularStringField(value: &self.buildUser)
default: break
}
}
@ -523,6 +661,18 @@ extension LightdInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
if self.blockHeight != 0 {
try visitor.visitSingularUInt64Field(value: self.blockHeight, fieldNumber: 7)
}
if !self.gitCommit.isEmpty {
try visitor.visitSingularStringField(value: self.gitCommit, fieldNumber: 8)
}
if !self.branch.isEmpty {
try visitor.visitSingularStringField(value: self.branch, fieldNumber: 9)
}
if !self.buildDate.isEmpty {
try visitor.visitSingularStringField(value: self.buildDate, fieldNumber: 10)
}
if !self.buildUser.isEmpty {
try visitor.visitSingularStringField(value: self.buildUser, fieldNumber: 11)
}
try unknownFields.traverse(visitor: &visitor)
}
@ -534,6 +684,10 @@ extension LightdInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
if lhs.saplingActivationHeight != rhs.saplingActivationHeight {return false}
if lhs.consensusBranchID != rhs.consensusBranchID {return false}
if lhs.blockHeight != rhs.blockHeight {return false}
if lhs.gitCommit != rhs.gitCommit {return false}
if lhs.branch != rhs.branch {return false}
if lhs.buildDate != rhs.buildDate {return false}
if lhs.buildUser != rhs.buildUser {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
@ -637,3 +791,295 @@ extension PingResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
return true
}
}
extension Address: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".Address"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "address"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.address)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.address.isEmpty {
try visitor.visitSingularStringField(value: self.address, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: Address, rhs: Address) -> Bool {
if lhs.address != rhs.address {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension AddressList: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".AddressList"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "addresses"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeRepeatedStringField(value: &self.addresses)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.addresses.isEmpty {
try visitor.visitRepeatedStringField(value: self.addresses, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: AddressList, rhs: AddressList) -> Bool {
if lhs.addresses != rhs.addresses {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Balance: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".Balance"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "valueZat"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularInt64Field(value: &self.valueZat)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.valueZat != 0 {
try visitor.visitSingularInt64Field(value: self.valueZat, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: Balance, rhs: Balance) -> Bool {
if lhs.valueZat != rhs.valueZat {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Exclude: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".Exclude"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "txid"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeRepeatedBytesField(value: &self.txid)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.txid.isEmpty {
try visitor.visitRepeatedBytesField(value: self.txid, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: Exclude, rhs: Exclude) -> Bool {
if lhs.txid != rhs.txid {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension TreeState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".TreeState"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "network"),
2: .same(proto: "height"),
3: .same(proto: "hash"),
4: .same(proto: "time"),
5: .same(proto: "tree"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.network)
case 2: try decoder.decodeSingularUInt64Field(value: &self.height)
case 3: try decoder.decodeSingularStringField(value: &self.hash)
case 4: try decoder.decodeSingularUInt32Field(value: &self.time)
case 5: try decoder.decodeSingularStringField(value: &self.tree)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.network.isEmpty {
try visitor.visitSingularStringField(value: self.network, fieldNumber: 1)
}
if self.height != 0 {
try visitor.visitSingularUInt64Field(value: self.height, fieldNumber: 2)
}
if !self.hash.isEmpty {
try visitor.visitSingularStringField(value: self.hash, fieldNumber: 3)
}
if self.time != 0 {
try visitor.visitSingularUInt32Field(value: self.time, fieldNumber: 4)
}
if !self.tree.isEmpty {
try visitor.visitSingularStringField(value: self.tree, fieldNumber: 5)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: TreeState, rhs: TreeState) -> Bool {
if lhs.network != rhs.network {return false}
if lhs.height != rhs.height {return false}
if lhs.hash != rhs.hash {return false}
if lhs.time != rhs.time {return false}
if lhs.tree != rhs.tree {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension GetAddressUtxosArg: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".GetAddressUtxosArg"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "address"),
2: .same(proto: "startHeight"),
3: .same(proto: "maxEntries"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.address)
case 2: try decoder.decodeSingularUInt64Field(value: &self.startHeight)
case 3: try decoder.decodeSingularUInt32Field(value: &self.maxEntries)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.address.isEmpty {
try visitor.visitSingularStringField(value: self.address, fieldNumber: 1)
}
if self.startHeight != 0 {
try visitor.visitSingularUInt64Field(value: self.startHeight, fieldNumber: 2)
}
if self.maxEntries != 0 {
try visitor.visitSingularUInt32Field(value: self.maxEntries, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: GetAddressUtxosArg, rhs: GetAddressUtxosArg) -> Bool {
if lhs.address != rhs.address {return false}
if lhs.startHeight != rhs.startHeight {return false}
if lhs.maxEntries != rhs.maxEntries {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension GetAddressUtxosReply: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".GetAddressUtxosReply"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "txid"),
2: .same(proto: "index"),
3: .same(proto: "script"),
4: .same(proto: "valueZat"),
5: .same(proto: "height"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularBytesField(value: &self.txid)
case 2: try decoder.decodeSingularInt32Field(value: &self.index)
case 3: try decoder.decodeSingularBytesField(value: &self.script)
case 4: try decoder.decodeSingularInt64Field(value: &self.valueZat)
case 5: try decoder.decodeSingularUInt64Field(value: &self.height)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.txid.isEmpty {
try visitor.visitSingularBytesField(value: self.txid, fieldNumber: 1)
}
if self.index != 0 {
try visitor.visitSingularInt32Field(value: self.index, fieldNumber: 2)
}
if !self.script.isEmpty {
try visitor.visitSingularBytesField(value: self.script, fieldNumber: 3)
}
if self.valueZat != 0 {
try visitor.visitSingularInt64Field(value: self.valueZat, fieldNumber: 4)
}
if self.height != 0 {
try visitor.visitSingularUInt64Field(value: self.height, fieldNumber: 5)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: GetAddressUtxosReply, rhs: GetAddressUtxosReply) -> Bool {
if lhs.txid != rhs.txid {return false}
if lhs.index != rhs.index {return false}
if lhs.script != rhs.script {return false}
if lhs.valueZat != rhs.valueZat {return false}
if lhs.height != rhs.height {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension GetAddressUtxosReplyList: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".GetAddressUtxosReplyList"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "addressUtxos"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeRepeatedMessageField(value: &self.addressUtxos)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.addressUtxos.isEmpty {
try visitor.visitRepeatedMessageField(value: self.addressUtxos, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: GetAddressUtxosReplyList, rhs: GetAddressUtxosReplyList) -> Bool {
if lhs.addressUtxos != rhs.addressUtxos {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

View File

@ -24,7 +24,6 @@ int64_t zcashlc_create_to_address(const uint8_t *db_data,
uintptr_t db_data_len,
int32_t account,
const char *extsk,
int32_t consensus_branch_id,
const char *to,
int64_t value,
const char *memo,
@ -192,6 +191,11 @@ bool zcashlc_is_valid_transparent_address(const char *address);
bool zcashlc_is_valid_viewing_key(const char *key);
/**
* returns whether the given viewing key is valid or not
*/
bool zcashlc_is_valid_viewing_key(const char *key);
/**
* Returns the length of the last error message to be logged.
*/

View File

@ -77,7 +77,7 @@ class AdvancedReOrgTests: XCTestCase {
let receivedTxHeight: BlockHeight = 663188
var initialTotalBalance: Int64 = -1
var initialVerifiedBalance: Int64 = -1
self.expectedReorgHeight = receivedTxHeight
self.expectedReorgHeight = receivedTxHeight + 1
/*
precondition:know balances before tx at received_Tx_height arrives
@ -402,7 +402,7 @@ class AdvancedReOrgTests: XCTestCase {
func testIncomingTransactionIndexChange() throws {
hookToReOrgNotification()
self.expectedReorgHeight = 663195
self.expectedReorgHeight = 663196
self.expectedRewindHeight = 663175
try coordinator.reset(saplingActivation: birthday)
try coordinator.resetBlocks(dataset: .predefined(dataset: .txIndexChangeBefore))
@ -779,7 +779,7 @@ class AdvancedReOrgTests: XCTestCase {
/*
9. sync to latest height
*/
self.expectedReorgHeight = sentTxHeight
self.expectedReorgHeight = sentTxHeight + 1
let afterReorgExpectation = XCTestExpectation(description: "after reorg sync")
try coordinator.sync(completion: { (s) in
@ -1062,7 +1062,7 @@ class AdvancedReOrgTests: XCTestCase {
return
}
self.expectedReorgHeight = sentTxHeight
self.expectedReorgHeight = sentTxHeight + 1
/*
4. stage transaction at sentTxHeight
*/

View File

@ -20,71 +20,101 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import GRPC
import NIO
import NIOHTTP1
import SwiftProtobuf
@testable import ZcashLightClientKit
/// Usage: instantiate DarksideStreamerClient, then call methods of this protocol to make API calls.
internal protocol DarksideStreamerClientProtocol {
func reset(_ request: DarksideMetaState, callOptions: CallOptions?) -> UnaryCall<DarksideMetaState, Empty>
func stageBlocksStream(callOptions: CallOptions?) -> ClientStreamingCall<DarksideBlock, Empty>
func stageBlocks(_ request: DarksideBlocksURL, callOptions: CallOptions?) -> UnaryCall<DarksideBlocksURL, Empty>
func stageBlocksCreate(_ request: DarksideEmptyBlocks, callOptions: CallOptions?) -> UnaryCall<DarksideEmptyBlocks, Empty>
func stageTransactionsStream(callOptions: CallOptions?) -> ClientStreamingCall<RawTransaction, Empty>
func stageTransactions(_ request: DarksideTransactionsURL, callOptions: CallOptions?) -> UnaryCall<DarksideTransactionsURL, Empty>
func applyStaged(_ request: DarksideHeight, callOptions: CallOptions?) -> UnaryCall<DarksideHeight, Empty>
func getIncomingTransactions(_ request: Empty, callOptions: CallOptions?, handler: @escaping (RawTransaction) -> Void) -> ServerStreamingCall<Empty, RawTransaction>
func clearIncomingTransactions(_ request: Empty, callOptions: CallOptions?) -> UnaryCall<Empty, Empty>
internal protocol DarksideStreamerClientProtocol: GRPCClient {
func reset(
_ request: DarksideMetaState,
callOptions: CallOptions?
) -> UnaryCall<DarksideMetaState, Empty>
func stageBlocksStream(
callOptions: CallOptions?
) -> ClientStreamingCall<DarksideBlock, Empty>
func stageBlocks(
_ request: DarksideBlocksURL,
callOptions: CallOptions?
) -> UnaryCall<DarksideBlocksURL, Empty>
func stageBlocksCreate(
_ request: DarksideEmptyBlocks,
callOptions: CallOptions?
) -> UnaryCall<DarksideEmptyBlocks, Empty>
func stageTransactionsStream(
callOptions: CallOptions?
) -> ClientStreamingCall<RawTransaction, Empty>
func stageTransactions(
_ request: DarksideTransactionsURL,
callOptions: CallOptions?
) -> UnaryCall<DarksideTransactionsURL, Empty>
func applyStaged(
_ request: DarksideHeight,
callOptions: CallOptions?
) -> UnaryCall<DarksideHeight, Empty>
func getIncomingTransactions(
_ request: Empty,
callOptions: CallOptions?,
handler: @escaping (RawTransaction) -> Void
) -> ServerStreamingCall<Empty, RawTransaction>
func clearIncomingTransactions(
_ request: Empty,
callOptions: CallOptions?
) -> UnaryCall<Empty, Empty>
}
internal final class DarksideStreamerClient: GRPCClient, DarksideStreamerClientProtocol {
internal let channel: GRPCChannel
internal var defaultCallOptions: CallOptions
/// Creates a client for the cash.z.wallet.sdk.rpc.DarksideStreamer service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
internal init(channel: GRPCChannel, defaultCallOptions: CallOptions = CallOptions()) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
}
extension DarksideStreamerClientProtocol {
/// Reset reverts all darksidewalletd state (active block range, latest height,
/// staged blocks and transactions) and lightwalletd state (cache) to empty,
/// the same as the initial state. This occurs synchronously and instantaneously;
/// no reorg happens in lightwalletd. This is good to do before each independent
/// test so that no state leaks from one test to another.
/// Also sets (some of) the values returned by GetLightdInfo().
/// Also sets (some of) the values returned by GetLightdInfo(). The Sapling
/// activation height specified here must be where the block range starts.
///
/// - Parameters:
/// - request: Request to send to Reset.
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
internal func reset(_ request: DarksideMetaState, callOptions: CallOptions? = nil) -> UnaryCall<DarksideMetaState, Empty> {
return self.makeUnaryCall(path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/Reset",
request: request,
callOptions: callOptions ?? self.defaultCallOptions)
internal func reset(
_ request: DarksideMetaState,
callOptions: CallOptions? = nil
) -> UnaryCall<DarksideMetaState, Empty> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/Reset",
request: request,
callOptions: callOptions ?? self.defaultCallOptions
)
}
/// StageBlocksStream accepts a list of blocks and saves them into the blocks
/// staging area until ApplyStaged() is called; there is no immediate effect on
/// the mock zcashd. Blocks are hex-encoded.
/// the mock zcashd. Blocks are hex-encoded. Order is important, see ApplyStaged.
///
/// Callers should use the `send` method on the returned object to send messages
/// to the server. The caller should send an `.end` after the final message has been sent.
///
/// - Parameters:
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - callOptions: Call options.
/// - Returns: A `ClientStreamingCall` with futures for the metadata, status and response.
internal func stageBlocksStream(callOptions: CallOptions? = nil) -> ClientStreamingCall<DarksideBlock, Empty> {
return self.makeClientStreamingCall(path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/StageBlocksStream",
callOptions: callOptions ?? self.defaultCallOptions)
internal func stageBlocksStream(
callOptions: CallOptions? = nil
) -> ClientStreamingCall<DarksideBlock, Empty> {
return self.makeClientStreamingCall(
path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/StageBlocksStream",
callOptions: callOptions ?? self.defaultCallOptions
)
}
/// StageBlocks is the same as StageBlocksStream() except the blocks are fetched
@ -92,31 +122,41 @@ internal final class DarksideStreamerClient: GRPCClient, DarksideStreamerClientP
///
/// - Parameters:
/// - request: Request to send to StageBlocks.
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
internal func stageBlocks(_ request: DarksideBlocksURL, callOptions: CallOptions? = nil) -> UnaryCall<DarksideBlocksURL, Empty> {
return self.makeUnaryCall(path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/StageBlocks",
request: request,
callOptions: callOptions ?? self.defaultCallOptions)
internal func stageBlocks(
_ request: DarksideBlocksURL,
callOptions: CallOptions? = nil
) -> UnaryCall<DarksideBlocksURL, Empty> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/StageBlocks",
request: request,
callOptions: callOptions ?? self.defaultCallOptions
)
}
/// StageBlocksCreate is like the previous two, except it creates 'count'
/// empty blocks at consecutive heights starting at height 'height'. The
/// 'nonce' is part of the header, so it contributes to the block hash; this
/// lets you create two fake blocks with the same transactions (or no
/// transactions) and same height, with two different hashes.
/// lets you create identical blocks (same transactions and height), but with
/// different hashes.
///
/// - Parameters:
/// - request: Request to send to StageBlocksCreate.
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
internal func stageBlocksCreate(_ request: DarksideEmptyBlocks, callOptions: CallOptions? = nil) -> UnaryCall<DarksideEmptyBlocks, Empty> {
return self.makeUnaryCall(path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/StageBlocksCreate",
request: request,
callOptions: callOptions ?? self.defaultCallOptions)
internal func stageBlocksCreate(
_ request: DarksideEmptyBlocks,
callOptions: CallOptions? = nil
) -> UnaryCall<DarksideEmptyBlocks, Empty> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/StageBlocksCreate",
request: request,
callOptions: callOptions ?? self.defaultCallOptions
)
}
/// StageTransactions stores the given transaction-height pairs in the
/// StageTransactionsStream stores the given transaction-height pairs in the
/// staging area until ApplyStaged() is called. Note that these transactions
/// are not returned by the production GetTransaction() gRPC until they
/// appear in a "mined" block (contained in the active blockchain presented
@ -126,34 +166,47 @@ internal final class DarksideStreamerClient: GRPCClient, DarksideStreamerClientP
/// to the server. The caller should send an `.end` after the final message has been sent.
///
/// - Parameters:
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - callOptions: Call options.
/// - Returns: A `ClientStreamingCall` with futures for the metadata, status and response.
internal func stageTransactionsStream(callOptions: CallOptions? = nil) -> ClientStreamingCall<RawTransaction, Empty> {
return self.makeClientStreamingCall(path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/StageTransactionsStream",
callOptions: callOptions ?? self.defaultCallOptions)
internal func stageTransactionsStream(
callOptions: CallOptions? = nil
) -> ClientStreamingCall<RawTransaction, Empty> {
return self.makeClientStreamingCall(
path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/StageTransactionsStream",
callOptions: callOptions ?? self.defaultCallOptions
)
}
/// Unary call to StageTransactions
/// StageTransactions is the same except the transactions are fetched from
/// the given url. They are all staged into the block at the given height.
/// Staging transactions to different heights requires multiple calls.
///
/// - Parameters:
/// - request: Request to send to StageTransactions.
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
internal func stageTransactions(_ request: DarksideTransactionsURL, callOptions: CallOptions? = nil) -> UnaryCall<DarksideTransactionsURL, Empty> {
return self.makeUnaryCall(path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/StageTransactions",
request: request,
callOptions: callOptions ?? self.defaultCallOptions)
internal func stageTransactions(
_ request: DarksideTransactionsURL,
callOptions: CallOptions? = nil
) -> UnaryCall<DarksideTransactionsURL, Empty> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/StageTransactions",
request: request,
callOptions: callOptions ?? self.defaultCallOptions
)
}
/// ApplyStaged iterates the list of blocks that were staged by the
/// StageBlocks*() gRPCs, in the order they were staged, and "merges" each
/// into the active, working blocks list that the mock zcashd is presenting
/// to lightwalletd. The resulting working block list can't have gaps; if the
/// working block range is 1000-1006, and the staged block range is 1003-1004,
/// the resulting range is 1000-1004, with 1000-1002 unchanged, blocks
/// 1003-1004 from the new range, and 1005-1006 dropped. After merging all
/// blocks, ApplyStaged() appends staged transactions (in the order received)
/// into each one's corresponding block. The staging area is then cleared.
/// to lightwalletd. Even as each block is applied, the active list can't
/// have gaps; if the active block range is 1000-1006, and the staged block
/// range is 1003-1004, the resulting range is 1000-1004, with 1000-1002
/// unchanged, blocks 1003-1004 from the new range, and 1005-1006 dropped.
///
/// After merging all blocks, ApplyStaged() appends staged transactions (in
/// the order received) into each one's corresponding (by height) block
/// The staging area is then cleared.
///
/// The argument specifies the latest block height that mock zcashd reports
/// (i.e. what's returned by GetLatestBlock). Note that ApplyStaged() can
@ -162,12 +215,17 @@ internal final class DarksideStreamerClient: GRPCClient, DarksideStreamerClientP
///
/// - Parameters:
/// - request: Request to send to ApplyStaged.
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
internal func applyStaged(_ request: DarksideHeight, callOptions: CallOptions? = nil) -> UnaryCall<DarksideHeight, Empty> {
return self.makeUnaryCall(path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/ApplyStaged",
request: request,
callOptions: callOptions ?? self.defaultCallOptions)
internal func applyStaged(
_ request: DarksideHeight,
callOptions: CallOptions? = nil
) -> UnaryCall<DarksideHeight, Empty> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/ApplyStaged",
request: request,
callOptions: callOptions ?? self.defaultCallOptions
)
}
/// Calls to the production gRPC SendTransaction() store the transaction in
@ -180,27 +238,52 @@ internal final class DarksideStreamerClient: GRPCClient, DarksideStreamerClientP
///
/// - Parameters:
/// - request: Request to send to GetIncomingTransactions.
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - callOptions: Call options.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.
internal func getIncomingTransactions(_ request: Empty, callOptions: CallOptions? = nil, handler: @escaping (RawTransaction) -> Void) -> ServerStreamingCall<Empty, RawTransaction> {
return self.makeServerStreamingCall(path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/GetIncomingTransactions",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
handler: handler)
internal func getIncomingTransactions(
_ request: Empty,
callOptions: CallOptions? = nil,
handler: @escaping (RawTransaction) -> Void
) -> ServerStreamingCall<Empty, RawTransaction> {
return self.makeServerStreamingCall(
path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/GetIncomingTransactions",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
handler: handler
)
}
/// Clear the incoming transaction pool.
///
/// - Parameters:
/// - request: Request to send to ClearIncomingTransactions.
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
internal func clearIncomingTransactions(_ request: Empty, callOptions: CallOptions? = nil) -> UnaryCall<Empty, Empty> {
return self.makeUnaryCall(path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/ClearIncomingTransactions",
request: request,
callOptions: callOptions ?? self.defaultCallOptions)
internal func clearIncomingTransactions(
_ request: Empty,
callOptions: CallOptions? = nil
) -> UnaryCall<Empty, Empty> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.DarksideStreamer/ClearIncomingTransactions",
request: request,
callOptions: callOptions ?? self.defaultCallOptions
)
}
}
internal final class DarksideStreamerClient: DarksideStreamerClientProtocol {
internal let channel: GRPCChannel
internal var defaultCallOptions: CallOptions
/// Creates a client for the cash.z.wallet.sdk.rpc.DarksideStreamer service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
internal init(channel: GRPCChannel, defaultCallOptions: CallOptions = CallOptions()) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
}
}

View File

@ -1,4 +1,5 @@
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: darkside.proto

View File

@ -53,12 +53,13 @@ service DarksideStreamer {
// the same as the initial state. This occurs synchronously and instantaneously;
// no reorg happens in lightwalletd. This is good to do before each independent
// test so that no state leaks from one test to another.
// Also sets (some of) the values returned by GetLightdInfo().
// Also sets (some of) the values returned by GetLightdInfo(). The Sapling
// activation height specified here must be where the block range starts.
rpc Reset(DarksideMetaState) returns (Empty) {}
// StageBlocksStream accepts a list of blocks and saves them into the blocks
// staging area until ApplyStaged() is called; there is no immediate effect on
// the mock zcashd. Blocks are hex-encoded.
// the mock zcashd. Blocks are hex-encoded. Order is important, see ApplyStaged.
rpc StageBlocksStream(stream DarksideBlock) returns (Empty) {}
// StageBlocks is the same as StageBlocksStream() except the blocks are fetched
@ -68,29 +69,33 @@ service DarksideStreamer {
// StageBlocksCreate is like the previous two, except it creates 'count'
// empty blocks at consecutive heights starting at height 'height'. The
// 'nonce' is part of the header, so it contributes to the block hash; this
// lets you create two fake blocks with the same transactions (or no
// transactions) and same height, with two different hashes.
// lets you create identical blocks (same transactions and height), but with
// different hashes.
rpc StageBlocksCreate(DarksideEmptyBlocks) returns (Empty) {}
// StageTransactions stores the given transaction-height pairs in the
// StageTransactionsStream stores the given transaction-height pairs in the
// staging area until ApplyStaged() is called. Note that these transactions
// are not returned by the production GetTransaction() gRPC until they
// appear in a "mined" block (contained in the active blockchain presented
// by the mock zcashd).
rpc StageTransactionsStream(stream RawTransaction) returns (Empty) {}
// StageTransactions is the same except the transactions are fetched from
// the given url. They are all staged into the block at the given height.
// Staging transactions to different heights requires multiple calls.
rpc StageTransactions(DarksideTransactionsURL) returns (Empty) {}
// ApplyStaged iterates the list of blocks that were staged by the
// StageBlocks*() gRPCs, in the order they were staged, and "merges" each
// into the active, working blocks list that the mock zcashd is presenting
// to lightwalletd. The resulting working block list can't have gaps; if the
// working block range is 1000-1006, and the staged block range is 1003-1004,
// the resulting range is 1000-1004, with 1000-1002 unchanged, blocks
// 1003-1004 from the new range, and 1005-1006 dropped. After merging all
// blocks, ApplyStaged() appends staged transactions (in the order received)
// into each one's corresponding block. The staging area is then cleared.
// to lightwalletd. Even as each block is applied, the active list can't
// have gaps; if the active block range is 1000-1006, and the staged block
// range is 1003-1004, the resulting range is 1000-1004, with 1000-1002
// unchanged, blocks 1003-1004 from the new range, and 1005-1006 dropped.
//
// After merging all blocks, ApplyStaged() appends staged transactions (in
// the order received) into each one's corresponding (by height) block
// The staging area is then cleared.
//
// The argument specifies the latest block height that mock zcashd reports
// (i.e. what's returned by GetLatestBlock). Note that ApplyStaged() can

View File

@ -6,55 +6,42 @@ use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::slice;
use std::str::FromStr;
use std::convert::Into;
use zcash_client_backend::{
address::RecipientAddress,
data_api::{
chain::{scan_cached_blocks, validate_chain},
error::Error,
wallet::{create_spend_to_address, decrypt_and_store_transaction},
WalletRead, WalletWrite,
},
encoding::{
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::AccountId,
wallet::OvkPolicy,
};
use zcash_client_sqlite::{
chain::{rewind_to_height, validate_combined_chain},
error::ErrorKind,
init::{init_accounts_table, init_blocks_table, init_data_database},
query::{
get_address, get_balance, get_received_memo_as_utf8, get_sent_memo_as_utf8,
get_verified_balance,
},
scan::{decrypt_and_store_transaction, scan_cached_blocks},
transact::{create_to_address, OvkPolicy},
error::SqliteClientError,
wallet::init::{init_accounts_table, init_blocks_table, init_wallet_db},
BlockDB, NoteId, WalletDB,
};
use zcash_primitives::{
block::BlockHash,
consensus::BranchId,
consensus::BlockHeight,
consensus::{BlockHeight, BranchId, Parameters},
note_encryption::Memo,
transaction::{components::Amount, Transaction},
zip32::ExtendedFullViewingKey,
};
#[cfg(feature = "mainnet")]
use zcash_primitives::consensus::MainNetwork as Network;
use zcash_primitives::consensus::{MainNetwork, MAIN_NETWORK};
#[cfg(not(feature = "mainnet"))]
use zcash_primitives::consensus::TestNetwork as Network;
use zcash_primitives::consensus::{TestNetwork, TEST_NETWORK};
use zcash_proofs::prover::LocalTxProver;
#[cfg(feature = "mainnet")]
use zcash_primitives::constants::mainnet::{
COIN_TYPE, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_EXTENDED_SPENDING_KEY,
HRP_SAPLING_PAYMENT_ADDRESS,
};
#[cfg(not(feature = "mainnet"))]
use zcash_primitives::constants::testnet::{
COIN_TYPE, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_EXTENDED_SPENDING_KEY,
HRP_SAPLING_PAYMENT_ADDRESS,
};
use std::convert::TryFrom;
// /////////////////////////////////////////////////////////////////////////////////////////////////
@ -64,12 +51,10 @@ use sha2::{Digest, Sha256};
// use zcash_primitives::legacy::TransparentAddress;
use hdwallet::{ExtendedPrivKey, KeyIndex};
use secp256k1::{PublicKey, Secp256k1};
use zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX;
// use crate::extended_key::{key_index::KeyIndex, ExtendedPrivKey, ExtendedPubKey, KeySeed};
// /////////////////////////////////////////////////////////////////////////////////////////////////
fn unwrap_exc_or<T>(exc: Result<T, ()>, def: T) -> T {
match exc {
Ok(value) => value,
@ -87,6 +72,44 @@ where
}
}
#[cfg(feature = "mainnet")]
pub const NETWORK: MainNetwork = MAIN_NETWORK;
#[cfg(not(feature = "mainnet"))]
pub const NETWORK: TestNetwork = TEST_NETWORK;
#[cfg(feature = "mainnet")]
fn wallet_db(
db_data: *const u8,
db_data_len: usize,
) -> Result<WalletDB<MainNetwork>, failure::Error> {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
WalletDB::for_path(db_data, NETWORK)
.map_err(|e| format_err!("Error opening wallet database connection: {}", e))
}
#[cfg(not(feature = "mainnet"))]
fn wallet_db(
db_data: *const u8,
db_data_len: usize,
) -> Result<WalletDB<TestNetwork>, failure::Error> {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
WalletDB::for_path(db_data, NETWORK)
.map_err(|e| format_err!("Error opening wallet database connection: {}", e))
}
fn block_db(cache_db: *const u8, cache_db_len: usize) -> Result<BlockDB, failure::Error> {
let cache_db = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(cache_db, cache_db_len)
}));
BlockDB::for_path(cache_db)
.map_err(|e| format_err!("Error opening block source database connection: {}", e))
}
/// Returns the length of the last error message to be logged.
#[no_mangle]
pub extern "C" fn zcashlc_last_error_length() -> i32 {
@ -113,8 +136,9 @@ pub extern "C" fn zcashlc_init_data_database(db_data: *const u8, db_data_len: us
slice::from_raw_parts(db_data, db_data_len)
}));
init_data_database(&db_data)
.map(|()| 1)
WalletDB::for_path(db_data, NETWORK)
.map(|db| init_wallet_db(&db))
.map(|_| 1)
.map_err(|e| format_err!("Error while initializing data DB: {}", e))
});
unwrap_exc_or_null(res)
@ -136,9 +160,7 @@ pub extern "C" fn zcashlc_init_accounts_table(
capacity_ret: *mut usize,
) -> *mut *mut c_char {
let res = catch_panic(|| {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let db_data = wallet_db(db_data, db_data_len)?;
let seed = unsafe { slice::from_raw_parts(seed, seed_len) };
let accounts = if accounts >= 0 {
accounts as u32
@ -147,34 +169,30 @@ pub extern "C" fn zcashlc_init_accounts_table(
};
let extsks: Vec<_> = (0..accounts)
.map(|account| spending_key(&seed, COIN_TYPE, account))
.map(|account| spending_key(&seed, NETWORK.coin_type(), account))
.collect();
let extfvks: Vec<_> = extsks.iter().map(ExtendedFullViewingKey::from).collect();
match init_accounts_table(&db_data, &Network, &extfvks) {
Ok(()) => (),
Err(e) => match e.kind() {
ErrorKind::TableNotEmpty => {
// Ignore this error.
}
_ => return Err(format_err!("Error while initializing accounts: {}", e)),
},
}
// Return the ExtendedSpendingKeys for the created accounts.
let mut v: Vec<_> = extsks
.iter()
.map(|extsk| {
let encoded =
encode_extended_spending_key(HRP_SAPLING_EXTENDED_SPENDING_KEY, extsk);
CString::new(encoded).unwrap().into_raw()
init_accounts_table(&db_data, &extfvks)
.map(|_| {
// Return the ExtendedSpendingKeys for the created accounts.
let mut v: Vec<_> = extsks
.iter()
.map(|extsk| {
let encoded = encode_extended_spending_key(
NETWORK.hrp_sapling_extended_spending_key(),
extsk,
);
CString::new(encoded).unwrap().into_raw()
})
.collect();
assert!(v.len() == accounts as usize);
unsafe { *capacity_ret.as_mut().unwrap() = v.capacity() };
let p = v.as_mut_ptr();
std::mem::forget(v);
return p;
})
.collect();
assert!(v.len() == accounts as usize);
unsafe { *capacity_ret.as_mut().unwrap() = v.capacity() };
let p = v.as_mut_ptr();
std::mem::forget(v);
Ok(p)
.map_err(|e| format_err!("Error while initializing accounts: {}", e))
});
unwrap_exc_or_null(res)
}
@ -189,30 +207,27 @@ pub extern "C" fn zcashlc_init_accounts_table_with_keys(
extfvks_len: usize,
) -> bool {
let res = catch_panic(|| {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let db_data = wallet_db(db_data, db_data_len)?;
let extfvks = unsafe { std::slice::from_raw_parts(extfvks, extfvks_len)
.into_iter()
.map(|s| CStr::from_ptr(*s).to_str().unwrap())
.map( |vkstr|
decode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &vkstr)
.unwrap()
.unwrap()
).collect::<Vec<_>>() };
match init_accounts_table(&db_data, &Network, &extfvks) {
let extfvks = unsafe {
std::slice::from_raw_parts(extfvks, extfvks_len)
.into_iter()
.map(|s| CStr::from_ptr(*s).to_str().unwrap())
.map(|vkstr| {
decode_extended_full_viewing_key(
NETWORK.hrp_sapling_extended_full_viewing_key(),
&vkstr,
)
.unwrap()
.unwrap()
})
.collect::<Vec<_>>()
};
match init_accounts_table(&db_data, &extfvks) {
Ok(()) => Ok(true),
Err(e) => match e.kind() {
ErrorKind::TableNotEmpty => {
// Ignore this error.
Ok(true)
}
_ => return Err(format_err!("Error while initializing accounts: {}", e)),
},
Err(e) => Err(format_err!("Error while initializing accounts: {}", e)),
}
});
unwrap_exc_or(res, false)
}
@ -238,15 +253,17 @@ pub unsafe extern "C" fn zcashlc_derive_extended_spending_keys(
};
let extsks: Vec<_> = (0..accounts)
.map(|account| spending_key(&seed, COIN_TYPE, account))
.map(|account| spending_key(&seed, NETWORK.coin_type(), account))
.collect();
// Return the ExtendedSpendingKeys for the created accounts.
let mut v: Vec<_> = extsks
.iter()
.map(|extsk| {
let encoded =
encode_extended_spending_key(HRP_SAPLING_EXTENDED_SPENDING_KEY, extsk);
let encoded = encode_extended_spending_key(
NETWORK.hrp_sapling_extended_spending_key(),
extsk,
);
CString::new(encoded).unwrap().into_raw()
})
.collect();
@ -279,15 +296,19 @@ pub unsafe extern "C" fn zcashlc_derive_extended_full_viewing_keys(
};
let extsks: Vec<_> = (0..accounts)
.map(|account| ExtendedFullViewingKey::from(&spending_key(&seed, COIN_TYPE, account)))
.map(|account| {
ExtendedFullViewingKey::from(&spending_key(&seed, NETWORK.coin_type(), account))
})
.collect();
// Return the ExtendedSpendingKeys for the created accounts.
let mut v: Vec<_> = extsks
.iter()
.map(|extsk| {
let encoded =
encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, extsk);
let encoded = encode_extended_full_viewing_key(
NETWORK.hrp_sapling_extended_full_viewing_key(),
extsk,
);
CString::new(encoded).unwrap().into_raw()
})
.collect();
@ -299,7 +320,7 @@ pub unsafe extern "C" fn zcashlc_derive_extended_full_viewing_keys(
});
unwrap_exc_or_null(res)
}
/// derives a shielded address from the given seed.
/// derives a shielded address from the given seed.
/// call zcashlc_string_free with the returned pointer when done using it
#[no_mangle]
pub unsafe extern "C" fn zcashlc_derive_shielded_address_from_seed(
@ -314,26 +335,25 @@ pub unsafe extern "C" fn zcashlc_derive_shielded_address_from_seed(
} else {
return Err(format_err!("accounts argument must be greater than zero"));
};
let address = spending_key(&seed, COIN_TYPE, account_index)
let address = spending_key(&seed, NETWORK.coin_type(), account_index)
.default_address()
.unwrap()
.1;
let address_str = encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &address);
let address_str = encode_payment_address(NETWORK.hrp_sapling_payment_address(), &address);
Ok(CString::new(address_str).unwrap().into_raw())
});
unwrap_exc_or_null(res)
}
/// derives a shielded address from the given viewing key.
/// derives a shielded address from the given viewing key.
/// call zcashlc_string_free with the returned pointer when done using it
#[no_mangle]
pub unsafe extern "C" fn zcashlc_derive_shielded_address_from_viewing_key(
extfvk: *const c_char,
) -> *mut c_char {
let res = catch_panic(|| {
let extfvk_string = CStr::from_ptr(extfvk).to_str()?;
let extfvk = match decode_extended_full_viewing_key(
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
NETWORK.hrp_sapling_extended_full_viewing_key(),
&extfvk_string,
) {
Ok(Some(extfvk)) => extfvk,
@ -348,13 +368,13 @@ pub unsafe extern "C" fn zcashlc_derive_shielded_address_from_viewing_key(
}
};
let address = extfvk.default_address().unwrap().1;
let address_str = encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &address);
let address_str = encode_payment_address(NETWORK.hrp_sapling_payment_address(), &address);
Ok(CString::new(address_str).unwrap().into_raw())
});
unwrap_exc_or_null(res)
}
/// derives a shielded address from the given extended full viewing key.
/// derives a shielded address from the given extended full viewing key.
/// call zcashlc_string_free with the returned pointer when done using it
#[no_mangle]
pub unsafe extern "C" fn zcashlc_derive_extended_full_viewing_key(
@ -362,7 +382,10 @@ pub unsafe extern "C" fn zcashlc_derive_extended_full_viewing_key(
) -> *mut c_char {
let res = catch_panic(|| {
let extsk = CStr::from_ptr(extsk).to_str()?;
let extfvk = match decode_extended_spending_key(HRP_SAPLING_EXTENDED_SPENDING_KEY, &extsk) {
let extfvk = match decode_extended_spending_key(
NETWORK.hrp_sapling_extended_spending_key(),
&extsk,
) {
Ok(Some(extsk)) => ExtendedFullViewingKey::from(&extsk),
Ok(None) => {
return Err(format_err!("Deriving viewing key from spending key returned no results. Encoding was valid but type was incorrect."));
@ -375,8 +398,10 @@ pub unsafe extern "C" fn zcashlc_derive_extended_full_viewing_key(
}
};
let encoded =
encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk);
let encoded = encode_extended_full_viewing_key(
NETWORK.hrp_sapling_extended_full_viewing_key(),
&extfvk,
);
Ok(CString::new(encoded).unwrap().into_raw())
});
@ -397,9 +422,7 @@ pub extern "C" fn zcashlc_init_blocks_table(
sapling_tree_hex: *const c_char,
) -> i32 {
let res = catch_panic(|| {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let db_data = wallet_db(db_data, db_data_len)?;
let hash = {
let mut hash = hex::decode(unsafe { CStr::from_ptr(hash_hex) }.to_str()?).unwrap();
hash.reverse();
@ -408,7 +431,13 @@ pub extern "C" fn zcashlc_init_blocks_table(
let sapling_tree =
hex::decode(unsafe { CStr::from_ptr(sapling_tree_hex) }.to_str()?).unwrap();
match init_blocks_table(&db_data, height, hash, time, &sapling_tree) {
match init_blocks_table(
&db_data,
BlockHeight::from_u32(height as u32),
hash,
time,
&sapling_tree,
) {
Ok(()) => Ok(1),
Err(e) => Err(format_err!("Error while initializing blocks table: {}", e)),
}
@ -426,20 +455,25 @@ pub extern "C" fn zcashlc_get_address(
account: i32,
) -> *mut c_char {
let res = catch_panic(|| {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let db_data = wallet_db(db_data, db_data_len)?;
let account = if account >= 0 {
account as u32
} else {
return Err(format_err!("accounts argument must be positive"));
};
match get_address(&db_data, account) {
Ok(addr) => {
let c_str_addr = CString::new(addr).unwrap();
let account = AccountId(account);
match (&db_data).get_address(account) {
Ok(Some(addr)) => {
let addr_str = encode_payment_address(NETWORK.hrp_sapling_payment_address(), &addr);
let c_str_addr = CString::new(addr_str).unwrap();
Ok(c_str_addr.into_raw())
}
Ok(None) => Err(format_err!(
"No payment address was available for account {:?}",
account
)),
Err(e) => Err(format_err!("Error while fetching address: {}", e)),
}
});
@ -454,7 +488,7 @@ pub unsafe extern "C" fn zcashlc_is_valid_shielded_address(address: *const c_cha
let res = catch_panic(|| {
let addr = CStr::from_ptr(address).to_str()?;
match RecipientAddress::decode(&Network, &addr) {
match RecipientAddress::decode(&NETWORK, &addr) {
Some(addr) => match addr {
RecipientAddress::Shielded(_) => Ok(true),
RecipientAddress::Transparent(_) => Ok(false),
@ -472,7 +506,7 @@ pub unsafe extern "C" fn zcashlc_is_valid_transparent_address(address: *const c_
let res = catch_panic(|| {
let addr = CStr::from_ptr(address).to_str()?;
match RecipientAddress::decode(&Network, &addr) {
match RecipientAddress::decode(&NETWORK, &addr) {
Some(addr) => match addr {
RecipientAddress::Shielded(_) => Ok(false),
RecipientAddress::Transparent(_) => Ok(true),
@ -482,13 +516,13 @@ pub unsafe extern "C" fn zcashlc_is_valid_transparent_address(address: *const c_
});
unwrap_exc_or(res, false)
}
/// returns whether the given viewing key is valid or not
#[no_mangle]
pub unsafe extern "C" fn zcashlc_is_valid_viewing_key(key: *const c_char) -> bool {
let res = catch_panic(|| {
let vkstr = CStr::from_ptr(key).to_str()?;
match decode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &vkstr) {
match decode_extended_full_viewing_key(&NETWORK.hrp_sapling_extended_full_viewing_key(), &vkstr) {
Ok(s) => match s {
None => Ok(false),
_ => Ok(true),
@ -498,24 +532,32 @@ pub unsafe extern "C" fn zcashlc_is_valid_viewing_key(key: *const c_char) -> boo
});
unwrap_exc_or(res, false)
}
/// Returns the balance for the account, including all unspent notes that we know about.
#[no_mangle]
pub extern "C" fn zcashlc_get_balance(db_data: *const u8, db_data_len: usize, account: i32) -> i64 {
let res = catch_panic(|| {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let account = if account >= 0 {
account as u32
} else {
return Err(format_err!("account argument must be positive"));
};
let db_data = wallet_db(db_data, db_data_len)?;
match get_balance(&db_data, account) {
Ok(balance) => Ok(balance.into()),
Err(e) => Err(format_err!("Error while fetching balance: {}", e)),
if account >= 0 {
let (_, max_height) = (&db_data)
.block_height_extrema()
.map_err(|e| format_err!("Error while fetching max block height: {}", e))
.and_then(|opt| {
opt.ok_or(format_err!(
"No blockchain information available; scan required."
))
})?;
(&db_data)
.get_balance_at(AccountId(account as u32), max_height)
.map(|b| b.into())
.map_err(|e| format_err!("Error while fetching balance: {}", e))
} else {
Err(format_err!("account argument must be positive"))
}
});
unwrap_exc_or(res, -1)
}
@ -528,20 +570,27 @@ pub extern "C" fn zcashlc_get_verified_balance(
account: i32,
) -> i64 {
let res = catch_panic(|| {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let account = if account >= 0 {
account as u32
let db_data = wallet_db(db_data, db_data_len)?;
if account >= 0 {
(&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(|(_, a)| a)
.ok_or(format_err!("Anchor height not available; scan required."))
})
.and_then(|anchor| {
(&db_data)
.get_balance_at(AccountId(account as u32), anchor)
.map_err(|e| format_err!("Error while fetching verified balance: {}", e))
})
.map(|amount| amount.into())
} else {
return Err(format_err!("account argument must be positive"));
};
match get_verified_balance(&db_data, account) {
Ok(balance) => Ok(balance.into()),
Err(e) => Err(format_err!("Error while fetching verified balance: {}", e)),
Err(format_err!("account argument must be positive"))
}
});
unwrap_exc_or(res, -1)
}
@ -558,11 +607,9 @@ pub extern "C" fn zcashlc_get_received_memo_as_utf8(
id_note: i64,
) -> *mut c_char {
let res = catch_panic(|| {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let db_data = wallet_db(db_data, db_data_len)?;
let memo = match get_received_memo_as_utf8(db_data, id_note) {
let memo = match (&db_data).get_memo_as_utf8(NoteId::ReceivedNoteId(id_note)) {
Ok(memo) => memo.unwrap_or_default(),
Err(e) => return Err(format_err!("Error while fetching memo: {}", e)),
};
@ -585,14 +632,12 @@ pub extern "C" fn zcashlc_get_sent_memo_as_utf8(
id_note: i64,
) -> *mut c_char {
let res = catch_panic(|| {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let db_data = wallet_db(db_data, db_data_len)?;
let memo = match get_sent_memo_as_utf8(db_data, id_note) {
Ok(memo) => memo.unwrap_or_default(),
Err(e) => return Err(format_err!("Error while fetching memo: {}", e)),
};
let memo = (&db_data)
.get_memo_as_utf8(NoteId::SentNoteId(id_note))
.map(|memo| memo.unwrap_or_default())
.map_err(|e| format_err!("Error while fetching memo: {}", e))?;
Ok(CString::new(memo).unwrap().into_raw())
});
@ -623,16 +668,21 @@ pub extern "C" fn zcashlc_validate_combined_chain(
db_data_len: usize,
) -> i32 {
let res = catch_panic(|| {
let db_cache = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_cache, db_cache_len)
}));
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
if let Err(e) = validate_combined_chain(Network, &db_cache, &db_data) {
match e.kind() {
ErrorKind::InvalidChain(upper_bound, _) => Ok(u32::from(*upper_bound) as i32),
let block_db = block_db(db_cache, db_cache_len)?;
let db_data = wallet_db(db_data, db_data_len)?;
let validate_from = (&db_data)
.get_max_height_hash()
.map_err(|e| format_err!("Error while validating chain: {}", e))?;
let val_res = validate_chain(&NETWORK, &block_db, validate_from);
if let Err(e) = val_res {
match e {
SqliteClientError::BackendError(Error::InvalidChain(upper_bound, _)) => {
let upper_bound_u32 = u32::from(upper_bound);
Ok(upper_bound_u32 as i32)
}
_ => Err(format_err!("Error while validating chain: {}", e)),
}
} else {
@ -654,18 +704,17 @@ pub extern "C" fn zcashlc_rewind_to_height(
height: i32,
) -> i32 {
let res = catch_panic(|| {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let db_data = wallet_db(db_data, db_data_len)?;
match rewind_to_height(Network,&db_data, BlockHeight::from(height as u32)) {
Ok(()) => Ok(1),
Err(e) => Err(format_err!(
"Error while rewinding data DB to height {}: {}",
height,
e
)),
}
let mut update_ops = (&db_data)
.get_update_ops()
.map_err(|e| format_err!("Could not obtain a writable database connection: {}", e))?;
let height = BlockHeight::try_from(height)?;
(&mut update_ops)
.transactionally(|ops| ops.rewind_to_height(height))
.map(|_| 1)
.map_err(|e| format_err!("Error while rewinding data DB to height {}: {}", height, e))
});
unwrap_exc_or_null(res)
}
@ -692,14 +741,11 @@ pub extern "C" fn zcashlc_scan_blocks(
db_data_len: usize,
) -> i32 {
let res = catch_panic(|| {
let db_cache = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_cache, db_cache_len)
}));
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let block_db = block_db(db_cache, db_cache_len)?;
let db_read = wallet_db(db_data, db_data_len)?;
let mut db_data = db_read.get_update_ops()?;
match scan_cached_blocks(&Network, &db_cache, &db_data, None) {
match scan_cached_blocks(&NETWORK, &block_db, &mut db_data, None) {
Ok(()) => Ok(1),
Err(e) => Err(format_err!("Error while scanning blocks: {}", e)),
}
@ -715,13 +761,12 @@ pub extern "C" fn zcashlc_decrypt_and_store_transaction(
tx_len: usize,
) -> i32 {
let res = catch_panic(|| {
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let db_read = wallet_db(db_data, db_data_len)?;
let mut db_data = db_read.get_update_ops()?;
let tx_bytes = unsafe { slice::from_raw_parts(tx, tx_len) };
let tx = Transaction::read(&tx_bytes[..])?;
match decrypt_and_store_transaction(&db_data, &Network, &tx) {
match decrypt_and_store_transaction(&NETWORK, &mut db_data, &tx) {
Ok(()) => Ok(1),
Err(e) => Err(format_err!("Error while decrypting transaction: {}", e)),
}
@ -743,7 +788,6 @@ pub extern "C" fn zcashlc_create_to_address(
db_data_len: usize,
account: i32,
extsk: *const c_char,
consensus_branch_id: i32,
to: *const c_char,
value: i64,
memo: *const c_char,
@ -753,16 +797,8 @@ pub extern "C" fn zcashlc_create_to_address(
output_params_len: usize,
) -> i64 {
let res = catch_panic(|| {
let branch_id = match BranchId::try_from(consensus_branch_id as u32) {
Ok(extsk) => extsk,
Err(e) => {
return Err(format_err!("Invalid consensus branch id: {}", e));
}
};
let db_data = Path::new(OsStr::from_bytes(unsafe {
slice::from_raw_parts(db_data, db_data_len)
}));
let db_read = wallet_db(db_data, db_data_len)?;
let mut db_data = db_read.get_update_ops()?;
let account = if account >= 0 {
account as u32
} else {
@ -783,17 +819,19 @@ pub extern "C" fn zcashlc_create_to_address(
slice::from_raw_parts(output_params, output_params_len)
}));
let extsk = match decode_extended_spending_key(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 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 to = match RecipientAddress::decode(&Network, &to) {
let to = match RecipientAddress::decode(&NETWORK, &to) {
Some(to) => to,
None => {
return Err(format_err!("PaymentAddress is for the wrong network"));
@ -804,12 +842,12 @@ pub extern "C" fn zcashlc_create_to_address(
let prover = LocalTxProver::new(spend_params, output_params);
create_to_address(
&db_data,
&Network,
branch_id,
create_spend_to_address(
&mut db_data,
&NETWORK,
prover,
(account, &extsk),
AccountId(account),
&extsk,
&to,
value,
Some(memo),
@ -823,7 +861,7 @@ pub extern "C" fn zcashlc_create_to_address(
#[no_mangle]
pub extern "C" fn zcashlc_branch_id_for_height(height: i32) -> i32 {
let res = catch_panic(|| {
let branch: BranchId = BranchId::for_height(&Network, BlockHeight::from(height as u32));
let branch: BranchId = BranchId::for_height(&NETWORK, BlockHeight::from(height as u32));
let branch_id: u32 = u32::from(branch);
Ok(branch_id as i32)
});
@ -854,25 +892,25 @@ pub extern "C" fn zcashlc_vec_string_free(v: *mut *mut c_char, len: usize, capac
};
}
/// TEST TEST 123 TEST
/// Derives a transparent address from the given seed
/// TEST TEST 123 TEST
/// Derives a transparent address from the given seed
#[no_mangle]
pub unsafe extern "C" fn zcashlc_derive_transparent_address_from_seed(
seed: *const u8,
seed_len: usize,
) -> *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(COIN_TYPE).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()
@ -887,7 +925,7 @@ pub unsafe extern "C" fn zcashlc_derive_transparent_address_from_seed(
hash160.update(Sha256::digest(&pk.serialize()[..].to_vec()));
let address_string = hash160
.finalize()
.to_base58check(&B58_PUBKEY_ADDRESS_PREFIX, &[]);
.to_base58check(&NETWORK.b58_pubkey_address_prefix(), &[]);
Ok(CString::new(address_string).unwrap().into_raw())
});
@ -922,4 +960,4 @@ pub fn double_sha256(payload: &[u8]) -> Vec<u8> {
let h1 = Sha256::digest(&payload);
let h2 = Sha256::digest(&h1);
h2.to_vec()
}
}