Skip to content
This repository has been archived by the owner on May 14, 2021. It is now read-only.

DP-701 queue #53

Open
wants to merge 54 commits into
base: epic/DP-665_batch-payments
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
385fa47
Merge branch 'DP-689_stub-api' into epic/DP-665_batch-payments
cnotethegr8 Aug 14, 2019
8a5e189
Merge branch 'DP-689_stub-api' into epic/DP-665_batch-payments
cnotethegr8 Aug 14, 2019
25b60fc
building queues
cnotethegr8 Aug 15, 2019
f584a01
building tests
cnotethegr8 Aug 15, 2019
e6440a7
tests for payments queue
cnotethegr8 Aug 18, 2019
3aca2d1
initial work on connecting transaction params to queue
cnotethegr8 Aug 19, 2019
1ad83c7
removed external signing and abstract account interface
cnotethegr8 Aug 19, 2019
0dc8c28
adding operation to tx params
cnotethegr8 Aug 19, 2019
57b3214
simplified kin to quark conversions
cnotethegr8 Aug 20, 2019
f80ad7e
assets tests
cnotethegr8 Aug 20, 2019
7a1bce3
restructured node to static prop
cnotethegr8 Aug 21, 2019
ddd25dc
connected transaction params operation values
cnotethegr8 Aug 21, 2019
df89a16
combined common operation code. working on tests
cnotethegr8 Aug 22, 2019
0d2b015
Network class now holds url
cnotethegr8 Aug 22, 2019
cb06f80
removed external passphrase
cnotethegr8 Aug 29, 2019
7eb03a0
fixed data types
cnotethegr8 Aug 29, 2019
334443c
async operation
cnotethegr8 Sep 2, 2019
f093999
tests and further connections to the queue
cnotethegr8 Sep 2, 2019
9bbbff2
fixed tests
cnotethegr8 Sep 2, 2019
f0ada8f
improved test
cnotethegr8 Sep 2, 2019
7db0d9b
optional interceptor
cnotethegr8 Sep 2, 2019
077ae69
nullable metadata
cnotethegr8 Sep 2, 2019
528b36b
removed unneeded code
cnotethegr8 Sep 2, 2019
6fd0ad3
async operation tests
cnotethegr8 Sep 2, 2019
454244a
improving tests
cnotethegr8 Sep 2, 2019
cb8ea66
improving tests
cnotethegr8 Sep 3, 2019
d2b7b68
created pending payments transaction
cnotethegr8 Sep 3, 2019
6be9e19
building transaction queue merge and tests
cnotethegr8 Sep 4, 2019
8fc3096
transaction queue tests completed
cnotethegr8 Sep 5, 2019
55b3c9e
starting to implement the interceptor
cnotethegr8 Sep 5, 2019
cb09395
working on the interceptor
cnotethegr8 Sep 8, 2019
75a7301
cleaned up warnings
cnotethegr8 Sep 8, 2019
e8284cc
building operations and transactions
cnotethegr8 Sep 9, 2019
26f08bf
fixed queue in progress var
cnotethegr8 Sep 9, 2019
a043f2b
removed comment
cnotethegr8 Sep 9, 2019
94c1686
fixed tests
cnotethegr8 Sep 9, 2019
85eca14
Merge remote-tracking branch 'origin/epic/DP-665_batch-payments' into…
cnotethegr8 Sep 9, 2019
6dc786f
passing source public address to batch payments transaction
cnotethegr8 Sep 9, 2019
ec1c59d
fixed failing tests
cnotethegr8 Sep 9, 2019
2b14db6
simplified common logic
cnotethegr8 Sep 9, 2019
972bd77
filled in transaction process send functions
cnotethegr8 Sep 10, 2019
08aa723
cleanup
cnotethegr8 Sep 10, 2019
d2f5617
interceptor is weak
cnotethegr8 Sep 10, 2019
820d991
starting tx process tests
cnotethegr8 Sep 10, 2019
dfc35d5
merged tests branch. extended interface to use generics
cnotethegr8 Sep 10, 2019
13e338d
removed async operation class
cnotethegr8 Sep 10, 2019
584ab19
essentials
cnotethegr8 Sep 11, 2019
c5c0d7b
fixed error
cnotethegr8 Sep 12, 2019
9377aaa
passing better defined data
cnotethegr8 Sep 12, 2019
c59eef3
removed account from stellar as property
cnotethegr8 Sep 12, 2019
6471b7b
fixed tests
cnotethegr8 Sep 12, 2019
ece94d4
stellaraccount public address is no longer optional
cnotethegr8 Sep 12, 2019
c61f01c
created kin account protocol
cnotethegr8 Sep 12, 2019
2cb3d2e
moved kin account protocol to its own file
cnotethegr8 Sep 12, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 71 additions & 3 deletions KinSDK.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions KinSDK/KinSDK/Assets.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// Asset.swift
// KinSDK
//
// Created by Corey Werner on 20/08/2019.
// Copyright © 2019 Kin Foundation. All rights reserved.
//

