Merge branch 'feature/zip-316-and-latest-upstream' into merge_master_to_zip_316
This commit is contained in:
commit
4428f9ffb2
|
@ -3,7 +3,7 @@ os: osx
|
|||
osx_image: xcode13.4
|
||||
xcode_project: ./Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj
|
||||
xcode_scheme: ZcashLightClientSample
|
||||
xcode_destination: platform=iOS Simulator,OS=15.2,name=iPhone 8
|
||||
xcode_destination: platform=iOS Simulator,OS=15.5,name=iPhone 8
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
"repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state": {
|
||||
"branch": "bin/librustzcash_0_7",
|
||||
"revision": "823f864a7952073fb9718cf75610691756e34d59",
|
||||
"revision": "61534023777235cc5b76d4ec44e27edc1658eea6",
|
||||
"version": null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
|
||||
import Foundation
|
||||
import SQLite
|
||||
struct PendingTransaction: PendingTransactionEntity, Codable {
|
||||
struct PendingTransaction: PendingTransactionEntity, Decodable, Encodable {
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case toAddress = "to_address"
|
||||
case accountIndex = "account_index"
|
||||
|
@ -57,7 +58,7 @@ struct PendingTransaction: PendingTransactionEntity, Codable {
|
|||
raw: entity.raw,
|
||||
id: entity.id,
|
||||
value: entity.value,
|
||||
memo: entity.memo,
|
||||
memo: entity.memo == nil ? Data(MemoBytes.empty().bytes) : entity.memo,
|
||||
rawTransactionId: entity.raw
|
||||
)
|
||||
}
|
||||
|
@ -144,8 +145,7 @@ struct PendingTransaction: PendingTransactionEntity, Codable {
|
|||
}
|
||||
|
||||
extension PendingTransaction {
|
||||
// TODO: Handle Memo
|
||||
init(value: Zatoshi, toAddress: String, memo: String?, account index: Int) {
|
||||
init(value: Zatoshi, toAddress: String, memo: MemoBytes, account index: Int) {
|
||||
self = PendingTransaction(
|
||||
toAddress: toAddress,
|
||||
accountIndex: index,
|
||||
|
@ -160,7 +160,7 @@ extension PendingTransaction {
|
|||
raw: nil,
|
||||
id: nil,
|
||||
value: value,
|
||||
memo: memo?.encodeAsZcashTransactionMemo(),
|
||||
memo: Data(memo.bytes),
|
||||
rawTransactionId: nil
|
||||
)
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@ public enum Memo: Equatable {
|
|||
case arbitrary([UInt8])
|
||||
|
||||
/// Parses the given bytes as in ZIP-302
|
||||
public init?(bytes: [UInt8]) throws {
|
||||
public init(bytes: [UInt8]) throws {
|
||||
self = try MemoBytes(bytes: bytes).intoMemo()
|
||||
}
|
||||
|
||||
/// Converts these memo bytes into a ZIP-302 Memo
|
||||
public init?(memoBytes: MemoBytes) throws {
|
||||
public init(memoBytes: MemoBytes) throws {
|
||||
self = try memoBytes.intoMemo()
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ public enum Memo: Equatable {
|
|||
/// - Throws:
|
||||
/// - `MemoBytes.Errors.tooLong(length)` if the UTF-8 length
|
||||
/// of this string is greater than `MemoBytes.capacity` (512 bytes)
|
||||
public init?(string: String) throws {
|
||||
public init(string: String) throws {
|
||||
self = .text(try MemoText(String(string.utf8)))
|
||||
}
|
||||
}
|
||||
|
@ -60,12 +60,14 @@ public struct MemoText: Equatable {
|
|||
public private(set) var string: String
|
||||
|
||||
init(_ string: String) throws {
|
||||
guard string.utf8.count <= MemoBytes.capacity else {
|
||||
throw MemoBytes.Errors.tooLong(string.utf8.count)
|
||||
let trimmedString = String(string.reversed().drop(while: { $0 == "\u{0}"}).reversed())
|
||||
|
||||
guard trimmedString.count == string.count else {
|
||||
throw MemoBytes.Errors.endsWithNullBytes
|
||||
}
|
||||
|
||||
guard !string.containsCStringNullBytesBeforeStringEnding() else {
|
||||
throw MemoBytes.Errors.invalidUTF8
|
||||
guard string.utf8.count <= MemoBytes.capacity else {
|
||||
throw MemoBytes.Errors.tooLong(string.utf8.count)
|
||||
}
|
||||
|
||||
self.string = string
|
||||
|
@ -74,7 +76,11 @@ public struct MemoText: Equatable {
|
|||
|
||||
public struct MemoBytes: Equatable {
|
||||
public enum Errors: Error {
|
||||
/// Invalid UTF-8 Bytes where detected when attempting to create a Text Memo
|
||||
case invalidUTF8
|
||||
/// Trailing null-bytes were found when attempting to create a Text memo
|
||||
case endsWithNullBytes
|
||||
/// the resulting bytes provided are too long to be stored as a Memo in any of its forms.
|
||||
case tooLong(Int)
|
||||
}
|
||||
|
||||
|
@ -100,6 +106,18 @@ public struct MemoBytes: Equatable {
|
|||
self.bytes = rawBytes
|
||||
}
|
||||
|
||||
init(contiguousBytes: ContiguousArray<UInt8>) throws {
|
||||
guard contiguousBytes.capacity <= Self.capacity else { throw Errors.tooLong(contiguousBytes.capacity) }
|
||||
|
||||
var rawBytes = [UInt8](repeating: 0x0, count: Self.capacity)
|
||||
|
||||
_ = contiguousBytes.withUnsafeBufferPointer { ptr in
|
||||
memmove(&rawBytes[0], ptr.baseAddress, ptr.count)
|
||||
}
|
||||
|
||||
self.bytes = rawBytes
|
||||
}
|
||||
|
||||
public static func empty() -> Self {
|
||||
try! Self(bytes: .emptyMemoBytes)
|
||||
}
|
||||
|
@ -108,6 +126,8 @@ public struct MemoBytes: Equatable {
|
|||
extension MemoBytes.Errors: LocalizedError {
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .endsWithNullBytes:
|
||||
return "MemoBytes.Errors.endsWithNullBytes: The UTF-8 bytes provided have trailing null-bytes."
|
||||
case .invalidUTF8:
|
||||
return "MemoBytes.Errors.invalidUTF8: Invalid UTF-8 byte found on memo bytes"
|
||||
case .tooLong(let length):
|
||||
|
@ -161,12 +181,7 @@ public extension MemoBytes {
|
|||
extension MemoBytes {
|
||||
/// Returns raw bytes, excluding null padding
|
||||
func unpaddedRawBytes() -> [UInt8] {
|
||||
guard let firstNullByte = self.bytes.enumerated()
|
||||
.reversed()
|
||||
.first(where: { $0.1 != 0 })
|
||||
.map({ $0.0 + 1 }) else { return [UInt8](bytes[0 ... 1]) }
|
||||
|
||||
return [UInt8](bytes[0 ... firstNullByte])
|
||||
self.bytes.unpaddedRawBytes()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,6 +191,17 @@ extension Array where Element == UInt8 {
|
|||
emptyMemo[0] = 0xF6
|
||||
return emptyMemo
|
||||
}
|
||||
|
||||
func unpaddedRawBytes() -> [UInt8] {
|
||||
guard let lastNullByte = self.enumerated()
|
||||
.reversed()
|
||||
.first(where: { $0.1 != 0 })
|
||||
.map({ $0.0 + 1 }) else {
|
||||
return [UInt8](self[0 ..< 1])
|
||||
}
|
||||
|
||||
return [UInt8](self[0 ..< lastNullByte])
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
|
@ -187,3 +213,25 @@ extension String {
|
|||
self = s
|
||||
}
|
||||
}
|
||||
|
||||
extension Optional where WrappedType == String {
|
||||
func intoMemo() throws -> Memo {
|
||||
switch self {
|
||||
case .none:
|
||||
return .empty
|
||||
case .some(let string):
|
||||
return try Memo(string: string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Optional where WrappedType == Data {
|
||||
func intoMemoBytes() throws -> MemoBytes {
|
||||
switch self {
|
||||
case .none:
|
||||
return .empty()
|
||||
case .some(let data):
|
||||
return try .init(bytes: data.bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "1686871",
|
||||
"hash": "0000000000443d2be0b66f0f2c03784cf267e50935562a23c10d9d3841343cd7",
|
||||
"time": 1654001768,
|
||||
"saplingTree": "01802ef6c171eca37050fbc6cdf8d9712a8c5e4650af1a31300c143e063a680e2100140000000001e684c306416b750c7b4883e145922c129e5919a658b02e6146d2da8d17a3ff42000001ae61a98ec05d976a41e46f0b7ae8dee5bda5ab490eea7b701153511a15be42590198c748a5e9516711db607e282f08013ba00b3e77bc66ec28702ca17893a5154b000139113a1e0f54d93c6180f2df7d16afbf7c320f6c7665cb10e1fc309878b0d716019a0214bcb1fd7a70e53166101992147512f3debb0fdce45ac625fffd64771010000134136b9f1f00c2e9d16dc7358ef920862511c8fc42fb7074cbc9016d8d4e8b4c015eddc191a81221b7900bbdcd8610e5df595e3cdc7fd5b3432e3825206ae35b05017eda713cd733ccc555123788692a1876f9ca292b0aa2ddf3f45ed2b47f027340000000015ec9e9b1295908beed437df4126032ca57ada8e3ebb67067cd22a73c79a84009"
|
||||
}
|
|
@ -359,25 +359,74 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
return WalletBalance(verified: Zatoshi(verified), total: Zatoshi(total))
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This function will be deprecated soon. Use `getReceivedMemo(dbData:idNote:networkType)` instead")
|
||||
static func getReceivedMemoAsUTF8(dbData: URL, idNote: Int64, networkType: NetworkType) -> String? {
|
||||
let dbData = dbData.osStr()
|
||||
|
||||
guard let memoCStr = zcashlc_get_received_memo_as_utf8(dbData.0, dbData.1, idNote, networkType.networkId) else { return nil }
|
||||
|
||||
let memo = String(validatingUTF8: memoCStr)
|
||||
zcashlc_string_free(memoCStr)
|
||||
return memo
|
||||
defer {
|
||||
zcashlc_string_free(memoCStr)
|
||||
}
|
||||
|
||||
return String(validatingUTF8: memoCStr)
|
||||
}
|
||||
|
||||
static func getSentMemoAsUTF8(dbData: URL, idNote: Int64, networkType: NetworkType) -> String? {
|
||||
|
||||
@available(*, deprecated, message: "This function will be deprecated soon. Use `getSentMemo(dbData:idNote:networkType)` instead")
|
||||
static func getSentMemoAsUTF8(
|
||||
dbData: URL,
|
||||
idNote: Int64,
|
||||
networkType: NetworkType
|
||||
) -> String? {
|
||||
let dbData = dbData.osStr()
|
||||
|
||||
guard let memoCStr = zcashlc_get_sent_memo_as_utf8(dbData.0, dbData.1, idNote, networkType.networkId) else { return nil }
|
||||
|
||||
let memo = String(validatingUTF8: memoCStr)
|
||||
zcashlc_string_free(memoCStr)
|
||||
return memo
|
||||
defer {
|
||||
zcashlc_string_free(memoCStr)
|
||||
}
|
||||
|
||||
return String(validatingUTF8: memoCStr)
|
||||
}
|
||||
|
||||
static func getSentMemo(
|
||||
dbData: URL,
|
||||
idNote: Int64,
|
||||
networkType: NetworkType
|
||||
) -> Memo? {
|
||||
let dbData = dbData.osStr()
|
||||
|
||||
var contiguousMemoBytes = ContiguousArray<UInt8>(MemoBytes.empty().bytes)
|
||||
var success = false
|
||||
|
||||
contiguousMemoBytes.withUnsafeMutableBytes{ memoBytePtr in
|
||||
success = zcashlc_get_sent_memo(dbData.0, dbData.1, idNote, memoBytePtr.baseAddress, networkType.networkId)
|
||||
}
|
||||
|
||||
guard success else { return nil }
|
||||
|
||||
return (try? MemoBytes(contiguousBytes: contiguousMemoBytes)).flatMap { try? $0.intoMemo() }
|
||||
}
|
||||
|
||||
static func getReceivedMemo(
|
||||
dbData: URL,
|
||||
idNote: Int64,
|
||||
networkType: NetworkType
|
||||
) -> Memo? {
|
||||
let dbData = dbData.osStr()
|
||||
|
||||
var contiguousMemoBytes = ContiguousArray<UInt8>(MemoBytes.empty().bytes)
|
||||
var success = false
|
||||
|
||||
contiguousMemoBytes.withUnsafeMutableBufferPointer { memoBytePtr in
|
||||
success = zcashlc_get_received_memo(dbData.0, dbData.1, idNote, memoBytePtr.baseAddress, networkType.networkId)
|
||||
}
|
||||
|
||||
guard success else { return nil }
|
||||
|
||||
return (try? MemoBytes(contiguousBytes: contiguousMemoBytes)).flatMap { try? $0.intoMemo() }
|
||||
}
|
||||
|
||||
|
||||
static func validateCombinedChain(dbCache: URL, dbData: URL, networkType: NetworkType) -> Int32 {
|
||||
let dbCache = dbCache.osStr()
|
||||
|
@ -420,13 +469,12 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
extsk: String,
|
||||
to address: String,
|
||||
value: Int64,
|
||||
memo: String?,
|
||||
memo: MemoBytes,
|
||||
spendParamsPath: String,
|
||||
outputParamsPath: String,
|
||||
networkType: NetworkType
|
||||
) -> Int64 {
|
||||
let dbData = dbData.osStr()
|
||||
let memoBytes = memo ?? ""
|
||||
|
||||
return zcashlc_create_to_address(
|
||||
dbData.0,
|
||||
|
@ -435,7 +483,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
[CChar](extsk.utf8CString),
|
||||
[CChar](address.utf8CString),
|
||||
value,
|
||||
[CChar](memoBytes.utf8CString),
|
||||
memo.bytes,
|
||||
spendParamsPath,
|
||||
UInt(spendParamsPath.lengthOfBytes(using: .utf8)),
|
||||
outputParamsPath,
|
||||
|
@ -450,20 +498,19 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
dbData: URL,
|
||||
account: Int32,
|
||||
xprv: String,
|
||||
memo: String?,
|
||||
memo: MemoBytes,
|
||||
spendParamsPath: String,
|
||||
outputParamsPath: String,
|
||||
networkType: NetworkType
|
||||
) -> Int64 {
|
||||
let dbData = dbData.osStr()
|
||||
let memoBytes = memo ?? ""
|
||||
|
||||
return zcashlc_shield_funds(
|
||||
dbData.0,
|
||||
dbData.1,
|
||||
account,
|
||||
[CChar](xprv.utf8CString),
|
||||
[CChar](memoBytes.utf8CString),
|
||||
memo.bytes,
|
||||
spendParamsPath,
|
||||
UInt(spendParamsPath.lengthOfBytes(using: .utf8)),
|
||||
outputParamsPath,
|
||||
|
|
|
@ -160,7 +160,16 @@ protocol ZcashRustBackendWelding {
|
|||
- dbData: location of the data db file
|
||||
- idNote: note_id of note where the memo is located
|
||||
*/
|
||||
@available(*, deprecated, message: "This function will be deprecated soon. Use `getReceivedMemo(dbData:idNote:networkType)` instead")
|
||||
static func getReceivedMemoAsUTF8(dbData: URL, idNote: Int64, networkType: NetworkType) -> String?
|
||||
|
||||
/**
|
||||
get received memo from note
|
||||
- Parameters:
|
||||
- dbData: location of the data db file
|
||||
- idNote: note_id of note where the memo is located
|
||||
*/
|
||||
static func getReceivedMemo(dbData: URL, idNote: Int64, networkType: NetworkType) -> Memo?
|
||||
|
||||
/**
|
||||
get sent memo from note
|
||||
|
@ -168,7 +177,16 @@ protocol ZcashRustBackendWelding {
|
|||
- dbData: location of the data db file
|
||||
- idNote: note_id of note where the memo is located
|
||||
*/
|
||||
@available(*, deprecated, message: "This function will be deprecated soon. Use `getSentMemo(dbData:idNote:networkType)` instead")
|
||||
static func getSentMemoAsUTF8(dbData: URL, idNote: Int64, networkType: NetworkType) -> String?
|
||||
|
||||
/**
|
||||
get sent memo from note
|
||||
- Parameters:
|
||||
- dbData: location of the data db file
|
||||
- idNote: note_id of note where the memo is located
|
||||
*/
|
||||
static func getSentMemo(dbData: URL, idNote: Int64, networkType: NetworkType) -> Memo?
|
||||
|
||||
/**
|
||||
Checks that the scanned blocks in the data database, when combined with the recent
|
||||
|
@ -301,7 +319,7 @@ protocol ZcashRustBackendWelding {
|
|||
extsk: String,
|
||||
to address: String,
|
||||
value: Int64,
|
||||
memo: String?,
|
||||
memo: MemoBytes,
|
||||
spendParamsPath: String,
|
||||
outputParamsPath: String,
|
||||
networkType: NetworkType
|
||||
|
@ -324,7 +342,7 @@ protocol ZcashRustBackendWelding {
|
|||
dbData: URL,
|
||||
account: Int32,
|
||||
xprv: String,
|
||||
memo: String?,
|
||||
memo: MemoBytes,
|
||||
spendParamsPath: String,
|
||||
outputParamsPath: String,
|
||||
networkType: NetworkType
|
||||
|
|
|
@ -76,7 +76,6 @@ public protocol Synchronizer {
|
|||
|
||||
/// reflects current connection state to LightwalletEndpoint
|
||||
var connectionState: ConnectionState { get }
|
||||
|
||||
|
||||
/// prepares this initializer to operate. Initializes the internal state with the given
|
||||
/// Extended Viewing Keys and a wallet birthday found in the initializer object
|
||||
|
@ -90,13 +89,11 @@ public protocol Synchronizer {
|
|||
|
||||
/// Stop this synchronizer. Implementations should ensure that calling this method cancels all jobs that were created by this instance.
|
||||
func stop() throws
|
||||
|
||||
|
||||
/// Gets the sapling shielded address for the given account.
|
||||
/// - Parameter accountIndex: the optional accountId whose address is of interest. By default, the first account is used.
|
||||
/// - Returns the address or nil if account index is incorrect
|
||||
func getSaplingAddress(accountIndex: Int) -> SaplingAddress?
|
||||
|
||||
|
||||
/// Gets the unified address for the given account.
|
||||
/// - Parameter accountIndex: the optional accountId whose address is of interest. By default, the first account is used.
|
||||
|
@ -108,36 +105,18 @@ public protocol Synchronizer {
|
|||
/// - Returns the address or nil if account index is incorrect
|
||||
func getTransparentAddress(accountIndex: Int) -> TransparentAddress?
|
||||
|
||||
/// Sends zatoshi.
|
||||
/// - Parameter spendingKey: the key that allows spends to occur.
|
||||
/// - Parameter zatoshi: the amount of zatoshi to send.
|
||||
/// - Parameter toAddress: the recipient's address.
|
||||
/// - Parameter memo: the optional memo to include as part of the transaction.
|
||||
/// - Parameter accountIndex: the optional account id to use. By default, the first account is used.
|
||||
@available(*, deprecated, message: "This function will be removed soon, use the one reveiving a `Zatoshi` value instead")
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
func sendToAddress(
|
||||
spendingKey: String,
|
||||
zatoshi: Int64,
|
||||
toAddress: String,
|
||||
memo: String?,
|
||||
from accountIndex: Int,
|
||||
resultBlock: @escaping (_ result: Result<PendingTransactionEntity, Error>) -> Void
|
||||
)
|
||||
|
||||
|
||||
/// Sends zatoshi.
|
||||
/// - Parameter spendingKey: the key that allows spends to occur.
|
||||
/// - Parameter zatoshi: the amount to send in Zatoshi.
|
||||
/// - Parameter toAddress: the recipient's address.
|
||||
/// - Parameter memo: the optional memo to include as part of the transaction.
|
||||
/// - Parameter memo: the memo to include as part of the transaction.
|
||||
/// - Parameter accountIndex: the optional account id to use. By default, the first account is used.
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
func sendToAddress(
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
toAddress: Recipient,
|
||||
memo: String?,
|
||||
memo: Memo,
|
||||
from accountIndex: Int,
|
||||
resultBlock: @escaping (_ result: Result<PendingTransactionEntity, Error>) -> Void
|
||||
)
|
||||
|
@ -148,7 +127,7 @@ public protocol Synchronizer {
|
|||
/// - Parameter accountIndex: the optional account id that will be used to shield your funds to. By default, the first account is used.
|
||||
func shieldFunds(
|
||||
transparentAccountPrivateKey: TransparentAccountPrivKey,
|
||||
memo: String?,
|
||||
memo: Memo,
|
||||
from accountIndex: Int,
|
||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
||||
)
|
||||
|
@ -194,14 +173,12 @@ public protocol Synchronizer {
|
|||
/// Returns the latest block height from the provided Lightwallet endpoint
|
||||
/// Blocking
|
||||
func latestHeight() throws -> BlockHeight
|
||||
|
||||
|
||||
/// Returns the latests UTXOs for the given address from the specified height on
|
||||
func refreshUTXOs(address: String, from height: BlockHeight, result: @escaping (Result<RefreshedUTXOs, Error>) -> Void)
|
||||
|
||||
/// Returns the last stored transparent balance
|
||||
func getTransparentBalance(accountIndex: Int) throws -> WalletBalance
|
||||
|
||||
|
||||
/// Returns the shielded total balance (includes verified and unverified balance)
|
||||
@available(*, deprecated, message: "This function will be removed soon, use the one returning a `Zatoshi` value instead")
|
||||
|
@ -237,23 +214,19 @@ public enum SyncStatus: Equatable {
|
|||
/// Indicates that this Synchronizer is actively downloading new blocks from the server.
|
||||
case downloading(_ status: BlockProgress)
|
||||
|
||||
|
||||
/// Indicates that this Synchronizer is actively validating new blocks that were downloaded
|
||||
/// from the server. Blocks need to be verified before they are scanned. This confirms that
|
||||
/// each block is chain-sequential, thereby detecting missing blocks and reorgs.
|
||||
case validating
|
||||
|
||||
|
||||
/// Indicates that this Synchronizer is actively scanning new valid blocks that were
|
||||
/// downloaded from the server.
|
||||
case scanning(_ progress: BlockProgress)
|
||||
|
||||
|
||||
/// Indicates that this Synchronizer is actively enhancing newly scanned blocks
|
||||
/// with additional transaction details, fetched from the server.
|
||||
case enhancing(_ progress: EnhancementProgress)
|
||||
|
||||
|
||||
/// fetches the transparent balance and stores it locally
|
||||
case fetching
|
||||
|
||||
|
@ -261,11 +234,9 @@ public enum SyncStatus: Equatable {
|
|||
/// When set, a UI element may want to turn green.
|
||||
case synced
|
||||
|
||||
|
||||
/// Indicates that [stop] has been called on this Synchronizer and it will no longer be used.
|
||||
case stopped
|
||||
|
||||
|
||||
/// Indicates that this Synchronizer is disconnected from its lightwalletd server.
|
||||
/// When set, a UI element may want to turn red.
|
||||
case disconnected
|
||||
|
@ -289,7 +260,6 @@ public enum SyncStatus: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Kind of transactions handled by a Synchronizer
|
||||
public enum TransactionKind {
|
||||
case sent
|
||||
|
@ -297,7 +267,6 @@ public enum TransactionKind {
|
|||
case all
|
||||
}
|
||||
|
||||
|
||||
/// Type of rewind available
|
||||
/// -birthday: rewinds the local state to this wallet's birthday
|
||||
/// -height: rewinds to the nearest blockheight to the one given as argument.
|
||||
|
|
|
@ -458,35 +458,12 @@ public class SDKSynchronizer: Synchronizer {
|
|||
|
||||
// MARK: Synchronizer methods
|
||||
|
||||
@available(*, deprecated, message: "This function will be removed soon, use the one reveiving a `Zatoshi` value instead")
|
||||
public func sendToAddress(
|
||||
spendingKey: String,
|
||||
zatoshi: Int64,
|
||||
toAddress: String,
|
||||
memo: String?,
|
||||
from accountIndex: Int,
|
||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
||||
) {
|
||||
do {
|
||||
sendToAddress(
|
||||
spendingKey: try SaplingExtendedSpendingKey(encoding: spendingKey, network: network.networkType),
|
||||
zatoshi: Zatoshi(zatoshi),
|
||||
toAddress: try Recipient(toAddress, network: network.networkType),
|
||||
memo: memo,
|
||||
from: accountIndex,
|
||||
resultBlock: resultBlock
|
||||
)
|
||||
} catch {
|
||||
resultBlock(.failure(SynchronizerError.invalidAccount))
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
public func sendToAddress(
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
toAddress: Recipient,
|
||||
memo: String?,
|
||||
memo: Memo,
|
||||
from accountIndex: Int,
|
||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
||||
) {
|
||||
|
@ -508,16 +485,16 @@ public class SDKSynchronizer: Synchronizer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func shieldFunds(
|
||||
transparentAccountPrivateKey: TransparentAccountPrivKey,
|
||||
memo: String?,
|
||||
memo: Memo,
|
||||
from accountIndex: Int,
|
||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
||||
) {
|
||||
// let's see if there are funds to shield
|
||||
let derivationTool = DerivationTool(networkType: self.network.networkType)
|
||||
|
||||
|
||||
do {
|
||||
let tAddr = try derivationTool.deriveTransparentAddressFromAccountPrivateKey(transparentAccountPrivateKey, index: 0)
|
||||
|
||||
|
@ -536,7 +513,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
return
|
||||
}
|
||||
|
||||
let shieldingSpend = try transactionManager.initSpend(zatoshi: tBalance.verified, toAddress: uAddr.stringEncoded, memo: memo, from: accountIndex)
|
||||
let shieldingSpend = try transactionManager.initSpend(zatoshi: tBalance.verified, toAddress: uAddr.stringEncoded, memo: try memo.asMemoBytes(), from: accountIndex)
|
||||
|
||||
transactionManager.encodeShieldingTransaction(
|
||||
xprv: transparentAccountPrivateKey,
|
||||
|
@ -558,12 +535,11 @@ public class SDKSynchronizer: Synchronizer {
|
|||
}
|
||||
|
||||
case .failure(let error):
|
||||
resultBlock(.failure(error))
|
||||
resultBlock(.failure(SynchronizerError.uncategorized(underlyingError: error)))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
resultBlock(.failure(error))
|
||||
return
|
||||
resultBlock(.failure(SynchronizerError.uncategorized(underlyingError: error)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -572,7 +548,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
toAddress: String,
|
||||
memo: String?,
|
||||
memo: Memo,
|
||||
from accountIndex: Int,
|
||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
||||
) {
|
||||
|
@ -580,7 +556,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
let spend = try transactionManager.initSpend(
|
||||
zatoshi: zatoshi,
|
||||
toAddress: toAddress,
|
||||
memo: memo,
|
||||
memo: memo.asMemoBytes(),
|
||||
from: accountIndex
|
||||
)
|
||||
|
||||
|
@ -600,11 +576,11 @@ public class SDKSynchronizer: Synchronizer {
|
|||
}
|
||||
|
||||
case .failure(let error):
|
||||
resultBlock(.failure(error))
|
||||
resultBlock(.failure(SynchronizerError.uncategorized(underlyingError: error)))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
resultBlock(.failure(error))
|
||||
resultBlock(.failure(SynchronizerError.uncategorized(underlyingError: error)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ enum TransactionManagerError: Error {
|
|||
}
|
||||
|
||||
class PersistentTransactionManager: OutboundTransactionManager {
|
||||
|
||||
var repository: PendingTransactionRepository
|
||||
var encoder: TransactionEncoder
|
||||
var service: LightWalletService
|
||||
|
@ -41,7 +42,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
func initSpend(
|
||||
zatoshi: Zatoshi,
|
||||
toAddress: String,
|
||||
memo: String?,
|
||||
memo: MemoBytes,
|
||||
from accountIndex: Int
|
||||
) throws -> PendingTransactionEntity {
|
||||
guard let insertedTx = try repository.find(
|
||||
|
@ -75,7 +76,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
do {
|
||||
let encodedTransaction = try self.encoder.createShieldingTransaction(
|
||||
tAccountPrivateKey: xprv,
|
||||
memo: pendingTransaction.memo?.asZcashTransactionMemo(),
|
||||
memoBytes: try pendingTransaction.memo.intoMemoBytes(),
|
||||
from: pendingTransaction.accountIndex
|
||||
)
|
||||
let transaction = try self.encoder.expandEncodedTransaction(encodedTransaction)
|
||||
|
@ -94,6 +95,14 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
DispatchQueue.main.async {
|
||||
result(.failure(TransactionManagerError.updateFailed(pendingTransaction)))
|
||||
}
|
||||
} catch MemoBytes.Errors.invalidUTF8 {
|
||||
DispatchQueue.main.async {
|
||||
result(.failure(TransactionManagerError.shieldingEncodingFailed(pendingTransaction, reason: "Memo contains invalid UTF-8 bytes")))
|
||||
}
|
||||
} catch MemoBytes.Errors.tooLong(let length) {
|
||||
DispatchQueue.main.async {
|
||||
result(.failure(TransactionManagerError.shieldingEncodingFailed(pendingTransaction, reason: "Memo is too long. expected 512 bytes, received \(length)")))
|
||||
}
|
||||
} catch {
|
||||
DispatchQueue.main.async {
|
||||
result(.failure(error))
|
||||
|
@ -109,14 +118,17 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
) {
|
||||
queue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
do {
|
||||
|
||||
let encodedTransaction = try self.encoder.createTransaction(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: pendingTransaction.value,
|
||||
to: pendingTransaction.toAddress,
|
||||
memo: pendingTransaction.memo?.asZcashTransactionMemo(),
|
||||
memoBytes: try pendingTransaction.memo.intoMemoBytes(),
|
||||
from: pendingTransaction.accountIndex
|
||||
)
|
||||
|
||||
let transaction = try self.encoder.expandEncodedTransaction(encodedTransaction)
|
||||
|
||||
var pending = pendingTransaction
|
||||
|
|
|
@ -28,7 +28,7 @@ protocol TransactionEncoder {
|
|||
/// - Parameter spendingKey: a `SaplingExtendedSpendingKey` containing the spending key
|
||||
/// - Parameter zatoshi: the amount to send in `Zatoshi`
|
||||
/// - Parameter to: string containing the recipient address
|
||||
/// - Parameter memo: string containing the memo (optional)
|
||||
/// - Parameter memoBytes: MemoBytes for this transaction
|
||||
/// - Parameter accountIndex: index of the account that will be used to send the funds
|
||||
///
|
||||
/// - Throws: a TransactionEncoderError
|
||||
|
@ -36,7 +36,7 @@ protocol TransactionEncoder {
|
|||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memo: String?,
|
||||
memoBytes: MemoBytes,
|
||||
from accountIndex: Int
|
||||
) throws -> EncodedTransaction
|
||||
|
||||
|
@ -50,7 +50,7 @@ protocol TransactionEncoder {
|
|||
/// - Parameter spendingKey: a `SaplingExtendedSpendingKey` containing the spending key
|
||||
/// - Parameter zatoshi: the amount to send in `Zatoshi`
|
||||
/// - Parameter to: string containing the recipient address
|
||||
/// - Parameter memo: string containing the memo (optional)
|
||||
/// - Parameter MemoBytes: string containing the memo (optional)
|
||||
/// - Parameter accountIndex: index of the account that will be used to send the funds
|
||||
/// - Parameter result: a non escaping closure that receives a Result containing either an EncodedTransaction or a /// TransactionEncoderError
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
|
@ -58,7 +58,7 @@ protocol TransactionEncoder {
|
|||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memo: String?,
|
||||
memoBytes: MemoBytes,
|
||||
from accountIndex: Int,
|
||||
result: @escaping TransactionEncoderResultBlock
|
||||
)
|
||||
|
@ -69,22 +69,19 @@ protocol TransactionEncoder {
|
|||
|
||||
- Parameters:
|
||||
- Parameter tAccountPrivateKey: transparent account private key to spend the UTXOs
|
||||
- Parameter memo: string containing the memo (optional)
|
||||
- Parameter memoBytes: containing the memo (optional)
|
||||
- Parameter accountIndex: index of the account that will be used to send the funds
|
||||
|
||||
- Throws: a TransactionEncoderError
|
||||
*/
|
||||
func createShieldingTransaction(
|
||||
tAccountPrivateKey: TransparentAccountPrivKey,
|
||||
memo: String?,
|
||||
memoBytes: MemoBytes,
|
||||
from accountIndex: Int
|
||||
) throws -> EncodedTransaction
|
||||
|
||||
/**
|
||||
Fetch the Transaction Entity from the encoded representation
|
||||
- Parameter encodedTransaction: The encoded transaction to expand
|
||||
- Returns: a TransactionEntity based on the given Encoded Transaction
|
||||
- Throws: a TransactionEncoderError
|
||||
*/
|
||||
|
||||
///Fetch the Transaction Entity from the encoded representation
|
||||
/// - Parameter encodedTransaction: The encoded transaction to expand
|
||||
/// - Returns: a TransactionEntity based on the given Encoded Transaction
|
||||
/// - Throws: a TransactionEncoderError
|
||||
func expandEncodedTransaction(_ encodedTransaction: EncodedTransaction) throws -> TransactionEntity
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ protocol OutboundTransactionManager {
|
|||
func initSpend(
|
||||
zatoshi: Zatoshi,
|
||||
toAddress: String,
|
||||
memo: String?,
|
||||
memo: MemoBytes,
|
||||
from accountIndex: Int
|
||||
) throws -> PendingTransactionEntity
|
||||
|
||||
|
|
|
@ -53,14 +53,14 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memo: String?,
|
||||
memoBytes: MemoBytes,
|
||||
from accountIndex: Int
|
||||
) throws -> EncodedTransaction {
|
||||
let txId = try createSpend(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: zatoshi,
|
||||
to: address,
|
||||
memo: memo,
|
||||
memoBytes: memoBytes,
|
||||
from: accountIndex
|
||||
)
|
||||
|
||||
|
@ -83,7 +83,7 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memo: String?,
|
||||
memoBytes: MemoBytes,
|
||||
from accountIndex: Int,
|
||||
result: @escaping TransactionEncoderResultBlock
|
||||
) {
|
||||
|
@ -96,7 +96,7 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
spendingKey: spendingKey,
|
||||
zatoshi: zatoshi,
|
||||
to: address,
|
||||
memo: memo,
|
||||
memoBytes: memoBytes,
|
||||
from: accountIndex
|
||||
)
|
||||
)
|
||||
|
@ -111,7 +111,7 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memo: String?,
|
||||
memoBytes: MemoBytes,
|
||||
from accountIndex: Int
|
||||
) throws -> Int {
|
||||
guard ensureParams(spend: self.spendParamsURL, output: self.spendParamsURL) else {
|
||||
|
@ -124,7 +124,7 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
extsk: spendingKey.stringEncoded,
|
||||
to: address,
|
||||
value: zatoshi.amount,
|
||||
memo: memo,
|
||||
memo: memoBytes,
|
||||
spendParamsPath: self.spendParamsURL.path,
|
||||
outputParamsPath: self.outputParamsURL.path,
|
||||
networkType: networkType
|
||||
|
@ -139,12 +139,12 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
|
||||
func createShieldingTransaction(
|
||||
tAccountPrivateKey: TransparentAccountPrivKey,
|
||||
memo: String?,
|
||||
memoBytes: MemoBytes,
|
||||
from accountIndex: Int
|
||||
) throws -> EncodedTransaction {
|
||||
let txId = try createShieldingSpend(
|
||||
xprv: tAccountPrivateKey.encoding,
|
||||
memo: memo,
|
||||
memo: memoBytes,
|
||||
accountIndex: accountIndex
|
||||
)
|
||||
|
||||
|
@ -162,7 +162,7 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
}
|
||||
}
|
||||
|
||||
func createShieldingSpend(xprv: String, memo: String?, accountIndex: Int) throws -> Int {
|
||||
func createShieldingSpend(xprv: String, memo: MemoBytes, accountIndex: Int) throws -> Int {
|
||||
guard ensureParams(spend: self.spendParamsURL, output: self.spendParamsURL) else {
|
||||
throw TransactionEncoderError.missingParams
|
||||
}
|
||||
|
|
|
@ -66,22 +66,21 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
reorgExpectation.fulfill()
|
||||
}
|
||||
|
||||
/*
|
||||
pre-condition: know balances before tx at received_Tx_height arrives
|
||||
1. Setup w/ default dataset
|
||||
2. applyStaged(received_Tx_height)
|
||||
3. sync up to received_Tx_height
|
||||
3a. verify that balance is previous balance + tx amount
|
||||
4. get that transaction hex encoded data
|
||||
5. stage 5 empty blocks w/heights received_Tx_height to received_Tx_height + 3
|
||||
6. stage tx at received_Tx_height + 3
|
||||
6a. applyheight(received_Tx_height + 1)
|
||||
7. sync to received_Tx_height + 1
|
||||
8. assert that reorg happened at received_Tx_height
|
||||
9. verify that balance equals initial balance
|
||||
10. sync up to received_Tx_height + 3
|
||||
11. verify that balance equals initial balance + tx amount
|
||||
*/
|
||||
|
||||
/// pre-condition: know balances before tx at received_Tx_height arrives
|
||||
/// 1. Setup w/ default dataset
|
||||
/// 2. applyStaged(received_Tx_height)
|
||||
/// 3. sync up to received_Tx_height
|
||||
/// 3a. verify that balance is previous balance + tx amount
|
||||
/// 4. get that transaction hex encoded data
|
||||
/// 5. stage 5 empty blocks w/heights received_Tx_height to received_Tx_height + 3
|
||||
/// 6. stage tx at received_Tx_height + 3
|
||||
/// 6a. applyheight(received_Tx_height + 1)
|
||||
/// 7. sync to received_Tx_height + 1
|
||||
/// 8. assert that reorg happened at received_Tx_height
|
||||
/// 9. verify that balance equals initial balance
|
||||
/// 10. sync up to received_Tx_height + 3
|
||||
/// 11. verify that balance equals initial balance + tx amount
|
||||
func testReOrgChangesInboundTxMinedHeight() throws {
|
||||
hookToReOrgNotification()
|
||||
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
||||
|
@ -242,32 +241,31 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
XCTAssertEqual(initialTotalBalance + receivedTx.value, finalReorgTxTotalBalance)
|
||||
}
|
||||
|
||||
/**
|
||||
An outbound, unconfirmed transaction in a specific block changes height in the event of a reorg
|
||||
|
||||
|
||||
The wallet handles this change, reflects it appropriately in local storage, and funds remain spendable post confirmation.
|
||||
|
||||
Pre-conditions:
|
||||
- Wallet has spendable funds
|
||||
|
||||
1. Setup w/ default dataset
|
||||
2. applyStaged(received_Tx_height)
|
||||
3. sync up to received_Tx_height
|
||||
4. create transaction
|
||||
5. stage 10 empty blocks
|
||||
6. submit tx at sentTxHeight
|
||||
6a. getIncomingTx
|
||||
6b. stageTransaction(sentTx, sentTxHeight)
|
||||
6c. applyheight(sentTxHeight + 1 )
|
||||
7. sync to sentTxHeight + 2
|
||||
8. stage sentTx and otherTx at sentTxheight
|
||||
9. applyStaged(sentTx + 2)
|
||||
10. sync up to received_Tx_height + 2
|
||||
11. verify that the sent tx is mined and balance is correct
|
||||
12. applyStaged(sentTx + 10)
|
||||
13. verify that there's no more pending transaction
|
||||
*/
|
||||
|
||||
/// An outbound, unconfirmed transaction in a specific block changes height in the event of a reorg
|
||||
///
|
||||
///
|
||||
/// The wallet handles this change, reflects it appropriately in local storage, and funds remain spendable post confirmation.
|
||||
///
|
||||
/// Pre-conditions:
|
||||
/// - Wallet has spendable funds
|
||||
///
|
||||
/// 1. Setup w/ default dataset
|
||||
/// 2. applyStaged(received_Tx_height)
|
||||
/// 3. sync up to received_Tx_height
|
||||
/// 4. create transaction
|
||||
/// 5. stage 10 empty blocks
|
||||
/// 6. submit tx at sentTxHeight
|
||||
/// a. getIncomingTx
|
||||
/// b. stageTransaction(sentTx, sentTxHeight)
|
||||
/// c. applyheight(sentTxHeight + 1 )
|
||||
/// 7. sync to sentTxHeight + 2
|
||||
/// 8. stage sentTx and otherTx at sentTxheight
|
||||
/// 9. applyStaged(sentTx + 2)
|
||||
/// 10. sync up to received_Tx_height + 2
|
||||
/// 11. verify that the sent tx is mined and balance is correct
|
||||
/// 12. applyStaged(sentTx + 10)
|
||||
/// 13. verify that there's no more pending transaction
|
||||
func testReorgChangesOutboundTxIndex() throws {
|
||||
try FakeChainBuilder.buildChain(darksideWallet: self.coordinator.service, branchID: branchID, chainName: chainName)
|
||||
let receivedTxHeight: BlockHeight = 663188
|
||||
|
@ -303,7 +301,7 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
spendingKey: coordinator.spendingKeys!.first!,
|
||||
zatoshi: sendAmount,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test transaction",
|
||||
memo: try Memo(string: "test transaction"),
|
||||
from: 0
|
||||
) { result in
|
||||
switch result {
|
||||
|
@ -522,17 +520,16 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
XCTAssertEqual(afterReOrgVerifiedBalance, initialVerifiedBalance)
|
||||
}
|
||||
|
||||
/**
|
||||
Steps:
|
||||
1. sync up to an incoming transaction (incomingTxHeight + 1)
|
||||
1a. save balances
|
||||
2. stage 4 blocks from incomingTxHeight - 1 with different nonce
|
||||
3. stage otherTx at incomingTxHeight
|
||||
4. stage incomingTx at incomingTxHeight
|
||||
5. applyHeight(incomingHeight + 3)
|
||||
6. sync to latest height
|
||||
7. check that balances still match
|
||||
*/
|
||||
|
||||
/// Steps:
|
||||
/// 1. sync up to an incoming transaction (incomingTxHeight + 1)
|
||||
/// 1a. save balances
|
||||
/// 2. stage 4 blocks from incomingTxHeight - 1 with different nonce
|
||||
/// 3. stage otherTx at incomingTxHeight
|
||||
/// 4. stage incomingTx at incomingTxHeight
|
||||
/// 5. applyHeight(incomingHeight + 3)
|
||||
/// 6. sync to latest height
|
||||
/// 7. check that balances still match
|
||||
func testReOrgChangesInboundTxIndexInBlock() throws {
|
||||
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
||||
|
||||
|
@ -649,33 +646,30 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
XCTAssertEqual(coordinator.synchronizer.initializer.getBalance(), initialBalance)
|
||||
XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), initialVerifiedBalance)
|
||||
}
|
||||
|
||||
/**
|
||||
A Re Org occurs and changes the height of an outbound transaction
|
||||
Pre-condition: Wallet has funds
|
||||
|
||||
Steps:
|
||||
1. create fake chain
|
||||
1a. sync to latest height
|
||||
2. send transaction to recipient address
|
||||
3. getIncomingTransaction
|
||||
4. stage transaction at sentTxHeight
|
||||
5. applyHeight(sentTxHeight)
|
||||
6. sync to latest height
|
||||
6a. verify that there's a pending transaction with a mined height of sentTxHeight
|
||||
7. stage 15 blocks from sentTxHeight
|
||||
7. a stage sent tx to sentTxHeight + 2
|
||||
8. applyHeight(sentTxHeight + 1) to cause a 1 block reorg
|
||||
9. sync to latest height
|
||||
10. verify that there's a pending transaction with -1 mined height
|
||||
11. applyHeight(sentTxHeight + 2)
|
||||
11a. sync to latest height
|
||||
12. verify that there's a pending transaction with a mined height of sentTxHeight + 2
|
||||
13. apply height(sentTxHeight + 15)
|
||||
14. sync to latest height
|
||||
15. verify that there's no pending transaction and that the tx is displayed on the sentTransactions collection
|
||||
|
||||
*/
|
||||
|
||||
/// A Re Org occurs and changes the height of an outbound transaction
|
||||
/// Pre-condition: Wallet has funds
|
||||
///
|
||||
/// Steps:
|
||||
/// 1. create fake chain
|
||||
/// 1a. sync to latest height
|
||||
/// 2. send transaction to recipient address
|
||||
/// 3. getIncomingTransaction
|
||||
/// 4. stage transaction at sentTxHeight
|
||||
/// 5. applyHeight(sentTxHeight)
|
||||
/// 6. sync to latest height
|
||||
/// 6a. verify that there's a pending transaction with a mined height of sentTxHeight
|
||||
/// 7. stage 15 blocks from sentTxHeight
|
||||
/// 7. a stage sent tx to sentTxHeight + 2
|
||||
/// 8. applyHeight(sentTxHeight + 1) to cause a 1 block reorg
|
||||
/// 9. sync to latest height
|
||||
/// 10. verify that there's a pending transaction with -1 mined height
|
||||
/// 11. applyHeight(sentTxHeight + 2)
|
||||
/// 11a. sync to latest height
|
||||
/// 12. verify that there's a pending transaction with a mined height of sentTxHeight + 2
|
||||
/// 13. apply height(sentTxHeight + 15)
|
||||
/// 14. sync to latest height
|
||||
/// 15. verify that there's no pending transaction and that the tx is displayed on the sentTransactions collection
|
||||
func testReOrgChangesOutboundTxMinedHeight() throws {
|
||||
hookToReOrgNotification()
|
||||
|
||||
|
@ -710,7 +704,7 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: Zatoshi(20000),
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
memo: try Memo(string: "this is a test"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
@ -889,21 +883,20 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Uses the zcash-hackworks data set.
|
||||
|
||||
A Re Org occurs at 663195, and sweeps an Inbound Tx that appears later on the chain.
|
||||
Steps:
|
||||
1. reset dlwd
|
||||
2. load blocks from txHeightReOrgBefore
|
||||
3. applyStaged(663195)
|
||||
4. sync to latest height
|
||||
5. get balances
|
||||
6. load blocks from dataset txHeightReOrgBefore
|
||||
7. apply stage 663200
|
||||
8. sync to latest height
|
||||
9. verify that the balance is equal to the one before the reorg
|
||||
*/
|
||||
/// Uses the zcash-hackworks data set.
|
||||
|
||||
/// A Re Org occurs at 663195, and sweeps an Inbound Tx that appears later on the chain.
|
||||
/// Steps:
|
||||
/// 1. reset dlwd
|
||||
/// 2. load blocks from txHeightReOrgBefore
|
||||
/// 3. applyStaged(663195)
|
||||
/// 4. sync to latest height
|
||||
/// 5. get balances
|
||||
/// 6. load blocks from dataset txHeightReOrgBefore
|
||||
/// 7. apply stage 663200
|
||||
/// 8. sync to latest height
|
||||
/// 9. verify that the balance is equal to the one before the reorg
|
||||
func testReOrgChangesInboundMinedHeight() throws {
|
||||
try coordinator.reset(saplingActivation: 663150, branchID: branchID, chainName: chainName)
|
||||
sleep(2)
|
||||
|
@ -950,16 +943,14 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
XCTAssert(afterReOrgTxHeight > initialTxHeight)
|
||||
}
|
||||
|
||||
/**
|
||||
Re Org removes incoming transaction and is never mined
|
||||
Steps:
|
||||
1. sync prior to incomingTxHeight - 1 to get balances there
|
||||
2. sync to latest height
|
||||
3. cause reorg
|
||||
4. sync to latest height
|
||||
5. verify that reorg Happened at reorgHeight
|
||||
6. verify that balances match initial balances
|
||||
*/
|
||||
/// Re Org removes incoming transaction and is never mined
|
||||
/// Steps:
|
||||
/// 1. sync prior to incomingTxHeight - 1 to get balances there
|
||||
/// 2. sync to latest height
|
||||
/// 3. cause reorg
|
||||
/// 4. sync to latest height
|
||||
/// 5. verify that reorg Happened at reorgHeight
|
||||
/// 6. verify that balances match initial balances
|
||||
func testReOrgRemovesIncomingTxForever() throws {
|
||||
hookToReOrgNotification()
|
||||
try coordinator.reset(saplingActivation: 663150, branchID: branchID, chainName: chainName)
|
||||
|
@ -1019,21 +1010,19 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
XCTAssertEqual(initialTotalBalance, coordinator.synchronizer.initializer.getBalance())
|
||||
}
|
||||
|
||||
/**
|
||||
Transaction was included in a block, and then is not included in a block after a reorg, and expires.
|
||||
Steps:
|
||||
1. create fake chain
|
||||
1a. sync to latest height
|
||||
2. send transaction to recipient address
|
||||
3. getIncomingTransaction
|
||||
4. stage transaction at sentTxHeight
|
||||
5. applyHeight(sentTxHeight)
|
||||
6. sync to latest height
|
||||
6a. verify that there's a pending transaction with a mined height of sentTxHeight
|
||||
7. stage 15 blocks from sentTxHeigth to cause a reorg
|
||||
8. sync to latest height
|
||||
9. verify that there's an expired transaction as a pending transaction
|
||||
*/
|
||||
/// Transaction was included in a block, and then is not included in a block after a reorg, and expires.
|
||||
/// Steps:
|
||||
/// 1. create fake chain
|
||||
/// 1a. sync to latest height
|
||||
/// 2. send transaction to recipient address
|
||||
/// 3. getIncomingTransaction
|
||||
/// 4. stage transaction at sentTxHeight
|
||||
/// 5. applyHeight(sentTxHeight)
|
||||
/// 6. sync to latest height
|
||||
/// 6a. verify that there's a pending transaction with a mined height of sentTxHeight
|
||||
/// 7. stage 15 blocks from sentTxHeigth to cause a reorg
|
||||
/// 8. sync to latest height
|
||||
/// 9. verify that there's an expired transaction as a pending transaction
|
||||
func testReOrgRemovesOutboundTxAndIsNeverMined() throws {
|
||||
hookToReOrgNotification()
|
||||
|
||||
|
@ -1070,7 +1059,7 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: Zatoshi(20000),
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
memo: try! Memo(string: "this is a test"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
|
|
@ -83,7 +83,7 @@ class BalanceTests: XCTestCase {
|
|||
spendingKey: spendingKey,
|
||||
zatoshi: maxBalance,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
memo: try Memo(string: "this is a test"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
@ -227,7 +227,7 @@ class BalanceTests: XCTestCase {
|
|||
spendingKey: spendingKey,
|
||||
zatoshi: maxBalanceMinusOne,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
memo: try Memo(string: "\(self.description) \(Date().description)"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
@ -368,7 +368,7 @@ class BalanceTests: XCTestCase {
|
|||
spendingKey: spendingKey,
|
||||
zatoshi: maxBalanceMinusOne,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
memo: try Memo(string: "test send \(self.description) \(Date().description)"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
@ -511,7 +511,7 @@ class BalanceTests: XCTestCase {
|
|||
spendingKey: spendingKey,
|
||||
zatoshi: sendAmount,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
memo: try Memo(string: "this is a test"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
@ -682,7 +682,7 @@ class BalanceTests: XCTestCase {
|
|||
spendingKey: spendingKey,
|
||||
zatoshi: sendAmount,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
memo: try Memo(string: "test send \(self.description) \(Date().description)"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
@ -831,7 +831,7 @@ class BalanceTests: XCTestCase {
|
|||
/*
|
||||
Send
|
||||
*/
|
||||
let memo = "shielding is fun!"
|
||||
let memo = try Memo(string: "shielding is fun!")
|
||||
var pendingTx: PendingTransactionEntity?
|
||||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: spendingKeys,
|
||||
|
@ -892,7 +892,12 @@ class BalanceTests: XCTestCase {
|
|||
*/
|
||||
XCTAssertEqual(confirmedTx.value, self.sendAmount)
|
||||
XCTAssertEqual(confirmedTx.toAddress, self.testRecipientAddress)
|
||||
XCTAssertEqual(confirmedTx.memo?.asZcashTransactionMemo(), memo)
|
||||
do {
|
||||
let confirmedMemo = try confirmedTx.memo.intoMemoBytes().intoMemo()
|
||||
XCTAssertEqual(confirmedMemo, memo)
|
||||
} catch {
|
||||
XCTFail("failed retrieving memo from confirmed transaction. Error: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
guard let transactionId = confirmedTx.rawTransactionId else {
|
||||
XCTFail("no raw transaction id")
|
||||
|
@ -1009,7 +1014,7 @@ class BalanceTests: XCTestCase {
|
|||
spendingKey: spendingKey,
|
||||
zatoshi: sendAmount,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description)",
|
||||
memo: try Memo(string: "test send \(self.description)"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
|
|
@ -83,7 +83,7 @@ class NetworkUpgradeTests: XCTestCase {
|
|||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: spendAmount,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
memo: try Memo(string: "this is a test"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
@ -172,7 +172,7 @@ class NetworkUpgradeTests: XCTestCase {
|
|||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: spendAmount,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
memo: try Memo(string: "this is a test"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
@ -241,7 +241,7 @@ class NetworkUpgradeTests: XCTestCase {
|
|||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: spendAmount,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
memo: try Memo(string: "this is a test"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
@ -342,7 +342,7 @@ class NetworkUpgradeTests: XCTestCase {
|
|||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: spendAmount,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
memo: try Memo(string: "this is a test"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
@ -454,7 +454,7 @@ class NetworkUpgradeTests: XCTestCase {
|
|||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: spendAmount,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
memo: try Memo(string: "this is a test"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
|
|
@ -98,7 +98,7 @@ class PendingTransactionUpdatesTest: XCTestCase {
|
|||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: Zatoshi(20000),
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
memo: try Memo(string: "this is a test"),
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
switch result {
|
||||
|
|
|
@ -170,7 +170,7 @@ class RewindRescanTests: XCTestCase {
|
|||
spendingKey: coordinator.spendingKey,
|
||||
zatoshi: Zatoshi(1000),
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: nil,
|
||||
memo: .empty,
|
||||
from: 0
|
||||
) { result in
|
||||
sendExpectation.fulfill()
|
||||
|
@ -271,7 +271,7 @@ class RewindRescanTests: XCTestCase {
|
|||
spendingKey: spendingKey,
|
||||
zatoshi: maxBalance,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
memo: try Memo(string: "test send \(self.description) \(Date().description)"),
|
||||
from: 0
|
||||
) { result in
|
||||
switch result {
|
||||
|
|
|
@ -210,7 +210,7 @@ class ShieldFundsTests: XCTestCase {
|
|||
// shield the funds
|
||||
coordinator.synchronizer.shieldFunds(
|
||||
transparentAccountPrivateKey: transparentAccountPrivateKey,
|
||||
memo: "shield funds",
|
||||
memo: try Memo(string: "shield funds"),
|
||||
from: 0
|
||||
) { result in
|
||||
switch result {
|
||||
|
|
|
@ -105,7 +105,7 @@ class Z2TReceiveTests: XCTestCase {
|
|||
spendingKey: coordinator.spendingKeys!.first!,
|
||||
zatoshi: sendAmount,
|
||||
toAddress: try! Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test transaction",
|
||||
memo: try Memo(string: "test transaction"),
|
||||
from: 0
|
||||
) { result in
|
||||
switch result {
|
||||
|
|
|
@ -122,10 +122,20 @@ class NullBytesTests: XCTestCase {
|
|||
|
||||
XCTAssertFalse(validZaddr.containsCStringNullBytesBeforeStringEnding())
|
||||
XCTAssertTrue(
|
||||
"zs1gqtfu59z20s\09t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp"
|
||||
"zs1gqtfu59z20s\u{0}9t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp"
|
||||
.containsCStringNullBytesBeforeStringEnding()
|
||||
)
|
||||
XCTAssertTrue("\0".containsCStringNullBytesBeforeStringEnding())
|
||||
XCTAssertTrue("\u{0}".containsCStringNullBytesBeforeStringEnding())
|
||||
XCTAssertFalse("".containsCStringNullBytesBeforeStringEnding())
|
||||
}
|
||||
|
||||
func testTrimTrailingNullBytes() throws {
|
||||
let nullTrailedString = "This Is a memo with text and trailing null bytes\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}"
|
||||
|
||||
let nonNullTrailedString = "This Is a memo with text and trailing null bytes"
|
||||
|
||||
let trimmedString = String(nullTrailedString.reversed().drop(while: { $0 == "\u{0}"}).reversed())
|
||||
|
||||
XCTAssertEqual(trimmedString, nonNullTrailedString)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,8 +157,8 @@ class PendingTransactionRepositoryTests: XCTestCase {
|
|||
XCTAssertEqual(updatedTransaction.toAddress, stored!.toAddress)
|
||||
}
|
||||
|
||||
func createAndStoreMockedTransaction() -> PendingTransactionEntity {
|
||||
var transaction = mockTransaction()
|
||||
func createAndStoreMockedTransaction(with value: Zatoshi = Zatoshi(1000)) -> PendingTransactionEntity {
|
||||
var transaction = mockTransaction(with: value)
|
||||
var id: Int?
|
||||
|
||||
XCTAssertNoThrow(try { id = try pendingRepository.create(transaction) }())
|
||||
|
@ -173,12 +173,7 @@ class PendingTransactionRepositoryTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private func mockTransaction() -> PendingTransactionEntity {
|
||||
PendingTransaction(
|
||||
value: Zatoshi(Int64.random(in: 1 ... 1_000_000)),
|
||||
toAddress: recipientAddress,
|
||||
memo: nil,
|
||||
account: 0
|
||||
)
|
||||
private func mockTransaction(with value: Zatoshi = Zatoshi(1000)) -> PendingTransactionEntity {
|
||||
PendingTransaction(value: value, toAddress: recipientAddress, memo: .empty(), account: 0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,10 +105,7 @@ class Zip302MemoTests: XCTestCase {
|
|||
func testItCreatesAMemoFromAValidAndShortEnoughText() throws {
|
||||
let almostTooLongString = "thiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeryyyyyyyyyyyyyyyyyyyyyyyyyy looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong meeeeeeeeeeeeeeeeeeemooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo but it's just short enough"
|
||||
|
||||
guard let memo = try Memo(string: almostTooLongString) else {
|
||||
XCTFail("Expected `Memo` or `Error` thrown but found `nil`")
|
||||
return
|
||||
}
|
||||
let memo = try Memo(string: almostTooLongString)
|
||||
|
||||
let memoBytes = try memo.asMemoBytes()
|
||||
|
||||
|
@ -128,12 +125,101 @@ class Zip302MemoTests: XCTestCase {
|
|||
switch error {
|
||||
case .tooLong(let count):
|
||||
XCTAssertEqual(count, 515)
|
||||
|
||||
case .invalidUTF8:
|
||||
XCTFail("Expected `.tooLong(515) but found `.invalidUTF8`")
|
||||
case .endsWithNullBytes:
|
||||
XCTFail("Expected `.tooLong(515) but found `.endsWithNullBytes`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testInitMemoBytesFromContiguousBytes() throws {
|
||||
let contiguousEmptyBytes = ContiguousArray<UInt8>(Zip302MemoTests.emptyMemoBytes)
|
||||
|
||||
let emptyMemoBytes = try MemoBytes(contiguousBytes: contiguousEmptyBytes)
|
||||
|
||||
XCTAssertEqual(emptyMemoBytes.bytes, .emptyMemoBytes)
|
||||
|
||||
let contiguousTextMemoBytes = ContiguousArray<UInt8>(Zip302MemoTests.helloImATextMemo)
|
||||
|
||||
let textMemoBytes = try MemoBytes(contiguousBytes: contiguousTextMemoBytes)
|
||||
|
||||
XCTAssertEqual(textMemoBytes.bytes, Zip302MemoTests.helloImATextMemo)
|
||||
}
|
||||
|
||||
func testThrowsWhenTextMemoIsConstructedWithTrailingNullBytes() throws {
|
||||
let nullTrailedString = "This Is a memo with text and trailing null bytes\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}"
|
||||
|
||||
XCTAssertThrowsError(try Memo(string: nullTrailedString)) { error in
|
||||
guard let thrownError = error as? MemoBytes.Errors else {
|
||||
XCTFail("Thrown erros is not MemoBytes.Error")
|
||||
return
|
||||
}
|
||||
|
||||
switch thrownError {
|
||||
case .invalidUTF8, .tooLong:
|
||||
XCTFail("Expected .endsWithNullBytes found other errors")
|
||||
case .endsWithNullBytes:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testThrowsWhenTextMemoIsConstructedWithNullBytes() throws {
|
||||
let nullTrailedString = "\u{0}"
|
||||
|
||||
XCTAssertThrowsError(try Memo(string: nullTrailedString)) { error in
|
||||
guard let thrownError = error as? MemoBytes.Errors else {
|
||||
XCTFail("Thrown erros is not MemoBytes.Error")
|
||||
return
|
||||
}
|
||||
|
||||
switch thrownError {
|
||||
case .invalidUTF8, .tooLong:
|
||||
XCTFail("Expected .endsWithNullBytes found other errors")
|
||||
case .endsWithNullBytes:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testTextMemoIsConstructedWithLeadingNullBytes() throws {
|
||||
let nullLedString = "\u{0}ABC"
|
||||
|
||||
let nullLedTextMemo = try MemoText(nullLedString)
|
||||
|
||||
let nullLedMemo = try Memo(string: nullLedString)
|
||||
|
||||
if case .text(let textMemo) = nullLedMemo {
|
||||
XCTAssertEqual(nullLedTextMemo, textMemo)
|
||||
} else {
|
||||
XCTFail("Expected a TextMemo")
|
||||
}
|
||||
}
|
||||
|
||||
func testTextMemoIsConstructedWithEmptyString() throws {
|
||||
let emptyString = ""
|
||||
|
||||
let emptyTextMemo = try MemoText(emptyString)
|
||||
|
||||
let emptyStringMemo = try Memo(string: emptyString)
|
||||
|
||||
if case .text(let textMemo) = emptyStringMemo {
|
||||
XCTAssertEqual(emptyTextMemo, textMemo)
|
||||
} else {
|
||||
XCTFail("Expected a TextMemo")
|
||||
}
|
||||
}
|
||||
|
||||
func testUnpaddedRawBytesWhenPaddingIsFound() throws {
|
||||
let expected: [UInt8] = [0x56, 0x17, 0xe0, 0xac, 0x3c, 0xbc, 0xde]
|
||||
|
||||
XCTAssertEqual(Zip302MemoTests.shortButPaddedBytes.unpaddedRawBytes(), expected)
|
||||
}
|
||||
|
||||
func testUnpaddedRawBytesWhenThereIsNoPadding() throws {
|
||||
XCTAssertEqual(Self.fullMemoBytes.unpaddedRawBytes(), Self.fullMemoBytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -81,6 +81,22 @@ extension LightWalletServiceMockResponse {
|
|||
}
|
||||
|
||||
class MockRustBackend: ZcashRustBackendWelding {
|
||||
static func getReceivedMemo(dbData: URL, idNote: Int64, networkType: ZcashLightClientKit.NetworkType) -> ZcashLightClientKit.Memo? {
|
||||
nil
|
||||
}
|
||||
|
||||
static func getSentMemo(dbData: URL, idNote: Int64, networkType: ZcashLightClientKit.NetworkType) -> ZcashLightClientKit.Memo? {
|
||||
nil
|
||||
}
|
||||
|
||||
static func createToAddress(dbData: URL, account: Int32, extsk: String, to address: String, value: Int64, memo: ZcashLightClientKit.MemoBytes, spendParamsPath: String, outputParamsPath: String, networkType: ZcashLightClientKit.NetworkType) -> Int64 {
|
||||
-1
|
||||
}
|
||||
|
||||
static func shieldFunds(dbCache: URL, dbData: URL, account: Int32, xprv: String, memo: ZcashLightClientKit.MemoBytes, spendParamsPath: String, outputParamsPath: String, networkType: ZcashLightClientKit.NetworkType) -> Int64 {
|
||||
-1
|
||||
}
|
||||
|
||||
|
||||
static func initDataDb(dbData: URL, seed: [UInt8]?, networkType: ZcashLightClientKit.NetworkType) throws -> ZcashLightClientKit.DbInitResult {
|
||||
.seedRequired
|
||||
|
|
|
@ -117,6 +117,7 @@ func deleteParametersFromDocuments() throws {
|
|||
output: documents.appendingPathComponent("sapling-output.params")
|
||||
)
|
||||
}
|
||||
|
||||
func deleteParamsFrom(spend: URL, output: URL) {
|
||||
try? FileManager.default.removeItem(at: spend)
|
||||
try? FileManager.default.removeItem(at: output)
|
||||
|
|
Loading…
Reference in New Issue