From 35873463b830a16a00f167a2ac9e146d0396bbb2 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 5 Oct 2023 23:00:15 +0200 Subject: -fixing missing '/' at end of paths in Debian package, implement coin history logic --- debian/etc-libtalerexchange/taler/taler.conf | 10 +- src/auditor/taler-helper-auditor-aggregation.c | 20 +- src/auditor/taler-helper-auditor-coins.c | 19 +- src/exchange/taler-exchange-httpd_coins_get.c | 8 + src/exchangedb/pg_get_coin_transactions.c | 18 +- src/exchangedb/pg_get_coin_transactions.h | 4 + src/exchangedb/test_exchangedb.c | 8 + src/include/taler_exchange_service.h | 318 ++++++++++++ src/include/taler_exchangedb_plugin.h | 17 +- src/lib/Makefile.am | 1 + src/lib/exchange_api_coins_history.c | 671 +++++++++++-------------- 11 files changed, 698 insertions(+), 396 deletions(-) diff --git a/debian/etc-libtalerexchange/taler/taler.conf b/debian/etc-libtalerexchange/taler/taler.conf index 1c86ccc36..2cf815656 100644 --- a/debian/etc-libtalerexchange/taler/taler.conf +++ b/debian/etc-libtalerexchange/taler/taler.conf @@ -35,11 +35,11 @@ [paths] -TALER_HOME = /var/lib/taler -TALER_RUNTIME_DIR = /run/taler -TALER_CACHE_HOME = /var/cache/taler -TALER_CONFIG_HOME = /etc/taler -TALER_DATA_HOME = /var/lib/taler +TALER_HOME = /var/lib/taler/ +TALER_RUNTIME_DIR = /run/taler/ +TALER_CACHE_HOME = /var/cache/taler/ +TALER_CONFIG_HOME = /etc/taler/ +TALER_DATA_HOME = /var/lib/taler/ # Inline configurations from all Taler components. diff --git a/src/auditor/taler-helper-auditor-aggregation.c b/src/auditor/taler-helper-auditor-aggregation.c index 8075e100b..fe95dfe3e 100644 --- a/src/auditor/taler-helper-auditor-aggregation.c +++ b/src/auditor/taler-helper-auditor-aggregation.c @@ -783,12 +783,20 @@ wire_transfer_information_cb ( /* TODO: could use 'start' mechanism to only fetch transactions we did not yet process, instead of going over them again and again.*/ - qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls, - coin_pub, - 0, - 0, - &etag_out, - &tl); + + { + struct TALER_Amount balance; + struct TALER_DenominationHashP h_denom_pub; + + qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls, + coin_pub, + 0, + 0, + &etag_out, + &balance, + &h_denom_pub, + &tl); + } if ( (qs < 0) || (NULL == tl) ) { diff --git a/src/auditor/taler-helper-auditor-coins.c b/src/auditor/taler-helper-auditor-coins.c index f873fa3cb..bc1598b07 100644 --- a/src/auditor/taler-helper-auditor-coins.c +++ b/src/auditor/taler-helper-auditor-coins.c @@ -440,12 +440,19 @@ check_coin_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, /* TODO: could use 'etag' mechanism to only fetch transactions we did not yet process, instead of going over them again and again. */ - qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls, - coin_pub, - 0, - 0, - &etag_out, - &tl); + { + struct TALER_Amount balance; + struct TALER_DenominationHashP h_denom_pub; + + qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls, + coin_pub, + 0, + 0, + &etag_out, + &balance, + &h_denom_pub, + &tl); + } if (0 >= qs) return qs; GNUNET_assert (GNUNET_OK == diff --git a/src/exchange/taler-exchange-httpd_coins_get.c b/src/exchange/taler-exchange-httpd_coins_get.c index 655a4c54a..cd453275e 100644 --- a/src/exchange/taler-exchange-httpd_coins_get.c +++ b/src/exchange/taler-exchange-httpd_coins_get.c @@ -544,6 +544,8 @@ TEH_handler_coins_get (struct TEH_RequestContext *rc, char etagp[24]; struct MHD_Response *resp; unsigned int http_status; + struct TALER_DenominationHashP h_denom_pub; + struct TALER_Amount balance; TALER_MHD_parse_request_number (rc->connection, "start", @@ -612,6 +614,8 @@ TEH_handler_coins_get (struct TEH_RequestContext *rc, start_off, etag_in, &etag_out, + &balance, + &h_denom_pub, &tl); switch (qs) { @@ -675,6 +679,10 @@ TEH_handler_coins_get (struct TEH_RequestContext *rc, "Failed to compile coin history"); } resp = TALER_MHD_MAKE_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("h_denom_pub", + &h_denom_pub), + TALER_JSON_pack_amount ("balance", + &balance), GNUNET_JSON_pack_array_steal ("history", history)); http_status = MHD_HTTP_OK; diff --git a/src/exchangedb/pg_get_coin_transactions.c b/src/exchangedb/pg_get_coin_transactions.c index e5d3b9b01..640f93ec7 100644 --- a/src/exchangedb/pg_get_coin_transactions.c +++ b/src/exchangedb/pg_get_coin_transactions.c @@ -810,6 +810,8 @@ TEH_PG_get_coin_transactions ( uint64_t start_off, uint64_t etag_in, uint64_t *etag_out, + struct TALER_Amount *balance, + struct TALER_DenominationHashP *h_denom_pub, struct TALER_EXCHANGEDB_TransactionList **tlp) { struct PostgresClosure *pg = cls; @@ -833,10 +835,16 @@ TEH_PG_get_coin_transactions ( "Getting transactions for coin %s\n", TALER_B2S (coin_pub)); PREPARE (pg, - "get_coin_history_etag", + "get_coin_history_etag_balance", "SELECT" - " coin_history_serial_id" - " FROM coin_history" + " ch.coin_history_serial_id" + ",kc.remaining" + ",denom.denom_pub_hash" + " FROM coin_history ch" + " JOIN known_coins kc" + " USING (coin_pub)" + " JOIN denominations denom" + " USING (denominations_serial)" " WHERE coin_pub=$1" " ORDER BY coin_history_serial_id DESC" " LIMIT 1;"); @@ -1045,6 +1053,10 @@ TEH_PG_get_coin_transactions ( struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("coin_history_serial_id", &end), + GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", + h_denom_pub), + TALER_PQ_RESULT_SPEC_AMOUNT ("remaining", + balance), GNUNET_PQ_result_spec_end }; diff --git a/src/exchangedb/pg_get_coin_transactions.h b/src/exchangedb/pg_get_coin_transactions.h index c19df3874..46e32e094 100644 --- a/src/exchangedb/pg_get_coin_transactions.h +++ b/src/exchangedb/pg_get_coin_transactions.h @@ -39,6 +39,8 @@ * @param etag_in up to this offset the client already has a response, do not * return anything unless @a etag_out will be larger * @param[out] etag_out set to the latest history offset known for this @a coin_pub + * @param[out] balance set to current balance of the coin + * @param[out] h_denom_pub set to denomination public key of the coin * @param[out] tlp set to list of transactions, set to NULL if coin has no * transaction history past @a start_off or if @a etag_in is equal * to the value written to @a etag_out. @@ -51,6 +53,8 @@ TEH_PG_get_coin_transactions ( uint64_t start_off, uint64_t etag_in, uint64_t *etag_out, + struct TALER_Amount *balance, + struct TALER_DenominationHashP *h_denom_pub, struct TALER_EXCHANGEDB_TransactionList **tlp); diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index f09489919..eeaaffad4 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -1724,12 +1724,16 @@ run (void *cls) struct TALER_EXCHANGEDB_TransactionList *tl; enum GNUNET_DB_QueryStatus qs; uint64_t etag; + struct TALER_Amount balance; + struct TALER_DenominationHashP h_denom_pub; qs = plugin->get_coin_transactions (plugin->cls, &refresh.coin.coin_pub, 0, 0, &etag, + &balance, + &h_denom_pub, &tl); FAILIF (0 >= qs); FAILIF (NULL == tl); @@ -1985,12 +1989,16 @@ run (void *cls) FAILIF (1 != auditor_row_cnt); { uint64_t etag = 0; + struct TALER_Amount balance; + struct TALER_DenominationHashP h_denom_pub; qs = plugin->get_coin_transactions (plugin->cls, &refund.coin.coin_pub, 0, 0, &etag, + &balance, + &h_denom_pub, &tl); } FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs); diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 662700d48..e0da5ed13 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -1576,6 +1576,324 @@ TALER_EXCHANGE_csr_withdraw_cancel ( struct TALER_EXCHANGE_CsRWithdrawHandle *csrh); +/* ********************* GET /coins/$COIN_PUB *********************** */ + +/** + * Ways how a coin's balance may change. + */ +enum TALER_EXCHANGE_CoinTransactionType +{ + + /** + * Reserved for uninitialized / none. + */ + TALER_EXCHANGE_CTT_NONE, + + /** + * Deposit into a contract. + */ + TALER_EXCHANGE_CTT_DEPOSIT, + + /** + * Spent on melt. + */ + TALER_EXCHANGE_CTT_MELT, + + /** + * Refunded by merchant. + */ + TALER_EXCHANGE_CTT_REFUND, + + /** + * Debited in recoup (to reserve) operation. + */ + TALER_EXCHANGE_CTT_RECOUP, + + /** + * Debited in recoup-and-refresh operation. + */ + TALER_EXCHANGE_CTT_RECOUP_REFRESH, + + /** + * Credited in recoup-refresh. + */ + TALER_EXCHANGE_CTT_OLD_COIN_RECOUP, + + /** + * Deposited into purse. + */ + TALER_EXCHANGE_CTT_PURSE_DEPOSIT, + + /** + * Refund from purse. + */ + TALER_EXCHANGE_CTT_PURSE_REFUND, + + /** + * Reserve open payment operation. + */ + TALER_EXCHANGE_CTT_RESERVE_OPEN_DEPOSIT + +}; + + +/** + * @brief Entry in the coin's transaction history. + */ +struct TALER_EXCHANGE_CoinHistoryEntry +{ + + /** + * Type of the transaction. + */ + enum TALER_EXCHANGE_CoinTransactionType type; + + /** + * Amount transferred (in or out). + */ + struct TALER_Amount amount; + + /** + * Details depending on @e type. + */ + union + { + + struct + { + struct TALER_MerchantWireHashP h_wire; + struct TALER_PrivateContractHashP h_contract_terms; + struct TALER_ExtensionPolicyHashP h_policy; + bool no_h_policy; + struct GNUNET_HashCode wallet_data_hash; + bool no_wallet_data_hash; + struct GNUNET_TIME_Timestamp wallet_timestamp; + struct TALER_MerchantPublicKeyP merchant_pub; + struct GNUNET_TIME_Timestamp refund_deadline; + struct TALER_CoinSpendSignatureP sig; + struct TALER_AgeCommitmentHash hac; + bool no_hac; + struct TALER_Amount deposit_fee; + } deposit; + + struct + { + struct TALER_CoinSpendSignatureP sig; + struct TALER_RefreshCommitmentP rc; + struct TALER_AgeCommitmentHash h_age_commitment; + bool no_hac; + struct TALER_Amount melt_fee; + } melt; + + struct + { + struct TALER_PrivateContractHashP h_contract_terms; + struct TALER_MerchantPublicKeyP merchant_pub; + struct TALER_MerchantSignatureP sig; + struct TALER_Amount refund_fee; + struct TALER_Amount sig_amount; + uint64_t rtransaction_id; + } refund; + + struct + { + struct TALER_ReservePublicKeyP reserve_pub; + struct GNUNET_TIME_Timestamp timestamp; + union TALER_DenominationBlindingKeyP coin_bks; + struct TALER_ExchangePublicKeyP exchange_pub; + struct TALER_ExchangeSignatureP exchange_sig; + struct TALER_CoinSpendSignatureP coin_sig; + } recoup; + + struct + { + struct TALER_CoinSpendPublicKeyP old_coin_pub; + union TALER_DenominationBlindingKeyP coin_bks; + struct GNUNET_TIME_Timestamp timestamp; + struct TALER_ExchangePublicKeyP exchange_pub; + struct TALER_ExchangeSignatureP exchange_sig; + struct TALER_CoinSpendSignatureP coin_sig; + } recoup_refresh; + + struct + { + struct TALER_ExchangePublicKeyP exchange_pub; + struct TALER_ExchangeSignatureP exchange_sig; + struct TALER_CoinSpendPublicKeyP new_coin_pub; + struct GNUNET_TIME_Timestamp timestamp; + } old_coin_recoup; + + struct + { + struct TALER_PurseContractPublicKeyP purse_pub; + struct TALER_CoinSpendSignatureP coin_sig; + const char *exchange_base_url; + bool refunded; + struct TALER_AgeCommitmentHash phac; + } purse_deposit; + + struct + { + struct TALER_PurseContractPublicKeyP purse_pub; + struct TALER_Amount refund_fee; + struct TALER_ExchangePublicKeyP exchange_pub; + struct TALER_ExchangeSignatureP exchange_sig; + } purse_refund; + + struct + { + struct TALER_ReserveSignatureP reserve_sig; + struct TALER_CoinSpendSignatureP coin_sig; + } reserve_open_deposit; + + } details; + +}; + + +/** + * @brief A /coins/$RID/history Handle + */ +struct TALER_EXCHANGE_CoinsHistoryHandle; + + +/** + * Parses and verifies a coin's transaction history as + * returned by the exchange. Note that in case of + * incremental histories, the client must first combine + * the incremental histories into one complete history. + * + * @param keys /keys data of the exchange + * @param dk denomination key of the coin + * @param history JSON array with the coin's history + * @param coin_pub public key of the coin + * @param currency currency of the coin + * @param[out] total_in set to total amount credited to the coin in @a history + * @param[out] total_out set to total amount debited to the coin in @a history + * @param rlen length of the @a rhistory array + * @param[out] rhistory array where to write the parsed @a history + * @return #GNUNET_OK if @a history is valid, + * #GNUNET_SYSERR if not + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_parse_coin_history ( + const struct TALER_EXCHANGE_Keys *keys, + const struct TALER_EXCHANGE_DenomPublicKey *dk, + const json_t *history, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + struct TALER_Amount *total_in, + struct TALER_Amount *total_out, + unsigned int rlen, + struct TALER_EXCHANGE_CoinHistoryEntry rhistory[static rlen]); + + +/** + * Verify that @a coin_sig does NOT appear in the @a history of a coin's + * transactions and thus whatever transaction is authorized by @a coin_sig is + * a conflict with @a proof. + * + * @param history coin history to check + * @param coin_sig signature that must not be in @a history + * @return #GNUNET_OK if @a coin_sig is not in @a history + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_signature_conflict ( + const json_t *history, + const struct TALER_CoinSpendSignatureP *coin_sig); + + +/** + * Response to a GET /coins/$COIN_PUB/history request. + */ +struct TALER_EXCHANGE_CoinHistory +{ + /** + * High-level HTTP response details. + */ + struct TALER_EXCHANGE_HttpResponse hr; + + /** + * Details depending on @e hr.http_status. + */ + union + { + + /** + * Information returned on success, if + * @e hr.http_status is #MHD_HTTP_OK + */ + struct + { + + /** + * Coin transaction history (possibly partial). + * Not yet validated, combine with other already + * known history data for this coin and then use + * #TALER_EXCHANGE_parse_coin_history() to validate + * the complete history and obtain it in binary + * format. + */ + const json_t *history; + + /** + * The hash of the coin denomination's public key + */ + struct TALER_DenominationHashP h_denom_pub; + + /** + * Coin balance. + */ + struct TALER_Amount balance; + + } ok; + + } details; + +}; + +typedef void +(*TALER_EXCHANGE_CoinsHistoryCallback)( + void *cls, + const struct TALER_EXCHANGE_CoinHistory *ch); + +/** + * Parses and verifies a coin's transaction history as + * returned by the exchange. Note that a client may + * have to combine multiple partial coin histories + * into one coherent history before calling this function. + * + * @param keys /keys data of the exchange + * @param dk denomination key of the coin + * @param history JSON array with the coin's full history + * @param coin_pub public key of the coin + * @param currency currency of the coin + * @param[out] total_in set to total amount credited to the coin in @a history + * @param[out] total_out set to total amount debited to the coin in @a history + * @param len length of the @a rhistory + * @param[out] rhistory where to write the parsed @a history + * @return #GNUNET_OK if @a history is valid, + * #GNUNET_SYSERR if not + */ +struct TALER_EXCHANGE_CoinsHistoryHandle * +TALER_EXCHANGE_coins_history ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + uint64_t start_off, + TALER_EXCHANGE_CoinsHistoryCallback cb, + void *cb_cls); + + +/** + * Cancel #TALER_EXCHANGE_coins_history() operation. + * + * @param[in] rsh operation to chancel + */ +void +TALER_EXCHANGE_coins_history_cancel ( + struct TALER_EXCHANGE_CoinsHistoryHandle *rsh); + + /* ********************* GET /reserves/$RESERVE_PUB *********************** */ /** diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 484b1155e..f0f4d6aaa 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -4672,18 +4672,23 @@ struct TALER_EXCHANGEDB_Plugin * @param etag_in up to this offset the client already has a response, do not * return anything unless @a etag_out will be larger * @param[out] etag_out set to the latest history offset known for this @a coin_pub + * @param[out] balance set to current balance of the coin + * @param[out] h_denom_pub set to denomination public key of the coin * @param[out] tlp set to list of transactions, set to NULL if coin has no * transaction history past @a start_off or if @a etag_in is equal * to the value written to @a etag_out. * @return database transaction status */ enum GNUNET_DB_QueryStatus - (*get_coin_transactions)(void *cls, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - uint64_t start_off, - uint64_t etag_in, - uint64_t *etag_out, - struct TALER_EXCHANGEDB_TransactionList **tlp); + (*get_coin_transactions)( + void *cls, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + uint64_t start_off, + uint64_t etag_in, + uint64_t *etag_out, + struct TALER_Amount *balance, + struct TALER_DenominationHashP *h_denom_pub, + struct TALER_EXCHANGEDB_TransactionList **tlp); /** diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index ff5d3b801..12f991d89 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -29,6 +29,7 @@ libtalerexchange_la_SOURCES = \ exchange_api_batch_withdraw.c \ exchange_api_batch_withdraw2.c \ exchange_api_curl_defaults.c exchange_api_curl_defaults.h \ + exchange_api_coins_history.c \ exchange_api_common.c exchange_api_common.h \ exchange_api_contracts_get.c \ exchange_api_csr_melt.c \ diff --git a/src/lib/exchange_api_coins_history.c b/src/lib/exchange_api_coins_history.c index ae718c459..b74e1a9b0 100644 --- a/src/lib/exchange_api_coins_history.c +++ b/src/lib/exchange_api_coins_history.c @@ -40,11 +40,6 @@ struct TALER_EXCHANGE_CoinsHistoryHandle { - /** - * The keys of the exchange this request handle will use - */ - struct TALER_EXCHANGE_Keys *keys; - /** * The url for this request. */ @@ -69,7 +64,7 @@ struct TALER_EXCHANGE_CoinsHistoryHandle /** * Public key of the coin we are querying. */ - struct TALER_CoinPublicKeyP coin_pub; + struct TALER_CoinSpendPublicKeyP coin_pub; /** * Closure for @a cb. @@ -85,6 +80,11 @@ struct TALER_EXCHANGE_CoinsHistoryHandle struct CoinHistoryParseContext { + /** + * Keys of the exchange. + */ + struct TALER_EXCHANGE_Keys *keys; + /** * Denomination of the coin. */ @@ -98,12 +98,12 @@ struct CoinHistoryParseContext /** * Where to sum up total refunds. */ - struct TALER_Amount rtotal; + struct TALER_Amount *total_in; /** * Total amount encountered. */ - struct TALER_Amount *total; + struct TALER_Amount *total_out; }; @@ -113,6 +113,7 @@ struct CoinHistoryParseContext * the coin's history entries. * * @param[in,out] pc overall context + * @param[out] rh where to write the history entry * @param amount main amount of this operation * @param transaction JSON details for the operation * @return #GNUNET_SYSERR on error, @@ -120,6 +121,7 @@ struct CoinHistoryParseContext */ typedef enum GNUNET_GenericReturnValue (*CoinCheckHelper)(struct CoinHistoryParseContext *pc, + struct TALER_EXCHANGE_CoinHistoryEntry *rh, const struct TALER_Amount *amount, json_t *transaction); @@ -135,54 +137,43 @@ typedef enum GNUNET_GenericReturnValue */ static enum GNUNET_GenericReturnValue help_deposit (struct CoinHistoryParseContext *pc, + struct TALER_EXCHANGE_CoinHistoryEntry *rh, const struct TALER_Amount *amount, json_t *transaction) { - struct TALER_MerchantWireHashP h_wire; - struct TALER_PrivateContractHashP h_contract_terms; - struct TALER_ExtensionPolicyHashP h_policy; - bool no_h_policy; - struct GNUNET_HashCode wallet_data_hash; - bool no_wallet_data_hash; - struct GNUNET_TIME_Timestamp wallet_timestamp; - struct TALER_MerchantPublicKeyP merchant_pub; - struct GNUNET_TIME_Timestamp refund_deadline = {0}; - struct TALER_CoinSpendSignatureP sig; - struct TALER_AgeCommitmentHash hac; - bool no_hac; - struct TALER_Amount deposit_fee; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("coin_sig", - &sig), + &rh->details.deposit.sig), GNUNET_JSON_spec_fixed_auto ("h_contract_terms", - &h_contract_terms), + &rh->details.deposit.h_contract_terms), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("wallet_data_hash", - &wallet_data_hash), - &no_wallet_data_hash), + &rh->details.deposit.wallet_data_hash), + &rh->details.deposit.no_wallet_data_hash), GNUNET_JSON_spec_fixed_auto ("h_wire", - &h_wire), + &rh->details.deposit.h_wire), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &hac), - &no_hac), + &rh->details.deposit.hac), + &rh->details.deposit.no_hac), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("h_policy", - &h_policy), - &no_h_policy), + &rh->details.deposit.h_policy), + &rh->details.deposit.no_h_policy), GNUNET_JSON_spec_timestamp ("timestamp", - &wallet_timestamp), + &rh->details.deposit.wallet_timestamp), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("refund_deadline", - &refund_deadline), + &rh->details.deposit.refund_deadline), NULL), TALER_JSON_spec_amount_any ("deposit_fee", - &deposit_fee), + &rh->details.deposit.deposit_fee), GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &merchant_pub), + &rh->details.deposit.merchant_pub), GNUNET_JSON_spec_end () }; + rh->details.deposit.refund_deadline = GNUNET_TIME_UNIT_ZERO_TS; if (GNUNET_OK != GNUNET_JSON_parse (transaction, spec, @@ -194,28 +185,34 @@ help_deposit (struct CoinHistoryParseContext *pc, if (GNUNET_OK != TALER_wallet_deposit_verify ( amount, - &deposit_fee, - &h_wire, - &h_contract_terms, - no_wallet_data_hash ? NULL : &wallet_data_hash, - no_hac ? NULL : &hac, - no_h_policy ? NULL : &h_policy, + &rh->details.deposit.deposit_fee, + &rh->details.deposit.h_wire, + &rh->details.deposit.h_contract_terms, + rh->details.deposit.no_wallet_data_hash + ? NULL + : &rh->details.deposit.wallet_data_hash, + rh->details.deposit.no_hac + ? NULL + : &rh->details.deposit.hac, + rh->details.deposit.no_h_policy + ? NULL + : &rh->details.deposit.h_policy, &pc->dk->h_key, - wallet_timestamp, - &merchant_pub, - refund_deadline, + rh->details.deposit.wallet_timestamp, + &rh->details.deposit.merchant_pub, + rh->details.deposit.refund_deadline, pc->coin_pub, - &sig)) + &rh->details.deposit.sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; } /* check that deposit fee matches our expectations from /keys! */ if ( (GNUNET_YES != - TALER_amount_cmp_currency (&deposit_fee, + TALER_amount_cmp_currency (&rh->details.deposit.deposit_fee, &pc->dk->fees.deposit)) || (0 != - TALER_amount_cmp (&deposit_fee, + TALER_amount_cmp (&rh->details.deposit.deposit_fee, &pc->dk->fees.deposit)) ) { GNUNET_break_op (0); @@ -236,25 +233,21 @@ help_deposit (struct CoinHistoryParseContext *pc, */ static enum GNUNET_GenericReturnValue help_melt (struct CoinHistoryParseContext *pc, + struct TALER_EXCHANGE_CoinHistoryEntry *rh, const struct TALER_Amount *amount, json_t *transaction) { - struct TALER_CoinSpendSignatureP sig; - struct TALER_RefreshCommitmentP rc; - struct TALER_AgeCommitmentHash h_age_commitment; - bool no_hac; - struct TALER_Amount melt_fee; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("coin_sig", - &sig), + &rh->details.melt.sig), GNUNET_JSON_spec_fixed_auto ("rc", - &rc), + &rh->details.melt.rc), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &h_age_commitment), - &no_hac), + &rh->details.melt.h_age_commitment), + &rh->details.melt.no_hac), TALER_JSON_spec_amount_any ("melt_fee", - &melt_fee), + &rh->details.melt.melt_fee), GNUNET_JSON_spec_end () }; @@ -269,10 +262,10 @@ help_melt (struct CoinHistoryParseContext *pc, /* check that melt fee matches our expectations from /keys! */ if ( (GNUNET_YES != - TALER_amount_cmp_currency (&melt_fee, + TALER_amount_cmp_currency (&rh->details.melt.melt_fee, &pc->dk->fees.refresh)) || (0 != - TALER_amount_cmp (&melt_fee, + TALER_amount_cmp (&rh->details.melt.melt_fee, &pc->dk->fees.refresh)) ) { GNUNET_break_op (0); @@ -281,14 +274,14 @@ help_melt (struct CoinHistoryParseContext *pc, if (GNUNET_OK != TALER_wallet_melt_verify ( amount, - &melt_fee, - &rc, + &rh->details.melt.melt_fee, + &rh->details.melt.rc, &pc->dk->h_key, - no_hac + rh->details.melt.no_hac ? NULL - : &h_age_commitment, + : &rh->details.melt.h_age_commitment, pc->coin_pub, - &sig)) + &rh->details.melt.sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -308,26 +301,21 @@ help_melt (struct CoinHistoryParseContext *pc, */ static enum GNUNET_GenericReturnValue help_refund (struct CoinHistoryParseContext *pc, + struct TALER_EXCHANGE_CoinHistoryEntry *rh, const struct TALER_Amount *amount, json_t *transaction) { - struct TALER_PrivateContractHashP h_contract_terms; - struct TALER_MerchantPublicKeyP merchant_pub; - struct TALER_MerchantSignatureP sig; - struct TALER_Amount refund_fee; - struct TALER_Amount sig_amount; - uint64_t rtransaction_id; struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_amount_any ("refund_fee", - &refund_fee), + &rh->details.refund.refund_fee), GNUNET_JSON_spec_fixed_auto ("merchant_sig", - &sig), + &rh->details.refund.sig), GNUNET_JSON_spec_fixed_auto ("h_contract_terms", - &h_contract_terms), + &rh->details.refund.h_contract_terms), GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &merchant_pub), + &rh->details.refund.merchant_pub), GNUNET_JSON_spec_uint64 ("rtransaction_id", - &rtransaction_id), + &rh->details.refund.rtransaction_id), GNUNET_JSON_spec_end () }; @@ -340,8 +328,8 @@ help_refund (struct CoinHistoryParseContext *pc, return GNUNET_SYSERR; } if (0 > - TALER_amount_add (&sig_amount, - &refund_fee, + TALER_amount_add (&rh->details.refund.sig_amount, + &rh->details.refund.refund_fee, amount)) { GNUNET_break_op (0); @@ -349,11 +337,11 @@ help_refund (struct CoinHistoryParseContext *pc, } if (GNUNET_OK != TALER_merchant_refund_verify (pc->coin_pub, - &h_contract_terms, - rtransaction_id, - &sig_amount, - &merchant_pub, - &sig)) + &rh->details.refund.h_contract_terms, + rh->details.refund.rtransaction_id, + &rh->details.refund.sig_amount, + &rh->details.refund.merchant_pub, + &rh->details.refund.sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -368,10 +356,10 @@ help_refund (struct CoinHistoryParseContext *pc, /* check that refund fee matches our expectations from /keys! */ if ( (GNUNET_YES != - TALER_amount_cmp_currency (&refund_fee, + TALER_amount_cmp_currency (&rh->details.refund.refund_fee, &pc->dk->fees.refund)) || (0 != - TALER_amount_cmp (&refund_fee, + TALER_amount_cmp (&rh->details.refund.refund_fee, &pc->dk->fees.refund)) ) { GNUNET_break_op (0); @@ -392,28 +380,23 @@ help_refund (struct CoinHistoryParseContext *pc, */ static enum GNUNET_GenericReturnValue help_recoup (struct CoinHistoryParseContext *pc, + struct TALER_EXCHANGE_CoinHistoryEntry *rh, const struct TALER_Amount *amount, json_t *transaction) { - struct TALER_ReservePublicKeyP reserve_pub; - struct GNUNET_TIME_Timestamp timestamp; - union TALER_DenominationBlindingKeyP coin_bks; - struct TALER_ExchangePublicKeyP exchange_pub; - struct TALER_ExchangeSignatureP exchange_sig; - struct TALER_CoinSpendSignatureP coin_sig; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &exchange_sig), + &rh->details.recoup.exchange_sig), GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &exchange_pub), + &rh->details.recoup.exchange_pub), GNUNET_JSON_spec_fixed_auto ("reserve_pub", - &reserve_pub), + &rh->details.recoup.reserve_pub), GNUNET_JSON_spec_fixed_auto ("coin_sig", - &coin_sig), + &rh->details.recoup.coin_sig), GNUNET_JSON_spec_fixed_auto ("coin_blind", - &coin_bks), + &rh->details.recoup.coin_bks), GNUNET_JSON_spec_timestamp ("timestamp", - ×tamp), + &rh->details.recoup.timestamp), GNUNET_JSON_spec_end () }; @@ -427,21 +410,21 @@ help_recoup (struct CoinHistoryParseContext *pc, } if (GNUNET_OK != TALER_exchange_online_confirm_recoup_verify ( - timestamp, + rh->details.recoup.timestamp, amount, pc->coin_pub, - &reserve_pub, - &exchange_pub, - &exchange_sig)) + &rh->details.recoup.reserve_pub, + &rh->details.recoup.exchange_pub, + &rh->details.recoup.exchange_sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; } if (GNUNET_OK != TALER_wallet_recoup_verify (&pc->dk->h_key, - &coin_bks, + &rh->details.recoup.coin_bks, pc->coin_pub, - &coin_sig)) + &rh->details.recoup.coin_sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -452,6 +435,8 @@ help_recoup (struct CoinHistoryParseContext *pc, /** * Handle recoup-refresh entry in the coin's history. + * This is the coin that was subjected to a recoup, + * the value being credited to the old coin. * * @param[in,out] pc overall context * @param amount main amount of this operation @@ -461,30 +446,23 @@ help_recoup (struct CoinHistoryParseContext *pc, */ static enum GNUNET_GenericReturnValue help_recoup_refresh (struct CoinHistoryParseContext *pc, + struct TALER_EXCHANGE_CoinHistoryEntry *rh, const struct TALER_Amount *amount, json_t *transaction) { - /* This is the coin that was subjected to a recoup, - the value being credited to the old coin. */ - struct TALER_CoinSpendPublicKeyP old_coin_pub; - union TALER_DenominationBlindingKeyP coin_bks; - struct GNUNET_TIME_Timestamp timestamp; - struct TALER_ExchangePublicKeyP exchange_pub; - struct TALER_ExchangeSignatureP exchange_sig; - struct TALER_CoinSpendSignatureP coin_sig; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &exchange_sig), + &rh->details.recoup_refresh.exchange_sig), GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &exchange_pub), + &rh->details.recoup_refresh.exchange_pub), GNUNET_JSON_spec_fixed_auto ("coin_sig", - &coin_sig), + &rh->details.recoup_refresh.coin_sig), GNUNET_JSON_spec_fixed_auto ("old_coin_pub", - &old_coin_pub), + &rh->details.recoup_refresh.old_coin_pub), GNUNET_JSON_spec_fixed_auto ("coin_blind", - &coin_bks), + &rh->details.recoup_refresh.coin_bks), GNUNET_JSON_spec_timestamp ("timestamp", - ×tamp), + &rh->details.recoup_refresh.timestamp), GNUNET_JSON_spec_end () }; @@ -498,21 +476,21 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc, } if (GNUNET_OK != TALER_exchange_online_confirm_recoup_refresh_verify ( - timestamp, + rh->details.recoup_refresh.timestamp, amount, pc->coin_pub, - &old_coin_pub, - &exchange_pub, - &exchange_sig)) + &rh->details.recoup_refresh.old_coin_pub, + &rh->details.recoup_refresh.exchange_pub, + &rh->details.recoup_refresh.exchange_sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; } if (GNUNET_OK != TALER_wallet_recoup_verify (&pc->dk->h_key, - &coin_bks, + &rh->details.recoup_refresh.coin_bks, pc->coin_pub, - &coin_sig)) + &rh->details.recoup_refresh.coin_sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -523,6 +501,8 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc, /** * Handle old coin recoup entry in the coin's history. + * This is the coin that was credited in a recoup, + * the value being credited to the this coin. * * @param[in,out] pc overall context * @param amount main amount of this operation @@ -532,24 +512,19 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc, */ static enum GNUNET_GenericReturnValue help_old_coin_recoup (struct CoinHistoryParseContext *pc, + struct TALER_EXCHANGE_CoinHistoryEntry *rh, const struct TALER_Amount *amount, json_t *transaction) { - /* This is the coin that was credited in a recoup, - the value being credited to the this coin. */ - struct TALER_ExchangePublicKeyP exchange_pub; - struct TALER_ExchangeSignatureP exchange_sig; - struct TALER_CoinSpendPublicKeyP new_coin_pub; - struct GNUNET_TIME_Timestamp timestamp; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &exchange_sig), + &rh->details.old_coin_recoup.exchange_sig), GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &exchange_pub), + &rh->details.old_coin_recoup.exchange_pub), GNUNET_JSON_spec_fixed_auto ("coin_pub", - &new_coin_pub), + &rh->details.old_coin_recoup.new_coin_pub), GNUNET_JSON_spec_timestamp ("timestamp", - ×tamp), + &rh->details.old_coin_recoup.timestamp), GNUNET_JSON_spec_end () }; @@ -563,12 +538,12 @@ help_old_coin_recoup (struct CoinHistoryParseContext *pc, } if (GNUNET_OK != TALER_exchange_online_confirm_recoup_refresh_verify ( - timestamp, + rh->details.old_coin_recoup.timestamp, amount, - &new_coin_pub, + &rh->details.old_coin_recoup.new_coin_pub, pc->coin_pub, - &exchange_pub, - &exchange_sig)) + &rh->details.old_coin_recoup.exchange_pub, + &rh->details.old_coin_recoup.exchange_sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -588,31 +563,23 @@ help_old_coin_recoup (struct CoinHistoryParseContext *pc, */ static enum GNUNET_GenericReturnValue help_purse_deposit (struct CoinHistoryParseContext *pc, + struct TALER_EXCHANGE_CoinHistoryEntry *rh, const struct TALER_Amount *amount, json_t *transaction) { - struct TALER_PurseContractPublicKeyP purse_pub; - struct TALER_CoinSpendSignatureP coin_sig; - const char *exchange_base_url; - bool refunded; - struct TALER_AgeCommitmentHash phac = { 0 }; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("purse_pub", - &purse_pub), + &rh->details.purse_deposit.purse_pub), GNUNET_JSON_spec_fixed_auto ("coin_sig", - &coin_sig), + &rh->details.purse_deposit.coin_sig), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &coin_sig), + &rh->details.purse_deposit.phac), NULL), GNUNET_JSON_spec_string ("exchange_base_url", - &exchange_base_url), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &phac), - NULL), + &rh->details.purse_deposit.exchange_base_url), GNUNET_JSON_spec_bool ("refunded", - &refunded), + &rh->details.purse_deposit.refunded), GNUNET_JSON_spec_end () }; @@ -626,23 +593,23 @@ help_purse_deposit (struct CoinHistoryParseContext *pc, } if (GNUNET_OK != TALER_wallet_purse_deposit_verify ( - exchange_base_url, - &purse_pub, + rh->details.purse_deposit.exchange_base_url, + &rh->details.purse_deposit.purse_pub, amount, &pc->dk->h_key, - &phac, + &rh->details.purse_deposit.phac, pc->coin_pub, - &coin_sig)) + &rh->details.purse_deposit.coin_sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; } - if (refunded) + if (rh->details.purse_deposit.refunded) { /* We wave the deposit fee. */ if (0 > - TALER_amount_add (&pc->rtotal, - &pc->rtotal, + TALER_amount_add (pc->total_in, + pc->total_in, &pc->dk->fees.deposit)) { /* overflow in refund history? inconceivable! Bad exchange! */ @@ -665,22 +632,19 @@ help_purse_deposit (struct CoinHistoryParseContext *pc, */ static enum GNUNET_GenericReturnValue help_purse_refund (struct CoinHistoryParseContext *pc, + struct TALER_EXCHANGE_CoinHistoryEntry *rh, const struct TALER_Amount *amount, json_t *transaction) { - struct TALER_PurseContractPublicKeyP purse_pub; - struct TALER_Amount refund_fee; - struct TALER_ExchangePublicKeyP exchange_pub; - struct TALER_ExchangeSignatureP exchange_sig; struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_amount_any ("refund_fee", - &refund_fee), + &rh->details.purse_refund.refund_fee), GNUNET_JSON_spec_fixed_auto ("purse_pub", - &purse_pub), + &rh->details.purse_refund.purse_pub), GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &exchange_sig), + &rh->details.purse_refund.exchange_sig), GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &exchange_pub), + &rh->details.purse_refund.exchange_pub), GNUNET_JSON_spec_end () }; @@ -695,20 +659,20 @@ help_purse_refund (struct CoinHistoryParseContext *pc, if (GNUNET_OK != TALER_exchange_online_purse_refund_verify ( amount, - &refund_fee, + &rh->details.purse_refund.refund_fee, pc->coin_pub, - &purse_pub, - &exchange_pub, - &exchange_sig)) + &rh->details.purse_refund.purse_pub, + &rh->details.purse_refund.exchange_pub, + &rh->details.purse_refund.exchange_sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; } if ( (GNUNET_YES != - TALER_amount_cmp_currency (&refund_fee, + TALER_amount_cmp_currency (&rh->details.purse_refund.refund_fee, &pc->dk->fees.refund)) || (0 != - TALER_amount_cmp (&refund_fee, + TALER_amount_cmp (&rh->details.purse_refund.refund_fee, &pc->dk->fees.refund)) ) { GNUNET_break_op (0); @@ -729,16 +693,15 @@ help_purse_refund (struct CoinHistoryParseContext *pc, */ static enum GNUNET_GenericReturnValue help_reserve_open_deposit (struct CoinHistoryParseContext *pc, + struct TALER_EXCHANGE_CoinHistoryEntry *rh, const struct TALER_Amount *amount, json_t *transaction) { - struct TALER_ReserveSignatureP reserve_sig; - struct TALER_CoinSpendSignatureP coin_sig; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("reserve_sig", - &reserve_sig), + &rh->details.reserve_open_deposit.reserve_sig), GNUNET_JSON_spec_fixed_auto ("coin_sig", - &coin_sig), + &rh->details.reserve_open_deposit.coin_sig), GNUNET_JSON_spec_end () }; @@ -753,9 +716,9 @@ help_reserve_open_deposit (struct CoinHistoryParseContext *pc, if (GNUNET_OK != TALER_wallet_reserve_open_deposit_verify ( amount, - &reserve_sig, + &rh->details.reserve_open_deposit.reserve_sig, pc->coin_pub, - &coin_sig)) + &rh->details.reserve_open_deposit.coin_sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -764,47 +727,57 @@ help_reserve_open_deposit (struct CoinHistoryParseContext *pc, } -/** - * Convenience function. Verifies a coin's transaction history as - * returned by the exchange. - * - * FIXME: add support for partial histories! - * NOTE: this API will thus still change! - * - * @param dk fee structure for the coin - * @param coin_pub public key of the coin - * @param history history of the coin in json encoding - * @param[out] total how much of the coin has been spent according to @a history - * @return #GNUNET_OK if @a history is valid, #GNUNET_SYSERR if not - */ -static enum GNUNET_GenericReturnValue -verify_coin_history ( +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_parse_coin_history ( + const struct TALER_EXCHANGE_Keys *keys, const struct TALER_EXCHANGE_DenomPublicKey *dk, - const struct TALER_CoinSpendPublicKeyP *coin_pub, const json_t *history, - struct TALER_Amount *total) + const struct TALER_CoinSpendPublicKeyP *coin_pub, + struct TALER_Amount *total_in, + struct TALER_Amount *total_out, + unsigned int rlen, + struct TALER_EXCHANGE_CoinHistoryEntry rhistory[static rlen]) { - const char *currency = dk->value.currency; const struct { const char *type; CoinCheckHelper helper; + enum TALER_EXCHANGE_CoinTransactionType ctt; } map[] = { - { "DEPOSIT", &help_deposit }, - { "MELT", &help_melt }, - { "REFUND", &help_refund }, - { "RECOUP", &help_recoup }, - { "RECOUP-REFRESH", &help_recoup_refresh }, - { "OLD-COIN-RECOUP", &help_old_coin_recoup }, - { "PURSE-DEPOSIT", &help_purse_deposit }, - { "PURSE-REFUND", &help_purse_refund }, - { "RESERVE-OPEN-DEPOSIT", &help_reserve_open_deposit }, - { NULL, NULL } + { "DEPOSIT", + &help_deposit, + TALER_EXCHANGE_CTT_DEPOSIT }, + { "MELT", + &help_melt, + TALER_EXCHANGE_CTT_MELT }, + { "REFUND", + &help_refund, + TALER_EXCHANGE_CTT_REFUND }, + { "RECOUP", + &help_recoup, + TALER_EXCHANGE_CTT_RECOUP }, + { "RECOUP-REFRESH", + &help_recoup_refresh, + TALER_EXCHANGE_CTT_RECOUP_REFRESH }, + { "OLD-COIN-RECOUP", + &help_old_coin_recoup, + TALER_EXCHANGE_CTT_OLD_COIN_RECOUP }, + { "PURSE-DEPOSIT", + &help_purse_deposit, + TALER_EXCHANGE_CTT_PURSE_DEPOSIT }, + { "PURSE-REFUND", + &help_purse_refund, + TALER_EXCHANGE_CTT_PURSE_REFUND }, + { "RESERVE-OPEN-DEPOSIT", + &help_reserve_open_deposit, + TALER_EXCHANGE_CTT_RESERVE_OPEN_DEPOSIT }, + { NULL, NULL, TALER_EXCHANGE_CTT_NONE } }; struct CoinHistoryParseContext pc = { .dk = dk, .coin_pub = coin_pub, - .total = total + .total_out = total_out, + .total_in = total_in }; size_t len; @@ -819,16 +792,16 @@ verify_coin_history ( GNUNET_break_op (0); return GNUNET_SYSERR; } + *total_in = dk->value; GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (currency, - total)); - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (currency, - &pc.rtotal)); + TALER_amount_set_zero (total_in->currency, + total_out)); for (size_t off = 0; offtype = map[i].ctt; add = map[i].helper (&pc, + rh, &amount, transaction); break; @@ -876,16 +849,17 @@ verify_coin_history ( { case GNUNET_SYSERR: /* entry type not supported, new version on server? */ + rh->type = TALER_EXCHANGE_CTT_NONE; GNUNET_break_op (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected type `%s' in response\n", type); return GNUNET_SYSERR; case GNUNET_YES: - /* This amount should be added to the total */ + /* This amount should be debited from the coin */ if (0 > - TALER_amount_add (total, - total, + TALER_amount_add (total_out, + total_out, &amount)) { /* overflow in history already!? inconceivable! Bad exchange! */ @@ -894,15 +868,10 @@ verify_coin_history ( } break; case GNUNET_NO: - /* This amount should be subtracted from the total. - - However, for the implementation, we first *add* up all of - these negative amounts, as we might get refunds before - deposits from a semi-evil exchange. Then, at the end, we do - the subtraction by calculating "total = total - rtotal" */ + /* This amount should be credited to the coin. */ if (0 > - TALER_amount_add (&pc.rtotal, - &pc.rtotal, + TALER_amount_add (total_in, + total_in, &amount)) { /* overflow in refund history? inconceivable! Bad exchange! */ @@ -912,110 +881,6 @@ verify_coin_history ( break; } /* end of switch(add) */ } - /* Finally, subtract 'rtotal' from total to handle the subtractions */ - if (0 > - TALER_amount_subtract (total, - total, - &pc.rtotal)) - { - /* underflow in history? inconceivable! Bad exchange! */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Verify that @a coin_sig does NOT appear in - * the history of @a proof and thus whatever transaction - * is authorized by @a coin_sig is a conflict with - * @a proof. - * - * @param proof a proof to check - * @param coin_sig signature that must not be in @a proof - * @return #GNUNET_OK if @a coin_sig is not in @a proof - */ -// FIXME: should be used... -enum GNUNET_GenericReturnValue -TALER_EXCHANGE_check_coin_signature_conflict_ ( - const json_t *proof, - const struct TALER_CoinSpendSignatureP *coin_sig) -{ - json_t *history; - size_t off; - json_t *entry; - - history = json_object_get (proof, - "history"); - if (NULL == history) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - json_array_foreach (history, off, entry) - { - struct TALER_CoinSpendSignatureP cs; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("coin_sig", - &cs), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (entry, - spec, - NULL, NULL)) - continue; /* entry without coin signature */ - if (0 == - GNUNET_memcmp (&cs, - coin_sig)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - return GNUNET_OK; -} - - -/** - * Check that the provided @a proof indeeds indicates - * a conflict for @a coin_pub. - * - * @param keys exchange keys - * @param proof provided conflict proof - * @param dk denomination of @a coin_pub that the client - * used - * @param coin_pub public key of the coin - * @param required balance required on the coin for the operation - * @return #GNUNET_OK if @a proof holds - */ -// FIXME: should be used! -enum GNUNET_GenericReturnValue -TALER_EXCHANGE_check_coin_conflict_ ( - const struct TALER_EXCHANGE_Keys *keys, - const json_t *proof, - const struct TALER_EXCHANGE_DenomPublicKey *dk, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_Amount *required) -{ - enum TALER_ErrorCode ec; - - ec = TALER_JSON_get_error_code (proof); - switch (ec) - { - case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: - /* Nothing to check anymore here, proof needs to be - checked in the GET /coins/$COIN_PUB handler */ - break; - case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: - // FIXME: write check! - break; - default: - GNUNET_break_op (0); - return GNUNET_SYSERR; - } return GNUNET_OK; } @@ -1032,8 +897,6 @@ static enum GNUNET_GenericReturnValue handle_coins_history_ok (struct TALER_EXCHANGE_CoinsHistoryHandle *rsh, const json_t *j) { - const json_t *history; - unsigned int len; struct TALER_EXCHANGE_CoinHistory rs = { .hr.reply = j, .hr.http_status = MHD_HTTP_OK @@ -1041,8 +904,10 @@ handle_coins_history_ok (struct TALER_EXCHANGE_CoinsHistoryHandle *rsh, struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_amount_any ("balance", &rs.details.ok.balance), + GNUNET_JSON_spec_fixed_auto ("h_denom_pub", + &rs.details.ok.h_denom_pub), GNUNET_JSON_spec_array_const ("history", - &history), + &rs.details.ok.history), GNUNET_JSON_spec_end () }; @@ -1055,39 +920,13 @@ handle_coins_history_ok (struct TALER_EXCHANGE_CoinsHistoryHandle *rsh, GNUNET_break_op (0); return GNUNET_SYSERR; } - len = json_array_size (history); + if (NULL != rsh->cb) { - struct TALER_EXCHANGE_CoinHistoryEntry *rhistory; - - rhistory = GNUNET_new_array (len, - struct TALER_EXCHANGE_CoinHistoryEntry); - if (GNUNET_OK != - parse_coin_history (rsh->keys, - history, - &rsh->coin_pub, - rs.details.ok.balance.currency, - &rs.details.ok.total_in, - &rs.details.ok.total_out, - len, - rhistory)) - { - GNUNET_break_op (0); - free_coin_history (len, - rhistory); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - if (NULL != rsh->cb) - { - rs.details.ok.history = rhistory; - rs.details.ok.history_len = len; - rsh->cb (rsh->cb_cls, - &rs); - rsh->cb = NULL; - } - free_coin_history (len, - rhistory); + rsh->cb (rsh->cb_cls, + &rs); + rsh->cb = NULL; } + GNUNET_JSON_parse_free (spec); return GNUNET_OK; } @@ -1178,15 +1017,14 @@ struct TALER_EXCHANGE_CoinsHistoryHandle * TALER_EXCHANGE_coins_history ( struct GNUNET_CURL_Context *ctx, const char *url, - struct TALER_EXCHANGE_Keys *keys, - const struct TALER_CoinPrivateKeyP *coin_priv, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, uint64_t start_off, TALER_EXCHANGE_CoinsHistoryCallback cb, void *cb_cls) { struct TALER_EXCHANGE_CoinsHistoryHandle *rsh; CURL *eh; - char arg_str[sizeof (struct TALER_CoinPublicKeyP) * 2 + 64]; + char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 64]; struct curl_slist *job_headers; rsh = GNUNET_new (struct TALER_EXCHANGE_CoinsHistoryHandle); @@ -1195,7 +1033,7 @@ TALER_EXCHANGE_coins_history ( GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, &rsh->coin_pub.eddsa_pub); { - char pub_str[sizeof (struct TALER_CoinPublicKeyP) * 2]; + char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; char *end; end = GNUNET_STRINGS_data_to_string ( @@ -1234,7 +1072,7 @@ TALER_EXCHANGE_coins_history ( } { - struct TALER_CoinSignatureP coin_sig; + struct TALER_CoinSpendSignatureP coin_sig; char *sig_hdr; char *hdr; @@ -1260,7 +1098,6 @@ TALER_EXCHANGE_coins_history ( } } - rsh->keys = TALER_EXCHANGE_keys_incref (keys); rsh->job = GNUNET_CURL_job_add2 (ctx, eh, job_headers, @@ -1282,9 +1119,103 @@ TALER_EXCHANGE_coins_history_cancel ( } TALER_curl_easy_post_finished (&rsh->post_ctx); GNUNET_free (rsh->url); - TALER_EXCHANGE_keys_decref (rsh->keys); GNUNET_free (rsh); } +/** + * Verify that @a coin_sig does NOT appear in the @a history of a coin's + * transactions and thus whatever transaction is authorized by @a coin_sig is + * a conflict with @a proof. + * + * @param history coin history to check + * @param coin_sig signature that must not be in @a history + * @return #GNUNET_OK if @a coin_sig is not in @a history + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_signature_conflict ( + const json_t *history, + const struct TALER_CoinSpendSignatureP *coin_sig) +{ + size_t off; + json_t *entry; + + json_array_foreach (history, off, entry) + { + struct TALER_CoinSpendSignatureP cs; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("coin_sig", + &cs), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (entry, + spec, + NULL, NULL)) + continue; /* entry without coin signature */ + if (0 == + GNUNET_memcmp (&cs, + coin_sig)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + return GNUNET_OK; +} + + +#if FIXME_IMPLEMENT +/** + * FIXME-Oec: we need some specific routines that show + * that certain coin operations are indeed in conflict, + * for example that the coin is of a different denomination + * or different age restrictions. + * This relates to unimplemented error handling for + * coins in the exchange! + * + * Check that the provided @a proof indeeds indicates + * a conflict for @a coin_pub. + * + * @param keys exchange keys + * @param proof provided conflict proof + * @param dk denomination of @a coin_pub that the client + * used + * @param coin_pub public key of the coin + * @param required balance required on the coin for the operation + * @return #GNUNET_OK if @a proof holds + */ +// FIXME: should be properly defined and implemented! +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_conflict_ ( + const struct TALER_EXCHANGE_Keys *keys, + const json_t *proof, + const struct TALER_EXCHANGE_DenomPublicKey *dk, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *required) +{ + enum TALER_ErrorCode ec; + + ec = TALER_JSON_get_error_code (proof); + switch (ec) + { + case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: + /* Nothing to check anymore here, proof needs to be + checked in the GET /coins/$COIN_PUB handler */ + break; + case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: + // FIXME: write check! + break; + default: + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +#endif + + /* end of exchange_api_coins_history.c */ -- cgit v1.2.3