Integrate Orchard support and latest Rust updates
This commit is contained in:
parent
730aee0c5f
commit
c98de89613
|
@ -176,7 +176,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "d1d038d653806f23fc291931823d95f2dc411486"
|
"revision" : "c918662aeb23b429abb400558483f9927dc56245",
|
||||||
|
"version" : "0.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -122,7 +122,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "d1d038d653806f23fc291931823d95f2dc411486"
|
"revision" : "c918662aeb23b429abb400558483f9927dc56245",
|
||||||
|
"version" : "0.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -16,8 +16,7 @@ let package = Package(
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.19.1"),
|
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.19.1"),
|
||||||
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1"),
|
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1"),
|
||||||
// Compiled from revision `70cce1272c26ed52fbe7bfa334be781373b64bfd`.
|
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", exact: "0.7.0")
|
||||||
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "d1d038d653806f23fc291931823d95f2dc411486")
|
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
|
|
|
@ -329,10 +329,10 @@ public enum ZcashError: Equatable, Error {
|
||||||
/// - `rustError` contains error generated by the rust layer.
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
/// ZRUST0058
|
/// ZRUST0058
|
||||||
case rustListAccounts(_ rustError: String)
|
case rustListAccounts(_ rustError: String)
|
||||||
/// Error from rust layer when calling ZcashRustBackend.isSeedRelevantToWallet
|
/// Error from rust layer when calling ZcashRustBackend.rustIsSeedRelevantToAnyDerivedAccount
|
||||||
/// - `rustError` contains error generated by the rust layer.
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
/// ZRUST0059
|
/// ZRUST0059
|
||||||
case rustIsSeedRelevantToWallet(_ rustError: String)
|
case rustIsSeedRelevantToAnyDerivedAccount(_ rustError: String)
|
||||||
/// SQLite query failed when fetching all accounts from the database.
|
/// SQLite query failed when fetching all accounts from the database.
|
||||||
/// - `sqliteError` is error produced by SQLite library.
|
/// - `sqliteError` is error produced by SQLite library.
|
||||||
/// ZADAO0001
|
/// ZADAO0001
|
||||||
|
@ -690,7 +690,7 @@ public enum ZcashError: Equatable, Error {
|
||||||
case .rustGetWalletSummary: return "Error from rust layer when calling ZcashRustBackend.getWalletSummary"
|
case .rustGetWalletSummary: return "Error from rust layer when calling ZcashRustBackend.getWalletSummary"
|
||||||
case .rustProposeTransferFromURI: return "Error from rust layer when calling ZcashRustBackend."
|
case .rustProposeTransferFromURI: return "Error from rust layer when calling ZcashRustBackend."
|
||||||
case .rustListAccounts: return "Error from rust layer when calling ZcashRustBackend."
|
case .rustListAccounts: return "Error from rust layer when calling ZcashRustBackend."
|
||||||
case .rustIsSeedRelevantToWallet: return "Error from rust layer when calling ZcashRustBackend.isSeedRelevantToWallet"
|
case .rustIsSeedRelevantToAnyDerivedAccount: return "Error from rust layer when calling ZcashRustBackend.rustIsSeedRelevantToAnyDerivedAccount"
|
||||||
case .accountDAOGetAll: return "SQLite query failed when fetching all accounts from the database."
|
case .accountDAOGetAll: return "SQLite query failed when fetching all accounts from the database."
|
||||||
case .accountDAOGetAllCantDecode: return "Fetched accounts from SQLite but can't decode them."
|
case .accountDAOGetAllCantDecode: return "Fetched accounts from SQLite but can't decode them."
|
||||||
case .accountDAOFindBy: return "SQLite query failed when seaching for accounts in the database."
|
case .accountDAOFindBy: return "SQLite query failed when seaching for accounts in the database."
|
||||||
|
@ -867,7 +867,7 @@ public enum ZcashError: Equatable, Error {
|
||||||
case .rustGetWalletSummary: return .rustGetWalletSummary
|
case .rustGetWalletSummary: return .rustGetWalletSummary
|
||||||
case .rustProposeTransferFromURI: return .rustProposeTransferFromURI
|
case .rustProposeTransferFromURI: return .rustProposeTransferFromURI
|
||||||
case .rustListAccounts: return .rustListAccounts
|
case .rustListAccounts: return .rustListAccounts
|
||||||
case .rustIsSeedRelevantToWallet: return .rustIsSeedRelevantToWallet
|
case .rustIsSeedRelevantToAnyDerivedAccount: return .rustIsSeedRelevantToAnyDerivedAccount
|
||||||
case .accountDAOGetAll: return .accountDAOGetAll
|
case .accountDAOGetAll: return .accountDAOGetAll
|
||||||
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
|
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
|
||||||
case .accountDAOFindBy: return .accountDAOFindBy
|
case .accountDAOFindBy: return .accountDAOFindBy
|
||||||
|
|
|
@ -179,8 +179,8 @@ public enum ZcashErrorCode: String {
|
||||||
case rustProposeTransferFromURI = "ZRUST0057"
|
case rustProposeTransferFromURI = "ZRUST0057"
|
||||||
/// Error from rust layer when calling ZcashRustBackend.
|
/// Error from rust layer when calling ZcashRustBackend.
|
||||||
case rustListAccounts = "ZRUST0058"
|
case rustListAccounts = "ZRUST0058"
|
||||||
/// Error from rust layer when calling ZcashRustBackend.isSeedRelevantToWallet
|
/// Error from rust layer when calling ZcashRustBackend.rustIsSeedRelevantToAnyDerivedAccount
|
||||||
case rustIsSeedRelevantToWallet = "ZRUST0059"
|
case rustIsSeedRelevantToAnyDerivedAccount = "ZRUST0059"
|
||||||
/// SQLite query failed when fetching all accounts from the database.
|
/// SQLite query failed when fetching all accounts from the database.
|
||||||
case accountDAOGetAll = "ZADAO0001"
|
case accountDAOGetAll = "ZADAO0001"
|
||||||
/// Fetched accounts from SQLite but can't decode them.
|
/// Fetched accounts from SQLite but can't decode them.
|
||||||
|
|
|
@ -356,10 +356,10 @@ enum ZcashErrorDefinition {
|
||||||
/// - `rustError` contains error generated by the rust layer.
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
// sourcery: code="ZRUST0058"
|
// sourcery: code="ZRUST0058"
|
||||||
case rustListAccounts(_ rustError: String)
|
case rustListAccounts(_ rustError: String)
|
||||||
/// Error from rust layer when calling ZcashRustBackend.isSeedRelevantToWallet
|
/// Error from rust layer when calling ZcashRustBackend.rustIsSeedRelevantToAnyDerivedAccount
|
||||||
/// - `rustError` contains error generated by the rust layer.
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
// sourcery: code="ZRUST0059"
|
// sourcery: code="ZRUST0059"
|
||||||
case rustIsSeedRelevantToWallet(_ rustError: String)
|
case rustIsSeedRelevantToAnyDerivedAccount(_ rustError: String)
|
||||||
|
|
||||||
// MARK: - Account DAO
|
// MARK: - Account DAO
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,10 @@ public struct PoolBalance: Equatable {
|
||||||
|
|
||||||
public struct AccountBalance: Equatable {
|
public struct AccountBalance: Equatable {
|
||||||
public let saplingBalance: PoolBalance
|
public let saplingBalance: PoolBalance
|
||||||
|
public let orchardBalance: PoolBalance
|
||||||
public let unshielded: Zatoshi
|
public let unshielded: Zatoshi
|
||||||
|
|
||||||
static let zero = AccountBalance(saplingBalance: .zero, unshielded: .zero)
|
static let zero = AccountBalance(saplingBalance: .zero, orchardBalance: .zero, unshielded: .zero)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ScanProgress: Equatable {
|
struct ScanProgress: Equatable {
|
||||||
|
@ -53,4 +54,5 @@ struct WalletSummary: Equatable {
|
||||||
let fullyScannedHeight: BlockHeight
|
let fullyScannedHeight: BlockHeight
|
||||||
let scanProgress: ScanProgress?
|
let scanProgress: ScanProgress?
|
||||||
let nextSaplingSubtreeIndex: UInt32
|
let nextSaplingSubtreeIndex: UInt32
|
||||||
|
let nextOrchardSubtreeIndex: UInt32
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,9 +105,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
return ffiBinaryKeyPtr.pointee.unsafeToUnifiedSpendingKey(network: networkType)
|
return ffiBinaryKeyPtr.pointee.unsafeToUnifiedSpendingKey(network: networkType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSeedRelevantToWallet(seed: [UInt8]) async throws -> Bool {
|
func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool {
|
||||||
globalDBLock.lock()
|
globalDBLock.lock()
|
||||||
let result = zcashlc_is_seed_relevant_to_wallet(
|
let result = zcashlc_is_seed_relevant_to_any_derived_account(
|
||||||
dbData.0,
|
dbData.0,
|
||||||
dbData.1,
|
dbData.1,
|
||||||
seed,
|
seed,
|
||||||
|
@ -118,7 +118,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
|
|
||||||
// -1 is the error sentinel.
|
// -1 is the error sentinel.
|
||||||
guard result >= 0 else {
|
guard result >= 0 else {
|
||||||
throw ZcashError.rustIsSeedRelevantToWallet(lastErrorMessage(fallback: "`isSeedRelevantToWallet` failed with unknown error"))
|
throw ZcashError.rustIsSeedRelevantToAnyDerivedAccount(lastErrorMessage(fallback: "`isSeedRelevantToAnyDerivedAccount` failed with unknown error"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0 is false, 1 is true.
|
// 0 is false, 1 is true.
|
||||||
|
@ -343,6 +343,8 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
return DbInitResult.success
|
return DbInitResult.success
|
||||||
case 1:
|
case 1:
|
||||||
return DbInitResult.seedRequired
|
return DbInitResult.seedRequired
|
||||||
|
case 2:
|
||||||
|
return DbInitResult.seedNotRelevant
|
||||||
default:
|
default:
|
||||||
throw ZcashError.rustInitDataDb(lastErrorMessage(fallback: "`initDataDb` failed with unknown error"))
|
throw ZcashError.rustInitDataDb(lastErrorMessage(fallback: "`initDataDb` failed with unknown error"))
|
||||||
}
|
}
|
||||||
|
@ -634,7 +636,8 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
chainTipHeight: BlockHeight(summaryPtr.pointee.chain_tip_height),
|
chainTipHeight: BlockHeight(summaryPtr.pointee.chain_tip_height),
|
||||||
fullyScannedHeight: BlockHeight(summaryPtr.pointee.fully_scanned_height),
|
fullyScannedHeight: BlockHeight(summaryPtr.pointee.fully_scanned_height),
|
||||||
scanProgress: summaryPtr.pointee.scan_progress?.pointee.toScanProgress(),
|
scanProgress: summaryPtr.pointee.scan_progress?.pointee.toScanProgress(),
|
||||||
nextSaplingSubtreeIndex: UInt32(summaryPtr.pointee.next_sapling_subtree_index)
|
nextSaplingSubtreeIndex: UInt32(summaryPtr.pointee.next_sapling_subtree_index),
|
||||||
|
nextOrchardSubtreeIndex: UInt32(summaryPtr.pointee.next_orchard_subtree_index)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -897,6 +900,7 @@ extension FfiAccountBalance {
|
||||||
func toAccountBalance() -> AccountBalance {
|
func toAccountBalance() -> AccountBalance {
|
||||||
.init(
|
.init(
|
||||||
saplingBalance: self.sapling_balance.toPoolBalance(),
|
saplingBalance: self.sapling_balance.toPoolBalance(),
|
||||||
|
orchardBalance: self.orchard_balance.toPoolBalance(),
|
||||||
unshielded: Zatoshi(self.unshielded)
|
unshielded: Zatoshi(self.unshielded)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,14 @@ import Foundation
|
||||||
enum ZcashRustBackendWeldingConstants {
|
enum ZcashRustBackendWeldingConstants {
|
||||||
static let validChain: Int32 = -1
|
static let validChain: Int32 = -1
|
||||||
}
|
}
|
||||||
/// Enumeration of potential return states for database initialization. If `seedRequired`
|
|
||||||
/// is returned, the caller must re-attempt initialization providing the seed
|
/// Enumeration of potential return states for database initialization.
|
||||||
|
///
|
||||||
|
/// If `seedRequired` is returned, the caller must re-attempt initialization providing the seed.
|
||||||
public enum DbInitResult {
|
public enum DbInitResult {
|
||||||
case success
|
case success
|
||||||
case seedRequired
|
case seedRequired
|
||||||
|
case seedNotRelevant
|
||||||
}
|
}
|
||||||
|
|
||||||
// sourcery: mockActor
|
// sourcery: mockActor
|
||||||
|
@ -44,10 +47,10 @@ protocol ZcashRustBackendWelding {
|
||||||
/// - Throws: `rustCreateAccount`.
|
/// - Throws: `rustCreateAccount`.
|
||||||
func createAccount(seed: [UInt8], treeState: TreeState, recoverUntil: UInt32?) async throws -> UnifiedSpendingKey
|
func createAccount(seed: [UInt8], treeState: TreeState, recoverUntil: UInt32?) async throws -> UnifiedSpendingKey
|
||||||
|
|
||||||
/// Checks whether the given seed is relevant to any of the accounts in the wallet.
|
/// Checks whether the given seed is relevant to any of the derived accounts in the wallet.
|
||||||
///
|
///
|
||||||
/// - parameter seed: byte array of the seed
|
/// - parameter seed: byte array of the seed
|
||||||
func isSeedRelevantToWallet(seed: [UInt8]) async throws -> Bool
|
func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool
|
||||||
|
|
||||||
/// Scans a transaction for any information that can be decrypted by the accounts in the wallet, and saves it to the wallet.
|
/// Scans a transaction for any information that can be decrypted by the accounts in the wallet, and saves it to the wallet.
|
||||||
/// - parameter tx: the transaction to decrypt
|
/// - parameter tx: the transaction to decrypt
|
||||||
|
|
|
@ -351,11 +351,11 @@ public protocol Synchronizer: AnyObject {
|
||||||
/// - Throws: ZcashError when failures occur and related to `synchronizer.start(retry: Bool)`, it's the only throwing operation
|
/// - Throws: ZcashError when failures occur and related to `synchronizer.start(retry: Bool)`, it's the only throwing operation
|
||||||
/// during the whole endpoint change.
|
/// during the whole endpoint change.
|
||||||
func switchTo(endpoint: LightWalletEndpoint) async throws
|
func switchTo(endpoint: LightWalletEndpoint) async throws
|
||||||
|
|
||||||
/// Checks whether the given seed is relevant to any of the accounts in the wallet.
|
/// Checks whether the given seed is relevant to any of the derived accounts in the wallet.
|
||||||
///
|
///
|
||||||
/// - parameter seed: byte array of the seed
|
/// - parameter seed: byte array of the seed
|
||||||
func isSeedRelevantToWallet(seed: [UInt8]) async throws -> Bool
|
func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SyncStatus: Equatable {
|
public enum SyncStatus: Equatable {
|
||||||
|
|
|
@ -603,11 +603,11 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
|
|
||||||
return subject.eraseToAnyPublisher()
|
return subject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isSeedRelevantToWallet(seed: [UInt8]) async throws -> Bool {
|
public func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool {
|
||||||
try await initializer.rustBackend.isSeedRelevantToWallet(seed: seed)
|
try await initializer.rustBackend.isSeedRelevantToAnyDerivedAccount(seed: seed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Server switch
|
// MARK: Server switch
|
||||||
|
|
||||||
public func switchTo(endpoint: LightWalletEndpoint) async throws {
|
public func switchTo(endpoint: LightWalletEndpoint) async throws {
|
||||||
|
|
|
@ -638,7 +638,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetTransparentBalanceSucceed() {
|
func testGetTransparentBalanceSucceed() {
|
||||||
let expectedBalance = AccountBalance(saplingBalance: .zero, unshielded: Zatoshi(200))
|
let expectedBalance = AccountBalance(saplingBalance: .zero, orchardBalance: .zero, unshielded: Zatoshi(200))
|
||||||
|
|
||||||
synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in
|
synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in
|
||||||
XCTAssertEqual(receivedAccountIndex, 3)
|
XCTAssertEqual(receivedAccountIndex, 3)
|
||||||
|
@ -685,7 +685,13 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
|
||||||
PoolBalance(
|
PoolBalance(
|
||||||
spendableValue: Zatoshi(333),
|
spendableValue: Zatoshi(333),
|
||||||
changePendingConfirmation: .zero,
|
changePendingConfirmation: .zero,
|
||||||
valuePendingSpendability: .zero), unshielded: .zero
|
valuePendingSpendability: .zero),
|
||||||
|
orchardBalance:
|
||||||
|
PoolBalance(
|
||||||
|
spendableValue: Zatoshi(333),
|
||||||
|
changePendingConfirmation: .zero,
|
||||||
|
valuePendingSpendability: .zero),
|
||||||
|
unshielded: .zero
|
||||||
)
|
)
|
||||||
|
|
||||||
synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in
|
synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in
|
||||||
|
@ -733,7 +739,13 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
|
||||||
PoolBalance(
|
PoolBalance(
|
||||||
spendableValue: .zero,
|
spendableValue: .zero,
|
||||||
changePendingConfirmation: Zatoshi(333),
|
changePendingConfirmation: Zatoshi(333),
|
||||||
valuePendingSpendability: .zero), unshielded: .zero
|
valuePendingSpendability: .zero),
|
||||||
|
orchardBalance:
|
||||||
|
PoolBalance(
|
||||||
|
spendableValue: .zero,
|
||||||
|
changePendingConfirmation: Zatoshi(333),
|
||||||
|
valuePendingSpendability: .zero),
|
||||||
|
unshielded: .zero
|
||||||
)
|
)
|
||||||
|
|
||||||
synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in
|
synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in
|
||||||
|
|
|
@ -748,7 +748,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetTransparentBalanceSucceed() {
|
func testGetTransparentBalanceSucceed() {
|
||||||
let expectedBalance = AccountBalance(saplingBalance: .zero, unshielded: Zatoshi(100))
|
let expectedBalance = AccountBalance(saplingBalance: .zero, orchardBalance: .zero, unshielded: Zatoshi(100))
|
||||||
|
|
||||||
synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in
|
synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in
|
||||||
XCTAssertEqual(receivedAccountIndex, 3)
|
XCTAssertEqual(receivedAccountIndex, 3)
|
||||||
|
@ -809,6 +809,11 @@ class CombineSynchronizerOfflineTests: XCTestCase {
|
||||||
spendableValue: .zero,
|
spendableValue: .zero,
|
||||||
changePendingConfirmation: Zatoshi(333),
|
changePendingConfirmation: Zatoshi(333),
|
||||||
valuePendingSpendability: .zero),
|
valuePendingSpendability: .zero),
|
||||||
|
orchardBalance:
|
||||||
|
PoolBalance(
|
||||||
|
spendableValue: .zero,
|
||||||
|
changePendingConfirmation: Zatoshi(333),
|
||||||
|
valuePendingSpendability: .zero),
|
||||||
unshielded: .zero
|
unshielded: .zero
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -871,6 +876,11 @@ class CombineSynchronizerOfflineTests: XCTestCase {
|
||||||
spendableValue: Zatoshi(333),
|
spendableValue: Zatoshi(333),
|
||||||
changePendingConfirmation: .zero,
|
changePendingConfirmation: .zero,
|
||||||
valuePendingSpendability: .zero),
|
valuePendingSpendability: .zero),
|
||||||
|
orchardBalance:
|
||||||
|
PoolBalance(
|
||||||
|
spendableValue: Zatoshi(333),
|
||||||
|
changePendingConfirmation: .zero,
|
||||||
|
valuePendingSpendability: .zero),
|
||||||
unshielded: .zero
|
unshielded: .zero
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,13 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCallPrepareWithAlreadyUsedAliasThrowsError() async throws {
|
func testCallPrepareWithAlreadyUsedAliasThrowsError() async throws {
|
||||||
|
// Pick a testnet height for which both Sapling and Orchard are active.
|
||||||
|
let walletBirthday = 1900000
|
||||||
|
|
||||||
let firstTestCoordinator = try await TestCoordinator(
|
let firstTestCoordinator = try await TestCoordinator(
|
||||||
alias: .custom("alias"),
|
alias: .custom("alias"),
|
||||||
container: mockContainer,
|
container: mockContainer,
|
||||||
walletBirthday: 10,
|
walletBirthday: walletBirthday,
|
||||||
network: network,
|
network: network,
|
||||||
callPrepareInConstructor: false
|
callPrepareInConstructor: false
|
||||||
)
|
)
|
||||||
|
@ -40,7 +43,7 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
let secondTestCoordinator = try await TestCoordinator(
|
let secondTestCoordinator = try await TestCoordinator(
|
||||||
alias: .custom("alias"),
|
alias: .custom("alias"),
|
||||||
container: mockContainer,
|
container: mockContainer,
|
||||||
walletBirthday: 10,
|
walletBirthday: walletBirthday,
|
||||||
network: network,
|
network: network,
|
||||||
callPrepareInConstructor: false
|
callPrepareInConstructor: false
|
||||||
)
|
)
|
||||||
|
@ -58,10 +61,13 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testWhenSynchronizerIsDeallocatedAliasIsntUsedAnymore() async throws {
|
func testWhenSynchronizerIsDeallocatedAliasIsntUsedAnymore() async throws {
|
||||||
|
// Pick a testnet height for which both Sapling and Orchard are active.
|
||||||
|
let walletBirthday = 1900000
|
||||||
|
|
||||||
var testCoordinator: TestCoordinator! = try await TestCoordinator(
|
var testCoordinator: TestCoordinator! = try await TestCoordinator(
|
||||||
alias: .default,
|
alias: .default,
|
||||||
container: mockContainer,
|
container: mockContainer,
|
||||||
walletBirthday: 10,
|
walletBirthday: walletBirthday,
|
||||||
network: network,
|
network: network,
|
||||||
callPrepareInConstructor: false
|
callPrepareInConstructor: false
|
||||||
)
|
)
|
||||||
|
@ -75,7 +81,7 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
testCoordinator = try await TestCoordinator(
|
testCoordinator = try await TestCoordinator(
|
||||||
alias: .default,
|
alias: .default,
|
||||||
container: mockContainer,
|
container: mockContainer,
|
||||||
walletBirthday: 10,
|
walletBirthday: walletBirthday,
|
||||||
network: network,
|
network: network,
|
||||||
callPrepareInConstructor: false
|
callPrepareInConstructor: false
|
||||||
)
|
)
|
||||||
|
@ -88,10 +94,13 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCallWipeWithAlreadyUsedAliasThrowsError() async throws {
|
func testCallWipeWithAlreadyUsedAliasThrowsError() async throws {
|
||||||
|
// Pick a testnet height for which both Sapling and Orchard are active.
|
||||||
|
let walletBirthday = 1900000
|
||||||
|
|
||||||
let firstTestCoordinator = try await TestCoordinator(
|
let firstTestCoordinator = try await TestCoordinator(
|
||||||
alias: .default,
|
alias: .default,
|
||||||
container: mockContainer,
|
container: mockContainer,
|
||||||
walletBirthday: 10,
|
walletBirthday: walletBirthday,
|
||||||
network: network,
|
network: network,
|
||||||
callPrepareInConstructor: false
|
callPrepareInConstructor: false
|
||||||
)
|
)
|
||||||
|
@ -99,7 +108,7 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
let secondTestCoordinator = try await TestCoordinator(
|
let secondTestCoordinator = try await TestCoordinator(
|
||||||
alias: .default,
|
alias: .default,
|
||||||
container: mockContainer,
|
container: mockContainer,
|
||||||
walletBirthday: 10,
|
walletBirthday: walletBirthday,
|
||||||
network: network,
|
network: network,
|
||||||
callPrepareInConstructor: false
|
callPrepareInConstructor: false
|
||||||
)
|
)
|
||||||
|
@ -146,10 +155,13 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPrepareCanBeCalledAfterWipeWithSameInstanceOfSDKSynchronizer() async throws {
|
func testPrepareCanBeCalledAfterWipeWithSameInstanceOfSDKSynchronizer() async throws {
|
||||||
|
// Pick a testnet height for which both Sapling and Orchard are active.
|
||||||
|
let walletBirthday = 1900000
|
||||||
|
|
||||||
let testCoordinator = try await TestCoordinator(
|
let testCoordinator = try await TestCoordinator(
|
||||||
alias: .default,
|
alias: .default,
|
||||||
container: mockContainer,
|
container: mockContainer,
|
||||||
walletBirthday: 10,
|
walletBirthday: walletBirthday,
|
||||||
network: network,
|
network: network,
|
||||||
callPrepareInConstructor: false
|
callPrepareInConstructor: false
|
||||||
)
|
)
|
||||||
|
@ -180,10 +192,13 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSendToAddressCalledWithoutPrepareThrowsError() async throws {
|
func testSendToAddressCalledWithoutPrepareThrowsError() async throws {
|
||||||
|
// Pick a testnet height for which both Sapling and Orchard are active.
|
||||||
|
let walletBirthday = 1900000
|
||||||
|
|
||||||
let testCoordinator = try await TestCoordinator(
|
let testCoordinator = try await TestCoordinator(
|
||||||
alias: .default,
|
alias: .default,
|
||||||
container: mockContainer,
|
container: mockContainer,
|
||||||
walletBirthday: 10,
|
walletBirthday: walletBirthday,
|
||||||
network: network,
|
network: network,
|
||||||
callPrepareInConstructor: false
|
callPrepareInConstructor: false
|
||||||
)
|
)
|
||||||
|
@ -205,10 +220,13 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testShieldFundsCalledWithoutPrepareThrowsError() async throws {
|
func testShieldFundsCalledWithoutPrepareThrowsError() async throws {
|
||||||
|
// Pick a testnet height for which both Sapling and Orchard are active.
|
||||||
|
let walletBirthday = 1900000
|
||||||
|
|
||||||
let testCoordinator = try await TestCoordinator(
|
let testCoordinator = try await TestCoordinator(
|
||||||
alias: .default,
|
alias: .default,
|
||||||
container: mockContainer,
|
container: mockContainer,
|
||||||
walletBirthday: 10,
|
walletBirthday: walletBirthday,
|
||||||
network: network,
|
network: network,
|
||||||
callPrepareInConstructor: false
|
callPrepareInConstructor: false
|
||||||
)
|
)
|
||||||
|
@ -229,10 +247,13 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRefreshUTXOCalledWithoutPrepareThrowsError() async throws {
|
func testRefreshUTXOCalledWithoutPrepareThrowsError() async throws {
|
||||||
|
// Pick a testnet height for which both Sapling and Orchard are active.
|
||||||
|
let walletBirthday = 1900000
|
||||||
|
|
||||||
let testCoordinator = try await TestCoordinator(
|
let testCoordinator = try await TestCoordinator(
|
||||||
alias: .default,
|
alias: .default,
|
||||||
container: mockContainer,
|
container: mockContainer,
|
||||||
walletBirthday: 10,
|
walletBirthday: walletBirthday,
|
||||||
network: network,
|
network: network,
|
||||||
callPrepareInConstructor: false
|
callPrepareInConstructor: false
|
||||||
)
|
)
|
||||||
|
@ -249,10 +270,13 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRewindCalledWithoutPrepareThrowsError() async throws {
|
func testRewindCalledWithoutPrepareThrowsError() async throws {
|
||||||
|
// Pick a testnet height for which both Sapling and Orchard are active.
|
||||||
|
let walletBirthday = 1900000
|
||||||
|
|
||||||
let testCoordinator = try await TestCoordinator(
|
let testCoordinator = try await TestCoordinator(
|
||||||
alias: .default,
|
alias: .default,
|
||||||
container: mockContainer,
|
container: mockContainer,
|
||||||
walletBirthday: 10,
|
walletBirthday: walletBirthday,
|
||||||
network: network,
|
network: network,
|
||||||
callPrepareInConstructor: false
|
callPrepareInConstructor: false
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,7 +32,7 @@ final class UnifiedTypecodesTests: XCTestCase {
|
||||||
|
|
||||||
let typecodes = try DerivationTool(networkType: .testnet).receiverTypecodesFromUnifiedAddress(address)
|
let typecodes = try DerivationTool(networkType: .testnet).receiverTypecodesFromUnifiedAddress(address)
|
||||||
|
|
||||||
XCTAssertEqual(typecodes, [.sapling, .p2pkh])
|
XCTAssertEqual(typecodes, [.orchard, .sapling, .p2pkh])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testUnifiedAddressHasTransparentSaplingReceivers() throws {
|
func testUnifiedAddressHasTransparentSaplingReceivers() throws {
|
||||||
|
@ -51,6 +51,7 @@ final class UnifiedTypecodesTests: XCTestCase {
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
Set<UnifiedAddress.ReceiverTypecodes>(typecodes),
|
Set<UnifiedAddress.ReceiverTypecodes>(typecodes),
|
||||||
Set([
|
Set([
|
||||||
|
.orchard,
|
||||||
.sapling,
|
.sapling,
|
||||||
.p2pkh
|
.p2pkh
|
||||||
])
|
])
|
||||||
|
|
|
@ -126,6 +126,11 @@ class ZcashRustBackendTests: XCTestCase {
|
||||||
XCTFail("not enough transparent receivers")
|
XCTFail("not enough transparent receivers")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The first address in the wallet is created when the account is created, using
|
||||||
|
// the default receivers specified inside `zcash_client_sqlite`. The remaining
|
||||||
|
// addresses are generated here, using the receivers specified in the Swift SDK's
|
||||||
|
// FFI backend.
|
||||||
var uAddresses: [UnifiedAddress] = []
|
var uAddresses: [UnifiedAddress] = []
|
||||||
for i in 0...2 {
|
for i in 0...2 {
|
||||||
uAddresses.append(
|
uAddresses.append(
|
||||||
|
|
|
@ -1800,27 +1800,27 @@ class SynchronizerMock: Synchronizer {
|
||||||
try await switchToEndpointClosure!(endpoint)
|
try await switchToEndpointClosure!(endpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - isSeedRelevantToWallet
|
// MARK: - isSeedRelevantToAnyDerivedAccount
|
||||||
|
|
||||||
var isSeedRelevantToWalletSeedThrowableError: Error?
|
var isSeedRelevantToAnyDerivedAccountSeedThrowableError: Error?
|
||||||
var isSeedRelevantToWalletSeedCallsCount = 0
|
var isSeedRelevantToAnyDerivedAccountSeedCallsCount = 0
|
||||||
var isSeedRelevantToWalletSeedCalled: Bool {
|
var isSeedRelevantToAnyDerivedAccountSeedCalled: Bool {
|
||||||
return isSeedRelevantToWalletSeedCallsCount > 0
|
return isSeedRelevantToAnyDerivedAccountSeedCallsCount > 0
|
||||||
}
|
}
|
||||||
var isSeedRelevantToWalletSeedReceivedSeed: [UInt8]?
|
var isSeedRelevantToAnyDerivedAccountSeedReceivedSeed: [UInt8]?
|
||||||
var isSeedRelevantToWalletSeedReturnValue: Bool!
|
var isSeedRelevantToAnyDerivedAccountSeedReturnValue: Bool!
|
||||||
var isSeedRelevantToWalletSeedClosure: (([UInt8]) async throws -> Bool)?
|
var isSeedRelevantToAnyDerivedAccountSeedClosure: (([UInt8]) async throws -> Bool)?
|
||||||
|
|
||||||
func isSeedRelevantToWallet(seed: [UInt8]) async throws -> Bool {
|
func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool {
|
||||||
if let error = isSeedRelevantToWalletSeedThrowableError {
|
if let error = isSeedRelevantToAnyDerivedAccountSeedThrowableError {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
isSeedRelevantToWalletSeedCallsCount += 1
|
isSeedRelevantToAnyDerivedAccountSeedCallsCount += 1
|
||||||
isSeedRelevantToWalletSeedReceivedSeed = seed
|
isSeedRelevantToAnyDerivedAccountSeedReceivedSeed = seed
|
||||||
if let closure = isSeedRelevantToWalletSeedClosure {
|
if let closure = isSeedRelevantToAnyDerivedAccountSeedClosure {
|
||||||
return try await closure(seed)
|
return try await closure(seed)
|
||||||
} else {
|
} else {
|
||||||
return isSeedRelevantToWalletSeedReturnValue
|
return isSeedRelevantToAnyDerivedAccountSeedReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2325,36 +2325,36 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - isSeedRelevantToWallet
|
// MARK: - isSeedRelevantToAnyDerivedAccount
|
||||||
|
|
||||||
var isSeedRelevantToWalletSeedThrowableError: Error?
|
var isSeedRelevantToAnyDerivedAccountSeedThrowableError: Error?
|
||||||
func setIsSeedRelevantToWalletSeedThrowableError(_ param: Error?) async {
|
func setIsSeedRelevantToAnyDerivedAccountSeedThrowableError(_ param: Error?) async {
|
||||||
isSeedRelevantToWalletSeedThrowableError = param
|
isSeedRelevantToAnyDerivedAccountSeedThrowableError = param
|
||||||
}
|
}
|
||||||
var isSeedRelevantToWalletSeedCallsCount = 0
|
var isSeedRelevantToAnyDerivedAccountSeedCallsCount = 0
|
||||||
var isSeedRelevantToWalletSeedCalled: Bool {
|
var isSeedRelevantToAnyDerivedAccountSeedCalled: Bool {
|
||||||
return isSeedRelevantToWalletSeedCallsCount > 0
|
return isSeedRelevantToAnyDerivedAccountSeedCallsCount > 0
|
||||||
}
|
}
|
||||||
var isSeedRelevantToWalletSeedReceivedSeed: [UInt8]?
|
var isSeedRelevantToAnyDerivedAccountSeedReceivedSeed: [UInt8]?
|
||||||
var isSeedRelevantToWalletSeedReturnValue: Bool!
|
var isSeedRelevantToAnyDerivedAccountSeedReturnValue: Bool!
|
||||||
func setIsSeedRelevantToWalletSeedReturnValue(_ param: Bool) async {
|
func setIsSeedRelevantToAnyDerivedAccountSeedReturnValue(_ param: Bool) async {
|
||||||
isSeedRelevantToWalletSeedReturnValue = param
|
isSeedRelevantToAnyDerivedAccountSeedReturnValue = param
|
||||||
}
|
}
|
||||||
var isSeedRelevantToWalletSeedClosure: (([UInt8]) async throws -> Bool)?
|
var isSeedRelevantToAnyDerivedAccountSeedClosure: (([UInt8]) async throws -> Bool)?
|
||||||
func setIsSeedRelevantToWalletSeedClosure(_ param: (([UInt8]) async throws -> Bool)?) async {
|
func setIsSeedRelevantToAnyDerivedAccountSeedClosure(_ param: (([UInt8]) async throws -> Bool)?) async {
|
||||||
isSeedRelevantToWalletSeedClosure = param
|
isSeedRelevantToAnyDerivedAccountSeedClosure = param
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSeedRelevantToWallet(seed: [UInt8]) async throws -> Bool {
|
func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool {
|
||||||
if let error = isSeedRelevantToWalletSeedThrowableError {
|
if let error = isSeedRelevantToAnyDerivedAccountSeedThrowableError {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
isSeedRelevantToWalletSeedCallsCount += 1
|
isSeedRelevantToAnyDerivedAccountSeedCallsCount += 1
|
||||||
isSeedRelevantToWalletSeedReceivedSeed = seed
|
isSeedRelevantToAnyDerivedAccountSeedReceivedSeed = seed
|
||||||
if let closure = isSeedRelevantToWalletSeedClosure {
|
if let closure = isSeedRelevantToAnyDerivedAccountSeedClosure {
|
||||||
return try await closure(seed)
|
return try await closure(seed)
|
||||||
} else {
|
} else {
|
||||||
return isSeedRelevantToWalletSeedReturnValue
|
return isSeedRelevantToAnyDerivedAccountSeedReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ extension SynchronizerState {
|
||||||
static var mock: SynchronizerState {
|
static var mock: SynchronizerState {
|
||||||
SynchronizerState(
|
SynchronizerState(
|
||||||
syncSessionID: .nullID,
|
syncSessionID: .nullID,
|
||||||
accountBalance: AccountBalance(saplingBalance: .zero, unshielded: Zatoshi(200)),
|
accountBalance: AccountBalance(saplingBalance: .zero, orchardBalance: .zero, unshielded: Zatoshi(200)),
|
||||||
internalSyncStatus: .syncing(0),
|
internalSyncStatus: .syncing(0),
|
||||||
latestBlockHeight: 222222
|
latestBlockHeight: 222222
|
||||||
)
|
)
|
||||||
|
|
|
@ -63,6 +63,8 @@ enum TestDbBuilder {
|
||||||
case .success: return provider
|
case .success: return provider
|
||||||
case .seedRequired:
|
case .seedRequired:
|
||||||
throw ZcashError.compactBlockProcessorDataDbInitFailed("Seed value required to initialize the wallet database")
|
throw ZcashError.compactBlockProcessorDataDbInitFailed("Seed value required to initialize the wallet database")
|
||||||
|
case .seedNotRelevant:
|
||||||
|
throw ZcashError.compactBlockProcessorDataDbInitFailed("Relevant seed value required")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -169,9 +169,9 @@ public let testVector: [[Any?]] =
|
||||||
[
|
[
|
||||||
["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/unified_address.py"],
|
["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/unified_address.py"],
|
||||||
["p2pkh_bytes, p2sh_bytes, sapling_raw_addr, orchard_raw_addr, unknown_typecode, unknown_bytes, unified_addr, root_seed, account, diversifier_index"],
|
["p2pkh_bytes, p2sh_bytes, sapling_raw_addr, orchard_raw_addr, unknown_typecode, unknown_bytes, unified_addr, root_seed, account, diversifier_index"],
|
||||||
["7bb83570b8fae146e03c5331a020b1e0892f631d", nil, "d8ef8293d26de832e7193f296ba1922d90f122c6135bc231eebd91efdb03b1a8606771cd4fd6480574d43e", nil, nil, nil, "u1l8xunezsvhq8fgzfl7404m450nwnd76zshscn6nfys7vyz2ywyh4cc5daaq0c7q2su5lqfh23sp7fkf3kt27ve5948mzpfdvckzaect2jtte308mkwlycj2u0eac077wu70vqcetkxf", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 0],
|
["7bb83570b8fae146e03c5331a020b1e0892f631d", nil, "d8ef8293d26de832e7193f296ba1922d90f122c6135bc231eebd91efdb03b1a8606771cd4fd6480574d43e", nil, nil, nil, "u1m2p65qdnhexpfcmejjf6hjqd485xwfsrlf3cvc6wx5xhwrcq8myrdlqyhdpklz5d87ct0epfty0cr9d6q9fqtycx0j3vdhc2tzmkeejhtdqj3zrjqk3dd5ufpqkmueg89e6a6alvpaaxcx4fxsxqk4yj7g8dayn94d3afrsx66m3vq4rk03hc0ufmtkcm8wca7xja42f9kjau9708z7", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 0],
|
||||||
["a7244a362f49f29644a955cf0039b88a61657861", nil, "435b0bbc95b5b7d52531a3944f2b85603ee22aaf850963bc156eb561edf2cbe7cf0e770e393ae5d7049026", nil, nil, nil, "u1fl5mprj0t9p4jg92hjjy8q5myvwc60c9wv0xachauqpn3c3k4xwzlaueafq27dcg7tzzzaz5jl8tyj93wgs983y0jq0qfhzu6n4r8rakpv5f4gg2lrw4z6pyqqcrcqx04d38yunc6je", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 3],
|
["a7244a362f49f29644a955cf0039b88a61657861", nil, "435b0bbc95b5b7d52531a3944f2b85603ee22aaf850963bc156eb561edf2cbe7cf0e770e393ae5d7049026", nil, nil, nil, "u196qfugjlzacex3pnhj5hgkj7ppjxeg3egcwkn364rnt59nx7p2f4pp35h4v677hla3negz4w0quy9ul6an2azqv0lt7f5n4dz5pspk0uvhkqfft4ntarat6mf0zfc46s5rsvmrd63lvnp2y8kyyes2r0k439de6je9ugy85qkq9ldc7hkkvjav69gzyfl5sen8kfxk3gyhjpzn39xj0", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 3],
|
||||||
["e256dcb03e05dde7c91212b47a7461311c415059", nil, "69a25a38699708e5f6e76e54e6a7a2ab84dcf288df0d1f2563670168d6c44ace0ef11155c60d5c225e9dec", nil, nil, nil, "u1qxqf8ctkxlsdh7xdcgkdtyw4mku7dxma8tsz45xd6ttgs322gdk7kazg3sdn52z7na3tzcrzf7lt3xrdtfp9d4pccderalchvvxk8hghduxrky5guzqlw65fmgp6x7aj4k8v5jkgwuw", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 4],
|
["e256dcb03e05dde7c91212b47a7461311c415059", nil, "69a25a38699708e5f6e76e54e6a7a2ab84dcf288df0d1f2563670168d6c44ace0ef11155c60d5c225e9dec", nil, nil, nil, "u1vs0kanmdm4vtp2jy5lfsfspty0l7umph3ny6v9xwqpamvw2adshzsh24ukvc2hamwd8wky7sfalzv4szttsrss26m5yvxnh2r0h9u5vtksr0puhw89h7gm202h5qhn53cm2pt9uj7cwnymmgp26jxe9c0lt5z3egvl03hmdrg6r923t5xudetg2f6kzzn5h6xhaye8wmufnguaj8pws", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 4],
|
||||||
["cad268758c5e71493066446b98e71df9d1d6a5ca", nil, "9f6e0bf90a18fc0b9b83ae9f23ad4358648638482b5def8975635b66fd8a708335f9235a3186ec0f033f84", "cecbe5e689a453a3fe10ccf7617e6c1fb382819d7fc9200a1f42092ac84a30378f8c1fb90dff71a6d5042d", nil, nil, "u1pg2aaph7jp8rpf6yhsza25722sg5fcn3vaca6ze27hqjw7jvvhhuxkpcg0ge9xh6drsgdkda8qjq5chpehkcpxf87rnjryjqwymdheptpvnljqqrjqzjwkc2ma6hcq666kgwfytxwac8eyex6ndgr6ezte66706e3vaqrd25dzvzkc69kw0jgywtd0cmq52q5lkw6uh7hyvzjse8ksx", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 1, 3],
|
["cad268758c5e71493066446b98e71df9d1d6a5ca", nil, "9f6e0bf90a18fc0b9b83ae9f23ad4358648638482b5def8975635b66fd8a708335f9235a3186ec0f033f84", "cecbe5e689a453a3fe10ccf7617e6c1fb382819d7fc9200a1f42092ac84a30378f8c1fb90dff71a6d5042d", nil, nil, "u1pg2aaph7jp8rpf6yhsza25722sg5fcn3vaca6ze27hqjw7jvvhhuxkpcg0ge9xh6drsgdkda8qjq5chpehkcpxf87rnjryjqwymdheptpvnljqqrjqzjwkc2ma6hcq666kgwfytxwac8eyex6ndgr6ezte66706e3vaqrd25dzvzkc69kw0jgywtd0cmq52q5lkw6uh7hyvzjse8ksx", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 1, 3],
|
||||||
["8d653347a0fd3cd0842a790a5eaf89d8e3854659", nil, "e1adf156a07d56bcac91bdb2f7bb3ea7c44569dcfee54273c09e8065807b6823faa94a77219554d0f6e017", "24f8a60cbd97e012618d56054ad39241411a28fdd50ee35efa91152f60d5fa21172e5d458ddbcb6b709896", nil, nil, "u19mzuf4l37ny393m59v4mxx4t3uyxkh7qpqjdfvlfk9f504cv9w4fpl7cql0kqvssz8jay8mgl8lnrtvg6yzh9pranjj963acc3h2z2qt7007du0lsmdf862dyy40c3wmt0kq35k5z836tfljgzsqtdsccchayfjpygqzkx24l77ga3ngfgskqddyepz8we7ny4ggmt7q48cgvgu57mz", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 1, 7],
|
["8d653347a0fd3cd0842a790a5eaf89d8e3854659", nil, "e1adf156a07d56bcac91bdb2f7bb3ea7c44569dcfee54273c09e8065807b6823faa94a77219554d0f6e017", "24f8a60cbd97e012618d56054ad39241411a28fdd50ee35efa91152f60d5fa21172e5d458ddbcb6b709896", nil, nil, "u19mzuf4l37ny393m59v4mxx4t3uyxkh7qpqjdfvlfk9f504cv9w4fpl7cql0kqvssz8jay8mgl8lnrtvg6yzh9pranjj963acc3h2z2qt7007du0lsmdf862dyy40c3wmt0kq35k5z836tfljgzsqtdsccchayfjpygqzkx24l77ga3ngfgskqddyepz8we7ny4ggmt7q48cgvgu57mz", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 1, 7],
|
||||||
["e511f439b5f96cf824cd5e0e6b2eb8ee1bc83cb7", nil, "60ba572f8e379312d86897025decdd64b4b95e2c4afa9d13726b8cc393edb4988c51b976028f890f108bd2", "1f24294ed1b405c7b3b1c3f13db5b9b27b5d0f2aca9d589a69e5be00eb978621e6776e87ea326d47a34c1a", nil, nil, "u1mtxw5nras5glkxz093282sv3n2h8qs7cpxcmmaxj96vtzjzl6rmdaxs4e9es7mxwmd0h3k5wz3ce4ll5g4jz2pn9su4pufq74pxhp4t235n6j7aed3hh8ss7pf3sekf7apsf6vtg84ue5zcq2k9q3xv5yth3q50fu4czdm8sn8q4de3m5k76g2vwwyjsf50hqfxgmwxqxu0rsy22ktw", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 1, 8],
|
["e511f439b5f96cf824cd5e0e6b2eb8ee1bc83cb7", nil, "60ba572f8e379312d86897025decdd64b4b95e2c4afa9d13726b8cc393edb4988c51b976028f890f108bd2", "1f24294ed1b405c7b3b1c3f13db5b9b27b5d0f2aca9d589a69e5be00eb978621e6776e87ea326d47a34c1a", nil, nil, "u1mtxw5nras5glkxz093282sv3n2h8qs7cpxcmmaxj96vtzjzl6rmdaxs4e9es7mxwmd0h3k5wz3ce4ll5g4jz2pn9su4pufq74pxhp4t235n6j7aed3hh8ss7pf3sekf7apsf6vtg84ue5zcq2k9q3xv5yth3q50fu4czdm8sn8q4de3m5k76g2vwwyjsf50hqfxgmwxqxu0rsy22ktw", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 1, 8],
|
||||||
|
|
Loading…
Reference in New Issue