exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 4ebee4ebbc7dc4b3710f452a01eaa6309fc0015c
parent 23b13365faafb8fd018c151b069c1aafce7640a9
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 12 Aug 2026 23:51:54 +0200

implement protocol v39 for #10713

Diffstat:
Msrc/auditor/taler-helper-auditor-aggregation.c | 3+++
Msrc/exchange/taler-exchange-aggregator.c | 1+
Msrc/exchange/taler-exchange-httpd_get-config.h | 2+-
Msrc/exchange/taler-exchange-httpd_get-transfers-WTID.c | 24++++++++++++++++++++++++
Msrc/exchangedb/insert_wire_out.c | 7++++++-
Msrc/exchangedb/iterate_wire_transfers.c | 12++++++++++--
Msrc/exchangedb/sql-schema/meson.build | 1+
Msrc/include/exchange-database/insert_wire_out.h | 23+++++++++++------------
Msrc/include/exchange-database/iterate_wire_transfers.h | 8++++++++
Msrc/include/taler/exchange/get-transfers-WTID.h | 10++++++++++
Msrc/lib/exchange_api_get-transfers-WTID.c | 7+++++++
Msrc/lib/exchange_api_handle.c | 4++--
12 files changed, 84 insertions(+), 18 deletions(-)

diff --git a/src/auditor/taler-helper-auditor-aggregation.c b/src/auditor/taler-helper-auditor-aggregation.c @@ -787,6 +787,8 @@ check_transaction_history_for_deposit ( * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) * @param account_pay_uri where did we transfer the funds? * @param h_payto hash over @a account_payto_uri as it is in the DB + * @param exchange_payto_uri which of the exchange's accounts was debited, + * `full_payto` NULL if not recorded; unused here * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls) * @param h_contract_terms which proposal was this payment about * @param denom_pub denomination of @a coin_pub @@ -803,6 +805,7 @@ wire_transfer_information_cb ( const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_FullPayto account_pay_uri, const struct TALER_FullPaytoHashP *h_payto, + const struct TALER_FullPayto exchange_payto_uri, struct GNUNET_TIME_Timestamp exec_time, const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_DenominationPublicKey *denom_pub, diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c @@ -761,6 +761,7 @@ trigger_wire_transfer (struct AggregationUnit *au) &au->wtid, &au->h_full_payto, au->wa->section_name, + au->wa->payto_uri, &au->final_amount, au->extra_wire_subject_metadata); GNUNET_log (qs >= 0 diff --git a/src/exchange/taler-exchange-httpd_get-config.h b/src/exchange/taler-exchange-httpd_get-config.h @@ -41,7 +41,7 @@ * * Returned via both /config and /keys endpoints. */ -#define EXCHANGE_PROTOCOL_VERSION "38:0:4" +#define EXCHANGE_PROTOCOL_VERSION "39:0:5" /** diff --git a/src/exchange/taler-exchange-httpd_get-transfers-WTID.c b/src/exchange/taler-exchange-httpd_get-transfers-WTID.c @@ -89,6 +89,8 @@ struct AggregatedDepositDetail * @param total total amount that was transferred * @param merchant_pub public key of the merchant * @param payto_uri destination account + * @param exchange_payto_uri our own account that was debited, `full_payto` + * may be NULL if we did not record it * @param wire_fee wire fee that was charged * @param exec_time execution time of the wire transfer * @param wdd_head linked list with details about the combined deposits @@ -99,6 +101,7 @@ reply_transfer_details (struct MHD_Connection *connection, const struct TALER_Amount *total, const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_FullPayto payto_uri, + const struct TALER_FullPayto exchange_payto_uri, const struct TALER_Amount *wire_fee, struct GNUNET_TIME_Timestamp exec_time, const struct AggregatedDepositDetail *wdd_head) @@ -187,6 +190,11 @@ reply_transfer_details (struct MHD_Connection *connection, merchant_pub), GNUNET_JSON_pack_data_auto ("h_payto", &h_payto), + /* Not covered by @e exchange_sig: this is a convenience for the + merchant's bookkeeping, not a claim the client needs to prove. */ + GNUNET_JSON_pack_allow_null ( + TALER_JSON_pack_full_payto ("exchange_payto_uri", + exchange_payto_uri)), GNUNET_JSON_pack_timestamp ("execution_time", exec_time), GNUNET_JSON_pack_array_steal ("deposits", @@ -249,6 +257,14 @@ struct WtidTransactionContext struct TALER_FullPayto payto_uri; /** + * Which of our own accounts were the funds wired from? + * `full_payto` is NULL if we did not record this, which is + * the case for all transfers aggregated before the exchange + * started to store it. + */ + struct TALER_FullPayto exchange_payto_uri; + + /** * JSON array with details about the individual deposits. */ json_t *deposits; @@ -304,6 +320,8 @@ add_refunds (struct TALER_Amount *total, * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) * @param account_payto_uri where the funds were sent * @param h_payto hash over @a account_payto_uri as it is in the DB + * @param exchange_payto_uri our own account the funds were sent from, + * `full_payto` NULL if we did not record it * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls) * @param h_contract_terms which proposal was this payment about * @param denom_pub denomination public key of the @a coin_pub (ignored) @@ -318,6 +336,7 @@ handle_deposit_data ( const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_FullPayto account_payto_uri, const struct TALER_FullPaytoHashP *h_payto, + const struct TALER_FullPayto exchange_payto_uri, struct GNUNET_TIME_Timestamp exec_time, const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_DenominationPublicKey *denom_pub, @@ -403,6 +422,9 @@ handle_deposit_data ( /* First one we encounter, setup general information in 'ctx' */ ctx->merchant_pub = *merchant_pub; ctx->payto_uri.full_payto = GNUNET_strdup (account_payto_uri.full_payto); + if (NULL != exchange_payto_uri.full_payto) + ctx->exchange_payto_uri.full_payto + = GNUNET_strdup (exchange_payto_uri.full_payto); ctx->exec_time = exec_time; ctx->is_valid = GNUNET_YES; if (0 > @@ -484,6 +506,7 @@ free_ctx (struct WtidTransactionContext *ctx) GNUNET_free (wdd); } GNUNET_free (ctx->payto_uri.full_payto); + GNUNET_free (ctx->exchange_payto_uri.full_payto); } @@ -651,6 +674,7 @@ TEH_handler_transfers_get (struct TEH_RequestContext *rc, &ctx.total, &ctx.merchant_pub, ctx.payto_uri, + ctx.exchange_payto_uri, &ctx.fees.wire, ctx.exec_time, ctx.wdd_head); diff --git a/src/exchangedb/insert_wire_out.c b/src/exchangedb/insert_wire_out.c @@ -30,6 +30,7 @@ TALER_EXCHANGEDB_insert_wire_out ( const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_FullPaytoHashP *h_payto, const char *exchange_account_section, + const struct TALER_FullPayto exchange_payto_uri, const struct TALER_Amount *amount, const char *extra_wire_subject_metadata) { @@ -38,6 +39,9 @@ TALER_EXCHANGEDB_insert_wire_out ( GNUNET_PQ_query_param_auto_from_type (wtid), GNUNET_PQ_query_param_auto_from_type (h_payto), GNUNET_PQ_query_param_string (exchange_account_section), + NULL == exchange_payto_uri.full_payto + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_string (exchange_payto_uri.full_payto), TALER_PQ_query_param_amount (pg->conn, amount), NULL == extra_wire_subject_metadata @@ -53,10 +57,11 @@ TALER_EXCHANGEDB_insert_wire_out ( ",wtid_raw" ",wire_target_h_payto" ",exchange_account_section" + ",exchange_payto_uri" ",amount" ",extra_wire_subject_metadata" ") VALUES " - "($1, $2, $3, $4, $5, $6);"); + "($1, $2, $3, $4, $5, $6, $7);"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_wire_out", params); diff --git a/src/exchangedb/iterate_wire_transfers.c b/src/exchangedb/iterate_wire_transfers.c @@ -78,6 +78,9 @@ handle_wt_result (void *cls, struct TALER_Amount deposit_fee; struct TALER_DenominationPublicKey denom_pub; struct TALER_FullPayto payto_uri; + struct TALER_FullPayto exchange_payto_uri = { + .full_payto = NULL + }; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id", &rowid), @@ -87,6 +90,10 @@ handle_wt_result (void *cls, &payto_uri.full_payto), GNUNET_PQ_result_spec_auto_from_type ("wire_target_h_payto", &h_payto), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("exchange_payto_uri", + &exchange_payto_uri.full_payto), + NULL), TALER_PQ_result_spec_denom_pub ("denom_pub", &denom_pub), GNUNET_PQ_result_spec_auto_from_type ("coin_pub", @@ -116,6 +123,7 @@ handle_wt_result (void *cls, &merchant_pub, payto_uri, &h_payto, + exchange_payto_uri, exec_time, &h_contract_terms, &denom_pub, @@ -155,11 +163,11 @@ TALER_EXCHANGEDB_iterate_wire_transfers ( ",wt.wire_target_h_payto" ",kc.coin_pub" ",bdep.merchant_pub" - ",wire_out.execution_date" + ",wo.execution_date" ",cdep.amount_with_fee" ",denom.fee_deposit" ",denom.denom_pub" - // ",wo.payto_uri" => FIXME: #10713 + ",wo.exchange_payto_uri" " FROM aggregation_tracking" " JOIN batch_deposits bdep" " USING (batch_deposit_serial_id)" diff --git a/src/exchangedb/sql-schema/meson.build b/src/exchangedb/sql-schema/meson.build @@ -187,6 +187,7 @@ generated_sql = [ ['exchange-0011.sql', exchange_0011_sql], ['exchange-0012.sql', exchange_0012_sql], ['exchange-0013.sql', exchange_0013_sql], + ['exchange-0014.sql', ['exchange-0014.sql']], ['tops-0001.sql', ['tops-0001.sql']], ] diff --git a/src/include/exchange-database/insert_wire_out.h b/src/include/exchange-database/insert_wire_out.h @@ -34,22 +34,21 @@ * @param h_payto identifies the receiver account of the wire transfer * @param exchange_account_section configuration section of the exchange specifying the * exchange's bank account being used + * @param exchange_payto_uri full payto://-URI of the exchange's bank account + * that is being debited, can be NULL * @param amount amount that was transmitted * @param extra_wire_subject_metadata additional meta data for the wire transfer subject, can be NULL * @return transaction status code */ enum GNUNET_DB_QueryStatus -TALER_EXCHANGEDB_insert_wire_out (struct - TALER_EXCHANGEDB_PostgresContext *pg, - struct GNUNET_TIME_Timestamp date, - const struct - TALER_WireTransferIdentifierRawP *wtid - , - const struct TALER_FullPaytoHashP * - h_payto, - const char *exchange_account_section, - const struct TALER_Amount *amount, - const char * - extra_wire_subject_metadata); +TALER_EXCHANGEDB_insert_wire_out ( + struct TALER_EXCHANGEDB_PostgresContext *pg, + struct GNUNET_TIME_Timestamp date, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_FullPaytoHashP *h_payto, + const char *exchange_account_section, + const struct TALER_FullPayto exchange_payto_uri, + const struct TALER_Amount *amount, + const char *extra_wire_subject_metadata); #endif diff --git a/src/include/exchange-database/iterate_wire_transfers.h b/src/include/exchange-database/iterate_wire_transfers.h @@ -35,6 +35,9 @@ * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) * @param account_payto_uri which account did the transfer go to? * @param h_payto hash over @a account_payto_uri as it is in the DB + * @param exchange_payto_uri which of the exchange's own accounts was debited; + * `full_payto` is NULL if the exchange did not record this (transfers + * made before the database tracked it) * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls) * @param h_contract_terms which proposal was this payment about * @param denom_pub denomination of @a coin_pub @@ -55,6 +58,7 @@ typedef void const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_FullPayto account_payto_uri, const struct TALER_FullPaytoHashP *h_payto, + const struct TALER_FullPayto exchange_payto_uri, struct GNUNET_TIME_Timestamp exec_time, const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_DenominationPublicKey *denom_pub, @@ -73,6 +77,9 @@ typedef void * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) * @param account_payto_uri which account did the transfer go to? * @param h_payto hash over @a account_payto_uri as it is in the DB + * @param exchange_payto_uri which of the exchange's own accounts was debited; + * `full_payto` is NULL if the exchange did not record this (transfers + * made before the database tracked it) * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls) * @param h_contract_terms which proposal was this payment about * @param denom_pub denomination of @a coin_pub @@ -87,6 +94,7 @@ typedef void const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_FullPayto account_payto_uri, const struct TALER_FullPaytoHashP *h_payto, + const struct TALER_FullPayto exchange_payto_uri, struct GNUNET_TIME_Timestamp exec_time, const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_DenominationPublicKey *denom_pub, diff --git a/src/include/taler/exchange/get-transfers-WTID.h b/src/include/taler/exchange/get-transfers-WTID.h @@ -50,6 +50,16 @@ struct TALER_EXCHANGE_TransferData struct TALER_FullPaytoHashP h_payto; /** + * Full payto:// URI of the exchange's own bank account the transfer + * was made from. `full_payto` is NULL if the exchange did not provide + * it; the field is optional as it only exists since protocol v39 and + * even then only for transfers the exchange recorded it for. + * + * Note that this is NOT covered by @e exchange_sig. + */ + struct TALER_FullPayto exchange_payto_uri; + + /** * Time when the exchange claims to have performed the wire transfer. */ struct GNUNET_TIME_Timestamp execution_time; diff --git a/src/lib/exchange_api_get-transfers-WTID.c b/src/lib/exchange_api_get-transfers-WTID.c @@ -114,6 +114,13 @@ check_transfers_get_response_ok ( &td->merchant_pub), GNUNET_JSON_spec_fixed_auto ("h_payto", &td->h_payto), + /* Optional: only exchanges running protocol v39 or later return + this, and even those only for transfers they recorded it for. + Aliases into @a json, so it needs no cleanup. */ + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_full_payto_uri ("exchange_payto_uri", + &td->exchange_payto_uri), + NULL), GNUNET_JSON_spec_timestamp ("execution_time", &td->execution_time), GNUNET_JSON_spec_array_const ("deposits", diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c @@ -35,12 +35,12 @@ * Which version of the Taler protocol is implemented * by this library? Used to determine compatibility. */ -#define EXCHANGE_PROTOCOL_CURRENT 38 +#define EXCHANGE_PROTOCOL_CURRENT 39 /** * How many versions are we backwards compatible with? */ -#define EXCHANGE_PROTOCOL_AGE 4 +#define EXCHANGE_PROTOCOL_AGE 5 /** * Set to 1 for extra debug logging.