Feature/reorg testing (#104)

* Add DarksideWalletD class

* darkside walletd reorg tests

* Removed hardcoded stuff from tests

* add parameters to support other kinds of reorgs

* Basic reorg test in place

* Integration to DarksideWalletD and tests

* tests improvements

* Implement reorg testing. fix error throwing
This commit is contained in:
Francisco Gindre 2020-04-06 12:54:31 -03:00 committed by GitHub
parent 51419010ea
commit 312a169911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1248 additions and 102 deletions

View File

@ -30,10 +30,10 @@ PODS:
- gRPC-Core (~> 1.23.0)
- SwiftProtobuf (~> 1.7.0)
- SwiftProtobuf (1.7.0)
- ZcashLightClientKit (0.2.1):
- ZcashLightClientKit (0.3.1):
- SQLite.swift (~> 0.12.2)
- SwiftGRPC (~> 0.10.0)
- ZcashLightClientKit/Tests (0.2.1):
- ZcashLightClientKit/Tests (0.3.1):
- SQLite.swift (~> 0.12.2)
- SwiftGRPC (~> 0.10.0)
@ -72,7 +72,7 @@ SPEC CHECKSUMS:
SQLite.swift: d2b4642190917051ce6bd1d49aab565fe794eea3
SwiftGRPC: f8fcfecb547c96cc6913de619f95fa3cd09838ee
SwiftProtobuf: 4fd9645e69b72cbae6ec8da5be0cdd20ca6565dd
ZcashLightClientKit: 8f195725d2ec3d579c78b228a0d3a384e21f56b9
ZcashLightClientKit: 24b01a37977bddfaadc7746acef40ad209bae84a
PODFILE CHECKSUM: 5a1fb98512fa179a4e83d67d14dd402f6d129a4d

View File

@ -204,7 +204,7 @@ public class CompactBlockProcessor {
}
var shouldStart: Bool {
switch self.state {
case .stopped, .synced, .error(_):
case .stopped, .synced, .error:
return maxAttemptsReached
default:
return false
@ -276,7 +276,6 @@ public class CompactBlockProcessor {
}
/**
Stops the CompactBlockProcessor
@ -408,7 +407,6 @@ public class CompactBlockProcessor {
self?.state = .scanning
}
scanBlocksOperation.completionHandler = { [weak self] (finished, cancelled) in
guard !cancelled else {
LoggerProxy.debug("Warning: scanBlocksOperation operation cancelled")
@ -571,7 +569,7 @@ public class CompactBlockProcessor {
self.retryAttempts = self.retryAttempts + 1
self.processingError = error
switch self.state {
case .error(_):
case .error:
notifyError(error)
default:
break

View File

@ -15,6 +15,8 @@ class ZcashRustBackend: ZcashRustBackendWelding {
zcashlc_clear_last_error()
if message.contains("couldn't load Sapling spend parameters") {
return RustWeldingError.saplingSpendParametersNotFound
} else if message.contains("is not empty") {
return RustWeldingError.dataDbNotEmpty
}
return RustWeldingError.genericError(message: message)
}
@ -83,7 +85,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
let dbData = dbData.osStr()
guard zcashlc_init_blocks_table(dbData.0, dbData.1, height, [CChar](hash.utf8CString), time, [CChar](saplingTree.utf8CString)) != 0 else {
if let error = lastError() {
throw throwDataDbError(error)
throw error
}
throw RustWeldingError.dataDbInitFailed(message: "Unknown Error")
}
@ -167,7 +169,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return nil
}
let derived = String(validatingUTF8: extsk)
let derived = String(validatingUTF8: extsk)
zcashlc_string_free(extsk)
return derived

View File

@ -21,11 +21,11 @@ public struct ZcashRustBackendWeldingConstants {
public protocol ZcashRustBackendWelding {
/**
gets the latest error if available
gets the latest error if available. Clear the existing error
*/
static func lastError() -> RustWeldingError?
/**
gets the latest error message from librustzcash
gets the latest error message from librustzcash. Does not clear existing error
*/
static func getLastError() -> String?
/**

View File

@ -3,16 +3,20 @@
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: compact_formats.proto
//
// For information on using the generated types, please see the documentation:
// For information on using the generated types, please see the documenation:
// https://github.com/apple/swift-protobuf/
// Copyright (c) 2019-2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// Please ensure that your are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
@ -51,14 +55,14 @@ struct CompactBlock {
init() {}
}
/// Index and hash will allow the receiver to call out to chain
/// explorers or other data structures to retrieve more information
/// about this transaction.
struct CompactTx {
// 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.
/// Index and hash will allow the receiver to call out to chain
/// explorers or other data structures to retrieve more information
/// about this transaction.
var index: UInt64 = 0
var hash: Data = SwiftProtobuf.Internal.emptyData

View File

@ -1,3 +1,7 @@
// Copyright (c) 2019-2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
syntax = "proto3";
package cash.z.wallet.sdk.rpc;
option go_package = "walletrpc";
@ -19,10 +23,10 @@ message CompactBlock {
repeated CompactTx vtx = 7; // compact transactions from this block
}
// Index and hash will allow the receiver to call out to chain
// explorers or other data structures to retrieve more information
// about this transaction.
message CompactTx {
// Index and hash will allow the receiver to call out to chain
// explorers or other data structures to retrieve more information
// about this transaction.
uint64 index = 1;
bytes hash = 2;

View File

@ -1,3 +1,7 @@
// Copyright (c) 2019-2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
syntax = "proto3";
package cash.z.wallet.sdk.rpc;
option go_package = "walletrpc";
@ -5,15 +9,14 @@ option swift_prefix = "";
import "compact_formats.proto";
// A BlockID message contains identifiers to select a block: a height or a
// hash. If the hash is present it takes precedence.
// hash. Specification by hash is not implemented, but may be in the future.
message BlockID {
uint64 height = 1;
bytes hash = 2;
}
// BlockRange technically allows ranging from hash to hash etc but this is not
// currently intended for support, though there is no reason you couldn't do
// it. Further permutations are left as an exercise.
// BlockRange specifies a series of blocks from start to end inclusive.
// Both BlockIDs must be heights; specification by hash is not yet supported.
message BlockRange {
BlockID start = 1;
BlockID end = 2;
@ -21,29 +24,81 @@ message BlockRange {
// A TxFilter contains the information needed to identify a particular
// transaction: either a block and an index, or a direct transaction hash.
// Currently, only specification by hash is supported.
message TxFilter {
BlockID block = 1;
uint64 index = 2;
bytes hash = 3;
}
// RawTransaction contains the complete transaction data.
// RawTransaction contains the complete transaction data. It also optionally includes
// the block height in which the transaction was included
message RawTransaction {
bytes data = 1;
uint64 height = 2;
}
// A SendResponse encodes an error code and a string. It is currently used
// only by SendTransaction(). If error code is zero, the operation was
// successful; if non-zero, it and the message specify the failure.
message SendResponse {
int32 errorCode = 1;
string errorMessage = 2;
}
// Empty placeholder. Someday we may want to specify e.g. a particular chain fork.
// Chainspec is a placeholder to allow specification of a particular chain fork.
message ChainSpec {}
// Empty is for gRPCs that take no arguments, currently only GetLightdInfo.
message Empty {}
// LightdInfo returns various information about this lightwalletd instance
// and the state of the blockchain.
message LightdInfo {
string version = 1;
string vendor = 2;
bool taddrSupport = 3;
string chainName = 4;
uint64 saplingActivationHeight = 5;
string consensusBranchId = 6;
uint64 blockHeight = 7;
}
// TransparentAddressBlockFilter restricts the results to the given address
// or block range.
message TransparentAddressBlockFilter {
string address = 1;
BlockRange range = 2;
}
// Duration is currently used only for testing, so that the Ping rpc
// can simulate a delay, to create many simultaneous connections. Units
// are microseconds.
message Duration {
int64 intervalUs = 1;
}
// PingResponse is used to indicate concurrency, how many Ping rpcs
// are executing upon entry and upon exit (after the delay).
message PingResponse {
int64 entry = 1;
int64 exit = 2;
}
service CompactTxStreamer {
// Compact Blocks
rpc GetLatestBlock(ChainSpec) returns (BlockID) {}
rpc GetBlock(BlockID) returns (CompactBlock) {}
rpc GetBlockRange(BlockRange) returns (stream CompactBlock) {}
// Transactions
rpc GetTransaction(TxFilter) returns (RawTransaction) {}
rpc SendTransaction(RawTransaction) returns (SendResponse) {}
// t-Address support
rpc GetAddressTxids(TransparentAddressBlockFilter) returns (stream RawTransaction) {}
// Misc
rpc GetLightdInfo(Empty) returns (LightdInfo) {}
rpc Ping(Duration) returns (PingResponse) {}
}

View File

@ -65,6 +65,34 @@ fileprivate final class CompactTxStreamerSendTransactionCallBase: ClientCallUnar
override class var method: String { return "/cash.z.wallet.sdk.rpc.CompactTxStreamer/SendTransaction" }
}
internal protocol CompactTxStreamerGetAddressTxidsCall: ClientCallServerStreaming {
/// Do not call this directly, call `receive()` in the protocol extension below instead.
func _receive(timeout: DispatchTime) throws -> RawTransaction?
/// Call this to wait for a result. Nonblocking.
func receive(completion: @escaping (ResultOrRPCError<RawTransaction?>) -> Void) throws
}
internal extension CompactTxStreamerGetAddressTxidsCall {
/// Call this to wait for a result. Blocking.
func receive(timeout: DispatchTime = .distantFuture) throws -> RawTransaction? { return try self._receive(timeout: timeout) }
}
fileprivate final class CompactTxStreamerGetAddressTxidsCallBase: ClientCallServerStreamingBase<TransparentAddressBlockFilter, RawTransaction>, CompactTxStreamerGetAddressTxidsCall {
override class var method: String { return "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressTxids" }
}
internal protocol CompactTxStreamerGetLightdInfoCall: ClientCallUnary {}
fileprivate final class CompactTxStreamerGetLightdInfoCallBase: ClientCallUnaryBase<Empty, LightdInfo>, CompactTxStreamerGetLightdInfoCall {
override class var method: String { return "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetLightdInfo" }
}
internal protocol CompactTxStreamerPingCall: ClientCallUnary {}
fileprivate final class CompactTxStreamerPingCallBase: ClientCallUnaryBase<Duration, PingResponse>, CompactTxStreamerPingCall {
override class var method: String { return "/cash.z.wallet.sdk.rpc.CompactTxStreamer/Ping" }
}
/// Instantiate CompactTxStreamerServiceClient, then call methods of this protocol to make API calls.
internal protocol CompactTxStreamerService: ServiceClient {
@ -97,6 +125,23 @@ internal protocol CompactTxStreamerService: ServiceClient {
@discardableResult
func sendTransaction(_ request: RawTransaction, metadata customMetadata: Metadata, completion: @escaping (SendResponse?, CallResult) -> Void) throws -> CompactTxStreamerSendTransactionCall
/// Asynchronous. Server-streaming.
/// Send the initial message.
/// Use methods on the returned object to get streamed responses.
func getAddressTxids(_ request: TransparentAddressBlockFilter, metadata customMetadata: Metadata, completion: ((CallResult) -> Void)?) throws -> CompactTxStreamerGetAddressTxidsCall
/// Synchronous. Unary.
func getLightdInfo(_ request: Empty, metadata customMetadata: Metadata) throws -> LightdInfo
/// Asynchronous. Unary.
@discardableResult
func getLightdInfo(_ request: Empty, metadata customMetadata: Metadata, completion: @escaping (LightdInfo?, CallResult) -> Void) throws -> CompactTxStreamerGetLightdInfoCall
/// Synchronous. Unary.
func ping(_ request: Duration, metadata customMetadata: Metadata) throws -> PingResponse
/// Asynchronous. Unary.
@discardableResult
func ping(_ request: Duration, metadata customMetadata: Metadata, completion: @escaping (PingResponse?, CallResult) -> Void) throws -> CompactTxStreamerPingCall
}
internal extension CompactTxStreamerService {
@ -145,6 +190,31 @@ internal extension CompactTxStreamerService {
return try self.sendTransaction(request, metadata: self.metadata, completion: completion)
}
/// Asynchronous. Server-streaming.
func getAddressTxids(_ request: TransparentAddressBlockFilter, completion: ((CallResult) -> Void)?) throws -> CompactTxStreamerGetAddressTxidsCall {
return try self.getAddressTxids(request, metadata: self.metadata, completion: completion)
}
/// Synchronous. Unary.
func getLightdInfo(_ request: Empty) throws -> LightdInfo {
return try self.getLightdInfo(request, metadata: self.metadata)
}
/// Asynchronous. Unary.
@discardableResult
func getLightdInfo(_ request: Empty, completion: @escaping (LightdInfo?, CallResult) -> Void) throws -> CompactTxStreamerGetLightdInfoCall {
return try self.getLightdInfo(request, metadata: self.metadata, completion: completion)
}
/// Synchronous. Unary.
func ping(_ request: Duration) throws -> PingResponse {
return try self.ping(request, metadata: self.metadata)
}
/// Asynchronous. Unary.
@discardableResult
func ping(_ request: Duration, completion: @escaping (PingResponse?, CallResult) -> Void) throws -> CompactTxStreamerPingCall {
return try self.ping(request, metadata: self.metadata, completion: completion)
}
}
internal final class CompactTxStreamerServiceClient: ServiceClientBase, CompactTxStreamerService {
@ -204,5 +274,37 @@ internal final class CompactTxStreamerServiceClient: ServiceClientBase, CompactT
.start(request: request, metadata: customMetadata, completion: completion)
}
/// Asynchronous. Server-streaming.
/// Send the initial message.
/// Use methods on the returned object to get streamed responses.
internal func getAddressTxids(_ request: TransparentAddressBlockFilter, metadata customMetadata: Metadata, completion: ((CallResult) -> Void)?) throws -> CompactTxStreamerGetAddressTxidsCall {
return try CompactTxStreamerGetAddressTxidsCallBase(channel)
.start(request: request, metadata: customMetadata, completion: completion)
}
/// Synchronous. Unary.
internal func getLightdInfo(_ request: Empty, metadata customMetadata: Metadata) throws -> LightdInfo {
return try CompactTxStreamerGetLightdInfoCallBase(channel)
.run(request: request, metadata: customMetadata)
}
/// Asynchronous. Unary.
@discardableResult
internal func getLightdInfo(_ request: Empty, metadata customMetadata: Metadata, completion: @escaping (LightdInfo?, CallResult) -> Void) throws -> CompactTxStreamerGetLightdInfoCall {
return try CompactTxStreamerGetLightdInfoCallBase(channel)
.start(request: request, metadata: customMetadata, completion: completion)
}
/// Synchronous. Unary.
internal func ping(_ request: Duration, metadata customMetadata: Metadata) throws -> PingResponse {
return try CompactTxStreamerPingCallBase(channel)
.run(request: request, metadata: customMetadata)
}
/// Asynchronous. Unary.
@discardableResult
internal func ping(_ request: Duration, metadata customMetadata: Metadata, completion: @escaping (PingResponse?, CallResult) -> Void) throws -> CompactTxStreamerPingCall {
return try CompactTxStreamerPingCallBase(channel)
.start(request: request, metadata: customMetadata, completion: completion)
}
}

View File

@ -3,16 +3,20 @@
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: service.proto
//
// For information on using the generated types, please see the documentation:
// For information on using the generated types, please see the documenation:
// https://github.com/apple/swift-protobuf/
// Copyright (c) 2019-2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// Please ensure that your are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
@ -20,7 +24,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
}
/// A BlockID message contains identifiers to select a block: a height or a
/// hash. If the hash is present it takes precedence.
/// hash. Specification by hash is not implemented, but may be in the future.
struct BlockID {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -35,68 +39,74 @@ struct BlockID {
init() {}
}
/// BlockRange technically allows ranging from hash to hash etc but this is not
/// currently intended for support, though there is no reason you couldn't do
/// it. Further permutations are left as an exercise.
/// BlockRange specifies a series of blocks from start to end inclusive.
/// Both BlockIDs must be heights; specification by hash is not yet supported.
struct BlockRange {
// 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 start: BlockID {
get {return _start ?? BlockID()}
set {_start = newValue}
get {return _storage._start ?? BlockID()}
set {_uniqueStorage()._start = newValue}
}
/// Returns true if `start` has been explicitly set.
var hasStart: Bool {return self._start != nil}
var hasStart: Bool {return _storage._start != nil}
/// Clears the value of `start`. Subsequent reads from it will return its default value.
mutating func clearStart() {self._start = nil}
mutating func clearStart() {_uniqueStorage()._start = nil}
var end: BlockID {
get {return _end ?? BlockID()}
set {_end = newValue}
get {return _storage._end ?? BlockID()}
set {_uniqueStorage()._end = newValue}
}
/// Returns true if `end` has been explicitly set.
var hasEnd: Bool {return self._end != nil}
var hasEnd: Bool {return _storage._end != nil}
/// Clears the value of `end`. Subsequent reads from it will return its default value.
mutating func clearEnd() {self._end = nil}
mutating func clearEnd() {_uniqueStorage()._end = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _start: BlockID? = nil
fileprivate var _end: BlockID? = nil
fileprivate var _storage = _StorageClass.defaultInstance
}
/// A TxFilter contains the information needed to identify a particular
/// transaction: either a block and an index, or a direct transaction hash.
/// Currently, only specification by hash is supported.
struct TxFilter {
// 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 block: BlockID {
get {return _block ?? BlockID()}
set {_block = newValue}
get {return _storage._block ?? BlockID()}
set {_uniqueStorage()._block = newValue}
}
/// Returns true if `block` has been explicitly set.
var hasBlock: Bool {return self._block != nil}
var hasBlock: Bool {return _storage._block != nil}
/// Clears the value of `block`. Subsequent reads from it will return its default value.
mutating func clearBlock() {self._block = nil}
mutating func clearBlock() {_uniqueStorage()._block = nil}
var index: UInt64 = 0
var index: UInt64 {
get {return _storage._index}
set {_uniqueStorage()._index = newValue}
}
var hash: Data = SwiftProtobuf.Internal.emptyData
var hash: Data {
get {return _storage._hash}
set {_uniqueStorage()._hash = newValue}
}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _block: BlockID? = nil
fileprivate var _storage = _StorageClass.defaultInstance
}
/// RawTransaction contains the complete transaction data.
/// RawTransaction contains the complete transaction data. It also optionally includes
/// the block height in which the transaction was included
struct RawTransaction {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -104,11 +114,16 @@ struct RawTransaction {
var data: Data = SwiftProtobuf.Internal.emptyData
var height: UInt64 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
/// A SendResponse encodes an error code and a string. It is currently used
/// only by SendTransaction(). If error code is zero, the operation was
/// successful; if non-zero, it and the message specify the failure.
struct SendResponse {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -123,7 +138,7 @@ struct SendResponse {
init() {}
}
/// Empty placeholder. Someday we may want to specify e.g. a particular chain fork.
/// Chainspec is a placeholder to allow specification of a particular chain fork.
struct ChainSpec {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -134,6 +149,102 @@ struct ChainSpec {
init() {}
}
/// Empty is for gRPCs that take no arguments, currently only GetLightdInfo.
struct Empty {
// 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 unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
/// LightdInfo returns various information about this lightwalletd instance
/// and the state of the blockchain.
struct LightdInfo {
// 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 version: String = String()
var vendor: String = String()
var taddrSupport: Bool = false
var chainName: String = String()
var saplingActivationHeight: UInt64 = 0
var consensusBranchID: String = String()
var blockHeight: UInt64 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
/// TransparentAddressBlockFilter restricts the results to the given address
/// or block range.
struct TransparentAddressBlockFilter {
// 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 {
get {return _storage._address}
set {_uniqueStorage()._address = newValue}
}
var range: BlockRange {
get {return _storage._range ?? BlockRange()}
set {_uniqueStorage()._range = newValue}
}
/// Returns true if `range` has been explicitly set.
var hasRange: Bool {return _storage._range != nil}
/// Clears the value of `range`. Subsequent reads from it will return its default value.
mutating func clearRange() {_uniqueStorage()._range = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _storage = _StorageClass.defaultInstance
}
/// Duration is currently used only for testing, so that the Ping rpc
/// can simulate a delay, to create many simultaneous connections. Units
/// are microseconds.
struct Duration {
// 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 intervalUs: Int64 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
/// PingResponse is used to indicate concurrency, how many Ping rpcs
/// are executing upon entry and upon exit (after the delay).
struct PingResponse {
// 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 entry: Int64 = 0
var exit: Int64 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "cash.z.wallet.sdk.rpc"
@ -180,29 +291,63 @@ extension BlockRange: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
2: .same(proto: "end"),
]
fileprivate class _StorageClass {
var _start: BlockID? = nil
var _end: BlockID? = nil
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_start = source._start
_end = source._end
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularMessageField(value: &self._start)
case 2: try decoder.decodeSingularMessageField(value: &self._end)
default: break
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularMessageField(value: &_storage._start)
case 2: try decoder.decodeSingularMessageField(value: &_storage._end)
default: break
}
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if let v = self._start {
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
}
if let v = self._end {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if let v = _storage._start {
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
}
if let v = _storage._end {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: BlockRange, rhs: BlockRange) -> Bool {
if lhs._start != rhs._start {return false}
if lhs._end != rhs._end {return false}
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._start != rhs_storage._start {return false}
if _storage._end != rhs_storage._end {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
@ -216,34 +361,70 @@ extension TxFilter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
3: .same(proto: "hash"),
]
fileprivate class _StorageClass {
var _block: BlockID? = nil
var _index: UInt64 = 0
var _hash: Data = SwiftProtobuf.Internal.emptyData
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_block = source._block
_index = source._index
_hash = source._hash
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularMessageField(value: &self._block)
case 2: try decoder.decodeSingularUInt64Field(value: &self.index)
case 3: try decoder.decodeSingularBytesField(value: &self.hash)
default: break
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularMessageField(value: &_storage._block)
case 2: try decoder.decodeSingularUInt64Field(value: &_storage._index)
case 3: try decoder.decodeSingularBytesField(value: &_storage._hash)
default: break
}
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if let v = self._block {
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
}
if self.index != 0 {
try visitor.visitSingularUInt64Field(value: self.index, fieldNumber: 2)
}
if !self.hash.isEmpty {
try visitor.visitSingularBytesField(value: self.hash, fieldNumber: 3)
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if let v = _storage._block {
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
}
if _storage._index != 0 {
try visitor.visitSingularUInt64Field(value: _storage._index, fieldNumber: 2)
}
if !_storage._hash.isEmpty {
try visitor.visitSingularBytesField(value: _storage._hash, fieldNumber: 3)
}
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: TxFilter, rhs: TxFilter) -> Bool {
if lhs._block != rhs._block {return false}
if lhs.index != rhs.index {return false}
if lhs.hash != rhs.hash {return false}
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._block != rhs_storage._block {return false}
if _storage._index != rhs_storage._index {return false}
if _storage._hash != rhs_storage._hash {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
@ -253,12 +434,14 @@ extension RawTransaction: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
static let protoMessageName: String = _protobuf_package + ".RawTransaction"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "data"),
2: .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.data)
case 2: try decoder.decodeSingularUInt64Field(value: &self.height)
default: break
}
}
@ -268,11 +451,15 @@ extension RawTransaction: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
if !self.data.isEmpty {
try visitor.visitSingularBytesField(value: self.data, fieldNumber: 1)
}
if self.height != 0 {
try visitor.visitSingularUInt64Field(value: self.height, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: RawTransaction, rhs: RawTransaction) -> Bool {
if lhs.data != rhs.data {return false}
if lhs.height != rhs.height {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
@ -331,3 +518,220 @@ extension ChainSpec: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
return true
}
}
extension Empty: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".Empty"
static let _protobuf_nameMap = SwiftProtobuf._NameMap()
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let _ = try decoder.nextFieldNumber() {
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: Empty, rhs: Empty) -> Bool {
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension LightdInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".LightdInfo"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "version"),
2: .same(proto: "vendor"),
3: .same(proto: "taddrSupport"),
4: .same(proto: "chainName"),
5: .same(proto: "saplingActivationHeight"),
6: .same(proto: "consensusBranchId"),
7: .same(proto: "blockHeight"),
]
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.version)
case 2: try decoder.decodeSingularStringField(value: &self.vendor)
case 3: try decoder.decodeSingularBoolField(value: &self.taddrSupport)
case 4: try decoder.decodeSingularStringField(value: &self.chainName)
case 5: try decoder.decodeSingularUInt64Field(value: &self.saplingActivationHeight)
case 6: try decoder.decodeSingularStringField(value: &self.consensusBranchID)
case 7: try decoder.decodeSingularUInt64Field(value: &self.blockHeight)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.version.isEmpty {
try visitor.visitSingularStringField(value: self.version, fieldNumber: 1)
}
if !self.vendor.isEmpty {
try visitor.visitSingularStringField(value: self.vendor, fieldNumber: 2)
}
if self.taddrSupport != false {
try visitor.visitSingularBoolField(value: self.taddrSupport, fieldNumber: 3)
}
if !self.chainName.isEmpty {
try visitor.visitSingularStringField(value: self.chainName, fieldNumber: 4)
}
if self.saplingActivationHeight != 0 {
try visitor.visitSingularUInt64Field(value: self.saplingActivationHeight, fieldNumber: 5)
}
if !self.consensusBranchID.isEmpty {
try visitor.visitSingularStringField(value: self.consensusBranchID, fieldNumber: 6)
}
if self.blockHeight != 0 {
try visitor.visitSingularUInt64Field(value: self.blockHeight, fieldNumber: 7)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: LightdInfo, rhs: LightdInfo) -> Bool {
if lhs.version != rhs.version {return false}
if lhs.vendor != rhs.vendor {return false}
if lhs.taddrSupport != rhs.taddrSupport {return false}
if lhs.chainName != rhs.chainName {return false}
if lhs.saplingActivationHeight != rhs.saplingActivationHeight {return false}
if lhs.consensusBranchID != rhs.consensusBranchID {return false}
if lhs.blockHeight != rhs.blockHeight {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension TransparentAddressBlockFilter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".TransparentAddressBlockFilter"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "address"),
2: .same(proto: "range"),
]
fileprivate class _StorageClass {
var _address: String = String()
var _range: BlockRange? = nil
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_address = source._address
_range = source._range
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &_storage._address)
case 2: try decoder.decodeSingularMessageField(value: &_storage._range)
default: break
}
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if !_storage._address.isEmpty {
try visitor.visitSingularStringField(value: _storage._address, fieldNumber: 1)
}
if let v = _storage._range {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: TransparentAddressBlockFilter, rhs: TransparentAddressBlockFilter) -> Bool {
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._address != rhs_storage._address {return false}
if _storage._range != rhs_storage._range {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Duration: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".Duration"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "intervalUs"),
]
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.intervalUs)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.intervalUs != 0 {
try visitor.visitSingularInt64Field(value: self.intervalUs, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: Duration, rhs: Duration) -> Bool {
if lhs.intervalUs != rhs.intervalUs {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension PingResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".PingResponse"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "entry"),
2: .same(proto: "exit"),
]
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.entry)
case 2: try decoder.decodeSingularInt64Field(value: &self.exit)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.entry != 0 {
try visitor.visitSingularInt64Field(value: self.entry, fieldNumber: 1)
}
if self.exit != 0 {
try visitor.visitSingularInt64Field(value: self.exit, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: PingResponse, rhs: PingResponse) -> Bool {
if lhs.entry != rhs.entry {return false}
if lhs.exit != rhs.exit {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

View File

@ -26,13 +26,20 @@ public extension WalletBirthday {
time: 1574579149,
tree: "01999fc372390699b15f71d41745abe6a2ea0db4ffa8894d3c5fe30b9261a1a43a01585112668685bd6783cb01b72d17dc86c6d740c27cccf66b75e959e4e4f5ea3710019b7f6b4457a97eadbe1a39bfcc6ba0a56d37010d0d799e1e652fc29733103e04016a0b4d2705e1feb2021d80e5785608536dde05aea5ef676a5427244228b19e2d00010973d03ad5f79fcac64ab3ffbdaaac1a24b74a3617770bf960fb004cbd422439000001984bfce9361025cc38574f944a3ed7b074b3bf88cfce6f14c4a9be4d91d6dc730105871ec1e3737a39bceb00b0c2d253ff36f472e92c361e7ef360d49ea8dc4c4200000001c145105e1bf401668a8f23ca70c47ee92d23bd366072020c83d26b855eeafd6d0001fa6980c053d84f809b6abcf35690f03a11f87b28e3240828e32e3f57af41e54e01319312241b0031e3a255b0d708750b4cb3f3fe79e3503fe488cc8db1dd00753801754bb593ea42d231a7ddf367640f09bbf59dc00f2c1d2003cc340e0c016b5b13"
)
case 663000 ..< 663700:
case 663000 ..< 663150:
return WalletBirthday(
height: 663000,
hash: "0000000000bd422264b700bb33cab167ab42392c89db0e7c8ce30d57f346fe69",
time: 1576810013,
tree: "0102f02a8cd23e35502f8efa55893c7a145168ac3fa00d4bd032b55097bbe0335a01b57c362e3c834f2216c72ae0d8a335fd2397cc80073d0f5b29c419028bc6c94c100000000157bfd70afa37c8bf0c60c9e160d2145bdfbcf07837b0ff90bf8a5108722cc85400000000011bc9521263584de20822f9483e7edb5af54150c4823c775b2efc6a1eded9625501a6030f8d4b588681eddb66cad63f09c5c7519db49500fc56ebd481ce5e903c22000163f4eec5a2fe00a5f45e71e1542ff01e937d2210c99f03addcce5314a5278b2d0163ab01f46a3bb6ea46f5a19d5bdd59eb3f81e19cfa6d10ab0fd5566c7a16992601fa6980c053d84f809b6abcf35690f03a11f87b28e3240828e32e3f57af41e54e01319312241b0031e3a255b0d708750b4cb3f3fe79e3503fe488cc8db1dd00753801754bb593ea42d231a7ddf367640f09bbf59dc00f2c1d2003cc340e0c016b5b13"
)
case 663150 ..< 663700:
return WalletBirthday(
height: 663150,
hash: "0000000002fd3be4c24c437bd22620901617125ec2a3a6c902ec9a6c06f734fc",
time: 1576821833,
tree: "01ec6278a1bed9e1b080fd60ef50eb17411645e3746ff129283712bc4757ecc833001001b4e1d4a26ac4a2810b57a14f4ffb69395f55dde5674ecd2462af96f9126e054701a36afb68534f640938bdffd80dfcb3f4d5e232488abbf67d049b33a761e7ed6901a16e35205fb7fe626a9b13fc43e1d2b98a9c241f99f93d5e93a735454073025401f5b9bcbf3d0e3c83f95ee79299e8aeadf30af07717bda15ffb7a3d00243b58570001fa6d4c2390e205f81d86b85ace0b48f3ce0afb78eeef3e14c70bcfd7c5f0191c0000011bc9521263584de20822f9483e7edb5af54150c4823c775b2efc6a1eded9625501a6030f8d4b588681eddb66cad63f09c5c7519db49500fc56ebd481ce5e903c22000163f4eec5a2fe00a5f45e71e1542ff01e937d2210c99f03addcce5314a5278b2d0163ab01f46a3bb6ea46f5a19d5bdd59eb3f81e19cfa6d10ab0fd5566c7a16992601fa6980c053d84f809b6abcf35690f03a11f87b28e3240828e32e3f57af41e54e01319312241b0031e3a255b0d708750b4cb3f3fe79e3503fe488cc8db1dd00753801754bb593ea42d231a7ddf367640f09bbf59dc00f2c1d2003cc340e0c016b5b13"
)
case 663700 ..< 670000:
return WalletBirthday(
height: 663700,

View File

@ -64,7 +64,6 @@ public extension Notification.Name {
*/
public class SDKSynchronizer: Synchronizer {
public struct NotificationKeys {
public static let progress = "SDKSynchronizer.progress"
public static let blockHeight = "SDKSynchronizer.blockHeight"
@ -516,7 +515,7 @@ public class SDKSynchronizer: Synchronizer {
}
private func mapError(_ error: Error) -> Error {
if let compactBlockProcessorError = error as? CompactBlockProcessorError {
if let compactBlockProcessorError = error as? CompactBlockProcessorError {
switch compactBlockProcessorError {
case .dataDbInitFailed(let path):
return SynchronizerError.initFailed(message: "DataDb init failed at path: \(path)")

View File

@ -86,7 +86,7 @@ class BlockDownloaderTests: XCTestCase {
}
func testFailure() {
let awfulDownloader = CompactBlockDownloader(service: AwfulLightWalletService(latestBlockHeight: 281_000), storage: ZcashConsoleFakeStorage())
let awfulDownloader = CompactBlockDownloader(service: AwfulLightWalletService(latestBlockHeight: ZcashSDK.SAPLING_ACTIVATION_HEIGHT + 1000), storage: ZcashConsoleFakeStorage())
let expect = XCTestExpectation(description: self.description)
expect.expectedFulfillmentCount = 1

View File

@ -18,7 +18,7 @@ class CompactBlockProcessorTests: XCTestCase {
var startedScanningNotificationExpectation: XCTestExpectation!
var startedValidatingNotificationExpectation: XCTestExpectation!
var idleNotificationExpectation: XCTestExpectation!
let mockLatestHeight = 282_000
let mockLatestHeight = ZcashSDK.SAPLING_ACTIVATION_HEIGHT + 2000
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
@ -108,27 +108,24 @@ class CompactBlockProcessorTests: XCTestCase {
// test first range
var latestDownloadedHeight = processorConfig.walletBirthday // this can be either this or Wallet Birthday.
var latestBlockchainHeight = BlockHeight(281_000)
var latestBlockchainHeight = BlockHeight(ZcashSDK.SAPLING_ACTIVATION_HEIGHT + 1000)
var expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight, upper:latestDownloadedHeight + processorConfig.downloadBatchSize - 1))
XCTAssertEqual(expectedBatchRange, processor.nextBatchBlockRange(latestHeight: latestBlockchainHeight, latestDownloadedHeight: latestDownloadedHeight))
// Test mid-range
latestDownloadedHeight = BlockHeight(280_100)
latestBlockchainHeight = BlockHeight(281_000)
latestDownloadedHeight = BlockHeight(ZcashSDK.SAPLING_ACTIVATION_HEIGHT + ZcashSDK.DEFAULT_BATCH_SIZE)
latestBlockchainHeight = BlockHeight(ZcashSDK.SAPLING_ACTIVATION_HEIGHT + 1000)
expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight + 1, upper:latestDownloadedHeight + processorConfig.downloadBatchSize))
XCTAssertEqual(expectedBatchRange, processor.nextBatchBlockRange(latestHeight: latestBlockchainHeight, latestDownloadedHeight: latestDownloadedHeight))
latestDownloadedHeight = BlockHeight(280_950)
latestBlockchainHeight = BlockHeight(281_000)
// Test last batch range
latestDownloadedHeight = BlockHeight(280_950)
latestBlockchainHeight = BlockHeight(281_000)
latestDownloadedHeight = BlockHeight(ZcashSDK.SAPLING_ACTIVATION_HEIGHT + 950)
latestBlockchainHeight = BlockHeight(ZcashSDK.SAPLING_ACTIVATION_HEIGHT + 1000)
expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight + 1, upper: latestBlockchainHeight))

View File

@ -20,7 +20,7 @@ class CompactBlockReorgTests: XCTestCase {
var startedValidatingNotificationExpectation: XCTestExpectation!
var idleNotificationExpectation: XCTestExpectation!
var reorgNotificationExpectation: XCTestExpectation!
let mockLatestHeight = 282_000
let mockLatestHeight = ZcashSDK.SAPLING_ACTIVATION_HEIGHT + 2000
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
@ -33,7 +33,7 @@ class CompactBlockReorgTests: XCTestCase {
let mockBackend = MockRustBackend.self
mockBackend.mockValidateCombinedChainFailAfterAttempts = 3
mockBackend.mockValidateCombinedChainKeepFailing = false
mockBackend.mockValidateCombinedChainFailureHeight = 280_320
mockBackend.mockValidateCombinedChainFailureHeight = ZcashSDK.SAPLING_ACTIVATION_HEIGHT + 320
processor = CompactBlockProcessor(downloader: downloader,
backend: mockBackend,
@ -65,7 +65,7 @@ class CompactBlockReorgTests: XCTestCase {
}
@objc func processorHandledReorg(_ notification: Notification) {
// DispatchQueue.main.sync {
XCTAssertNotNil(notification.userInfo)
if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
let rewind = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight {
@ -76,8 +76,6 @@ class CompactBlockReorgTests: XCTestCase {
} else {
XCTFail("CompactBlockProcessor reorg notification is malformed")
}
// }
}
@objc func processorFailed(_ notification: Notification) {
@ -88,7 +86,6 @@ class CompactBlockReorgTests: XCTestCase {
} else {
XCTFail("CompactBlockProcessor failed")
}
}
}
@ -115,10 +112,9 @@ class CompactBlockReorgTests: XCTestCase {
downloadStartedExpect,
startedValidatingNotificationExpectation,
startedScanningNotificationExpectation,
reorgNotificationExpectation,
idleNotificationExpectation,
], timeout: 3000,enforceOrder: true)
], timeout: 300,enforceOrder: true)
}
private func expectedBatches(currentHeight: BlockHeight, targetHeight: BlockHeight, batchSize: Int) -> Int {

View File

@ -0,0 +1,243 @@
//
// ReOrgTests.swift
// ZcashLightClientKit-Unit-Tests
//
// Created by Francisco Gindre on 3/23/20.
//
import XCTest
@testable import ZcashLightClientKit
/**
basic reorg test. Scan, get a reorg and then reach latest height.
* connect to dLWD
* request latest height -> receive 663250
* download and sync blocks from 663150 to 663250
* trigger reorg by calling API (no need to pass params)**
* request latest height -> receive 663251!
* download that block
* observe that the prev hash of that block does not match the hash that we have for 663250
* rewind 10 blocks and request blocks 663241 to 663251
*/
class ReOrgTests: XCTestCase {
var processorConfig: CompactBlockProcessor.Configuration!
var processor: CompactBlockProcessor!
var darksideWalletService: DarksideWalletService!
var downloader: CompactBlockDownloader!
var downloadStartedExpect: XCTestExpectation!
var updatedNotificationExpectation: XCTestExpectation!
var stopNotificationExpectation: XCTestExpectation!
var startedScanningNotificationExpectation: XCTestExpectation!
var startedValidatingNotificationExpectation: XCTestExpectation!
var idleNotificationExpectation: XCTestExpectation!
var reorgNotificationExpectation: XCTestExpectation!
var afterReorgIdleNotification: XCTestExpectation!
var waitExpectation: XCTestExpectation!
let mockLatestHeight = BlockHeight(663250)
let targetLatestHeight = BlockHeight(663251)
let walletBirthday = BlockHeight(663150)
override func setUpWithError() throws {
logger = SampleLogger(logLevel: .debug)
var config = CompactBlockProcessor.Configuration.standard
let birthday = WalletBirthday.birthday(with: walletBirthday)
config.walletBirthday = birthday.height
processorConfig = config
try? FileManager.default.removeItem(at: processorConfig.cacheDb)
try? FileManager.default.removeItem(at: processorConfig.dataDb)
let service = DarksideWalletService()
darksideWalletService = service
let storage = CompactBlockStorage.init(connectionProvider: SimpleConnectionProvider(path: processorConfig.cacheDb.absoluteString))
try! storage.createTable()
downloader = CompactBlockDownloader(service: service, storage: storage)
processor = CompactBlockProcessor(downloader: downloader,
backend: ZcashRustBackend.self,
config: processorConfig)
downloadStartedExpect = XCTestExpectation(description: self.description + " downloadStartedExpect")
stopNotificationExpectation = XCTestExpectation(description: self.description + " stopNotificationExpectation")
updatedNotificationExpectation = XCTestExpectation(description: self.description + " updatedNotificationExpectation")
startedValidatingNotificationExpectation = XCTestExpectation(description: self.description + " startedValidatingNotificationExpectation")
startedScanningNotificationExpectation = XCTestExpectation(description: self.description + " startedScanningNotificationExpectation")
idleNotificationExpectation = XCTestExpectation(description: self.description + " idleNotificationExpectation")
afterReorgIdleNotification = XCTestExpectation(description: self.description + " afterReorgIdleNotification")
reorgNotificationExpectation = XCTestExpectation(description: self.description + " reorgNotificationExpectation")
waitExpectation = XCTestExpectation(description: self.description + "waitExpectation")
NotificationCenter.default.addObserver(self, selector: #selector(processorHandledReorg(_:)), name: Notification.Name.blockProcessorHandledReOrg, object: processor)
NotificationCenter.default.addObserver(self, selector: #selector(processorFailed(_:)), name: Notification.Name.blockProcessorFailed, object: processor)
}
override func tearDownWithError() throws {
try! FileManager.default.removeItem(at: processorConfig.cacheDb)
try? FileManager.default.removeItem(at: processorConfig.dataDb)
downloadStartedExpect.unsubscribeFromNotifications()
stopNotificationExpectation.unsubscribeFromNotifications()
updatedNotificationExpectation.unsubscribeFromNotifications()
startedScanningNotificationExpectation.unsubscribeFromNotifications()
startedValidatingNotificationExpectation.unsubscribeFromNotifications()
idleNotificationExpectation.unsubscribeFromNotifications()
reorgNotificationExpectation.unsubscribeFromNotifications()
afterReorgIdleNotification.unsubscribeFromNotifications()
NotificationCenter.default.removeObserver(self)
}
fileprivate func startProcessing() throws {
XCTAssertNotNil(processor)
// Subscribe to notifications
downloadStartedExpect.subscribe(to: Notification.Name.blockProcessorStartedDownloading, object: processor)
stopNotificationExpectation.subscribe(to: Notification.Name.blockProcessorStopped, object: processor)
updatedNotificationExpectation.subscribe(to: Notification.Name.blockProcessorUpdated, object: processor)
startedValidatingNotificationExpectation.subscribe(to: Notification.Name.blockProcessorStartedValidating, object: processor)
startedScanningNotificationExpectation.subscribe(to: Notification.Name.blockProcessorStartedScanning, object: processor)
NotificationCenter.default.addObserver(self, selector: #selector(firstIdleNotification(_:)), name: Notification.Name.blockProcessorIdle, object: processor)
NotificationCenter.default.addObserver(self, selector: #selector(handleReOrgNotification(_:)), name: Notification.Name.blockProcessorHandledReOrg, object: processor)
try processor.start()
}
@objc func firstIdleNotification(_ notification: Notification) {
idleNotificationExpectation.fulfill()
}
@objc func reOrgIdleNotification(_ notification: Notification) {
afterReorgIdleNotification.fulfill()
}
@objc func handleReOrgNotification(_ notification: Notification) {
reorgNotificationExpectation.fulfill()
guard let reorgHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
let rewindHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight else {
XCTFail("malformed reorg userInfo")
return
}
print("reorgHeight: \(reorgHeight)")
print("rewindHeight: \(rewindHeight)")
XCTAssertTrue(reorgHeight > 0)
XCTAssertNoThrow(rewindHeight > 0)
}
func testBasicReOrg() throws {
let mockLatestHeight = BlockHeight(663200)
let targetLatestHeight = BlockHeight(663250)
let reOrgHeight = BlockHeight(663195)
let walletBirthday = WalletBirthday.birthday(with: 663151).height
try basicReOrgTest(firstLatestHeight: mockLatestHeight, reorgHeight: reOrgHeight, walletBirthday: walletBirthday, targetHeight: targetLatestHeight)
}
func testTenPlusBlockReOrg() throws {
let mockLatestHeight = BlockHeight(663200)
let targetLatestHeight = BlockHeight(663250)
let reOrgHeight = BlockHeight(663180)
let walletBirthday = WalletBirthday.birthday(with: BlockHeight(663150)).height
try basicReOrgTest(firstLatestHeight: mockLatestHeight, reorgHeight: reOrgHeight, walletBirthday: walletBirthday, targetHeight: targetLatestHeight)
}
func basicReOrgTest(firstLatestHeight: BlockHeight, reorgHeight: BlockHeight, walletBirthday: BlockHeight, targetHeight: BlockHeight) throws {
do {
try darksideWalletService.setLatestHeight(firstLatestHeight)
} catch {
XCTFail("Error: \(error)")
return
}
/**
connect to dLWD
request latest height -> receive firstLatestHeight
*/
do {
print("first latest height: \(try darksideWalletService.latestBlockHeight())")
} catch {
XCTFail("Error: \(error)")
return
}
/**
download and sync blocks from walletBirthday to firstLatestHeight
*/
do {
try startProcessing()
} catch {
XCTFail("Error: \(error)")
}
wait(for: [downloadStartedExpect, startedValidatingNotificationExpectation,startedScanningNotificationExpectation, idleNotificationExpectation], timeout: 30)
idleNotificationExpectation.unsubscribeFromNotifications()
/**
verify that mock height has been reached
*/
var latestDownloadedHeight = BlockHeight(0)
XCTAssertNoThrow(try {latestDownloadedHeight = try downloader.lastDownloadedBlockHeight()}())
XCTAssertTrue(latestDownloadedHeight > 0)
/**
trigger reorg!
*/
XCTAssertNoThrow(
try darksideWalletService.triggerReOrg(latestHeight: targetHeight, reOrgHeight: reorgHeight)
)
/**
request latest height -> receive targetHeight!
*/
XCTAssertNoThrow(try {latestDownloadedHeight = try downloader.lastDownloadedBlockHeight()}())
afterReorgIdleNotification.subscribe(to: .blockProcessorIdle, object: processor)
/**
request latest height -> receive targetHeight!
download that block
observe that the prev hash of that block does not match the hash that we have for firstLatestHeight
rewind 10 blocks and request blocks targetHeight-10 to targetHeight
*/
try processor.start(retry: true)
// now reorg should happen and reorg notifications and idle notification should be triggered
wait(for: [reorgNotificationExpectation, afterReorgIdleNotification], timeout: 10)
// now everything should be fine. latest block should be targetHeight
XCTAssertNoThrow(try {latestDownloadedHeight = try downloader.lastDownloadedBlockHeight()}())
XCTAssertEqual(latestDownloadedHeight, targetHeight)
}
@objc func processorHandledReorg(_ notification: Notification) {
XCTAssertNotNil(notification.userInfo)
if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
let rewind = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight {
XCTAssertTrue( rewind <= mockLatestHeight - processorConfig.rewindDistance)
XCTAssertTrue( rewind <= reorg )
reorgNotificationExpectation.fulfill()
} else {
XCTFail("CompactBlockProcessor reorg notification is malformed")
}
}
@objc func processorFailed(_ notification: Notification) {
XCTAssertNotNil(notification.userInfo)
if let error = notification.userInfo?["error"] {
XCTFail("CompactBlockProcessor failed with Error: \(error)")
} else {
XCTFail("CompactBlockProcessor failed")
}
}
}

View File

@ -0,0 +1,106 @@
//
// DO NOT EDIT.
//
// Generated by the protocol buffer compiler.
// Source: darkside.proto
//
//
// Copyright 2018, gRPC Authors All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Dispatch
import Foundation
import SwiftGRPC
import SwiftProtobuf
@testable import ZcashLightClientKit
internal protocol DarksideStreamerDarksideGetIncomingTransactionsCall: ClientCallServerStreaming {
/// Do not call this directly, call `receive()` in the protocol extension below instead.
func _receive(timeout: DispatchTime) throws -> RawTransaction?
/// Call this to wait for a result. Nonblocking.
func receive(completion: @escaping (ResultOrRPCError<RawTransaction?>) -> Void) throws
}
internal extension DarksideStreamerDarksideGetIncomingTransactionsCall {
/// Call this to wait for a result. Blocking.
func receive(timeout: DispatchTime = .distantFuture) throws -> RawTransaction? { return try self._receive(timeout: timeout) }
}
fileprivate final class DarksideStreamerDarksideGetIncomingTransactionsCallBase: ClientCallServerStreamingBase<Empty, RawTransaction>, DarksideStreamerDarksideGetIncomingTransactionsCall {
override class var method: String { return "/cash.z.wallet.sdk.rpc.DarksideStreamer/DarksideGetIncomingTransactions" }
}
internal protocol DarksideStreamerDarksideSetStateCall: ClientCallUnary {}
fileprivate final class DarksideStreamerDarksideSetStateCallBase: ClientCallUnaryBase<DarksideState, Empty>, DarksideStreamerDarksideSetStateCall {
override class var method: String { return "/cash.z.wallet.sdk.rpc.DarksideStreamer/DarksideSetState" }
}
/// Instantiate DarksideStreamerServiceClient, then call methods of this protocol to make API calls.
internal protocol DarksideStreamerService: ServiceClient {
/// Asynchronous. Server-streaming.
/// Send the initial message.
/// Use methods on the returned object to get streamed responses.
func darksideGetIncomingTransactions(_ request: Empty, metadata customMetadata: Metadata, completion: ((CallResult) -> Void)?) throws -> DarksideStreamerDarksideGetIncomingTransactionsCall
/// Synchronous. Unary.
func darksideSetState(_ request: DarksideState, metadata customMetadata: Metadata) throws -> Empty
/// Asynchronous. Unary.
@discardableResult
func darksideSetState(_ request: DarksideState, metadata customMetadata: Metadata, completion: @escaping (Empty?, CallResult) -> Void) throws -> DarksideStreamerDarksideSetStateCall
}
internal extension DarksideStreamerService {
/// Asynchronous. Server-streaming.
func darksideGetIncomingTransactions(_ request: Empty, completion: ((CallResult) -> Void)?) throws -> DarksideStreamerDarksideGetIncomingTransactionsCall {
return try self.darksideGetIncomingTransactions(request, metadata: self.metadata, completion: completion)
}
/// Synchronous. Unary.
func darksideSetState(_ request: DarksideState) throws -> Empty {
return try self.darksideSetState(request, metadata: self.metadata)
}
/// Asynchronous. Unary.
@discardableResult
func darksideSetState(_ request: DarksideState, completion: @escaping (Empty?, CallResult) -> Void) throws -> DarksideStreamerDarksideSetStateCall {
return try self.darksideSetState(request, metadata: self.metadata, completion: completion)
}
}
internal final class DarksideStreamerServiceClient: ServiceClientBase, DarksideStreamerService {
/// Asynchronous. Server-streaming.
/// Send the initial message.
/// Use methods on the returned object to get streamed responses.
internal func darksideGetIncomingTransactions(_ request: Empty, metadata customMetadata: Metadata, completion: ((CallResult) -> Void)?) throws -> DarksideStreamerDarksideGetIncomingTransactionsCall {
return try DarksideStreamerDarksideGetIncomingTransactionsCallBase(channel)
.start(request: request, metadata: customMetadata, completion: completion)
}
/// Synchronous. Unary.
internal func darksideSetState(_ request: DarksideState, metadata customMetadata: Metadata) throws -> Empty {
return try DarksideStreamerDarksideSetStateCallBase(channel)
.run(request: request, metadata: customMetadata)
}
/// Asynchronous. Unary.
@discardableResult
internal func darksideSetState(_ request: DarksideState, metadata customMetadata: Metadata, completion: @escaping (Empty?, CallResult) -> Void) throws -> DarksideStreamerDarksideSetStateCall {
return try DarksideStreamerDarksideSetStateCallBase(channel)
.start(request: request, metadata: customMetadata, completion: completion)
}
}

View File

@ -0,0 +1,77 @@
// DO NOT EDIT.
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: darkside.proto
//
// For information on using the generated types, please see the documenation:
// https://github.com/apple/swift-protobuf/
// Copyright (c) 2019-2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that your are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
struct DarksideState {
// 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 latestHeight: UInt64 = 0
var reorgHeight: UInt64 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "cash.z.wallet.sdk.rpc"
extension DarksideState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".DarksideState"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "latestHeight"),
2: .same(proto: "reorgHeight"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularUInt64Field(value: &self.latestHeight)
case 2: try decoder.decodeSingularUInt64Field(value: &self.reorgHeight)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.latestHeight != 0 {
try visitor.visitSingularUInt64Field(value: self.latestHeight, fieldNumber: 1)
}
if self.reorgHeight != 0 {
try visitor.visitSingularUInt64Field(value: self.reorgHeight, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: DarksideState, rhs: DarksideState) -> Bool {
if lhs.latestHeight != rhs.latestHeight {return false}
if lhs.reorgHeight != rhs.reorgHeight {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

View File

@ -0,0 +1,25 @@
// Copyright (c) 2019-2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
syntax = "proto3";
package cash.z.wallet.sdk.rpc;
option go_package = "walletrpc";
option swift_prefix = "";
import "service.proto";
message DarksideState {
uint64 latestHeight = 1;
uint64 reorgHeight = 2;
}
service DarksideStreamer {
// Darkside (testing, see --darkside-very-insecure):
// Return the list of transactions that have been submitted (via SendTransaction).
rpc DarksideGetIncomingTransactions(Empty) returns (stream RawTransaction) {}
// Set the information that GetLightdInfo returns, except that chainName specifies
// a file of blocks within testdata/darkside that GetBlock will return.
rpc DarksideSetState(DarksideState) returns (Empty) {}
}

View File

@ -0,0 +1,62 @@
//
// DarkSideWalletService.swift
// ZcashLightClientKit-Unit-Tests
//
// Created by Francisco Gindre on 3/23/20.
//
import Foundation
import ZcashLightClientKit
import SwiftGRPC
class DarksideWalletService: LightWalletService {
var channel: Channel
init() {
let channel = ChannelProvider().channel()
self.channel = channel
self.service = LightWalletGRPCService(channel: channel)
self.darksideService = DarksideStreamerServiceClient(channel: channel)
}
var service: LightWalletGRPCService
var darksideService: DarksideStreamerServiceClient
func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void) {
service.latestBlockHeight(result: result)
}
func latestBlockHeight() throws -> BlockHeight {
try service.latestBlockHeight()
}
func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) {
service.blockRange(range, result: result)
}
func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock] {
try service.blockRange(range)
}
func submit(spendTransaction: Data, result: @escaping (Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
service.submit(spendTransaction: spendTransaction, result: result)
}
func submit(spendTransaction: Data) throws -> LightWalletServiceResponse {
try service.submit(spendTransaction: spendTransaction)
}
func triggerReOrg(latestHeight: BlockHeight, reOrgHeight: BlockHeight) throws {
var darksideState = DarksideState()
darksideState.latestHeight = UInt64(latestHeight)
darksideState.reorgHeight = UInt64(reOrgHeight)
_ = try darksideService.darksideSetState(darksideState, metadata: Metadata())
}
func setLatestHeight(_ latestHeight: BlockHeight) throws {
var state = DarksideState()
state.reorgHeight = 0
state.latestHeight = UInt64(latestHeight)
_ = try darksideService.darksideSetState(state, metadata: Metadata())
}
}

View File

@ -0,0 +1,61 @@
//
// SampleLogger.swift
// ZcashLightClientSample
//
// Created by Francisco Gindre on 3/9/20.
// Copyright © 2020 Electric Coin Company. All rights reserved.
//
import Foundation
import os
import ZcashLightClientKit
class SampleLogger: Logger {
enum LogLevel: Int {
case debug
case error
case warning
case event
case info
}
var level: LogLevel
init(logLevel: LogLevel) {
self.level = logLevel
}
private static let subsystem = Bundle.main.bundleIdentifier!
static let oslog = OSLog(subsystem: subsystem, category: "test-logs")
func debug(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue == LogLevel.debug.rawValue else { return }
log(level: "DEBUG 🐞", message: message, file: file, function: function, line: line)
}
func error(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.error.rawValue else { return }
log(level: "ERROR 💥", message: message, file: file, function: function, line: line)
}
func warn(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.warning.rawValue else { return }
log(level: "WARNING ⚠️", message: message, file: file, function: function, line: line)
}
func event(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.event.rawValue else { return }
log(level: "EVENT ⏱", message: message, file: file, function: function, line: line)
}
func info(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.info.rawValue else { return }
log(level: "INFO ", message: message, file: file, function: function, line: line)
}
private func log(level: String, message: String, file: String, function: String, line: Int) {
let fileName = file as NSString
os_log("[%@] %@ - %@ - Line: %d -> %@", log: Self.oslog, type: .default, level, fileName.lastPathComponent, function, line, message)
}
}

View File

@ -219,4 +219,8 @@ class MockRustBackend: ZcashRustBackendWelding {
nil
}
static func decryptAndStoreTransaction(dbData: URL, tx: [UInt8]) -> Bool {
false
}
}

View File

@ -18,8 +18,8 @@ class LightWalletEndpointBuilder {
}
class ChannelProvider {
func channel() -> SwiftGRPC.Channel {
Channel(address: Constants.address, secure: false)
func channel(secure: Bool = false) -> SwiftGRPC.Channel {
Channel(address: Constants.address, secure: secure)
}
}