commit 7477ca4281127d9aea06418df72c43fe9f43cc2c
parent fae2a018d0de3cc92e01ded53f2ac462999980de
Author: Antoine A <>
Date: Wed, 8 Nov 2023 00:50:25 +0000
Improve monitor
Diffstat:
9 files changed, 253 insertions(+), 190 deletions(-)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
@@ -37,6 +37,7 @@ import org.slf4j.LoggerFactory
import tech.libeufin.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
+import kotlinx.coroutines.future.await
private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.bank.accountsMgmtHandlers")
@@ -527,7 +528,7 @@ private fun Routing.coreBankCashoutApi(db: Database, ctx: BankConfig) {
val process = ProcessBuilder(tanScript, res.tanInfo).start()
try {
process.outputWriter().use { it.write(res.tanCode) }
- process.waitFor(10, TimeUnit.MINUTES)
+ process.onExit().await()
} catch (e: Exception) {
process.destroy()
}
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
@@ -118,15 +118,19 @@ data class TokenRequest(
@Serializable
sealed class MonitorResponse {
- abstract val talerPayoutCount: Long
- abstract val talerPayoutInternalVolume: TalerAmount
+ abstract val talerInCount: Long
+ abstract val talerInInternalVolume: TalerAmount
+ abstract val talerOutCount: Long
+ abstract val talerOutInternalVolume: TalerAmount
}
@Serializable
@SerialName("just-payouts")
data class MonitorJustPayouts(
- override val talerPayoutCount: Long,
- override val talerPayoutInternalVolume: TalerAmount
+ override val talerInCount: Long,
+ override val talerInInternalVolume: TalerAmount,
+ override val talerOutCount: Long,
+ override val talerOutInternalVolume: TalerAmount
) : MonitorResponse()
@Serializable
@@ -136,8 +140,10 @@ data class MonitorWithCashout(
val cashinExternalVolume: TalerAmount,
val cashoutCount: Long,
val cashoutExternalVolume: TalerAmount,
- override val talerPayoutCount: Long,
- override val talerPayoutInternalVolume: TalerAmount
+ override val talerInCount: Long,
+ override val talerInInternalVolume: TalerAmount,
+ override val talerOutCount: Long,
+ override val talerOutInternalVolume: TalerAmount
) : MonitorResponse()
/**
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/Database.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/Database.kt
@@ -621,10 +621,9 @@ class Database(dbConfig: String, internal val bankCurrency: String, internal val
if (it.getBoolean("out_creditor_is_exchange")) {
val rowId = it.getLong("out_credit_row_id")
if (metadata is IncomingTxMetadata) {
- val stmt = conn.prepareStatement("CALL register_incoming(?, ?, ?)")
+ val stmt = conn.prepareStatement("CALL register_incoming(?, ?)")
stmt.setBytes(1, metadata.reservePub.raw)
stmt.setLong(2, rowId)
- stmt.setLong(3, creditorAccountId)
stmt.executeUpdate()
} else {
// TODO bounce
@@ -828,14 +827,17 @@ class Database(dbConfig: String, internal val bankCurrency: String, internal val
val stmt = conn.prepareStatement("""
SELECT
cashin_count
- ,(cashin_volume_in_fiat).val as cashin_volume_in_fiat_val
- ,(cashin_volume_in_fiat).frac as cashin_volume_in_fiat_frac
+ ,(cashin_volume).val as cashin_volume_val
+ ,(cashin_volume).frac as cashin_volume_frac
,cashout_count
- ,(cashout_volume_in_fiat).val as cashout_volume_in_fiat_val
- ,(cashout_volume_in_fiat).frac as cashout_volume_in_fiat_frac
- ,internal_taler_payments_count
- ,(internal_taler_payments_volume).val as internal_taler_payments_volume_val
- ,(internal_taler_payments_volume).frac as internal_taler_payments_volume_frac
+ ,(cashout_volume).val as cashout_volume_val
+ ,(cashout_volume).frac as cashout_volume_frac
+ ,taler_in_count
+ ,(taler_in_volume).val as taler_in_volume_val
+ ,(taler_in_volume).frac as taler_in_volume_frac
+ ,taler_out_count
+ ,(taler_out_volume).val as taler_out_volume_val
+ ,(taler_out_volume).frac as taler_out_volume_frac
FROM stats_get_frame(now()::timestamp, ?::stat_timeframe_enum, ?)
""")
stmt.setString(1, params.timeframe.name)
@@ -849,28 +851,40 @@ class Database(dbConfig: String, internal val bankCurrency: String, internal val
MonitorWithCashout(
cashinCount = it.getLong("cashin_count"),
cashinExternalVolume = TalerAmount(
- value = it.getLong("cashin_volume_in_fiat_val"),
- frac = it.getInt("cashin_volume_in_fiat_frac"),
+ value = it.getLong("cashin_volume_val"),
+ frac = it.getInt("cashin_volume_frac"),
currency = this
),
cashoutCount = it.getLong("cashout_count"),
cashoutExternalVolume = TalerAmount(
- value = it.getLong("cashout_volume_in_fiat_val"),
- frac = it.getInt("cashout_volume_in_fiat_frac"),
+ value = it.getLong("cashout_volume_val"),
+ frac = it.getInt("cashout_volume_frac"),
currency = this
),
- talerPayoutCount = it.getLong("internal_taler_payments_count"),
- talerPayoutInternalVolume = TalerAmount(
- value = it.getLong("internal_taler_payments_volume_val"),
- frac = it.getInt("internal_taler_payments_volume_frac"),
+ talerInCount = it.getLong("taler_in_count"),
+ talerInInternalVolume = TalerAmount(
+ value = it.getLong("taler_in_volume_val"),
+ frac = it.getInt("taler_in_volume_frac"),
+ currency = bankCurrency
+ ),
+ talerOutCount = it.getLong("taler_out_count"),
+ talerOutInternalVolume = TalerAmount(
+ value = it.getLong("taler_out_volume_val"),
+ frac = it.getInt("taler_out_volume_frac"),
currency = bankCurrency
)
)
} ?: MonitorJustPayouts(
- talerPayoutCount = it.getLong("internal_taler_payments_count"),
- talerPayoutInternalVolume = TalerAmount(
- value = it.getLong("internal_taler_payments_volume_val"),
- frac = it.getInt("internal_taler_payments_volume_frac"),
+ talerInCount = it.getLong("taler_in_count"),
+ talerInInternalVolume = TalerAmount(
+ value = it.getLong("taler_in_volume_val"),
+ frac = it.getInt("taler_in_volume_frac"),
+ currency = bankCurrency
+ ),
+ talerOutCount = it.getLong("taler_out_count"),
+ talerOutInternalVolume = TalerAmount(
+ value = it.getLong("taler_out_volume_val"),
+ frac = it.getInt("taler_out_volume_frac"),
currency = bankCurrency
)
)
diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt
@@ -1030,26 +1030,6 @@ class CoreBankWithdrawalApiTest {
class CoreBankCashoutApiTest {
- suspend fun smsCode(info: String): String? {
- val file = File("/tmp/tan-$info.txt");
- if (file.exists()) {
- val code = file.readText()
- file.delete()
- return code;
- } else {
- return null
- }
- }
-
-
- private suspend fun ApplicationTestBuilder.convert(amount: String): TalerAmount {
- // Check conversion
- client.get("/cashout-rate?amount_debit=$amount").assertOk().run {
- val resp = json<ConversionResponse>()
- return resp.amount_credit
- }
- }
-
// POST /accounts/{USERNAME}/cashouts
@Test
fun create() = bankSetup { _ ->
diff --git a/bank/src/test/kotlin/StatsTest.kt b/bank/src/test/kotlin/StatsTest.kt
@@ -25,6 +25,7 @@ import io.ktor.server.testing.*
import java.time.*
import java.util.*
import kotlin.test.*
+import kotlin.reflect.full.declaredMemberProperties
import kotlinx.serialization.json.Json
import org.junit.Test
import tech.libeufin.bank.*
@@ -34,37 +35,79 @@ class StatsTest {
@Test
fun transfer() = bankSetup { _ ->
setMaxDebt("exchange", TalerAmount("KUDOS:1000"))
-
- suspend fun transfer(amount: TalerAmount) {
- client.post("/accounts/exchange/taler-wire-gateway/transfer") {
- basicAuth("exchange", "exchange-password")
- jsonBody {
- "request_uid" to randHashCode()
- "amount" to amount
- "exchange_base_url" to "http://exchange.example.com/"
- "wtid" to randShortHashCode()
- "credit_account" to "payto://iban/MERCHANT-IBAN-XYZ"
+ setMaxDebt("customer", TalerAmount("KUDOS:1000"))
+ client.patch("/accounts/customer") {
+ basicAuth("customer", "customer-password")
+ jsonBody(json {
+ "cashout_payto_uri" to IbanPayTo(genIbanPaytoUri())
+ "challenge_contact_data" to json {
+ "phone" to "+99"
}
- }.assertOk()
+ })
+ }.assertNoContent()
+
+ suspend fun cashout(amount: String) {
+ client.post("/accounts/customer/cashouts") {
+ basicAuth("customer", "customer-password")
+ jsonBody(json {
+ "request_uid" to randShortHashCode()
+ "amount_debit" to amount
+ "amount_credit" to convert(amount)
+ })
+ }.assertOk().run {
+ val uuid = json<CashoutPending>().cashout_id
+ client.post("/accounts/customer/cashouts/$uuid/confirm") {
+ basicAuth("customer", "customer-password")
+ jsonBody { "tan" to smsCode("+99") }
+ }.assertNoContent()
+ }
}
- suspend fun monitor(count: Long, amount: TalerAmount) {
+ suspend fun monitor(countName: String, volumeName: String, count: Long, amount: String) {
Timeframe.entries.forEach { timestamp ->
client.get("/monitor?timestamp=${timestamp.name}") { basicAuth("admin", "admin-password") }.assertOk().run {
val resp = json<MonitorResponse>()
- assertEquals(count, resp.talerPayoutCount)
- assertEquals(amount, resp.talerPayoutInternalVolume)
+ assertEquals(count, resp.javaClass.kotlin.declaredMemberProperties.first { it.name == countName }.get(resp))
+ assertEquals(TalerAmount(amount), resp.javaClass.kotlin.declaredMemberProperties.first { it.name == volumeName }.get(resp))
}
}
}
- monitor(0, TalerAmount("KUDOS:0"))
- transfer(TalerAmount("KUDOS:10.0"))
- monitor(1, TalerAmount("KUDOS:10.0"))
- transfer(TalerAmount("KUDOS:30.5"))
- monitor(2, TalerAmount("KUDOS:40.5"))
- transfer(TalerAmount("KUDOS:42"))
- monitor(3, TalerAmount("KUDOS:82.5"))
+ suspend fun monitorTalerOut(count: Long, amount: String) = monitor("talerOutCount" , "talerOutInternalVolume", count, amount)
+ suspend fun monitorTalerIn(count: Long, amount: String) = monitor("talerInCount" , "talerInInternalVolume", count, amount)
+ suspend fun monitorCashin(count: Long, amount: String) = monitor("cashinCount" , "cashinExternalVolume", count, amount)
+ suspend fun monitorCashout(count: Long, amount: String) = monitor("cashoutCount" , "cashoutExternalVolume", count, amount)
+
+ monitorTalerOut(0, "KUDOS:0")
+ monitorTalerIn(0, "KUDOS:0")
+ monitorCashin(0, "FIAT:0")
+ monitorCashout(0, "FIAT:0")
+
+ transfer("KUDOS:10.0")
+ monitorTalerOut(1, "KUDOS:10.0")
+ transfer("KUDOS:30.5")
+ monitorTalerOut(2, "KUDOS:40.5")
+ transfer("KUDOS:42")
+ monitorTalerOut(3, "KUDOS:82.5")
+
+ addIncoming("KUDOS:3")
+ monitorTalerIn(1, "KUDOS:3")
+ addIncoming("KUDOS:7.6")
+ monitorTalerIn(2, "KUDOS:10.6")
+ addIncoming("KUDOS:12.3")
+ monitorTalerIn(3, "KUDOS:22.9")
+
+ cashout("KUDOS:3")
+ monitorCashout(1, "FIAT:3.747")
+ cashout("KUDOS:7.6")
+ monitorCashout(2, "FIAT:13.244")
+ cashout("KUDOS:12.3")
+ monitorCashout(3, "FIAT:28.616")
+
+ monitorTalerOut(3, "KUDOS:82.5")
+ monitorTalerIn(3, "KUDOS:22.9")
+ monitorCashin(0, "FIAT:0")
+ monitorCashout(3, "FIAT:28.616")
}
@Test
@@ -73,7 +116,7 @@ class StatsTest {
suspend fun register(now: OffsetDateTime, amount: TalerAmount) {
val stmt =
conn.prepareStatement(
- "CALL stats_register_internal_taler_payment(?::timestamp, (?, ?)::taler_amount)"
+ "CALL stats_register_payment('taler_out', ?::timestamp, (?, ?)::taler_amount)"
)
stmt.setObject(1, now)
stmt.setLong(2, amount.value)
@@ -88,17 +131,14 @@ class StatsTest {
count: Long,
amount: TalerAmount
) {
- val stmt = conn.prepareStatement(
- """
+ val stmt = conn.prepareStatement("""
SELECT
- internal_taler_payments_count
- ,(internal_taler_payments_volume).val as internal_taler_payments_volume_val
- ,(internal_taler_payments_volume).frac as internal_taler_payments_volume_frac
+ taler_out_count
+ ,(taler_out_volume).val as taler_out_volume_val
+ ,(taler_out_volume).frac as taler_out_volume_frac
FROM stats_get_frame(?::timestamp, ?::stat_timeframe_enum, ?)
- """
- )
+ """)
stmt.setObject(1, now)
-
stmt.setString(2, timeframe.name)
if (which != null) {
stmt.setInt(3, which)
@@ -106,16 +146,15 @@ class StatsTest {
stmt.setNull(3, java.sql.Types.INTEGER)
}
stmt.oneOrNull {
- val talerPayoutCount = it.getLong("internal_taler_payments_count")
- val talerPayoutInternalVolume =
- TalerAmount(
- value = it.getLong("internal_taler_payments_volume_val"),
- frac = it.getInt("internal_taler_payments_volume_frac"),
- currency = "KUDOS"
- )
- println("$timeframe $talerPayoutCount $talerPayoutInternalVolume")
- assertEquals(count, talerPayoutCount)
- assertEquals(amount, talerPayoutInternalVolume)
+ val talerOutCount = it.getLong("taler_out_count")
+ val talerOutInternalVolume = TalerAmount(
+ value = it.getLong("taler_out_volume_val"),
+ frac = it.getInt("taler_out_volume_frac"),
+ currency = "KUDOS"
+ )
+ println("$timeframe $talerOutCount $talerOutInternalVolume")
+ assertEquals(count, talerOutCount)
+ assertEquals(amount, talerOutInternalVolume)
}!!
}
diff --git a/bank/src/test/kotlin/WireGatewayApiTest.kt b/bank/src/test/kotlin/WireGatewayApiTest.kt
@@ -17,36 +17,6 @@ import kotlin.test.assertNotNull
import randHashCode
class WireGatewayApiTest {
- suspend fun Database.genTransfer(from: String, to: IbanPayTo, amount: String = "KUDOS:10") {
- exchange.transfer(
- req = TransferRequest(
- request_uid = randHashCode(),
- amount = TalerAmount(amount),
- exchange_base_url = ExchangeUrl("http://exchange.example.com/"),
- wtid = randShortHashCode(),
- credit_account = to
- ),
- username = from,
- timestamp = Instant.now()
- ).run {
- assertEquals(TalerTransferResult.SUCCESS, txResult)
- }
- }
-
- suspend fun Database.genIncoming(to: String, from: IbanPayTo) {
- exchange.addIncoming(
- req = AddIncomingRequest(
- reserve_pub = randShortHashCode(),
- amount = TalerAmount("KUDOS:10"),
- debit_account = from,
- ),
- username = to,
- timestamp = Instant.now()
- ).run {
- assertEquals(TalerAddIncomingResult.SUCCESS, txResult)
- }
- }
-
suspend fun Database.genTransaction(from: String, to: IbanPayTo, subject: String) {
bankTransaction(
creditAccountPayto = to,
@@ -245,7 +215,7 @@ class WireGatewayApiTest {
// Gen three transactions using clean add incoming logic
repeat(3) {
- db.genIncoming("exchange", IbanPayTo("payto://iban/MERCHANT-IBAN-XYZ"))
+ addIncoming("KUDOS:10")
}
// Should not show up in the taler wire gateway API history
db.genTransaction("merchant", IbanPayTo("payto://iban/exchange-IBAN-XYZ"), "bogus")
@@ -311,7 +281,7 @@ class WireGatewayApiTest {
}
}
delay(200)
- db.genIncoming("exchange", IbanPayTo("payto://iban/MERCHANT-IBAN-XYZ"))
+ addIncoming("KUDOS:10")
}
// Test trigger by raw transaction
@@ -357,7 +327,7 @@ class WireGatewayApiTest {
// Testing ranges.
repeat(20) {
- db.genIncoming("exchange", IbanPayTo("payto://iban/MERCHANT-IBAN-XYZ"))
+ addIncoming("KUDOS:10")
}
// forward range:
@@ -414,7 +384,7 @@ class WireGatewayApiTest {
// Gen three transactions using clean transfer logic
repeat(3) {
- db.genTransfer("exchange", IbanPayTo("payto://iban/MERCHANT-IBAN-XYZ"))
+ transfer("KUDOS:10")
}
// Should not show up in the taler wire gateway API history
db.genTransaction("exchange", IbanPayTo("payto://iban/MERCHANT-IBAN-XYZ"), "bogus")
@@ -465,12 +435,12 @@ class WireGatewayApiTest {
}
}
delay(200)
- db.genTransfer("exchange", IbanPayTo("payto://iban/MERCHANT-IBAN-XYZ"))
+ transfer("KUDOS:10")
}
// Testing ranges.
repeat(20) {
- db.genTransfer("exchange", IbanPayTo("payto://iban/MERCHANT-IBAN-XYZ"))
+ transfer("KUDOS:10")
}
// forward range:
diff --git a/bank/src/test/kotlin/helpers.kt b/bank/src/test/kotlin/helpers.kt
@@ -9,6 +9,7 @@ import net.taler.common.errorcodes.TalerErrorCode
import kotlin.test.*
import tech.libeufin.bank.*
import java.io.ByteArrayOutputStream
+import java.io.File
import java.util.zip.DeflaterOutputStream
import tech.libeufin.util.CryptoUtil
import tech.libeufin.util.*
@@ -102,6 +103,47 @@ suspend fun ApplicationTestBuilder.assertBalance(account: String, info: CreditDe
}
}
+suspend fun ApplicationTestBuilder.transfer(amount: String) {
+ client.post("/accounts/exchange/taler-wire-gateway/transfer") {
+ basicAuth("exchange", "exchange-password")
+ jsonBody {
+ "request_uid" to randHashCode()
+ "amount" to TalerAmount(amount)
+ "exchange_base_url" to "http://exchange.example.com/"
+ "wtid" to randShortHashCode()
+ "credit_account" to "payto://iban/MERCHANT-IBAN-XYZ"
+ }
+ }.assertOk()
+}
+
+suspend fun ApplicationTestBuilder.addIncoming(amount: String) {
+ client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") {
+ basicAuth("admin", "admin-password")
+ jsonBody {
+ "amount" to TalerAmount(amount)
+ "reserve_pub" to randEddsaPublicKey()
+ "debit_account" to "payto://iban/MERCHANT-IBAN-XYZ"
+ }
+ }.assertOk()
+}
+
+suspend fun ApplicationTestBuilder.convert(amount: String): TalerAmount {
+ client.get("/cashout-rate?amount_debit=$amount").assertOk().run {
+ return json<ConversionResponse>().amount_credit
+ }
+}
+
+suspend fun smsCode(info: String): String? {
+ val file = File("/tmp/tan-$info.txt");
+ if (file.exists()) {
+ val code = file.readText()
+ file.delete()
+ return code;
+ } else {
+ return null
+ }
+}
+
/* ----- Assert ----- */
diff --git a/database-versioning/libeufin-bank-0001.sql b/database-versioning/libeufin-bank-0001.sql
@@ -250,27 +250,31 @@ COMMENT ON COLUMN taler_withdrawal_operations.confirmation_done
-- end of: Taler integration
-- start of: Statistics
-CREATE TABLE IF NOT EXISTS regional_stats (
+CREATE TABLE IF NOT EXISTS bank_stats (
timeframe stat_timeframe_enum NOT NULL
,start_time timestamp NOT NULL
- ,cashin_count BIGINT NOT NULL
- ,cashin_volume_in_fiat taler_amount NOT NULL
- ,cashout_count BIGINT NOT NULL
- ,cashout_volume_in_fiat taler_amount NOT NULL
- ,internal_taler_payments_count BIGINT NOT NULL
- ,internal_taler_payments_volume taler_amount NOT NULL
+ ,cashin_count BIGINT NOT NULL DEFAULT 0
+ ,cashin_volume taler_amount NOT NULL DEFAULT (0, 0)
+ ,cashout_count BIGINT NOT NULL DEFAULT 0
+ ,cashout_volume taler_amount NOT NULL DEFAULT (0, 0)
+ ,taler_in_count BIGINT NOT NULL DEFAULT 0
+ ,taler_in_volume taler_amount NOT NULL DEFAULT (0, 0)
+ ,taler_out_count BIGINT NOT NULL DEFAULT 0
+ ,taler_out_volume taler_amount NOT NULL DEFAULT (0, 0)
,PRIMARY KEY (start_time, timeframe)
);
-- TODO garbage collection
-COMMENT ON TABLE regional_stats IS 'Stores statistics about the regional currency usage.';
-COMMENT ON COLUMN regional_stats.timeframe IS 'particular timeframe that this row accounts for';
-COMMENT ON COLUMN regional_stats.start_time IS 'timestamp of the start of the timeframe that this row accounts for, truncated according to the precision of the timeframe';
-COMMENT ON COLUMN regional_stats.cashin_count IS 'how many cashin operations took place in the timeframe';
-COMMENT ON COLUMN regional_stats.cashin_volume_in_fiat IS 'how much fiat currency was cashed in in the timeframe';
-COMMENT ON COLUMN regional_stats.cashout_count IS 'how many cashout operations took place in the timeframe';
-COMMENT ON COLUMN regional_stats.cashout_volume_in_fiat IS 'how much fiat currency was payed by the bank to customers in the timeframe';
-COMMENT ON COLUMN regional_stats.internal_taler_payments_count IS 'how many internal payments were made by a Taler exchange';
-COMMENT ON COLUMN regional_stats.internal_taler_payments_volume IS 'how much internal currency was paid by a Taler exchange';
+COMMENT ON TABLE bank_stats IS 'Stores statistics about the bank usage.';
+COMMENT ON COLUMN bank_stats.timeframe IS 'particular timeframe that this row accounts for';
+COMMENT ON COLUMN bank_stats.start_time IS 'timestamp of the start of the timeframe that this row accounts for, truncated according to the precision of the timeframe';
+COMMENT ON COLUMN bank_stats.cashin_count IS 'how many cashin operations took place in the timeframe';
+COMMENT ON COLUMN bank_stats.cashin_volume IS 'how much fiat currency was cashed in in the timeframe';
+COMMENT ON COLUMN bank_stats.cashout_count IS 'how many cashout operations took place in the timeframe';
+COMMENT ON COLUMN bank_stats.cashout_volume IS 'how much fiat currency was payed by the bank to customers in the timeframe';
+COMMENT ON COLUMN bank_stats.taler_out_count IS 'how many internal payments were made by a Taler exchange';
+COMMENT ON COLUMN bank_stats.taler_out_volume IS 'how much internal currency was paid by a Taler exchange';
+COMMENT ON COLUMN bank_stats.taler_in_count IS 'how many internal payments were made to a Taler exchange';
+COMMENT ON COLUMN bank_stats.taler_in_volume IS 'how much internal currency was paid to a Taler exchange';
-- end of: Statistics
diff --git a/database-versioning/libeufin-bank-procedures.sql b/database-versioning/libeufin-bank-procedures.sql
@@ -265,7 +265,7 @@ DECLARE
local_amount taler_amount;
local_bank_account_id BIGINT;
BEGIN
--- Register outgoing transaction
+-- register outgoing transaction
INSERT
INTO taler_exchange_outgoing (
request_uid,
@@ -279,10 +279,11 @@ INSERT
in_tx_row_id
);
-- TODO check if not drain
+-- update stats
SELECT (amount).val, (amount).frac, bank_account_id
INTO local_amount.val, local_amount.frac, local_bank_account_id
FROM bank_account_transactions WHERE bank_transaction_id=in_tx_row_id;
-CALL stats_register_internal_taler_payment(now()::TIMESTAMP, local_amount);
+CALL stats_register_payment('taler_out', now()::TIMESTAMP, local_amount);
-- notify new transaction
PERFORM pg_notify('outgoing_tx', local_bank_account_id || ' ' || in_tx_row_id);
END $$;
@@ -291,10 +292,12 @@ COMMENT ON PROCEDURE register_outgoing
CREATE OR REPLACE PROCEDURE register_incoming(
IN in_reserve_pub BYTEA,
- IN in_tx_row_id BIGINT,
- IN in_exchange_bank_account_id BIGINT
+ IN in_tx_row_id BIGINT
)
LANGUAGE plpgsql AS $$
+DECLARE
+local_amount taler_amount;
+local_bank_account_id BIGINT;
BEGIN
-- Register incoming transaction
INSERT
@@ -305,8 +308,13 @@ INSERT
in_reserve_pub,
in_tx_row_id
);
+-- update stats
+SELECT (amount).val, (amount).frac, bank_account_id
+INTO local_amount.val, local_amount.frac, local_bank_account_id
+FROM bank_account_transactions WHERE bank_transaction_id=in_tx_row_id;
+CALL stats_register_payment('taler_in', now()::TIMESTAMP, local_amount);
-- notify new transaction
-PERFORM pg_notify('incoming_tx', in_exchange_bank_account_id || ' ' || in_tx_row_id);
+PERFORM pg_notify('incoming_tx', local_bank_account_id || ' ' || in_tx_row_id);
END $$;
COMMENT ON PROCEDURE register_incoming
IS 'Register a bank transaction as a taler incoming transaction';
@@ -484,7 +492,7 @@ IF out_debitor_balance_insufficient THEN
RETURN;
END IF;
-- Register incoming transaction
-CALL register_incoming(in_reserve_pub, out_tx_row_id, exchange_bank_account_id);
+CALL register_incoming(in_reserve_pub, out_tx_row_id);
END $$;
-- TODO new comment
COMMENT ON FUNCTION taler_add_incoming IS 'function that (1) inserts the TWG requests'
@@ -741,7 +749,7 @@ UPDATE taler_withdrawal_operations
WHERE withdrawal_uuid=in_withdrawal_uuid;
-- Register incoming transaction
-CALL register_incoming(reserve_pub_local, tx_row_id, exchange_bank_account_id);
+CALL register_incoming(reserve_pub_local, tx_row_id);
END $$;
COMMENT ON FUNCTION confirm_taler_withdrawal
IS 'Set a withdrawal operation as confirmed and wire the funds to the exchange.';
@@ -1188,6 +1196,9 @@ END IF;
UPDATE cashout_operations
SET local_transaction = tx_id
WHERE cashout_uuid=in_cashout_uuid;
+
+-- update stats
+CALL stats_register_payment('cashout', now()::TIMESTAMP, amount_credit_local);
END $$;
CREATE OR REPLACE FUNCTION challenge_create (
@@ -1282,11 +1293,13 @@ CREATE OR REPLACE FUNCTION stats_get_frame(
IN in_timeframe stat_timeframe_enum,
IN which INTEGER,
OUT cashin_count BIGINT,
- OUT cashin_volume_in_fiat taler_amount,
+ OUT cashin_volume taler_amount,
OUT cashout_count BIGINT,
- OUT cashout_volume_in_fiat taler_amount,
- OUT internal_taler_payments_count BIGINT,
- OUT internal_taler_payments_volume taler_amount
+ OUT cashout_volume taler_amount,
+ OUT taler_in_count BIGINT,
+ OUT taler_in_volume taler_amount,
+ OUT taler_out_count BIGINT,
+ OUT taler_out_volume taler_amount
)
LANGUAGE plpgsql AS $$
DECLARE
@@ -1301,61 +1314,55 @@ BEGIN
END;
SELECT
s.cashin_count
- ,(s.cashin_volume_in_fiat).val
- ,(s.cashin_volume_in_fiat).frac
+ ,(s.cashin_volume).val
+ ,(s.cashin_volume).frac
,s.cashout_count
- ,(s.cashout_volume_in_fiat).val
- ,(s.cashout_volume_in_fiat).frac
- ,s.internal_taler_payments_count
- ,(s.internal_taler_payments_volume).val
- ,(s.internal_taler_payments_volume).frac
+ ,(s.cashout_volume).val
+ ,(s.cashout_volume).frac
+ ,s.taler_in_count
+ ,(s.taler_in_volume).val
+ ,(s.taler_in_volume).frac
+ ,s.taler_out_count
+ ,(s.taler_out_volume).val
+ ,(s.taler_out_volume).frac
INTO
cashin_count
- ,cashin_volume_in_fiat.val
- ,cashin_volume_in_fiat.frac
+ ,cashin_volume.val
+ ,cashin_volume.frac
,cashout_count
- ,cashout_volume_in_fiat.val
- ,cashout_volume_in_fiat.frac
- ,internal_taler_payments_count
- ,internal_taler_payments_volume.val
- ,internal_taler_payments_volume.frac
- FROM regional_stats AS s
+ ,cashout_volume.val
+ ,cashout_volume.frac
+ ,taler_in_count
+ ,taler_in_volume.val
+ ,taler_in_volume.frac
+ ,taler_out_count
+ ,taler_out_volume.val
+ ,taler_out_volume.frac
+ FROM bank_stats AS s
WHERE s.timeframe = in_timeframe
AND s.start_time = local_start_time;
END $$;
-CREATE OR REPLACE PROCEDURE stats_register_internal_taler_payment(
+CREATE OR REPLACE PROCEDURE stats_register_payment(
+ IN name TEXT,
IN now TIMESTAMP,
IN amount taler_amount
)
LANGUAGE plpgsql AS $$
DECLARE
frame stat_timeframe_enum;
+ query TEXT;
BEGIN
+ query = format('INSERT INTO bank_stats AS s '
+ '(timeframe, start_time, %1$I_count, %1$I_volume) '
+ 'VALUES ($1, $2, 1, $3) '
+ 'ON CONFLICT (timeframe, start_time) DO UPDATE '
+ 'SET %1$I_count=s.%1$I_count+1, '
+ ' %1$I_volume=(SELECT amount_add(s.%1$I_volume, $3))',
+ name);
+
FOREACH frame IN ARRAY enum_range(null::stat_timeframe_enum) LOOP
- INSERT INTO regional_stats AS s (
- timeframe
- ,start_time
- ,cashin_count
- ,cashin_volume_in_fiat
- ,cashout_count
- ,cashout_volume_in_fiat
- ,internal_taler_payments_count
- ,internal_taler_payments_volume
- )
- VALUES (
- frame
- ,date_trunc(frame::text, now)
- ,0
- ,(0, 0)::taler_amount
- ,0
- ,(0, 0)::taler_amount
- ,1
- ,amount
- )
- ON CONFLICT (timeframe, start_time) DO UPDATE
- SET internal_taler_payments_count = s.internal_taler_payments_count+1
- ,internal_taler_payments_volume = (SELECT amount_add(s.internal_taler_payments_volume, amount));
+ EXECUTE query USING frame, date_trunc(frame::text, now), amount;
END LOOP;
END $$;