import Foundation

/**
Kin is the native currency of the network.
*/
public typealias Kin = Decimal

extension Kin {
/**
Convert `Kin` into `Quark`.

This value should only be used to fees.
*/
public func toQuark() -> Quark {
return (self * Decimal(AssetUnitDivisor) as NSDecimalNumber).uint32Value
}

func toQuarkAsBlockchainUnit() -> Int64 {
return (self * Decimal(AssetUnitDivisor) as NSDecimalNumber).int64Value
}
}

/**
Quark is the smallest amount unit. It is one-hundred-thousandth of a Kin: `1/100000` or `0.00001`.
*/
public typealias Quark = UInt32

extension Quark {
public func toKin() -> Kin {
return Decimal(self) / Decimal(AssetUnitDivisor)
}
}

@available(*, deprecated, renamed: "Quark")
public typealias Stroop = Quark

let AssetUnitDivisor: UInt32 = 100_000
31 changes: 26 additions & 5 deletions KinSDK/KinSDK/BulkPayments/BatchPaymentTransaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,33 @@
import Foundation

public class BatchPaymentTransaction: BaseTransaction {
public var payments: [PaymentOperation] {
return []
public let payments: [PaymentOperation]

private static func findPaymentOperations(operations: [Operation]) -> [PaymentOp]? {
var paymentOperations: [PaymentOp] = []

for operation in operations {
if case let Operation.Body.PAYMENT(paymentOperation) = operation.body {
paymentOperations.append(paymentOperation)
}
}

return paymentOperations.count > 0 ? paymentOperations : nil
}

required init(tryWrapping transaction: Transaction, sourcePublicAddress: String) throws {
guard let operations = BatchPaymentTransaction.findPaymentOperations(operations: transaction.operations) else {
throw StellarError.decodeTransactionFailed
}

self.payments = operations.map({ operation -> PaymentOperation in
return PaymentOperation(sourcePublicAddress: sourcePublicAddress, destinationPublicAddress: operation.destination.publicKey, amount: Kin(operation.amount))
})

super.init(wrapping: transaction)
}

public var memo: String {
return ""
public var memo: String? {
return transaction.memo.text
}
}

4 changes: 2 additions & 2 deletions KinSDK/KinSDK/BulkPayments/PaymentOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation

public struct PaymentOperation {
public let source: String
public let destination: String
public let sourcePublicAddress: String
public let destinationPublicAddress: String
public let amount: Kin
}
127 changes: 71 additions & 56 deletions KinSDK/KinSDK/BulkPayments/PaymentQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,83 +9,98 @@
import Foundation

public protocol PaymentQueueDelegate: NSObjectProtocol {
func paymentEnqueued(pendingPayment: PaymentQueue.PendingPayment)
func transactionSend(transaction: BatchPaymentTransaction, payments: [PaymentQueue.PendingPayment])
func transactionSendSuccess(transaction: BatchPaymentTransaction, payments: [PaymentQueue.PendingPayment])
func transactionSendFailed(transaction: BatchPaymentTransaction, payments: [PaymentQueue.PendingPayment], error: Error)
func paymentEnqueued(pendingPayment: PendingPayment)
func transactionSend(transaction: BatchPaymentTransaction, payments: [PendingPayment])
func transactionSendSuccess(transaction: BatchPaymentTransaction, payments: [PendingPayment])
func transactionSendFailed(transaction: BatchPaymentTransaction, payments: [PendingPayment], error: Error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

failed delegate shouldn't have transaction object (or have the standard (?) pattern of single transactionCompleted completion with both optional error and result)

}

public class PaymentQueue {
public weak var delegate: PaymentQueue?
public class PaymentQueue: NSObject {
public weak var delegate: PaymentQueueDelegate?

public func enqueuePayment(publicAddress: String, amount: Kin, metadata: AnyObject? = nil) throws {
let stellar: StellarProtocol
let stellarAccount: StellarAccount

}
private let paymentsQueueManager = PaymentsQueueManager()
private lazy var transactionTasksQueueManager: TransactionTasksQueueManager = {
return TransactionTasksQueueManager(stellar: stellar, stellarAccount: stellarAccount)
}()

init(stellar: StellarProtocol, stellarAccount: StellarAccount) {
self.stellar = stellar
self.stellarAccount = stellarAccount

public func setTransactionInterceptor(_ interceptor: TransactionInterceptor) {
super.init()

paymentsQueueManager.delegate = self
}

public var fee: Int = 0 {
didSet {
// MARK: Enqueuing

}
}
public func enqueuePayment(publicAddress: String, amount: Kin, metadata: AnyObject? = nil) throws -> PendingPayment {
let pendingPayment = PendingPayment(destinationPublicAddress: publicAddress, sourcePublicAddress: stellarAccount.publicAddress, amount: amount, metadata: metadata)

public var status: Status {
return Status()
}
}
paymentsQueueManager.enqueue(pendingPayment: pendingPayment)

extension PaymentQueue {
public class Status {
public var transactionInProgress: Bool {
return false
}
return pendingPayment
}

public var pendingPaymentsCount: Int {
return 0
func enqueueTransactionParams(_ params: SendTransactionParams, completion: @escaping (Result<TransactionId, Error>) -> Void) {
DispatchQueue.global(qos: .utility).async {
let transactionParamsOperation = self.transactionTasksQueueManager.enqueue(transactionParams: params)

transactionParamsOperation.completionBlock = {
DispatchQueue.main.async {
if transactionParamsOperation.isCancelled {
completion(.failure(KinError.transactionOperationCancelled))
return
}

guard let result = transactionParamsOperation.result else {
// This should never happen.
completion(.failure(KinError.internalInconsistency))
return
}

completion(result)
}
}
}
}
}

extension PaymentQueue {
// ???: change to struct
public class PendingPayment {
public var destinationPublicKey: String {
return ""
}
// MARK: Inspecting

public var sourcePublicKey: String {
return ""
}

public var amount: Kin {
return 0
}
public var transactionInProgress: Bool {
return transactionTasksQueueManager.inProgress
}

public var operationIndex: Int {
return 0
}
public var pendingPaymentsCount: Int {
return paymentsQueueManager.operationsCount
}

public func transaction() -> BatchPaymentTransaction {
return nil!
}
// MARK: Operation Properties

public var metaData: Any {
return {}
}
/**
- Note: The delegate functions will be called from a background thread.
*/
weak public var transactionInterceptor: TransactionInterceptor?

public var status: Status {
return .pending
}
}
public var fee: Quark = 0
}

extension PaymentQueue.PendingPayment {
public enum Status {
case pending
case completed
case failed
extension PaymentQueue: PaymentsQueueManagerDelegate {
func paymentsQueueManager(_ manager: PaymentsQueueManager, dequeueing pendingPayments: [PendingPayment]) {
DispatchQueue.global(qos: .utility).async {
func enqueue() {
self.transactionTasksQueueManager.enqueue(pendingPayments: pendingPayments, fee: self.fee, transactionInterceptor: self.transactionInterceptor)
}

if self.fee == 0 {
self.stellar.minFee().then({ self.fee = $0 }).finally({ enqueue() })
}
else {
enqueue()
}
}
}
}
59 changes: 59 additions & 0 deletions KinSDK/KinSDK/BulkPayments/PaymentQueueTransactionProcess.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// PaymentQueueTransactionProcess.swift
// KinSDK
//
// Created by Corey Werner on 05/09/2019.
// Copyright © 2019 Kin Foundation. All rights reserved.
//

import Foundation

public class PaymentQueueTransactionProcess: TransactionProcess {
public let pendingPayments: [PendingPayment]
let fee: Quark
let stellarAccount: StellarAccount

init(pendingPayments: [PendingPayment], fee: Quark, stellar: StellarProtocol, stellarAccount: StellarAccount) {
self.pendingPayments = pendingPayments
self.fee = fee
self.stellarAccount = stellarAccount

super.init(stellar: stellar)
}

public override func transaction() throws -> BatchPaymentTransaction {
return try transaction(memo: nil)
}

public func transaction(memo memoString: String?) throws -> BatchPaymentTransaction {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()

var result: Result<BatchPaymentTransaction, Error> = .failure(KinError.internalInconsistency)

var memo: Memo = .MEMO_NONE

if let memoString = memoString {
memo = try Memo(memoString)
}

stellar.transaction(sourceStellarAccount: stellarAccount, pendingPayments: pendingPayments, memo: memo, fee: fee)
.then { transaction in
result = .success(transaction)
dispatchGroup.leave()
}
.error { error in
result = .failure(error)
dispatchGroup.leave()
}

dispatchGroup.wait()

switch result {
case .success(let transaction):
return transaction
case .failure(let error):
throw error
}
}
}
83 changes: 83 additions & 0 deletions KinSDK/KinSDK/BulkPayments/PaymentsQueueManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//
// PaymentsQueueManager.swift
// KinSDK
//
// Created by Corey Werner on 15/08/2019.
// Copyright © 2019 Kin Foundation. All rights reserved.
//

import Foundation

protocol PaymentsQueueManagerDelegate: NSObjectProtocol {
func paymentsQueueManager(_ manager: PaymentsQueueManager, dequeueing pendingPayments: [PendingPayment])
}

class PaymentsQueueManager {
let maxPendingPayments = TransactionTasksQueueManager.maxPendingPaymentCount
let maxPaymentsTime: TimeInterval
let maxTimeoutTime: TimeInterval
private var paymentsTimer: Timer?
private var timeoutTimer: Timer?
private var payments: [PendingPayment] = []

weak var delegate: PaymentsQueueManagerDelegate?

init(maxPaymentsTime: TimeInterval = 4, maxTimeoutTime: TimeInterval = 10) {
self.maxPaymentsTime = maxPaymentsTime
self.maxTimeoutTime = maxTimeoutTime
}

var inProgress: Bool {
cnotethegr8 marked this conversation as resolved.
Show resolved Hide resolved
return timeoutTimer != nil || paymentsTimer != nil
}

var operationsCount: Int {
return payments.count
}

func enqueue(pendingPayment: PendingPayment) {
cnotethegr8 marked this conversation as resolved.
Show resolved Hide resolved
func enqueu() {
updateTimers()

payments.append(pendingPayment)

if operationsCount >= maxPendingPayments {
dequeue()
}
}

if Thread.isMainThread {
enqueu()
}
else {
DispatchQueue.main.async {
enqueu()
}
}
}

@objc private func dequeue() {
removeTimers()

delegate?.paymentsQueueManager(self, dequeueing: payments)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aren't you using the same reference here? I think you should clone payments o.w. unexpected things can happen when we clear here the list.


payments.removeAll()
}

private func updateTimers() {
if timeoutTimer == nil {
timeoutTimer = .scheduledTimer(timeInterval: maxTimeoutTime, target: self, selector: #selector(dequeue), userInfo: nil, repeats: false)
}

paymentsTimer?.invalidate()
paymentsTimer = .scheduledTimer(timeInterval: maxPaymentsTime, target: self, selector: #selector(dequeue), userInfo: nil, repeats: false)
}

private func removeTimers() {
timeoutTimer?.invalidate()
timeoutTimer = nil

paymentsTimer?.invalidate()
paymentsTimer = nil
}
}
Loading