From d11fd6306d1ba00e58aca5ae831fbf819a089ffb Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 15 Aug 2019 11:32:30 +0300 Subject: [PATCH 01/12] 1. Add TransactionInternal in order to encapsulate baseTransaction from the outside world 2. Add all necessary classes, methods, and stubs for the "PaymentsQueueManager" Jira Task 3. --- .../src/main/java/kin/sdk/PaymentInfo.java | 1 + .../sdk/internal/account/KinAccountImpl.java | 1 - .../blockchain/TransactionInternal.java | 18 +++ .../blockchain/TransactionSender.java | 2 +- .../internal/data/PaymentOperationImpl.java | 34 ++++++ .../internal/data/PendingBalanceUpdater.java | 9 ++ .../data/PendingBalanceUpdaterImpl.java | 13 ++ .../sdk/internal/events/EventsManager.java | 5 + .../internal/events/EventsManagerImpl.java | 7 ++ .../sdk/internal/queue/PaymentQueueImpl.java | 48 +++++--- .../internal/queue/PaymentQueueManager.java | 28 +++++ .../queue/PaymentQueueManagerImpl.java | 113 ++++++++++++++++++ .../internal/queue/PendingPaymentImpl.java | 37 ++++-- .../sdk/internal/queue/QueueScheduler.java | 28 +++++ .../internal/queue/QueueSchedulerImpl.java | 36 ++++++ .../queue/TransactionProcessImpl.java | 2 +- .../queue/TransactionTaskQueueManager.java | 15 +++ .../TransactionTaskQueueManagerImpl.java | 19 +++ .../main/java/kin/sdk/queue/PaymentQueue.java | 25 ++-- .../java/kin/sdk/queue/PendingPayment.java | 18 ++- .../BatchPaymentTransaction.java | 50 ++++++-- .../transactiondata/PaymentTransaction.java | 4 +- .../sdk/transactiondata/RawTransaction.java | 7 +- .../kin/sdk/transactiondata/Transaction.java | 6 +- 24 files changed, 453 insertions(+), 73 deletions(-) create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionInternal.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PaymentOperationImpl.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PendingBalanceUpdater.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PendingBalanceUpdaterImpl.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManager.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManager.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueScheduler.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueSchedulerImpl.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManager.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManagerImpl.java diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentInfo.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentInfo.java index 4a95ff57..b139d877 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentInfo.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentInfo.java @@ -13,6 +13,7 @@ public interface PaymentInfo { */ String createdAt(); + // TODO: 2019-08-14 namings??? /** * Destination account public id. */ 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 22427285..9931479e 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 @@ -16,7 +16,6 @@ import java.math.BigDecimal; - public final class KinAccountImpl extends AbstractKinAccount { private final KeyPair account; diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionInternal.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionInternal.java new file mode 100644 index 00000000..b5c092a9 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionInternal.java @@ -0,0 +1,18 @@ +package kin.sdk.internal.blockchain; + +import kin.sdk.transactiondata.Transaction; + +public class TransactionInternal extends Transaction { + + private final kin.base.Transaction baseTransaction; + + public TransactionInternal(kin.base.Transaction baseTransaction) { + super(baseTransaction); + this.baseTransaction = baseTransaction; + } + + kin.base.Transaction baseTransaction() { + return baseTransaction; + } + +} 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 8213645b..9c007474 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 @@ -59,7 +59,7 @@ public PaymentTransaction buildTransaction(@NonNull KeyPair from, @NonNull Strin } public TransactionId sendTransaction(Transaction transaction) throws OperationFailedException { - return sendTransaction(transaction.baseTransaction()); + return sendTransaction(((TransactionInternal) transaction).baseTransaction()); } public TransactionId sendWhitelistTransaction(String whitelist) throws OperationFailedException { diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PaymentOperationImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PaymentOperationImpl.java new file mode 100644 index 00000000..9e08b7a0 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PaymentOperationImpl.java @@ -0,0 +1,34 @@ +package kin.sdk.internal.data; + +import kin.sdk.transactiondata.BatchPaymentTransaction; + +import java.math.BigDecimal; + +public class PaymentOperationImpl implements BatchPaymentTransaction.PaymentOperation { + + private final String destinationPublicKey; + private final String sourcePublicKey; + private final BigDecimal amount; + + public PaymentOperationImpl(String destinationPublicKey, String sourcePublicKey, BigDecimal amount) { + // TODO: 2019-08-15 namings? + this.destinationPublicKey = destinationPublicKey; + this.sourcePublicKey = sourcePublicKey; + this.amount = amount; + } + + @Override + public String destination() { + return null; + } + + @Override + public String source() { + return null; + } + + @Override + public BigDecimal amount() { + return null; + } +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PendingBalanceUpdater.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PendingBalanceUpdater.java new file mode 100644 index 00000000..2857f6aa --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PendingBalanceUpdater.java @@ -0,0 +1,9 @@ +package kin.sdk.internal.data; + +import kin.sdk.Balance; + +public interface PendingBalanceUpdater { + + // TODO: 2019-08-08 this should be async? it should probably take it from the blockchain... + Balance getPendingBalance(); +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PendingBalanceUpdaterImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PendingBalanceUpdaterImpl.java new file mode 100644 index 00000000..63c0ecac --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PendingBalanceUpdaterImpl.java @@ -0,0 +1,13 @@ +package kin.sdk.internal.data; + +import kin.sdk.Balance; + +public class PendingBalanceUpdaterImpl implements PendingBalanceUpdater { + + // TODO: 2019-08-14 implement + + @Override + public Balance getPendingBalance() { + return null; + } +} 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 new file mode 100644 index 00000000..5f9bfc25 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManager.java @@ -0,0 +1,5 @@ +package kin.sdk.internal.events; + +public interface EventsManager { + +} 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 new file mode 100644 index 00000000..4ed0c809 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java @@ -0,0 +1,7 @@ +package kin.sdk.internal.events; + +public class EventsManagerImpl implements EventsManager { + + // 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 1d11c48d..9313beda 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 @@ -3,6 +3,8 @@ import android.support.annotation.NonNull; import kin.sdk.TransactionInterceptor; import kin.sdk.exception.InsufficientKinException; +import kin.sdk.internal.data.PendingBalanceUpdaterImpl; +import kin.sdk.internal.events.EventsManagerImpl; import kin.sdk.queue.PaymentQueue; import kin.sdk.queue.PendingPayment; @@ -10,49 +12,57 @@ public class PaymentQueueImpl implements PaymentQueue { - // TODO: 2019-08-04 implement + 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 PaymentQueueManager paymentQueueManager; + private final TransactionTaskQueueManager txTaskQueueManager; + + public PaymentQueueImpl(@NonNull String sourcePublicAddress) { + this.sourcePublicAddress = sourcePublicAddress; + this.txTaskQueueManager = new TransactionTaskQueueManagerImpl(); + this.paymentQueueManager = new PaymentQueueManagerImpl(txTaskQueueManager, new QueueSchedulerImpl(), + new PendingBalanceUpdaterImpl(), new EventsManagerImpl(), + DELAY_BETWEEN_PAYMENTS_MILLIS, QUEUE_TIMEOUT_MILLIS, MAX_NUM_OF_PAYMENTS); + } @Override public PendingPayment enqueuePayment(@NonNull String publicAddress, @NonNull BigDecimal amount) throws InsufficientKinException { - return null; + return enqueuePayment(publicAddress, amount, null); } @Override public PendingPayment enqueuePayment(@NonNull String publicAddress, @NonNull BigDecimal amount, Object metadata) throws InsufficientKinException { - return null; + PendingPayment pendingPayment = new PendingPaymentImpl(publicAddress, sourcePublicAddress, amount, metadata); + paymentQueueManager.enqueue(pendingPayment); + return pendingPayment; } @Override public void setTransactionInterceptor(TransactionInterceptor interceptor) { - + // TODO: 2019-08-15 implement } @Override public void addEventsListener(EventsListener listener) { - + // TODO: 2019-08-15 implement } @Override public void setFee(int fee) { - + // TODO: 2019-08-14 should we keep it here or set it directly into the task queue manager } @Override - public Status status() { - return null; + public boolean transactionInProgress() { + return false; // TODO: 2019-08-15 implement } - public static class StatusImpl implements PaymentQueue.Status { - - @Override - public boolean transactionInProgress() { - return false; - } - - @Override - public int pendingPaymentsCount() { - return 0; - } + @Override + public int pendingPaymentsCount() { + return paymentQueueManager.getPendingPaymentCount(); } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManager.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManager.java new file mode 100644 index 00000000..40c15a0e --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManager.java @@ -0,0 +1,28 @@ +package kin.sdk.internal.queue; + +import kin.sdk.exception.InsufficientKinException; +import kin.sdk.queue.PendingPayment; + +import java.util.List; + +public interface PaymentQueueManager { + + /** + * enqueue this pending payment. + * + * @param pendingPayment the pending payment to enqueue + * @throws InsufficientKinException if currently there is not enough balance to enqueue this pending payment. + */ + void enqueue(PendingPayment pendingPayment) throws InsufficientKinException; + + /** + * @return the list of pending payments that are in the queue. + */ + List getPaymentQueue(); + + /** + * @return the number of pending payments in the queue. + */ + int getPendingPaymentCount(); + +} 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 new file mode 100644 index 00000000..c079a520 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java @@ -0,0 +1,113 @@ +package kin.sdk.internal.queue; + +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 TransactionTaskQueueManager txTaskQueueManager; + 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 List queue; + private Runnable delayTask; + + PaymentQueueManagerImpl(TransactionTaskQueueManager txTaskQueueManager, QueueScheduler queueScheduler, + PendingBalanceUpdater pendingBalanceUpdater, EventsManager eventsManager, + long delayBetweenPayments, long queueTimeout, int maxNumOfPayments) { + this.txTaskQueueManager = txTaskQueueManager; + this.queueScheduler = queueScheduler; + this.pendingBalanceUpdater = pendingBalanceUpdater; + this.eventsManager = eventsManager; + this.delayBetweenPayments = delayBetweenPayments; + this.queueTimeout = queueTimeout; + this.maxNumOfPayments = maxNumOfPayments; + queue = new ArrayList<>(maxNumOfPayments); + delayTask = new DelayTask(); + } + + @Override + public void enqueue(final PendingPayment pendingPayment) { + queueScheduler.schedule(new Runnable() { + @Override + public void run() { + // 1. validate pending balance + validatePendingBalance(); // TODO: 2019-08-15 handle it + + if (getPendingPaymentCount() == maxNumOfPayments - 1) { + // 2. add to queue + // 3. cancel scheduling + // 4. maybe events in later stages + // 5. clear and send to task queue manager + + queue.add(pendingPayment); + // TODO: 2019-08-08 maybe send events here + sendPendingPaymentsToTaskQueue(); + } else { + addToQueue(); + } + } + + private void addToQueue() { + // 1. add to list + // 2. handle events later (maybe also change the execution) + // 3. reset and schedule delay + // 4. schedule timeout if first in list + + queue.add(pendingPayment); + // TODO: 2019-08-08 maybe send events here + queueScheduler.removePendingTask(delayTask); + delayTask = new DelayTask(); + queueScheduler.scheduleDelayed(delayTask, delayBetweenPayments); + if (queue.size() == 1) { + queueScheduler.scheduleDelayed(new DelayTask(), queueTimeout); + } + } + }); + + } + + @Override + public List getPaymentQueue() { + // TODO: 2019-08-15 need to think why exactly do we need it? is it only for the pending balance updater? + // maybe return a copy... + return queue; + } + + @Override + public int getPendingPaymentCount() { + return queue.size(); + } + + private void validatePendingBalance() { + Balance pendingBalance = pendingBalanceUpdater.getPendingBalance(); + // TODO: 2019-08-08 implement check and validate the pending balance + } + + private void sendPendingPaymentsToTaskQueue() { + queueScheduler.removeAllPendingTasks(); + // TODO: 2019-08-08 maybe send events + List pendingPayments = queue; + queue = new ArrayList<>(maxNumOfPayments); + txTaskQueueManager.enqueue(pendingPayments); + } + + private class DelayTask implements Runnable { + + @Override + public void run() { + validatePendingBalance(); + // TODO: 2019-08-08 act upon the validation + sendPendingPaymentsToTaskQueue(); + } + } + +} 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 6afa68b1..60bba63c 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 @@ -6,35 +6,50 @@ public class PendingPaymentImpl implements PendingPayment { - // TODO: 2019-08-04 implement + private final String destinationPublicKey; + private final String sourcePublicKey; + private final BigDecimal amount; + private final Object metadata; + private Status status; + + public PendingPaymentImpl(String destinationPublicKey, String sourcePublicKey, BigDecimal amount) { + this(destinationPublicKey, sourcePublicKey, amount, null); + } + + PendingPaymentImpl(String destinationPublicKey, String sourcePublicKey, BigDecimal amount, Object metadata) { + this.destinationPublicKey = destinationPublicKey; + this.sourcePublicKey = sourcePublicKey; + this.amount = amount; + this.metadata = metadata; + this.status = Status.PENDING; + } @Override public String destinationPublicKey() { - return null; + return destinationPublicKey; } @Override public String sourcePublicKey() { - return null; + return sourcePublicKey; } @Override public BigDecimal amount() { - return null; - } - - @Override - public int operationIndex() { - return 0; + return amount; } @Override public Object metadata() { - return null; + return metadata; } @Override public Status status() { - return null; + return status; + } + + void setStatus(Status status) { + this.status = status; } } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueScheduler.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueScheduler.java new file mode 100644 index 00000000..9ba37469 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueScheduler.java @@ -0,0 +1,28 @@ +package kin.sdk.internal.queue; + +public interface QueueScheduler { + + /** + * schedule a task + */ + void schedule(Runnable runnable); + + /** + * schedule a task to be run after the specified amount of time elapses. + * + * @param delayInMillis The delay (in milliseconds) until the task will be executed + */ + void scheduleDelayed(Runnable runnable, long delayInMillis); + + /** + * Remove all the pending tasks + */ + void removeAllPendingTasks(); + + /** + * Remove a specific pending task + * + * @param task is the task to remove + */ + void removePendingTask(Runnable task); +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueSchedulerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueSchedulerImpl.java new file mode 100644 index 00000000..a64e5a85 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueSchedulerImpl.java @@ -0,0 +1,36 @@ +package kin.sdk.internal.queue; + +import android.os.Handler; +import android.os.Looper; + +class QueueSchedulerImpl implements QueueScheduler { + + private final Handler handler; + + QueueSchedulerImpl() { + handler = new Handler(Looper.getMainLooper()); + } + + @Override + public void schedule(Runnable runnable) { + handler.post(runnable); + } + + @Override + public void scheduleDelayed(Runnable runnable, long delayInMillis) { + handler.postDelayed(runnable, delayInMillis); + } + + @Override + public void removeAllPendingTasks() { + handler.removeCallbacksAndMessages(null); + } + + @Override + public void removePendingTask(Runnable runnable) { + handler.removeCallbacks(runnable); + } +} + + + 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 index 047e11f7..a5fcab5d 100644 --- 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 @@ -9,7 +9,7 @@ public class TransactionProcessImpl implements TransactionProcess { - // TODO: 2019-08-04 implement + // TODO: 2019-08-04 implement and add java docs @Override public Transaction transaction() { 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/TransactionTaskQueueManager.java new file mode 100644 index 00000000..a00bc565 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManager.java @@ -0,0 +1,15 @@ +package kin.sdk.internal.queue; + +import kin.sdk.SendTransactionParams; +import kin.sdk.queue.PendingPayment; + +import java.util.List; + +public interface TransactionTaskQueueManager { + + // TODO: 2019-08-15 add java docs + + void enqueue(List queueToSend); + + void enqueue(SendTransactionParams sendTransactionParams); +} 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/TransactionTaskQueueManagerImpl.java new file mode 100644 index 00000000..c39a324e --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManagerImpl.java @@ -0,0 +1,19 @@ +package kin.sdk.internal.queue; + +import kin.sdk.SendTransactionParams; +import kin.sdk.queue.PendingPayment; + +import java.util.List; + +public class TransactionTaskQueueManagerImpl implements TransactionTaskQueueManager { + + @Override + public void enqueue(List queueToSend) { + + } + + @Override + public void enqueue(SendTransactionParams sendTransactionParams) { + + } +} 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 1bc2f3c4..ae79f585 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 @@ -11,19 +11,6 @@ public interface PaymentQueue { - interface Status { - - /** - * @return true if there is a transaction in process. - */ - boolean transactionInProgress(); - - /** - * @return the number of pending payments in the queue - */ - int pendingPaymentsCount(); - } - /** * Register/unregister for queue events. */ @@ -117,11 +104,13 @@ void onTransactionSendFailed(BatchPaymentTransaction transaction, List payments() { - // TODO: 2019-08-04 implement - return null; + List paymentOperations = new ArrayList<>(); + for (Operation o : baseTransaction.getOperations()) { + if (o instanceof kin.base.PaymentOperation) { + kin.base.PaymentOperation operation = (kin.base.PaymentOperation) o; + PaymentOperation paymentOperation = + new PaymentOperationImpl(operation.getDestination().getAccountId(), + operation.getSourceAccount().getAccountId(), new BigDecimal(operation.getAmount())); + paymentOperations.add(paymentOperation); + } + } + return paymentOperations; } + /** + * @return the memo of this transaction. + * In this case you will get a string representation of a memo as opposed to {@link RawTransaction#memo()} + */ public String memo() { - // TODO: 2019-08-04 implement - return null; + 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 22ba4fdd..4ae3137c 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,8 +1,10 @@ package kin.sdk.transactiondata; +import kin.sdk.internal.blockchain.TransactionInternal; + import java.math.BigDecimal; -public class PaymentTransaction extends Transaction { +public class PaymentTransaction extends TransactionInternal { private final String destination; private final BigDecimal amount; diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/RawTransaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/RawTransaction.java index 0c859fbb..1fbe9bcc 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/RawTransaction.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/transactiondata/RawTransaction.java @@ -5,8 +5,11 @@ public class RawTransaction extends Transaction { + private final kin.base.Transaction baseTransaction; + public RawTransaction(kin.base.Transaction baseTransaction) { super(baseTransaction); + this.baseTransaction = baseTransaction; } /** @@ -26,7 +29,7 @@ public RawTransaction(kin.base.Transaction baseTransaction) { * */ public Memo memo() { - return baseTransaction().getMemo(); + return baseTransaction.getMemo(); } /** @@ -40,7 +43,7 @@ public Memo memo() { * then they require signatures of the accounts in question. */ public Operation[] operations() { - return baseTransaction().getOperations(); + return baseTransaction.getOperations(); } } 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 07acb60c..a54a34f3 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 @@ -71,10 +71,6 @@ public static RawTransaction decodeRawTransaction(String transactionEnvelope) th } } - public kin.base.Transaction baseTransaction() { - return baseTransaction; - } - /** * Each transaction sets a fee that is paid by the source account. * If this fee is below the network minimum the transaction will fail. @@ -183,7 +179,7 @@ public WhitelistPayload whitelistPayload() { * @param account {@link KinAccount} object which is the account who we add his signature. */ public void addSignature(KinAccount account) { - baseTransaction().sign(((KinAccountImpl) account).getKeyPair()); + baseTransaction.sign(((KinAccountImpl) account).getKeyPair()); } } From c5af6044d5986e540f8d25d240cda25aa689a696 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 15 Aug 2019 11:40:02 +0300 Subject: [PATCH 02/12] Replace PublicKey with PublicAddress --- .../src/main/java/kin/sdk/PaymentInfo.java | 2 +- .../internal/data/PaymentOperationImpl.java | 14 +++++------ .../internal/queue/PendingPaymentImpl.java | 23 ++++++++++--------- .../sdk/internal/storage/KeyStoreImpl.java | 6 ++--- .../java/kin/sdk/queue/PendingPayment.java | 8 +++---- .../BatchPaymentTransaction.java | 8 +++---- 6 files changed, 31 insertions(+), 30 deletions(-) diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentInfo.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentInfo.java index b139d877..35116461 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentInfo.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/PaymentInfo.java @@ -13,7 +13,7 @@ public interface PaymentInfo { */ String createdAt(); - // TODO: 2019-08-14 namings??? + // TODO: 2019-08-14 should we change namings??? instead of key use address? /** * Destination account public id. */ diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PaymentOperationImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PaymentOperationImpl.java index 9e08b7a0..b4f86ce7 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PaymentOperationImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/data/PaymentOperationImpl.java @@ -6,24 +6,24 @@ public class PaymentOperationImpl implements BatchPaymentTransaction.PaymentOperation { - private final String destinationPublicKey; - private final String sourcePublicKey; + private final String destinationPublicAddress; + private final String sourcePublicAddress; private final BigDecimal amount; - public PaymentOperationImpl(String destinationPublicKey, String sourcePublicKey, BigDecimal amount) { + public PaymentOperationImpl(String destinationPublicAddress, String sourcePublicAddress, BigDecimal amount) { // TODO: 2019-08-15 namings? - this.destinationPublicKey = destinationPublicKey; - this.sourcePublicKey = sourcePublicKey; + this.destinationPublicAddress = destinationPublicAddress; + this.sourcePublicAddress = sourcePublicAddress; this.amount = amount; } @Override - public String destination() { + public String destinationPublicAddress() { return null; } @Override - public String source() { + public String sourcePublicAddress() { return null; } 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 60bba63c..e8ecb18b 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 @@ -6,32 +6,33 @@ public class PendingPaymentImpl implements PendingPayment { - private final String destinationPublicKey; - private final String sourcePublicKey; + private final String destinationPublicAddress; + private final String sourcePublicAddress; private final BigDecimal amount; private final Object metadata; private Status status; - public PendingPaymentImpl(String destinationPublicKey, String sourcePublicKey, BigDecimal amount) { - this(destinationPublicKey, sourcePublicKey, amount, null); + public PendingPaymentImpl(String destinationPublicAddress, String sourcePublicAddress, BigDecimal amount) { + this(destinationPublicAddress, sourcePublicAddress, amount, null); } - PendingPaymentImpl(String destinationPublicKey, String sourcePublicKey, BigDecimal amount, Object metadata) { - this.destinationPublicKey = destinationPublicKey; - this.sourcePublicKey = sourcePublicKey; + PendingPaymentImpl(String destinationPublicAddress, String sourcePublicAddress, BigDecimal amount, + Object metadata) { + this.destinationPublicAddress = destinationPublicAddress; + this.sourcePublicAddress = sourcePublicAddress; this.amount = amount; this.metadata = metadata; this.status = Status.PENDING; } @Override - public String destinationPublicKey() { - return destinationPublicKey; + public String destinationPublicAddress() { + return destinationPublicAddress; } @Override - public String sourcePublicKey() { - return sourcePublicKey; + public String sourcePublicAddress() { + return sourcePublicAddress; } @Override diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/storage/KeyStoreImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/storage/KeyStoreImpl.java index e00983d0..c078bf82 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/storage/KeyStoreImpl.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/storage/KeyStoreImpl.java @@ -94,10 +94,10 @@ public KeyPair newAccount() throws CreateAccountException { private KeyPair addKeyPairToStorage(KeyPair newKeyPair) throws CreateAccountException { try { String encryptedSeed = String.valueOf(newKeyPair.getSecretSeed()); - String publicKey = newKeyPair.getAccountId(); + String publicAddress = newKeyPair.getAccountId(); String accounts = store.getString(STORE_KEY_ACCOUNTS); - if (TextUtils.isEmpty(accounts) || !accounts.contains(publicKey)) { - JSONObject accountsJson = addKeyPairToAccountsJson(encryptedSeed, publicKey); + if (TextUtils.isEmpty(accounts) || !accounts.contains(publicAddress)) { + JSONObject accountsJson = addKeyPairToAccountsJson(encryptedSeed, publicAddress); store.saveString(STORE_KEY_ACCOUNTS, accountsJson.toString()); } return newKeyPair; 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 7b333bc9..0c3dd43a 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 @@ -13,14 +13,14 @@ enum Status { // TODO: 2019-08-14 namings??? /** - * Destination account public id. + * Destination account public address. */ - String destinationPublicKey(); + String destinationPublicAddress(); /** - * Source account public id. + * Source account public address. */ - String sourcePublicKey(); + String sourcePublicAddress(); /** * Pending Payment amount in kin. 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 f32dc971..86fc61bd 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 @@ -16,14 +16,14 @@ public class BatchPaymentTransaction extends TransactionInternal { public interface PaymentOperation { /** - * Destination account public id. + * Destination account public address. */ - String destination(); + String destinationPublicAddress(); /** - * Source account public id. + * Source account public address. */ - String source(); + String sourcePublicAddress(); /** * Payment amount in kin. From 5966eff34f68362cdf51a8a20605a17245911d3f Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 15 Aug 2019 12:18:52 +0300 Subject: [PATCH 03/12] Add some methods and stubs also to KinAccount --- .../src/main/java/kin/sdk/KinAccount.java | 118 +++++++++++++++--- .../sdk/internal/account/KinAccountImpl.java | 37 ++++++ 2 files changed, 136 insertions(+), 19 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 54acc606..e5df7e48 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 @@ -3,6 +3,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import kin.sdk.exception.*; +import kin.sdk.queue.PaymentQueue; import kin.sdk.transactiondata.PaymentTransaction; import kin.utils.Request; @@ -22,42 +23,52 @@ public interface KinAccount { /** * 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

+ * * @param publicAddress the account address to send the specified kin amount. - * @param amount the amount of kin to transfer. - * @param fee the amount of fee(in Quarks) for this transfer (1 Quark = 0.00001 KIN). + * @param amount the amount of kin to transfer. + * @param fee the amount of fee(in Quarks) for this transfer (1 Quark = 0.00001 KIN). * @return {@code Request}, TransactionId - the transaction identifier. */ + @Deprecated Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee); /** - * 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). + * 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

+ * * @param publicAddress the account address to send the specified kin amount. - * @param amount the amount of kin to transfer. - * @param fee the amount of fee(in Quarks) for this transfer (1 Quark = 0.00001 KIN). - * @param memo An optional string, can contain a utf-8 string up to 21 bytes in length, included on the transaction record. + * @param amount the amount of kin to transfer. + * @param fee the amount of fee(in Quarks) for this transfer (1 Quark = 0.00001 KIN). + * @param memo An optional string, can contain a utf-8 string up to 21 bytes in length, included on the + * transaction record. * @return {@code Request}, TransactionId - the transaction identifier */ + @Deprecated Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo); /** * Create {@link Request} for signing and sending a transaction *

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

+ * * @param transaction is the transaction object to send. * @return {@code Request}, TransactionId - the transaction identifier. */ @NonNull + @Deprecated Request sendTransaction(PaymentTransaction transaction); /** * 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

+ * * @param whitelist is the whitelist data (got from the server) which will be used to send the transaction. * @return {@code Request}, TransactionId - the transaction identifier. */ @NonNull + @Deprecated Request sendWhitelistTransaction(String whitelist); /** @@ -65,27 +76,32 @@ Request buildTransaction(@NonNull String publicAddress, @Non *

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

* * @param publicAddress the account address to send the specified kin amount. - * @param amount the amount of kin to transfer. - * @param fee the amount of fee(in Quarks) for this transfer (1 Quark = 0.00001 KIN). + * @param amount the amount of kin to transfer. + * @param fee the amount of fee(in Quarks) for this transfer (1 Quark = 0.00001 KIN). * @return a Transaction object which also includes the transaction id. * @throws AccountNotFoundException if the sender or destination account was not created. * @throws OperationFailedException other error occurred. */ + @Deprecated PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException; /** - * 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). + * 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.

* * @param publicAddress the account address to send the specified kin amount. - * @param amount the amount of kin to transfer. - * @param fee the amount of fee(in Quarks) for this transfer (1 Quark = 0.00001 KIN). - * @param memo An optional string, can contain a utf-8 string up to 21 bytes in length, included on the transaction record. + * @param amount the amount of kin to transfer. + * @param fee the amount of fee(in Quarks) for this transfer (1 Quark = 0.00001 KIN). + * @param memo An optional string, can contain a utf-8 string up to 21 bytes in length, included on the + * transaction record. * @return a Transaction object which also includes the transaction id. * @throws AccountNotFoundException if the sender or destination account was not created. * @throws OperationFailedException other error occurred. */ - PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException; + @Deprecated + PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, + @Nullable String memo) throws OperationFailedException; /** * send a transaction. @@ -93,12 +109,13 @@ Request buildTransaction(@NonNull String publicAddress, @Non * * @param transaction is the transaction object to send. * @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 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. + * @throws OperationFailedException other error occurred. */ @NonNull + @Deprecated TransactionId sendTransactionSync(PaymentTransaction transaction) throws OperationFailedException; /** @@ -108,14 +125,65 @@ Request buildTransaction(@NonNull String publicAddress, @Non * * @param whitelist is the whitelist data (got from the server) which will be used to send the transaction. * @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 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. + * @throws OperationFailedException other error occurred. */ @NonNull + @Deprecated 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.

