summaryrefslogtreecommitdiff
path: root/src/commonMain
diff options
context:
space:
mode:
Diffstat (limited to 'src/commonMain')
-rw-r--r--src/commonMain/kotlin/net/taler/wallet/kotlin/Time.kt2
-rw-r--r--src/commonMain/kotlin/net/taler/wallet/kotlin/reserve/ReserveManager.kt126
-rw-r--r--src/commonMain/kotlin/net/taler/wallet/kotlin/reserve/ReserveRecord.kt160
3 files changed, 287 insertions, 1 deletions
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/Time.kt b/src/commonMain/kotlin/net/taler/wallet/kotlin/Time.kt
index ebf445e..4143389 100644
--- a/src/commonMain/kotlin/net/taler/wallet/kotlin/Time.kt
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/Time.kt
@@ -80,6 +80,6 @@ data class Duration(
val ms: Long
) {
companion object {
- const val FOREVER: Long = -1
+ const val FOREVER: Long = -1 // TODO or UINT64_MAX?
}
}
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/reserve/ReserveManager.kt b/src/commonMain/kotlin/net/taler/wallet/kotlin/reserve/ReserveManager.kt
new file mode 100644
index 0000000..c8e7bd4
--- /dev/null
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/reserve/ReserveManager.kt
@@ -0,0 +1,126 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.kotlin.reserve
+
+import io.ktor.client.HttpClient
+import net.taler.wallet.kotlin.Amount
+import net.taler.wallet.kotlin.Base32Crockford
+import net.taler.wallet.kotlin.Db
+import net.taler.wallet.kotlin.DbFactory
+import net.taler.wallet.kotlin.Timestamp
+import net.taler.wallet.kotlin.crypto.Crypto
+import net.taler.wallet.kotlin.crypto.CryptoFactory
+import net.taler.wallet.kotlin.exchange.Exchange
+import net.taler.wallet.kotlin.getDefaultHttpClient
+import net.taler.wallet.kotlin.reserve.ReserveRecordStatus.REGISTERING_BANK
+import net.taler.wallet.kotlin.reserve.ReserveRecordStatus.UNCONFIRMED
+
+internal class ReserveManager(
+ private val crypto: Crypto = CryptoFactory.getCrypto(),
+ private val httpClient: HttpClient = getDefaultHttpClient(),
+ private val db: Db = DbFactory().openDb()
+) {
+
+ /**
+ * Request to create a reserve.
+ */
+ data class CreateReserveRequest(
+ /**
+ * The initial amount for the reserve.
+ */
+ val amount: Amount,
+
+ /**
+ * Exchange URL where the bank should create the reserve.
+ */
+ val exchangeBaseUrl: String,
+
+ /**
+ * Payto URI that identifies the exchange's account that the funds for this reserve go into.
+ */
+ val exchangePaytoUri: String,
+
+ /**
+ * Wire details (as a payto URI) for the bank account that sent the funds to the exchange.
+ */
+ val senderPaytoUri: String? = null,
+
+ /**
+ * URL to fetch the withdraw status from the bank.
+ */
+ val bankWithdrawStatusUrl: String? = null
+ )
+
+ /**
+ * Response for the create reserve request to the wallet.
+ */
+ data class CreateReserveResponse(
+ /**
+ * Exchange URL where the bank should create the reserve.
+ * The URL is normalized in the response.
+ */
+ val exchangeBaseUrl: String,
+
+ /**
+ * Reserve public key of the newly created reserve.
+ */
+ val reservePub: String
+ )
+
+ /**
+ * Create a reserve, but do not flag it as confirmed yet.
+ */
+ fun create(request: CreateReserveRequest): CreateReserveResponse {
+ val keyPair = crypto.createEddsaKeyPair()
+ val now = Timestamp.now()
+ val exchangeBaseUrl = Exchange.normalizeUrl(request.exchangeBaseUrl)
+ val bankInfo = request.bankWithdrawStatusUrl?.let { bankWithdrawStatusUrl ->
+ ReserveBankInfo(
+ statusUrl = bankWithdrawStatusUrl,
+ amount = request.amount,
+ bankWithdrawalGroupId = Base32Crockford.encode(crypto.getRandomBytes(32)),
+ withdrawalStarted = false
+ )
+ }
+ val reserve = Reserve(
+ reservePub = keyPair.encodedPublicKey,
+ reservePriv = keyPair.encodedPrivateKey,
+ exchangeBaseUrl = exchangeBaseUrl,
+ currency = request.amount.currency,
+ timestampCreated = now,
+ senderPaytoUri = request.senderPaytoUri,
+ exchangePaytoUri = request.exchangePaytoUri,
+ bankInfo = bankInfo,
+ reserveStatus = if (request.bankWithdrawStatusUrl == null) UNCONFIRMED else REGISTERING_BANK,
+ retryInfo = RetryInfo.getInitial()
+ )
+ // TODO store reserve history in DB
+ // TODO store senderPaytoUri in DB
+ // TODO store currency record in DB
+ // TODO store withdraw URI in DB
+// db.put(reserve)
+ return CreateReserveResponse(
+ exchangeBaseUrl = exchangeBaseUrl,
+ reservePub = keyPair.encodedPublicKey
+ )
+ }
+
+ fun processBankStatus(reservePub: String) {
+ TODO("Not yet implemented")
+ }
+
+}
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/reserve/ReserveRecord.kt b/src/commonMain/kotlin/net/taler/wallet/kotlin/reserve/ReserveRecord.kt
new file mode 100644
index 0000000..b618719
--- /dev/null
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/reserve/ReserveRecord.kt
@@ -0,0 +1,160 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.kotlin.reserve
+
+import net.taler.wallet.kotlin.Amount
+import net.taler.wallet.kotlin.Timestamp
+import net.taler.wallet.kotlin.exchange.DenominationSelectionInfo
+
+/**
+ * A reserve record as stored in the wallet's database.
+ */
+data class Reserve(
+ /**
+ * The reserve public key.
+ */
+ val reservePub: String,
+
+ /**
+ * The reserve private key.
+ */
+ val reservePriv: String,
+
+ /**
+ * The exchange base URL.
+ */
+ val exchangeBaseUrl: String,
+
+ /**
+ * Currency of the reserve.
+ */
+ val currency: String,
+
+ /**
+ * Time when the reserve was created.
+ */
+ val timestampCreated: Timestamp,
+
+ /**
+ * Time when the information about this reserve was posted to the bank.
+ *
+ * Only applies if bankWithdrawStatusUrl is not null.
+ *
+ * Set to 0 if that hasn't happened yet.
+ */
+ val timestampReserveInfoPosted: Timestamp? = null,
+
+ /**
+ * Time when the reserve was confirmed, either manually by the user or by the bank.
+ *
+ * Set to undefined if not confirmed yet.
+ */
+ val timestampConfirmed: Timestamp? = null,
+
+ /**
+ * Wire information (as payto URI) for the bank account that transferred funds for this reserve.
+ */
+ val senderPaytoUri: String?,
+
+ /**
+ * Wire information (as payto URI) for the exchange,
+ * specifically the account that was transferred to when creating the reserve.
+ */
+ val exchangePaytoUri: String,
+
+ /**
+ * Extra state for when this is a withdrawal involving a Taler-integrated bank.
+ */
+ val bankInfo: ReserveBankInfo?,
+
+ val reserveStatus: ReserveRecordStatus,
+
+ /**
+ * Time of the last successful status query.
+ */
+ val lastSuccessfulStatusQuery: Timestamp? = null,
+
+ /**
+ * Retry info. This field is present even if no retry is scheduled,
+ * because we need it to be present for the index on the object store
+ * to work.
+ */
+ val retryInfo: RetryInfo
+)
+
+enum class ReserveRecordStatus(val value: String) {
+ /**
+ * Waiting for manual confirmation.
+ */
+ UNCONFIRMED("unconfirmed"),
+
+ /**
+ * Reserve must be registered with the bank.
+ */
+ REGISTERING_BANK("registering-bank"),
+
+ /**
+ * We've registered reserve's information with the bank
+ * and are now waiting for the user to confirm the withdraw
+ * with the bank (typically 2nd factor auth).
+ */
+ WAIT_CONFIRM_BANK("wait-confirm-bank"),
+
+ /**
+ * Querying reserve status with the exchange.
+ */
+ QUERYING_STATUS("querying-status"),
+
+ /**
+ * Status is queried, the wallet must now select coins
+ * and start withdrawing.
+ */
+ WITHDRAWING("withdrawing"),
+
+ /**
+ * The corresponding withdraw record has been created.
+ * No further processing is done, unless explicitly requested
+ * by the user.
+ */
+ DORMANT("dormant")
+}
+
+data class ReserveBankInfo(
+ val statusUrl: String,
+ val confirmUrl: String? = null,
+ val amount: Amount,
+ val bankWithdrawalGroupId: String,
+ val withdrawalStarted: Boolean,
+ val denomSel: DenominationSelectionInfo? = null // TODO do we really need this? nullable for now
+)
+
+data class RetryInfo(
+ val firstTry: Timestamp,
+ val nextRetry: Timestamp,
+ val retryCounter: Int,
+ val active: Boolean
+) {
+ // TODO implement retry policies and set proper nextRetry
+ companion object {
+ fun getInitial() = RetryInfo(
+ firstTry = Timestamp.now(),
+ nextRetry = Timestamp(0),
+ retryCounter = 0,
+ active = true
+ )
+ }
+}