From 29a2f9b345ecd8a6c062b3b3cfa719a8a4ec2a08 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 3 Apr 2017 16:40:31 +0200 Subject: implement rest of exchange logic for #3887 (return payback information in reserve and coin histories) --- src/exchange-lib/exchange_api_payback.c | 8 +- src/exchange/taler-exchange-httpd_db.c | 8 +- src/exchange/taler-exchange-httpd_responses.c | 163 +++++++++++++++++++------- src/exchange/taler-exchange-httpd_responses.h | 4 +- src/exchangedb/plugin_exchangedb_postgres.c | 11 +- src/exchangedb/test_exchangedb.c | 5 +- src/include/taler_exchange_service.h | 4 +- src/include/taler_exchangedb_plugin.h | 4 +- src/include/taler_signatures.h | 48 +++++++- 9 files changed, 185 insertions(+), 70 deletions(-) (limited to 'src') diff --git a/src/exchange-lib/exchange_api_payback.c b/src/exchange-lib/exchange_api_payback.c index b08fe3b68..eff459693 100644 --- a/src/exchange-lib/exchange_api_payback.c +++ b/src/exchange-lib/exchange_api_payback.c @@ -94,13 +94,13 @@ verify_payback_signature_ok (const struct TALER_EXCHANGE_PaybackHandle *ph, struct TALER_ExchangePublicKeyP exchange_pub; struct TALER_ExchangeSignatureP exchange_sig; struct TALER_Amount amount; - struct GNUNET_TIME_Absolute deadline; + struct GNUNET_TIME_Absolute timestamp; const struct TALER_EXCHANGE_Keys *key_state; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig), GNUNET_JSON_spec_fixed_auto ("exchange_pub", &exchange_pub), TALER_JSON_spec_amount ("amount", &amount), - GNUNET_JSON_spec_absolute_time ("payback_deadline", &deadline), + GNUNET_JSON_spec_absolute_time ("timestamp", ×tamp), GNUNET_JSON_spec_fixed_auto ("reserve_pub", &pc.reserve_pub), GNUNET_JSON_spec_end() }; @@ -123,7 +123,7 @@ verify_payback_signature_ok (const struct TALER_EXCHANGE_PaybackHandle *ph, } pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK); pc.purpose.size = htonl (sizeof (pc)); - pc.payback_deadline = GNUNET_TIME_absolute_hton (deadline); + pc.timestamp = GNUNET_TIME_absolute_hton (timestamp); TALER_amount_hton (&pc.payback_amount, &amount); pc.coin_pub = ph->coin_pub; @@ -140,7 +140,7 @@ verify_payback_signature_ok (const struct TALER_EXCHANGE_PaybackHandle *ph, MHD_HTTP_OK, TALER_EC_NONE, &amount, - deadline, + timestamp, &pc.reserve_pub, json); return GNUNET_OK; diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c index 3dc4a3248..11f8d31d3 100644 --- a/src/exchange/taler-exchange-httpd_db.c +++ b/src/exchange/taler-exchange-httpd_db.c @@ -2335,7 +2335,7 @@ TEH_DB_execute_payback (struct MHD_Connection *connection, struct TALER_ReservePublicKeyP reserve_pub; struct TALER_Amount amount; struct TALER_Amount spent; - struct GNUNET_TIME_Absolute payback_deadline; + struct GNUNET_TIME_Absolute now; if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls))) { @@ -2406,6 +2406,8 @@ TEH_DB_execute_payback (struct MHD_Connection *connection, } TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, tl); + now = GNUNET_TIME_absolute_get (); + (void) GNUNET_TIME_round_abs (&now); /* add coin to list of wire transfers for payback */ ret = TEH_plugin->insert_payback_request (TEH_plugin->cls, @@ -2416,7 +2418,7 @@ TEH_DB_execute_payback (struct MHD_Connection *connection, coin_blind, &amount, h_blind, - &payback_deadline); + now); if (GNUNET_SYSERR == ret) { TALER_LOG_WARNING ("Failed to store /payback information in database\n"); @@ -2432,7 +2434,7 @@ TEH_DB_execute_payback (struct MHD_Connection *connection, &coin->coin_pub, &reserve_pub, &amount, - payback_deadline); + now); } diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 01dcb6ca0..29ba9e02f 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016, 2017 Inria & GNUnet e.V. + Copyright (C) 2014-2017 Inria & GNUnet e.V. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -554,32 +554,34 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl) case TALER_EXCHANGEDB_TT_PAYBACK: { const struct TALER_EXCHANGEDB_Payback *payback = pos->details.payback; - struct TALER_PaybackRequestPS pr; + struct TALER_PaybackConfirmationPS pc; + struct TALER_ExchangePublicKeyP epub; + struct TALER_ExchangeSignatureP esig; type = "PAYBACK"; value = payback->value; - pr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_PAYBACK); - pr.purpose.size = htonl (sizeof (pr)); - pr.coin_pub = payback->coin_pub; - GNUNET_CRYPTO_rsa_public_key_hash (payback->denom_pub.rsa_public_key, - &pr.h_denom_pub); - pr.coin_blind = payback->coin_blind; - - /* internal sanity check before we hand out a bogus sig... */ - sig = &payback->coin_sig.eddsa_signature; - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_PAYBACK, - &pr.purpose, - sig, - &payback->coin_pub.eddsa_pub)) - { - GNUNET_break (0); - json_decref (history); - return NULL; - } - details = GNUNET_JSON_from_data_auto (&pr); + pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK); + pc.purpose.size = htonl (sizeof (pc)); + pc.timestamp = GNUNET_TIME_absolute_hton (payback->timestamp); + TALER_amount_hton (&pc.payback_amount, + &payback->value); + pc.coin_pub = payback->coin_pub; + pc.reserve_pub = payback->reserve_pub; + TEH_KS_sign (&pc.purpose, + &epub, + &esig); + details = GNUNET_JSON_from_data_auto (&pc); + GNUNET_assert (0 == + json_array_append_new (history, + json_pack ("{s:s, s:o, s:o, s:o, s:o}", + "type", type, + "amount", TALER_JSON_from_amount (&value), + "exchange_sig", GNUNET_JSON_from_data_auto (&esig), + "exchange_pub", GNUNET_JSON_from_data_auto (&epub), + "details", details))); } - break; + /* do not go to the default handler, we already appended! */ + continue; default: GNUNET_assert (0); } @@ -646,6 +648,11 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh, int ret; const struct TALER_EXCHANGEDB_ReserveHistory *pos; struct TALER_WithdrawRequestPS wr; + const struct TALER_EXCHANGEDB_Payback *payback; + struct TALER_PaybackConfirmationPS pc; + struct TALER_ReserveCloseConfirmationPS rcc; + struct TALER_ExchangePublicKeyP pub; + struct TALER_ExchangeSignatureP sig; json_history = json_array (); ret = 0; @@ -654,7 +661,7 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh, switch (pos->type) { case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE: - if (0 == ret) + if (0 == (1 & ret)) deposit_total = pos->details.bank->amount; else if (GNUNET_OK != @@ -665,7 +672,7 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh, json_decref (json_history); return NULL; } - ret = 1; + ret |= 1; GNUNET_assert (0 == json_array_append_new (json_history, json_pack ("{s:s, s:O, s:O, s:o}", @@ -674,21 +681,9 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh, "transfer_details", pos->details.bank->transfer_details, "amount", TALER_JSON_from_amount (&pos->details.bank->amount)))); break; - case TALER_EXCHANGEDB_RO_WITHDRAW_COIN: - break; - } - } - - ret = 0; - for (pos = rh; NULL != pos; pos = pos->next) - { - switch (pos->type) - { - case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE: - break; case TALER_EXCHANGEDB_RO_WITHDRAW_COIN: value = pos->details.withdraw->amount_with_fee; - if (0 == ret) + if (0 == (2 & ret)) { withdraw_total = value; } @@ -703,7 +698,7 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh, return NULL; } } - ret = 1; + ret |= 2; wr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); wr.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); wr.reserve_pub = pos->details.withdraw->reserve_pub; @@ -720,11 +715,91 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh, "type", "WITHDRAW", "signature", GNUNET_JSON_from_data_auto (&pos->details.withdraw->reserve_sig), "details", GNUNET_JSON_from_data_auto (&wr), + + "amount", TALER_JSON_from_amount (&value)))); + break; + case TALER_EXCHANGEDB_RO_PAYBACK_COIN: + payback = pos->details.payback; + if (0 == (1 & ret)) + deposit_total = payback->value; + else + if (GNUNET_OK != + TALER_amount_add (&deposit_total, + &deposit_total, + &payback->value)) + { + json_decref (json_history); + return NULL; + } + ret |= 1; + pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK); + pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS)); + pc.timestamp = GNUNET_TIME_absolute_hton (payback->timestamp); + TALER_amount_hton (&pc.payback_amount, + &payback->value); + pc.coin_pub = payback->coin_pub; + pc.reserve_pub = payback->reserve_pub; + TEH_KS_sign (&pc.purpose, + &pub, + &sig); + + GNUNET_assert (0 == + json_array_append_new (json_history, + json_pack ("{s:s, s:o, s:o, s:o, s:o}", + "type", "PAYBACK", + "exchange_pub", GNUNET_JSON_from_data_auto (&pub), + "exchange_sig", GNUNET_JSON_from_data_auto (&sig), + "timestamp", GNUNET_JSON_from_time_abs (payback->timestamp), + "amount", TALER_JSON_from_amount (&payback->value)))); + break; + case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK: + value = pos->details.bank->amount; + if (0 == (2 & ret)) + { + withdraw_total = value; + } + else + { + if (GNUNET_OK != + TALER_amount_add (&withdraw_total, + &withdraw_total, + &value)) + { + json_decref (json_history); + return NULL; + } + } + ret |= 2; + + rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED); + rcc.purpose.size = htonl (sizeof (struct TALER_ReserveCloseConfirmationPS)); + rcc.timestamp = GNUNET_TIME_absolute_hton (pos->details.bank->execution_date); + TALER_amount_hton (&rcc.closing_amount, + &value); + rcc.reserve_pub = pos->details.bank->reserve_pub; + TALER_JSON_hash (pos->details.bank->sender_account_details, + &rcc.h_wire); + TEH_KS_sign (&rcc.purpose, + &pub, + &sig); + GNUNET_assert (0 == + json_array_append_new (json_history, + json_pack ("{s:s, s:o, s:o, s:o, s:o}", + "type", "CLOSING", + "exchange_pub", GNUNET_JSON_from_data_auto (&pub), + "exchange_sig", GNUNET_JSON_from_data_auto (&sig), + "details", GNUNET_JSON_from_data_auto (&rcc), "amount", TALER_JSON_from_amount (&value)))); break; } } - if (0 == ret) + if (0 == (1 & ret)) + { + GNUNET_break (0); + json_decref (json_history); + return NULL; + } + if (0 == (2 & ret)) { /* did not encounter any withdraw operations, set to zero */ TALER_amount_get_zero (deposit_total.currency, @@ -1352,7 +1427,7 @@ TEH_RESPONSE_reply_payback_unknown (struct MHD_Connection *connection, * @param coin_pub coin for which we are processing the payback request * @param reserve_pub public key of the reserve that will receive the payback * @param amount the amount we will wire back - * @param payback_deadline deadline by which the exchange promises to pay + * @param timestamp when did the exchange receive the /payback request * @return MHD result code */ int @@ -1360,7 +1435,7 @@ TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *amount, - struct GNUNET_TIME_Absolute payback_deadline) + struct GNUNET_TIME_Absolute timestamp) { struct TALER_PaybackConfirmationPS pc; struct TALER_ExchangePublicKeyP pub; @@ -1368,7 +1443,7 @@ TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection, pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK); pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS)); - pc.payback_deadline = GNUNET_TIME_absolute_hton (payback_deadline); + pc.timestamp = GNUNET_TIME_absolute_hton (timestamp); TALER_amount_hton (&pc.payback_amount, amount); pc.coin_pub = *coin_pub; @@ -1380,7 +1455,7 @@ TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection, MHD_HTTP_OK, "{s:o, s:o, s:o, s:o, s:o}", "reserve_pub", GNUNET_JSON_from_data_auto (reserve_pub), - "payback_deadline", GNUNET_JSON_from_time_abs (payback_deadline), + "timestamp", GNUNET_JSON_from_time_abs (timestamp), "amount", TALER_JSON_from_amount (amount), "exchange_sig", GNUNET_JSON_from_data_auto (&sig), "exchange_pub", GNUNET_JSON_from_data_auto (&pub)); diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h index 6b68949e5..83dafdca3 100644 --- a/src/exchange/taler-exchange-httpd_responses.h +++ b/src/exchange/taler-exchange-httpd_responses.h @@ -579,7 +579,7 @@ TEH_RESPONSE_reply_payback_unknown (struct MHD_Connection *connection, * @param coin_pub coin for which we are processing the payback request * @param reserve_pub public key of the reserve that will receive the payback * @param amount the amount we will wire back - * @param payback_deadline deadline by which the exchange promises to pay + * @param timestamp when did the exchange receive the /payback request * @return MHD result code */ int @@ -587,7 +587,7 @@ TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *amount, - struct GNUNET_TIME_Absolute payback_deadline); + struct GNUNET_TIME_Absolute timestamp); #endif diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index df0c1a5fc..e869bcd95 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -5629,7 +5629,7 @@ postgres_select_wire_out_above_serial_id (void *cls, * @param amount total amount to be paid back * @param receiver_account_details who should receive the funds * @parma h_blind_ev hash of the blinded coin's envelope (must match reserves_out entry) - * @param[out] deadline set to absolute time by when the exchange plans to pay it back + * @param timestamp current time (rounded) * @return #GNUNET_OK on success, * #GNUNET_SYSERR on DB errors */ @@ -5642,10 +5642,9 @@ postgres_insert_payback_request (void *cls, const struct TALER_DenominationBlindingKeyP *coin_blind, const struct TALER_Amount *amount, const struct GNUNET_HashCode *h_blind_ev, - struct GNUNET_TIME_Absolute *deadline) + struct GNUNET_TIME_Absolute timestamp) { PGresult *result; - struct GNUNET_TIME_Absolute now; struct GNUNET_TIME_Absolute expiry; struct TALER_EXCHANGEDB_Reserve reserve; struct GNUNET_PQ_QueryParam params[] = { @@ -5654,12 +5653,11 @@ postgres_insert_payback_request (void *cls, GNUNET_PQ_query_param_auto_from_type (coin_sig), GNUNET_PQ_query_param_auto_from_type (coin_blind), TALER_PQ_query_param_amount (amount), - GNUNET_PQ_query_param_absolute_time (&now), + GNUNET_PQ_query_param_absolute_time (×tamp), GNUNET_PQ_query_param_auto_from_type (h_blind_ev), GNUNET_PQ_query_param_end }; - now = GNUNET_TIME_absolute_get (); result = GNUNET_PQ_exec_prepared (session->conn, "payback_insert", params); @@ -5689,7 +5687,7 @@ postgres_insert_payback_request (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - expiry = GNUNET_TIME_absolute_add (now, + expiry = GNUNET_TIME_absolute_add (timestamp, TALER_IDLE_RESERVE_EXPIRATION_TIME); reserve.expiry = GNUNET_TIME_absolute_max (expiry, reserve.expiry); @@ -5700,7 +5698,6 @@ postgres_insert_payback_request (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - *deadline = reserve.expiry; return GNUNET_OK; } diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index 0fc0f92b4..e8fd21194 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -1511,6 +1511,7 @@ run (void *cls) RND_BLK (&coin_sig); RND_BLK (&coin_blind); + deadline = GNUNET_TIME_absolute_get (); FAILIF (GNUNET_OK != plugin->insert_payback_request (plugin->cls, session, @@ -1520,7 +1521,7 @@ run (void *cls) &coin_blind, &value, &cbc.h_coin_envelope, - &deadline)); + deadline)); result = 7; rh = plugin->get_reserve_history (plugin->cls, @@ -1719,7 +1720,7 @@ run (void *cls) &coin_blind, &value, &cbc.h_coin_envelope, - &deadline)); + deadline)); auditor_row_cnt = 0; FAILIF (GNUNET_OK != diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 39aa96d11..4aa3d0240 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -1344,7 +1344,7 @@ struct TALER_EXCHANGE_PaybackHandle; * 0 if the exchange's reply is bogus (fails to follow the protocol) * @param ec taler-specific error code, #TALER_EC_NONE on success * @param amount amount the exchange will wire back for this coin - * @param deadline by when will the exchange wire the funds? + * @param timestamp what time did the exchange receive the /payback request * @param reserve_pub public key of the reserve receiving the payback * @param full_response full response from the exchange (for logging, in case of errors) */ @@ -1353,7 +1353,7 @@ typedef void unsigned int http_status, enum TALER_ErrorCode ec, const struct TALER_Amount *amount, - struct GNUNET_TIME_Absolute deadline, + struct GNUNET_TIME_Absolute timestamp, const struct TALER_ReservePublicKeyP *reserve_pub, const json_t *full_response); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index b578abf95..8a1a82838 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -1956,7 +1956,7 @@ struct TALER_EXCHANGEDB_Plugin * @param amount total amount to be paid back * @param receiver_account_details who should receive the funds * @parma h_blind_ev hash of the blinded coin's envelope (must match reserves_out entry) - * @param[out] deadline set to absolute time by when the exchange plans to pay it back + * @param now timestamp to store * @return #GNUNET_OK on success, * #GNUNET_SYSERR on DB errors */ @@ -1969,7 +1969,7 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_DenominationBlindingKeyP *coin_blind, const struct TALER_Amount *amount, const struct GNUNET_HashCode *h_blind_ev, - struct GNUNET_TIME_Absolute *deadline); + struct GNUNET_TIME_Absolute timestamp); /** diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 2d0e8bb37..c58ea1915 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -131,6 +131,11 @@ */ #define TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK 1039 +/** + * Signature where the Exchange confirms it closed a reserve. + */ +#define TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED 1040 + /*********************/ /* Wallet signatures */ @@ -1170,15 +1175,16 @@ struct TALER_PaybackConfirmationPS { /** - * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK + * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; /** - * By what deadline does the exchange promise to initiate - * the wire transfer? + * When did the exchange receive the payback request? + * Indirectly determines when the wire transfer is (likely) + * to happen. */ - struct GNUNET_TIME_AbsoluteNBO payback_deadline; + struct GNUNET_TIME_AbsoluteNBO timestamp; /** * How much of the coin's value will the exchange transfer? @@ -1198,6 +1204,40 @@ struct TALER_PaybackConfirmationPS }; +/** + * Response by which the exchange affirms that it has + * closed a reserve and send back the funds. + */ +struct TALER_ReserveCloseConfirmationPS +{ + + /** + * Purpose is #TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * When did the exchange initiate the wire transfer. + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + + /** + * How much did the exchange send? + */ + struct TALER_AmountNBO closing_amount; + + /** + * Public key of the reserve that received the payback. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Hash of the receiver's bank account. + */ + struct GNUNET_HashCode h_wire; +}; + + GNUNET_NETWORK_STRUCT_END #endif -- cgit v1.2.3