diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 59832005..be6a9923 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,4 +1,4 @@
-#Tue Sep 03 16:14:18 IDT 2019
+#Tue Aug 13 08:37:24 IDT 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/kin-sdk/kin-sdk-lib/build.gradle b/kin-sdk/kin-sdk-lib/build.gradle
index 17d609a2..95118798 100644
--- a/kin-sdk/kin-sdk-lib/build.gradle
+++ b/kin-sdk/kin-sdk-lib/build.gradle
@@ -50,27 +50,26 @@ dependencies {
api 'com.github.kinecosystem:kin-utils-android:1.1'
testImplementation 'junit:junit:4.12'
- testImplementation 'org.mockito:mockito-core:2.27.0'
+ testImplementation 'org.mockito:mockito-core:2.25.0'
testImplementation 'org.hamcrest:hamcrest-library:1.3'
testImplementation 'com.squareup.okhttp3:mockwebserver:3.9.1'
testImplementation 'org.robolectric:robolectric:4.3'
testImplementation 'com.google.code.gson:gson:2.8.5'
testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
- testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0'
+ testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0'
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
androidTestImplementation 'junit:junit:4.12'
- androidTestImplementation 'org.mockito:mockito-core:2.27.0'
+ androidTestImplementation 'org.mockito:mockito-core:2.25.0'
+ androidTestImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0'
androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
- androidTestImplementation 'org.mockito:mockito-android:2.10.0'
- androidTestImplementation 'org.mockito:mockito-android:2.10.0'
+ androidTestImplementation 'org.mockito:mockito-android:2.25.0'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
androidTestImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
- androidTestImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0'
}
//bundle javadocs with published aar
diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java
index 501aa818..a42915bd 100644
--- a/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java
+++ b/kin-sdk/kin-sdk-lib/src/androidTest/java/kin/sdk/IntegConsts.java
@@ -1,17 +1,18 @@
package kin.sdk;
-final class IntegConsts {
+public final class IntegConsts {
- static final String TEST_NETWORK_URL = BuildConfig.INTEG_TESTS_NETWORK_URL.isEmpty() ?
+ public static final String TEST_NETWORK_URL = BuildConfig.INTEG_TESTS_NETWORK_URL.isEmpty() ?
Environment.TEST.getNetworkUrl() : BuildConfig.INTEG_TESTS_NETWORK_URL;
- static final String TEST_NETWORK_ID = BuildConfig.INTEG_TESTS_NETWORK_PASSPHRASE.isEmpty() ?
+ public static final String TEST_NETWORK_ID =
+ BuildConfig.INTEG_TESTS_NETWORK_PASSPHRASE.isEmpty() ?
Environment.TEST.getNetworkPassphrase() : BuildConfig.INTEG_TESTS_NETWORK_PASSPHRASE;
- static final String FRIENDBOT_URL = BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT.isEmpty() ?
+ public static final String FRIENDBOT_URL = BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT.isEmpty() ?
"https://friendbot-testnet.kininfrastructure.com" : BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT;
- static final String URL_CREATE_ACCOUNT = FRIENDBOT_URL + "?addr=%s&amount=%d";
- static final String URL_FUND = FRIENDBOT_URL + "/fund?addr=%s&amount="; // faucet
- static final String URL_WHITELISTING_SERVICE = (BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT.isEmpty() ?
+ public static final String URL_CREATE_ACCOUNT = FRIENDBOT_URL + "?addr=%s&amount=%d";
+ public static final String URL_FUND = FRIENDBOT_URL + "/fund?addr=%s&amount="; // faucet
+ public static final String URL_WHITELISTING_SERVICE = (BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT.isEmpty() ?
"http://34.239.111.38:3000" : BuildConfig.INTEG_TESTS_NETWORK_FRIENDBOT)
+ "/whitelist";
}
diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt
index b3635188..faa99c54 100644
--- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt
+++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/FakeKinOnBoard.kt
@@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit
/**
* Fake on board for integration test, support creating and funding accounts on stellar test net
*/
-internal class FakeKinOnBoard @Throws(IOException::class)
+class FakeKinOnBoard @Throws(IOException::class)
constructor() {
private val client: OkHttpClient = OkHttpClient.Builder()
diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/IntegrationTestKinClientInjector.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/IntegrationTestKinClientInjector.kt
new file mode 100644
index 00000000..ff6b24e5
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/IntegrationTestKinClientInjector.kt
@@ -0,0 +1,28 @@
+package kin.sdk
+
+import android.content.Context
+import kin.sdk.internal.blockchain.TransactionSender
+import kin.sdk.internal.queue.PaymentQueueImpl
+
+class IntegrationTestKinClientInjector(context: Context?, environment: Environment?, appId: String?, storeKey: String?,
+ private val txSender: TransactionSender? = null,
+ private val configuration: PaymentQueueImpl.PaymentQueueConfiguration?) :
+ KinClientInjector(context, environment, appId, storeKey) {
+
+
+ override fun getPaymentQueueConfiguration(): PaymentQueueImpl.PaymentQueueConfiguration {
+ return configuration
+ ?: PaymentQueueImpl.PaymentQueueConfiguration(Constants.delayBetweenPaymentsMillis, Constants.queueTimeoutMillis, Constants.maxNumOfPayments)
+ }
+
+ override fun getTransactionSender(): TransactionSender {
+ return txSender ?: super.getTransactionSender()
+ }
+
+ object Constants {
+ const val delayBetweenPaymentsMillis: Long = 250
+ const val queueTimeoutMillis: Long = 2000
+ const val maxNumOfPayments: Int = 5
+ }
+
+}
\ No newline at end of file
diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt
index 435224bf..06e34a09 100644
--- a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt
+++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/KinAccountIntegrationTest.kt
@@ -30,7 +30,6 @@ class KinAccountIntegrationTest {
private val appId = "1a2c"
private val fee: Int = 100
private val feeInKin: BigDecimal = BigDecimal.valueOf(0.001)
- private val appIdVersionPrefix = "1"
private val timeoutDurationSeconds: Long = 15
private val timeoutDurationSecondsLong: Long = 20
@@ -106,10 +105,10 @@ class KinAccountIntegrationTest {
@LargeTest
@Throws(Exception::class)
fun sendTransaction_WithMemo() {
- val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100)
+ val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100)
val memo = "fake memo"
- val expectedMemo = addAppIdToMemo(memo)
+ val expectedMemo = addAppIdToMemo(memo, appId)
val transactionId = sendTransactionAndAssert(kinAccountSender, kinAccountReceiver, memo)
@@ -123,8 +122,8 @@ class KinAccountIntegrationTest {
@LargeTest
@Throws(Exception::class)
fun sendTransaction_WithoutMemoJustPrefix() {
- val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100)
- val expectedMemo = addAppIdToMemo("")
+ val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100)
+ val expectedMemo = addAppIdToMemo("", appId)
val transactionId = sendTransactionAndAssert(kinAccountSender, kinAccountReceiver, null)
val server = Server(TEST_NETWORK_URL)
val transactionResponse = server.transactions().transaction(transactionId.id())
@@ -136,10 +135,8 @@ class KinAccountIntegrationTest {
@LargeTest
@Throws(Exception::class)
fun sendTransaction_WithoutMemo() {
- val (kinAccountSender, kinAccountReceiver) = onboardAccounts(
- senderFundAmount = 100,
- kinClient = KinClient(InstrumentationRegistry.getTargetContext(), environment, null))
-
+ val (kinAccountSender, kinAccountReceiver) = onboardAccounts(KinClient(InstrumentationRegistry.getTargetContext(), environment, null),
+ fakeKinOnBoard, senderFundAmount = 100)
val transactionId = sendTransactionAndAssert(kinAccountSender, kinAccountReceiver, null)
val server = Server(TEST_NETWORK_URL)
val transactionResponse = server.transactions().transaction(transactionId.id())
@@ -151,10 +148,8 @@ class KinAccountIntegrationTest {
@LargeTest
@Throws(Exception::class)
fun sendTransaction_WithoutMemoPrefix() {
- val (kinAccountSender, kinAccountReceiver) = onboardAccounts(
- senderFundAmount = 100,
- kinClient = KinClient(InstrumentationRegistry.getTargetContext(), environment, null))
-
+ val (kinAccountSender, kinAccountReceiver) = onboardAccounts(KinClient(InstrumentationRegistry.getTargetContext(), environment, null),
+ fakeKinOnBoard, senderFundAmount = 100)
val memo = "fake memo"
val transactionId = sendTransactionAndAssert(kinAccountSender, kinAccountReceiver, memo)
val server = Server(TEST_NETWORK_URL)
@@ -234,7 +229,7 @@ class KinAccountIntegrationTest {
@LargeTest
@Throws(Exception::class)
fun sendTransaction_NotEnoughFee_InsufficientFeeException() {
- val (kinAccountSender, kinAccountReceiver) = onboardAccounts()
+ val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard)
expectedEx.expect(InsufficientFeeException::class.java)
val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync)
@@ -246,7 +241,7 @@ class KinAccountIntegrationTest {
@LargeTest
@Throws(Exception::class)
fun sendWhitelistTransaction_FeeNotReduce() {
- val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100)
+ val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100)
val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync)
val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(),
@@ -260,7 +255,7 @@ class KinAccountIntegrationTest {
@LargeTest
@Throws(Exception::class)
fun sendWhitelistTransaction_Success() {
- val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100)
+ val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100)
val minFee: Int = Math.toIntExact(kinClient.minimumFeeSync)
val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(),
@@ -275,7 +270,7 @@ class KinAccountIntegrationTest {
@LargeTest
@Throws(Exception::class)
fun sendTransaction_Success() {
- val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100)
+ val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100)
val transactionId = sendTransactionAndAssert(kinAccountSender, kinAccountReceiver, "fake memo")
assertNotNull(transactionId)
assertThat(transactionId.id(), not(isEmptyString()))
@@ -302,7 +297,7 @@ class KinAccountIntegrationTest {
val fundingAmount = BigDecimal("100")
val transactionAmount = BigDecimal("21.123")
- val (kinAccountSender, kinAccountReceiver) = onboardAccounts(0, 0)
+ val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, 0, 0)
//register listeners for testing
val actualPaymentsResults = ArrayList()
@@ -324,7 +319,7 @@ class KinAccountIntegrationTest {
//send the transaction we want to observe
fakeKinOnBoard.fundWithKin(kinAccountSender.publicAddress.orEmpty(), "100")
val memo = "memo"
- val expectedMemo = addAppIdToMemo(memo)
+ val expectedMemo = addAppIdToMemo(memo, appId)
val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), transactionAmount, fee, memo)
val expectedTransactionId = kinAccountSender.sendTransactionSync(transaction)
@@ -351,7 +346,7 @@ class KinAccountIntegrationTest {
@LargeTest
@Throws(Exception::class)
fun createPaymentListener_RemoveListener_NoEvents() {
- val (kinAccountSender, kinAccountReceiver) = onboardAccounts(senderFundAmount = 100)
+ val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard, senderFundAmount = 100)
val latch = CountDownLatch(1)
val listenerRegistration = kinAccountReceiver.addPaymentListener {
@@ -368,27 +363,13 @@ class KinAccountIntegrationTest {
@LargeTest
@Throws(Exception::class)
fun sendTransaction_NotEnoughKin_InsufficientKinException() {
- val (kinAccountSender, kinAccountReceiver) = onboardAccounts()
+ val (kinAccountSender, kinAccountReceiver) = onboardAccounts(kinClient, fakeKinOnBoard)
expectedEx.expect(InsufficientKinException::class.java)
val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal("21.123"), fee)
kinAccountSender.sendTransactionSync(transaction)
}
- private fun onboardAccounts(senderFundAmount: Int = 0,
- receiverFundAmount: Int = 0,
- kinClient: KinClient = this.kinClient): Pair {
- val kinAccountSender = kinClient.addAccount()
- val kinAccountReceiver = kinClient.addAccount()
- fakeKinOnBoard.createAccount(kinAccountSender.publicAddress.orEmpty(), senderFundAmount)
- fakeKinOnBoard.createAccount(kinAccountReceiver.publicAddress.orEmpty(), receiverFundAmount)
- return Pair(kinAccountSender, kinAccountReceiver)
- }
-
- private fun addAppIdToMemo(memo: String): String {
- return appIdVersionPrefix.plus("-").plus(appId).plus("-").plus(memo)
- }
-
private fun sendTransactionAndAssert(kinAccountSender: KinAccount, kinAccountReceiver: KinAccount, memo: String?): TransactionId {
val transaction = kinAccountSender.buildTransactionSync(kinAccountReceiver.publicAddress.orEmpty(),
BigDecimal("21.123"), fee, memo)
diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/Utils.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/Utils.kt
new file mode 100644
index 00000000..aeaf50d5
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/Utils.kt
@@ -0,0 +1,29 @@
+package kin.sdk
+
+import android.support.test.InstrumentationRegistry
+import kin.sdk.internal.blockchain.TransactionSender
+import kin.sdk.internal.queue.PaymentQueueImpl
+
+
+private const val appIdVersionPrefix = "1"
+
+fun onboardAccounts(kinClient: KinClient, fakeKinOnBoard: FakeKinOnBoard, senderFundAmount: Int = 0,
+ receiverFundAmount: Int = 0): Pair {
+ val kinAccountSender = kinClient.addAccount()
+ val kinAccountReceiver = kinClient.addAccount()
+ fakeKinOnBoard.createAccount(kinAccountSender.publicAddress.orEmpty(), senderFundAmount)
+ fakeKinOnBoard.createAccount(kinAccountReceiver.publicAddress.orEmpty(), receiverFundAmount)
+ return Pair(kinAccountSender, kinAccountReceiver)
+}
+
+fun addAppIdToMemo(memo: String, appId: String): String {
+ return appIdVersionPrefix.plus("-").plus(appId).plus("-").plus(memo)
+}
+
+fun getPaymentQueueTestKinClient(environment: Environment, appId: String, txSender: TransactionSender? = null,
+ configuration: PaymentQueueImpl.PaymentQueueConfiguration? = null): KinClient {
+ val injector = IntegrationTestKinClientInjector(InstrumentationRegistry.getTargetContext(),
+ environment, appId, "", txSender, configuration)
+ return KinClient(injector)
+}
+
diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueEndToEndIntegrationTest.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueEndToEndIntegrationTest.kt
new file mode 100644
index 00000000..345ce4ab
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueEndToEndIntegrationTest.kt
@@ -0,0 +1,174 @@
+package kin.sdk.internal.queue
+
+import kin.base.MemoText
+import kin.base.Server
+import kin.sdk.*
+import kin.sdk.exception.KinException
+import kin.sdk.queue.PaymentQueue
+import kin.sdk.queue.PaymentQueueTransactionProcess
+import kin.sdk.queue.PendingPayment
+import kin.sdk.transactiondata.BatchPaymentTransaction
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.equalTo
+import org.junit.After
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import java.io.IOException
+import java.math.BigDecimal
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlin.test.assertTrue
+import kotlin.test.fail
+
+
+class PaymentQueueEndToEndIntegrationTest {
+
+ private val appId = "1a2c"
+ private val fee: Int = 100
+ private val feeInKin: BigDecimal = BigDecimal.valueOf(0.001)
+ private val timeoutDurationSecondsLong: Long = 20
+ private val environment: Environment = Environment(IntegConsts.TEST_NETWORK_URL, IntegConsts.TEST_NETWORK_ID)
+ private lateinit var kinClient: KinClient
+
+
+ @Before
+ fun setup() {
+ kinClient = getPaymentQueueTestKinClient(environment, appId)
+ kinClient.clearAllAccounts()
+ }
+
+ @After
+ fun teardown() {
+ if (::kinClient.isInitialized) {
+ kinClient.clearAllAccounts()
+ }
+ }
+
+ @Test
+ fun enqueuePayments_DelayBetweenPayments_Success() {
+ val amount = BigDecimal(50)
+ val kinAccountSender = kinClient.addAccount()
+ fakeKinOnBoard.createAccount(kinAccountSender.publicAddress.orEmpty(), 250)
+ val kinAccountReceiver1 = addAndCreateAccount()
+ val kinAccountReceiver2 = addAndCreateAccount()
+ val kinAccountReceiver3 = addAndCreateAccount()
+
+ val latch = CountDownLatch(1)
+ val paymentQueue = kinAccountSender.paymentQueue()
+ setEventListener(paymentQueue, latch)
+
+ paymentQueue.setFee(fee)
+ paymentQueue.enqueuePayment(kinAccountReceiver2.publicAddress.orEmpty(), amount, "2")
+ paymentQueue.enqueuePayment(kinAccountReceiver1.publicAddress.orEmpty(), amount, "1")
+ paymentQueue.enqueuePayment(kinAccountReceiver3.publicAddress.orEmpty(), amount, "3")
+ paymentQueue.enqueuePayment(kinAccountReceiver1.publicAddress.orEmpty(), amount, "4")
+ Thread.sleep(50)
+ assertThat(paymentQueue.pendingPaymentsCount(), equalTo(4))
+
+ assertTrue(latch.await(timeoutDurationSecondsLong, TimeUnit.SECONDS))
+
+ assertThat(kinAccountReceiver1.balanceSync.value(), equalTo(BigDecimal("100.00000")))
+ assertThat(kinAccountReceiver2.balanceSync.value(), equalTo(BigDecimal("50.00000")))
+ assertThat(kinAccountReceiver3.balanceSync.value(), equalTo(BigDecimal("50.00000")))
+ assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("50.00000").subtract(feeInKin.multiply(BigDecimal(4)))))
+ }
+
+ @Test
+ fun enqueuePayments_MaxQueueElements_Intercept_Success() {
+ val amount = BigDecimal(50)
+ val pendingPayments = mutableListOf()
+ val kinAccountSender = kinClient.addAccount()
+ fakeKinOnBoard.createAccount(kinAccountSender.publicAddress.orEmpty(), 350)
+
+ val kinAccountReceiver1 = addAndCreateAccount()
+ val kinAccountReceiver2 = addAndCreateAccount()
+ val kinAccountReceiver3 = addAndCreateAccount()
+
+ val latch = CountDownLatch(1)
+ val paymentQueue = kinAccountSender.paymentQueue()
+ setEventListener(paymentQueue, latch)
+
+ paymentQueue.setFee(fee)
+
+ val memo = "fake memo"
+ val expectedMemo = addAppIdToMemo(memo, appId)
+
+
+ var transactionId: TransactionId? = null
+ paymentQueue.setTransactionInterceptor(object : TransactionInterceptor {
+ override fun interceptTransactionSending(process: PaymentQueueTransactionProcess?): TransactionId? {
+ val transaction = process?.transaction(memo)
+ // simulate some work
+ Thread.sleep(5000)
+ transactionId = process?.send(transaction)
+ return transactionId
+ }
+ })
+
+ paymentQueue.enqueuePayment(kinAccountReceiver1.publicAddress.orEmpty(), amount, "1")
+ paymentQueue.enqueuePayment(kinAccountReceiver2.publicAddress.orEmpty(), amount, "2")
+ paymentQueue.enqueuePayment(kinAccountReceiver3.publicAddress.orEmpty(), amount, "3")
+ paymentQueue.enqueuePayment(kinAccountReceiver1.publicAddress.orEmpty(), amount, "4")
+ paymentQueue.enqueuePayment(kinAccountReceiver3.publicAddress.orEmpty(), amount, "5")
+ Thread.sleep(100)
+ assertThat(paymentQueue.pendingPaymentsCount(), equalTo(0))
+
+ assertTrue(latch.await(timeoutDurationSecondsLong, TimeUnit.SECONDS))
+
+ assertThat(kinAccountReceiver1.balanceSync.value(), equalTo(BigDecimal("100.00000")))
+ assertThat(kinAccountReceiver2.balanceSync.value(), equalTo(BigDecimal("50.00000")))
+ assertThat(kinAccountReceiver3.balanceSync.value(), equalTo(BigDecimal("100.00000")))
+ assertThat(kinAccountSender.balanceSync.value(), equalTo(BigDecimal("100.00000").subtract(feeInKin.multiply(BigDecimal(5)))))
+
+ val server = Server(IntegConsts.TEST_NETWORK_URL)
+ val transactionResponse = server.transactions().transaction(transactionId?.id())
+ val actualMemo = transactionResponse.memo
+ assertThat((actualMemo as MemoText).text, equalTo(expectedMemo))
+ }
+
+
+ private fun setEventListener(paymentQueue: PaymentQueue, latch: CountDownLatch) {
+ paymentQueue.setEventListener(object : PaymentQueue.EventListener {
+ override fun onPaymentEnqueued(payment: PendingPayment?) {
+ }
+
+ override fun onTransactionSend(transaction: BatchPaymentTransaction?, payments: MutableList?) {
+ assertThat(paymentQueue.transactionInProgress(), equalTo(true))
+ }
+
+ override fun onTransactionSendSuccess(transactionId: TransactionId?, payments: MutableList?) {
+ latch.countDown()
+ }
+
+ override fun onTransactionSendFailed(payments: MutableList?, exception: KinException?) {
+ latch.countDown()
+ }
+
+ })
+ }
+
+ //TODO include events manager and balance updater to the happy path and transaction params happy path
+
+ private fun addAndCreateAccount(): KinAccount {
+ val kinAccount = kinClient.addAccount()
+ kinAccount.publicAddress?.let {
+ fakeKinOnBoard.createAccount(it)
+ } ?: kotlin.run {
+ fail("public address is null")
+ }
+ return kinAccount
+ }
+
+ companion object {
+ private lateinit var fakeKinOnBoard: FakeKinOnBoard
+
+ @BeforeClass
+ @JvmStatic
+ @Throws(IOException::class)
+ fun setupKinOnBoard() {
+ fakeKinOnBoard = FakeKinOnBoard()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueIntegrationTests.kt b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueIntegrationTests.kt
new file mode 100644
index 00000000..70385bfc
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/androidTest/kotlin/kin/sdk/internal/queue/PaymentQueueIntegrationTests.kt
@@ -0,0 +1,193 @@
+package kin.sdk.internal.queue
+
+import com.nhaarman.mockitokotlin2.*
+import kin.sdk.*
+import kin.sdk.IntegrationTestKinClientInjector.Constants.delayBetweenPaymentsMillis
+import kin.sdk.IntegrationTestKinClientInjector.Constants.maxNumOfPayments
+import kin.sdk.IntegrationTestKinClientInjector.Constants.queueTimeoutMillis
+import kin.sdk.exception.KinException
+import kin.sdk.internal.blockchain.TransactionSender
+import kin.sdk.internal.data.TransactionIdImpl
+import kin.sdk.internal.queue.PaymentQueueIntegrationTests.CONSTANTS.APP_ID
+import kin.sdk.internal.queue.PaymentQueueIntegrationTests.CONSTANTS.FEE
+import kin.sdk.internal.queue.PaymentQueueIntegrationTests.CONSTANTS.TIMEOUT_DURATION_SECONDS_LONG
+import kin.sdk.queue.PaymentQueue
+import kin.sdk.queue.PaymentQueueTransactionProcess
+import kin.sdk.queue.PendingPayment
+import kin.sdk.transactiondata.BatchPaymentTransaction
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.*
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.`when`
+import java.math.BigDecimal
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlin.test.assertFailsWith
+import kotlin.test.assertTrue
+
+class PaymentQueueIntegrationTests {
+
+ object CONSTANTS {
+ const val APP_ID = "1a2c"
+ const val TIMEOUT_DURATION_SECONDS_LONG: Long = 20
+ const val FEE: Int = 100
+ }
+
+ private val environment: Environment = Environment(IntegConsts.TEST_NETWORK_URL, IntegConsts.TEST_NETWORK_ID)
+ private lateinit var kinClient: KinClient
+
+ private val transactionSender: TransactionSender = mock()
+ private val transactionInterceptor: TransactionInterceptor = mock()
+
+
+ @Before
+ fun setup() {
+ val paymentQueueConfiguration = PaymentQueueImpl.PaymentQueueConfiguration(50, queueTimeoutMillis, maxNumOfPayments)
+ kinClient = getPaymentQueueTestKinClient(environment, APP_ID, transactionSender, paymentQueueConfiguration)
+ kinClient.clearAllAccounts()
+ }
+
+ @After
+ fun teardown() {
+ if (::kinClient.isInitialized) {
+ kinClient.clearAllAccounts()
+ }
+ }
+
+ @Test
+ fun enqueuePayments_NegativeAmount_IllegalArgumentException() {
+ val amount = BigDecimal(-50)
+ val kinAccountSender = kinClient.addAccount()
+ val kinAccountReceiver = kinClient.addAccount()
+ val paymentQueue = kinAccountSender.paymentQueue()
+
+ assertFailsWith { paymentQueue.enqueuePayment(kinAccountReceiver.publicAddress.orEmpty(), amount) }
+ }
+
+ @Test
+ fun enqueuePayments_NegativeFee_IllegalArgumentException() {
+ val kinAccountSender = kinClient.addAccount()
+ val paymentQueue = kinAccountSender.paymentQueue()
+ assertFailsWith { paymentQueue.setFee(-FEE) }
+ }
+
+ @Test
+ fun enqueuePayments_ZeroFee_GotMinimumFee() {
+ //TODO captor the fee parameter from build and see that it is the minimum
+ val argumentCaptor = argumentCaptor()
+ val kinAccountSender = kinClient.addAccount()
+ val kinAccountReceiver = kinClient.addAccount()
+ val paymentQueue = kinAccountSender.paymentQueue()
+
+// `when`(transactionSender.buildBatchPaymentTransaction(any(), any(), any(), any())).thenReturn(null)
+
+ paymentQueue.enqueuePayment(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal(50))
+ Thread.sleep(delayBetweenPaymentsMillis + 1000)
+
+ verify(transactionSender).buildBatchPaymentTransaction(any(), any(), argumentCaptor.capture(), eq(null))
+ assertThat(kinClient.minimumFeeSync.toInt(), equalTo((argumentCaptor.firstValue)))
+ }
+
+ @Test
+ fun enqueuePayments_UseInterceptor_VerifyInterceptorCalled() {
+ val kinAccountSender = kinClient.addAccount()
+ val kinAccountReceiver = kinClient.addAccount()
+ val paymentQueue = kinAccountSender.paymentQueue()
+ paymentQueue.setTransactionInterceptor(transactionInterceptor)
+ paymentQueue.setFee(FEE)
+
+ val latch = CountDownLatch(5)
+ paymentQueue.setEventListener(object : PaymentQueue.EventListener {
+ override fun onPaymentEnqueued(payment: PendingPayment?) {
+ }
+
+ override fun onTransactionSend(transaction: BatchPaymentTransaction?, payments: MutableList?) {
+
+ }
+
+ override fun onTransactionSendSuccess(transactionId: TransactionId?, payments: MutableList?) {
+ }
+
+ override fun onTransactionSendFailed(payments: MutableList?, exception: KinException?) {
+ // this will also verify that it fails 5 times
+ latch.countDown()
+ }
+
+ })
+
+ `when`(transactionInterceptor.interceptTransactionSending(any())).thenAnswer {
+ Thread.sleep(200)
+ null
+ }
+ for (x in 0 until 5) {
+ for (y in 0 until 4) {
+ paymentQueue.enqueuePayment(kinAccountReceiver.publicAddress.orEmpty(), BigDecimal(50))
+ }
+ Thread.sleep(delayBetweenPaymentsMillis + 10)
+ }
+ assertTrue(latch.await(TIMEOUT_DURATION_SECONDS_LONG, TimeUnit.SECONDS))
+
+ verify(transactionInterceptor, times(5)).interceptTransactionSending(any())
+ }
+
+ @Test
+ fun enqueuePayments_AddMultipleTasks_TasksInvokedInCorrectOrder() {
+ // in this tests we are inserting to the task queue 5 lists of pending payments, each with 4 elements. the first one is taken but the others are not because the first one is in progress.
+ // after they merged in the task queue we have 4 lists, the top 3 with 5 elements and the last one with 1.
+ // then we check that indeed we send 5 transactions with the correct order and number of elements with their ids.
+
+ val amount = BigDecimal(50)
+ val kinAccountSender = kinClient.addAccount()
+ val kinAccountReceiver = kinClient.addAccount()
+ val paymentQueue = kinAccountSender.paymentQueue()
+ paymentQueue.setFee(FEE)
+
+ `when`(transactionSender.sendTransaction(null)).thenAnswer {
+ Thread.sleep(1000) // wait for the tasks to accumulate and merged in the task queue
+ TransactionIdImpl("fake transaction id")
+ }
+
+ val listOfListOfPayments: MutableList?> = mutableListOf()
+ val latch = CountDownLatch(5)
+ paymentQueue.setEventListener(object : PaymentQueue.EventListener {
+ override fun onPaymentEnqueued(payment: PendingPayment?) {
+ }
+
+ override fun onTransactionSend(transaction: BatchPaymentTransaction?, payments: MutableList?) {
+ listOfListOfPayments.add(payments)
+ latch.countDown()
+ }
+
+ override fun onTransactionSendSuccess(transactionId: TransactionId?, payments: MutableList?) {
+ }
+
+ override fun onTransactionSendFailed(payments: MutableList?, exception: KinException?) {
+ }
+
+ })
+
+ var idCounter = 1
+ // create 'maxNumOfPayments' lists, each one with 4 pending payments
+ for (x in 0 until maxNumOfPayments) {
+ for (y in 0 until 4) {
+ paymentQueue.enqueuePayment(kinAccountReceiver.publicAddress.orEmpty(), amount, idCounter)
+ idCounter++
+ }
+ Thread.sleep(delayBetweenPaymentsMillis + 10)
+ }
+
+ assertTrue(latch.await(TIMEOUT_DURATION_SECONDS_LONG, TimeUnit.SECONDS))
+
+ val listOfListOfIds = listOfListOfPayments.map { it?.map { pendingPayment -> pendingPayment.metadata() as Int } }
+ val expectedListOfListOfIds = listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8, 9), listOf(10, 11, 12, 13, 14), listOf(15, 16, 17, 18, 19), listOf(20))
+
+ assertThat(listOfListOfIds, hasSize(expectedListOfListOfIds.size))
+ for (i in listOfListOfIds.indices) {
+ assertThat(listOfListOfIds[i], `is`(expectedListOfListOfIds[i]))
+ }
+ }
+
+ //TODO add tests which includes testings for the events manager, balance updater and transaction params, and tests for negative/exception path (fee to low, all exceptions, etc)
+}
\ No newline at end of file
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java
index c9698e81..65f76f77 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinAccount.java
@@ -2,13 +2,20 @@
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.queue.TransactionProcess;
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 +27,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 +45,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 +64,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 +78,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 +93,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 +110,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 +131,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 +149,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.
@@ -135,28 +168,25 @@ PaymentTransaction buildTransactionSync(@NonNull String publicAddress, @NonNull
TransactionId sendWhitelistTransactionSync(String whitelist) throws OperationFailedException;
/**
- * send a transaction.
- * Note: This method accesses the network, and should not be called on the android main thread.
+ * Create {@link Request} for signing and sending a transaction
+ * See {@link KinAccount#sendTransaction(TransactionParams, TransactionInterceptor)}
+ * for possibles errors
*
- * @param 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.
+ * @param transactionParams is the transaction parameters which define the transaction object to send.
+ * @return {@code Request}, TransactionId - the transaction identifier.
*/
- TransactionId sendTransactionSync(SendTransactionParams transaction, TransactionInterceptor interceptor) throws OperationFailedException;
+ Request sendTransaction(final TransactionParams transactionParams);
/**
* Create {@link Request} for signing and sending a transaction
- * See {@link KinAccount#sendTransactionSync(PaymentTransaction)} for possibles errors
+ * 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,
+ @Nullable TransactionInterceptor interceptor);
/**
* @return the payment queue.
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java
index 748ca264..4db89676 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClient.java
@@ -4,30 +4,31 @@
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
-import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
import 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;
import kin.sdk.internal.blockchain.AccountInfoRetriever;
import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetrieverImpl;
import kin.sdk.internal.blockchain.TransactionSender;
import kin.sdk.internal.blockchain.events.BlockchainEventsCreator;
+import kin.sdk.internal.queue.PaymentQueueImpl;
import kin.sdk.internal.storage.KeyStore;
-import kin.sdk.internal.storage.KeyStoreImpl;
-import kin.sdk.internal.storage.SharedPrefStore;
+import kin.sdk.queue.PaymentQueue;
import kin.utils.Request;
-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;
/**
@@ -35,8 +36,6 @@
*/
public class KinClient {
- private static final String STORE_NAME_PREFIX = "KinKeyStore_";
- private static final int TRANSACTIONS_TIMEOUT = 30;
private final Environment environment;
private final KeyStore keyStore;
private final TransactionSender transactionSender;
@@ -46,48 +45,72 @@ public class KinClient {
private final BackupRestore backupRestore;
private final String appId;
private final String storeKey;
+ private final PaymentQueueImpl.PaymentQueueConfiguration paymentQueueConfiguration;
@NonNull
private List kinAccounts = new ArrayList<>(1);
/**
- * For more details please look at {@link #KinClient(Context context,Environment environment, String appId, String storeKey)}
+ * For more details please look at
+ * {@link #KinClient(Context context, Environment environment, String appId, String storeKey)}
*/
public KinClient(@NonNull Context context, @NonNull Environment environment, String appId) {
- this(context, environment, appId,"");
+ this(context, environment, appId, "");
}
/**
* Build KinClient object.
- * @param context android context
+ *
+ * @param context android context
* @param environment the blockchain network details.
- * @param appId a 3 or 4 character string which represent the application id which will be added to each
- * transaction.
- *
Note: appId must contain only upper and/or lower case letters and/or digits and that the total string length is between 3 to 4.
- * For example 1234 or 2ab3 or bca, etc.
- * @param storeKey an optional param which is the key for storing this KinClient data, different keys will store a different accounts.
+ * @param appId a 3 or 4 character string which represent the application id which will
+ * be added to each
+ * transaction.
+ *
Note: appId must contain only upper and/or lower case
+ * letters and/or digits and that the total string length is between 3 to 4.
+ * For example 1234 or 2ab3 or bca, etc.
+ * @param storeKey an optional param which is the key for storing this KinClient data,
+ * different keys will store a different accounts.
*/
- public KinClient(@NonNull Context context, @NonNull Environment environment, @NonNull String appId, @NonNull String storeKey) {
+ public KinClient(@NonNull Context context, @NonNull Environment environment,
+ @NonNull String appId, @NonNull String storeKey) {
checkNotNull(storeKey, "storeKey");
checkNotNull(context, "context");
checkNotNull(environment, "environment");
- validateAppId(appId);
+ KinClientInjector injector = new KinClientInjector(context, environment, appId, storeKey);
this.environment = environment;
- this.backupRestore = new BackupRestoreImpl();
- Server server = initServer();
+ this.backupRestore = injector.getBackupRestore();
this.appId = appId;
this.storeKey = storeKey;
- keyStore = initKeyStore(context.getApplicationContext(), storeKey);
- transactionSender = new TransactionSender(server, appId);
- accountInfoRetriever = new AccountInfoRetriever(server);
- generalBlockchainInfoRetriever = new GeneralBlockchainInfoRetrieverImpl(server);
- blockchainEventsCreator = new BlockchainEventsCreator(server);
+ keyStore = injector.getKeyStore();
+ transactionSender = injector.getTransactionSender();
+ accountInfoRetriever = injector.getAccountInfoRetriever();
+ generalBlockchainInfoRetriever = injector.getGeneralBlockchainInfoRetriever();
+ blockchainEventsCreator = injector.getBlockchainEventsCreator();
+ this.paymentQueueConfiguration = injector.getPaymentQueueConfiguration();
+ loadAccounts();
+ }
+
+ @VisibleForTesting
+ KinClient(KinClientInjector injector) {
+ this.environment = injector.getEnvironment();
+ this.keyStore = injector.getKeyStore();
+ this.transactionSender = injector.getTransactionSender();
+ this.accountInfoRetriever = injector.getAccountInfoRetriever();
+ this.generalBlockchainInfoRetriever = injector.getGeneralBlockchainInfoRetriever();
+ this.blockchainEventsCreator = injector.getBlockchainEventsCreator();
+ this.backupRestore = injector.getBackupRestore();
+ this.appId = injector.getAppId();
+ this.storeKey = injector.getStoreKey();
+ this.paymentQueueConfiguration = injector.getPaymentQueueConfiguration();
loadAccounts();
}
@VisibleForTesting
KinClient(Environment environment, KeyStore keyStore, TransactionSender transactionSender,
- AccountInfoRetriever accountInfoRetriever, GeneralBlockchainInfoRetrieverImpl generalBlockchainInfoRetriever,
- BlockchainEventsCreator blockchainEventsCreator, BackupRestore backupRestore, String appId, String storeKey) {
+ AccountInfoRetriever accountInfoRetriever,
+ GeneralBlockchainInfoRetrieverImpl generalBlockchainInfoRetriever,
+ BlockchainEventsCreator blockchainEventsCreator, BackupRestore backupRestore,
+ String appId, String storeKey) {
this.environment = environment;
this.keyStore = keyStore;
this.transactionSender = transactionSender;
@@ -97,20 +120,10 @@ public KinClient(@NonNull Context context, @NonNull Environment environment, @No
this.backupRestore = backupRestore;
this.appId = appId;
this.storeKey = storeKey;
+ this.paymentQueueConfiguration = new PaymentQueueImpl.PaymentQueueConfiguration(0, 0, 0);
loadAccounts();
}
- private Server initServer() {
- Network.use(environment.getNetwork());
- return new Server(environment.getNetworkUrl(), TRANSACTIONS_TIMEOUT, TimeUnit.SECONDS);
- }
-
- private KeyStore initKeyStore(Context context, String id) {
- SharedPrefStore store = new SharedPrefStore(
- context.getSharedPreferences(STORE_NAME_PREFIX + id, Context.MODE_PRIVATE));
- return new KeyStoreImpl(store, backupRestore);
- }
-
private void loadAccounts() {
List accounts = null;
try {
@@ -141,16 +154,6 @@ private void updateKinAccounts(List storageAccounts) {
kinAccounts = newKinAccountsList;
}
- private void validateAppId(String appId) {
- if (appId == null || appId.equals("")) {
- Log.w("KinClient","WARNING: KinClient instance was created without a proper application ID. Is this what you intended to do?");
- }
- else if (!appId.matches("[a-zA-Z0-9]{3,4}")) {
- throw new IllegalArgumentException("appId must contain only upper and/or lower case letters and/or digits and that the total string length is between 3 to 4.\n" +
- "for example 1234 or 2ab3 or cd2 or fqa, etc.");
- }
- }
-
/**
* Creates and adds an account.
* Once created, the account information will be stored securely on the device and can
@@ -168,19 +171,20 @@ KinAccount addAccount() throws CreateAccountException {
* Import an account from a JSON-formatted string.
*
* @param exportedJson The exported JSON-formatted string.
- * @param passphrase The passphrase to decrypt the secret key.
+ * @param passphrase The passphrase to decrypt the secret key.
* @return The imported account
*/
@NonNull
public KinAccount importAccount(@NonNull String exportedJson, @NonNull String passphrase)
- throws CryptoException, CreateAccountException, CorruptedDataException {
+ throws CryptoException, CreateAccountException, CorruptedDataException {
KeyPair account = keyStore.importAccount(exportedJson, passphrase);
KinAccount kinAccount = getAccountByPublicAddress(account.getAccountId());
return kinAccount != null ? kinAccount : addKeyPair(account);
}
@Nullable
- private KinAccount getAccountByPublicAddress(String accountId) { //TODO we should make this method public
+ private KinAccount getAccountByPublicAddress(String accountId) { //TODO we should make this
+ // method public
loadAccounts();
KinAccount kinAccount = null;
for (int i = 0; i < kinAccounts.size(); i++) {
@@ -229,8 +233,10 @@ public int getAccountCount() {
/**
* Deletes the account at input index (if it exists)
+ *
* @return true if the delete was successful or false otherwise
- * @throws DeleteAccountException in case of a delete account exception while trying to delete the account
+ * @throws DeleteAccountException in case of a delete account exception while trying to
+ * delete the account
*/
public boolean deleteAccount(int index) throws DeleteAccountException {
boolean deleteSuccess = false;
@@ -240,6 +246,7 @@ public boolean deleteAccount(int index) throws DeleteAccountException {
KinAccountImpl removedAccount = kinAccounts.remove(index);
removedAccount.markAsDeleted();
deleteSuccess = true;
+ removedAccount.paymentQueue().releaseQueue(); // TODO: 2019-09-11 add tests for that
}
return deleteSuccess;
}
@@ -251,6 +258,7 @@ public void clearAllAccounts() {
keyStore.clearAllAccounts();
for (KinAccountImpl kinAccount : kinAccounts) {
kinAccount.markAsDeleted();
+ kinAccount.paymentQueue().releaseQueue(); // TODO: 2019-09-11 add tests for that
}
kinAccounts.clear();
}
@@ -261,8 +269,10 @@ public Environment getEnvironment() {
@NonNull
private KinAccountImpl createNewKinAccount(KeyPair account) {
- return new KinAccountImpl(account, backupRestore, transactionSender,
- accountInfoRetriever, blockchainEventsCreator);
+ PaymentQueue paymentQueue = new PaymentQueueImpl(account, transactionSender,
+ generalBlockchainInfoRetriever, paymentQueueConfiguration);
+ return new KinAccountImpl(account, backupRestore, transactionSender, accountInfoRetriever,
+ blockchainEventsCreator, paymentQueue);
}
/**
@@ -283,7 +293,8 @@ public Long call() throws Exception {
/**
* Get the current minimum fee that the network charges per operation.
* This value is expressed in Quarks (1 Quark = 0.00001 KIN).
- *
Note: This method accesses the network, and should not be called on the android main thread.
+ * Note: This method accesses the network, and should not be called on the android
+ * main thread.
*
* @return the minimum fee.
*/
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClientInjector.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClientInjector.java
new file mode 100644
index 00000000..24bfd0f2
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/KinClientInjector.java
@@ -0,0 +1,129 @@
+package kin.sdk;
+
+import android.content.Context;
+
+import java.util.concurrent.TimeUnit;
+
+import kin.base.Network;
+import kin.base.Server;
+import kin.sdk.internal.backuprestore.BackupRestoreImpl;
+import kin.sdk.internal.blockchain.AccountInfoRetriever;
+import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetrieverImpl;
+import kin.sdk.internal.blockchain.TransactionSender;
+import kin.sdk.internal.blockchain.events.BlockchainEventsCreator;
+import kin.sdk.internal.queue.PaymentQueueImpl;
+import kin.sdk.internal.storage.KeyStore;
+import kin.sdk.internal.storage.KeyStoreImpl;
+import kin.sdk.internal.storage.SharedPrefStore;
+
+import static kin.sdk.internal.Utils.checkNotNull;
+import static kin.sdk.internal.Utils.validateAppId;
+
+public class KinClientInjector {
+
+ private static final long delayBetweenPaymentsMillis = 3000; // TODO: 2019-08-15 get those
+ // number from somewhere
+ private static final long queueTimeoutMillis = 10000; // TODO: 2019-08-15 get those
+ // number from somewhere
+ private static final int maxNumOfPayments = 100; // TODO: 2019-08-15 get those
+ // number from somewhere
+ private static final String STORE_NAME_PREFIX = "KinKeyStore_";
+ private static final int TRANSACTIONS_TIMEOUT = 30;
+
+ private Context context;
+ private Environment environment;
+ private String appId;
+ private String storeKey;
+ private Server server;
+ private BackupRestoreImpl backupRestore;
+
+ public KinClientInjector(Context context, Environment environment, String appId,
+ String storeKey) {
+ checkNotNull(storeKey, "storeKey");
+ checkNotNull(context, "context");
+ checkNotNull(environment, "environment");
+ validateAppId(appId);
+ this.context = context;
+ this.environment = environment;
+ this.appId = appId;
+ this.storeKey = storeKey;
+ }
+
+ KeyStore getKeyStore() {
+ return initKeyStore(context.getApplicationContext(), storeKey);
+ }
+
+ TransactionSender getTransactionSender() {
+ return new TransactionSender(getServer(), appId);
+ }
+
+ BackupRestoreImpl getBackupRestore() {
+ if (backupRestore == null) {
+ backupRestore = new BackupRestoreImpl();
+ }
+ return backupRestore;
+ }
+
+ AccountInfoRetriever getAccountInfoRetriever() {
+ return new AccountInfoRetriever(getServer());
+ }
+
+ GeneralBlockchainInfoRetrieverImpl getGeneralBlockchainInfoRetriever() {
+ return new GeneralBlockchainInfoRetrieverImpl(getServer());
+ }
+
+ BlockchainEventsCreator getBlockchainEventsCreator() {
+ return new BlockchainEventsCreator(getServer());
+ }
+
+ PaymentQueueImpl.PaymentQueueConfiguration getPaymentQueueConfiguration() {
+ return new PaymentQueueImpl.PaymentQueueConfiguration(delayBetweenPaymentsMillis,
+ queueTimeoutMillis, maxNumOfPayments);
+ }
+
+ Server getServer() {
+ if (server != null) {
+ return server;
+ } else {
+ return initServer();
+ }
+ }
+
+ void setServer(Server server) {
+ this.server = server;
+ }
+
+ String getStoreNamePrefix() {
+ return STORE_NAME_PREFIX;
+ }
+
+ int getTransactionTimeout() {
+ return TRANSACTIONS_TIMEOUT;
+ }
+
+ public Environment getEnvironment() {
+ return environment;
+ }
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public String getStoreKey() {
+ return storeKey;
+ }
+
+ private KeyStore initKeyStore(Context context, String id) {
+ SharedPrefStore store = new SharedPrefStore(
+ context.getSharedPreferences(getStoreNamePrefix() + id, Context.MODE_PRIVATE));
+ return new KeyStoreImpl(store, getBackupRestore());
+ }
+
+ private Server initServer() {
+ Network.use(environment.getNetwork());
+ setServer(new Server(environment.getNetworkUrl(), getTransactionTimeout(),
+ TimeUnit.SECONDS));
+ return new Server(environment.getNetworkUrl(), getTransactionTimeout(), TimeUnit.SECONDS);
+ }
+
+}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/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/TransactionInterceptor.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionInterceptor.java
index 757e47e7..b7299b7e 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionInterceptor.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/TransactionInterceptor.java
@@ -5,7 +5,7 @@
/**
* provide the generated transaction before sending to the blockchain.
*/
-public interface TransactionInterceptor {
+public interface TransactionInterceptor {
/**
* Intercept the generated transaction before sending to the blockchain
@@ -21,6 +21,6 @@ public interface TransactionInterceptor {
* @return the transaction identifier.
* @throws Exception
*/
- TransactionId interceptTransactionSending(TransactionProcess transactionProcess) throws Exception;
+ TransactionId interceptTransactionSending(T transactionProcess) throws Exception;
}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java
index 9e84988f..aedb15b7 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/Utils.java
@@ -2,12 +2,15 @@
import android.support.annotation.NonNull;
+import android.util.Log;
+
+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() {
@@ -32,6 +35,17 @@ public static String byteArrayToHex(byte[] a) {
return sb.toString();
}
+ public static void validateAppId(String appId) {
+ if (appId == null || appId.equals("")) {
+ Log.w("KinClient", "WARNING: KinClient instance was created without a proper " +
+ "application ID. Is this what you intended to do?");
+ } else if (!appId.matches("[a-zA-Z0-9]{3,4}")) {
+ throw new IllegalArgumentException("appId must contain only upper and/or lower case " +
+ "letters and/or digits and that the total string length is between 3 to 4.\n" +
+ "for example 1234 or 2ab3 or cd2 or fqa, etc.");
+ }
+ }
+
public static void checkNotNull(Object obj, String paramName) {
if (obj == null) {
throw new IllegalArgumentException(paramName + " == null");
@@ -43,4 +57,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..fafb46fe 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/AbstractKinAccount.java
@@ -2,36 +2,51 @@
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.queue.PaymentQueue;
+import kin.sdk.queue.TransactionProcess;
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 {
+ private final PaymentQueue paymentQueue;
+
+ AbstractKinAccount(PaymentQueue paymentQueue) {
+ this.paymentQueue = paymentQueue;
+ }
+
@NonNull
@Override
public Request buildTransaction(@NonNull final String publicAddress,
- @NonNull final BigDecimal amount, final int fee) {
+ @NonNull final BigDecimal amount,
+ final int fee) {
return new Request<>(new Callable() {
@Override
public PaymentTransaction call() throws Exception {
return buildTransactionSync(publicAddress, amount, fee);
}
});
- }@NonNull
+ }
+
+ @NonNull
@Override
public Request buildTransaction(@NonNull final String publicAddress,
@NonNull final BigDecimal amount,
- final int fee, @Nullable final String memo) {
+ final int fee,
+ @Nullable final String memo) {
return new Request<>(new Callable() {
@Override
public PaymentTransaction call() throws Exception {
- return buildTransactionSync(publicAddress, amount, fee, memo);
+ return buildTransactionSync(publicAddress, amount, fee, memo);
}
});
}
@@ -58,6 +73,37 @@ public TransactionId call() throws Exception {
});
}
+ @Override
+ public Request sendTransaction(final TransactionParams transactionParams) {
+ return sendTransaction(transactionParams, null);
+ }
+
+ @Override
+ public Request sendTransaction(final TransactionParams transactionParams,
+ final TransactionInterceptor interceptor) {
+ return new Request<>(new Callable() {
+ @Override
+ public TransactionId call() {
+// return ((PaymentQueueImpl) paymentQueue).enqueueTransactionParams
+// (transactionParams, interceptor);
+ // TODO: 2019-09-09 implement
+ return null;
+ }
+
+ });
+ }
+
+ @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..0659d885 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/KinAccountImpl.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/account/KinAccountImpl.java
@@ -2,8 +2,15 @@
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.exception.AccountDeletedException;
import kin.sdk.exception.CryptoException;
import kin.sdk.exception.OperationFailedException;
@@ -12,12 +19,8 @@
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;
public final class KinAccountImpl extends AbstractKinAccount {
@@ -29,14 +32,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,
+ PaymentQueue paymentQueue) {
+ super(paymentQueue);
this.account = account;
this.backupRestore = backupRestore;
this.transactionSender = transactionSender;
this.accountInfoRetriever = accountInfoRetriever;
this.blockchainEvents = blockchainEventsCreator.create(account.getAccountId());
- this.paymentQueue = new PaymentQueueImpl(account.getAccountId());
+ this.paymentQueue = paymentQueue;
}
@Override
@@ -48,17 +55,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
@@ -75,27 +84,11 @@ 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 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..35b251e1 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/blockchain/TransactionSender.java
@@ -3,32 +3,48 @@
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 String MEMO_APP_ID_VERSION_PREFIX = "1";
- private static String MEMO_DELIMITER = "-";
+ // Memo length limitation(in bytes) is 28 but we add 7(or 6) more bytes which includes the
+ // appId and some characters.
+ private static final int MEMO_BYTES_LENGTH_LIMIT = 21; // TODO: 2019-09-08 we should fix this
+ // because it could be 22 in case of a 3 char appId
+ private static final int MAX_NUM_OF_DECIMAL_PLACES = 4;
+ private static final String MEMO_APP_ID_VERSION_PREFIX = "1";
+ private static final String MEMO_DELIMITER = "-";
private static final String INSUFFICIENT_KIN_RESULT_CODE = "op_underfunded";
private static final String INSUFFICIENT_FEE_RESULT_CODE = "tx_insufficient_fee";
private static final String INSUFFICIENT_BALANCE_RESULT_CODE = "tx_insufficient_balance";
+
private final Server server; //horizon server
private final String appId;
@@ -37,14 +53,16 @@ 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,11 +71,35 @@ 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);
}
+ public BatchPaymentTransaction buildBatchPaymentTransaction(@NonNull KeyPair from,
+ @NonNull List pendingPayments,
+ int fee, @Nullable String memo) throws OperationFailedException {
+ checkBatchPaymentkParams(from, fee, memo);
+ if (appId != null && !appId.equals("")) {
+ memo = addAppIdToMemo(memo);
+ }
+
+ AccountResponse sourceAccount = loadSourceAccount(from);
+
+ Builder transactionBuilder = new Builder(sourceAccount);
+ for (PendingPayment pendingPayment : pendingPayments) {
+ KeyPair destination =
+ generateAddresseeKeyPair(pendingPayment.destinationPublicAddress());
+ addPaymentOperationToTransaction(transactionBuilder, destination,
+ pendingPayment.amount());
+ }
+
+ kin.base.Transaction transaction = addTransactionParametersAndSign(from, fee, memo,
+ transactionBuilder);
+ return new BatchPaymentTransaction(transaction);
+ }
+
+
public TransactionId sendTransaction(Transaction transaction) throws OperationFailedException {
return sendTransaction(((TransactionInternal) transaction).baseTransaction());
}
@@ -87,49 +129,39 @@ 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");
}
}
private void checkMemo(String memo) {
try {
if (memo != null && memo.getBytes("UTF-8").length > MEMO_BYTES_LENGTH_LIMIT) {
- throw new IllegalArgumentException("Memo cannot be longer that " + MEMO_BYTES_LENGTH_LIMIT + " bytes(UTF-8 characters)");
+ throw new IllegalArgumentException(
+ "Memo cannot be longer that " + MEMO_BYTES_LENGTH_LIMIT + " bytes(UTF-8 " +
+ "characters)");
}
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Memo text have unsupported characters encoding");
@@ -145,12 +177,31 @@ 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);
+ }
+
+ 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 +259,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)) {
@@ -221,8 +273,9 @@ private TransactionId createFailureException(SubmitTransactionResponse response)
private boolean isInsufficientKinException(TransactionFailedException transactionException) {
List resultCodes = transactionException.getOperationsResultCodes();
String transactionResultCode = transactionException.getTransactionResultCode();
- return ((resultCodes != null && resultCodes.size() > 0 && INSUFFICIENT_KIN_RESULT_CODE.equals(resultCodes.get(0))) ||
- !TextUtils.isEmpty(transactionResultCode) && INSUFFICIENT_BALANCE_RESULT_CODE.equals(transactionResultCode));
+ return (
+ (resultCodes != null && resultCodes.size() > 0 && INSUFFICIENT_KIN_RESULT_CODE.equals(resultCodes.get(0)))
+ || !TextUtils.isEmpty(transactionResultCode) && INSUFFICIENT_BALANCE_RESULT_CODE.equals(transactionResultCode));
}
private boolean isInsufficientFeeException(TransactionFailedException transactionException) {
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManager.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManager.java
index 5f9bfc25..35d290bf 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManager.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManager.java
@@ -1,5 +1,62 @@
package kin.sdk.internal.events;
+import java.util.List;
+
+import kin.sdk.TransactionId;
+import kin.sdk.exception.KinException;
+import kin.sdk.queue.PaymentQueue;
+import kin.sdk.queue.PendingPayment;
+import kin.sdk.transactiondata.BatchPaymentTransaction;
+
public interface EventsManager {
+ /**
+ * set the event listener
+ *
+ * @param eventListener the event listener to set
+ */
+ void setEventListener(PaymentQueue.EventListener eventListener);
+
+ /**
+ * Invoked when a new payment is enqueued
+ *
+ * @param payment the pending payment which was created when the payment was enqueued
+ * See {@link PendingPayment}
+ */
+ void onPaymentEnqueued(PendingPayment payment);
+
+ /**
+ * Invoked when a Transaction just before the transaction will be send.
+ *
+ * @param transaction the transaction to send
+ * @param payments the list of payments that the transaction consist of.
+ * See {@link BatchPaymentTransaction} and {@link PendingPayment} for
+ * more
+ * information
+ */
+ void onTransactionSend(BatchPaymentTransaction transaction, List payments);
+
+ /**
+ * Invoked when a Transaction just before the transaction will be send.
+ *
+ * @param transaction the transaction to send
+ * @param payments the list of payments that the transaction consist of.
+ * See {@link BatchPaymentTransaction} and {@link PendingPayment} for
+ * more
+ * information
+ */
+ void onTransactionSendSuccess(TransactionId transactionId, List payments);
+
+ /**
+ * Invoked when a Transaction just before the transaction will be send.
+ *
+ * @param transaction the transaction to send
+ * @param payments the list of payments that the transaction consist of.
+ * @param exception the exception the occurred.
+ * See {@link BatchPaymentTransaction} and {@link PendingPayment} for
+ * more
+ * information
+ */
+ void onTransactionSendFailed(List payments, KinException exception);
+
}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java
index 4ed0c809..4f5774c2 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/events/EventsManagerImpl.java
@@ -1,7 +1,64 @@
package kin.sdk.internal.events;
+import java.util.List;
+
+import kin.sdk.TransactionId;
+import kin.sdk.exception.KinException;
+import kin.sdk.queue.PaymentQueue;
+import kin.sdk.queue.PendingPayment;
+import kin.sdk.transactiondata.BatchPaymentTransaction;
+
public class EventsManagerImpl implements EventsManager {
- // TODO: 2019-08-14 implement
+ private PaymentQueue.EventListener eventListener;
+
+ // TODO: 2019-09-11 if we should use the main thread in the callback then we will have a
+ // problem because we also use this event manager for our needs and not necessarily want to
+ // run on main thread
+
+ public EventsManagerImpl() {
+ }
+
+ public EventsManagerImpl(PaymentQueue.EventListener eventListener) {
+ this.eventListener = eventListener;
+ }
+
+ @Override
+ public void setEventListener(PaymentQueue.EventListener eventListener) {
+ // TODO: 2019-09-05 probably implement as a list of them and not one
+ this.eventListener = eventListener;
+ }
+
+ @Override
+ public void onPaymentEnqueued(PendingPayment payment) {
+ if (eventListener != null) {
+ eventListener.onPaymentEnqueued(payment);
+ }
+ }
+
+ @Override
+ public void onTransactionSend(BatchPaymentTransaction transaction,
+ List payments) {
+ if (eventListener != null) {
+ eventListener.onTransactionSend(transaction, payments);
+ }
+ }
+
+ @Override
+ public void onTransactionSendSuccess(TransactionId transactionId,
+ List payments) {
+ if (eventListener != null) {
+ eventListener.onTransactionSendSuccess(transactionId, payments);
+ }
+ }
+
+ @Override
+ public void onTransactionSendFailed(List payments, KinException exception) {
+ if (eventListener != null) {
+ eventListener.onTransactionSendFailed(payments, exception);
+ }
+ }
+
+
}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java
index 1c5e6e25..b23522a4 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueImpl.java
@@ -4,60 +4,92 @@
import java.math.BigDecimal;
+import kin.base.KeyPair;
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.PaymentQueueTransactionProcess;
import kin.sdk.queue.PendingPayment;
+import kin.sdk.queue.TransactionProcess;
+import kin.sdk.transactiondata.TransactionParams;
public class PaymentQueueImpl implements PaymentQueue {
- private static final long DELAY_BETWEEN_PAYMENTS_MILLIS = 3000; // TODO: 2019-08-15 get those number from somewhere
- private static final long QUEUE_TIMEOUT_MILLIS = 10000; // TODO: 2019-08-15 get those number from somewhere
- private static final int MAX_NUM_OF_PAYMENTS = 100;
-
- private final String sourcePublicAddress;
+ private final KeyPair accountFrom;
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);
+ private final TransactionTasksQueueManager txTasksQueueManager;
+ private final EventsManager eventsManager;
+ private final TasksQueueImpl tasksQueue;
+
+ public PaymentQueueImpl(@NonNull KeyPair accountFrom, TransactionSender transactionSender,
+ GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever,
+ PaymentQueueConfiguration configuration) {
+ this.accountFrom = accountFrom;
+ this.eventsManager = new EventsManagerImpl();
+ tasksQueue = new TasksQueueImpl(transactionSender, eventsManager,
+ generalBlockchainInfoRetriever, accountFrom);
+ this.txTasksQueueManager = new TransactionTasksQueueManagerImpl(tasksQueue, eventsManager
+ , configuration.maxNumOfPayments);
+ this.paymentQueueManager = new PaymentQueueManagerImpl(txTasksQueueManager,
+ new QueueSchedulerImpl(), new PendingBalanceUpdaterImpl(), eventsManager,
+ configuration);
}
@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 transactionInterceptor an optional interceptor that will be used to intercept the
+ * transaction.
+ */
+ public void enqueueTransactionParams(TransactionParams transactionParams,
+ TransactionInterceptor transactionInterceptor) {
+
+ tasksQueue.scheduleTransactionParamsTask(transactionParams, transactionInterceptor);
+ }
+
@Override
- public void setTransactionInterceptor(TransactionInterceptor interceptor) {
- // TODO: 2019-08-15 implement
+ public void setTransactionInterceptor(TransactionInterceptor transactionInterceptor) {
+ tasksQueue.setPaymentQueueTransactionInterceptor(transactionInterceptor);
}
@Override
- public void addEventsListener(EventsListener listener) {
- // TODO: 2019-08-15 implement
+ public void setEventListener(EventListener eventListener) {
+ eventsManager.setEventListener(eventListener);
}
@Override
public void setFee(int fee) {
+ Utils.checkForNegativeFee(fee);
+ tasksQueue.setBatchPaymentsFee(fee);
}
@Override
public boolean transactionInProgress() {
- return false; // TODO: 2019-08-15 implement
+ return txTasksQueueManager.transactionInProgress();
}
@Override
@@ -65,4 +97,35 @@ public int pendingPaymentsCount() {
return paymentQueueManager.getPendingPaymentCount();
}
+ @Override
+ public void releaseQueue() {
+ tasksQueue.stopTaskQueue();
+ }
+
+ public static class PaymentQueueConfiguration {
+
+ private long delayBetweenPaymentsMillis; // TODO: 2019-08-15 get this number from somewhere
+ private long queueTimeoutMillis; // TODO: 2019-08-15 get this number from somewhere
+ private int maxNumOfPayments; // TODO: 2019-08-15 get this number from somewhere
+
+ public PaymentQueueConfiguration(long delayBetweenPaymentsMillis, long queueTimeoutMillis,
+ int maxNumOfPayments) {
+ this.delayBetweenPaymentsMillis = delayBetweenPaymentsMillis;
+ this.queueTimeoutMillis = queueTimeoutMillis;
+ this.maxNumOfPayments = maxNumOfPayments;
+ }
+
+ long getDelayBetweenPayments() {
+ return delayBetweenPaymentsMillis;
+ }
+
+ long getQueueTimeout() {
+ return queueTimeoutMillis;
+ }
+
+ int getMaxNumOfPayments() {
+ return maxNumOfPayments;
+ }
+ }
+
}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java
index a2846389..05633ede 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PaymentQueueManagerImpl.java
@@ -11,28 +11,24 @@
class PaymentQueueManagerImpl implements PaymentQueueManager {
- private final TransactionTaskQueueManager txTaskQueueManager;
+ private final TransactionTasksQueueManager txTasksQueueManager;
private final QueueScheduler queueScheduler;
private final PendingBalanceUpdater pendingBalanceUpdater;
private final EventsManager eventsManager;
- private final long delayBetweenPayments;
- private final long queueTimeout;
- private final int maxNumOfPayments;
+ private final PaymentQueueImpl.PaymentQueueConfiguration configuration;
private List queue;
private Runnable dequeueTask;
private Runnable timeoutTask;
- PaymentQueueManagerImpl(TransactionTaskQueueManager txTaskQueueManager, QueueScheduler queueScheduler,
+ PaymentQueueManagerImpl(TransactionTasksQueueManager txTasksQueueManager, QueueScheduler queueScheduler,
PendingBalanceUpdater pendingBalanceUpdater, EventsManager eventsManager,
- long delayBetweenPayments, long queueTimeout, int maxNumOfPayments) {
- this.txTaskQueueManager = txTaskQueueManager;
+ PaymentQueueImpl.PaymentQueueConfiguration configuration) {
+ this.txTasksQueueManager = txTasksQueueManager;
this.queueScheduler = queueScheduler;
this.pendingBalanceUpdater = pendingBalanceUpdater;
this.eventsManager = eventsManager;
- this.delayBetweenPayments = delayBetweenPayments;
- this.queueTimeout = queueTimeout;
- this.maxNumOfPayments = maxNumOfPayments;
- queue = new ArrayList<>(maxNumOfPayments);
+ this.configuration = configuration;
+ queue = new ArrayList<>(configuration.getMaxNumOfPayments());
dequeueTask = new DequeueTask();
}
@@ -44,7 +40,7 @@ public void enqueue(final PendingPayment pendingPayment) {
public void run() {
validatePendingBalance(); // TODO: 2019-08-15 handle it
- if (getPendingPaymentCount() == maxNumOfPayments - 1) {
+ if (getPendingPaymentCount() == configuration.getMaxNumOfPayments() - 1) {
addToQueue(pendingPayment);
sendPendingPaymentsToTaskQueue();
} else {
@@ -57,19 +53,21 @@ private void handlePendingPayment() {
if (queue.size() == 1) {
queueScheduler.removePendingTask(timeoutTask);
timeoutTask = new DequeueTask();
- queueScheduler.scheduleDelayed(timeoutTask, queueTimeout);
+ queueScheduler.scheduleDelayed(timeoutTask, configuration.getQueueTimeout());
}
resetScheduler();
}
private void addToQueue(PendingPayment pendingPayment) {
queue.add(pendingPayment);
+ eventsManager.onPaymentEnqueued(pendingPayment);
}
private void resetScheduler() {
queueScheduler.removePendingTask(dequeueTask);
dequeueTask = new DequeueTask();
- queueScheduler.scheduleDelayed(dequeueTask, delayBetweenPayments);
+ queueScheduler.scheduleDelayed(dequeueTask,
+ configuration.getDelayBetweenPayments());
}
});
}
@@ -94,8 +92,8 @@ private void validatePendingBalance() {
private void sendPendingPaymentsToTaskQueue() {
queueScheduler.removeAllPendingTasks();
List pendingPayments = queue;
- queue = new ArrayList<>(maxNumOfPayments);
- txTaskQueueManager.enqueue(pendingPayments);
+ queue = new ArrayList<>(configuration.getMaxNumOfPayments());
+ txTasksQueueManager.enqueue(pendingPayments);
}
private class DequeueTask implements Runnable {
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 4f8d6e73..22303951 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PendingPaymentImpl.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/PendingPaymentImpl.java
@@ -10,7 +10,6 @@ public class PendingPaymentImpl implements PendingPayment {
private final String sourcePublicAddress;
private final BigDecimal amount;
private final Object metadata;
- private Status status;
public PendingPaymentImpl(String destinationPublicAddress, String sourcePublicAddress, BigDecimal amount) {
this(destinationPublicAddress, sourcePublicAddress, amount, null);
@@ -22,7 +21,6 @@ public PendingPaymentImpl(String destinationPublicAddress, String sourcePublicAd
this.sourcePublicAddress = sourcePublicAddress;
this.amount = amount;
this.metadata = metadata;
- this.status = Status.PENDING;
}
@Override
@@ -44,13 +42,4 @@ public BigDecimal amount() {
public Object metadata() {
return metadata;
}
-
- @Override
- public Status status() {
- return status;
- }
-
- void setStatus(Status status) {
- this.status = status;
- }
}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java
new file mode 100644
index 00000000..471acd22
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendPendingPaymentsTask.java
@@ -0,0 +1,103 @@
+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.KinException;
+import kin.sdk.exception.OperationFailedException;
+import kin.sdk.internal.blockchain.GeneralBlockchainInfoRetriever;
+import kin.sdk.internal.blockchain.TransactionSender;
+import kin.sdk.internal.events.EventsManager;
+import kin.sdk.queue.PaymentQueueTransactionProcess;
+import kin.sdk.queue.PendingPayment;
+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() {
+ PaymentQueueTransactionProcess transactionProcess =
+ new PaymentQueueTransactionProcess(transactionSender, pendingPayments,
+ accountFrom, fee);
+ try {
+ TransactionId transactionId =
+ transactionInterceptor.interceptTransactionSending(transactionProcess);
+ handleTransactionFinished(transactionId);
+ } catch (Exception e) {
+ if (!(e instanceof KinException)) {
+ e = new OperationFailedException(e);
+ }
+
+ //TODO send it with events manager
+ }
+ }
+
+ @Override
+ void handleTransactionFinished(TransactionId transactionId) {
+ super.handleTransactionFinished(transactionId);
+ if (transactionId != null) {
+ eventsManager.onTransactionSendSuccess(transactionId, pendingPayments);
+ // TODO: 2019-09-01 invoke events for transaction success
+ } else {
+ //TODO need to create a meaningful message for this scenario
+ eventsManager.onTransactionSendFailed(pendingPayments, new OperationFailedException(
+ "Transaction Id is null"));
+ // TODO: 2019-09-01 invoke events for transaction fail
+ }
+ }
+
+ @Override
+ void buildAndSendTransaction() throws OperationFailedException {
+ BatchPaymentTransaction transaction =
+ transactionSender.buildBatchPaymentTransaction(accountFrom, pendingPayments, fee,
+ null);
+ eventsManager.onTransactionSend(transaction, pendingPayments);
+ 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) {
+ e.printStackTrace();
+ // TODO: 2019-08-27 need to maybe add an exception to the events manager or
+ // something...
+ }
+ }
+ }
+}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java
new file mode 100644
index 00000000..61386f3a
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionParamsTask.java
@@ -0,0 +1,69 @@
+package kin.sdk.internal.queue;
+
+import kin.base.KeyPair;
+import kin.sdk.TransactionId;
+import kin.sdk.TransactionInterceptor;
+import kin.sdk.exception.KinException;
+import kin.sdk.exception.OperationFailedException;
+import kin.sdk.internal.blockchain.TransactionSender;
+import kin.sdk.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 KeyPair accountFrom;
+
+ SendTransactionParamsTask(TransactionParams transactionParams,
+ TransactionSender transactionSender,
+ TransactionInterceptor transactionInterceptor,
+ TaskFinishListener taskFinishListener, KeyPair accountFrom) {
+ super(transactionSender, transactionInterceptor, taskFinishListener);
+ this.transactionParams = transactionParams;
+ this.transactionSender = transactionSender;
+ this.transactionInterceptor = transactionInterceptor;
+ this.accountFrom = accountFrom;
+ }
+
+ @Override
+ void invokeInterceptor() {
+ TransactionProcess transactionProcess =
+ new TransactionParamsProcessImpl(transactionSender, transactionParams,
+ accountFrom);
+ TransactionId transactionId = null;
+ try {
+ transactionId = transactionInterceptor.interceptTransactionSending(transactionProcess);
+ } catch (Exception e) {
+ if (!(e instanceof KinException)) {
+ e = new OperationFailedException(e);
+ }
+ // TODO: 2019-09-11 because transaction params doesn't have any event listener then
+ // what should we do here?
+ }
+ handleTransactionFinished(transactionId);
+ }
+
+ @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);
+ }
+}
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..522398bf
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/SendTransactionTask.java
@@ -0,0 +1,58 @@
+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
+ }
+ taskFinishListener.onTaskFinish();
+ }
+
+ @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-09-11 because transaction params doesn't have any event listener then
+ // what should we do here?
+ }
+ }
+
+ abstract void invokeInterceptor();
+
+ 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..1e1a0b31
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TaskFinishListener.java
@@ -0,0 +1,10 @@
+package kin.sdk.internal.queue;
+
+public interface TaskFinishListener {
+
+ /**
+ * Callback for when a task has finished successfully or with failure.
+ */
+ void onTaskFinish();
+
+}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java
new file mode 100644
index 00000000..64dfccd0
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueue.java
@@ -0,0 +1,75 @@
+package kin.sdk.internal.queue;
+
+import android.support.annotation.Nullable;
+
+import java.util.List;
+
+import kin.sdk.TransactionInterceptor;
+import kin.sdk.queue.PaymentQueueTransactionProcess;
+import kin.sdk.queue.PendingPayment;
+import kin.sdk.queue.TransactionProcess;
+import kin.sdk.transactiondata.TransactionParams;
+
+public interface TasksQueue {
+
+ /**
+ * 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
+ * @param transactionParamsInterceptor an optional interceptor that will be used to intercept
+ * the transaction.
+ */
+ void scheduleTransactionParamsTask(TransactionParams paymentTransactionParams,
+ @Nullable TransactionInterceptor transactionParamsInterceptor);
+
+ /**
+ * 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 setBatchPaymentsFee(int fee);
+
+ /**
+ * Set a transaction interceptor.
+ *
+ * @param pendingPaymentsTransactionInterceptor is the TransactionInterceptor for batch
+ * payments transactions to set.
+ * See {@link TransactionInterceptor}
+ */
+ void setPaymentQueueTransactionInterceptor(TransactionInterceptor pendingPaymentsTransactionInterceptor);
+}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TasksQueueImpl.java
new file mode 100644
index 00000000..81f6ba58
--- /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.PaymentQueueTransactionProcess;
+import kin.sdk.queue.PendingPayment;
+import kin.sdk.queue.TransactionProcess;
+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 paymentQueueTransactionInterceptor;
+ private final EventsManager eventsManager;
+ private final GeneralBlockchainInfoRetriever generalBlockchainInfoRetriever;
+ private final KeyPair accountFrom;
+ private TaskFinishListener taskFinishListener;
+ private Handler backgroundHandler;
+ private int batchPaymentsFee = -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();
+ }
+
+ @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,
+ paymentQueueTransactionInterceptor, taskFinishListener,
+ eventsManager, generalBlockchainInfoRetriever, batchPaymentsFee,
+ accountFrom);
+ backgroundHandler.post(sendPendingPaymentsTask);
+ }
+ }
+
+ @Override
+ public void scheduleTransactionParamsTask(TransactionParams transactionParams,
+ TransactionInterceptor transactionParamsInterceptor) {
+ if (backgroundHandler != null) {
+ SendTransactionParamsTask sendTransactionParamsTask =
+ new SendTransactionParamsTask(transactionParams,
+ transactionSender,
+ transactionParamsInterceptor, taskFinishListener, accountFrom);
+ backgroundHandler.postAtFrontOfQueue(sendTransactionParamsTask);
+ }
+ }
+
+ @Override
+ public void stopTaskQueue() {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ quitSafely();
+ } else {
+ quit();
+ }
+ }
+
+ @Override
+ public void setTaskFinishListener(TaskFinishListener taskFinishListener) {
+ this.taskFinishListener = taskFinishListener;
+ }
+
+ @Override
+ public void setBatchPaymentsFee(int batchPaymentsFee) {
+ this.batchPaymentsFee = batchPaymentsFee;
+ }
+
+ @Override
+ public void setPaymentQueueTransactionInterceptor(TransactionInterceptor pendingPaymentsTransactionInterceptor) {
+ this.paymentQueueTransactionInterceptor = pendingPaymentsTransactionInterceptor;
+ }
+}
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/TransactionTaskQueueManager.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManager.java
deleted file mode 100644
index a00bc565..00000000
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManager.java
+++ /dev/null
@@ -1,15 +0,0 @@
-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
deleted file mode 100644
index c39a324e..00000000
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTaskQueueManagerImpl.java
+++ /dev/null
@@ -1,19 +0,0 @@
-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/internal/queue/TransactionTasksQueueManager.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java
new file mode 100644
index 00000000..9284f154
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManager.java
@@ -0,0 +1,36 @@
+package kin.sdk.internal.queue;
+
+import java.util.List;
+
+import kin.sdk.TransactionInterceptor;
+import kin.sdk.queue.PendingPayment;
+import kin.sdk.queue.TransactionProcess;
+import kin.sdk.transactiondata.PaymentTransactionParams;
+
+public interface TransactionTasksQueueManager {
+
+
+ /**
+ * 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
+ * @param transactionInterceptor an optional interceptor that will be used to intercept
+ * * the transaction.
+ */
+ void enqueue(PaymentTransactionParams transactionParams,
+ TransactionInterceptor transactionInterceptor);
+
+ /**
+ * @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
new file mode 100644
index 00000000..8ab6389a
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/internal/queue/TransactionTasksQueueManagerImpl.java
@@ -0,0 +1,154 @@
+package kin.sdk.internal.queue;
+
+import android.util.Log;
+
+import java.util.ArrayDeque;
+import java.util.List;
+import java.util.Queue;
+
+import kin.sdk.TransactionInterceptor;
+import kin.sdk.internal.events.EventsManager;
+import kin.sdk.queue.PendingPayment;
+import kin.sdk.queue.TransactionProcess;
+import kin.sdk.transactiondata.PaymentTransactionParams;
+
+/**
+ * Class which manages the transaction task queue.
+ */
+class TransactionTasksQueueManagerImpl implements TransactionTasksQueueManager, TaskFinishListener {
+
+ private static final String TAG = TransactionTasksQueueManagerImpl.class.getSimpleName();
+
+ private final TasksQueue taskQueue;
+ private final EventsManager eventsManager; // TODO: 2019-09-09 verify if it is necessary here
+ private final int maxNumOfPayments;
+ private Queue> batchPendingPaymentsQueue;
+ private PaymentTransactionParams currentTransactionParams;
+ private TransactionInterceptor currentTransactionParamsInterceptor;
+ private boolean transactionInProgress = false;
+
+ 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(List pendingPayments) {
+ mergePayments(pendingPayments);
+ sendTask();
+ }
+
+ @Override
+ public synchronized void enqueue(PaymentTransactionParams paymentTransactionParams,
+ TransactionInterceptor transactionInterceptor) {
+ if (currentTransactionParams != null) {
+ // TODO: 2019-08-26 throw some exception or use event manager
+ } else {
+ currentTransactionParams = paymentTransactionParams;
+ currentTransactionParamsInterceptor = transactionInterceptor;
+ sendTask();
+ }
+ }
+
+ @Override
+ public synchronized boolean transactionInProgress() {
+ return transactionInProgress;
+ }
+
+ @Override
+ public synchronized void onTaskFinish() {
+ 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 numOfPendingPaymentsToBatch = pendingPayments.size();
+ // the index in the pending payments list in which to start take elements to next batch
+ // payments list.
+ int pendingPaymentIndexToTakeFrom = 0;
+
+ for (List batchPayments : batchPendingPaymentsQueue) {
+ // the current number of pending payments that can be entered to the current batch
+ // payments 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 >= numOfPendingPaymentsToBatch) {
+ batchPayments.addAll(pendingPayments);
+ return;
+ } else {
+ // update the rest of pending payments that still need to be batched.
+ numOfPendingPaymentsToBatch -= numOfEntriesAvailableInBatchList;
+ List subPendingPayments =
+ pendingPayments.subList(pendingPaymentIndexToTakeFrom,
+ pendingPaymentIndexToTakeFrom + numOfEntriesAvailableInBatchList);
+ // update the index
+ pendingPaymentIndexToTakeFrom += numOfEntriesAvailableInBatchList;
+ batchPayments.addAll(subPendingPayments);
+ }
+ }
+ }
+ // add to the queue
+ batchPendingPaymentsQueue.add(pendingPayments.subList(pendingPaymentIndexToTakeFrom,
+ pendingPaymentIndexToTakeFrom + numOfPendingPaymentsToBatch));
+ }
+
+ /**
+ * 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) {
+ sendTransactionParamsTask();
+ } else if (batchPendingPaymentsQueue.size() > 0) {
+ sendPendingPaymentsTask();
+ }
+ } else {
+ Log.d(TAG, "sendTask: can't send task because there is a transaction in progress");
+ // TODO: 2019-09-08 should we add logs here or something? probably we should
+ }
+ }
+
+ private void sendTransactionParamsTask() {
+ transactionInProgress = true;
+ PaymentTransactionParams toSend = currentTransactionParams;
+ currentTransactionParams = null;
+ TransactionInterceptor transactionInterceptor =
+ currentTransactionParamsInterceptor;
+ currentTransactionParamsInterceptor = null;
+ taskQueue.scheduleTransactionParamsTask(toSend, transactionInterceptor);
+ }
+
+ private void sendPendingPaymentsTask() {
+ transactionInProgress = true;
+ taskQueue.schedulePendingPaymentsTask(batchPendingPaymentsQueue.poll());
+ }
+
+}
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..fa2dbed5 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PaymentQueue.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PaymentQueue.java
@@ -1,20 +1,22 @@
package kin.sdk.queue;
import android.support.annotation.NonNull;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import kin.sdk.TransactionId;
import kin.sdk.TransactionInterceptor;
import kin.sdk.exception.InsufficientKinException;
import kin.sdk.exception.KinException;
import kin.sdk.transactiondata.BatchPaymentTransaction;
-import java.math.BigDecimal;
-import java.util.List;
-
public interface PaymentQueue {
/**
* Register/unregister for queue events.
*/
- interface EventsListener {
+ interface EventListener {
/**
* Invoked when a new payment is enqueued
@@ -42,7 +44,7 @@ interface EventsListener {
* See {@link BatchPaymentTransaction} and {@link PendingPayment} for more
* information
*/
- void onTransactionSendSuccess(BatchPaymentTransaction transaction, List payments);
+ void onTransactionSendSuccess(TransactionId transactionId, List payments);
/**
* Invoked when a Transaction just before the transaction will be send.
@@ -53,8 +55,7 @@ interface EventsListener {
* See {@link BatchPaymentTransaction} and {@link PendingPayment} for more
* information
*/
- void onTransactionSendFailed(BatchPaymentTransaction transaction, List payments,
- KinException exception);
+ void onTransactionSendFailed(List payments, KinException exception);
}
/**
@@ -88,17 +89,17 @@ void onTransactionSendFailed(BatchPaymentTransaction transaction, List See {@link TransactionInterceptor}
*/
- void setTransactionInterceptor(TransactionInterceptor interceptor);
+ void setTransactionInterceptor(TransactionInterceptor interceptor);
/**
* @param listener is a queue event listener.
*/
- void addEventsListener(EventsListener listener);
+ void setEventListener(EventListener listener);
/**
* Setter for the fee.
* 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);
@@ -113,4 +114,14 @@ void onTransactionSendFailed(BatchPaymentTransaction transaction, List pendingPayments;
+ private final KeyPair accountFrom;
+ private final int fee;
+
+ public PaymentQueueTransactionProcess(TransactionSender transactionSender,
+ List pendingPayments,
+ KeyPair accountFrom, int fee) {
+ super(transactionSender);
+ this.transactionSender = transactionSender;
+ this.pendingPayments = pendingPayments;
+ this.accountFrom = accountFrom;
+ this.fee = fee;
+ }
+
+ /**
+ * @return the list of pending payment that the current transaction consist of.
+ * Can be null or empty.
+ */
+ public List payments() {
+ return pendingPayments;
+ }
+
+ @Override
+ public BatchPaymentTransaction transaction() throws OperationFailedException {
+ return transaction(null);
+ }
+
+ /**
+ * @return a transaction that consist of the list of pending payments.
+ * Also add a memo to that transaction
+ */
+
+ public BatchPaymentTransaction transaction(String memo) throws OperationFailedException {
+ return buildTransaction(memo);
+ }
+
+ /**
+ * Build the transaction
+ *
+ * @param memo the memo that should be added to the transaction
+ * @return a new created transaction.
+ * @throws OperationFailedException in case it couldn't be build.
+ */
+ private BatchPaymentTransaction buildTransaction(String memo) throws OperationFailedException {
+ return transactionSender.buildBatchPaymentTransaction(accountFrom, pendingPayments, fee,
+ memo);
+ }
+}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java
index 3bdc093b..c17fbc51 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/PendingPayment.java
@@ -6,10 +6,6 @@
public interface PendingPayment {
- enum Status {
- PENDING, COMPLETED, FAILED
- }
-
/**
* Destination account public address.
*/
@@ -31,9 +27,4 @@ enum Status {
*/
@Nullable
Object metadata();
-
- /**
- * Current Payment status
- */
- Status status();
}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionParamsProcessImpl.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionParamsProcessImpl.java
new file mode 100644
index 00000000..5af51bd4
--- /dev/null
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionParamsProcessImpl.java
@@ -0,0 +1,49 @@
+package kin.sdk.queue;
+
+import kin.base.KeyPair;
+import kin.sdk.exception.OperationFailedException;
+import kin.sdk.internal.blockchain.TransactionSender;
+import kin.sdk.transactiondata.PaymentTransactionParams;
+import kin.sdk.transactiondata.Transaction;
+import kin.sdk.transactiondata.TransactionParams;
+
+public class TransactionParamsProcessImpl extends TransactionProcess {
+
+ private final TransactionSender transactionSender;
+ private final TransactionParams transactionParams;
+ private final KeyPair accountFrom;
+
+ public TransactionParamsProcessImpl(TransactionSender transactionSender,
+ TransactionParams transactionParams, KeyPair accountFrom) {
+ super(transactionSender);
+ this.transactionSender = transactionSender;
+ this.transactionParams = transactionParams;
+ this.accountFrom = accountFrom;
+ }
+
+ @Override
+ public Transaction transaction() throws OperationFailedException {
+ return buildTransaction();
+ }
+
+ /**
+ * Build the transaction
+ *
+ * @return a new created transaction.
+ * @throws OperationFailedException in case it couldn't be build.
+ */
+ private Transaction buildTransaction() throws OperationFailedException {
+ if (transactionParams instanceof PaymentTransactionParams) {
+ PaymentTransactionParams paymentTransactionParams =
+ (PaymentTransactionParams) transactionParams;
+ return transactionSender.buildPaymentTransaction(accountFrom,
+ paymentTransactionParams.destinationPublicAddress(),
+ paymentTransactionParams.amount(),
+ paymentTransactionParams.fee(), paymentTransactionParams.memo());
+ } else {
+ // will be implemented in the future when we will add more types
+ return null;
+ }
+
+ }
+}
diff --git a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionProcess.java b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionProcess.java
index 78bdba43..32693fc8 100644
--- a/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionProcess.java
+++ b/kin-sdk/kin-sdk-lib/src/main/java/kin/sdk/queue/TransactionProcess.java
@@ -1,33 +1,26 @@
package kin.sdk.queue;
-import android.support.annotation.Nullable;
import kin.sdk.TransactionId;
+import kin.sdk.exception.OperationFailedException;
+import kin.sdk.internal.blockchain.TransactionSender;
import kin.sdk.transactiondata.Transaction;
-import java.util.List;
-
/**
- * This class can be used to access both generated transaction and all of its associated PendingPayments.
+ * This class can be used to access both generated transaction and all of its associated
+ * PendingPayments.
*/
-public interface TransactionProcess {
+public abstract class TransactionProcess {
- /**
- * @return a transaction that consist of the list of pending payments.
- */
- Transaction transaction();
+ private final TransactionSender transactionSender;
- /**
- * @return a transaction that consist of the list of pending payments.
- * Also add a memo to that transaction
- */
- Transaction transaction(String memo);
+ TransactionProcess(TransactionSender transactionSender) {
+ this.transactionSender = transactionSender;
+ }
/**
- * @return the list of pending payment that the current transaction consist of.
- * Can be null or empty.
+ * @return a transaction that consist of the list of pending payments.
*/
- @Nullable
- List payments();
+ public abstract Transaction transaction() throws OperationFailedException;
/**
* Send the transaction with a Transaction object
@@ -35,7 +28,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 +38,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 4d6dc244..84a5d1aa 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
@@ -10,9 +10,10 @@ public class PaymentTransaction extends TransactionInternal {
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 destinationPublicAddress, BigDecimal amount,
- String memo) {
+ String destinationPublicAddress, BigDecimal amount, String memo) {
super(baseTransaction);
this.destinationPublicAddress = destinationPublicAddress;
this.amount = amount;
@@ -35,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 db636949..100d28cf 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
@@ -17,6 +17,7 @@
import kin.sdk.internal.blockchain.events.BlockchainEventsCreator;
import kin.sdk.internal.data.BalanceImpl;
import kin.sdk.internal.data.TransactionIdImpl;
+import kin.sdk.queue.PaymentQueue;
import kin.sdk.transactiondata.PaymentTransaction;
import static junit.framework.Assert.assertEquals;
@@ -35,18 +36,22 @@ public class KinAccountImplTest {
private AccountInfoRetriever mockAccountInfoRetriever;
@Mock
private BlockchainEventsCreator mockBlockchainEventsCreator;
+ @Mock
+ private PaymentQueue mockPaymentQueue;
+
private KinAccountImpl kinAccount;
private KeyPair expectedRandomAccount;
@Before
- public void setUp() throws Exception {
+ public void setUp() {
MockitoAnnotations.initMocks(this);
}
private void initWithRandomAccount() {
expectedRandomAccount = KeyPair.random();
- kinAccount = new KinAccountImpl(expectedRandomAccount, new FakeBackupRestore(), mockTransactionSender,
- mockAccountInfoRetriever, mockBlockchainEventsCreator);
+ kinAccount = new KinAccountImpl(expectedRandomAccount, new FakeBackupRestore(),
+ mockTransactionSender, mockAccountInfoRetriever, mockBlockchainEventsCreator,
+ mockPaymentQueue);
}
@Test
@@ -66,7 +71,8 @@ public void sendTransactionSync() throws Exception {
when(mockTransactionSender.sendTransaction((PaymentTransaction) any())).thenReturn(expectedTransactionId);
- PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100);
+ PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId,
+ expectedAmount, 100);
TransactionId transactionId = kinAccount.sendTransactionSync(transaction);
verify(mockTransactionSender).sendTransaction(transaction);
@@ -84,7 +90,8 @@ public void sendTransactionSync_WithMemo() throws Exception {
when(mockTransactionSender.sendTransaction((PaymentTransaction) any())).thenReturn(expectedTransactionId);
- PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId, expectedAmount, 100, memo);
+ PaymentTransaction transaction = kinAccount.buildTransactionSync(expectedAccountId,
+ expectedAmount, 100, memo);
TransactionId transactionId = kinAccount.sendTransactionSync(transaction);
verify(mockTransactionSender).sendTransaction(transaction);
@@ -122,7 +129,8 @@ public void sendTransactionSync_DeletedAccount_Exception() throws Exception {
kinAccount.markAsDeleted();
PaymentTransaction transaction = kinAccount.buildTransactionSync(
- "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX", new BigDecimal("12.2"), 100);
+ "GDKJAMCTGZGD6KM7RBEII6QUYAHQQUGERXKM3ESHBX2UUNTNAVNB3OGX",
+ new BigDecimal("12.2"), 100);
kinAccount.sendTransactionSync(transaction);
}
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/FakeQueueScheduler.kt b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt
index 9f38d3b3..dcb7c720 100644
--- a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt
+++ b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/FakeQueueScheduler.kt
@@ -7,7 +7,7 @@ import java.util.concurrent.TimeUnit
open class FakeQueueScheduler : QueueScheduler {
- var scheduler: ScheduledThreadPoolExecutor = ScheduledThreadPoolExecutor(1)
+ private var scheduler: ScheduledThreadPoolExecutor = ScheduledThreadPoolExecutor(1)
private val futureTasks: HashMap?> = HashMap()
val numOfTasks: Int
get() = futureTasks.size
@@ -25,19 +25,12 @@ open class FakeQueueScheduler : QueueScheduler {
// killing all future futureTasks not including the current running task if there is one.
scheduler.shutdown()
scheduler = ScheduledThreadPoolExecutor(1)
- println(futureTasks.size)
futureTasks.clear()
- println(futureTasks.size)
- println()
}
override fun removePendingTask(runnable: Runnable?) {
- println(futureTasks.size)
val removedRunnable = futureTasks.remove(runnable)
- println(removedRunnable)
- println(futureTasks.size)
removedRunnable?.cancel(false)
- println()
}
override fun schedule(runnable: Runnable?) {
diff --git a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt
index 270b9a14..18e0c574 100644
--- a/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt
+++ b/kin-sdk/kin-sdk-lib/src/test/kotlin/kin/sdk/internal/queue/PaymentQueueManagerTest.kt
@@ -22,7 +22,7 @@ class PaymentQueueManagerTest {
}
private var queueScheduler: FakeQueueScheduler = FakeQueueScheduler()
- private var txTaskQueueManager: TransactionTaskQueueManager = mock()
+ private var txTaskQueueManager: TransactionTasksQueueManager = mock()
private var pendingBalanceUpdater: PendingBalanceUpdater = mock()
private var eventsManager: EventsManager = mock()
@@ -40,8 +40,10 @@ class PaymentQueueManagerTest {
private fun initPaymentQueueManager() {
destinationAccount = KeyPair.random().accountId
sourceAccount = KeyPair.random().accountId
- paymentQueueManager = PaymentQueueManagerImpl(txTaskQueueManager, queueScheduler, pendingBalanceUpdater, eventsManager,
- Constants.DELAY_BETWEEN_PAYMENTS_MILLIS, Constants.QUEUE_TIMEOUT_MILLIS, Constants.MAX_NUM_OF_PAYMENTS)
+ val configuration = PaymentQueueImpl.PaymentQueueConfiguration(Constants.DELAY_BETWEEN_PAYMENTS_MILLIS,
+ Constants.QUEUE_TIMEOUT_MILLIS, Constants.MAX_NUM_OF_PAYMENTS)
+ paymentQueueManager = PaymentQueueManagerImpl(txTaskQueueManager, queueScheduler,
+ pendingBalanceUpdater, eventsManager, configuration)
}
@Test
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