From a520f44f19508f9ec1b0b4722cfdcceae91ed54a Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Tue, 20 Aug 2019 14:38:44 +0300 Subject: [PATCH 1/5] Add 's' to 'Task' so it will be in plural --- .../internal/queue/PaymentQueueManagerIntegrationTest.kt | 2 +- .../java/kin/sdk/internal/queue/PaymentQueueImpl.java | 6 +++--- .../kin/sdk/internal/queue/PaymentQueueManagerImpl.java | 8 ++++---- ...ueueManager.java => TransactionTasksQueueManager.java} | 2 +- ...gerImpl.java => TransactionTasksQueueManagerImpl.java} | 2 +- .../kin/sdk/internal/queue/PaymentQueueManagerTest.kt | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) rename kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/{TransactionTaskQueueManager.java => TransactionTasksQueueManager.java} (85%) rename kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/{TransactionTaskQueueManagerImpl.java => TransactionTasksQueueManagerImpl.java} (77%) diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueManagerIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueManagerIntegrationTest.kt index 2dba5080..afc6081a 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueManagerIntegrationTest.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueManagerIntegrationTest.kt @@ -21,7 +21,7 @@ class PaymentQueueManagerIntegrationTest { } private var queueScheduler = FakeIntegrationTestQueueScheduler() - private var txTaskQueueManager = TransactionTaskQueueManagerImpl() + private var txTaskQueueManager = TransactionTasksQueueManagerImpl() private var pendingBalanceUpdater = PendingBalanceUpdaterImpl() private var eventsManager = EventsManagerImpl() diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java index 9313beda..3afa0068 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java @@ -18,12 +18,12 @@ public class PaymentQueueImpl implements PaymentQueue { private final String sourcePublicAddress; private final PaymentQueueManager paymentQueueManager; - private final TransactionTaskQueueManager txTaskQueueManager; + private final TransactionTasksQueueManager txTasksQueueManager; public PaymentQueueImpl(@NonNull String sourcePublicAddress) { this.sourcePublicAddress = sourcePublicAddress; - this.txTaskQueueManager = new TransactionTaskQueueManagerImpl(); - this.paymentQueueManager = new PaymentQueueManagerImpl(txTaskQueueManager, new QueueSchedulerImpl(), + this.txTasksQueueManager = new TransactionTasksQueueManagerImpl(); + this.paymentQueueManager = new PaymentQueueManagerImpl(txTasksQueueManager, new QueueSchedulerImpl(), new PendingBalanceUpdaterImpl(), new EventsManagerImpl(), DELAY_BETWEEN_PAYMENTS_MILLIS, QUEUE_TIMEOUT_MILLIS, MAX_NUM_OF_PAYMENTS); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java index a5d3849a..feaad03a 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java @@ -10,7 +10,7 @@ class PaymentQueueManagerImpl implements PaymentQueueManager { - private final TransactionTaskQueueManager txTaskQueueManager; + private final TransactionTasksQueueManager txTasksQueueManager; private final QueueScheduler queueScheduler; private final PendingBalanceUpdater pendingBalanceUpdater; private final EventsManager eventsManager; @@ -20,10 +20,10 @@ class PaymentQueueManagerImpl implements PaymentQueueManager { private List queue; private Runnable delayTask; - PaymentQueueManagerImpl(TransactionTaskQueueManager txTaskQueueManager, QueueScheduler queueScheduler, + PaymentQueueManagerImpl(TransactionTasksQueueManager txTasksQueueManager, QueueScheduler queueScheduler, PendingBalanceUpdater pendingBalanceUpdater, EventsManager eventsManager, long delayBetweenPayments, long queueTimeout, int maxNumOfPayments) { - this.txTaskQueueManager = txTaskQueueManager; + this.txTasksQueueManager = txTasksQueueManager; this.queueScheduler = queueScheduler; this.pendingBalanceUpdater = pendingBalanceUpdater; this.eventsManager = eventsManager; @@ -96,7 +96,7 @@ private void sendPendingPaymentsToTaskQueue() { // TODO: 2019-08-08 maybe send events List pendingPayments = queue; queue = new ArrayList<>(maxNumOfPayments); - txTaskQueueManager.enqueue(pendingPayments); + txTasksQueueManager.enqueue(pendingPayments); } private class DelayTask implements Runnable { diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManager.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java similarity index 85% rename from kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManager.java rename to kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java index a00bc565..572bda26 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManager.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java @@ -5,7 +5,7 @@ import java.util.List; -public interface TransactionTaskQueueManager { +public interface TransactionTasksQueueManager { // TODO: 2019-08-15 add java docs diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java similarity index 77% rename from kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManagerImpl.java rename to kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java index c39a324e..389f6d89 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManagerImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java @@ -5,7 +5,7 @@ import java.util.List; -public class TransactionTaskQueueManagerImpl implements TransactionTaskQueueManager { +public class TransactionTasksQueueManagerImpl implements TransactionTasksQueueManager { @Override public void enqueue(List queueToSend) { diff --git a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt index b8089feb..c5281598 100644 --- a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt +++ b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt @@ -21,7 +21,7 @@ class PaymentQueueManagerTest { } private var queueScheduler: FakeQueueScheduler = spy() //TODO use a spy in order to be able to verify method calls on that object, is this fine? - private var txTaskQueueManager: TransactionTaskQueueManager = mock() + private var txTaskQueueManager: TransactionTasksQueueManager = mock() private var pendingBalanceUpdater: PendingBalanceUpdater = mock() private var eventsManager: EventsManager = mock() From 018b70fef014e0c760e9af6eea66f8426f3e5621 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 2 Sep 2019 10:06:24 +0300 Subject: [PATCH 2/5] Add all the code (without tests) that are necessary for the transaction task queue manager --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- kin-sdk/kin-sdk-lib/build.gradle | 2 +- .../PaymentQueueManagerIntegrationTest.kt | 4 +- .../queue/TasksQueueIntegrationTest.kt | 30 ++++ .../src/main/java/kin/sdk/KinAccount.java | 52 +++++-- .../src/main/java/kin/sdk/KinClient.java | 24 +-- .../java/kin/sdk/SendTransactionParams.java | 44 ------ .../src/main/java/kin/sdk/internal/Utils.java | 26 +++- .../internal/account/AbstractKinAccount.java | 32 +++- .../sdk/internal/account/KinAccountImpl.java | 51 ++++--- .../GeneralBlockchainInfoRetriever.java | 2 +- .../GeneralBlockchainInfoRetrieverImpl.java | 11 +- .../blockchain/TransactionSender.java | 143 ++++++++++++------ .../internal/events/EventsManagerImpl.java | 4 + .../sdk/internal/queue/PaymentQueueImpl.java | 80 +++++++--- .../queue/PaymentQueueManagerImpl.java | 8 +- .../queue/SendPendingPaymentsTask.java | 81 ++++++++++ .../queue/SendTransactionParamsTask.java | 66 ++++++++ .../internal/queue/SendTransactionTask.java | 57 +++++++ .../internal/queue/TaskFinishListener.java | 13 ++ .../kin/sdk/internal/queue/TasksQueue.java | 67 ++++++++ .../sdk/internal/queue/TasksQueueImpl.java | 102 +++++++++++++ .../queue/TransactionInterceptorImpl.java | 16 -- .../queue/TransactionProcessImpl.java | 38 ----- .../queue/TransactionTasksQueueManager.java | 29 +++- .../TransactionTasksQueueManagerImpl.java | 120 ++++++++++++++- .../queue/BatchPaymentTransactionProcess.java | 24 +++ .../BatchPaymentTransactionProcessImpl.java | 39 +++++ .../main/java/kin/sdk/queue/PaymentQueue.java | 9 +- .../queue/TransactionParamsProcessImpl.java | 44 ++++++ .../kin/sdk/queue/TransactionProcess.java | 49 ++++-- .../BatchPaymentTransaction.java | 9 +- .../transactiondata/PaymentTransaction.java | 8 +- .../PaymentTransactionParams.java | 40 +++++ .../kin/sdk/transactiondata/Transaction.java | 16 +- .../transactiondata/TransactionParams.java | 21 +++ .../test/java/kin/sdk/KinAccountImplTest.java | 20 ++- .../java/kin/sdk/TransactionSenderTest.java | 95 +++++++----- .../kin/sdk/internal/queue/TasksQueueTest.kt | 20 +++ .../java/sdk/sample/TransactionActivity.java | 13 +- 41 files changed, 1195 insertions(+), 320 deletions(-) create mode 100644 kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/TasksQueueIntegrationTest.kt delete mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/SendTransactionParams.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TaskFinishListener.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java delete mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionInterceptorImpl.java delete mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionProcessImpl.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcess.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcessImpl.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionParamsProcessImpl.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/PaymentTransactionParams.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/TransactionParams.java create mode 100644 kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/TasksQueueTest.kt diff --git a/build.gradle b/build.gradle index ae8521cd..481f9762 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { jcenter() } dependencies { - classpath "com.android.tools.build:gradle:3.4.2" + classpath 'com.android.tools.build:gradle:3.5.0' classpath 'org.jacoco:org.jacoco.core:0.8.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f452f1c3..64700d5c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Aug 13 08:37:24 IDT 2019 +#Wed Aug 21 09:30:12 IDT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/kin-sdk/kin-sdk-lib/build.gradle b/kin-sdk/kin-sdk-lib/build.gradle index 18c32227..7f8769f6 100644 --- a/kin-sdk/kin-sdk-lib/build.gradle +++ b/kin-sdk/kin-sdk-lib/build.gradle @@ -50,7 +50,7 @@ dependencies { api 'com.github.kinecosystem:kin-utils-android:1.1' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.23.0' + testImplementation 'org.mockito:mockito-core:2.27.0' testImplementation 'org.hamcrest:hamcrest-library:1.3' testImplementation 'com.squareup.okhttp3:mockwebserver:3.9.1' testImplementation 'org.robolectric:robolectric:4.3' diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueManagerIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueManagerIntegrationTest.kt index afc6081a..d9d239f5 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueManagerIntegrationTest.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueManagerIntegrationTest.kt @@ -20,10 +20,10 @@ class PaymentQueueManagerIntegrationTest { const val MAX_NUM_OF_PAYMENTS = 5 } + private var eventsManager = EventsManagerImpl() private var queueScheduler = FakeIntegrationTestQueueScheduler() - private var txTaskQueueManager = TransactionTasksQueueManagerImpl() + private var txTaskQueueManager = TransactionTasksQueueManagerImpl(eventsManager) private var pendingBalanceUpdater = PendingBalanceUpdaterImpl() - private var eventsManager = EventsManagerImpl() private lateinit var paymentQueueManager: PaymentQueueManager private lateinit var destinationAccount: String diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/TasksQueueIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/TasksQueueIntegrationTest.kt new file mode 100644 index 00000000..bcdf7c2d --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/TasksQueueIntegrationTest.kt @@ -0,0 +1,30 @@ +package kin.sdk.internal.queue + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.Test + +class TasksQueueIntegrationTest { + + @Test + fun test() { + //given + val tasksQueue = TasksQueueImpl() + var value = 5 + + tasksQueue.execute { + value = 10 + Thread.sleep(15000) + } + tasksQueue.execute { + value = 10 + Thread.sleep(15000) + } + + + Thread.sleep(40000) + //then + assertThat(value, equalTo(10)) + } + +} \ No newline at end of file diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java index c9698e81..96002b84 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java @@ -2,13 +2,19 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import kin.sdk.exception.*; + +import java.math.BigDecimal; + +import kin.sdk.exception.AccountNotFoundException; +import kin.sdk.exception.CryptoException; +import kin.sdk.exception.InsufficientKinException; +import kin.sdk.exception.OperationFailedException; +import kin.sdk.exception.TransactionFailedException; import kin.sdk.queue.PaymentQueue; import kin.sdk.transactiondata.PaymentTransaction; +import kin.sdk.transactiondata.TransactionParams; import kin.utils.Request; -import java.math.BigDecimal; - /** * Represents an account which holds Kin. */ @@ -20,7 +26,12 @@ public interface KinAccount { @Nullable String getPublicAddress(); + // TODO: 2019-08-21 finalize the deprecated texts + /** + * @deprecated This method was deprecated in version 2.0.0, please use the PaymentQueue, + * TransactionInterceptor and/or sendTransaction(sync or not) classes + * * Build a Transaction object of the given amount in kin, to the specified public address. *

See {@link KinAccount#buildTransactionSync(String, BigDecimal, int)} for possibles errors

* @@ -33,6 +44,9 @@ public interface KinAccount { Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee); /** + * @deprecated This method was deprecated in version 2.0.0, please use the PaymentQueue, + * TransactionInterceptor and/or sendTransaction(sync or not) classes + * * Build a Transaction object of the given amount in kin, to the specified public address and with a memo(that * can be empty or null). *

See {@link KinAccount#buildTransactionSync(String, BigDecimal, int, String)} for possibles errors

@@ -49,6 +63,9 @@ Request buildTransaction(@NonNull String publicAddress, @Non @Nullable String memo); /** + * @deprecated This method was deprecated in version 2.0.0, please use the PaymentQueue, + * TransactionInterceptor and/or sendTransaction(sync or not) classes + * * Create {@link Request} for signing and sending a transaction *

See {@link KinAccount#sendTransactionSync(PaymentTransaction)} for possibles errors

* @@ -60,6 +77,9 @@ Request buildTransaction(@NonNull String publicAddress, @Non Request sendTransaction(PaymentTransaction transaction); /** + * @deprecated This method was deprecated in version 2.0.0, please use the PaymentQueue, + * TransactionInterceptor and/or sendTransaction(sync or not) classes + * * Create {@link Request} for signing and sending a transaction from a whitelist. * whitelist a transaction means that the user will not pay any fee(if your App is in the Kin whitelist) *

See {@link KinAccount#sendWhitelistTransactionSync(String)} for possibles errors

@@ -72,6 +92,9 @@ Request buildTransaction(@NonNull String publicAddress, @Non Request sendWhitelistTransaction(String whitelist); /** + * @deprecated This method was deprecated in version 2.0.0, please use the PaymentQueue, + * TransactionInterceptor and/or sendTransaction(sync or not) classes + * * Build a Transaction object of the given amount in kin, to the specified public address. *

Note: This method accesses the network, and should not be called on the android main thread.

* @@ -86,6 +109,9 @@ Request buildTransaction(@NonNull String publicAddress, @Non PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException; /** + * @deprecated This method was deprecated in version 2.0.0, please use the PaymentQueue, + * TransactionInterceptor and/or sendTransaction(sync or not) classes + * * Build a Transaction object of the given amount in kin, to the specified public address and with a memo(that * can be empty or null). *

Note: This method accesses the network, and should not be called on the android main thread.

@@ -104,6 +130,9 @@ PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull @Nullable String memo) throws OperationFailedException; /** + * @deprecated This method was deprecated in version 2.0.0, please use the PaymentQueue, + * TransactionInterceptor and/or sendTransaction(sync or not) classes + * * send a transaction. *

Note: This method accesses the network, and should not be called on the android main thread.

* @@ -119,6 +148,9 @@ PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull TransactionId sendTransactionSync(PaymentTransaction transaction) throws OperationFailedException; /** + * @deprecated This method was deprecated in version 2.0.0, please use the PaymentQueue, + * TransactionInterceptor and/or sendTransaction(sync or not) classes + * * send a whitelist transaction. * whitelist a transaction means that the user will not pay any fee(if your App is in the Kin whitelist) *

Note: This method accesses the network, and should not be called on the android main thread.

@@ -136,9 +168,10 @@ PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull /** * send a transaction. - *

Note: This method accesses the network, and should not be called on the android main thread.

+ *

Note: This method accesses the network, and should not be called on the android + * main thread.

* - * @param transaction is the transaction object to send. + * @param transactionParams is the transaction parameters which define the transaction object to send. * @param interceptor is the interceptor for the transaction before it is being sent. * @return TransactionId the transaction identifier. * @throws AccountNotFoundException if the sender or destination account was not created. @@ -146,17 +179,18 @@ PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull * @throws TransactionFailedException if transaction failed, contains blockchain failure details. * @throws OperationFailedException other error occurred. */ - TransactionId sendTransactionSync(SendTransactionParams transaction, TransactionInterceptor interceptor) throws OperationFailedException; + TransactionId sendTransactionSync(TransactionParams transactionParams, TransactionInterceptor interceptor) throws OperationFailedException; /** * Create {@link Request} for signing and sending a transaction - *

See {@link KinAccount#sendTransactionSync(PaymentTransaction)} for possibles errors

+ *

See {@link KinAccount#sendTransactionSync(TransactionParams, TransactionInterceptor)} + * for possibles errors

* - * @param transaction is the transaction object to send. + * @param transactionParams is the transaction parameters which define the transaction object to send. * @param interceptor is the interceptor for the transaction before it is being sent. * @return {@code Request}, TransactionId - the transaction identifier. */ - Request sendTransaction(SendTransactionParams transaction, TransactionInterceptor interceptor); + Request sendTransaction(TransactionParams transactionParams, TransactionInterceptor interceptor); /** * @return the payment queue. diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java index 748ca264..1ce7584c 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java @@ -5,10 +5,23 @@ import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + import kin.base.KeyPair; import kin.base.Network; import kin.base.Server; -import kin.sdk.exception.*; +import kin.sdk.exception.CorruptedDataException; +import kin.sdk.exception.CreateAccountException; +import kin.sdk.exception.CryptoException; +import kin.sdk.exception.DeleteAccountException; +import kin.sdk.exception.LoadAccountException; +import kin.sdk.exception.OperationFailedException; import kin.sdk.internal.account.KinAccountImpl; import kin.sdk.internal.backuprestore.BackupRestore; import kin.sdk.internal.backuprestore.BackupRestoreImpl; @@ -21,13 +34,6 @@ import kin.sdk.internal.storage.SharedPrefStore; import kin.utils.Request; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - import static kin.sdk.internal.Utils.checkNotNull; /** @@ -262,7 +268,7 @@ public Environment getEnvironment() { @NonNull private KinAccountImpl createNewKinAccount(KeyPair account) { return new KinAccountImpl(account, backupRestore, transactionSender, - accountInfoRetriever, blockchainEventsCreator); + accountInfoRetriever, blockchainEventsCreator, generalBlockchainInfoRetriever); } /** diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/SendTransactionParams.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/SendTransactionParams.java deleted file mode 100644 index 12f64b50..00000000 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/SendTransactionParams.java +++ /dev/null @@ -1,44 +0,0 @@ -package kin.sdk; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import kin.base.Memo; -import kin.base.Operation; - -import java.math.BigDecimal; -import java.util.List; - -public class SendTransactionParams { - - // TODO: 2019-08-06 implement - - // Private Constructor, cannot init from outside, only by factory methods - private SendTransactionParams() { - - } - - public static SendTransactionParams createSendPaymentParams(@NonNull String publicAddress, - @NonNull BigDecimal amount, int fee) { - return null; - } - - public static SendTransactionParams createSendPaymentParams(@NonNull String publicAddress, - @NonNull BigDecimal amount, int fee, - @Nullable String memo) { - return null; - } - - public List operations() { - return null; - } - - public int fee() { - return 0; - } - - @Nullable - public Memo memo() { - return null; - } - -} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java index 9e84988f..22ad388f 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java @@ -2,12 +2,14 @@ import android.support.annotation.NonNull; + +import java.math.BigDecimal; +import java.util.ArrayList; + import kin.base.responses.SubmitTransactionResponse; import kin.base.responses.SubmitTransactionResponse.Extras.ResultCodes; import kin.sdk.exception.TransactionFailedException; -import java.util.ArrayList; - public final class Utils { private Utils() { @@ -43,4 +45,24 @@ public static void checkNotEmpty(String string, String paramName) { throw new IllegalArgumentException(paramName + " cannot be null or empty."); } } + + public static void checkForNegativeFee(int fee) { + if (fee < 0) { + throw new IllegalArgumentException("Fee can't be negative"); + } + } + + public static void checkForNegativeAmount(@NonNull BigDecimal amount) { + if (amount.signum() == -1) { + throw new IllegalArgumentException("Amount can't be negative"); + } + } + + @SuppressWarnings("ConstantConditions") + public static void checkAddressNotEmpty(@NonNull String publicAddress) { + if (publicAddress == null || publicAddress.isEmpty()) { + throw new IllegalArgumentException("Addressee not valid - public address can't be " + + "null or empty"); + } + } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java index ccf7b0c8..c724dd4f 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java @@ -2,15 +2,19 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; + +import java.math.BigDecimal; +import java.util.concurrent.Callable; + import kin.sdk.Balance; import kin.sdk.KinAccount; import kin.sdk.TransactionId; +import kin.sdk.TransactionInterceptor; +import kin.sdk.exception.OperationFailedException; import kin.sdk.transactiondata.PaymentTransaction; +import kin.sdk.transactiondata.TransactionParams; import kin.utils.Request; -import java.math.BigDecimal; -import java.util.concurrent.Callable; - abstract class AbstractKinAccount implements KinAccount { @NonNull @@ -58,6 +62,28 @@ public TransactionId call() throws Exception { }); } + @Override + public Request sendTransaction(final TransactionParams transactionParams, + final TransactionInterceptor interceptor) { + return new Request<>(new Callable() { + @Override + public TransactionId call() throws OperationFailedException { + return sendTransactionSync(transactionParams, interceptor); + } + }); + } + + @Override + public Request getPendingBalance() { + return new Request<>(new Callable() { + @Override + public Balance call() { + return getPendingBalanceSync(); + } + }); + } + + @NonNull @Override public Request getBalance() { diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/KinAccountImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/KinAccountImpl.java index 84ef3d41..108c1dcb 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/KinAccountImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/KinAccountImpl.java @@ -2,22 +2,29 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; + +import java.math.BigDecimal; + import kin.base.KeyPair; -import kin.sdk.*; +import kin.sdk.Balance; +import kin.sdk.EventListener; +import kin.sdk.ListenerRegistration; +import kin.sdk.PaymentInfo; +import kin.sdk.TransactionId; +import kin.sdk.TransactionInterceptor; import kin.sdk.exception.AccountDeletedException; import kin.sdk.exception.CryptoException; import kin.sdk.exception.OperationFailedException; import kin.sdk.internal.backuprestore.BackupRestore; import kin.sdk.internal.blockchain.AccountInfoRetriever; +import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetriever; import kin.sdk.internal.blockchain.TransactionSender; import kin.sdk.internal.blockchain.events.BlockchainEvents; import kin.sdk.internal.blockchain.events.BlockchainEventsCreator; import kin.sdk.internal.queue.PaymentQueueImpl; import kin.sdk.queue.PaymentQueue; import kin.sdk.transactiondata.PaymentTransaction; -import kin.utils.Request; - -import java.math.BigDecimal; +import kin.sdk.transactiondata.TransactionParams; public final class KinAccountImpl extends AbstractKinAccount { @@ -29,14 +36,18 @@ public final class KinAccountImpl extends AbstractKinAccount { private final PaymentQueue paymentQueue; private boolean isDeleted = false; - public KinAccountImpl(KeyPair account, BackupRestore backupRestore, TransactionSender transactionSender, - AccountInfoRetriever accountInfoRetriever, BlockchainEventsCreator blockchainEventsCreator) { + public KinAccountImpl(KeyPair account, BackupRestore backupRestore, + TransactionSender transactionSender, + AccountInfoRetriever accountInfoRetriever, + BlockchainEventsCreator blockchainEventsCreator, + GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever) { this.account = account; this.backupRestore = backupRestore; this.transactionSender = transactionSender; this.accountInfoRetriever = accountInfoRetriever; this.blockchainEvents = blockchainEventsCreator.create(account.getAccountId()); - this.paymentQueue = new PaymentQueueImpl(account.getAccountId()); + this.paymentQueue = new PaymentQueueImpl(account, transactionSender, + generalBlockchainInfoRetriever); } @Override @@ -48,17 +59,19 @@ public String getPublicAddress() { } @Override - public PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, + public PaymentTransaction buildTransactionSync(@NonNull String publicAddress, + @NonNull BigDecimal amount, int fee) throws OperationFailedException { checkValidAccount(); - return transactionSender.buildTransaction(account, publicAddress, amount, fee); + return transactionSender.buildPaymentTransaction(account, publicAddress, amount, fee); } @Override - public PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, + public PaymentTransaction buildTransactionSync(@NonNull String publicAddress, + @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException { checkValidAccount(); - return transactionSender.buildTransaction(account, publicAddress, amount, fee, memo); + return transactionSender.buildPaymentTransaction(account, publicAddress, amount, fee, memo); } @NonNull @@ -76,14 +89,9 @@ public TransactionId sendWhitelistTransactionSync(String whitelist) throws Opera } @Override - public TransactionId sendTransactionSync(SendTransactionParams transaction, TransactionInterceptor interceptor) throws OperationFailedException { - return null; // TODO: 2019-08-15 implement - } - - @Override - public Request sendTransaction(SendTransactionParams transaction, - TransactionInterceptor interceptor) { - return null; // TODO: 2019-08-15 implement + public TransactionId sendTransactionSync(TransactionParams transactionParams, + TransactionInterceptor interceptor) throws OperationFailedException { + return ((PaymentQueueImpl) paymentQueue).enqueueTransactionParams(transactionParams, interceptor); } @Override @@ -91,11 +99,6 @@ public PaymentQueue paymentQueue() { return paymentQueue; } - @Override - public Request getPendingBalance() { - return null; // TODO: 2019-08-15 implement - } - @NonNull @Override public Balance getPendingBalanceSync() { diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/GeneralBlockchainInfoRetriever.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/GeneralBlockchainInfoRetriever.java index 71925e3d..fd3ccb44 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/GeneralBlockchainInfoRetriever.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/GeneralBlockchainInfoRetriever.java @@ -2,7 +2,7 @@ import kin.sdk.exception.OperationFailedException; -interface GeneralBlockchainInfoRetriever { +public interface GeneralBlockchainInfoRetriever { /** * Get the current minimum fee that the network charges per operation. diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/GeneralBlockchainInfoRetrieverImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/GeneralBlockchainInfoRetrieverImpl.java index bb137778..8bb18bcf 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/GeneralBlockchainInfoRetrieverImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/GeneralBlockchainInfoRetrieverImpl.java @@ -1,5 +1,8 @@ package kin.sdk.internal.blockchain; +import java.io.IOException; +import java.util.ArrayList; + import kin.base.Server; import kin.base.requests.LedgersRequestBuilder; import kin.base.requests.RequestBuilder; @@ -7,11 +10,10 @@ import kin.base.responses.Page; import kin.sdk.exception.OperationFailedException; -import java.io.IOException; -import java.util.ArrayList; - public class GeneralBlockchainInfoRetrieverImpl implements GeneralBlockchainInfoRetriever { + private static final String ERROR_MESSAGE = "Couldn't retrieve minimum fee data"; + private final Server server; public GeneralBlockchainInfoRetrieverImpl(Server server) { @@ -21,6 +23,7 @@ public GeneralBlockchainInfoRetrieverImpl(Server server) { @Override public long getMinimumFeeSync() throws OperationFailedException { LedgersRequestBuilder builder = server.ledgers().order(RequestBuilder.Order.DESC).limit(1); + try { Page response = builder.execute(); ArrayList records = response.getRecords(); @@ -32,7 +35,7 @@ public long getMinimumFeeSync() throws OperationFailedException { } throw new OperationFailedException("Couldn't retrieve minimum fee data"); } catch (IOException e) { - throw new OperationFailedException(e); + throw new OperationFailedException(ERROR_MESSAGE, e); } } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java index 9c007474..3531767f 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java @@ -3,27 +3,40 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; -import kin.base.*; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.util.List; + +import kin.base.AssetTypeNative; +import kin.base.KeyPair; +import kin.base.Memo; +import kin.base.PaymentOperation; +import kin.base.Server; import kin.base.Transaction.Builder; import kin.base.responses.AccountResponse; import kin.base.responses.HttpResponseException; import kin.base.responses.SubmitTransactionResponse; import kin.sdk.TransactionId; -import kin.sdk.exception.*; +import kin.sdk.exception.AccountNotFoundException; +import kin.sdk.exception.IllegalAmountException; +import kin.sdk.exception.InsufficientFeeException; +import kin.sdk.exception.InsufficientKinException; +import kin.sdk.exception.OperationFailedException; +import kin.sdk.exception.TransactionFailedException; import kin.sdk.internal.Utils; import kin.sdk.internal.data.TransactionIdImpl; +import kin.sdk.queue.PendingPayment; +import kin.sdk.transactiondata.BatchPaymentTransaction; import kin.sdk.transactiondata.PaymentTransaction; import kin.sdk.transactiondata.Transaction; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; -import java.util.List; - public class TransactionSender { - private static final int MEMO_BYTES_LENGTH_LIMIT = 21; //Memo length limitation(in bytes) is 28 but we add 7 more bytes which includes the appId and some characters. - private static final int MAX_NUM_OF_DECIMAL_PLACES = 4 ; + private static final int MEMO_BYTES_LENGTH_LIMIT = 21; //Memo length limitation(in bytes) is + // 28 but we add 7 more bytes which includes the appId and some characters. + private static final int MAX_NUM_OF_DECIMAL_PLACES = 4; private static String MEMO_APP_ID_VERSION_PREFIX = "1"; private static String MEMO_DELIMITER = "-"; private static final String INSUFFICIENT_KIN_RESULT_CODE = "op_underfunded"; @@ -37,14 +50,17 @@ public TransactionSender(Server server, String appId) { this.appId = appId; } - public PaymentTransaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, - @NonNull BigDecimal amount, - int fee) throws OperationFailedException { - return buildTransaction(from, publicAddress, amount, fee, null); + public PaymentTransaction buildPaymentTransaction(@NonNull KeyPair from, + @NonNull String publicAddress, + @NonNull BigDecimal amount, + int fee) throws OperationFailedException { + return buildPaymentTransaction(from, publicAddress, amount, fee, null); } - public PaymentTransaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, - int fee, @Nullable String memo) throws OperationFailedException { + public PaymentTransaction buildPaymentTransaction(@NonNull KeyPair from, + @NonNull String publicAddress, + @NonNull BigDecimal amount, + int fee, @Nullable String memo) throws OperationFailedException { checkParams(from, publicAddress, amount, fee, memo); if (appId != null && !appId.equals("")) { memo = addAppIdToMemo(memo); @@ -53,8 +69,8 @@ public PaymentTransaction buildTransaction(@NonNull KeyPair from, @NonNull Strin KeyPair addressee = generateAddresseeKeyPair(publicAddress); AccountResponse sourceAccount = loadSourceAccount(from); verifyAddresseeAccount(generateAddresseeKeyPair(addressee.getAccountId())); - kin.base.Transaction stellarTransaction = buildStellarTransaction(from, amount, addressee, sourceAccount, fee - , memo); + kin.base.Transaction stellarTransaction = buildStellarTransaction(from, amount, addressee + , sourceAccount, fee, memo); return new PaymentTransaction(stellarTransaction, addressee.getAccountId(), amount, memo); } @@ -87,42 +103,30 @@ private String addAppIdToMemo(@Nullable String memo) { return sb.toString(); } - private void checkParams(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, + private void checkParams(@NonNull KeyPair from, @NonNull String publicAddress, + @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException { Utils.checkNotNull(from, "account"); Utils.checkNotNull(amount, "amount"); validateAmountDecimalPoint(amount); - checkForNegativeFee(fee); - checkAddressNotEmpty(publicAddress); - checkForNegativeAmount(amount); + Utils.checkForNegativeFee(fee); + Utils.checkAddressNotEmpty(publicAddress); + Utils.checkForNegativeAmount(amount); checkMemo(memo); } + private void checkBatchPaymentkParams(@NonNull KeyPair from, int fee, @Nullable String memo) { + Utils.checkNotNull(from, "account"); + Utils.checkForNegativeFee(fee); + checkMemo(memo); + } private void validateAmountDecimalPoint(BigDecimal amount) throws OperationFailedException { BigDecimal amountWithoutTrailingZeros = amount.stripTrailingZeros(); int numOfDecimalPlaces = amountWithoutTrailingZeros.scale(); if (numOfDecimalPlaces > MAX_NUM_OF_DECIMAL_PLACES) { - throw new IllegalAmountException("amount can't have more then 5 digits after the decimal point"); - } - } - - @SuppressWarnings("ConstantConditions") - private void checkAddressNotEmpty(@NonNull String publicAddress) { - if (publicAddress == null || publicAddress.isEmpty()) { - throw new IllegalArgumentException("Addressee not valid - public address can't be null or empty"); - } - } - - private void checkForNegativeAmount(@NonNull BigDecimal amount) { - if (amount.signum() == -1) { - throw new IllegalArgumentException("Amount can't be negative"); - } - } - - private void checkForNegativeFee(int fee) { - if (fee < 0) { - throw new IllegalArgumentException("Fee can't be negative"); + throw new IllegalAmountException("amount can't have more then 5 digits after the " + + "decimal point"); } } @@ -145,12 +149,56 @@ private KeyPair generateAddresseeKeyPair(@NonNull String publicAddress) throws O } } + void foo() { + + } + @NonNull - private kin.base.Transaction buildStellarTransaction(@NonNull KeyPair from, @NonNull BigDecimal amount, KeyPair addressee, - AccountResponse sourceAccount, int fee, @Nullable String memo) { - Builder transactionBuilder = new Builder(sourceAccount) - .addOperation( - new PaymentOperation.Builder(addressee, new AssetTypeNative(), amount.toString()).build()); + private kin.base.Transaction buildStellarTransaction(@NonNull KeyPair from, + @NonNull BigDecimal amount, + KeyPair addressee, + AccountResponse sourceAccount, int fee, + @Nullable String memo) { + Builder transactionBuilder = new Builder(sourceAccount); + addPaymentOperationToTransaction(transactionBuilder, addressee, amount); + + return addTransactionParametersAndSign(from, fee, memo, transactionBuilder); + } + + + public BatchPaymentTransaction buildBatchPaymentTransaction(@NonNull KeyPair from, + @NonNull List pendingPayments, + int fee, @Nullable String memo) throws OperationFailedException { + + checkBatchPaymentkParams(from, fee, memo); + if (appId != null && !appId.equals("")) { + memo = addAppIdToMemo(memo); + } + + AccountResponse sourceAccount = loadSourceAccount(from); + + Builder transactionBuilder = new Builder(sourceAccount); + for (PendingPayment pendingPayment : pendingPayments) { + KeyPair destination = + generateAddresseeKeyPair(pendingPayment.destinationPublicAddress()); + addPaymentOperationToTransaction(transactionBuilder, destination, + pendingPayment.amount()); + } + + kin.base.Transaction transaction = addTransactionParametersAndSign(from, fee, memo, + transactionBuilder); + return new BatchPaymentTransaction(transaction); + } + + private void addPaymentOperationToTransaction(Builder transactionBuilder, KeyPair destination, + BigDecimal amount) { + transactionBuilder.addOperation(new PaymentOperation.Builder(destination, + new AssetTypeNative(), amount.toString()).build()); + } + + private kin.base.Transaction addTransactionParametersAndSign(@NonNull KeyPair from, int fee, + String memo, + Builder transactionBuilder) { transactionBuilder.addFee(fee); if (memo != null) { transactionBuilder.addMemo(Memo.text(memo)); @@ -208,7 +256,8 @@ private TransactionId sendTransaction(kin.base.Transaction transaction) throws O private TransactionId createFailureException(SubmitTransactionResponse response) throws TransactionFailedException, InsufficientKinException, InsufficientFeeException { - TransactionFailedException transactionException = Utils.createTransactionException(response); + TransactionFailedException transactionException = + Utils.createTransactionException(response); if (isInsufficientKinException(transactionException)) { throw new InsufficientKinException(); } else if (isInsufficientFeeException(transactionException)) { diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java index 4ed0c809..f736dfb2 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java @@ -2,6 +2,10 @@ public class EventsManagerImpl implements EventsManager { + public EventsManagerImpl(/*PaymentQueue.EventsListener eventListener*/) { + + } + // TODO: 2019-08-14 implement } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java index 3afa0068..43b0e24e 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java @@ -1,63 +1,103 @@ package kin.sdk.internal.queue; import android.support.annotation.NonNull; + +import java.math.BigDecimal; + +import kin.base.KeyPair; +import kin.sdk.TransactionId; import kin.sdk.TransactionInterceptor; import kin.sdk.exception.InsufficientKinException; +import kin.sdk.internal.Utils; +import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetriever; +import kin.sdk.internal.blockchain.TransactionSender; import kin.sdk.internal.data.PendingBalanceUpdaterImpl; +import kin.sdk.internal.events.EventsManager; import kin.sdk.internal.events.EventsManagerImpl; import kin.sdk.queue.PaymentQueue; import kin.sdk.queue.PendingPayment; - -import java.math.BigDecimal; +import kin.sdk.transactiondata.TransactionParams; public class PaymentQueueImpl implements PaymentQueue { - private static final long DELAY_BETWEEN_PAYMENTS_MILLIS = 3000; // TODO: 2019-08-15 get those number from somewhere - private static final long QUEUE_TIMEOUT_MILLIS = 10000; // TODO: 2019-08-15 get those number from somewhere + private static final long DELAY_BETWEEN_PAYMENTS_MILLIS = 3000; // TODO: 2019-08-15 get those + // number from somewhere + private static final long QUEUE_TIMEOUT_MILLIS = 10000; // TODO: 2019-08-15 get those + // number from somewhere private static final int MAX_NUM_OF_PAYMENTS = 100; - private final String sourcePublicAddress; + private final KeyPair accountFrom; private final PaymentQueueManager paymentQueueManager; private final TransactionTasksQueueManager txTasksQueueManager; + private final EventsManager eventsManager; + private final TasksQueueImpl tasksQueue; - public PaymentQueueImpl(@NonNull String sourcePublicAddress) { - this.sourcePublicAddress = sourcePublicAddress; - this.txTasksQueueManager = new TransactionTasksQueueManagerImpl(); - this.paymentQueueManager = new PaymentQueueManagerImpl(txTasksQueueManager, new QueueSchedulerImpl(), - new PendingBalanceUpdaterImpl(), new EventsManagerImpl(), + public PaymentQueueImpl(@NonNull KeyPair accountFrom, TransactionSender transactionSender, + GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever) { + this.accountFrom = accountFrom; + this.eventsManager = new EventsManagerImpl(); + tasksQueue = new TasksQueueImpl(transactionSender, eventsManager, + generalBlockchainInfoRetriever, accountFrom); + this.txTasksQueueManager = new TransactionTasksQueueManagerImpl(tasksQueue, eventsManager + , MAX_NUM_OF_PAYMENTS); + this.paymentQueueManager = new PaymentQueueManagerImpl(txTasksQueueManager, + new QueueSchedulerImpl(), + new PendingBalanceUpdaterImpl(), eventsManager, DELAY_BETWEEN_PAYMENTS_MILLIS, QUEUE_TIMEOUT_MILLIS, MAX_NUM_OF_PAYMENTS); } @Override - public PendingPayment enqueuePayment(@NonNull String publicAddress, @NonNull BigDecimal amount) throws InsufficientKinException { - return enqueuePayment(publicAddress, amount, null); + public PendingPayment enqueuePayment(@NonNull String destinationPublicAddress, + @NonNull BigDecimal amount) throws InsufficientKinException { + return enqueuePayment(destinationPublicAddress, amount, null); } @Override - public PendingPayment enqueuePayment(@NonNull String publicAddress, @NonNull BigDecimal amount, Object metadata) throws InsufficientKinException { - PendingPayment pendingPayment = new PendingPaymentImpl(publicAddress, sourcePublicAddress, amount, metadata); + public PendingPayment enqueuePayment(@NonNull String destinationPublicAddress, + @NonNull BigDecimal amount, Object metadata) throws InsufficientKinException { + Utils.checkForNegativeAmount(amount); + Utils.checkAddressNotEmpty(destinationPublicAddress); + PendingPayment pendingPayment = new PendingPaymentImpl(destinationPublicAddress, + accountFrom.getAccountId(), amount, metadata); paymentQueueManager.enqueue(pendingPayment); return pendingPayment; } + /** + * enqueue a non blocking transaction. + * This transaction will be pushed to the top of the queue. + * + * @param transactionParams the transaction parameters + * @param interceptor the interceptor to set + */ + public TransactionId enqueueTransactionParams(TransactionParams transactionParams, + TransactionInterceptor interceptor) { + tasksQueue.setTransactionInterceptor(interceptor); + tasksQueue.scheduleTransactionParamsTask(transactionParams); + return null; + // TODO: 2019-09-02 because we want it to be sync then we should handle it here and + // return the transaction id + } + @Override - public void setTransactionInterceptor(TransactionInterceptor interceptor) { - // TODO: 2019-08-15 implement + public void setTransactionInterceptor(TransactionInterceptor transactionInterceptor) { + tasksQueue.setTransactionInterceptor(transactionInterceptor); } @Override - public void addEventsListener(EventsListener listener) { - // TODO: 2019-08-15 implement + public void addEventsListener(EventsListener eventListener) { + // TODO: 2019-08-27 did we agree that we call it add? meaning we will maintain a list of + // them in the events manager? } @Override public void setFee(int fee) { - // TODO: 2019-08-14 should we keep it here or set it directly into the task queue manager + tasksQueue.setFee(fee); } @Override public boolean transactionInProgress() { - return false; // TODO: 2019-08-15 implement + return txTasksQueueManager.transactionInProgress(); } @Override diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java index feaad03a..d43c0cfe 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java @@ -1,13 +1,13 @@ package kin.sdk.internal.queue; +import java.util.ArrayList; +import java.util.List; + import kin.sdk.Balance; import kin.sdk.internal.data.PendingBalanceUpdater; import kin.sdk.internal.events.EventsManager; import kin.sdk.queue.PendingPayment; -import java.util.ArrayList; -import java.util.List; - class PaymentQueueManagerImpl implements PaymentQueueManager { private final TransactionTasksQueueManager txTasksQueueManager; @@ -40,7 +40,7 @@ public void enqueue(final PendingPayment pendingPayment) { @Override public void run() { // 1. validate pending balance - validatePendingBalance(); // TODO: 2019-08-15 handle it + validatePendingBalance(); // TODO: 2019-08-15 handle it when starting the task if (getPendingPaymentCount() == maxNumOfPayments - 1) { // 2. add to queue diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java new file mode 100644 index 00000000..04d62e42 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java @@ -0,0 +1,81 @@ +package kin.sdk.internal.queue; + +import java.util.List; + +import kin.base.KeyPair; +import kin.sdk.TransactionId; +import kin.sdk.TransactionInterceptor; +import kin.sdk.exception.OperationFailedException; +import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetriever; +import kin.sdk.internal.blockchain.TransactionSender; +import kin.sdk.internal.events.EventsManager; +import kin.sdk.queue.BatchPaymentTransactionProcessImpl; +import kin.sdk.queue.PendingPayment; +import kin.sdk.queue.TransactionProcess; +import kin.sdk.transactiondata.BatchPaymentTransaction; + +class SendPendingPaymentsTask extends SendTransactionTask { + + private final List pendingPayments; + private final TransactionSender transactionSender; + private final TransactionInterceptor transactionInterceptor; + private final EventsManager eventsManager; + private final GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever; + private int fee; + private final KeyPair accountFrom; + + SendPendingPaymentsTask(List pendingPayments, + TransactionSender transactionSender, + TransactionInterceptor transactionInterceptor, + TaskFinishListener taskFinishListener, + EventsManager eventsManager, + GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever, + int fee, KeyPair accountFrom) { + super(transactionSender, transactionInterceptor, taskFinishListener); + this.pendingPayments = pendingPayments; + this.transactionSender = transactionSender; + this.transactionInterceptor = transactionInterceptor; + this.eventsManager = eventsManager; + this.generalBlockchainInfoRetriever = generalBlockchainInfoRetriever; + this.fee = fee; + this.accountFrom = accountFrom; + } + + @Override + public void run() { + setFeeIfNeeded(); + super.run(); + } + + @Override + void invokeInterceptor() throws Exception { + TransactionProcess transactionProcess = + new BatchPaymentTransactionProcessImpl(transactionSender, pendingPayments, + accountFrom, fee, eventsManager); + // TODO: 2019-09-01 if we give them the TransactionProcess then they wont have access to + // the pending payments... need to be changed + TransactionId transactionId = + transactionInterceptor.interceptTransactionSending(transactionProcess); + handleTransactionFinished(transactionId); + } + + @Override + void buildAndSendTransaction() throws OperationFailedException { + BatchPaymentTransaction transaction = + transactionSender.buildBatchPaymentTransaction(accountFrom, + pendingPayments, fee, null); + sendTransaction(transaction); + } + + private void setFeeIfNeeded() { + // If fee is not set then retrieve the minimum fee. + if (fee < 0) { + try { + fee = (int) generalBlockchainInfoRetriever.getMinimumFeeSync(); + } catch (OperationFailedException e) { + // TODO: 2019-08-27 need to maybe add an exception to the events manager or + // something + } + } + } +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java new file mode 100644 index 00000000..ce6f9380 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java @@ -0,0 +1,66 @@ +package kin.sdk.internal.queue; + +import kin.base.KeyPair; +import kin.sdk.TransactionId; +import kin.sdk.TransactionInterceptor; +import kin.sdk.exception.OperationFailedException; +import kin.sdk.internal.blockchain.TransactionSender; +import kin.sdk.internal.events.EventsManager; +import kin.sdk.queue.TransactionParamsProcessImpl; +import kin.sdk.queue.TransactionProcess; +import kin.sdk.transactiondata.PaymentTransactionParams; +import kin.sdk.transactiondata.Transaction; +import kin.sdk.transactiondata.TransactionParams; + +class SendTransactionParamsTask extends SendTransactionTask { + + private final TransactionParams transactionParams; + private final TransactionSender transactionSender; + private final TransactionInterceptor transactionInterceptor; + private final EventsManager eventsManager; + private final KeyPair accountFrom; + + SendTransactionParamsTask(TransactionParams transactionParams, + TransactionSender transactionSender, + TransactionInterceptor transactionInterceptor, + TaskFinishListener taskFinishListener, + EventsManager eventsManager, + KeyPair accountFrom) { + super(transactionSender, transactionInterceptor, taskFinishListener); + this.transactionParams = transactionParams; + this.transactionSender = transactionSender; + this.transactionInterceptor = transactionInterceptor; + this.eventsManager = eventsManager; + this.accountFrom = accountFrom; + } + + @Override + void invokeInterceptor() throws Exception { + TransactionProcess transactionProcess = + new TransactionParamsProcessImpl(transactionSender, transactionParams, + accountFrom, eventsManager); + TransactionId transactionId = + transactionInterceptor.interceptTransactionSending(transactionProcess); + handleTransactionFinished(transactionId); + } + + @Override + void buildAndSendTransaction() throws OperationFailedException { + // TODO: 2019-09-02 don't like this instanceof of shit. maybe we should think about an + // Object Oriented Solution, + // for example, create a PaymentTransactionTask Class and in the future + // SomeOtherTransactionTask + Transaction transaction = null; + if (transactionParams instanceof PaymentTransactionParams) { + PaymentTransactionParams paymentTransactionParams = + (PaymentTransactionParams) transactionParams; + transaction = + transactionSender.buildPaymentTransaction(accountFrom, + paymentTransactionParams.destinationPublicAddress(), + paymentTransactionParams.amount(), paymentTransactionParams.fee(), + paymentTransactionParams.memo()); + } + sendTransaction(transaction); + } + // TODO: 2019-09-01 handle pending payment status +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java new file mode 100644 index 00000000..6b87219d --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java @@ -0,0 +1,57 @@ +package kin.sdk.internal.queue; + +import kin.sdk.TransactionId; +import kin.sdk.TransactionInterceptor; +import kin.sdk.exception.OperationFailedException; +import kin.sdk.internal.blockchain.TransactionSender; +import kin.sdk.transactiondata.Transaction; + +// A class that sends a transaction to the blockchain. +abstract class SendTransactionTask implements Runnable { + + private final TransactionSender transactionSender; + private final TransactionInterceptor transactionInterceptor; + private final TaskFinishListener taskFinishListener; + + SendTransactionTask(TransactionSender transactionSender, + TransactionInterceptor transactionInterceptor, + TaskFinishListener taskFinishListener) { + this.transactionSender = transactionSender; + this.transactionInterceptor = transactionInterceptor; + this.taskFinishListener = taskFinishListener; + } + + void sendTransaction(Transaction transaction) throws OperationFailedException { + TransactionId transactionId = transactionSender.sendTransaction(transaction); + handleTransactionFinished(transactionId); + } + + void handleTransactionFinished(TransactionId transactionId) { + // if transactionId is null then it means that the transaction has failed + if (transactionId != null) { + // TODO: 2019-09-01 invoke events for transaction success + } else { + // TODO: 2019-09-01 invoke events for transaction fail + } + } + + @Override + public void run() { + try { + if (transactionInterceptor != null) { + invokeInterceptor(); + } else { + // TODO: 2019-09-01 if they wanted to send a memo then they should intercept it. + // but maybe we should let them add memo easily without the need to intercept? + buildAndSendTransaction(); + } + } catch (Exception e) { + // TODO: 2019-08-26 use events manager and send them the exception if it is not a kin + // exception then create a KinException with the exception + } + } + + abstract void invokeInterceptor() throws Exception; + + abstract void buildAndSendTransaction() throws OperationFailedException; +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TaskFinishListener.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TaskFinishListener.java new file mode 100644 index 00000000..3a2a83a0 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TaskFinishListener.java @@ -0,0 +1,13 @@ +package kin.sdk.internal.queue; + +public interface TaskFinishListener { + + /** + * Callback for when a task has finished successfully or with failure. + * + * @param task the task itself + */ + void onTaskFinish(SendTransactionTask task); // TODO: 2019-08-27 check if we really need to + // give back the task itself + +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java new file mode 100644 index 00000000..2b3486b5 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java @@ -0,0 +1,67 @@ +package kin.sdk.internal.queue; + +import java.util.List; + +import kin.sdk.TransactionInterceptor; +import kin.sdk.queue.PendingPayment; +import kin.sdk.transactiondata.TransactionParams; + +public interface TasksQueue { + + /** + * Scheduling a batch payment transaction task to run as soon as possible. + * Task are running sequentially so if there are tasks in the queue then they will run before + * this task. + * If this task has high priority then it will enter to the top of the queue, meaning that it + * will be executed next. + * + * @param batchPendingPayments the list of batched pending payments from which we will create + * a batch transaction. + */ + void schedulePendingPaymentsTask(List batchPendingPayments); + + /** + * Scheduling a payment transaction params task to run as soon as possible. + * Task are running sequentially so if there are tasks in the queue then they will run before + * this task. + * If this task has high priority then it will enter to the top of the queue, meaning that it + * will be executed next. + * + * @param paymentTransactionParams is the object from which we will create the payment + * transaction + */ + void scheduleTransactionParamsTask(TransactionParams paymentTransactionParams); + + /** + * Stop all tasks in the queue. + *

Note that after calling this method then this task queue is not usable anymore and + * any call to scheduleTask wont work. + * You will need to create a new TasksQueue.

+ */ + void stopTaskQueue(); // TODO: 2019-08-27 should we even try to stop the queue or it will + // always live? i think it should live as long as needed + + /** + * Setter for the task finish listener. + * + * @param taskFinishListener is listener + */ + void setTaskFinishListener(TaskFinishListener taskFinishListener); + + /** + * Setter for the fee + * If this fee is below the minimum then the transaction will fail. + * If no fee will be set then the minimum blockchain fee wil be used. + * + * @param fee the amount of fee(in Quarks) for each payment (1 Quark = 0.00001 KIN). + */ + void setFee(int fee); + + /** + * Set a transaction interceptor. + * + * @param transactionInterceptor is the TransactionInterceptor to set. + *

See {@link TransactionInterceptor}

+ */ + void setTransactionInterceptor(TransactionInterceptor transactionInterceptor); +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java new file mode 100644 index 00000000..fd39063b --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java @@ -0,0 +1,102 @@ +package kin.sdk.internal.queue; + +import android.os.Handler; +import android.os.HandlerThread; +import android.support.annotation.NonNull; + +import java.util.List; + +import kin.base.KeyPair; +import kin.sdk.TransactionInterceptor; +import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetriever; +import kin.sdk.internal.blockchain.TransactionSender; +import kin.sdk.internal.events.EventsManager; +import kin.sdk.queue.PendingPayment; +import kin.sdk.transactiondata.TransactionParams; + +/** + * Class which handles the task queue. + * When finishing then please call to 'stopTaskQueue' in order to free the resources. + */ +class TasksQueueImpl extends HandlerThread implements TasksQueue { + + private static final String nameTag = "TaskQueueHandlerThread"; + + private final TransactionSender transactionSender; + private TransactionInterceptor transactionInterceptor; + private final EventsManager eventsManager; + private final GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever; + private final KeyPair accountFrom; + private TaskFinishListener taskFinishListener; + private Handler backgroundHandler; + private int fee = -1; + + TasksQueueImpl(@NonNull TransactionSender transactionSender, + @NonNull EventsManager eventsManager, + @NonNull GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever, + @NonNull KeyPair accountFrom) { + super(nameTag); + this.transactionSender = transactionSender; + this.eventsManager = eventsManager; + this.generalBlockchainInfoRetriever = generalBlockchainInfoRetriever; + this.accountFrom = accountFrom; + + start(); + // TODO: 2019-08-21 check the threading priority and what is the difference between this + // and Process.THREAD_PRIORITY_... + } + + @Override + protected void onLooperPrepared() { + super.onLooperPrepared(); + backgroundHandler = new Handler(getLooper()); + } + + @Override + public void schedulePendingPaymentsTask(List batchPendingPayments) { + if (backgroundHandler != null) { + SendPendingPaymentsTask sendPendingPaymentsTask = + new SendPendingPaymentsTask(batchPendingPayments, transactionSender, + transactionInterceptor, taskFinishListener, eventsManager, + generalBlockchainInfoRetriever, fee, accountFrom); + backgroundHandler.post(sendPendingPaymentsTask); + } + } + + @Override + public void scheduleTransactionParamsTask(TransactionParams transactionParams) { + if (backgroundHandler != null) { + SendTransactionParamsTask sendTransactionParamsTask = + new SendTransactionParamsTask(transactionParams, + transactionSender, + transactionInterceptor, taskFinishListener, eventsManager, accountFrom); + // TODO: 2019-08-27 although currently we only have one task each time and we are + // dealing with the param at the manager level, + // if in the future something will change in the manager then it will still post it + // at front of queue. if not needed then we can currently just do backgroundHandler + // .post(...) + backgroundHandler.postAtFrontOfQueue(sendTransactionParamsTask); + } + } + + @Override + public void stopTaskQueue() { + // TODO: 2019-08-22 need to call quit from outside when finishing + quit(); + } + + @Override + public void setTaskFinishListener(TaskFinishListener taskFinishListener) { + this.taskFinishListener = taskFinishListener; + } + + @Override + public void setFee(int fee) { + this.fee = fee; + } + + @Override + public void setTransactionInterceptor(TransactionInterceptor transactionInterceptor) { + this.transactionInterceptor = transactionInterceptor; + } +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionInterceptorImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionInterceptorImpl.java deleted file mode 100644 index 9d8b0b6f..00000000 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionInterceptorImpl.java +++ /dev/null @@ -1,16 +0,0 @@ -package kin.sdk.internal.queue; - -import kin.sdk.TransactionId; -import kin.sdk.TransactionInterceptor; -import kin.sdk.queue.TransactionProcess; - -public class TransactionInterceptorImpl implements TransactionInterceptor { - - // TODO: 2019-08-04 implement - - @Override - public TransactionId interceptTransactionSending(TransactionProcess transactionProcess) throws Exception { - return null; - } - -} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionProcessImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionProcessImpl.java deleted file mode 100644 index a5fcab5d..00000000 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionProcessImpl.java +++ /dev/null @@ -1,38 +0,0 @@ -package kin.sdk.internal.queue; - -import kin.sdk.TransactionId; -import kin.sdk.queue.PendingPayment; -import kin.sdk.queue.TransactionProcess; -import kin.sdk.transactiondata.Transaction; - -import java.util.List; - -public class TransactionProcessImpl implements TransactionProcess { - - // TODO: 2019-08-04 implement and add java docs - - @Override - public Transaction transaction() { - return null; - } - - @Override - public Transaction transaction(String memo) { - return null; - } - - @Override - public List payments() { - return null; - } - - @Override - public TransactionId send(Transaction transaction) { - return null; - } - - @Override - public TransactionId send(String whitelistPayload) { - return null; - } -} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java index 572bda26..e312ea73 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java @@ -1,15 +1,32 @@ package kin.sdk.internal.queue; -import kin.sdk.SendTransactionParams; -import kin.sdk.queue.PendingPayment; - import java.util.List; +import kin.sdk.queue.PendingPayment; +import kin.sdk.transactiondata.PaymentTransactionParams; + public interface TransactionTasksQueueManager { - // TODO: 2019-08-15 add java docs - void enqueue(List queueToSend); + /** + * enqueue pending payments list. + * + * @param pendingPayments the list of pending payments to enqueue. + */ + void enqueue(List pendingPayments); + + /** + * enqueue the transaction parameters object which will later on become a transaction. + * + * @param transactionParams the transaction parameters + */ + void enqueue(PaymentTransactionParams transactionParams); // TODO: 2019-08-26 should we use + // PaymentTransactionParams or TransactionParams - void enqueue(SendTransactionParams sendTransactionParams); + /** + * @return true if currently there is a transaction in progress, meaning it will return true if + * there is a transaction that was entered to the task queue and until it is finished(success + * or fail). + */ + boolean transactionInProgress(); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java index 389f6d89..9a097a01 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java @@ -1,19 +1,129 @@ package kin.sdk.internal.queue; -import kin.sdk.SendTransactionParams; +import java.util.ArrayDeque; +import java.util.List; +import java.util.Queue; + +import kin.sdk.internal.events.EventsManager; import kin.sdk.queue.PendingPayment; +import kin.sdk.transactiondata.PaymentTransactionParams; -import java.util.List; +/** + * Class which manage the transaction task queue. + */ +class TransactionTasksQueueManagerImpl implements TransactionTasksQueueManager, TaskFinishListener { + + private final TasksQueue taskQueue; + private final EventsManager eventsManager; + private final int maxNumOfPayments; + private Queue> batchPendingPaymentsQueue; + private PaymentTransactionParams currentTransactionParams; + private boolean transactionInProgress = false; -public class TransactionTasksQueueManagerImpl implements TransactionTasksQueueManager { + TransactionTasksQueueManagerImpl(TasksQueue taskQueue, EventsManager eventsManager, + int maxNumOfPayments) { + this.taskQueue = taskQueue; + this.taskQueue.setTaskFinishListener(this); + this.eventsManager = eventsManager; + this.maxNumOfPayments = maxNumOfPayments; + batchPendingPaymentsQueue = new ArrayDeque<>(); + } + + @Override + public synchronized void enqueue(final List pendingPayments) { + mergePayments(pendingPayments); + sendTask(); + } @Override - public void enqueue(List queueToSend) { + public synchronized void enqueue(final PaymentTransactionParams paymentTransactionParams) { + if (currentTransactionParams != null) { + // TODO: 2019-08-26 double check - throw exception somehow + } else { + currentTransactionParams = paymentTransactionParams; + sendTask(); + } + } + @Override + public synchronized boolean transactionInProgress() { + return transactionInProgress; } @Override - public void enqueue(SendTransactionParams sendTransactionParams) { + public synchronized void onTaskFinish(final SendTransactionTask task) { + transactionInProgress = false; + sendTask(); + } + + /** + * Taking the new pending payment and instead of just adding it to the queue, we will + * merge/combine this list with the with whatever we can in the queue of batched payments list + * In this way instead sending many transactions we will have less transactions to send. + * For example, if currently our list consist of a list of 2 items, the first with 100 batch + * payments and the second with 40 and our new pending payments consist of 70 items. + * Then our new queue of list of batch payments will now have the first item still 100, + * second item 100 and the third item with 10. + * + * @param pendingPayments the new pending payments list that needed to be enqueued and batched. + */ + private void mergePayments(List pendingPayments) { + // the current number of pending payments that need to be batched + int numOfRestOfPendingPaymentsToBatch = pendingPayments.size(); + // the index in the pending payments list in which to start take elements to next batch + // payments list. + int pendingPaymentIndexToTakeFrom = 0; + + for (List batchPayments : batchPendingPaymentsQueue) { + // the current number of pending payment the can be entered to the current batch + // payment list + int numOfEntriesAvailableInBatchList = + Math.abs(maxNumOfPayments - batchPayments.size()); + if (numOfEntriesAvailableInBatchList == 0) { + // no room to add pending payments so go to next batch payments + continue; + } else { + // If there are more pending payments then the number of payments to fill then take + // a sublist of them and fill and take the rest and fill the next batch payments, + // otherwise just add all of them to the current batch payments + if (numOfEntriesAvailableInBatchList >= numOfRestOfPendingPaymentsToBatch) { + batchPayments.addAll(pendingPayments); + return; + } else { + // update the rest of pending payments that still need to be batched. + numOfRestOfPendingPaymentsToBatch = -numOfEntriesAvailableInBatchList; + List subPendingPayments = + pendingPayments.subList(pendingPaymentIndexToTakeFrom, + pendingPaymentIndexToTakeFrom + numOfEntriesAvailableInBatchList); + // update the index + pendingPaymentIndexToTakeFrom = +numOfEntriesAvailableInBatchList; + batchPayments.addAll(subPendingPayments); + } + } + } + // add to the queue in case that there are no batchPayments + batchPendingPaymentsQueue.add(pendingPayments.subList(pendingPaymentIndexToTakeFrom, + pendingPaymentIndexToTakeFrom + numOfRestOfPendingPaymentsToBatch)); + } + /** + * Sending the next task to the task queue if there is no transaction in progress. + * If there is a transaction param task then send it first, otherwise send the next task in + * the queue. + * If no task in the queue then we don't do anything. + */ + private void sendTask() { + // Check if there is a task running and if not then schedule a task + if (!transactionInProgress()) { + if (currentTransactionParams != null) { + PaymentTransactionParams toSend = currentTransactionParams; + currentTransactionParams = null; + taskQueue.scheduleTransactionParamsTask(toSend); + transactionInProgress = true; + } else if (batchPendingPaymentsQueue.size() > 0) { + taskQueue.schedulePendingPaymentsTask(batchPendingPaymentsQueue.poll()); + transactionInProgress = true; + } + } } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcess.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcess.java new file mode 100644 index 00000000..381565c6 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcess.java @@ -0,0 +1,24 @@ +package kin.sdk.queue; + +import android.support.annotation.Nullable; + +import java.util.List; + +import kin.sdk.internal.blockchain.TransactionSender; +import kin.sdk.internal.events.EventsManager; + +abstract class BatchPaymentTransactionProcess extends TransactionProcess { + + BatchPaymentTransactionProcess(TransactionSender transactionSender, + EventsManager eventsManager) { + super(transactionSender, eventsManager); + } + + /** + * @return the list of pending payment that the current transaction consist of. + * Can be null or empty. + */ + @Nullable + public abstract List payments(); + +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcessImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcessImpl.java new file mode 100644 index 00000000..dac8b1af --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcessImpl.java @@ -0,0 +1,39 @@ +package kin.sdk.queue; + +import java.util.List; + +import kin.base.KeyPair; +import kin.sdk.exception.OperationFailedException; +import kin.sdk.internal.blockchain.TransactionSender; +import kin.sdk.internal.events.EventsManager; +import kin.sdk.transactiondata.Transaction; + +public class BatchPaymentTransactionProcessImpl extends BatchPaymentTransactionProcess { + + private final TransactionSender transactionSender; + private final List pendingPayments; + private final KeyPair accountFrom; + private final int fee; + + public BatchPaymentTransactionProcessImpl(TransactionSender transactionSender, + List pendingPayments, + KeyPair accountFrom, + int fee, EventsManager eventsManager) { + super(transactionSender, eventsManager); + this.transactionSender = transactionSender; + this.pendingPayments = pendingPayments; + this.accountFrom = accountFrom; + this.fee = fee; + } + + @Override + public List payments() { + return pendingPayments; + } + + @Override + protected Transaction buildTransaction(String memo) throws OperationFailedException { + return transactionSender.buildBatchPaymentTransaction(accountFrom, pendingPayments, fee, + memo); + } +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PaymentQueue.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PaymentQueue.java index ae79f585..718b729c 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PaymentQueue.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PaymentQueue.java @@ -1,14 +1,15 @@ package kin.sdk.queue; import android.support.annotation.NonNull; + +import java.math.BigDecimal; +import java.util.List; + import kin.sdk.TransactionInterceptor; import kin.sdk.exception.InsufficientKinException; import kin.sdk.exception.KinException; import kin.sdk.transactiondata.BatchPaymentTransaction; -import java.math.BigDecimal; -import java.util.List; - public interface PaymentQueue { /** @@ -98,7 +99,7 @@ void onTransactionSendFailed(BatchPaymentTransaction transaction, List payments(); + public Transaction transaction(String memo) throws OperationFailedException { + return buildTransaction(memo); + } /** * Send the transaction with a Transaction object @@ -35,7 +52,9 @@ public interface TransactionProcess { * @param transaction the Transaction object to send * @return the transaction id. */ - TransactionId send(Transaction transaction); + public TransactionId send(Transaction transaction) throws OperationFailedException { + return transactionSender.sendTransaction(transaction); + } /** * Send the transaction with a whitelisted payload @@ -43,5 +62,7 @@ public interface TransactionProcess { * @param whitelistPayload the whitelist payload * @return the transaction id. */ - TransactionId send(String whitelistPayload); + public TransactionId send(String whitelistPayload) throws OperationFailedException { + return transactionSender.sendWhitelistTransaction(whitelistPayload); + } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/BatchPaymentTransaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/BatchPaymentTransaction.java index 86fc61bd..699e4f42 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/BatchPaymentTransaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/BatchPaymentTransaction.java @@ -1,14 +1,14 @@ package kin.sdk.transactiondata; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + import kin.base.Operation; import kin.base.Transaction; import kin.sdk.internal.blockchain.TransactionInternal; import kin.sdk.internal.data.PaymentOperationImpl; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.List; - public class BatchPaymentTransaction extends TransactionInternal { private final Transaction baseTransaction; @@ -61,4 +61,5 @@ public List payments() { public String memo() { return memo(); } + } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/PaymentTransaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/PaymentTransaction.java index 4ae3137c..12f0c0a1 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/PaymentTransaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/PaymentTransaction.java @@ -1,15 +1,17 @@ package kin.sdk.transactiondata; -import kin.sdk.internal.blockchain.TransactionInternal; - import java.math.BigDecimal; +import kin.sdk.internal.blockchain.TransactionInternal; + public class PaymentTransaction extends TransactionInternal { private final String destination; private final BigDecimal amount; private final String memo; + // TODO: 2019-08-26 We should state that this class represent a transaction with a single + // payment operation public PaymentTransaction(kin.base.Transaction baseTransaction, String destination, BigDecimal amount, String memo) { super(baseTransaction); @@ -34,7 +36,7 @@ public BigDecimal amount() { /** * @return the memo of this transaction. - * In this case you will get a string representation of a memo as opposed to {@link RawTransaction#memo()} + * In this case you will get a string representation of a memo. */ public String memo() { return memo; diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/PaymentTransactionParams.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/PaymentTransactionParams.java new file mode 100644 index 00000000..97193af4 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/PaymentTransactionParams.java @@ -0,0 +1,40 @@ +package kin.sdk.transactiondata; + +import android.support.annotation.NonNull; + +import java.math.BigDecimal; + +public class PaymentTransactionParams extends TransactionParams { + + // TODO: 2019-08-21 implement + + private PaymentTransactionParams() { + } + + public String destinationPublicAddress() { + return null; + } + + public BigDecimal amount() { + return null; + } + + public String memo() { + return null; + } + + public class Builder { + + Builder(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) { + } + + public Builder memo(String memo) { + return null; + } + + public PaymentTransactionParams build() { + return null; + } + } + +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/Transaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/Transaction.java index a54a34f3..f9397a21 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/Transaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/Transaction.java @@ -1,6 +1,14 @@ package kin.sdk.transactiondata; -import kin.base.*; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.List; + +import kin.base.MemoText; +import kin.base.Network; +import kin.base.Operation; +import kin.base.PaymentOperation; +import kin.base.TimeBounds; import kin.base.xdr.DecoratedSignature; import kin.sdk.KinAccount; import kin.sdk.TransactionId; @@ -10,10 +18,6 @@ import kin.sdk.internal.account.KinAccountImpl; import kin.sdk.internal.data.TransactionIdImpl; -import java.io.IOException; -import java.math.BigDecimal; -import java.util.List; - public abstract class Transaction { private final kin.base.Transaction baseTransaction; @@ -48,6 +52,8 @@ public static Transaction decodeTransaction(String transactionEnvelope) throws D if (paymentOperationCount == 1) { return new PaymentTransaction(transaction, paymentOperation.getDestination().getAccountId(), new BigDecimal(paymentOperation.getAmount()), ((MemoText) transaction.getMemo()).getText()); + } else if (paymentOperationCount > 1) { + return new BatchPaymentTransaction(transaction); } else { return new RawTransaction(transaction); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/TransactionParams.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/TransactionParams.java new file mode 100644 index 00000000..8a7c3b5e --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/TransactionParams.java @@ -0,0 +1,21 @@ +package kin.sdk.transactiondata; + +import android.support.annotation.NonNull; + +import java.math.BigDecimal; + +public class TransactionParams { + + // TODO: 2019-08-06 implement + + static PaymentTransactionParams.Builder paymentTransactionBuilder(@NonNull String publicAddress, + @NonNull BigDecimal amount, + int fee) { + return null; + } + + public int fee() { + return 0; + } + +} diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java index 0d2cfa14..0c348488 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java @@ -1,20 +1,22 @@ package kin.sdk; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.math.BigDecimal; + import kin.base.KeyPair; import kin.sdk.exception.AccountDeletedException; import kin.sdk.internal.account.KinAccountImpl; import kin.sdk.internal.blockchain.AccountInfoRetriever; +import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetriever; import kin.sdk.internal.blockchain.TransactionSender; import kin.sdk.internal.blockchain.events.BlockchainEventsCreator; import kin.sdk.internal.data.BalanceImpl; import kin.sdk.internal.data.TransactionIdImpl; import kin.sdk.transactiondata.PaymentTransaction; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.math.BigDecimal; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; @@ -31,6 +33,9 @@ public class KinAccountImplTest { private AccountInfoRetriever mockAccountInfoRetriever; @Mock private BlockchainEventsCreator mockBlockchainEventsCreator; + @Mock + private GeneralBlockchainInfoRetriever mockGeneralBlockchainInfoRetriever; + private KinAccountImpl kinAccount; private KeyPair expectedRandomAccount; @@ -42,7 +47,8 @@ public void setUp() throws Exception { private void initWithRandomAccount() { expectedRandomAccount = KeyPair.random(); kinAccount = new KinAccountImpl(expectedRandomAccount, new FakeBackupRestore(), mockTransactionSender, - mockAccountInfoRetriever, mockBlockchainEventsCreator); + mockAccountInfoRetriever, mockBlockchainEventsCreator, + mockGeneralBlockchainInfoRetriever); } @Test diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java index f9243675..a08a95db 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderTest.java @@ -1,17 +1,5 @@ package kin.sdk; -import kin.base.FormatException; -import kin.base.KeyPair; -import kin.base.Network; -import kin.base.Server; -import kin.base.responses.HttpResponseException; -import kin.sdk.exception.*; -import kin.sdk.internal.blockchain.TransactionSender; -import kin.sdk.internal.storage.KeyStore; -import kin.sdk.transactiondata.PaymentTransaction; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.SocketPolicy; import org.hamcrest.beans.HasPropertyWithValue; import org.junit.Assert; import org.junit.Before; @@ -29,9 +17,33 @@ import java.net.SocketTimeoutException; import java.util.concurrent.TimeUnit; +import kin.base.FormatException; +import kin.base.KeyPair; +import kin.base.Network; +import kin.base.Server; +import kin.base.responses.HttpResponseException; +import kin.sdk.exception.AccountNotFoundException; +import kin.sdk.exception.IllegalAmountException; +import kin.sdk.exception.InsufficientFeeException; +import kin.sdk.exception.InsufficientKinException; +import kin.sdk.exception.OperationFailedException; +import kin.sdk.exception.TransactionFailedException; +import kin.sdk.internal.blockchain.TransactionSender; +import kin.sdk.internal.storage.KeyStore; +import kin.sdk.transactiondata.PaymentTransaction; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.SocketPolicy; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.fail; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isA; import static org.junit.Assert.assertThat; @RunWith(RobolectricTestRunner.class) @@ -83,7 +95,8 @@ public void sendTransaction_success() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1" + + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, + ACCOUNT_ID_TO, new BigDecimal("1" + ".5"), FEE); TransactionId transactionId = transactionSender.sendTransaction(transaction); @@ -104,7 +117,8 @@ public void sendTransaction_WithMemo_success() throws Exception { mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); String fakeMemo = "fake memo"; - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, fakeMemo); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, + ACCOUNT_ID_TO, new BigDecimal("200"), FEE, fakeMemo); TransactionId transactionId = transactionSender.sendTransaction(transaction); assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); @@ -123,7 +137,8 @@ public void sendTransaction_ToAccountNotExist() throws Exception { expectedEx.expect(AccountNotFoundException.class); expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_TO))); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, + ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); transactionSender.sendTransaction(transaction); } @@ -143,7 +158,7 @@ public void sendTransaction_Http_307_Response_Success() throws Exception { mockWebServerHttp307.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); PaymentTransaction transaction = transactionSender - .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + .buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); TransactionId transactionId = transactionSender.sendTransaction(transaction); assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); @@ -162,7 +177,7 @@ public void sendTransaction_FromAccountNotExist() throws Exception { expectedEx.expect(AccountNotFoundException.class); expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_FROM))); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); transactionSender.sendTransaction(transaction); } @@ -180,7 +195,7 @@ public void sendTransaction_GeneralStellarError() throws Exception { expectedEx.expect(new HasPropertyWithValue<>("operationsResultCodes", contains("op_malformed"))); expectedEx.expect(new HasPropertyWithValue<>("operationsResultCodes", hasSize(1))); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -196,7 +211,7 @@ public void sendTransaction_Underfunded_InsufficientKinException() throws Except expectedEx.expect(InsufficientKinException.class); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -211,7 +226,7 @@ public void sendTransaction_InsufficientFeeException() throws Exception { expectedEx.expect(InsufficientFeeException.class); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -226,7 +241,7 @@ public void sendTransaction_InsufficientBalanceException() throws Exception { expectedEx.expect(InsufficientKinException.class); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -254,7 +269,7 @@ public void sendTransaction_FirstQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(IOException.class)); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -265,7 +280,7 @@ public void sendTransaction_SecondQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -277,7 +292,7 @@ public void sendTransaction_ThirdQuery_ConnectionException() throws Exception { expectedEx.expect(OperationFailedException.class); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -293,7 +308,7 @@ public void sendTransaction_changeTimeOut() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(SocketTimeoutException.class)); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -304,7 +319,7 @@ public void sendTransaction_FirstQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage(ACCOUNT_ID_FROM); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -316,7 +331,7 @@ public void sendTransaction_SecondQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage(ACCOUNT_ID_TO); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -329,7 +344,7 @@ public void sendTransaction_ThirdQuery_NullResponse() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectMessage("transaction"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -339,7 +354,7 @@ public void sendTransaction_TooLongMemo() throws Exception { String tooLongMemo = "memo string can be only 21 characters"; expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Memo cannot be longer that 21 bytes(UTF-8 characters)"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, tooLongMemo); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, tooLongMemo); transactionSender.sendTransaction(transaction); assertThat(mockWebServer.getRequestCount(), equalTo(0)); } @@ -349,7 +364,7 @@ public void sendTransaction_TooLongMemo() throws Exception { public void sendTransaction_NullAccount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("account"); - PaymentTransaction transaction = transactionSender.buildTransaction(null, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(null, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -358,7 +373,7 @@ public void sendTransaction_NullAccount() throws Exception { public void sendTransaction_NullPublicAddress() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("public address"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, null, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, null, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -367,7 +382,7 @@ public void sendTransaction_NullPublicAddress() throws Exception { public void sendTransaction_NullAmount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("amount"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, null, FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, null, FEE); transactionSender.sendTransaction(transaction); } @@ -375,7 +390,7 @@ public void sendTransaction_NullAmount() throws Exception { public void sendTransaction_EmptyPublicAddress() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("public address"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, "", new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, "", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -383,7 +398,7 @@ public void sendTransaction_EmptyPublicAddress() throws Exception { public void sendTransaction_NegativeAmount() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Amount"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("-200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("-200"), FEE); transactionSender.sendTransaction(transaction); } @@ -391,7 +406,7 @@ public void sendTransaction_NegativeAmount() throws Exception { public void sendTransaction_NegativeFee() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("Fee"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), -FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), -FEE); transactionSender.sendTransaction(transaction); } @@ -399,7 +414,7 @@ public void sendTransaction_NegativeFee() throws Exception { public void sendTransaction_AmountExceedNumOfDecimalPlaces() throws Exception { expectedEx.expect(IllegalAmountException.class); expectedEx.expectMessage("amount can't have more then 5 digits after the decimal point"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("20.012345"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("20.012345"), FEE); transactionSender.sendTransaction(transaction); } @@ -409,7 +424,7 @@ public void sendTransaction_InvalidPublicIdLength() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(FormatException.class)); expectedEx.expectMessage("public address"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, "ABCDEF", new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, "ABCDEF", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -419,7 +434,7 @@ public void sendTransaction_InvalidChecksumPublicId() throws Exception { expectedEx.expect(OperationFailedException.class); expectedEx.expectCause(isA(FormatException.class)); expectedEx.expectMessage("public address"); - PaymentTransaction transaction = transactionSender.buildTransaction(account, + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OG3", new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); } @@ -427,7 +442,7 @@ public void sendTransaction_InvalidChecksumPublicId() throws Exception { @SuppressWarnings("SameParameterValue") private void testHttpResponseCode(int resCode) { try { - PaymentTransaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + PaymentTransaction transaction = transactionSender.buildPaymentTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); transactionSender.sendTransaction(transaction); fail("Expected OperationFailedException"); } catch (Exception ex) { diff --git a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/TasksQueueTest.kt b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/TasksQueueTest.kt new file mode 100644 index 00000000..9a404c82 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/TasksQueueTest.kt @@ -0,0 +1,20 @@ +package kin.sdk.internal.queue + +class TasksQueueTest { + + +// @Test +// fun `enqueue, list size is increased by one`() { +// //given +// val tasksQueue = TasksQueueImpl() +// var value = 5 +// +// tasksQueue.execute { +// value = 10 +// } +// +// //then +// assertThat(value, equalTo(10)) +// } + +} \ No newline at end of file diff --git a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java index b15cbbc4..e9ce3c2d 100644 --- a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java +++ b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/TransactionActivity.java @@ -10,6 +10,11 @@ import android.util.Log; import android.view.View; import android.widget.EditText; + +import org.json.JSONException; + +import java.math.BigDecimal; + import kin.sdk.KinAccount; import kin.sdk.TransactionId; import kin.sdk.exception.AccountDeletedException; @@ -18,9 +23,6 @@ import kin.sdk.transactiondata.PaymentTransaction; import kin.utils.Request; import kin.utils.ResultCallback; -import org.json.JSONException; - -import java.math.BigDecimal; /** * Displays form to enter public address and amount and a button to send a transaction @@ -281,7 +283,8 @@ private class BuildTransactionCallback implements ResultCallback Date: Tue, 3 Sep 2019 11:52:01 +0300 Subject: [PATCH 3/5] 1. Remove status from pending payment and related classes. 2. Remove send transaction synchronously and do it with Request 3. Add enqueue only for transaction params --- .../src/main/java/kin/sdk/KinAccount.java | 19 +++--- .../src/main/java/kin/sdk/KinClient.java | 63 ++++++++++++------- .../internal/account/AbstractKinAccount.java | 30 ++++++--- .../sdk/internal/account/KinAccountImpl.java | 16 +---- .../blockchain/TransactionSender.java | 12 ++-- .../sdk/internal/queue/PaymentQueueImpl.java | 17 ++--- .../internal/queue/PendingPaymentImpl.java | 15 +---- .../queue/SendPendingPaymentsTask.java | 28 +++++++-- .../queue/SendTransactionParamsTask.java | 12 ++-- .../internal/queue/SendTransactionTask.java | 2 +- .../kin/sdk/internal/queue/TasksQueue.java | 20 +++--- .../sdk/internal/queue/TasksQueueImpl.java | 16 ++--- .../queue/TransactionTasksQueueManager.java | 7 ++- .../TransactionTasksQueueManagerImpl.java | 32 +++++++--- .../java/kin/sdk/queue/PendingPayment.java | 9 --- .../test/java/kin/sdk/KinAccountImplTest.java | 21 ++++--- 16 files changed, 184 insertions(+), 135 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java index 96002b84..528232bb 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java @@ -167,30 +167,25 @@ PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull TransactionId sendWhitelistTransactionSync(String whitelist) throws OperationFailedException; /** - * send a transaction. - *

Note: This method accesses the network, and should not be called on the android - * main thread.

+ * Create {@link Request} for signing and sending a transaction + *

See {@link KinAccount#sendTransaction(TransactionParams, TransactionInterceptor)} + * for possibles errors

* * @param transactionParams is the transaction parameters which define the transaction object to send. - * @param interceptor is the interceptor for the transaction before it is being sent. - * @return TransactionId the transaction identifier. - * @throws AccountNotFoundException if the sender or destination account was not created. - * @throws InsufficientKinException if account balance has not enough kin. - * @throws TransactionFailedException if transaction failed, contains blockchain failure details. - * @throws OperationFailedException other error occurred. + * @return {@code Request}, TransactionId - the transaction identifier. */ - TransactionId sendTransactionSync(TransactionParams transactionParams, TransactionInterceptor interceptor) throws OperationFailedException; + Request sendTransaction(final TransactionParams transactionParams); /** * Create {@link Request} for signing and sending a transaction - *

See {@link KinAccount#sendTransactionSync(TransactionParams, TransactionInterceptor)} * for possibles errors

* * @param transactionParams is the transaction parameters which define the transaction object to send. * @param interceptor is the interceptor for the transaction before it is being sent. * @return {@code Request}, TransactionId - the transaction identifier. */ - Request sendTransaction(TransactionParams transactionParams, TransactionInterceptor interceptor); + Request sendTransaction(TransactionParams transactionParams, + @Nullable TransactionInterceptor interceptor); /** * @return the payment queue. diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java index 1ce7584c..ca032eb9 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java @@ -29,9 +29,11 @@ import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetrieverImpl; import kin.sdk.internal.blockchain.TransactionSender; import kin.sdk.internal.blockchain.events.BlockchainEventsCreator; +import kin.sdk.internal.queue.PaymentQueueImpl; import kin.sdk.internal.storage.KeyStore; import kin.sdk.internal.storage.KeyStoreImpl; import kin.sdk.internal.storage.SharedPrefStore; +import kin.sdk.queue.PaymentQueue; import kin.utils.Request; import static kin.sdk.internal.Utils.checkNotNull; @@ -56,23 +58,29 @@ public class KinClient { private List kinAccounts = new ArrayList<>(1); /** - * For more details please look at {@link #KinClient(Context context,Environment environment, String appId, String storeKey)} + * For more details please look at + * {@link #KinClient(Context context, Environment environment, String appId, String storeKey)} */ public KinClient(@NonNull Context context, @NonNull Environment environment, String appId) { - this(context, environment, appId,""); + this(context, environment, appId, ""); } /** * Build KinClient object. - * @param context android context + * + * @param context android context * @param environment the blockchain network details. - * @param appId a 3 or 4 character string which represent the application id which will be added to each - * transaction. - *
Note: appId must contain only upper and/or lower case letters and/or digits and that the total string length is between 3 to 4. - * For example 1234 or 2ab3 or bca, etc.
- * @param storeKey an optional param which is the key for storing this KinClient data, different keys will store a different accounts. + * @param appId a 3 or 4 character string which represent the application id which will + * be added to each + * transaction. + *
Note: appId must contain only upper and/or lower case + * letters and/or digits and that the total string length is between 3 to 4. + * For example 1234 or 2ab3 or bca, etc.
+ * @param storeKey an optional param which is the key for storing this KinClient data, + * different keys will store a different accounts. */ - public KinClient(@NonNull Context context, @NonNull Environment environment, @NonNull String appId, @NonNull String storeKey) { + public KinClient(@NonNull Context context, @NonNull Environment environment, + @NonNull String appId, @NonNull String storeKey) { checkNotNull(storeKey, "storeKey"); checkNotNull(context, "context"); checkNotNull(environment, "environment"); @@ -92,8 +100,10 @@ public KinClient(@NonNull Context context, @NonNull Environment environment, @No @VisibleForTesting KinClient(Environment environment, KeyStore keyStore, TransactionSender transactionSender, - AccountInfoRetriever accountInfoRetriever, GeneralBlockchainInfoRetrieverImpl generalBlockchainInfoRetriever, - BlockchainEventsCreator blockchainEventsCreator, BackupRestore backupRestore, String appId, String storeKey) { + AccountInfoRetriever accountInfoRetriever, + GeneralBlockchainInfoRetrieverImpl generalBlockchainInfoRetriever, + BlockchainEventsCreator blockchainEventsCreator, BackupRestore backupRestore, + String appId, String storeKey) { this.environment = environment; this.keyStore = keyStore; this.transactionSender = transactionSender; @@ -113,7 +123,7 @@ private Server initServer() { private KeyStore initKeyStore(Context context, String id) { SharedPrefStore store = new SharedPrefStore( - context.getSharedPreferences(STORE_NAME_PREFIX + id, Context.MODE_PRIVATE)); + context.getSharedPreferences(STORE_NAME_PREFIX + id, Context.MODE_PRIVATE)); return new KeyStoreImpl(store, backupRestore); } @@ -149,10 +159,11 @@ private void updateKinAccounts(List storageAccounts) { private void validateAppId(String appId) { if (appId == null || appId.equals("")) { - Log.w("KinClient","WARNING: KinClient instance was created without a proper application ID. Is this what you intended to do?"); - } - else if (!appId.matches("[a-zA-Z0-9]{3,4}")) { - throw new IllegalArgumentException("appId must contain only upper and/or lower case letters and/or digits and that the total string length is between 3 to 4.\n" + + Log.w("KinClient", "WARNING: KinClient instance was created without a proper " + + "application ID. Is this what you intended to do?"); + } else if (!appId.matches("[a-zA-Z0-9]{3,4}")) { + throw new IllegalArgumentException("appId must contain only upper and/or lower case " + + "letters and/or digits and that the total string length is between 3 to 4.\n" + "for example 1234 or 2ab3 or cd2 or fqa, etc."); } } @@ -174,19 +185,20 @@ KinAccount addAccount() throws CreateAccountException { * Import an account from a JSON-formatted string. * * @param exportedJson The exported JSON-formatted string. - * @param passphrase The passphrase to decrypt the secret key. + * @param passphrase The passphrase to decrypt the secret key. * @return The imported account */ @NonNull public KinAccount importAccount(@NonNull String exportedJson, @NonNull String passphrase) - throws CryptoException, CreateAccountException, CorruptedDataException { + throws CryptoException, CreateAccountException, CorruptedDataException { KeyPair account = keyStore.importAccount(exportedJson, passphrase); KinAccount kinAccount = getAccountByPublicAddress(account.getAccountId()); return kinAccount != null ? kinAccount : addKeyPair(account); } @Nullable - private KinAccount getAccountByPublicAddress(String accountId) { //TODO we should make this method public + private KinAccount getAccountByPublicAddress(String accountId) { //TODO we should make this + // method public loadAccounts(); KinAccount kinAccount = null; for (int i = 0; i < kinAccounts.size(); i++) { @@ -235,8 +247,10 @@ public int getAccountCount() { /** * Deletes the account at input index (if it exists) + * * @return true if the delete was successful or false otherwise - * @throws DeleteAccountException in case of a delete account exception while trying to delete the account + * @throws DeleteAccountException in case of a delete account exception while trying to + * delete the account */ public boolean deleteAccount(int index) throws DeleteAccountException { boolean deleteSuccess = false; @@ -267,8 +281,10 @@ public Environment getEnvironment() { @NonNull private KinAccountImpl createNewKinAccount(KeyPair account) { - return new KinAccountImpl(account, backupRestore, transactionSender, - accountInfoRetriever, blockchainEventsCreator, generalBlockchainInfoRetriever); + PaymentQueue paymentQueue = new PaymentQueueImpl(account, transactionSender, + generalBlockchainInfoRetriever); + return new KinAccountImpl(account, backupRestore, transactionSender, accountInfoRetriever, + blockchainEventsCreator, paymentQueue); } /** @@ -289,7 +305,8 @@ public Long call() throws Exception { /** * Get the current minimum fee that the network charges per operation. * This value is expressed in Quarks (1 Quark = 0.00001 KIN). - *

Note: This method accesses the network, and should not be called on the android main thread.

+ *

Note: This method accesses the network, and should not be called on the android + * main thread.

* * @return the minimum fee. */ diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java index c724dd4f..a1b8128f 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java @@ -10,32 +10,43 @@ import kin.sdk.KinAccount; import kin.sdk.TransactionId; import kin.sdk.TransactionInterceptor; -import kin.sdk.exception.OperationFailedException; +import kin.sdk.internal.queue.PaymentQueueImpl; +import kin.sdk.queue.PaymentQueue; import kin.sdk.transactiondata.PaymentTransaction; import kin.sdk.transactiondata.TransactionParams; import kin.utils.Request; abstract class AbstractKinAccount implements KinAccount { + private final PaymentQueue paymentQueue; + + AbstractKinAccount(PaymentQueue paymentQueue) { + this.paymentQueue = paymentQueue; + } + @NonNull @Override public Request buildTransaction(@NonNull final String publicAddress, - @NonNull final BigDecimal amount, final int fee) { + @NonNull final BigDecimal amount, + final int fee) { return new Request<>(new Callable() { @Override public PaymentTransaction call() throws Exception { return buildTransactionSync(publicAddress, amount, fee); } }); - }@NonNull + } + + @NonNull @Override public Request buildTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, - final int fee, @Nullable final String memo) { + final int fee, + @Nullable final String memo) { return new Request<>(new Callable() { @Override public PaymentTransaction call() throws Exception { - return buildTransactionSync(publicAddress, amount, fee, memo); + return buildTransactionSync(publicAddress, amount, fee, memo); } }); } @@ -62,13 +73,18 @@ public TransactionId call() throws Exception { }); } + @Override + public Request sendTransaction(final TransactionParams transactionParams) { + return sendTransaction(transactionParams, null); + } + @Override public Request sendTransaction(final TransactionParams transactionParams, final TransactionInterceptor interceptor) { return new Request<>(new Callable() { @Override - public TransactionId call() throws OperationFailedException { - return sendTransactionSync(transactionParams, interceptor); + public TransactionId call() { + return ((PaymentQueueImpl) paymentQueue).enqueueTransactionParams(transactionParams, interceptor); } }); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/KinAccountImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/KinAccountImpl.java index 108c1dcb..0659d885 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/KinAccountImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/KinAccountImpl.java @@ -11,20 +11,16 @@ import kin.sdk.ListenerRegistration; import kin.sdk.PaymentInfo; import kin.sdk.TransactionId; -import kin.sdk.TransactionInterceptor; import kin.sdk.exception.AccountDeletedException; import kin.sdk.exception.CryptoException; import kin.sdk.exception.OperationFailedException; import kin.sdk.internal.backuprestore.BackupRestore; import kin.sdk.internal.blockchain.AccountInfoRetriever; -import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetriever; import kin.sdk.internal.blockchain.TransactionSender; import kin.sdk.internal.blockchain.events.BlockchainEvents; import kin.sdk.internal.blockchain.events.BlockchainEventsCreator; -import kin.sdk.internal.queue.PaymentQueueImpl; import kin.sdk.queue.PaymentQueue; import kin.sdk.transactiondata.PaymentTransaction; -import kin.sdk.transactiondata.TransactionParams; public final class KinAccountImpl extends AbstractKinAccount { @@ -40,14 +36,14 @@ public KinAccountImpl(KeyPair account, BackupRestore backupRestore, TransactionSender transactionSender, AccountInfoRetriever accountInfoRetriever, BlockchainEventsCreator blockchainEventsCreator, - GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever) { + PaymentQueue paymentQueue) { + super(paymentQueue); this.account = account; this.backupRestore = backupRestore; this.transactionSender = transactionSender; this.accountInfoRetriever = accountInfoRetriever; this.blockchainEvents = blockchainEventsCreator.create(account.getAccountId()); - this.paymentQueue = new PaymentQueueImpl(account, transactionSender, - generalBlockchainInfoRetriever); + this.paymentQueue = paymentQueue; } @Override @@ -88,12 +84,6 @@ public TransactionId sendWhitelistTransactionSync(String whitelist) throws Opera return transactionSender.sendWhitelistTransaction(whitelist); } - @Override - public TransactionId sendTransactionSync(TransactionParams transactionParams, - TransactionInterceptor interceptor) throws OperationFailedException { - return ((PaymentQueueImpl) paymentQueue).enqueueTransactionParams(transactionParams, interceptor); - } - @Override public PaymentQueue paymentQueue() { return paymentQueue; diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java index 3531767f..b956c632 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java @@ -52,8 +52,7 @@ public TransactionSender(Server server, String appId) { public PaymentTransaction buildPaymentTransaction(@NonNull KeyPair from, @NonNull String publicAddress, - @NonNull BigDecimal amount, - int fee) throws OperationFailedException { + @NonNull BigDecimal amount, int fee) throws OperationFailedException { return buildPaymentTransaction(from, publicAddress, amount, fee, null); } @@ -133,7 +132,9 @@ private void validateAmountDecimalPoint(BigDecimal amount) throws OperationFaile private void checkMemo(String memo) { try { if (memo != null && memo.getBytes("UTF-8").length > MEMO_BYTES_LENGTH_LIMIT) { - throw new IllegalArgumentException("Memo cannot be longer that " + MEMO_BYTES_LENGTH_LIMIT + " bytes(UTF-8 characters)"); + throw new IllegalArgumentException( + "Memo cannot be longer that " + MEMO_BYTES_LENGTH_LIMIT + " bytes(UTF-8 " + + "characters)"); } } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException("Memo text have unsupported characters encoding"); @@ -270,8 +271,9 @@ private TransactionId createFailureException(SubmitTransactionResponse response) private boolean isInsufficientKinException(TransactionFailedException transactionException) { List resultCodes = transactionException.getOperationsResultCodes(); String transactionResultCode = transactionException.getTransactionResultCode(); - return ((resultCodes != null && resultCodes.size() > 0 && INSUFFICIENT_KIN_RESULT_CODE.equals(resultCodes.get(0))) || - !TextUtils.isEmpty(transactionResultCode) && INSUFFICIENT_BALANCE_RESULT_CODE.equals(transactionResultCode)); + return ( + (resultCodes != null && resultCodes.size() > 0 && INSUFFICIENT_KIN_RESULT_CODE.equals(resultCodes.get(0))) + || !TextUtils.isEmpty(transactionResultCode) && INSUFFICIENT_BALANCE_RESULT_CODE.equals(transactionResultCode)); } private boolean isInsufficientFeeException(TransactionFailedException transactionException) { diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java index 43b0e24e..eda7104a 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java @@ -64,16 +64,16 @@ public PendingPayment enqueuePayment(@NonNull String destinationPublicAddress, } /** - * enqueue a non blocking transaction. - * This transaction will be pushed to the top of the queue. + * enqueue a non blocking transaction. This transaction will be pushed to the top of the queue. * - * @param transactionParams the transaction parameters - * @param interceptor the interceptor to set + * @param transactionParams the transaction parameters + * @param transactionInterceptor an optional interceptor that will be used to intercept the + * transaction. */ public TransactionId enqueueTransactionParams(TransactionParams transactionParams, - TransactionInterceptor interceptor) { - tasksQueue.setTransactionInterceptor(interceptor); - tasksQueue.scheduleTransactionParamsTask(transactionParams); + TransactionInterceptor transactionInterceptor) { + + tasksQueue.scheduleTransactionParamsTask(transactionParams, transactionInterceptor); return null; // TODO: 2019-09-02 because we want it to be sync then we should handle it here and // return the transaction id @@ -81,7 +81,7 @@ public TransactionId enqueueTransactionParams(TransactionParams transactionParam @Override public void setTransactionInterceptor(TransactionInterceptor transactionInterceptor) { - tasksQueue.setTransactionInterceptor(transactionInterceptor); + tasksQueue.setPendingPaymentsTransactionInterceptor(transactionInterceptor); } @Override @@ -92,6 +92,7 @@ public void addEventsListener(EventsListener eventListener) { @Override public void setFee(int fee) { + Utils.checkForNegativeFee(fee); tasksQueue.setFee(fee); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PendingPaymentImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PendingPaymentImpl.java index e8ecb18b..22303951 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PendingPaymentImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PendingPaymentImpl.java @@ -1,16 +1,15 @@ package kin.sdk.internal.queue; -import kin.sdk.queue.PendingPayment; - import java.math.BigDecimal; +import kin.sdk.queue.PendingPayment; + public class PendingPaymentImpl implements PendingPayment { private final String destinationPublicAddress; private final String sourcePublicAddress; private final BigDecimal amount; private final Object metadata; - private Status status; public PendingPaymentImpl(String destinationPublicAddress, String sourcePublicAddress, BigDecimal amount) { this(destinationPublicAddress, sourcePublicAddress, amount, null); @@ -22,7 +21,6 @@ public PendingPaymentImpl(String destinationPublicAddress, String sourcePublicAd this.sourcePublicAddress = sourcePublicAddress; this.amount = amount; this.metadata = metadata; - this.status = Status.PENDING; } @Override @@ -44,13 +42,4 @@ public BigDecimal amount() { public Object metadata() { return metadata; } - - @Override - public Status status() { - return status; - } - - void setStatus(Status status) { - this.status = status; - } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java index 04d62e42..aebd2425 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java @@ -48,15 +48,32 @@ public void run() { } @Override - void invokeInterceptor() throws Exception { + void invokeInterceptor() { TransactionProcess transactionProcess = new BatchPaymentTransactionProcessImpl(transactionSender, pendingPayments, accountFrom, fee, eventsManager); // TODO: 2019-09-01 if we give them the TransactionProcess then they wont have access to // the pending payments... need to be changed - TransactionId transactionId = - transactionInterceptor.interceptTransactionSending(transactionProcess); - handleTransactionFinished(transactionId); + try { + TransactionId transactionId = + transactionInterceptor.interceptTransactionSending(transactionProcess); + handleTransactionFinished(transactionId); + } catch (Exception e) { + // TODO: 2019-09-03 if it is not kinsdkexception then wrap it with kinsdkexception + // and send it with events manager + } + } + + @Override + void handleTransactionFinished(TransactionId transactionId) { + super.handleTransactionFinished(transactionId); + if (transactionId != null) { + // TODO: 2019-09-02 need to update pending payments with their status?... + + // TODO: 2019-09-01 invoke events for transaction success + } else { + // TODO: 2019-09-01 invoke events for transaction fail + } } @Override @@ -73,8 +90,7 @@ private void setFeeIfNeeded() { try { fee = (int) generalBlockchainInfoRetriever.getMinimumFeeSync(); } catch (OperationFailedException e) { - // TODO: 2019-08-27 need to maybe add an exception to the events manager or - // something + // TODO: 2019-08-27 need to maybe add an exception to the events manager or something... } } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java index ce6f9380..01c9fd41 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java @@ -35,12 +35,17 @@ class SendTransactionParamsTask extends SendTransactionTask { } @Override - void invokeInterceptor() throws Exception { + void invokeInterceptor() { TransactionProcess transactionProcess = new TransactionParamsProcessImpl(transactionSender, transactionParams, accountFrom, eventsManager); - TransactionId transactionId = - transactionInterceptor.interceptTransactionSending(transactionProcess); + TransactionId transactionId = null; + try { + transactionId = transactionInterceptor.interceptTransactionSending(transactionProcess); + } catch (Exception e) { + // TODO: 2019-09-03 if it is not kinsdkexception then wrap it with kinsdkexception + // and send it with events manager + } handleTransactionFinished(transactionId); } @@ -62,5 +67,4 @@ void buildAndSendTransaction() throws OperationFailedException { } sendTransaction(transaction); } - // TODO: 2019-09-01 handle pending payment status } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java index 6b87219d..a48609ec 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java @@ -51,7 +51,7 @@ public void run() { } } - abstract void invokeInterceptor() throws Exception; + abstract void invokeInterceptor(); abstract void buildAndSendTransaction() throws OperationFailedException; } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java index 2b3486b5..d57cb33d 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java @@ -1,5 +1,7 @@ package kin.sdk.internal.queue; +import android.support.annotation.Nullable; + import java.util.List; import kin.sdk.TransactionInterceptor; @@ -16,7 +18,7 @@ public interface TasksQueue { * will be executed next. * * @param batchPendingPayments the list of batched pending payments from which we will create - * a batch transaction. + * a batch transaction. */ void schedulePendingPaymentsTask(List batchPendingPayments); @@ -27,10 +29,13 @@ public interface TasksQueue { * If this task has high priority then it will enter to the top of the queue, meaning that it * will be executed next. * - * @param paymentTransactionParams is the object from which we will create the payment - * transaction + * @param paymentTransactionParams is the object from which we will create the payment + * transaction + * @param transactionParamsInterceptor an optional interceptor that will be used to intercept + * the transaction. */ - void scheduleTransactionParamsTask(TransactionParams paymentTransactionParams); + void scheduleTransactionParamsTask(TransactionParams paymentTransactionParams, + @Nullable TransactionInterceptor transactionParamsInterceptor); /** * Stop all tasks in the queue. @@ -60,8 +65,9 @@ public interface TasksQueue { /** * Set a transaction interceptor. * - * @param transactionInterceptor is the TransactionInterceptor to set. - *

See {@link TransactionInterceptor}

+ * @param pendingPaymentsTransactionInterceptor is the TransactionInterceptor for batch + * payments transactions to set. + *

See {@link TransactionInterceptor}

*/ - void setTransactionInterceptor(TransactionInterceptor transactionInterceptor); + void setPendingPaymentsTransactionInterceptor(TransactionInterceptor pendingPaymentsTransactionInterceptor); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java index fd39063b..cf4e3363 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java @@ -23,7 +23,7 @@ class TasksQueueImpl extends HandlerThread implements TasksQueue { private static final String nameTag = "TaskQueueHandlerThread"; private final TransactionSender transactionSender; - private TransactionInterceptor transactionInterceptor; + private TransactionInterceptor pendingPaymentsTransactionInterceptor; private final EventsManager eventsManager; private final GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever; private final KeyPair accountFrom; @@ -57,19 +57,21 @@ public void schedulePendingPaymentsTask(List batchPendingPayment if (backgroundHandler != null) { SendPendingPaymentsTask sendPendingPaymentsTask = new SendPendingPaymentsTask(batchPendingPayments, transactionSender, - transactionInterceptor, taskFinishListener, eventsManager, - generalBlockchainInfoRetriever, fee, accountFrom); + pendingPaymentsTransactionInterceptor, taskFinishListener, + eventsManager, generalBlockchainInfoRetriever, fee, accountFrom); backgroundHandler.post(sendPendingPaymentsTask); } } @Override - public void scheduleTransactionParamsTask(TransactionParams transactionParams) { + public void scheduleTransactionParamsTask(TransactionParams transactionParams, + TransactionInterceptor transactionParamsInterceptor) { if (backgroundHandler != null) { SendTransactionParamsTask sendTransactionParamsTask = new SendTransactionParamsTask(transactionParams, transactionSender, - transactionInterceptor, taskFinishListener, eventsManager, accountFrom); + transactionParamsInterceptor, taskFinishListener, eventsManager, + accountFrom); // TODO: 2019-08-27 although currently we only have one task each time and we are // dealing with the param at the manager level, // if in the future something will change in the manager then it will still post it @@ -96,7 +98,7 @@ public void setFee(int fee) { } @Override - public void setTransactionInterceptor(TransactionInterceptor transactionInterceptor) { - this.transactionInterceptor = transactionInterceptor; + public void setPendingPaymentsTransactionInterceptor(TransactionInterceptor pendingPaymentsTransactionInterceptor) { + this.pendingPaymentsTransactionInterceptor = pendingPaymentsTransactionInterceptor; } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java index e312ea73..f043f2b2 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java @@ -2,6 +2,7 @@ import java.util.List; +import kin.sdk.TransactionInterceptor; import kin.sdk.queue.PendingPayment; import kin.sdk.transactiondata.PaymentTransactionParams; @@ -19,9 +20,11 @@ public interface TransactionTasksQueueManager { * enqueue the transaction parameters object which will later on become a transaction. * * @param transactionParams the transaction parameters + * @param transactionInterceptor an optional interceptor that will be used to intercept + * * the transaction. */ - void enqueue(PaymentTransactionParams transactionParams); // TODO: 2019-08-26 should we use - // PaymentTransactionParams or TransactionParams + void enqueue(PaymentTransactionParams transactionParams, + TransactionInterceptor transactionInterceptor); /** * @return true if currently there is a transaction in progress, meaning it will return true if diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java index 9a097a01..48e2b837 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Queue; +import kin.sdk.TransactionInterceptor; import kin.sdk.internal.events.EventsManager; import kin.sdk.queue.PendingPayment; import kin.sdk.transactiondata.PaymentTransactionParams; @@ -18,6 +19,7 @@ class TransactionTasksQueueManagerImpl implements TransactionTasksQueueManager, private final int maxNumOfPayments; private Queue> batchPendingPaymentsQueue; private PaymentTransactionParams currentTransactionParams; + private TransactionInterceptor currentTransactionParamsInterceptor; private boolean transactionInProgress = false; TransactionTasksQueueManagerImpl(TasksQueue taskQueue, EventsManager eventsManager, @@ -30,17 +32,19 @@ class TransactionTasksQueueManagerImpl implements TransactionTasksQueueManager, } @Override - public synchronized void enqueue(final List pendingPayments) { + public synchronized void enqueue(List pendingPayments) { mergePayments(pendingPayments); sendTask(); } @Override - public synchronized void enqueue(final PaymentTransactionParams paymentTransactionParams) { + public synchronized void enqueue(PaymentTransactionParams paymentTransactionParams, + TransactionInterceptor transactionInterceptor) { if (currentTransactionParams != null) { - // TODO: 2019-08-26 double check - throw exception somehow + // TODO: 2019-08-26 throw some exception, will decide later } else { currentTransactionParams = paymentTransactionParams; + currentTransactionParamsInterceptor = transactionInterceptor; sendTask(); } } @@ -116,14 +120,24 @@ private void sendTask() { // Check if there is a task running and if not then schedule a task if (!transactionInProgress()) { if (currentTransactionParams != null) { - PaymentTransactionParams toSend = currentTransactionParams; - currentTransactionParams = null; - taskQueue.scheduleTransactionParamsTask(toSend); - transactionInProgress = true; + sendTransactionParamsTask(); } else if (batchPendingPaymentsQueue.size() > 0) { - taskQueue.schedulePendingPaymentsTask(batchPendingPaymentsQueue.poll()); - transactionInProgress = true; + sendPendingPaymentsTask(); } } } + + private void sendTransactionParamsTask() { + PaymentTransactionParams toSend = currentTransactionParams; + currentTransactionParams = null; + TransactionInterceptor transactionInterceptor = currentTransactionParamsInterceptor; + currentTransactionParamsInterceptor = null; + taskQueue.scheduleTransactionParamsTask(toSend, transactionInterceptor); + transactionInProgress = true; + } + + private void sendPendingPaymentsTask() { + taskQueue.schedulePendingPaymentsTask(batchPendingPaymentsQueue.poll()); + transactionInProgress = true; + } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java index 0c3dd43a..01fa676e 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java @@ -6,10 +6,6 @@ public interface PendingPayment { - enum Status { - PENDING, COMPLETED, FAILED - } - // TODO: 2019-08-14 namings??? /** @@ -33,9 +29,4 @@ enum Status { */ @Nullable Object metadata(); - - /** - * Current Payment status - */ - Status status(); } diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java index 0c348488..ce1c410d 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinAccountImplTest.java @@ -11,11 +11,11 @@ import kin.sdk.exception.AccountDeletedException; import kin.sdk.internal.account.KinAccountImpl; import kin.sdk.internal.blockchain.AccountInfoRetriever; -import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetriever; import kin.sdk.internal.blockchain.TransactionSender; import kin.sdk.internal.blockchain.events.BlockchainEventsCreator; import kin.sdk.internal.data.BalanceImpl; import kin.sdk.internal.data.TransactionIdImpl; +import kin.sdk.queue.PaymentQueue; import kin.sdk.transactiondata.PaymentTransaction; import static junit.framework.Assert.assertEquals; @@ -34,21 +34,21 @@ public class KinAccountImplTest { @Mock private BlockchainEventsCreator mockBlockchainEventsCreator; @Mock - private GeneralBlockchainInfoRetriever mockGeneralBlockchainInfoRetriever; + private PaymentQueue mockPaymentQueue; private KinAccountImpl kinAccount; private KeyPair expectedRandomAccount; @Before - public void setUp() throws Exception { + public void setUp() { MockitoAnnotations.initMocks(this); } private void initWithRandomAccount() { expectedRandomAccount = KeyPair.random(); - kinAccount = new KinAccountImpl(expectedRandomAccount, new FakeBackupRestore(), mockTransactionSender, - mockAccountInfoRetriever, mockBlockchainEventsCreator, - mockGeneralBlockchainInfoRetriever); + kinAccount = new KinAccountImpl(expectedRandomAccount, new FakeBackupRestore(), + mockTransactionSender, mockAccountInfoRetriever, mockBlockchainEventsCreator, + mockPaymentQueue); } @Test @@ -68,7 +68,8 @@ public void sendTransactionSync() throws Exception { when(mockTransactionSender.sendTransaction((PaymentTransaction) any())).thenReturn(expectedTransactionId); - PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100); + PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, + expectedAmount, 100); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); verify(mockTransactionSender).sendTransaction(transaction); @@ -86,7 +87,8 @@ public void sendTransactionSync_WithMemo() throws Exception { when(mockTransactionSender.sendTransaction((PaymentTransaction) any())).thenReturn(expectedTransactionId); - PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100, memo); + PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, + expectedAmount, 100, memo); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); verify(mockTransactionSender).sendTransaction(transaction); @@ -124,7 +126,8 @@ public void sendTransactionSync_DeletedAccount_Exception() throws Exception { kinAccount.markAsDeleted(); PaymentTransaction transaction = kinAccount.buildTransactionSync( - "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX", new BigDecimal("12.2"), 100); + "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX", + new BigDecimal("12.2"), 100); kinAccount.sendTransactionSync(transaction); } From 149030e4aacd2a0c9598fab66b78f8ef10d8410e Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Wed, 11 Sep 2019 11:53:00 +0300 Subject: [PATCH 4/5] 1. Add Integration Tests 2. Create KinClientInjector 3. Start to add code to Event Manager 4. Add configuration to the queue from outside 5. Finish implementing the Tasks 6. Add an option to release the queue and invoke it when deleting an account 7. Create the new Hierarchy of TransactionProcess * still need to add event manager related code --- kin-sdk/kin-sdk-lib/build.gradle | 11 +- .../androidTest/java/kin/sdk/IntegConsts.java | 15 +- .../kotlin/kin/sdk/FakeKinOnBoard.kt | 2 +- .../sdk/IntegrationTestKinClientInjector.kt | 28 +++ .../kin/sdk/KinAccountIntegrationTest.kt | 51 ++--- .../src/androidTest/kotlin/kin/sdk/Utils.kt | 29 +++ .../PaymentQueueEndToEndIntegrationTest.kt | 174 ++++++++++++++++ .../queue/PaymentQueueIntegrationTests.kt | 193 ++++++++++++++++++ .../queue/TasksQueueIntegrationTest.kt | 30 --- .../src/main/java/kin/sdk/KinAccount.java | 3 +- .../src/main/java/kin/sdk/KinClient.java | 68 +++--- .../main/java/kin/sdk/KinClientInjector.java | 129 ++++++++++++ .../java/kin/sdk/TransactionInterceptor.java | 4 +- .../src/main/java/kin/sdk/internal/Utils.java | 12 ++ .../internal/account/AbstractKinAccount.java | 10 +- .../blockchain/TransactionSender.java | 60 +++--- .../sdk/internal/events/EventsManager.java | 57 ++++++ .../internal/events/EventsManagerImpl.java | 53 ++++- .../sdk/internal/queue/PaymentQueueImpl.java | 68 +++--- .../queue/PaymentQueueManagerImpl.java | 22 +- .../queue/SendPendingPaymentsTask.java | 38 ++-- .../queue/SendTransactionParamsTask.java | 21 +- .../internal/queue/SendTransactionTask.java | 5 +- .../internal/queue/TaskFinishListener.java | 5 +- .../kin/sdk/internal/queue/TasksQueue.java | 8 +- .../sdk/internal/queue/TasksQueueImpl.java | 38 ++-- .../queue/TransactionTasksQueueManager.java | 3 +- .../TransactionTasksQueueManagerImpl.java | 45 ++-- .../queue/BatchPaymentTransactionProcess.java | 24 --- .../BatchPaymentTransactionProcessImpl.java | 39 ---- .../main/java/kin/sdk/queue/PaymentQueue.java | 22 +- .../queue/PaymentQueueTransactionProcess.java | 60 ++++++ .../java/kin/sdk/queue/PendingPayment.java | 2 - .../queue/TransactionParamsProcessImpl.java | 21 +- .../kin/sdk/queue/TransactionProcess.java | 28 +-- .../sdk/internal/queue/FakeQueueScheduler.kt | 9 +- .../internal/queue/PaymentQueueManagerTest.kt | 6 +- 37 files changed, 1013 insertions(+), 380 deletions(-) create mode 100644 kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/IntegrationTestKinClientInjector.kt create mode 100644 kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/Utils.kt create mode 100644 kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueEndToEndIntegrationTest.kt create mode 100644 kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueIntegrationTests.kt delete mode 100644 kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/TasksQueueIntegrationTest.kt create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClientInjector.java delete mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcess.java delete mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcessImpl.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PaymentQueueTransactionProcess.java diff --git a/kin-sdk/kin-sdk-lib/build.gradle b/kin-sdk/kin-sdk-lib/build.gradle index 17d609a2..95118798 100644 --- a/kin-sdk/kin-sdk-lib/build.gradle +++ b/kin-sdk/kin-sdk-lib/build.gradle @@ -50,27 +50,26 @@ dependencies { api 'com.github.kinecosystem:kin-utils-android:1.1' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.27.0' + testImplementation 'org.mockito:mockito-core:2.25.0' testImplementation 'org.hamcrest:hamcrest-library:1.3' testImplementation 'com.squareup.okhttp3:mockwebserver:3.9.1' testImplementation 'org.robolectric:robolectric:4.3' testImplementation 'com.google.code.gson:gson:2.8.5' testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0' + testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" androidTestImplementation 'junit:junit:4.12' - androidTestImplementation 'org.mockito:mockito-core:2.27.0' + androidTestImplementation 'org.mockito:mockito-core:2.25.0' + androidTestImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' androidTestImplementation 'org.hamcrest:hamcrest-library:1.3' androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'org.mockito:mockito-android:2.10.0' - androidTestImplementation 'org.mockito:mockito-android:2.10.0' + androidTestImplementation 'org.mockito:mockito-android:2.25.0' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:rules:1.0.2' androidTestImplementation 'org.hamcrest:hamcrest-library:1.3' androidTestImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - androidTestImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0' } //bundle javadocs with published aar diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java index 501aa818..a42915bd 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java +++ b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java @@ -1,17 +1,18 @@ package kin.sdk; -final class IntegConsts { +public final class IntegConsts { - static final String TEST_NETWORK_URL = BuildConfig.INTEG_TESTS_NETWORK_URL.isEmpty() ? + public static final String TEST_NETWORK_URL = BuildConfig.INTEG_TESTS_NETWORK_URL.isEmpty() ? Environment.TEST.getNetworkUrl() : BuildConfig.INTEG_TESTS_NETWORK_URL; - static final String TEST_NETWORK_ID = BuildConfig.INTEG_TESTS_NETWORK_PASSPHRASE.isEmpty() ? + public static final String TEST_NETWORK_ID = + BuildConfig.INTEG_TESTS_NETWORK_PASSPHRASE.isEmpty() ? Environment.TEST.getNetworkPassphrase() : BuildConfig.INTEG_TESTS_NETWORK_PASSPHRASE; - static final String FRIENDBOT_URL = BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT.isEmpty() ? + public static final String FRIENDBOT_URL = BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT.isEmpty() ? "https://friendbot-testnet.kininfrastructure.com" : BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT; - static final String URL_CREATE_ACCOUNT = FRIENDBOT_URL + "?addr=%s&amount=%d"; - static final String URL_FUND = FRIENDBOT_URL + "/fund?addr=%s&amount="; // faucet - static final String URL_WHITELISTING_SERVICE = (BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT.isEmpty() ? + public static final String URL_CREATE_ACCOUNT = FRIENDBOT_URL + "?addr=%s&amount=%d"; + public static final String URL_FUND = FRIENDBOT_URL + "/fund?addr=%s&amount="; // faucet + public static final String URL_WHITELISTING_SERVICE = (BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT.isEmpty() ? "http://34.239.111.38:3000" : BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT) + "/whitelist"; } diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt index b3635188..faa99c54 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt @@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit /** * Fake on board for integration test, support creating and funding accounts on stellar test net */ -internal class FakeKinOnBoard @Throws(IOException::class) +class FakeKinOnBoard @Throws(IOException::class) constructor() { private val client: OkHttpClient = OkHttpClient.Builder() diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/IntegrationTestKinClientInjector.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/IntegrationTestKinClientInjector.kt new file mode 100644 index 00000000..ff6b24e5 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/IntegrationTestKinClientInjector.kt @@ -0,0 +1,28 @@ +package kin.sdk + +import android.content.Context +import kin.sdk.internal.blockchain.TransactionSender +import kin.sdk.internal.queue.PaymentQueueImpl + +class IntegrationTestKinClientInjector(context: Context?, environment: Environment?, appId: String?, storeKey: String?, + private val txSender: TransactionSender? = null, + private val configuration: PaymentQueueImpl.PaymentQueueConfiguration?) : + KinClientInjector(context, environment, appId, storeKey) { + + + override fun getPaymentQueueConfiguration(): PaymentQueueImpl.PaymentQueueConfiguration { + return configuration + ?: PaymentQueueImpl.PaymentQueueConfiguration(Constants.delayBetweenPaymentsMillis, Constants.queueTimeoutMillis, Constants.maxNumOfPayments) + } + + override fun getTransactionSender(): TransactionSender { + return txSender ?: super.getTransactionSender() + } + + object Constants { + const val delayBetweenPaymentsMillis: Long = 250 + const val queueTimeoutMillis: Long = 2000 + const val maxNumOfPayments: Int = 5 + } + +} \ No newline at end of file diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt index 435224bf..06e34a09 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt @@ -30,7 +30,6 @@ class KinAccountIntegrationTest { private val appId = "1a2c" private val fee: Int = 100 private val feeInKin: BigDecimal = BigDecimal.valueOf(0.001) - private val appIdVersionPrefix = "1" private val timeoutDurationSeconds: Long = 15 private val timeoutDurationSecondsLong: Long = 20 @@ -106,10 +105,10 @@ class KinAccountIntegrationTest { @LargeTest @Throws(Exception::class) fun sendTransaction_WithMemo() { - val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100) + val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100) val memo = "fake memo" - val expectedMemo = addAppIdToMemo(memo) + val expectedMemo = addAppIdToMemo(memo, appId) val transactionId = sendTransactionAndAssert(kinAccountSender, kinAccountReceiver, memo) @@ -123,8 +122,8 @@ class KinAccountIntegrationTest { @LargeTest @Throws(Exception::class) fun sendTransaction_WithoutMemoJustPrefix() { - val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100) - val expectedMemo = addAppIdToMemo("") + val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100) + val expectedMemo = addAppIdToMemo("", appId) val transactionId = sendTransactionAndAssert(kinAccountSender, kinAccountReceiver, null) val server = Server(TEST_NETWORK_URL) val transactionResponse = server.transactions().transaction(transactionId.id()) @@ -136,10 +135,8 @@ class KinAccountIntegrationTest { @LargeTest @Throws(Exception::class) fun sendTransaction_WithoutMemo() { - val (kinAccountSender, kinAccountReceiver) = onboardAccounts( - senderFundAmount = 100, - kinClient = KinClient(InstrumentationRegistry.getTargetContext(), environment, null)) - + val (kinAccountSender, kinAccountReceiver) = onboardAccounts(KinClient(InstrumentationRegistry.getTargetContext(), environment, null), + fakeKinOnBoard, senderFundAmount = 100) val transactionId = sendTransactionAndAssert(kinAccountSender, kinAccountReceiver, null) val server = Server(TEST_NETWORK_URL) val transactionResponse = server.transactions().transaction(transactionId.id()) @@ -151,10 +148,8 @@ class KinAccountIntegrationTest { @LargeTest @Throws(Exception::class) fun sendTransaction_WithoutMemoPrefix() { - val (kinAccountSender, kinAccountReceiver) = onboardAccounts( - senderFundAmount = 100, - kinClient = KinClient(InstrumentationRegistry.getTargetContext(), environment, null)) - + val (kinAccountSender, kinAccountReceiver) = onboardAccounts(KinClient(InstrumentationRegistry.getTargetContext(), environment, null), + fakeKinOnBoard, senderFundAmount = 100) val memo = "fake memo" val transactionId = sendTransactionAndAssert(kinAccountSender, kinAccountReceiver, memo) val server = Server(TEST_NETWORK_URL) @@ -234,7 +229,7 @@ class KinAccountIntegrationTest { @LargeTest @Throws(Exception::class) fun sendTransaction_NotEnoughFee_InsufficientFeeException() { - val (kinAccountSender, kinAccountReceiver) = onboardAccounts() + val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard) expectedEx.expect(InsufficientFeeException::class.java) val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) @@ -246,7 +241,7 @@ class KinAccountIntegrationTest { @LargeTest @Throws(Exception::class) fun sendWhitelistTransaction_FeeNotReduce() { - val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100) + val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100) val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), @@ -260,7 +255,7 @@ class KinAccountIntegrationTest { @LargeTest @Throws(Exception::class) fun sendWhitelistTransaction_Success() { - val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100) + val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100) val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), @@ -275,7 +270,7 @@ class KinAccountIntegrationTest { @LargeTest @Throws(Exception::class) fun sendTransaction_Success() { - val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100) + val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100) val transactionId = sendTransactionAndAssert(kinAccountSender, kinAccountReceiver, "fake memo") assertNotNull(transactionId) assertThat(transactionId.id(), not(isEmptyString())) @@ -302,7 +297,7 @@ class KinAccountIntegrationTest { val fundingAmount = BigDecimal("100") val transactionAmount = BigDecimal("21.123") - val (kinAccountSender, kinAccountReceiver) = onboardAccounts(0, 0) + val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, 0, 0) //register listeners for testing val actualPaymentsResults = ArrayList() @@ -324,7 +319,7 @@ class KinAccountIntegrationTest { //send the transaction we want to observe fakeKinOnBoard.fundWithKin(kinAccountSender.publicAddress.orEmpty(), "100") val memo = "memo" - val expectedMemo = addAppIdToMemo(memo) + val expectedMemo = addAppIdToMemo(memo, appId) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), transactionAmount, fee, memo) val expectedTransactionId = kinAccountSender.sendTransactionSync(transaction) @@ -351,7 +346,7 @@ class KinAccountIntegrationTest { @LargeTest @Throws(Exception::class) fun createPaymentListener_RemoveListener_NoEvents() { - val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100) + val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100) val latch = CountDownLatch(1) val listenerRegistration = kinAccountReceiver.addPaymentListener { @@ -368,27 +363,13 @@ class KinAccountIntegrationTest { @LargeTest @Throws(Exception::class) fun sendTransaction_NotEnoughKin_InsufficientKinException() { - val (kinAccountSender, kinAccountReceiver) = onboardAccounts() + val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard) expectedEx.expect(InsufficientKinException::class.java) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) kinAccountSender.sendTransactionSync(transaction) } - private fun onboardAccounts(senderFundAmount: Int = 0, - receiverFundAmount: Int = 0, - kinClient: KinClient = this.kinClient): Pair { - val kinAccountSender = kinClient.addAccount() - val kinAccountReceiver = kinClient.addAccount() - fakeKinOnBoard.createAccount(kinAccountSender.publicAddress.orEmpty(), senderFundAmount) - fakeKinOnBoard.createAccount(kinAccountReceiver.publicAddress.orEmpty(), receiverFundAmount) - return Pair(kinAccountSender, kinAccountReceiver) - } - - private fun addAppIdToMemo(memo: String): String { - return appIdVersionPrefix.plus("-").plus(appId).plus("-").plus(memo) - } - private fun sendTransactionAndAssert(kinAccountSender: KinAccount, kinAccountReceiver: KinAccount, memo: String?): TransactionId { val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee, memo) diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/Utils.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/Utils.kt new file mode 100644 index 00000000..aeaf50d5 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/Utils.kt @@ -0,0 +1,29 @@ +package kin.sdk + +import android.support.test.InstrumentationRegistry +import kin.sdk.internal.blockchain.TransactionSender +import kin.sdk.internal.queue.PaymentQueueImpl + + +private const val appIdVersionPrefix = "1" + +fun onboardAccounts(kinClient: KinClient, fakeKinOnBoard: FakeKinOnBoard, senderFundAmount: Int = 0, + receiverFundAmount: Int = 0): Pair { + val kinAccountSender = kinClient.addAccount() + val kinAccountReceiver = kinClient.addAccount() + fakeKinOnBoard.createAccount(kinAccountSender.publicAddress.orEmpty(), senderFundAmount) + fakeKinOnBoard.createAccount(kinAccountReceiver.publicAddress.orEmpty(), receiverFundAmount) + return Pair(kinAccountSender, kinAccountReceiver) +} + +fun addAppIdToMemo(memo: String, appId: String): String { + return appIdVersionPrefix.plus("-").plus(appId).plus("-").plus(memo) +} + +fun getPaymentQueueTestKinClient(environment: Environment, appId: String, txSender: TransactionSender? = null, + configuration: PaymentQueueImpl.PaymentQueueConfiguration? = null): KinClient { + val injector = IntegrationTestKinClientInjector(InstrumentationRegistry.getTargetContext(), + environment, appId, "", txSender, configuration) + return KinClient(injector) +} + diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueEndToEndIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueEndToEndIntegrationTest.kt new file mode 100644 index 00000000..345ce4ab --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueEndToEndIntegrationTest.kt @@ -0,0 +1,174 @@ +package kin.sdk.internal.queue + +import kin.base.MemoText +import kin.base.Server +import kin.sdk.* +import kin.sdk.exception.KinException +import kin.sdk.queue.PaymentQueue +import kin.sdk.queue.PaymentQueueTransactionProcess +import kin.sdk.queue.PendingPayment +import kin.sdk.transactiondata.BatchPaymentTransaction +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.After +import org.junit.Before +import org.junit.BeforeClass +import org.junit.Test +import java.io.IOException +import java.math.BigDecimal +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import kotlin.test.assertTrue +import kotlin.test.fail + + +class PaymentQueueEndToEndIntegrationTest { + + private val appId = "1a2c" + private val fee: Int = 100 + private val feeInKin: BigDecimal = BigDecimal.valueOf(0.001) + private val timeoutDurationSecondsLong: Long = 20 + private val environment: Environment = Environment(IntegConsts.TEST_NETWORK_URL, IntegConsts.TEST_NETWORK_ID) + private lateinit var kinClient: KinClient + + + @Before + fun setup() { + kinClient = getPaymentQueueTestKinClient(environment, appId) + kinClient.clearAllAccounts() + } + + @After + fun teardown() { + if (::kinClient.isInitialized) { + kinClient.clearAllAccounts() + } + } + + @Test + fun enqueuePayments_DelayBetweenPayments_Success() { + val amount = BigDecimal(50) + val kinAccountSender = kinClient.addAccount() + fakeKinOnBoard.createAccount(kinAccountSender.publicAddress.orEmpty(), 250) + val kinAccountReceiver1 = addAndCreateAccount() + val kinAccountReceiver2 = addAndCreateAccount() + val kinAccountReceiver3 = addAndCreateAccount() + + val latch = CountDownLatch(1) + val paymentQueue = kinAccountSender.paymentQueue() + setEventListener(paymentQueue, latch) + + paymentQueue.setFee(fee) + paymentQueue.enqueuePayment(kinAccountReceiver2.publicAddress.orEmpty(), amount, "2") + paymentQueue.enqueuePayment(kinAccountReceiver1.publicAddress.orEmpty(), amount, "1") + paymentQueue.enqueuePayment(kinAccountReceiver3.publicAddress.orEmpty(), amount, "3") + paymentQueue.enqueuePayment(kinAccountReceiver1.publicAddress.orEmpty(), amount, "4") + Thread.sleep(50) + assertThat(paymentQueue.pendingPaymentsCount(), equalTo(4)) + + assertTrue(latch.await(timeoutDurationSecondsLong, TimeUnit.SECONDS)) + + assertThat(kinAccountReceiver1.balanceSync.value(), equalTo(BigDecimal("100.00000"))) + assertThat(kinAccountReceiver2.balanceSync.value(), equalTo(BigDecimal("50.00000"))) + assertThat(kinAccountReceiver3.balanceSync.value(), equalTo(BigDecimal("50.00000"))) + assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("50.00000").subtract(feeInKin.multiply(BigDecimal(4))))) + } + + @Test + fun enqueuePayments_MaxQueueElements_Intercept_Success() { + val amount = BigDecimal(50) + val pendingPayments = mutableListOf() + val kinAccountSender = kinClient.addAccount() + fakeKinOnBoard.createAccount(kinAccountSender.publicAddress.orEmpty(), 350) + + val kinAccountReceiver1 = addAndCreateAccount() + val kinAccountReceiver2 = addAndCreateAccount() + val kinAccountReceiver3 = addAndCreateAccount() + + val latch = CountDownLatch(1) + val paymentQueue = kinAccountSender.paymentQueue() + setEventListener(paymentQueue, latch) + + paymentQueue.setFee(fee) + + val memo = "fake memo" + val expectedMemo = addAppIdToMemo(memo, appId) + + + var transactionId: TransactionId? = null + paymentQueue.setTransactionInterceptor(object : TransactionInterceptor { + override fun interceptTransactionSending(process: PaymentQueueTransactionProcess?): TransactionId? { + val transaction = process?.transaction(memo) + // simulate some work + Thread.sleep(5000) + transactionId = process?.send(transaction) + return transactionId + } + }) + + paymentQueue.enqueuePayment(kinAccountReceiver1.publicAddress.orEmpty(), amount, "1") + paymentQueue.enqueuePayment(kinAccountReceiver2.publicAddress.orEmpty(), amount, "2") + paymentQueue.enqueuePayment(kinAccountReceiver3.publicAddress.orEmpty(), amount, "3") + paymentQueue.enqueuePayment(kinAccountReceiver1.publicAddress.orEmpty(), amount, "4") + paymentQueue.enqueuePayment(kinAccountReceiver3.publicAddress.orEmpty(), amount, "5") + Thread.sleep(100) + assertThat(paymentQueue.pendingPaymentsCount(), equalTo(0)) + + assertTrue(latch.await(timeoutDurationSecondsLong, TimeUnit.SECONDS)) + + assertThat(kinAccountReceiver1.balanceSync.value(), equalTo(BigDecimal("100.00000"))) + assertThat(kinAccountReceiver2.balanceSync.value(), equalTo(BigDecimal("50.00000"))) + assertThat(kinAccountReceiver3.balanceSync.value(), equalTo(BigDecimal("100.00000"))) + assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("100.00000").subtract(feeInKin.multiply(BigDecimal(5))))) + + val server = Server(IntegConsts.TEST_NETWORK_URL) + val transactionResponse = server.transactions().transaction(transactionId?.id()) + val actualMemo = transactionResponse.memo + assertThat((actualMemo as MemoText).text, equalTo(expectedMemo)) + } + + + private fun setEventListener(paymentQueue: PaymentQueue, latch: CountDownLatch) { + paymentQueue.setEventListener(object : PaymentQueue.EventListener { + override fun onPaymentEnqueued(payment: PendingPayment?) { + } + + override fun onTransactionSend(transaction: BatchPaymentTransaction?, payments: MutableList?) { + assertThat(paymentQueue.transactionInProgress(), equalTo(true)) + } + + override fun onTransactionSendSuccess(transactionId: TransactionId?, payments: MutableList?) { + latch.countDown() + } + + override fun onTransactionSendFailed(payments: MutableList?, exception: KinException?) { + latch.countDown() + } + + }) + } + + //TODO include events manager and balance updater to the happy path and transaction params happy path + + private fun addAndCreateAccount(): KinAccount { + val kinAccount = kinClient.addAccount() + kinAccount.publicAddress?.let { + fakeKinOnBoard.createAccount(it) + } ?: kotlin.run { + fail("public address is null") + } + return kinAccount + } + + companion object { + private lateinit var fakeKinOnBoard: FakeKinOnBoard + + @BeforeClass + @JvmStatic + @Throws(IOException::class) + fun setupKinOnBoard() { + fakeKinOnBoard = FakeKinOnBoard() + } + } + +} \ No newline at end of file diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueIntegrationTests.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueIntegrationTests.kt new file mode 100644 index 00000000..70385bfc --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueIntegrationTests.kt @@ -0,0 +1,193 @@ +package kin.sdk.internal.queue + +import com.nhaarman.mockitokotlin2.* +import kin.sdk.* +import kin.sdk.IntegrationTestKinClientInjector.Constants.delayBetweenPaymentsMillis +import kin.sdk.IntegrationTestKinClientInjector.Constants.maxNumOfPayments +import kin.sdk.IntegrationTestKinClientInjector.Constants.queueTimeoutMillis +import kin.sdk.exception.KinException +import kin.sdk.internal.blockchain.TransactionSender +import kin.sdk.internal.data.TransactionIdImpl +import kin.sdk.internal.queue.PaymentQueueIntegrationTests.CONSTANTS.APP_ID +import kin.sdk.internal.queue.PaymentQueueIntegrationTests.CONSTANTS.FEE +import kin.sdk.internal.queue.PaymentQueueIntegrationTests.CONSTANTS.TIMEOUT_DURATION_SECONDS_LONG +import kin.sdk.queue.PaymentQueue +import kin.sdk.queue.PaymentQueueTransactionProcess +import kin.sdk.queue.PendingPayment +import kin.sdk.transactiondata.BatchPaymentTransaction +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.* +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito.`when` +import java.math.BigDecimal +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue + +class PaymentQueueIntegrationTests { + + object CONSTANTS { + const val APP_ID = "1a2c" + const val TIMEOUT_DURATION_SECONDS_LONG: Long = 20 + const val FEE: Int = 100 + } + + private val environment: Environment = Environment(IntegConsts.TEST_NETWORK_URL, IntegConsts.TEST_NETWORK_ID) + private lateinit var kinClient: KinClient + + private val transactionSender: TransactionSender = mock() + private val transactionInterceptor: TransactionInterceptor = mock() + + + @Before + fun setup() { + val paymentQueueConfiguration = PaymentQueueImpl.PaymentQueueConfiguration(50, queueTimeoutMillis, maxNumOfPayments) + kinClient = getPaymentQueueTestKinClient(environment, APP_ID, transactionSender, paymentQueueConfiguration) + kinClient.clearAllAccounts() + } + + @After + fun teardown() { + if (::kinClient.isInitialized) { + kinClient.clearAllAccounts() + } + } + + @Test + fun enqueuePayments_NegativeAmount_IllegalArgumentException() { + val amount = BigDecimal(-50) + val kinAccountSender = kinClient.addAccount() + val kinAccountReceiver = kinClient.addAccount() + val paymentQueue = kinAccountSender.paymentQueue() + + assertFailsWith { paymentQueue.enqueuePayment(kinAccountReceiver.publicAddress.orEmpty(), amount) } + } + + @Test + fun enqueuePayments_NegativeFee_IllegalArgumentException() { + val kinAccountSender = kinClient.addAccount() + val paymentQueue = kinAccountSender.paymentQueue() + assertFailsWith { paymentQueue.setFee(-FEE) } + } + + @Test + fun enqueuePayments_ZeroFee_GotMinimumFee() { + //TODO captor the fee parameter from build and see that it is the minimum + val argumentCaptor = argumentCaptor() + val kinAccountSender = kinClient.addAccount() + val kinAccountReceiver = kinClient.addAccount() + val paymentQueue = kinAccountSender.paymentQueue() + +// `when`(transactionSender.buildBatchPaymentTransaction(any(), any(), any(), any())).thenReturn(null) + + paymentQueue.enqueuePayment(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal(50)) + Thread.sleep(delayBetweenPaymentsMillis + 1000) + + verify(transactionSender).buildBatchPaymentTransaction(any(), any(), argumentCaptor.capture(), eq(null)) + assertThat(kinClient.minimumFeeSync.toInt(), equalTo((argumentCaptor.firstValue))) + } + + @Test + fun enqueuePayments_UseInterceptor_VerifyInterceptorCalled() { + val kinAccountSender = kinClient.addAccount() + val kinAccountReceiver = kinClient.addAccount() + val paymentQueue = kinAccountSender.paymentQueue() + paymentQueue.setTransactionInterceptor(transactionInterceptor) + paymentQueue.setFee(FEE) + + val latch = CountDownLatch(5) + paymentQueue.setEventListener(object : PaymentQueue.EventListener { + override fun onPaymentEnqueued(payment: PendingPayment?) { + } + + override fun onTransactionSend(transaction: BatchPaymentTransaction?, payments: MutableList?) { + + } + + override fun onTransactionSendSuccess(transactionId: TransactionId?, payments: MutableList?) { + } + + override fun onTransactionSendFailed(payments: MutableList?, exception: KinException?) { + // this will also verify that it fails 5 times + latch.countDown() + } + + }) + + `when`(transactionInterceptor.interceptTransactionSending(any())).thenAnswer { + Thread.sleep(200) + null + } + for (x in 0 until 5) { + for (y in 0 until 4) { + paymentQueue.enqueuePayment(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal(50)) + } + Thread.sleep(delayBetweenPaymentsMillis + 10) + } + assertTrue(latch.await(TIMEOUT_DURATION_SECONDS_LONG, TimeUnit.SECONDS)) + + verify(transactionInterceptor, times(5)).interceptTransactionSending(any()) + } + + @Test + fun enqueuePayments_AddMultipleTasks_TasksInvokedInCorrectOrder() { + // in this tests we are inserting to the task queue 5 lists of pending payments, each with 4 elements. the first one is taken but the others are not because the first one is in progress. + // after they merged in the task queue we have 4 lists, the top 3 with 5 elements and the last one with 1. + // then we check that indeed we send 5 transactions with the correct order and number of elements with their ids. + + val amount = BigDecimal(50) + val kinAccountSender = kinClient.addAccount() + val kinAccountReceiver = kinClient.addAccount() + val paymentQueue = kinAccountSender.paymentQueue() + paymentQueue.setFee(FEE) + + `when`(transactionSender.sendTransaction(null)).thenAnswer { + Thread.sleep(1000) // wait for the tasks to accumulate and merged in the task queue + TransactionIdImpl("fake transaction id") + } + + val listOfListOfPayments: MutableList?> = mutableListOf() + val latch = CountDownLatch(5) + paymentQueue.setEventListener(object : PaymentQueue.EventListener { + override fun onPaymentEnqueued(payment: PendingPayment?) { + } + + override fun onTransactionSend(transaction: BatchPaymentTransaction?, payments: MutableList?) { + listOfListOfPayments.add(payments) + latch.countDown() + } + + override fun onTransactionSendSuccess(transactionId: TransactionId?, payments: MutableList?) { + } + + override fun onTransactionSendFailed(payments: MutableList?, exception: KinException?) { + } + + }) + + var idCounter = 1 + // create 'maxNumOfPayments' lists, each one with 4 pending payments + for (x in 0 until maxNumOfPayments) { + for (y in 0 until 4) { + paymentQueue.enqueuePayment(kinAccountReceiver.publicAddress.orEmpty(), amount, idCounter) + idCounter++ + } + Thread.sleep(delayBetweenPaymentsMillis + 10) + } + + assertTrue(latch.await(TIMEOUT_DURATION_SECONDS_LONG, TimeUnit.SECONDS)) + + val listOfListOfIds = listOfListOfPayments.map { it?.map { pendingPayment -> pendingPayment.metadata() as Int } } + val expectedListOfListOfIds = listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8, 9), listOf(10, 11, 12, 13, 14), listOf(15, 16, 17, 18, 19), listOf(20)) + + assertThat(listOfListOfIds, hasSize(expectedListOfListOfIds.size)) + for (i in listOfListOfIds.indices) { + assertThat(listOfListOfIds[i], `is`(expectedListOfListOfIds[i])) + } + } + + //TODO add tests which includes testings for the events manager, balance updater and transaction params, and tests for negative/exception path (fee to low, all exceptions, etc) +} \ No newline at end of file diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/TasksQueueIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/TasksQueueIntegrationTest.kt deleted file mode 100644 index bcdf7c2d..00000000 --- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/TasksQueueIntegrationTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -package kin.sdk.internal.queue - -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.equalTo -import org.junit.Test - -class TasksQueueIntegrationTest { - - @Test - fun test() { - //given - val tasksQueue = TasksQueueImpl() - var value = 5 - - tasksQueue.execute { - value = 10 - Thread.sleep(15000) - } - tasksQueue.execute { - value = 10 - Thread.sleep(15000) - } - - - Thread.sleep(40000) - //then - assertThat(value, equalTo(10)) - } - -} \ No newline at end of file diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java index 528232bb..65f76f77 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java @@ -11,6 +11,7 @@ import kin.sdk.exception.OperationFailedException; import kin.sdk.exception.TransactionFailedException; import kin.sdk.queue.PaymentQueue; +import kin.sdk.queue.TransactionProcess; import kin.sdk.transactiondata.PaymentTransaction; import kin.sdk.transactiondata.TransactionParams; import kin.utils.Request; @@ -185,7 +186,7 @@ PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull * @return {@code Request}, TransactionId - the transaction identifier. */ Request sendTransaction(TransactionParams transactionParams, - @Nullable TransactionInterceptor interceptor); + @Nullable TransactionInterceptor interceptor); /** * @return the payment queue. diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java index ca032eb9..4db89676 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java @@ -4,18 +4,14 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; -import android.util.Log; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; import kin.base.KeyPair; -import kin.base.Network; -import kin.base.Server; import kin.sdk.exception.CorruptedDataException; import kin.sdk.exception.CreateAccountException; import kin.sdk.exception.CryptoException; @@ -24,15 +20,12 @@ import kin.sdk.exception.OperationFailedException; import kin.sdk.internal.account.KinAccountImpl; import kin.sdk.internal.backuprestore.BackupRestore; -import kin.sdk.internal.backuprestore.BackupRestoreImpl; import kin.sdk.internal.blockchain.AccountInfoRetriever; import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetrieverImpl; import kin.sdk.internal.blockchain.TransactionSender; import kin.sdk.internal.blockchain.events.BlockchainEventsCreator; import kin.sdk.internal.queue.PaymentQueueImpl; import kin.sdk.internal.storage.KeyStore; -import kin.sdk.internal.storage.KeyStoreImpl; -import kin.sdk.internal.storage.SharedPrefStore; import kin.sdk.queue.PaymentQueue; import kin.utils.Request; @@ -43,8 +36,6 @@ */ public class KinClient { - private static final String STORE_NAME_PREFIX = "KinKeyStore_"; - private static final int TRANSACTIONS_TIMEOUT = 30; private final Environment environment; private final KeyStore keyStore; private final TransactionSender transactionSender; @@ -54,6 +45,7 @@ public class KinClient { private final BackupRestore backupRestore; private final String appId; private final String storeKey; + private final PaymentQueueImpl.PaymentQueueConfiguration paymentQueueConfiguration; @NonNull private List kinAccounts = new ArrayList<>(1); @@ -84,17 +76,32 @@ public KinClient(@NonNull Context context, @NonNull Environment environment, checkNotNull(storeKey, "storeKey"); checkNotNull(context, "context"); checkNotNull(environment, "environment"); - validateAppId(appId); + KinClientInjector injector = new KinClientInjector(context, environment, appId, storeKey); this.environment = environment; - this.backupRestore = new BackupRestoreImpl(); - Server server = initServer(); + this.backupRestore = injector.getBackupRestore(); this.appId = appId; this.storeKey = storeKey; - keyStore = initKeyStore(context.getApplicationContext(), storeKey); - transactionSender = new TransactionSender(server, appId); - accountInfoRetriever = new AccountInfoRetriever(server); - generalBlockchainInfoRetriever = new GeneralBlockchainInfoRetrieverImpl(server); - blockchainEventsCreator = new BlockchainEventsCreator(server); + keyStore = injector.getKeyStore(); + transactionSender = injector.getTransactionSender(); + accountInfoRetriever = injector.getAccountInfoRetriever(); + generalBlockchainInfoRetriever = injector.getGeneralBlockchainInfoRetriever(); + blockchainEventsCreator = injector.getBlockchainEventsCreator(); + this.paymentQueueConfiguration = injector.getPaymentQueueConfiguration(); + loadAccounts(); + } + + @VisibleForTesting + KinClient(KinClientInjector injector) { + this.environment = injector.getEnvironment(); + this.keyStore = injector.getKeyStore(); + this.transactionSender = injector.getTransactionSender(); + this.accountInfoRetriever = injector.getAccountInfoRetriever(); + this.generalBlockchainInfoRetriever = injector.getGeneralBlockchainInfoRetriever(); + this.blockchainEventsCreator = injector.getBlockchainEventsCreator(); + this.backupRestore = injector.getBackupRestore(); + this.appId = injector.getAppId(); + this.storeKey = injector.getStoreKey(); + this.paymentQueueConfiguration = injector.getPaymentQueueConfiguration(); loadAccounts(); } @@ -113,20 +120,10 @@ public KinClient(@NonNull Context context, @NonNull Environment environment, this.backupRestore = backupRestore; this.appId = appId; this.storeKey = storeKey; + this.paymentQueueConfiguration = new PaymentQueueImpl.PaymentQueueConfiguration(0, 0, 0); loadAccounts(); } - private Server initServer() { - Network.use(environment.getNetwork()); - return new Server(environment.getNetworkUrl(), TRANSACTIONS_TIMEOUT, TimeUnit.SECONDS); - } - - private KeyStore initKeyStore(Context context, String id) { - SharedPrefStore store = new SharedPrefStore( - context.getSharedPreferences(STORE_NAME_PREFIX + id, Context.MODE_PRIVATE)); - return new KeyStoreImpl(store, backupRestore); - } - private void loadAccounts() { List accounts = null; try { @@ -157,17 +154,6 @@ private void updateKinAccounts(List storageAccounts) { kinAccounts = newKinAccountsList; } - private void validateAppId(String appId) { - if (appId == null || appId.equals("")) { - Log.w("KinClient", "WARNING: KinClient instance was created without a proper " + - "application ID. Is this what you intended to do?"); - } else if (!appId.matches("[a-zA-Z0-9]{3,4}")) { - throw new IllegalArgumentException("appId must contain only upper and/or lower case " + - "letters and/or digits and that the total string length is between 3 to 4.\n" + - "for example 1234 or 2ab3 or cd2 or fqa, etc."); - } - } - /** * Creates and adds an account. *

Once created, the account information will be stored securely on the device and can @@ -260,6 +246,7 @@ public boolean deleteAccount(int index) throws DeleteAccountException { KinAccountImpl removedAccount = kinAccounts.remove(index); removedAccount.markAsDeleted(); deleteSuccess = true; + removedAccount.paymentQueue().releaseQueue(); // TODO: 2019-09-11 add tests for that } return deleteSuccess; } @@ -271,6 +258,7 @@ public void clearAllAccounts() { keyStore.clearAllAccounts(); for (KinAccountImpl kinAccount : kinAccounts) { kinAccount.markAsDeleted(); + kinAccount.paymentQueue().releaseQueue(); // TODO: 2019-09-11 add tests for that } kinAccounts.clear(); } @@ -282,7 +270,7 @@ public Environment getEnvironment() { @NonNull private KinAccountImpl createNewKinAccount(KeyPair account) { PaymentQueue paymentQueue = new PaymentQueueImpl(account, transactionSender, - generalBlockchainInfoRetriever); + generalBlockchainInfoRetriever, paymentQueueConfiguration); return new KinAccountImpl(account, backupRestore, transactionSender, accountInfoRetriever, blockchainEventsCreator, paymentQueue); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClientInjector.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClientInjector.java new file mode 100644 index 00000000..24bfd0f2 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClientInjector.java @@ -0,0 +1,129 @@ +package kin.sdk; + +import android.content.Context; + +import java.util.concurrent.TimeUnit; + +import kin.base.Network; +import kin.base.Server; +import kin.sdk.internal.backuprestore.BackupRestoreImpl; +import kin.sdk.internal.blockchain.AccountInfoRetriever; +import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetrieverImpl; +import kin.sdk.internal.blockchain.TransactionSender; +import kin.sdk.internal.blockchain.events.BlockchainEventsCreator; +import kin.sdk.internal.queue.PaymentQueueImpl; +import kin.sdk.internal.storage.KeyStore; +import kin.sdk.internal.storage.KeyStoreImpl; +import kin.sdk.internal.storage.SharedPrefStore; + +import static kin.sdk.internal.Utils.checkNotNull; +import static kin.sdk.internal.Utils.validateAppId; + +public class KinClientInjector { + + private static final long delayBetweenPaymentsMillis = 3000; // TODO: 2019-08-15 get those + // number from somewhere + private static final long queueTimeoutMillis = 10000; // TODO: 2019-08-15 get those + // number from somewhere + private static final int maxNumOfPayments = 100; // TODO: 2019-08-15 get those + // number from somewhere + private static final String STORE_NAME_PREFIX = "KinKeyStore_"; + private static final int TRANSACTIONS_TIMEOUT = 30; + + private Context context; + private Environment environment; + private String appId; + private String storeKey; + private Server server; + private BackupRestoreImpl backupRestore; + + public KinClientInjector(Context context, Environment environment, String appId, + String storeKey) { + checkNotNull(storeKey, "storeKey"); + checkNotNull(context, "context"); + checkNotNull(environment, "environment"); + validateAppId(appId); + this.context = context; + this.environment = environment; + this.appId = appId; + this.storeKey = storeKey; + } + + KeyStore getKeyStore() { + return initKeyStore(context.getApplicationContext(), storeKey); + } + + TransactionSender getTransactionSender() { + return new TransactionSender(getServer(), appId); + } + + BackupRestoreImpl getBackupRestore() { + if (backupRestore == null) { + backupRestore = new BackupRestoreImpl(); + } + return backupRestore; + } + + AccountInfoRetriever getAccountInfoRetriever() { + return new AccountInfoRetriever(getServer()); + } + + GeneralBlockchainInfoRetrieverImpl getGeneralBlockchainInfoRetriever() { + return new GeneralBlockchainInfoRetrieverImpl(getServer()); + } + + BlockchainEventsCreator getBlockchainEventsCreator() { + return new BlockchainEventsCreator(getServer()); + } + + PaymentQueueImpl.PaymentQueueConfiguration getPaymentQueueConfiguration() { + return new PaymentQueueImpl.PaymentQueueConfiguration(delayBetweenPaymentsMillis, + queueTimeoutMillis, maxNumOfPayments); + } + + Server getServer() { + if (server != null) { + return server; + } else { + return initServer(); + } + } + + void setServer(Server server) { + this.server = server; + } + + String getStoreNamePrefix() { + return STORE_NAME_PREFIX; + } + + int getTransactionTimeout() { + return TRANSACTIONS_TIMEOUT; + } + + public Environment getEnvironment() { + return environment; + } + + public String getAppId() { + return appId; + } + + public String getStoreKey() { + return storeKey; + } + + private KeyStore initKeyStore(Context context, String id) { + SharedPrefStore store = new SharedPrefStore( + context.getSharedPreferences(getStoreNamePrefix() + id, Context.MODE_PRIVATE)); + return new KeyStoreImpl(store, getBackupRestore()); + } + + private Server initServer() { + Network.use(environment.getNetwork()); + setServer(new Server(environment.getNetworkUrl(), getTransactionTimeout(), + TimeUnit.SECONDS)); + return new Server(environment.getNetworkUrl(), getTransactionTimeout(), TimeUnit.SECONDS); + } + +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionInterceptor.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionInterceptor.java index 757e47e7..b7299b7e 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionInterceptor.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionInterceptor.java @@ -5,7 +5,7 @@ /** * provide the generated transaction before sending to the blockchain. */ -public interface TransactionInterceptor { +public interface TransactionInterceptor { /** * Intercept the generated transaction before sending to the blockchain @@ -21,6 +21,6 @@ public interface TransactionInterceptor { * @return the transaction identifier. * @throws Exception */ - TransactionId interceptTransactionSending(TransactionProcess transactionProcess) throws Exception; + TransactionId interceptTransactionSending(T transactionProcess) throws Exception; } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java index 22ad388f..aedb15b7 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java @@ -2,6 +2,7 @@ import android.support.annotation.NonNull; +import android.util.Log; import java.math.BigDecimal; import java.util.ArrayList; @@ -34,6 +35,17 @@ public static String byteArrayToHex(byte[] a) { return sb.toString(); } + public static void validateAppId(String appId) { + if (appId == null || appId.equals("")) { + Log.w("KinClient", "WARNING: KinClient instance was created without a proper " + + "application ID. Is this what you intended to do?"); + } else if (!appId.matches("[a-zA-Z0-9]{3,4}")) { + throw new IllegalArgumentException("appId must contain only upper and/or lower case " + + "letters and/or digits and that the total string length is between 3 to 4.\n" + + "for example 1234 or 2ab3 or cd2 or fqa, etc."); + } + } + public static void checkNotNull(Object obj, String paramName) { if (obj == null) { throw new IllegalArgumentException(paramName + " == null"); diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java index a1b8128f..fafb46fe 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java @@ -10,8 +10,8 @@ import kin.sdk.KinAccount; import kin.sdk.TransactionId; import kin.sdk.TransactionInterceptor; -import kin.sdk.internal.queue.PaymentQueueImpl; import kin.sdk.queue.PaymentQueue; +import kin.sdk.queue.TransactionProcess; import kin.sdk.transactiondata.PaymentTransaction; import kin.sdk.transactiondata.TransactionParams; import kin.utils.Request; @@ -80,12 +80,16 @@ public Request sendTransaction(final TransactionParams transactio @Override public Request sendTransaction(final TransactionParams transactionParams, - final TransactionInterceptor interceptor) { + final TransactionInterceptor interceptor) { return new Request<>(new Callable() { @Override public TransactionId call() { - return ((PaymentQueueImpl) paymentQueue).enqueueTransactionParams(transactionParams, interceptor); +// return ((PaymentQueueImpl) paymentQueue).enqueueTransactionParams +// (transactionParams, interceptor); + // TODO: 2019-09-09 implement + return null; } + }); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java index b956c632..35b251e1 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java @@ -34,14 +34,17 @@ public class TransactionSender { - private static final int MEMO_BYTES_LENGTH_LIMIT = 21; //Memo length limitation(in bytes) is - // 28 but we add 7 more bytes which includes the appId and some characters. + // Memo length limitation(in bytes) is 28 but we add 7(or 6) more bytes which includes the + // appId and some characters. + private static final int MEMO_BYTES_LENGTH_LIMIT = 21; // TODO: 2019-09-08 we should fix this + // because it could be 22 in case of a 3 char appId private static final int MAX_NUM_OF_DECIMAL_PLACES = 4; - private static String MEMO_APP_ID_VERSION_PREFIX = "1"; - private static String MEMO_DELIMITER = "-"; + private static final String MEMO_APP_ID_VERSION_PREFIX = "1"; + private static final String MEMO_DELIMITER = "-"; private static final String INSUFFICIENT_KIN_RESULT_CODE = "op_underfunded"; private static final String INSUFFICIENT_FEE_RESULT_CODE = "tx_insufficient_fee"; private static final String INSUFFICIENT_BALANCE_RESULT_CODE = "tx_insufficient_balance"; + private final Server server; //horizon server private final String appId; @@ -73,6 +76,30 @@ public PaymentTransaction buildPaymentTransaction(@NonNull KeyPair from, return new PaymentTransaction(stellarTransaction, addressee.getAccountId(), amount, memo); } + public BatchPaymentTransaction buildBatchPaymentTransaction(@NonNull KeyPair from, + @NonNull List pendingPayments, + int fee, @Nullable String memo) throws OperationFailedException { + checkBatchPaymentkParams(from, fee, memo); + if (appId != null && !appId.equals("")) { + memo = addAppIdToMemo(memo); + } + + AccountResponse sourceAccount = loadSourceAccount(from); + + Builder transactionBuilder = new Builder(sourceAccount); + for (PendingPayment pendingPayment : pendingPayments) { + KeyPair destination = + generateAddresseeKeyPair(pendingPayment.destinationPublicAddress()); + addPaymentOperationToTransaction(transactionBuilder, destination, + pendingPayment.amount()); + } + + kin.base.Transaction transaction = addTransactionParametersAndSign(from, fee, memo, + transactionBuilder); + return new BatchPaymentTransaction(transaction); + } + + public TransactionId sendTransaction(Transaction transaction) throws OperationFailedException { return sendTransaction(((TransactionInternal) transaction).baseTransaction()); } @@ -166,31 +193,6 @@ private kin.base.Transaction buildStellarTransaction(@NonNull KeyPair from, return addTransactionParametersAndSign(from, fee, memo, transactionBuilder); } - - public BatchPaymentTransaction buildBatchPaymentTransaction(@NonNull KeyPair from, - @NonNull List pendingPayments, - int fee, @Nullable String memo) throws OperationFailedException { - - checkBatchPaymentkParams(from, fee, memo); - if (appId != null && !appId.equals("")) { - memo = addAppIdToMemo(memo); - } - - AccountResponse sourceAccount = loadSourceAccount(from); - - Builder transactionBuilder = new Builder(sourceAccount); - for (PendingPayment pendingPayment : pendingPayments) { - KeyPair destination = - generateAddresseeKeyPair(pendingPayment.destinationPublicAddress()); - addPaymentOperationToTransaction(transactionBuilder, destination, - pendingPayment.amount()); - } - - kin.base.Transaction transaction = addTransactionParametersAndSign(from, fee, memo, - transactionBuilder); - return new BatchPaymentTransaction(transaction); - } - private void addPaymentOperationToTransaction(Builder transactionBuilder, KeyPair destination, BigDecimal amount) { transactionBuilder.addOperation(new PaymentOperation.Builder(destination, diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManager.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManager.java index 5f9bfc25..35d290bf 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManager.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManager.java @@ -1,5 +1,62 @@ package kin.sdk.internal.events; +import java.util.List; + +import kin.sdk.TransactionId; +import kin.sdk.exception.KinException; +import kin.sdk.queue.PaymentQueue; +import kin.sdk.queue.PendingPayment; +import kin.sdk.transactiondata.BatchPaymentTransaction; + public interface EventsManager { + /** + * set the event listener + * + * @param eventListener the event listener to set + */ + void setEventListener(PaymentQueue.EventListener eventListener); + + /** + * Invoked when a new payment is enqueued + * + * @param payment the pending payment which was created when the payment was enqueued + *

See {@link PendingPayment}

+ */ + void onPaymentEnqueued(PendingPayment payment); + + /** + * Invoked when a Transaction just before the transaction will be send. + * + * @param transaction the transaction to send + * @param payments the list of payments that the transaction consist of. + *

See {@link BatchPaymentTransaction} and {@link PendingPayment} for + * more + * information

+ */ + void onTransactionSend(BatchPaymentTransaction transaction, List payments); + + /** + * Invoked when a Transaction just before the transaction will be send. + * + * @param transaction the transaction to send + * @param payments the list of payments that the transaction consist of. + *

See {@link BatchPaymentTransaction} and {@link PendingPayment} for + * more + * information

+ */ + void onTransactionSendSuccess(TransactionId transactionId, List payments); + + /** + * Invoked when a Transaction just before the transaction will be send. + * + * @param transaction the transaction to send + * @param payments the list of payments that the transaction consist of. + * @param exception the exception the occurred. + *

See {@link BatchPaymentTransaction} and {@link PendingPayment} for + * more + * information

+ */ + void onTransactionSendFailed(List payments, KinException exception); + } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java index f736dfb2..9b4eb5af 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java @@ -1,11 +1,60 @@ package kin.sdk.internal.events; +import java.util.List; + +import kin.sdk.TransactionId; +import kin.sdk.exception.KinException; +import kin.sdk.queue.PaymentQueue; +import kin.sdk.queue.PendingPayment; +import kin.sdk.transactiondata.BatchPaymentTransaction; + public class EventsManagerImpl implements EventsManager { - public EventsManagerImpl(/*PaymentQueue.EventsListener eventListener*/) { + private PaymentQueue.EventListener eventListener; + + public EventsManagerImpl() { + } + + public EventsManagerImpl(PaymentQueue.EventListener eventListener) { + this.eventListener = eventListener; + } + + @Override + public void setEventListener(PaymentQueue.EventListener eventListener) { + // TODO: 2019-09-05 probably implement as a list of them and not one + this.eventListener = eventListener; + } + @Override + public void onPaymentEnqueued(PendingPayment payment) { + if (eventListener != null) { + eventListener.onPaymentEnqueued(payment); + } } - // TODO: 2019-08-14 implement + @Override + public void onTransactionSend(BatchPaymentTransaction transaction, + List payments) { + if (eventListener != null) { + eventListener.onTransactionSend(transaction, payments); + } + } + + @Override + public void onTransactionSendSuccess(TransactionId transactionId, + List payments) { + if (eventListener != null) { + eventListener.onTransactionSendSuccess(transactionId, payments); + } + } + + @Override + public void onTransactionSendFailed(List payments, KinException exception) { + if (eventListener != null) { + eventListener.onTransactionSendFailed(payments, exception); + } + } + + } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java index eda7104a..b23522a4 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java @@ -5,7 +5,6 @@ import java.math.BigDecimal; import kin.base.KeyPair; -import kin.sdk.TransactionId; import kin.sdk.TransactionInterceptor; import kin.sdk.exception.InsufficientKinException; import kin.sdk.internal.Utils; @@ -15,17 +14,13 @@ import kin.sdk.internal.events.EventsManager; import kin.sdk.internal.events.EventsManagerImpl; import kin.sdk.queue.PaymentQueue; +import kin.sdk.queue.PaymentQueueTransactionProcess; import kin.sdk.queue.PendingPayment; +import kin.sdk.queue.TransactionProcess; import kin.sdk.transactiondata.TransactionParams; public class PaymentQueueImpl implements PaymentQueue { - private static final long DELAY_BETWEEN_PAYMENTS_MILLIS = 3000; // TODO: 2019-08-15 get those - // number from somewhere - private static final long QUEUE_TIMEOUT_MILLIS = 10000; // TODO: 2019-08-15 get those - // number from somewhere - private static final int MAX_NUM_OF_PAYMENTS = 100; - private final KeyPair accountFrom; private final PaymentQueueManager paymentQueueManager; private final TransactionTasksQueueManager txTasksQueueManager; @@ -33,17 +28,17 @@ public class PaymentQueueImpl implements PaymentQueue { private final TasksQueueImpl tasksQueue; public PaymentQueueImpl(@NonNull KeyPair accountFrom, TransactionSender transactionSender, - GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever) { + GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever, + PaymentQueueConfiguration configuration) { this.accountFrom = accountFrom; this.eventsManager = new EventsManagerImpl(); tasksQueue = new TasksQueueImpl(transactionSender, eventsManager, generalBlockchainInfoRetriever, accountFrom); this.txTasksQueueManager = new TransactionTasksQueueManagerImpl(tasksQueue, eventsManager - , MAX_NUM_OF_PAYMENTS); + , configuration.maxNumOfPayments); this.paymentQueueManager = new PaymentQueueManagerImpl(txTasksQueueManager, - new QueueSchedulerImpl(), - new PendingBalanceUpdaterImpl(), eventsManager, - DELAY_BETWEEN_PAYMENTS_MILLIS, QUEUE_TIMEOUT_MILLIS, MAX_NUM_OF_PAYMENTS); + new QueueSchedulerImpl(), new PendingBalanceUpdaterImpl(), eventsManager, + configuration); } @Override @@ -70,30 +65,26 @@ public PendingPayment enqueuePayment(@NonNull String destinationPublicAddress, * @param transactionInterceptor an optional interceptor that will be used to intercept the * transaction. */ - public TransactionId enqueueTransactionParams(TransactionParams transactionParams, - TransactionInterceptor transactionInterceptor) { + public void enqueueTransactionParams(TransactionParams transactionParams, + TransactionInterceptor transactionInterceptor) { tasksQueue.scheduleTransactionParamsTask(transactionParams, transactionInterceptor); - return null; - // TODO: 2019-09-02 because we want it to be sync then we should handle it here and - // return the transaction id } @Override - public void setTransactionInterceptor(TransactionInterceptor transactionInterceptor) { - tasksQueue.setPendingPaymentsTransactionInterceptor(transactionInterceptor); + public void setTransactionInterceptor(TransactionInterceptor transactionInterceptor) { + tasksQueue.setPaymentQueueTransactionInterceptor(transactionInterceptor); } @Override - public void addEventsListener(EventsListener eventListener) { - // TODO: 2019-08-27 did we agree that we call it add? meaning we will maintain a list of - // them in the events manager? + public void setEventListener(EventListener eventListener) { + eventsManager.setEventListener(eventListener); } @Override public void setFee(int fee) { Utils.checkForNegativeFee(fee); - tasksQueue.setFee(fee); + tasksQueue.setBatchPaymentsFee(fee); } @Override @@ -106,4 +97,35 @@ public int pendingPaymentsCount() { return paymentQueueManager.getPendingPaymentCount(); } + @Override + public void releaseQueue() { + tasksQueue.stopTaskQueue(); + } + + public static class PaymentQueueConfiguration { + + private long delayBetweenPaymentsMillis; // TODO: 2019-08-15 get this number from somewhere + private long queueTimeoutMillis; // TODO: 2019-08-15 get this number from somewhere + private int maxNumOfPayments; // TODO: 2019-08-15 get this number from somewhere + + public PaymentQueueConfiguration(long delayBetweenPaymentsMillis, long queueTimeoutMillis, + int maxNumOfPayments) { + this.delayBetweenPaymentsMillis = delayBetweenPaymentsMillis; + this.queueTimeoutMillis = queueTimeoutMillis; + this.maxNumOfPayments = maxNumOfPayments; + } + + long getDelayBetweenPayments() { + return delayBetweenPaymentsMillis; + } + + long getQueueTimeout() { + return queueTimeoutMillis; + } + + int getMaxNumOfPayments() { + return maxNumOfPayments; + } + } + } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java index ebe66bb5..05633ede 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java @@ -15,24 +15,20 @@ class PaymentQueueManagerImpl implements PaymentQueueManager { private final QueueScheduler queueScheduler; private final PendingBalanceUpdater pendingBalanceUpdater; private final EventsManager eventsManager; - private final long delayBetweenPayments; - private final long queueTimeout; - private final int maxNumOfPayments; + private final PaymentQueueImpl.PaymentQueueConfiguration configuration; private List queue; private Runnable dequeueTask; private Runnable timeoutTask; PaymentQueueManagerImpl(TransactionTasksQueueManager txTasksQueueManager, QueueScheduler queueScheduler, PendingBalanceUpdater pendingBalanceUpdater, EventsManager eventsManager, - long delayBetweenPayments, long queueTimeout, int maxNumOfPayments) { + PaymentQueueImpl.PaymentQueueConfiguration configuration) { this.txTasksQueueManager = txTasksQueueManager; this.queueScheduler = queueScheduler; this.pendingBalanceUpdater = pendingBalanceUpdater; this.eventsManager = eventsManager; - this.delayBetweenPayments = delayBetweenPayments; - this.queueTimeout = queueTimeout; - this.maxNumOfPayments = maxNumOfPayments; - queue = new ArrayList<>(maxNumOfPayments); + this.configuration = configuration; + queue = new ArrayList<>(configuration.getMaxNumOfPayments()); dequeueTask = new DequeueTask(); } @@ -44,7 +40,7 @@ public void enqueue(final PendingPayment pendingPayment) { public void run() { validatePendingBalance(); // TODO: 2019-08-15 handle it - if (getPendingPaymentCount() == maxNumOfPayments - 1) { + if (getPendingPaymentCount() == configuration.getMaxNumOfPayments() - 1) { addToQueue(pendingPayment); sendPendingPaymentsToTaskQueue(); } else { @@ -57,19 +53,21 @@ private void handlePendingPayment() { if (queue.size() == 1) { queueScheduler.removePendingTask(timeoutTask); timeoutTask = new DequeueTask(); - queueScheduler.scheduleDelayed(timeoutTask, queueTimeout); + queueScheduler.scheduleDelayed(timeoutTask, configuration.getQueueTimeout()); } resetScheduler(); } private void addToQueue(PendingPayment pendingPayment) { queue.add(pendingPayment); + eventsManager.onPaymentEnqueued(pendingPayment); } private void resetScheduler() { queueScheduler.removePendingTask(dequeueTask); dequeueTask = new DequeueTask(); - queueScheduler.scheduleDelayed(dequeueTask, delayBetweenPayments); + queueScheduler.scheduleDelayed(dequeueTask, + configuration.getDelayBetweenPayments()); } }); } @@ -94,7 +92,7 @@ private void validatePendingBalance() { private void sendPendingPaymentsToTaskQueue() { queueScheduler.removeAllPendingTasks(); List pendingPayments = queue; - queue = new ArrayList<>(maxNumOfPayments); + queue = new ArrayList<>(configuration.getMaxNumOfPayments()); txTasksQueueManager.enqueue(pendingPayments); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java index aebd2425..471acd22 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java @@ -5,20 +5,20 @@ import kin.base.KeyPair; import kin.sdk.TransactionId; import kin.sdk.TransactionInterceptor; +import kin.sdk.exception.KinException; import kin.sdk.exception.OperationFailedException; import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetriever; import kin.sdk.internal.blockchain.TransactionSender; import kin.sdk.internal.events.EventsManager; -import kin.sdk.queue.BatchPaymentTransactionProcessImpl; +import kin.sdk.queue.PaymentQueueTransactionProcess; import kin.sdk.queue.PendingPayment; -import kin.sdk.queue.TransactionProcess; import kin.sdk.transactiondata.BatchPaymentTransaction; class SendPendingPaymentsTask extends SendTransactionTask { private final List pendingPayments; private final TransactionSender transactionSender; - private final TransactionInterceptor transactionInterceptor; + private final TransactionInterceptor transactionInterceptor; private final EventsManager eventsManager; private final GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever; private int fee; @@ -26,7 +26,7 @@ class SendPendingPaymentsTask extends SendTransactionTask { SendPendingPaymentsTask(List pendingPayments, TransactionSender transactionSender, - TransactionInterceptor transactionInterceptor, + TransactionInterceptor transactionInterceptor, TaskFinishListener taskFinishListener, EventsManager eventsManager, GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever, @@ -49,18 +49,19 @@ public void run() { @Override void invokeInterceptor() { - TransactionProcess transactionProcess = - new BatchPaymentTransactionProcessImpl(transactionSender, pendingPayments, - accountFrom, fee, eventsManager); - // TODO: 2019-09-01 if we give them the TransactionProcess then they wont have access to - // the pending payments... need to be changed + PaymentQueueTransactionProcess transactionProcess = + new PaymentQueueTransactionProcess(transactionSender, pendingPayments, + accountFrom, fee); try { TransactionId transactionId = transactionInterceptor.interceptTransactionSending(transactionProcess); handleTransactionFinished(transactionId); } catch (Exception e) { - // TODO: 2019-09-03 if it is not kinsdkexception then wrap it with kinsdkexception - // and send it with events manager + if (!(e instanceof KinException)) { + e = new OperationFailedException(e); + } + + //TODO send it with events manager } } @@ -68,10 +69,12 @@ void invokeInterceptor() { void handleTransactionFinished(TransactionId transactionId) { super.handleTransactionFinished(transactionId); if (transactionId != null) { - // TODO: 2019-09-02 need to update pending payments with their status?... - + eventsManager.onTransactionSendSuccess(transactionId, pendingPayments); // TODO: 2019-09-01 invoke events for transaction success } else { + //TODO need to create a meaningful message for this scenario + eventsManager.onTransactionSendFailed(pendingPayments, new OperationFailedException( + "Transaction Id is null")); // TODO: 2019-09-01 invoke events for transaction fail } } @@ -79,8 +82,9 @@ void handleTransactionFinished(TransactionId transactionId) { @Override void buildAndSendTransaction() throws OperationFailedException { BatchPaymentTransaction transaction = - transactionSender.buildBatchPaymentTransaction(accountFrom, - pendingPayments, fee, null); + transactionSender.buildBatchPaymentTransaction(accountFrom, pendingPayments, fee, + null); + eventsManager.onTransactionSend(transaction, pendingPayments); sendTransaction(transaction); } @@ -90,7 +94,9 @@ private void setFeeIfNeeded() { try { fee = (int) generalBlockchainInfoRetriever.getMinimumFeeSync(); } catch (OperationFailedException e) { - // TODO: 2019-08-27 need to maybe add an exception to the events manager or something... + e.printStackTrace(); + // TODO: 2019-08-27 need to maybe add an exception to the events manager or + // something... } } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java index 01c9fd41..61386f3a 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java @@ -3,9 +3,9 @@ import kin.base.KeyPair; import kin.sdk.TransactionId; import kin.sdk.TransactionInterceptor; +import kin.sdk.exception.KinException; import kin.sdk.exception.OperationFailedException; import kin.sdk.internal.blockchain.TransactionSender; -import kin.sdk.internal.events.EventsManager; import kin.sdk.queue.TransactionParamsProcessImpl; import kin.sdk.queue.TransactionProcess; import kin.sdk.transactiondata.PaymentTransactionParams; @@ -16,21 +16,17 @@ class SendTransactionParamsTask extends SendTransactionTask { private final TransactionParams transactionParams; private final TransactionSender transactionSender; - private final TransactionInterceptor transactionInterceptor; - private final EventsManager eventsManager; + private final TransactionInterceptor transactionInterceptor; private final KeyPair accountFrom; SendTransactionParamsTask(TransactionParams transactionParams, TransactionSender transactionSender, - TransactionInterceptor transactionInterceptor, - TaskFinishListener taskFinishListener, - EventsManager eventsManager, - KeyPair accountFrom) { + TransactionInterceptor transactionInterceptor, + TaskFinishListener taskFinishListener, KeyPair accountFrom) { super(transactionSender, transactionInterceptor, taskFinishListener); this.transactionParams = transactionParams; this.transactionSender = transactionSender; this.transactionInterceptor = transactionInterceptor; - this.eventsManager = eventsManager; this.accountFrom = accountFrom; } @@ -38,13 +34,16 @@ class SendTransactionParamsTask extends SendTransactionTask { void invokeInterceptor() { TransactionProcess transactionProcess = new TransactionParamsProcessImpl(transactionSender, transactionParams, - accountFrom, eventsManager); + accountFrom); TransactionId transactionId = null; try { transactionId = transactionInterceptor.interceptTransactionSending(transactionProcess); } catch (Exception e) { - // TODO: 2019-09-03 if it is not kinsdkexception then wrap it with kinsdkexception - // and send it with events manager + if (!(e instanceof KinException)) { + e = new OperationFailedException(e); + } + // TODO: 2019-09-11 because transaction params doesn't have any event listener then + // what should we do here? } handleTransactionFinished(transactionId); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java index a48609ec..522398bf 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java @@ -33,6 +33,7 @@ void handleTransactionFinished(TransactionId transactionId) { } else { // TODO: 2019-09-01 invoke events for transaction fail } + taskFinishListener.onTaskFinish(); } @Override @@ -46,8 +47,8 @@ public void run() { buildAndSendTransaction(); } } catch (Exception e) { - // TODO: 2019-08-26 use events manager and send them the exception if it is not a kin - // exception then create a KinException with the exception + // TODO: 2019-09-11 because transaction params doesn't have any event listener then + // what should we do here? } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TaskFinishListener.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TaskFinishListener.java index 3a2a83a0..1e1a0b31 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TaskFinishListener.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TaskFinishListener.java @@ -4,10 +4,7 @@ public interface TaskFinishListener { /** * Callback for when a task has finished successfully or with failure. - * - * @param task the task itself */ - void onTaskFinish(SendTransactionTask task); // TODO: 2019-08-27 check if we really need to - // give back the task itself + void onTaskFinish(); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java index d57cb33d..64dfccd0 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java @@ -5,7 +5,9 @@ import java.util.List; import kin.sdk.TransactionInterceptor; +import kin.sdk.queue.PaymentQueueTransactionProcess; import kin.sdk.queue.PendingPayment; +import kin.sdk.queue.TransactionProcess; import kin.sdk.transactiondata.TransactionParams; public interface TasksQueue { @@ -35,7 +37,7 @@ public interface TasksQueue { * the transaction. */ void scheduleTransactionParamsTask(TransactionParams paymentTransactionParams, - @Nullable TransactionInterceptor transactionParamsInterceptor); + @Nullable TransactionInterceptor transactionParamsInterceptor); /** * Stop all tasks in the queue. @@ -60,7 +62,7 @@ void scheduleTransactionParamsTask(TransactionParams paymentTransactionParams, * * @param fee the amount of fee(in Quarks) for each payment (1 Quark = 0.00001 KIN). */ - void setFee(int fee); + void setBatchPaymentsFee(int fee); /** * Set a transaction interceptor. @@ -69,5 +71,5 @@ void scheduleTransactionParamsTask(TransactionParams paymentTransactionParams, * payments transactions to set. *

See {@link TransactionInterceptor}

*/ - void setPendingPaymentsTransactionInterceptor(TransactionInterceptor pendingPaymentsTransactionInterceptor); + void setPaymentQueueTransactionInterceptor(TransactionInterceptor pendingPaymentsTransactionInterceptor); } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java index cf4e3363..81f6ba58 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java @@ -11,7 +11,9 @@ import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetriever; import kin.sdk.internal.blockchain.TransactionSender; import kin.sdk.internal.events.EventsManager; +import kin.sdk.queue.PaymentQueueTransactionProcess; import kin.sdk.queue.PendingPayment; +import kin.sdk.queue.TransactionProcess; import kin.sdk.transactiondata.TransactionParams; /** @@ -23,13 +25,13 @@ class TasksQueueImpl extends HandlerThread implements TasksQueue { private static final String nameTag = "TaskQueueHandlerThread"; private final TransactionSender transactionSender; - private TransactionInterceptor pendingPaymentsTransactionInterceptor; + private TransactionInterceptor paymentQueueTransactionInterceptor; private final EventsManager eventsManager; private final GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever; private final KeyPair accountFrom; private TaskFinishListener taskFinishListener; private Handler backgroundHandler; - private int fee = -1; + private int batchPaymentsFee = -1; TasksQueueImpl(@NonNull TransactionSender transactionSender, @NonNull EventsManager eventsManager, @@ -42,8 +44,6 @@ class TasksQueueImpl extends HandlerThread implements TasksQueue { this.accountFrom = accountFrom; start(); - // TODO: 2019-08-21 check the threading priority and what is the difference between this - // and Process.THREAD_PRIORITY_... } @Override @@ -57,34 +57,32 @@ public void schedulePendingPaymentsTask(List batchPendingPayment if (backgroundHandler != null) { SendPendingPaymentsTask sendPendingPaymentsTask = new SendPendingPaymentsTask(batchPendingPayments, transactionSender, - pendingPaymentsTransactionInterceptor, taskFinishListener, - eventsManager, generalBlockchainInfoRetriever, fee, accountFrom); + paymentQueueTransactionInterceptor, taskFinishListener, + eventsManager, generalBlockchainInfoRetriever, batchPaymentsFee, + accountFrom); backgroundHandler.post(sendPendingPaymentsTask); } } @Override public void scheduleTransactionParamsTask(TransactionParams transactionParams, - TransactionInterceptor transactionParamsInterceptor) { + TransactionInterceptor transactionParamsInterceptor) { if (backgroundHandler != null) { SendTransactionParamsTask sendTransactionParamsTask = new SendTransactionParamsTask(transactionParams, transactionSender, - transactionParamsInterceptor, taskFinishListener, eventsManager, - accountFrom); - // TODO: 2019-08-27 although currently we only have one task each time and we are - // dealing with the param at the manager level, - // if in the future something will change in the manager then it will still post it - // at front of queue. if not needed then we can currently just do backgroundHandler - // .post(...) + transactionParamsInterceptor, taskFinishListener, accountFrom); backgroundHandler.postAtFrontOfQueue(sendTransactionParamsTask); } } @Override public void stopTaskQueue() { - // TODO: 2019-08-22 need to call quit from outside when finishing - quit(); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) { + quitSafely(); + } else { + quit(); + } } @Override @@ -93,12 +91,12 @@ public void setTaskFinishListener(TaskFinishListener taskFinishListener) { } @Override - public void setFee(int fee) { - this.fee = fee; + public void setBatchPaymentsFee(int batchPaymentsFee) { + this.batchPaymentsFee = batchPaymentsFee; } @Override - public void setPendingPaymentsTransactionInterceptor(TransactionInterceptor pendingPaymentsTransactionInterceptor) { - this.pendingPaymentsTransactionInterceptor = pendingPaymentsTransactionInterceptor; + public void setPaymentQueueTransactionInterceptor(TransactionInterceptor pendingPaymentsTransactionInterceptor) { + this.paymentQueueTransactionInterceptor = pendingPaymentsTransactionInterceptor; } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java index f043f2b2..9284f154 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java @@ -4,6 +4,7 @@ import kin.sdk.TransactionInterceptor; import kin.sdk.queue.PendingPayment; +import kin.sdk.queue.TransactionProcess; import kin.sdk.transactiondata.PaymentTransactionParams; public interface TransactionTasksQueueManager { @@ -24,7 +25,7 @@ public interface TransactionTasksQueueManager { * * the transaction. */ void enqueue(PaymentTransactionParams transactionParams, - TransactionInterceptor transactionInterceptor); + TransactionInterceptor transactionInterceptor); /** * @return true if currently there is a transaction in progress, meaning it will return true if diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java index 48e2b837..8ab6389a 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java @@ -1,5 +1,7 @@ package kin.sdk.internal.queue; +import android.util.Log; + import java.util.ArrayDeque; import java.util.List; import java.util.Queue; @@ -7,19 +9,22 @@ import kin.sdk.TransactionInterceptor; import kin.sdk.internal.events.EventsManager; import kin.sdk.queue.PendingPayment; +import kin.sdk.queue.TransactionProcess; import kin.sdk.transactiondata.PaymentTransactionParams; /** - * Class which manage the transaction task queue. + * Class which manages the transaction task queue. */ class TransactionTasksQueueManagerImpl implements TransactionTasksQueueManager, TaskFinishListener { + private static final String TAG = TransactionTasksQueueManagerImpl.class.getSimpleName(); + private final TasksQueue taskQueue; - private final EventsManager eventsManager; + private final EventsManager eventsManager; // TODO: 2019-09-09 verify if it is necessary here private final int maxNumOfPayments; private Queue> batchPendingPaymentsQueue; private PaymentTransactionParams currentTransactionParams; - private TransactionInterceptor currentTransactionParamsInterceptor; + private TransactionInterceptor currentTransactionParamsInterceptor; private boolean transactionInProgress = false; TransactionTasksQueueManagerImpl(TasksQueue taskQueue, EventsManager eventsManager, @@ -39,9 +44,9 @@ public synchronized void enqueue(List pendingPayments) { @Override public synchronized void enqueue(PaymentTransactionParams paymentTransactionParams, - TransactionInterceptor transactionInterceptor) { + TransactionInterceptor transactionInterceptor) { if (currentTransactionParams != null) { - // TODO: 2019-08-26 throw some exception, will decide later + // TODO: 2019-08-26 throw some exception or use event manager } else { currentTransactionParams = paymentTransactionParams; currentTransactionParamsInterceptor = transactionInterceptor; @@ -55,7 +60,7 @@ public synchronized boolean transactionInProgress() { } @Override - public synchronized void onTaskFinish(final SendTransactionTask task) { + public synchronized void onTaskFinish() { transactionInProgress = false; sendTask(); } @@ -73,14 +78,14 @@ public synchronized void onTaskFinish(final SendTransactionTask task) { */ private void mergePayments(List pendingPayments) { // the current number of pending payments that need to be batched - int numOfRestOfPendingPaymentsToBatch = pendingPayments.size(); + int numOfPendingPaymentsToBatch = pendingPayments.size(); // the index in the pending payments list in which to start take elements to next batch // payments list. int pendingPaymentIndexToTakeFrom = 0; for (List batchPayments : batchPendingPaymentsQueue) { - // the current number of pending payment the can be entered to the current batch - // payment list + // the current number of pending payments that can be entered to the current batch + // payments list int numOfEntriesAvailableInBatchList = Math.abs(maxNumOfPayments - batchPayments.size()); if (numOfEntriesAvailableInBatchList == 0) { @@ -90,24 +95,24 @@ private void mergePayments(List pendingPayments) { // If there are more pending payments then the number of payments to fill then take // a sublist of them and fill and take the rest and fill the next batch payments, // otherwise just add all of them to the current batch payments - if (numOfEntriesAvailableInBatchList >= numOfRestOfPendingPaymentsToBatch) { + if (numOfEntriesAvailableInBatchList >= numOfPendingPaymentsToBatch) { batchPayments.addAll(pendingPayments); return; } else { // update the rest of pending payments that still need to be batched. - numOfRestOfPendingPaymentsToBatch = -numOfEntriesAvailableInBatchList; + numOfPendingPaymentsToBatch -= numOfEntriesAvailableInBatchList; List subPendingPayments = pendingPayments.subList(pendingPaymentIndexToTakeFrom, pendingPaymentIndexToTakeFrom + numOfEntriesAvailableInBatchList); // update the index - pendingPaymentIndexToTakeFrom = +numOfEntriesAvailableInBatchList; + pendingPaymentIndexToTakeFrom += numOfEntriesAvailableInBatchList; batchPayments.addAll(subPendingPayments); } } } - // add to the queue in case that there are no batchPayments + // add to the queue batchPendingPaymentsQueue.add(pendingPayments.subList(pendingPaymentIndexToTakeFrom, - pendingPaymentIndexToTakeFrom + numOfRestOfPendingPaymentsToBatch)); + pendingPaymentIndexToTakeFrom + numOfPendingPaymentsToBatch)); } /** @@ -119,25 +124,31 @@ private void mergePayments(List pendingPayments) { private void sendTask() { // Check if there is a task running and if not then schedule a task if (!transactionInProgress()) { + if (currentTransactionParams != null) { sendTransactionParamsTask(); } else if (batchPendingPaymentsQueue.size() > 0) { sendPendingPaymentsTask(); } + } else { + Log.d(TAG, "sendTask: can't send task because there is a transaction in progress"); + // TODO: 2019-09-08 should we add logs here or something? probably we should } } private void sendTransactionParamsTask() { + transactionInProgress = true; PaymentTransactionParams toSend = currentTransactionParams; currentTransactionParams = null; - TransactionInterceptor transactionInterceptor = currentTransactionParamsInterceptor; + TransactionInterceptor transactionInterceptor = + currentTransactionParamsInterceptor; currentTransactionParamsInterceptor = null; taskQueue.scheduleTransactionParamsTask(toSend, transactionInterceptor); - transactionInProgress = true; } private void sendPendingPaymentsTask() { - taskQueue.schedulePendingPaymentsTask(batchPendingPaymentsQueue.poll()); transactionInProgress = true; + taskQueue.schedulePendingPaymentsTask(batchPendingPaymentsQueue.poll()); } + } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcess.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcess.java deleted file mode 100644 index 381565c6..00000000 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcess.java +++ /dev/null @@ -1,24 +0,0 @@ -package kin.sdk.queue; - -import android.support.annotation.Nullable; - -import java.util.List; - -import kin.sdk.internal.blockchain.TransactionSender; -import kin.sdk.internal.events.EventsManager; - -abstract class BatchPaymentTransactionProcess extends TransactionProcess { - - BatchPaymentTransactionProcess(TransactionSender transactionSender, - EventsManager eventsManager) { - super(transactionSender, eventsManager); - } - - /** - * @return the list of pending payment that the current transaction consist of. - * Can be null or empty. - */ - @Nullable - public abstract List payments(); - -} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcessImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcessImpl.java deleted file mode 100644 index dac8b1af..00000000 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/BatchPaymentTransactionProcessImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package kin.sdk.queue; - -import java.util.List; - -import kin.base.KeyPair; -import kin.sdk.exception.OperationFailedException; -import kin.sdk.internal.blockchain.TransactionSender; -import kin.sdk.internal.events.EventsManager; -import kin.sdk.transactiondata.Transaction; - -public class BatchPaymentTransactionProcessImpl extends BatchPaymentTransactionProcess { - - private final TransactionSender transactionSender; - private final List pendingPayments; - private final KeyPair accountFrom; - private final int fee; - - public BatchPaymentTransactionProcessImpl(TransactionSender transactionSender, - List pendingPayments, - KeyPair accountFrom, - int fee, EventsManager eventsManager) { - super(transactionSender, eventsManager); - this.transactionSender = transactionSender; - this.pendingPayments = pendingPayments; - this.accountFrom = accountFrom; - this.fee = fee; - } - - @Override - public List payments() { - return pendingPayments; - } - - @Override - protected Transaction buildTransaction(String memo) throws OperationFailedException { - return transactionSender.buildBatchPaymentTransaction(accountFrom, pendingPayments, fee, - memo); - } -} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PaymentQueue.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PaymentQueue.java index 718b729c..fa2dbed5 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PaymentQueue.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PaymentQueue.java @@ -5,6 +5,7 @@ import java.math.BigDecimal; import java.util.List; +import kin.sdk.TransactionId; import kin.sdk.TransactionInterceptor; import kin.sdk.exception.InsufficientKinException; import kin.sdk.exception.KinException; @@ -15,7 +16,7 @@ public interface PaymentQueue { /** * Register/unregister for queue events. */ - interface EventsListener { + interface EventListener { /** * Invoked when a new payment is enqueued @@ -43,7 +44,7 @@ interface EventsListener { *

See {@link BatchPaymentTransaction} and {@link PendingPayment} for more * information

*/ - void onTransactionSendSuccess(BatchPaymentTransaction transaction, List payments); + void onTransactionSendSuccess(TransactionId transactionId, List payments); /** * Invoked when a Transaction just before the transaction will be send. @@ -54,8 +55,7 @@ interface EventsListener { *

See {@link BatchPaymentTransaction} and {@link PendingPayment} for more * information

*/ - void onTransactionSendFailed(BatchPaymentTransaction transaction, List payments, - KinException exception); + void onTransactionSendFailed(List payments, KinException exception); } /** @@ -89,12 +89,12 @@ void onTransactionSendFailed(BatchPaymentTransaction transaction, List See {@link TransactionInterceptor}

*/ - void setTransactionInterceptor(TransactionInterceptor interceptor); + void setTransactionInterceptor(TransactionInterceptor interceptor); /** * @param listener is a queue event listener. */ - void addEventsListener(EventsListener listener); + void setEventListener(EventListener listener); /** * Setter for the fee. @@ -114,4 +114,14 @@ void onTransactionSendFailed(BatchPaymentTransaction transaction, List pendingPayments; + private final KeyPair accountFrom; + private final int fee; + + public PaymentQueueTransactionProcess(TransactionSender transactionSender, + List pendingPayments, + KeyPair accountFrom, int fee) { + super(transactionSender); + this.transactionSender = transactionSender; + this.pendingPayments = pendingPayments; + this.accountFrom = accountFrom; + this.fee = fee; + } + + /** + * @return the list of pending payment that the current transaction consist of. + * Can be null or empty. + */ + public List payments() { + return pendingPayments; + } + + @Override + public BatchPaymentTransaction transaction() throws OperationFailedException { + return transaction(null); + } + + /** + * @return a transaction that consist of the list of pending payments. + * Also add a memo to that transaction + */ + + public BatchPaymentTransaction transaction(String memo) throws OperationFailedException { + return buildTransaction(memo); + } + + /** + * Build the transaction + * + * @param memo the memo that should be added to the transaction + * @return a new created transaction. + * @throws OperationFailedException in case it couldn't be build. + */ + private BatchPaymentTransaction buildTransaction(String memo) throws OperationFailedException { + return transactionSender.buildBatchPaymentTransaction(accountFrom, pendingPayments, fee, + memo); + } +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java index a3bcfd20..c17fbc51 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java @@ -6,8 +6,6 @@ public interface PendingPayment { - // TODO: 2019-08-14 namings??? - /** * Destination account public address. */ diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionParamsProcessImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionParamsProcessImpl.java index e1f04914..5af51bd4 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionParamsProcessImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionParamsProcessImpl.java @@ -3,7 +3,6 @@ import kin.base.KeyPair; import kin.sdk.exception.OperationFailedException; import kin.sdk.internal.blockchain.TransactionSender; -import kin.sdk.internal.events.EventsManager; import kin.sdk.transactiondata.PaymentTransactionParams; import kin.sdk.transactiondata.Transaction; import kin.sdk.transactiondata.TransactionParams; @@ -14,20 +13,26 @@ public class TransactionParamsProcessImpl extends TransactionProcess { private final TransactionParams transactionParams; private final KeyPair accountFrom; - // TODO: 2019-08-04 implement and add java docs public TransactionParamsProcessImpl(TransactionSender transactionSender, - TransactionParams transactionParams, KeyPair accountFrom, - EventsManager eventsManager) { - super(transactionSender, eventsManager); + TransactionParams transactionParams, KeyPair accountFrom) { + super(transactionSender); this.transactionSender = transactionSender; this.transactionParams = transactionParams; this.accountFrom = accountFrom; } @Override - Transaction buildTransaction(String memo) throws OperationFailedException { - // TODO: 2019-08-28 which memo should we use? probably the last one, which is the one - // they set here, no? only if it is null or empty use the first one? we need to decide! + public Transaction transaction() throws OperationFailedException { + return buildTransaction(); + } + + /** + * Build the transaction + * + * @return a new created transaction. + * @throws OperationFailedException in case it couldn't be build. + */ + private Transaction buildTransaction() throws OperationFailedException { if (transactionParams instanceof PaymentTransactionParams) { PaymentTransactionParams paymentTransactionParams = (PaymentTransactionParams) transactionParams; diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionProcess.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionProcess.java index bcb23d2d..32693fc8 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionProcess.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionProcess.java @@ -1,11 +1,8 @@ package kin.sdk.queue; -import android.support.annotation.Nullable; - import kin.sdk.TransactionId; import kin.sdk.exception.OperationFailedException; import kin.sdk.internal.blockchain.TransactionSender; -import kin.sdk.internal.events.EventsManager; import kin.sdk.transactiondata.Transaction; /** @@ -15,36 +12,15 @@ public abstract class TransactionProcess { private final TransactionSender transactionSender; - private final EventsManager eventsManager; - TransactionProcess(TransactionSender transactionSender, EventsManager eventsManager) { + TransactionProcess(TransactionSender transactionSender) { this.transactionSender = transactionSender; - this.eventsManager = eventsManager; } - /** - * Build the transaction - * - * @param memo the memo that should be added to the transaction - * @return a new created transaction. - * @throws OperationFailedException in case it couldn't be build. - */ - abstract Transaction buildTransaction(@Nullable String memo) throws OperationFailedException; - /** * @return a transaction that consist of the list of pending payments. */ - public Transaction transaction() throws OperationFailedException { - return buildTransaction(null); - } - - /** - * @return a transaction that consist of the list of pending payments. - * Also add a memo to that transaction - */ - public Transaction transaction(String memo) throws OperationFailedException { - return buildTransaction(memo); - } + public abstract Transaction transaction() throws OperationFailedException; /** * Send the transaction with a Transaction object diff --git a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt index 9f38d3b3..dcb7c720 100644 --- a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt +++ b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt @@ -7,7 +7,7 @@ import java.util.concurrent.TimeUnit open class FakeQueueScheduler : QueueScheduler { - var scheduler: ScheduledThreadPoolExecutor = ScheduledThreadPoolExecutor(1) + private var scheduler: ScheduledThreadPoolExecutor = ScheduledThreadPoolExecutor(1) private val futureTasks: HashMap?> = HashMap() val numOfTasks: Int get() = futureTasks.size @@ -25,19 +25,12 @@ open class FakeQueueScheduler : QueueScheduler { // killing all future futureTasks not including the current running task if there is one. scheduler.shutdown() scheduler = ScheduledThreadPoolExecutor(1) - println(futureTasks.size) futureTasks.clear() - println(futureTasks.size) - println() } override fun removePendingTask(runnable: Runnable?) { - println(futureTasks.size) val removedRunnable = futureTasks.remove(runnable) - println(removedRunnable) - println(futureTasks.size) removedRunnable?.cancel(false) - println() } override fun schedule(runnable: Runnable?) { diff --git a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt index f090678d..18e0c574 100644 --- a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt +++ b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt @@ -40,8 +40,10 @@ class PaymentQueueManagerTest { private fun initPaymentQueueManager() { destinationAccount = KeyPair.random().accountId sourceAccount = KeyPair.random().accountId - paymentQueueManager = PaymentQueueManagerImpl(txTaskQueueManager, queueScheduler, pendingBalanceUpdater, eventsManager, - Constants.DELAY_BETWEEN_PAYMENTS_MILLIS, Constants.QUEUE_TIMEOUT_MILLIS, Constants.MAX_NUM_OF_PAYMENTS) + val configuration = PaymentQueueImpl.PaymentQueueConfiguration(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS, + Constants.QUEUE_TIMEOUT_MILLIS, Constants.MAX_NUM_OF_PAYMENTS) + paymentQueueManager = PaymentQueueManagerImpl(txTaskQueueManager, queueScheduler, + pendingBalanceUpdater, eventsManager, configuration) } @Test From 9c075c59b30426ea3d35a40cda0080052bdc3b53 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Wed, 11 Sep 2019 13:57:36 +0300 Subject: [PATCH 5/5] Add todo comment regarding the event manager --- .../main/java/kin/sdk/internal/events/EventsManagerImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java index 9b4eb5af..4f5774c2 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java @@ -12,6 +12,10 @@ public class EventsManagerImpl implements EventsManager { private PaymentQueue.EventListener eventListener; + // TODO: 2019-09-11 if we should use the main thread in the callback then we will have a + // problem because we also use this event manager for our needs and not necessarily want to + // run on main thread + public EventsManagerImpl() { }