exchange

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

commit daaa5f3d6e5a1594514fadffdd6e54ba8d7caba2
parent eadd86ae0dd44d4135a5ad020a73babe54963a7d
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri, 20 Jun 2025 15:49:15 +0200

fix coin history implementation with new crypto-mas (#9975)

Diffstat:
Msrc/exchange/taler-exchange-httpd_coins_get.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/exchangedb/pg_get_coin_transactions.c | 25++++++++++++++++++++++++-
Msrc/include/taler_exchangedb_plugin.h | 15++++++++++++---
Msrc/util/wallet_signatures.c | 9++-------
4 files changed, 96 insertions(+), 21 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_coins_get.c b/src/exchange/taler-exchange-httpd_coins_get.c @@ -116,27 +116,46 @@ compile_transaction_history ( GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("type", "DEPOSIT"), + GNUNET_JSON_pack_uint64 ("history_offset", + pos->coin_history_id), TALER_JSON_pack_amount ("amount", &deposit->amount_with_fee), TALER_JSON_pack_amount ("deposit_fee", &deposit->deposit_fee), + GNUNET_JSON_pack_data_auto ("merchant_pub", + &deposit->merchant_pub), GNUNET_JSON_pack_timestamp ("timestamp", deposit->timestamp), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_timestamp ("refund_deadline", deposit->refund_deadline)), - GNUNET_JSON_pack_data_auto ("merchant_pub", - &deposit->merchant_pub), GNUNET_JSON_pack_data_auto ("h_contract_terms", &deposit->h_contract_terms), GNUNET_JSON_pack_data_auto ("h_wire", &h_wire), + GNUNET_JSON_pack_data_auto ("h_denom_pub", + &deposit->h_denom_pub), GNUNET_JSON_pack_allow_null ( - deposit->no_age_commitment ? - GNUNET_JSON_pack_string ( - "h_age_commitment", NULL) : - GNUNET_JSON_pack_data_auto ("h_age_commitment", - &deposit->h_age_commitment)), + deposit->has_policy + ? GNUNET_JSON_pack_data_auto ("h_policy", + &deposit->h_policy) + : GNUNET_JSON_pack_string ( + "h_policy", + NULL)), + GNUNET_JSON_pack_allow_null ( + deposit->no_wallet_data_hash + ? GNUNET_JSON_pack_string ( + "wallet_data_hash", + NULL) + : GNUNET_JSON_pack_data_auto ("wallet_data_hash", + &deposit->wallet_data_hash)), + GNUNET_JSON_pack_allow_null ( + deposit->no_age_commitment + ? GNUNET_JSON_pack_string ( + "h_age_commitment", + NULL) + : GNUNET_JSON_pack_data_auto ("h_age_commitment", + &deposit->h_age_commitment)), GNUNET_JSON_pack_data_auto ("coin_sig", &deposit->csig)))) { @@ -170,7 +189,6 @@ compile_transaction_history ( return NULL; } #endif - phac = (melt->no_age_commitment) ? NULL : &melt->h_age_commitment; @@ -183,12 +201,16 @@ compile_transaction_history ( GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("type", "MELT"), + GNUNET_JSON_pack_uint64 ("history_offset", + pos->coin_history_id), TALER_JSON_pack_amount ("amount", &melt->amount_with_fee), TALER_JSON_pack_amount ("melt_fee", &melt->melt_fee), GNUNET_JSON_pack_data_auto ("rc", &melt->rc), + GNUNET_JSON_pack_data_auto ("h_denom_pub", + &melt->h_denom_pub), GNUNET_JSON_pack_data_auto ("refresh_seed", &melt->refresh_seed), GNUNET_JSON_pack_allow_null ( @@ -243,6 +265,8 @@ compile_transaction_history ( GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("type", "REFUND"), + GNUNET_JSON_pack_uint64 ("history_offset", + pos->coin_history_id), TALER_JSON_pack_amount ("amount", &value), TALER_JSON_pack_amount ("refund_fee", @@ -294,6 +318,8 @@ compile_transaction_history ( GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("type", "OLD-COIN-RECOUP"), + GNUNET_JSON_pack_uint64 ("history_offset", + pos->coin_history_id), TALER_JSON_pack_amount ("amount", &pr->value), GNUNET_JSON_pack_data_auto ("exchange_sig", @@ -338,6 +364,8 @@ compile_transaction_history ( GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("type", "RECOUP"), + GNUNET_JSON_pack_uint64 ("history_offset", + pos->coin_history_id), TALER_JSON_pack_amount ("amount", &recoup->value), GNUNET_JSON_pack_data_auto ("exchange_sig", @@ -348,8 +376,12 @@ compile_transaction_history ( &recoup->reserve_pub), GNUNET_JSON_pack_data_auto ("coin_sig", &recoup->coin_sig), + GNUNET_JSON_pack_data_auto ("h_denom_pub", + &recoup->h_denom_pub), GNUNET_JSON_pack_data_auto ("coin_blind", &recoup->coin_blind), + // FIXME-9828: spec says we should have h_commitment? + // FIXME-9828: spec says we should have coin_index? GNUNET_JSON_pack_data_auto ("reserve_pub", &recoup->reserve_pub), GNUNET_JSON_pack_timestamp ("timestamp", @@ -393,6 +425,8 @@ compile_transaction_history ( GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("type", "RECOUP-REFRESH"), + GNUNET_JSON_pack_uint64 ("history_offset", + pos->coin_history_id), TALER_JSON_pack_amount ("amount", &pr->value), GNUNET_JSON_pack_data_auto ("exchange_sig", @@ -403,8 +437,13 @@ compile_transaction_history ( &pr->old_coin_pub), GNUNET_JSON_pack_data_auto ("coin_sig", &pr->coin_sig), + // FIXME-#9828: spec says to return h_denom_pub GNUNET_JSON_pack_data_auto ("coin_blind", &pr->coin_blind), + // FIXME-#9828: spec says to return h_commitment + // FIXME-#9828: spec says to return coin_index + // FIXME-#9828: spec says to return new_coin_blinding_secret + // FIXME-#9828: spec says to return new_coin_ev GNUNET_JSON_pack_timestamp ("timestamp", pr->timestamp)))) { @@ -423,13 +462,14 @@ compile_transaction_history ( if (! pd->no_age_commitment) phac = &pd->h_age_commitment; - if (0 != json_array_append_new ( history, GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("type", "PURSE-DEPOSIT"), + GNUNET_JSON_pack_uint64 ("history_offset", + pos->coin_history_id), TALER_JSON_pack_amount ("amount", &pd->amount), GNUNET_JSON_pack_string ("exchange_base_url", @@ -439,12 +479,16 @@ compile_transaction_history ( GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_data_auto ("h_age_commitment", phac)), + TALER_JSON_pack_amount ("deposit_fee", + &pd->deposit_fee), GNUNET_JSON_pack_data_auto ("purse_pub", &pd->purse_pub), GNUNET_JSON_pack_bool ("refunded", pd->refunded), GNUNET_JSON_pack_data_auto ("coin_sig", - &pd->coin_sig)))) + &pd->coin_sig), + GNUNET_JSON_pack_data_auto ("h_denom_pub", + &pd->h_denom_pub)))) { GNUNET_break (0); json_decref (history); @@ -491,6 +535,8 @@ compile_transaction_history ( GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("type", "PURSE-REFUND"), + GNUNET_JSON_pack_uint64 ("history_offset", + pos->coin_history_id), TALER_JSON_pack_amount ("amount", &value), TALER_JSON_pack_amount ("refund_fee", @@ -520,6 +566,8 @@ compile_transaction_history ( GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("type", "RESERVE-OPEN-DEPOSIT"), + GNUNET_JSON_pack_uint64 ("history_offset", + pos->coin_history_id), TALER_JSON_pack_amount ("coin_contribution", &role->coin_contribution), GNUNET_JSON_pack_data_auto ("reserve_sig", diff --git a/src/exchangedb/pg_get_coin_transactions.c b/src/exchangedb/pg_get_coin_transactions.c @@ -58,6 +58,11 @@ struct CoinHistoryContext struct PostgresClosure *pg; /** + * Our current offset in the coin history. + */ + uint64_t chid; + + /** * Set to 'true' if the transaction failed. */ bool failed; @@ -144,6 +149,7 @@ add_coin_deposit (void *cls, tl->type = TALER_EXCHANGEDB_TT_DEPOSIT; tl->details.deposit = deposit; tl->serial_id = serial_id; + tl->coin_history_id = chc->chid; chc->head = tl; } } @@ -197,6 +203,8 @@ add_coin_purse_deposit (void *cls, GNUNET_PQ_result_spec_bool ("refunded", &deposit->refunded), &not_finished), + GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", + &deposit->h_denom_pub), GNUNET_PQ_result_spec_end }; @@ -212,13 +220,17 @@ add_coin_purse_deposit (void *cls, } if (not_finished) deposit->refunded = false; - deposit->no_age_commitment = GNUNET_is_zero (&deposit->h_age_commitment); + /* double-check for all-zeros age commitment */ + if (! deposit->no_age_commitment) + deposit->no_age_commitment + = GNUNET_is_zero (&deposit->h_age_commitment); } tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList); tl->next = chc->head; tl->type = TALER_EXCHANGEDB_TT_PURSE_DEPOSIT; tl->details.purse_deposit = deposit; tl->serial_id = serial_id; + tl->coin_history_id = chc->chid; chc->head = tl; } } @@ -291,6 +303,7 @@ add_coin_melt (void *cls, tl->type = TALER_EXCHANGEDB_TT_MELT; tl->details.melt = melt; tl->serial_id = serial_id; + tl->coin_history_id = chc->chid; chc->head = tl; } } @@ -354,6 +367,7 @@ add_coin_refund (void *cls, tl->type = TALER_EXCHANGEDB_TT_REFUND; tl->details.refund = refund; tl->serial_id = serial_id; + tl->coin_history_id = chc->chid; chc->head = tl; } } @@ -411,6 +425,7 @@ add_coin_purse_decision (void *cls, tl->type = TALER_EXCHANGEDB_TT_PURSE_REFUND; tl->details.purse_refund = prefund; tl->serial_id = serial_id; + tl->coin_history_id = chc->chid; chc->head = tl; } } @@ -477,6 +492,7 @@ add_old_coin_recoup (void *cls, tl->type = TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP; tl->details.old_coin_recoup = recoup; tl->serial_id = serial_id; + tl->coin_history_id = chc->chid; chc->head = tl; } } @@ -540,6 +556,7 @@ add_coin_recoup (void *cls, tl->type = TALER_EXCHANGEDB_TT_RECOUP; tl->details.recoup = recoup; tl->serial_id = serial_id; + tl->coin_history_id = chc->chid; chc->head = tl; } } @@ -606,6 +623,7 @@ add_coin_recoup_refresh (void *cls, tl->type = TALER_EXCHANGEDB_TT_RECOUP_REFRESH; tl->details.recoup_refresh = recoup; tl->serial_id = serial_id; + tl->coin_history_id = chc->chid; chc->head = tl; } } @@ -663,6 +681,7 @@ add_coin_reserve_open (void *cls, tl->type = TALER_EXCHANGEDB_TT_RESERVE_OPEN; tl->details.reserve_open = role; tl->serial_id = serial_id; + tl->coin_history_id = chc->chid; chc->head = tl; } } @@ -751,6 +770,8 @@ handle_history_entry (void *cls, &table_name), GNUNET_PQ_result_spec_uint64 ("serial_id", &serial_id), + GNUNET_PQ_result_spec_uint64 ("coin_history_serial_id", + &chc->chid), GNUNET_PQ_result_spec_end }; struct GNUNET_PQ_QueryParam params[] = { @@ -862,6 +883,7 @@ TEH_PG_get_coin_transactions ( "SELECT" " table_name" ",serial_id" + ",coin_history_serial_id" " FROM coin_history" " WHERE coin_pub=$1" " AND coin_history_serial_id > $2" @@ -920,6 +942,7 @@ TEH_PG_get_coin_transactions ( " partner_base_url" ",pd.amount_with_fee" ",denoms.fee_deposit" + ",denoms.denom_pub_hash" ",pd.purse_pub" ",kc.age_commitment_hash" ",pd.coin_sig" diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -2288,7 +2288,7 @@ struct TALER_EXCHANGEDB_MeltListEntry struct TALER_AgeCommitmentHash h_age_commitment; /** - * true, if no h_age_commitment is applicable + * true, if no @e h_age_commitment is applicable */ bool no_age_commitment; @@ -2318,7 +2318,6 @@ struct TALER_EXCHANGEDB_MeltListEntry */ struct TALER_PublicRefreshMasterSeedP refresh_seed; - /** * If false, @e blinding_seed is present */ @@ -2372,6 +2371,11 @@ struct TALER_EXCHANGEDB_PurseDepositListEntry struct TALER_AgeCommitmentHash h_age_commitment; /** + * Hash of the public denomination key used to sign the coin. + */ + struct TALER_DenominationHashP h_denom_pub; + + /** * Set to true if the coin was refunded. */ bool refunded; @@ -2736,11 +2740,16 @@ struct TALER_EXCHANGEDB_TransactionList enum TALER_EXCHANGEDB_TransactionType type; /** - * Serial ID of this entry in the database. + * Serial ID of this entry in the @e type-specific table. */ uint64_t serial_id; /** + * Serial ID of this entry in the coin history table. + */ + uint64_t coin_history_id; + + /** * Details about the transaction, depending on @e type. */ union diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c @@ -479,14 +479,11 @@ TALER_wallet_melt_sign ( .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT), .purpose.size = htonl (sizeof (melt)), .rc = *rc, - .h_denom_pub = *h_denom_pub, - .h_age_commitment = {{{0}}}, + .h_denom_pub = *h_denom_pub }; if (NULL != h_age_commitment) melt.h_age_commitment = *h_age_commitment; - - TALER_amount_hton (&melt.amount_with_fee, amount_with_fee); TALER_amount_hton (&melt.melt_fee, @@ -511,13 +508,11 @@ TALER_wallet_melt_verify ( .purpose.size = htonl (sizeof (melt)), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT), .rc = *rc, - .h_denom_pub = *h_denom_pub, - .h_age_commitment = {{{0}}}, + .h_denom_pub = *h_denom_pub }; if (NULL != h_age_commitment) melt.h_age_commitment = *h_age_commitment; - TALER_amount_hton (&melt.amount_with_fee, amount_with_fee); TALER_amount_hton (&melt.melt_fee,