+ * + * @param transaction is 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. + */ + TransactionId sendTransactionSync(SendTransactionParams transaction, TransactionInterceptor interceptor) throws OperationFailedException; + + /** + * Create {@link Request} for signing and sending a transaction + *

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

+ * + * @param transaction is 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); + + /** + * @return the payment queue. + */ + PaymentQueue paymentQueue(); + + /** + * Create {@link Request} for getting the current pending balance + *

pending balance is a balance that calculates the expected balance including all of the queued transactions + * .

+ *

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

+ * + * @return {@code Request} Balance - the pending balance in kin + */ + Request getPendingBalance(); + + /** + * Get the current confirmed pending balance in kin + *

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

+ * + * @return the pending balance in kin + * @throws AccountNotFoundException if account was not created + * @throws OperationFailedException any other error + */ + @NonNull + Request getPendingBalanceSync(); + /** * Create {@link Request} for getting the current confirmed balance in kin *

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

@@ -179,6 +247,18 @@ Request buildTransaction(@NonNull String publicAddress, @Non */ ListenerRegistration addAccountCreationListener(final EventListener listener); + /** + * Creates and adds listener for pending balance changes of this account, use returned + * {@link ListenerRegistration} to + * stop listening.

Note: Events will be fired on background thread.

+ * + *

pending balance is a balance that calculates the expected balance including all of the queued transactions + * .

+ * + * @param listener listener object for payment events + */ + ListenerRegistration addPendingBalanceListener(final EventListener listener); + /** * Export the account data as a JSON string. The seed is encrypted. * 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 9931479e..a6fde74e 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 @@ -12,7 +12,10 @@ 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; @@ -23,6 +26,7 @@ public final class KinAccountImpl extends AbstractKinAccount { private final TransactionSender transactionSender; private final AccountInfoRetriever accountInfoRetriever; private final BlockchainEvents blockchainEvents; + private final PaymentQueue paymentQueue; private boolean isDeleted = false; public KinAccountImpl(KeyPair account, BackupRestore backupRestore, TransactionSender transactionSender, @@ -32,6 +36,7 @@ public KinAccountImpl(KeyPair account, BackupRestore backupRestore, TransactionS this.transactionSender = transactionSender; this.accountInfoRetriever = accountInfoRetriever; this.blockchainEvents = blockchainEventsCreator.create(account.getAccountId()); + this.paymentQueue = new PaymentQueueImpl(account.getAccountId()); } @Override @@ -70,6 +75,33 @@ public TransactionId sendWhitelistTransactionSync(String whitelist) throws Opera return transactionSender.sendWhitelistTransaction(whitelist); } + @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 + } + + @Override + public PaymentQueue paymentQueue() { + return paymentQueue; + } + + @Override + public Request getPendingBalance() { + return null; // TODO: 2019-08-15 implement + } + + @NonNull + @Override + public Request getPendingBalanceSync() { + return null; // TODO: 2019-08-15 implement + } + @NonNull @Override public Balance getBalanceSync() throws OperationFailedException { @@ -98,6 +130,11 @@ public ListenerRegistration addAccountCreationListener(EventListener liste return blockchainEvents.addAccountCreationListener(listener); } + @Override + public ListenerRegistration addPendingBalanceListener(EventListener listener) { + return null; // TODO: 2019-08-15 implement + } + @Override public String export(@NonNull String passphrase) throws CryptoException { return backupRestore.exportWallet(account, passphrase); From f30df702f673fe4c10e171597abfea6c12ac84f5 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 15 Aug 2019 12:20:01 +0300 Subject: [PATCH 04/12] Fix one of the methods return value --- kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java | 2 +- .../src/main/java/kin/sdk/internal/account/KinAccountImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 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 e5df7e48..c9698e81 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 @@ -182,7 +182,7 @@ PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull * @throws OperationFailedException any other error */ @NonNull - Request getPendingBalanceSync(); + Balance getPendingBalanceSync(); /** * Create {@link Request} for getting the current confirmed balance in kin 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 a6fde74e..84ef3d41 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 @@ -98,7 +98,7 @@ public Request getPendingBalance() { @NonNull @Override - public Request getPendingBalanceSync() { + public Balance getPendingBalanceSync() { return null; // TODO: 2019-08-15 implement } From a1ee173ccdfe9dda7976fe82af640550ec7644ad Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Thu, 15 Aug 2019 15:00:06 +0300 Subject: [PATCH 05/12] Update whatever we forgot in gradle --- build.gradle | 2 +- .../kin-backup-and-restore-lib/build.gradle | 10 +++++----- kin-sdk/kin-base/build.gradle | 12 ++++++------ kin-sdk/kin-sdk-lib/build.gradle | 8 ++++---- kin-sdk/kin-sdk-sample/build.gradle | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/build.gradle b/build.gradle index 99d3b7d8..ae8521cd 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ ext { } buildscript { - ext.kotlin_version = '1.3.31' + ext.kotlin_version = '1.3.41' repositories { google() jcenter() diff --git a/kin-backup-and-restore/kin-backup-and-restore-lib/build.gradle b/kin-backup-and-restore/kin-backup-and-restore-lib/build.gradle index 3304213d..eb4e56e5 100644 --- a/kin-backup-and-restore/kin-backup-and-restore-lib/build.gradle +++ b/kin-backup-and-restore/kin-backup-and-restore-lib/build.gradle @@ -36,18 +36,18 @@ dependencies { implementation project(':kin-sdk:kin-sdk-lib') implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:support-annotations:28.0.0' - implementation 'com.android.support:appcompat-v7:26.1.0' + implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.google.zxing:core:3.3.3' androidTestImplementation 'junit:junit:4.12' - androidTestImplementation 'org.mockito:mockito-core:2.10.0' + androidTestImplementation 'org.mockito:mockito-core:2.23.0' androidTestImplementation 'org.hamcrest:hamcrest-library:1.3' androidTestImplementation 'com.android.support.test:runner:1.0.2' - testImplementation 'org.jetbrains.kotlin:kotlin-stdlib:1.2.71' - testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0-RC1' + testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.13.0' + testImplementation 'org.mockito:mockito-core:2.23.0' testImplementation 'org.robolectric:robolectric:4.3' } diff --git a/kin-sdk/kin-base/build.gradle b/kin-sdk/kin-base/build.gradle index 8d52f3fd..8df5ee10 100644 --- a/kin-sdk/kin-base/build.gradle +++ b/kin-sdk/kin-base/build.gradle @@ -21,13 +21,13 @@ android { dependencies { implementation fileTree(dir: 'libs', include: '*.jar') implementation 'com.moandjiezana.toml:toml4j:0.5.1' - implementation 'com.google.code.gson:gson:2.8.2' - implementation 'com.squareup.okhttp3:okhttp:3.9.1' + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'net.i2p.crypto:eddsa:0.3.0' api 'com.github.kinecosystem:oksse:93f4ef7445f9c3db3c3bc2d1ccb2691fc7246810' - testImplementation 'org.mockito:mockito-core:2.13.0' - testImplementation "org.robolectric:robolectric:3.6.1" - testImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1' - testImplementation group: 'junit', name: 'junit', version: '4.11' + testImplementation 'org.mockito:mockito-core:2.23.0' + testImplementation "org.robolectric:robolectric:4.3" + testImplementation 'com.squareup.okhttp3:mockwebserver:3.9.1' + testImplementation group: 'junit', name: 'junit', version: '4.12' } diff --git a/kin-sdk/kin-sdk-lib/build.gradle b/kin-sdk/kin-sdk-lib/build.gradle index de65f6f9..ae0086d6 100644 --- a/kin-sdk/kin-sdk-lib/build.gradle +++ b/kin-sdk/kin-sdk-lib/build.gradle @@ -50,14 +50,14 @@ dependencies { api 'com.github.kinecosystem:kin-utils-android:1.1' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.10.0' + testImplementation 'org.mockito:mockito-core:2.23.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.2' + testImplementation 'com.google.code.gson:gson:2.8.5' androidTestImplementation 'junit:junit:4.12' - androidTestImplementation 'org.mockito:mockito-core:2.10.0' + androidTestImplementation 'org.mockito:mockito-core:2.23.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' @@ -65,7 +65,7 @@ dependencies { 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 'com.android.support:multidex:1.0.2' + androidTestImplementation 'com.android.support:multidex:1.0.3' androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" androidTestImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" } diff --git a/kin-sdk/kin-sdk-sample/build.gradle b/kin-sdk/kin-sdk-sample/build.gradle index 3c84230f..1ddc9571 100644 --- a/kin-sdk/kin-sdk-sample/build.gradle +++ b/kin-sdk/kin-sdk-sample/build.gradle @@ -40,5 +40,5 @@ dependencies { implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' api project(':kin-sdk:kin-sdk-lib') - implementation 'com.squareup.okhttp3:okhttp:3.9.1' + implementation 'com.squareup.okhttp3:okhttp:3.10.0' } From 49fab68390fa97f551cbafb5a79b8f6f83b089d8 Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 19 Aug 2019 12:18:35 +0300 Subject: [PATCH 06/12] Add unit tests for PaymentQueueManager --- kin-sdk/kin-sdk-lib/build.gradle | 2 + .../queue/PaymentQueueManagerImpl.java | 5 +- .../sdk/internal/queue/QueueScheduler.java | 5 +- .../sdk/internal/queue/FakeQueueScheduler.kt | 57 ++++++ .../queue/PaymentQueueManagerImplTest.kt | 170 ++++++++++++++++++ 5 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt create mode 100644 kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerImplTest.kt diff --git a/kin-sdk/kin-sdk-lib/build.gradle b/kin-sdk/kin-sdk-lib/build.gradle index ae0086d6..ac4145cf 100644 --- a/kin-sdk/kin-sdk-lib/build.gradle +++ b/kin-sdk/kin-sdk-lib/build.gradle @@ -55,6 +55,8 @@ dependencies { 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' androidTestImplementation 'junit:junit:4.12' androidTestImplementation 'org.mockito:mockito-core:2.23.0' 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 c079a520..a5d3849a 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 @@ -72,13 +72,12 @@ private void addToQueue() { } } }); - } @Override public List getPaymentQueue() { - // TODO: 2019-08-15 need to think why exactly do we need it? is it only for the pending balance updater? - // maybe return a copy... + // TODO: 2019-08-15 need to think why exactly if we really need it? is it only for the pending balance updater? + // maybe return a copy or make it immutable/read only... return queue; } diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueScheduler.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueScheduler.java index 9ba37469..a2ab47f2 100644 --- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueScheduler.java +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/QueueScheduler.java @@ -10,6 +10,7 @@ public interface QueueScheduler { /** * schedule a task to be run after the specified amount of time elapses. * + * @param runnable is the task to be executed * @param delayInMillis The delay (in milliseconds) until the task will be executed */ void scheduleDelayed(Runnable runnable, long delayInMillis); @@ -22,7 +23,7 @@ public interface QueueScheduler { /** * Remove a specific pending task * - * @param task is the task to remove + * @param runnable is the task to remove */ - void removePendingTask(Runnable task); + void removePendingTask(Runnable runnable); } 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 new file mode 100644 index 00000000..3b71cb5d --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt @@ -0,0 +1,57 @@ +package kin.sdk.internal.queue + +import java.util.* +import kotlin.concurrent.schedule + +open class FakeQueueScheduler : QueueScheduler { + + private var timer: Timer? = Timer() + private val futureTasks: HashMap = HashMap() + + //TODO what is better? one of the commented one or the non commented one. Which one? + +// override fun scheduleDelayed(runnable: Runnable?, delayInMillis: Long) { +// Handler().postDelayed({ +// runnable?.run() +// }, delayInMillis) +// } + +// override fun scheduleDelayed(runnable: Runnable?, delayInMillis: Long) { +// val timer = Timer() +// timer.schedule(object: TimerTask() { +// override fun run() { +// runnable?.run() +// } +// }, delayInMillis) +// } + +// override fun scheduleDelayed(runnable: Runnable?, delayInMillis: Long) { +// val timer = Timer() +// timer.schedule(timerTask {runnable?.run() }, delayInMillis) +// } + + override fun scheduleDelayed(runnable: Runnable?, delayInMillis: Long) { + val task = timer?.schedule(delayInMillis) { + runnable?.run() + } + futureTasks[runnable] = task + } + + override fun removeAllPendingTasks() { + // killing all future futureTasks not including the current running task if there is one. + timer?.cancel() + timer?.purge() + futureTasks.clear() + timer = Timer() + } + + override fun removePendingTask(runnable: Runnable?) { + futureTasks[runnable]?.cancel() + } + + override fun schedule(runnable: Runnable?) { + runnable?.run() + } + + +} \ No newline at end of file diff --git a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerImplTest.kt b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerImplTest.kt new file mode 100644 index 00000000..c02082d1 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerImplTest.kt @@ -0,0 +1,170 @@ +package kin.sdk.internal.queue + +import com.nhaarman.mockitokotlin2.* +import kin.base.KeyPair +import kin.sdk.internal.data.PendingBalanceUpdater +import kin.sdk.internal.events.EventsManager +import kin.sdk.queue.PendingPayment +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.Before +import org.junit.Test +import java.math.BigDecimal + +class PaymentQueueManagerImplTest { + + object Constants { + const val ONE_MILLI = 1000L + const val DELAY_BETWEEN_PAYMENTS_MILLIS = 3000L + const val QUEUE_TIMEOUT_MILLIS = 5000L + const val MAX_NUM_OF_PAYMENTS = 5 + } + + 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 pendingBalanceUpdater: PendingBalanceUpdater = mock() + private var eventsManager: EventsManager = mock() + + private lateinit var paymentQueueManager: PaymentQueueManagerImpl + private lateinit var destinationAccount: String + private lateinit var sourceAccount: String + private val amount: BigDecimal = BigDecimal.TEN + + @Before + @Throws(Exception::class) + fun setUp() { + initPaymentQueueManager() + } + + private fun initPaymentQueueManager() { + destinationAccount = KeyPair.random().accountId + sourceAccount = KeyPair.random().accountId +// kinAccount = KinAccountImpl(expectedRandomAccount, FakeBackupRestore(), mock(), mock(), mock()) + paymentQueueManager = PaymentQueueManagerImpl(txTaskQueueManager, queueScheduler, pendingBalanceUpdater, eventsManager, + Constants.DELAY_BETWEEN_PAYMENTS_MILLIS, Constants.QUEUE_TIMEOUT_MILLIS, Constants.MAX_NUM_OF_PAYMENTS) + } + + @Test + fun `enqueue list size is increased by one`() { + //given + val pendingPayment = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + + //when + paymentQueueManager.enqueue(pendingPayment) + + //then + assertThat(paymentQueueManager.pendingPaymentCount, equalTo(1)) + } + + @Test + fun `enqueue wait for delay between payments list is empty after delay`() { + //given + val pendingPayment = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + + //when + paymentQueueManager.enqueue(pendingPayment) + // TODO because we know how much time the task should take then i use sleep for that time plus one milli - is this the correct pattern? + Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS + Constants.ONE_MILLI) + + //then + assertThat(paymentQueueManager.pendingPaymentCount, equalTo(0)) + + val pendingPayments: MutableList = mutableListOf() + pendingPayments.add(pendingPayment) + + //TODO should i really verify it? maybe this shouldn't be verified because it is + //TODO like a whitebox for us we should verify the outcome and not the way we got to that outcome. no? + verify(queueScheduler).removePendingTask(any()) + verify(queueScheduler).removeAllPendingTasks() + + verify(txTaskQueueManager).enqueue(pendingPayments) + } + + @Test + fun `enqueue wait for queue timeout list is empty only after timeout`() { + //given + val pendingPayment1 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + val pendingPayment2 = PendingPaymentImpl(destinationAccount, sourceAccount, amount.add(BigDecimal.TEN)) + val pendingPayment3 = PendingPaymentImpl(destinationAccount, sourceAccount, amount.add(BigDecimal.ONE)) + + //when + paymentQueueManager.enqueue(pendingPayment1) + Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS - Constants.ONE_MILLI) + paymentQueueManager.enqueue(pendingPayment2) + Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS - Constants.ONE_MILLI) + paymentQueueManager.enqueue(pendingPayment3) + + //then + assertThat(paymentQueueManager.pendingPaymentCount, equalTo(3)) + Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS) + assertThat(paymentQueueManager.pendingPaymentCount, equalTo(0)) + + val pendingPayments: MutableList = mutableListOf() + pendingPayments.add(pendingPayment1) + pendingPayments.add(pendingPayment2) + pendingPayments.add(pendingPayment3) + + verify(queueScheduler).removeAllPendingTasks() + verify(txTaskQueueManager).enqueue(pendingPayments) + } + + @Test + fun `enqueue max items list is empty only after queue has been filled`() { + //given + val pendingPayment1 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + val pendingPayment2 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + val pendingPayment3 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + val pendingPayment4 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + val pendingPayment5 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + + //when + paymentQueueManager.enqueue(pendingPayment1) + paymentQueueManager.enqueue(pendingPayment2) + paymentQueueManager.enqueue(pendingPayment3) + paymentQueueManager.enqueue(pendingPayment4) + assertThat(paymentQueueManager.pendingPaymentCount, equalTo(4)) + paymentQueueManager.enqueue(pendingPayment5) + + //then + assertThat(paymentQueueManager.pendingPaymentCount, equalTo(0)) + + val pendingPayments: MutableList = mutableListOf() + pendingPayments.add(pendingPayment1) + pendingPayments.add(pendingPayment2) + pendingPayments.add(pendingPayment3) + pendingPayments.add(pendingPayment4) + pendingPayments.add(pendingPayment5) + + verify(queueScheduler).removeAllPendingTasks() + verify(txTaskQueueManager).enqueue(pendingPayments) + } + + @Test + fun `enqueue items schedule queue timeout only for first item each time`() { + //given + val pendingPayment1 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + val pendingPayment2 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + val pendingPayment3 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + val pendingPayment4 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + val pendingPayment5 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + val pendingPayment6 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + + //when + paymentQueueManager.enqueue(pendingPayment1) + paymentQueueManager.enqueue(pendingPayment2) + Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS + Constants.ONE_MILLI) + paymentQueueManager.enqueue(pendingPayment3) + paymentQueueManager.enqueue(pendingPayment4) + Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS + Constants.ONE_MILLI) + paymentQueueManager.enqueue(pendingPayment5) + paymentQueueManager.enqueue(pendingPayment6) + + //then + assertThat(paymentQueueManager.pendingPaymentCount, equalTo(2)) + verify(queueScheduler, times(2)).removeAllPendingTasks() + verify(queueScheduler, times(3)).scheduleDelayed(any(), eq(Constants.QUEUE_TIMEOUT_MILLIS)) + } + + //TODO maybe make better method names, look at conventions, also later add tests with pending balance + +} \ No newline at end of file From 97e80e5f43c4eb42ff5816e71dfa31b94675bc3f Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Mon, 19 Aug 2019 15:47:05 +0300 Subject: [PATCH 07/12] Add the foundation for integration tests for the queue. Need to discuss it. --- kin-sdk/kin-sdk-lib/build.gradle | 5 +- .../sdk/internal/queue/FakeQueueScheduler.kt | 57 ++++++ .../PaymentQueueManagerIntegrationTest.kt | 165 ++++++++++++++++++ ...ImplTest.kt => PaymentQueueManagerTest.kt} | 18 +- 4 files changed, 234 insertions(+), 11 deletions(-) create mode 100644 kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt create mode 100644 kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueManagerIntegrationTest.kt rename kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/{PaymentQueueManagerImplTest.kt => PaymentQueueManagerTest.kt} (93%) diff --git a/kin-sdk/kin-sdk-lib/build.gradle b/kin-sdk/kin-sdk-lib/build.gradle index ac4145cf..18c32227 100644 --- a/kin-sdk/kin-sdk-lib/build.gradle +++ b/kin-sdk/kin-sdk-lib/build.gradle @@ -57,6 +57,7 @@ dependencies { 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 "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" androidTestImplementation 'junit:junit:4.12' androidTestImplementation 'org.mockito:mockito-core:2.23.0' @@ -67,9 +68,9 @@ dependencies { 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 'com.android.support:multidex:1.0.3' - androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 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/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt new file mode 100644 index 00000000..0347edcf --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt @@ -0,0 +1,57 @@ +package kin.sdk.internal.queue + +import java.util.* +import kotlin.concurrent.schedule + +open class FakeIntegrationTestQueueScheduler : QueueScheduler { + + private var timer: Timer? = Timer() + private val futureTasks: HashMap = HashMap() + + //TODO what is better? one of the commented one or the non commented one. Which one? + +// override fun scheduleDelayed(runnable: Runnable?, delayInMillis: Long) { +// Handler().postDelayed({ +// runnable?.run() +// }, delayInMillis) +// } + +// override fun scheduleDelayed(runnable: Runnable?, delayInMillis: Long) { +// val timer = Timer() +// timer.schedule(object: TimerTask() { +// override fun run() { +// runnable?.run() +// } +// }, delayInMillis) +// } + +// override fun scheduleDelayed(runnable: Runnable?, delayInMillis: Long) { +// val timer = Timer() +// timer.schedule(timerTask {runnable?.run() }, delayInMillis) +// } + + override fun scheduleDelayed(runnable: Runnable?, delayInMillis: Long) { + val task = timer?.schedule(delayInMillis) { + runnable?.run() + } + futureTasks[runnable] = task + } + + override fun removeAllPendingTasks() { + // killing all future futureTasks not including the current running task if there is one. + timer?.cancel() + timer?.purge() + futureTasks.clear() + timer = Timer() + } + + override fun removePendingTask(runnable: Runnable?) { + futureTasks[runnable]?.cancel() + } + + override fun schedule(runnable: Runnable?) { + runnable?.run() + } + + +} \ No newline at end of file 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 new file mode 100644 index 00000000..2dba5080 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueManagerIntegrationTest.kt @@ -0,0 +1,165 @@ +package kin.sdk.internal.queue + +import kin.base.KeyPair +import kin.sdk.internal.data.PendingBalanceUpdaterImpl +import kin.sdk.internal.events.EventsManagerImpl +import kin.sdk.queue.PendingPayment +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.equalTo +import org.junit.Before +import org.junit.Test +import java.math.BigDecimal + +class PaymentQueueManagerIntegrationTest { + + object Constants { + const val ONE_MILLI = 1000L + const val DELAY_BETWEEN_PAYMENTS_MILLIS = 3000L + const val QUEUE_TIMEOUT_MILLIS = 5000L + const val MAX_NUM_OF_PAYMENTS = 5 + } + + private var queueScheduler = FakeIntegrationTestQueueScheduler() + private var txTaskQueueManager = TransactionTaskQueueManagerImpl() + private var pendingBalanceUpdater = PendingBalanceUpdaterImpl() + private var eventsManager = EventsManagerImpl() + + private lateinit var paymentQueueManager: PaymentQueueManager + private lateinit var destinationAccount: String + private lateinit var sourceAccount: String + private val amount: BigDecimal = BigDecimal.TEN + + @Before + @Throws(Exception::class) + fun setUp() { + initPaymentQueueManager() + } + + private fun initPaymentQueueManager() { + destinationAccount = KeyPair.random().accountId + sourceAccount = KeyPair.random().accountId +// kinAccount = KinAccountImpl(expectedRandomAccount, FakeBackupRestore(), mock(), mock(), mock()) + paymentQueueManager = PaymentQueueManagerImpl(txTaskQueueManager, queueScheduler, pendingBalanceUpdater, eventsManager, + Constants.DELAY_BETWEEN_PAYMENTS_MILLIS, Constants.QUEUE_TIMEOUT_MILLIS, Constants.MAX_NUM_OF_PAYMENTS) + } + + @Test + fun enqueuePayment_ListSizeIncreasedByOne() { + //given + val pendingPayment = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + + //when + paymentQueueManager.enqueue(pendingPayment) + + //then + assertThat(paymentQueueManager.pendingPaymentCount, equalTo(1)) + val pendingPayments: MutableList = mutableListOf() + pendingPayments.add(pendingPayment) + assertThat(pendingPayments, `is`(equalTo(paymentQueueManager.paymentQueue))) + } + + @Test + fun enqueuePayment_DelayBetweenPayments_ListIsEmpty() { + //given + val pendingPayment = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + + //when + paymentQueueManager.enqueue(pendingPayment) + Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS + Constants.ONE_MILLI) + + //then + assertThat(paymentQueueManager.pendingPaymentCount, equalTo(0)) + + //TODO how to verify stuff in integration tests, like num of method calls and a specific method that was called with specific parameters? + + } +// +// @Test +// fun `enqueue wait for queue timeout list is empty only after timeout`() { +// //given +// val pendingPayment1 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// val pendingPayment2 = PendingPaymentImpl(destinationAccount, sourceAccount, amount.add(BigDecimal.TEN)) +// val pendingPayment3 = PendingPaymentImpl(destinationAccount, sourceAccount, amount.add(BigDecimal.ONE)) +// +// //when +// paymentQueueManager.enqueue(pendingPayment1) +// Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS - Constants.ONE_MILLI) +// paymentQueueManager.enqueue(pendingPayment2) +// Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS - Constants.ONE_MILLI) +// paymentQueueManager.enqueue(pendingPayment3) +// +// //then +// assertThat(paymentQueueManager.pendingPaymentCount, equalTo(3)) +// Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS) +// assertThat(paymentQueueManager.pendingPaymentCount, equalTo(0)) +// +// val pendingPayments: MutableList = mutableListOf() +// pendingPayments.add(pendingPayment1) +// pendingPayments.add(pendingPayment2) +// pendingPayments.add(pendingPayment3) +// +// verify(queueScheduler).removeAllPendingTasks() +// verify(txTaskQueueManager).enqueue(pendingPayments) +// } +// +// @Test +// fun `enqueue max items list is empty only after queue has been filled`() { +// //given +// val pendingPayment1 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// val pendingPayment2 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// val pendingPayment3 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// val pendingPayment4 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// val pendingPayment5 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// +// //when +// paymentQueueManager.enqueue(pendingPayment1) +// paymentQueueManager.enqueue(pendingPayment2) +// paymentQueueManager.enqueue(pendingPayment3) +// paymentQueueManager.enqueue(pendingPayment4) +// assertThat(paymentQueueManager.pendingPaymentCount, equalTo(4)) +// paymentQueueManager.enqueue(pendingPayment5) +// +// //then +// assertThat(paymentQueueManager.pendingPaymentCount, equalTo(0)) +// +// val pendingPayments: MutableList = mutableListOf() +// pendingPayments.add(pendingPayment1) +// pendingPayments.add(pendingPayment2) +// pendingPayments.add(pendingPayment3) +// pendingPayments.add(pendingPayment4) +// pendingPayments.add(pendingPayment5) +// +// verify(queueScheduler).removeAllPendingTasks() +// verify(txTaskQueueManager).enqueue(pendingPayments) +// } +// +// @Test +// fun `enqueue items schedule queue timeout only for first item each time`() { +// //given +// val pendingPayment1 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// val pendingPayment2 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// val pendingPayment3 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// val pendingPayment4 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// val pendingPayment5 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// val pendingPayment6 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) +// +// //when +// paymentQueueManager.enqueue(pendingPayment1) +// paymentQueueManager.enqueue(pendingPayment2) +// Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS + Constants.ONE_MILLI) +// paymentQueueManager.enqueue(pendingPayment3) +// paymentQueueManager.enqueue(pendingPayment4) +// Thread.sleep(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS + Constants.ONE_MILLI) +// paymentQueueManager.enqueue(pendingPayment5) +// paymentQueueManager.enqueue(pendingPayment6) +// +// //then +// assertThat(paymentQueueManager.pendingPaymentCount, equalTo(2)) +// verify(queueScheduler, times(2)).removeAllPendingTasks() +// verify(queueScheduler, times(3)).scheduleDelayed(any(), eq(Constants.QUEUE_TIMEOUT_MILLIS)) +// } + + //TODO maybe make better method names, look at conventions, also later add tests with pending balance + +} \ No newline at end of file diff --git a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerImplTest.kt b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt similarity index 93% rename from kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerImplTest.kt rename to kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt index c02082d1..b8089feb 100644 --- a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerImplTest.kt +++ b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt @@ -11,7 +11,7 @@ import org.junit.Before import org.junit.Test import java.math.BigDecimal -class PaymentQueueManagerImplTest { +class PaymentQueueManagerTest { object Constants { const val ONE_MILLI = 1000L @@ -25,7 +25,7 @@ class PaymentQueueManagerImplTest { private var pendingBalanceUpdater: PendingBalanceUpdater = mock() private var eventsManager: EventsManager = mock() - private lateinit var paymentQueueManager: PaymentQueueManagerImpl + private lateinit var paymentQueueManager: PaymentQueueManager private lateinit var destinationAccount: String private lateinit var sourceAccount: String private val amount: BigDecimal = BigDecimal.TEN @@ -45,7 +45,7 @@ class PaymentQueueManagerImplTest { } @Test - fun `enqueue list size is increased by one`() { + fun `enqueue, list size is increased by one`() { //given val pendingPayment = PendingPaymentImpl(destinationAccount, sourceAccount, amount) @@ -57,7 +57,7 @@ class PaymentQueueManagerImplTest { } @Test - fun `enqueue wait for delay between payments list is empty after delay`() { + fun `enqueue, then wait for delay between payments, after the delay the list will be empty`() { //given val pendingPayment = PendingPaymentImpl(destinationAccount, sourceAccount, amount) @@ -81,11 +81,11 @@ class PaymentQueueManagerImplTest { } @Test - fun `enqueue wait for queue timeout list is empty only after timeout`() { + fun `enqueue, then wait for queue timeout, after the timeout the list will be empty`() { //given val pendingPayment1 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) - val pendingPayment2 = PendingPaymentImpl(destinationAccount, sourceAccount, amount.add(BigDecimal.TEN)) - val pendingPayment3 = PendingPaymentImpl(destinationAccount, sourceAccount, amount.add(BigDecimal.ONE)) + val pendingPayment2 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) + val pendingPayment3 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) //when paymentQueueManager.enqueue(pendingPayment1) @@ -109,7 +109,7 @@ class PaymentQueueManagerImplTest { } @Test - fun `enqueue max items list is empty only after queue has been filled`() { + fun `enqueue max items, after the queue has been filled then the list will be empty`() { //given val pendingPayment1 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) val pendingPayment2 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) @@ -140,7 +140,7 @@ class PaymentQueueManagerImplTest { } @Test - fun `enqueue items schedule queue timeout only for first item each time`() { + fun `enqueue items, verify that schedule queue timeout happens only for first item`() { //given val pendingPayment1 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) val pendingPayment2 = PendingPaymentImpl(destinationAccount, sourceAccount, amount) From 5a79922865517c7bbdea193518dbdb2ad41bb5ed Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Tue, 20 Aug 2019 13:00:48 +0300 Subject: [PATCH 08/12] Update appId java doc --- kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 6ec30377..748ca264 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 @@ -60,9 +60,10 @@ public KinClient(@NonNull Context context, @NonNull Environment environment, Str * Build KinClient object. * @param context android context * @param environment the blockchain network details. - * @param appId a 4 character string which represent the application id which will be added to each transaction. + * @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 bcda, etc.
+ * 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) { From a520f44f19508f9ec1b0b4722cfdcceae91ed54a Mon Sep 17 00:00:00 2001 From: Amitai Efrati Date: Tue, 20 Aug 2019 14:38:44 +0300 Subject: [PATCH 09/12] 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 10/12] 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: Mon, 2 Sep 2019 11:59:53 +0300 Subject: [PATCH 11/12] revert API changes in before batch payment API * revert Sample app to use old API to make sure nothing breaks (copied from master) * add back Transaction and WhitelistableTransaction, mark them as deprecated --- .../src/main/java/kin/sdk/KinAccount.java | 14 ++-- .../src/main/java/kin/sdk/Transaction.java | 70 +++++++++++++++++++ .../kin/sdk/WhitelistableTransaction.java | 27 +++++++ .../internal/account/AbstractKinAccount.java | 18 +++-- .../sdk/internal/account/KinAccountImpl.java | 13 ++-- .../blockchain/TransactionSender.java | 41 ++++++++--- .../test/java/kin/sdk/KinAccountImplTest.java | 9 ++- .../src/test/java/kin/sdk/KinClientTest.java | 3 + .../java/sdk/sample/TransactionActivity.java | 20 +++--- .../java/sdk/sample/WhitelistService.java | 27 ++++--- 10 files changed, 183 insertions(+), 59 deletions(-) create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java create mode 100644 kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/WhitelistableTransaction.java 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..6c4512ae 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 @@ -41,7 +41,7 @@ public interface KinAccount { * @return {@code Request}, TransactionId - the transaction identifier. */ @Deprecated - Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee); + Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee); /** * @deprecated This method was deprecated in version 2.0.0, please use the PaymentQueue, @@ -59,7 +59,7 @@ public interface KinAccount { * @return {@code Request}, TransactionId - the transaction identifier */ @Deprecated - Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, + Request buildTransaction(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo); /** @@ -67,14 +67,14 @@ Request buildTransaction(@NonNull String publicAddress, @Non * 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

+ *

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

* * @param transaction is the transaction object to send. * @return {@code Request}, TransactionId - the transaction identifier. */ @NonNull @Deprecated - Request sendTransaction(PaymentTransaction transaction); + Request sendTransaction(Transaction transaction); /** * @deprecated This method was deprecated in version 2.0.0, please use the PaymentQueue, @@ -106,7 +106,7 @@ Request buildTransaction(@NonNull String publicAddress, @Non * @throws OperationFailedException other error occurred. */ @Deprecated - PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException; + Transaction 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, @@ -126,7 +126,7 @@ Request buildTransaction(@NonNull String publicAddress, @Non * @throws OperationFailedException other error occurred. */ @Deprecated - PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, + Transaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException; /** @@ -145,7 +145,7 @@ PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull */ @NonNull @Deprecated - TransactionId sendTransactionSync(PaymentTransaction transaction) throws OperationFailedException; + TransactionId sendTransactionSync(Transaction transaction) throws OperationFailedException; /** * @deprecated This method was deprecated in version 2.0.0, please use the PaymentQueue, diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java new file mode 100644 index 00000000..cd1951fd --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/Transaction.java @@ -0,0 +1,70 @@ +package kin.sdk; + +import java.math.BigDecimal; +import kin.base.KeyPair; + +/** + * @deprecated replaced by {@link kin.sdk.transactiondata.Transaction}, exists to supports backward compatibility + * for {@link KinAccount#buildTransaction(String, BigDecimal, int, String)} + */ +@Deprecated +public class Transaction { + + private final KeyPair destination; + private final KeyPair source; + private final BigDecimal amount; + private final int fee; + private final String memo; + + /** + * The transaction hash + */ + private final TransactionId id; + + private final kin.base.Transaction stellarTransaction; + private final WhitelistableTransaction whitelistableTransaction; + + public Transaction(KeyPair destination, KeyPair source, BigDecimal amount, int fee, String memo, + TransactionId id, kin.base.Transaction stellarTransaction, WhitelistableTransaction whitelistableTransaction) { + this.destination = destination; + this.source = source; + this.amount = amount; + this.fee = fee; + this.memo = memo; + this.id = id; + this.stellarTransaction = stellarTransaction; + this.whitelistableTransaction = whitelistableTransaction; + } + + public KeyPair getDestination() { + return destination; + } + + public KeyPair getSource() { + return source; + } + + public BigDecimal getAmount() { + return amount; + } + + public int getFee() { + return fee; + } + + public String getMemo() { + return memo; + } + + public TransactionId getId() { + return id; + } + + public kin.base.Transaction getBaseTransaction() { + return stellarTransaction; + } + + public WhitelistableTransaction getWhitelistableTransaction() { + return whitelistableTransaction; + } +} diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/WhitelistableTransaction.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/WhitelistableTransaction.java new file mode 100644 index 00000000..c9087478 --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/WhitelistableTransaction.java @@ -0,0 +1,27 @@ +package kin.sdk; + +/** + * This class wraps a transaction envelope xdr in base 64(transaction payload) + * and a network passphrase(the network id as string). * + * Those fields are necessary for the whitelist server in order to sign this transaction to be a whitelist transaction. + * @deprecated replaced by {@link WhitelistPayload} + */ +public class WhitelistableTransaction { + + private final String transactionPayload; + private final String networkPassphrase; + + public WhitelistableTransaction(String transactionPayload, String networkPassphrase) { + this.transactionPayload = transactionPayload; + this.networkPassphrase = networkPassphrase; + } + + public String getTransactionPayload() { + return transactionPayload; + } + + public String getNetworkPassphrase() { + return networkPassphrase; + } + +} 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..9d4f31b4 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,16 +2,14 @@ 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.Transaction; 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; @@ -19,22 +17,22 @@ abstract class AbstractKinAccount implements KinAccount { @NonNull @Override - public Request buildTransaction(@NonNull final String publicAddress, + public Request buildTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, final int fee) { - return new Request<>(new Callable() { + return new Request<>(new Callable() { @Override - public PaymentTransaction call() throws Exception { + public Transaction call() throws Exception { return buildTransactionSync(publicAddress, amount, fee); } }); }@NonNull @Override - public Request buildTransaction(@NonNull final String publicAddress, + public Request buildTransaction(@NonNull final String publicAddress, @NonNull final BigDecimal amount, final int fee, @Nullable final String memo) { - return new Request<>(new Callable() { + return new Request<>(new Callable() { @Override - public PaymentTransaction call() throws Exception { + public Transaction call() throws Exception { return buildTransactionSync(publicAddress, amount, fee, memo); } }); @@ -42,7 +40,7 @@ public PaymentTransaction call() throws Exception { @NonNull @Override - public Request sendTransaction(final PaymentTransaction transaction) { + public Request sendTransaction(final Transaction transaction) { return new Request<>(new Callable() { @Override public TransactionId call() throws Exception { 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..e092c057 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 @@ -10,6 +10,7 @@ import kin.sdk.EventListener; import kin.sdk.ListenerRegistration; import kin.sdk.PaymentInfo; +import kin.sdk.Transaction; import kin.sdk.TransactionId; import kin.sdk.TransactionInterceptor; import kin.sdk.exception.AccountDeletedException; @@ -59,24 +60,22 @@ public String getPublicAddress() { } @Override - public PaymentTransaction buildTransactionSync(@NonNull String publicAddress, - @NonNull BigDecimal amount, + public Transaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee) throws OperationFailedException { checkValidAccount(); - return transactionSender.buildPaymentTransaction(account, publicAddress, amount, fee); + return transactionSender.buildTransaction(account, publicAddress, amount, fee); } @Override - public PaymentTransaction buildTransactionSync(@NonNull String publicAddress, - @NonNull BigDecimal amount, + public Transaction buildTransactionSync(@NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException { checkValidAccount(); - return transactionSender.buildPaymentTransaction(account, publicAddress, amount, fee, memo); + return transactionSender.buildTransaction(account, publicAddress, amount, fee, memo); } @NonNull @Override - public TransactionId sendTransactionSync(PaymentTransaction transaction) throws OperationFailedException { + public TransactionId sendTransactionSync(Transaction transaction) throws OperationFailedException { checkValidAccount(); return transactionSender.sendTransaction(transaction); } 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..a0be761f 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,15 +3,14 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; - 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.Network; import kin.base.PaymentOperation; import kin.base.Server; import kin.base.Transaction.Builder; @@ -19,6 +18,7 @@ import kin.base.responses.HttpResponseException; import kin.base.responses.SubmitTransactionResponse; import kin.sdk.TransactionId; +import kin.sdk.WhitelistableTransaction; import kin.sdk.exception.AccountNotFoundException; import kin.sdk.exception.IllegalAmountException; import kin.sdk.exception.InsufficientFeeException; @@ -37,8 +37,8 @@ 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 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"; @@ -50,6 +50,20 @@ public TransactionSender(Server server, String appId) { this.appId = appId; } + public kin.sdk.Transaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, + int fee) throws OperationFailedException { + return buildTransaction(from, publicAddress, amount, fee, null); + } + + public kin.sdk.Transaction buildTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, + int fee, @Nullable String memo) throws OperationFailedException { + kin.base.Transaction stellarTransaction = buildKinBaseTransaction(from, publicAddress, amount, fee, memo); + TransactionId id = new TransactionIdImpl(Utils.byteArrayToHex(stellarTransaction.hash())); + WhitelistableTransaction whitelistableTransaction = + new WhitelistableTransaction(stellarTransaction.toEnvelopeXdrBase64(), Network.current().getNetworkPassphrase()); + return new kin.sdk.Transaction(KeyPair.fromAccountId(publicAddress), from, amount, fee, memo, id, stellarTransaction, whitelistableTransaction); + } + public PaymentTransaction buildPaymentTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, @@ -61,6 +75,12 @@ public PaymentTransaction buildPaymentTransaction(@NonNull KeyPair from, @NonNull String publicAddress, @NonNull BigDecimal amount, int fee, @Nullable String memo) throws OperationFailedException { + kin.base.Transaction stellarTransaction = buildKinBaseTransaction(from, publicAddress, amount, fee, memo); + return new PaymentTransaction(stellarTransaction, publicAddress, amount, memo); + } + + private kin.base.Transaction buildKinBaseTransaction(@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); @@ -69,15 +89,18 @@ public PaymentTransaction buildPaymentTransaction(@NonNull KeyPair from, KeyPair addressee = generateAddresseeKeyPair(publicAddress); AccountResponse sourceAccount = loadSourceAccount(from); verifyAddresseeAccount(generateAddresseeKeyPair(addressee.getAccountId())); - kin.base.Transaction stellarTransaction = buildStellarTransaction(from, amount, addressee - , sourceAccount, fee, memo); - return new PaymentTransaction(stellarTransaction, addressee.getAccountId(), amount, memo); + return buildStellarTransaction(from, amount, addressee + , sourceAccount, fee, memo); } public TransactionId sendTransaction(Transaction transaction) throws OperationFailedException { return sendTransaction(((TransactionInternal) transaction).baseTransaction()); } + public TransactionId sendTransaction(kin.sdk.Transaction transaction) throws OperationFailedException { + return sendTransaction(transaction.getBaseTransaction()); + } + public TransactionId sendWhitelistTransaction(String whitelist) throws OperationFailedException { try { kin.base.Transaction transaction = kin.base.Transaction.fromEnvelopeXdr(whitelist); @@ -149,10 +172,6 @@ private KeyPair generateAddresseeKeyPair(@NonNull String publicAddress) throws O } } - void foo() { - - } - @NonNull private kin.base.Transaction buildStellarTransaction(@NonNull KeyPair from, @NonNull BigDecimal amount, 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..13fa9f3d 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 @@ -2,6 +2,7 @@ import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -17,6 +18,7 @@ import kin.sdk.internal.data.BalanceImpl; import kin.sdk.internal.data.TransactionIdImpl; import kin.sdk.transactiondata.PaymentTransaction; +import org.robolectric.RobolectricTestRunner; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; @@ -25,6 +27,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +@RunWith(RobolectricTestRunner.class) public class KinAccountImplTest { @Mock @@ -68,7 +71,7 @@ public void sendTransactionSync() throws Exception { when(mockTransactionSender.sendTransaction((PaymentTransaction) any())).thenReturn(expectedTransactionId); - PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100); + Transaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); verify(mockTransactionSender).sendTransaction(transaction); @@ -86,7 +89,7 @@ public void sendTransactionSync_WithMemo() throws Exception { when(mockTransactionSender.sendTransaction((PaymentTransaction) any())).thenReturn(expectedTransactionId); - PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100, memo); + Transaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100, memo); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); verify(mockTransactionSender).sendTransaction(transaction); @@ -123,7 +126,7 @@ public void sendTransactionSync_DeletedAccount_Exception() throws Exception { initWithRandomAccount(); kinAccount.markAsDeleted(); - PaymentTransaction transaction = kinAccount.buildTransactionSync( + Transaction transaction = kinAccount.buildTransactionSync( "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX", new BigDecimal("12.2"), 100); kinAccount.sendTransactionSync(transaction); } diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinClientTest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinClientTest.java index 6c57b873..565f60e2 100644 --- a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinClientTest.java +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/KinClientTest.java @@ -13,11 +13,13 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Arrays; +import org.robolectric.RobolectricTestRunner; import static junit.framework.Assert.*; import static org.hamcrest.CoreMatchers.equalTo; @@ -28,6 +30,7 @@ import static org.mockito.Mockito.when; @SuppressWarnings("deprecation") +@RunWith(RobolectricTestRunner.class) public class KinClientTest { private static final String APP_ID = "1a2c"; 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 e9ce3c2d..4b1cb230 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 @@ -15,14 +15,16 @@ import java.math.BigDecimal; +import java.math.BigDecimal; import kin.sdk.KinAccount; +import kin.sdk.Transaction; import kin.sdk.TransactionId; import kin.sdk.exception.AccountDeletedException; import kin.sdk.exception.OperationFailedException; import kin.sdk.sample.R; -import kin.sdk.transactiondata.PaymentTransaction; import kin.utils.Request; import kin.utils.ResultCallback; +import org.json.JSONException; /** * Displays form to enter public address and amount and a button to send a transaction @@ -40,7 +42,7 @@ public static Intent getIntent(Context context) { private EditText toAddressInput, amountInput, feeInput, memoInput; private SwitchCompat switchButton; private Request gertMinimumFeeRequest; - private Request buildTransactionRequest; + private Request buildTransactionRequest; private Request sendTransactionRequest; private WhitelistService whitelistService; @@ -251,17 +253,16 @@ private void buildTransaction(String toAddress, BigDecimal amount, int fee, Stri buildTransactionRequest.run(new BuildTransactionCallback()); } - private void handleWhitelistTransaction(PaymentTransaction transaction) { + private void handleWhitelistTransaction(Transaction transaction) { try { - whitelistService.whitelistTransaction(transaction.whitelistPayload(), new WhitelistServiceListener()); + whitelistService.whitelistTransaction(transaction.getWhitelistableTransaction(), new WhitelistServiceListener()); } catch (JSONException e) { Utils.logError(e, "handleWhitelistTransaction"); KinAlertDialog.createErrorDialog(TransactionActivity.this, e.getMessage()).show(); } } - private void sendTransaction(PaymentTransaction transaction, KinAccount account, - DisplayCallback callback) { + private void sendTransaction(Transaction transaction, KinAccount account, DisplayCallback callback) { sendTransactionRequest = account.sendTransaction(transaction); sendTransactionRequest.run(callback); } @@ -279,12 +280,11 @@ public void displayResult(Context context, View view, TransactionId transactionI } } - private class BuildTransactionCallback implements ResultCallback { + private class BuildTransactionCallback implements ResultCallback { @Override - public void onResult(PaymentTransaction transaction) { - Log.d(TAG, "buildPaymentTransaction: build transaction " + transaction.id().id() + " " + - "succeeded"); + public void onResult(Transaction transaction) { + Log.d(TAG, "buildTransaction: build transaction " + transaction.getId().id() + " succeeded"); // This is just to differentiate between whitelist transaction and regular transaction if (switchButton.isChecked()) { diff --git a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/WhitelistService.java b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/WhitelistService.java index d4e38186..c8261e23 100644 --- a/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/WhitelistService.java +++ b/kin-sdk/kin-sdk-sample/src/main/java/sdk/sample/WhitelistService.java @@ -3,13 +3,18 @@ import android.os.Handler; import android.os.Looper; import android.support.annotation.NonNull; -import kin.sdk.WhitelistPayload; -import okhttp3.*; -import org.json.JSONException; -import org.json.JSONObject; - import java.io.IOException; import java.util.concurrent.TimeUnit; +import kin.sdk.WhitelistableTransaction; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.json.JSONException; +import org.json.JSONObject; class WhitelistService { @@ -27,9 +32,9 @@ class WhitelistService { .build(); } - void whitelistTransaction(WhitelistPayload whitelistPayload, - TransactionActivity.WhitelistServiceListener whitelistServiceListener) throws JSONException { - RequestBody requestBody = RequestBody.create(JSON, toJson(whitelistPayload)); + void whitelistTransaction(WhitelistableTransaction whitelistableTransaction, + TransactionActivity.WhitelistServiceListener whitelistServiceListener) throws JSONException { + RequestBody requestBody = RequestBody.create(JSON, toJson(whitelistableTransaction)); Request request = new Request.Builder() .url(URL_WHITELISTING_SERVICE) .post(requestBody) @@ -65,10 +70,10 @@ private void handleResponse(@NonNull Response response, TransactionActivity.Whit } } - private String toJson(WhitelistPayload whitelistPayload) throws JSONException { + private String toJson(WhitelistableTransaction whitelistableTransaction) throws JSONException { JSONObject jo = new JSONObject(); - jo.put("envelope", whitelistPayload.getTransactionPayload()); - jo.put("network_id", whitelistPayload.getNetworkPassphrase()); + jo.put("envelope", whitelistableTransaction.getTransactionPayload()); + jo.put("network_id", whitelistableTransaction.getNetworkPassphrase()); return jo.toString(); } From a06b0a5720b6ff44a8cebaa951a6b4784ce0c978 Mon Sep 17 00:00:00 2001 From: Yossi Rizgan Date: Mon, 2 Sep 2019 13:48:25 +0300 Subject: [PATCH 12/12] Fix tests fix android tests, duplicate TransactionSender tests for old API --- .../java/kin/sdk/KinAccountTest.java | 7 +- .../java/kin/sdk/WhitelistServiceForTest.java | 10 +- .../kin/sdk/KinAccountIntegrationTest.kt | 12 +- .../PaymentQueueManagerIntegrationTest.kt | 2 +- .../queue/TasksQueueIntegrationTest.kt | 4 +- .../test/java/kin/sdk/KinAccountImplTest.java | 4 +- .../kin/sdk/TransactionSenderOldAPITest.java | 467 ++++++++++++++++++ 7 files changed, 489 insertions(+), 17 deletions(-) create mode 100644 kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderOldAPITest.java diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java index dfb6ae4f..3946d414 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java +++ b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/KinAccountTest.java @@ -53,7 +53,7 @@ public void getStatusSync_DeletedAccount_AccountDeletedException() throws Except public void sendTransactionSync_DeletedAccount_AccountDeletedException() throws Exception { KinAccount kinAccount = kinClient.addAccount(); kinClient.deleteAccount(0); - PaymentTransaction transaction = kinAccount.buildTransactionSync( + Transaction transaction = kinAccount.buildTransactionSync( "GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", new BigDecimal(10), FEE); kinAccount.sendTransactionSync(transaction); @@ -63,10 +63,11 @@ public void sendTransactionSync_DeletedAccount_AccountDeletedException() throws public void sendWhitelistTransaction_DeletedAccount_AccountDeletedException() throws Exception { KinAccount kinAccount = kinClient.addAccount(); kinClient.deleteAccount(0); - PaymentTransaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", + Transaction transaction = kinAccount.buildTransactionSync("GBA2XHZRUAHEL4DZX7XNHR7HLBAUYPRNKLD2PIUKWV2LVVE6OJT4NDLM", new BigDecimal(10), 0); - String whitelist = new WhitelistServiceForTest().whitelistTransaction(transaction.whitelistPayload()); + String whitelist = new WhitelistServiceForTest().whitelistTransaction(transaction.getWhitelistableTransaction().getTransactionPayload(), + transaction.getWhitelistableTransaction().getNetworkPassphrase()); kinAccount.sendWhitelistTransactionSync(whitelist); } diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/WhitelistServiceForTest.java b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/WhitelistServiceForTest.java index 027f7583..c5c779ce 100644 --- a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/WhitelistServiceForTest.java +++ b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/WhitelistServiceForTest.java @@ -21,8 +21,8 @@ public class WhitelistServiceForTest { .build(); } - String whitelistTransaction(WhitelistPayload whitelistPayload) throws Exception { - RequestBody requestBody = RequestBody.create(JSON, toJson(whitelistPayload)); + String whitelistTransaction(String txPayload, String passphrase) throws Exception { + RequestBody requestBody = RequestBody.create(JSON, toJson(txPayload, passphrase)); okhttp3.Request request = new Request.Builder() .url(IntegConsts.URL_WHITELISTING_SERVICE) .post(requestBody) @@ -40,10 +40,10 @@ String whitelistTransaction(WhitelistPayload whitelistPayload) throws Exception return whitelist; } - private String toJson(WhitelistPayload whitelistPayload) throws JSONException { + private String toJson(String txPayload, String passphrase) throws JSONException { JSONObject jo = new JSONObject(); - jo.put("envelop", whitelistPayload.getTransactionPayload()); - jo.put("network_id", whitelistPayload.getNetworkPassphrase()); + jo.put("envelop", txPayload); + jo.put("network_id", passphrase); return jo.toString(); } 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..bf56fe04 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 @@ -208,7 +208,8 @@ class KinAccountIntegrationTest { expectedEx.expectMessage(kinAccountSender.publicAddress) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) - val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistPayload()) + val whitelist = WhitelistServiceForTest().whitelistTransaction( + transaction.whitelistableTransaction.transactionPayload, transaction.whitelistableTransaction.networkPassphrase) kinAccountSender.sendWhitelistTransactionSync(whitelist) } @@ -225,7 +226,8 @@ class KinAccountIntegrationTest { expectedEx.expectMessage(kinAccountReceiver.publicAddress) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee) - val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistPayload()) + val whitelist = WhitelistServiceForTest().whitelistTransaction( + transaction.whitelistableTransaction.transactionPayload, transaction.whitelistableTransaction.networkPassphrase) kinAccountSender.sendWhitelistTransactionSync(whitelist) } @@ -251,7 +253,8 @@ class KinAccountIntegrationTest { val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("20"), minFee + 100000) - val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistPayload()) + val whitelist = WhitelistServiceForTest().whitelistTransaction( + transaction.whitelistableTransaction.transactionPayload, transaction.whitelistableTransaction.networkPassphrase) kinAccountSender.sendWhitelistTransactionSync(whitelist) assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("80.00000"))) } @@ -265,7 +268,8 @@ class KinAccountIntegrationTest { val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync) val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("20"), minFee) - val whitelist = WhitelistServiceForTest().whitelistTransaction(transaction.whitelistPayload()) + val whitelist = WhitelistServiceForTest().whitelistTransaction( + transaction.whitelistableTransaction.transactionPayload, transaction.whitelistableTransaction.networkPassphrase) kinAccountSender.sendWhitelistTransactionSync(whitelist) assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("80.00000"))) assertThat(kinAccountReceiver.balanceSync.value(), equalTo(BigDecimal("20.00000"))) 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 d9d239f5..14551c61 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 @@ -22,7 +22,7 @@ class PaymentQueueManagerIntegrationTest { private var eventsManager = EventsManagerImpl() private var queueScheduler = FakeIntegrationTestQueueScheduler() - private var txTaskQueueManager = TransactionTasksQueueManagerImpl(eventsManager) + private var txTaskQueueManager = TransactionTasksQueueManagerImpl(null, eventsManager, 10) private var pendingBalanceUpdater = PendingBalanceUpdaterImpl() private lateinit var paymentQueueManager: PaymentQueueManager 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 index bcdf7c2d..41b0d9fc 100644 --- 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 @@ -6,7 +6,7 @@ import org.junit.Test class TasksQueueIntegrationTest { - @Test + /*@Test fun test() { //given val tasksQueue = TasksQueueImpl() @@ -25,6 +25,6 @@ class TasksQueueIntegrationTest { Thread.sleep(40000) //then assertThat(value, equalTo(10)) - } + }*/ } \ No newline at end of file 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 13fa9f3d..12614f9f 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 @@ -69,7 +69,7 @@ public void sendTransactionSync() throws Exception { BigDecimal expectedAmount = new BigDecimal("12.2"); TransactionId expectedTransactionId = new TransactionIdImpl("myId"); - when(mockTransactionSender.sendTransaction((PaymentTransaction) any())).thenReturn(expectedTransactionId); + when(mockTransactionSender.sendTransaction((Transaction) any())).thenReturn(expectedTransactionId); Transaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); @@ -87,7 +87,7 @@ public void sendTransactionSync_WithMemo() throws Exception { TransactionId expectedTransactionId = new TransactionIdImpl("myId"); String memo = "Dummy Memo"; - when(mockTransactionSender.sendTransaction((PaymentTransaction) any())).thenReturn(expectedTransactionId); + when(mockTransactionSender.sendTransaction((Transaction) any())).thenReturn(expectedTransactionId); Transaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100, memo); TransactionId transactionId = kinAccount.sendTransactionSync(transaction); diff --git a/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderOldAPITest.java b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderOldAPITest.java new file mode 100644 index 00000000..c1029fdc --- /dev/null +++ b/kin-sdk/kin-sdk-lib/src/test/java/kin/sdk/TransactionSenderOldAPITest.java @@ -0,0 +1,467 @@ +package kin.sdk; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.fail; +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; + +import java.io.IOException; +import java.math.BigDecimal; +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 okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.SocketPolicy; +import org.hamcrest.beans.HasPropertyWithValue; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class TransactionSenderOldAPITest { + + private static final String ACCOUNT_ID_FROM = "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX"; + private static final String SECRET_SEED_FROM = "SB6PCLT2WUQF44HVOTEGCXIDYNX2U4BJUPWUX453ODRGD4CXGPJP3HUX"; + private static final String ACCOUNT_ID_TO = "GDJOJJVIWI6YVPUI3PX4BQCC4SQUZTRYIAMV2YBT6QVL54QGQUQSFKGM"; + private static final String SECRET_SEED_TO = "SCJFLXKUY6VQT2LYSP6XDP23WNEP5OITSC3LZEJUJO7GFZM7QLDF2BCN"; + private static final String TX_BODY = "tx=AAAAANSQMFM2TD8pn4hIhHoUwA8IUMSN1M2SRw31SjZtBVodAAAAZABpZ8AAAAAEAAAAAAAAAAEAAAAHMS0xYTJjLQAAAAABAAAAAAAAAAEAAAAA0uSmqLI9ir6I2%2B%2FAwELkoUzOOEAZXWAz9Cq%2B8gaFISIAAAAAAAAAAAACSfAAAAAAAAAAAW0FWh0AAABAl5hewshgOkfTHymdIJTtIK2L%2FUpDcznSSGW3tZPRRGdmnCfOdTnZcfhDkWna0w14W1oVP2GzfZy3GrJPkb2XCA%3D%3D"; + private static final String TX_BODY_WITH_MEMO = "tx=AAAAANSQMFM2TD8pn4hIhHoUwA8IUMSN1M2SRw31SjZtBVodAAAAZABpZ8AAAAAEAAAAAAAAAAEAAAAQMS0xYTJjLWZha2UgbWVtbwAAAAEAAAAAAAAAAQAAAADS5Kaosj2Kvojb78DAQuShTM44QBldYDP0Kr7yBoUhIgAAAAAAAAAAATEtAAAAAAAAAAABbQVaHQAAAEA8ATZESAblmH%2BOaT5RZbdmlv3IgqV7HPkiHGva%2Bhcqv4dNancWW8PcprGwGMUlEU0GqlbszOLXyR1DsQlIcTEC"; + private static final String APP_ID = "1a2c"; + private static final int FEE = 100; + + @Rule + public ExpectedException expectedEx = ExpectedException.none(); + + @Mock + private KeyStore mockKeyStore; + private Server server; + private MockWebServer mockWebServer; + private TransactionSender transactionSender; + private KeyPair account; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + mockServer(); + Network.useTestNetwork(); + + transactionSender = new TransactionSender(server, APP_ID); + account = KeyPair.fromSecretSeed(SECRET_SEED_FROM); + } + + private void mockServer() throws IOException { + mockWebServer = new MockWebServer(); + mockWebServer.start(); + String url = mockWebServer.url("").toString(); + server = new Server(url); + } + + @Test + public void sendTransaction_success() throws Exception { + //send transaction fetch first to account details, then from account details, and finally perform tx, + //here we mock all 3 responses from server to achieve success operation + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + TransactionId transactionId = transactionSender.sendTransaction(transaction); + + assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); + + //verify sent requests data + assertThat(mockWebServer.takeRequest().getRequestUrl().toString(), containsString(ACCOUNT_ID_FROM)); + assertThat(mockWebServer.takeRequest().getRequestUrl().toString(), containsString(ACCOUNT_ID_TO)); + assertThat(mockWebServer.takeRequest().getBody().readUtf8(), equalTo(TX_BODY)); + } + + @Test + public void sendTransaction_WithMemo_success() throws Exception { + //send transaction fetch first to account details, then from account details, and finally perform tx, + //here we mock all 3 responses from server to achieve success operation + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); + String fakeMemo = "fake memo"; + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, fakeMemo); + TransactionId transactionId = transactionSender.sendTransaction(transaction); + + assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); + + //verify sent requests data + assertThat(mockWebServer.takeRequest().getRequestUrl().toString(), containsString(ACCOUNT_ID_FROM)); + assertThat(mockWebServer.takeRequest().getRequestUrl().toString(), containsString(ACCOUNT_ID_TO)); + assertThat(mockWebServer.takeRequest().getBody().readUtf8(), equalTo(TX_BODY_WITH_MEMO)); + } + + @Test + public void sendTransaction_ToAccountNotExist() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(new MockResponse().setResponseCode(404)); + + expectedEx.expect(AccountNotFoundException.class); + expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_TO))); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_Http_307_Response_Success() throws Exception { + MockWebServer mockWebServerHttp307 = new MockWebServer(); + mockWebServerHttp307.start(); + String location = mockWebServerHttp307.url("").toString(); + + //send transaction fetch first to account details, then from account details, and finally perform tx which will return 307 + // and then 200. here we mock all 4 responses from server to achieve success operation + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + // No need for a real location because any way it is local host + mockWebServer + .enqueue(TestUtils.generateSuccessHttp307MockResponse(location)); + mockWebServerHttp307.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_success_res.json")); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + TransactionId transactionId = transactionSender.sendTransaction(transaction); + + assertEquals("8f1e0cd1d922f4c57cc1898ececcf47375e52ec4abf77a7e32d0d9bb4edecb69", transactionId.id()); + + //verify sent requests data + assertThat(mockWebServer.takeRequest().getRequestUrl().toString(), containsString(ACCOUNT_ID_FROM)); + assertThat(mockWebServer.takeRequest().getRequestUrl().toString(), containsString(ACCOUNT_ID_TO)); + assertThat(mockWebServer.takeRequest().getBody().readUtf8(), equalTo(TX_BODY)); + assertThat(mockWebServerHttp307.takeRequest().getBody().readUtf8(), equalTo(TX_BODY)); + } + + @Test + public void sendTransaction_FromAccountNotExist() throws Exception { + mockWebServer.enqueue(new MockResponse().setResponseCode(404)); + + expectedEx.expect(AccountNotFoundException.class); + expectedEx.expect(new HasPropertyWithValue<>("accountId", equalTo(ACCOUNT_ID_FROM))); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("1.5"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_GeneralStellarError() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(new MockResponse() + .setBody(TestUtils.loadResource(this.getClass(), "tx_failure_res_general_stellar_error.json")) + .setResponseCode(400) + ); + + expectedEx.expect(TransactionFailedException.class); + expectedEx.expect(new HasPropertyWithValue<>("transactionResultCode", equalTo("tx_failed"))); + expectedEx.expect(new HasPropertyWithValue<>("operationsResultCodes", contains("op_malformed"))); + expectedEx.expect(new HasPropertyWithValue<>("operationsResultCodes", hasSize(1))); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + + @Test + public void sendTransaction_Underfunded_InsufficientKinException() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(new MockResponse() + .setBody(TestUtils.loadResource(this.getClass(), "tx_failure_res_underfunded.json")) + .setResponseCode(400) + ); + + expectedEx.expect(InsufficientKinException.class); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_InsufficientFeeException() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(new MockResponse() + .setBody(TestUtils.loadResource(this.getClass(), "tx_failure_res_insufficient_fee.json")) + .setResponseCode(400) + ); + + expectedEx.expect(InsufficientFeeException.class); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_InsufficientBalanceException() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(new MockResponse() + .setBody(TestUtils.loadResource(this.getClass(), "tx_failure_res_insufficient_balance.json")) + .setResponseCode(400) + ); + + expectedEx.expect(InsufficientKinException.class); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_FirstQuery_HttpResponseError() throws Exception { + mockWebServer.enqueue(new MockResponse().setResponseCode(500)); + + testHttpResponseCode(500); + } + + @Test + public void sendTransaction_SecondQuery_HttpResponseError() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + mockWebServer.enqueue(new MockResponse() + .setResponseCode(500) + ); + + testHttpResponseCode(500); + } + + @Test + public void sendTransaction_FirstQuery_ConnectionException() throws Exception { + mockWebServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); + + expectedEx.expect(OperationFailedException.class); + expectedEx.expectCause(isA(IOException.class)); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_SecondQuery_ConnectionException() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + mockWebServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); + + expectedEx.expect(OperationFailedException.class); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_ThirdQuery_ConnectionException() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + mockWebServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); + + expectedEx.expect(OperationFailedException.class); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test(timeout = 500) + public void sendTransaction_changeTimeOut() throws Exception { + String url = mockWebServer.url("").toString(); + server = new Server(url, 100, TimeUnit.MILLISECONDS); + transactionSender = new TransactionSender(server, APP_ID); + + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + mockWebServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.NO_RESPONSE)); + + expectedEx.expect(OperationFailedException.class); + expectedEx.expectCause(isA(SocketTimeoutException.class)); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_FirstQuery_NullResponse() throws Exception { + TestUtils.enqueueEmptyResponse(mockWebServer); + + expectedEx.expect(OperationFailedException.class); + expectedEx.expectMessage(ACCOUNT_ID_FROM); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_SecondQuery_NullResponse() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + TestUtils.enqueueEmptyResponse(mockWebServer); + + expectedEx.expect(OperationFailedException.class); + expectedEx.expectMessage(ACCOUNT_ID_TO); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_ThirdQuery_NullResponse() throws Exception { + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_from.json")); + mockWebServer.enqueue(TestUtils.generateSuccessMockResponse(this.getClass(), "tx_account_to.json")); + TestUtils.enqueueEmptyResponse(mockWebServer); + + expectedEx.expect(OperationFailedException.class); + expectedEx.expectMessage("transaction"); + + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + @SuppressWarnings("ConstantConditions") + 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)"); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE, tooLongMemo); + transactionSender.sendTransaction(transaction); + assertThat(mockWebServer.getRequestCount(), equalTo(0)); + } + + @Test + @SuppressWarnings("ConstantConditions") + public void sendTransaction_NullAccount() throws Exception { + expectedEx.expect(IllegalArgumentException.class); + expectedEx.expectMessage("account"); + Transaction transaction = transactionSender.buildTransaction(null, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + @SuppressWarnings("ConstantConditions") + public void sendTransaction_NullPublicAddress() throws Exception { + expectedEx.expect(IllegalArgumentException.class); + expectedEx.expectMessage("public address"); + Transaction transaction = transactionSender.buildTransaction(account, null, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + @SuppressWarnings("ConstantConditions") + public void sendTransaction_NullAmount() throws Exception { + expectedEx.expect(IllegalArgumentException.class); + expectedEx.expectMessage("amount"); + Transaction transaction = transactionSender.buildTransaction(account, ACCOUNT_ID_TO, null, FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_EmptyPublicAddress() throws Exception { + expectedEx.expect(IllegalArgumentException.class); + expectedEx.expectMessage("public address"); + Transaction transaction = transactionSender.buildTransaction(account, "", new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_NegativeAmount() throws Exception { + expectedEx.expect(IllegalArgumentException.class); + expectedEx.expectMessage("Amount"); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("-200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_NegativeFee() throws Exception { + expectedEx.expect(IllegalArgumentException.class); + expectedEx.expectMessage("Fee"); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), -FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + public void sendTransaction_AmountExceedNumOfDecimalPlaces() throws Exception { + expectedEx.expect(IllegalAmountException.class); + expectedEx.expectMessage("amount can't have more then 5 digits after the decimal point"); + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("20.012345"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + @SuppressWarnings("ConstantConditions") + public void sendTransaction_InvalidPublicIdLength() throws Exception { + expectedEx.expect(OperationFailedException.class); + expectedEx.expectCause(isA(FormatException.class)); + expectedEx.expectMessage("public address"); + Transaction transaction = transactionSender.buildTransaction(account, "ABCDEF", new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @Test + @SuppressWarnings("ConstantConditions") + public void sendTransaction_InvalidChecksumPublicId() throws Exception { + expectedEx.expect(OperationFailedException.class); + expectedEx.expectCause(isA(FormatException.class)); + expectedEx.expectMessage("public address"); + Transaction transaction = transactionSender + .buildTransaction(account, "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OG3", + new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + } + + @SuppressWarnings("SameParameterValue") + private void testHttpResponseCode(int resCode) { + try { + Transaction transaction = transactionSender + .buildTransaction(account, ACCOUNT_ID_TO, new BigDecimal("200"), FEE); + transactionSender.sendTransaction(transaction); + fail("Expected OperationFailedException"); + } catch (Exception ex) { + Assert.assertThat(ex, is(instanceOf(OperationFailedException.class))); + Assert.assertThat(ex.getCause(), is(instanceOf(HttpResponseException.class))); + Assert.assertThat(((HttpResponseException) ex.getCause()).getStatusCode(), is(resCode)); + } + } + +} \ No newline at end of file