From 346c351e5ff2fc184f1c044f4ffbf82bc80fe655 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Fri, 22 Dec 2017 21:22:57 +0100 Subject: redefining/adding bank error codes --- src/bank-lib/fakebank.c | 2 +- src/include/taler_error_codes.h | 83 +++++++++++++++++++++++++---------------- 2 files changed, 51 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c index 3d4acf4c..b54cf44c 100644 --- a/src/bank-lib/fakebank.c +++ b/src/bank-lib/fakebank.c @@ -613,7 +613,7 @@ handle_reject (struct TALER_FAKEBANK_Handle *h, if (GNUNET_OK != found) return create_bank_error (connection, MHD_HTTP_NOT_FOUND, - TALER_EC_BANK_REJECT_NOT_FOUND, + TALER_EC_BANK_REJECT_TRANSACTION_NOT_FOUND, "transaction unknown"); /* finally build regular response */ resp = MHD_create_response_from_buffer (0, diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h index 2d0c8325..759e6fc7 100644 --- a/src/include/taler_error_codes.h +++ b/src/include/taler_error_codes.h @@ -1503,67 +1503,84 @@ enum TALER_ErrorCode /* *************** Taler BANK/FAKEBANK error codes *************** */ /** - * Authentication failed for an unspecified request. - * To return when the view name is not available, or - * no specific error code is defined yet. + * The request cannot be served because the client failed to + * login. To be returned along HTTP 401 Unauthorized. */ - TALER_EC_BANK_NOT_AUTHORIZED = 5000, + TALER_EC_BANK_REJECT_LOGIN_FAILED = 5312, /** - * The bank could not find the bank account specified - * in the request. Returned with a status code of MHD_HTTP_NOT_FOUND. + * The transaction cannot be rejected becasue it does not exist + * at the bank. To be returned along HTTP 404 Not Found. */ - TALER_EC_BANK_UNKNOWN_ACCOUNT = 5001, + TALER_EC_BANK_REJECT_TRANSACTION_NOT_FOUND = 5301, /** - * Authentication failed for the /admin/add/incoming request. - * Returned with a status code of MHD_HTTP_FORBIDDEN. + * The client does not own the account credited by the transaction + * which is to be rejected, so it has no rights do reject it. To be + * returned along HTTP 403 Forbidden. */ - TALER_EC_BANK_TRANSFER_NOT_AUHTORIZED = 5100, + TALER_EC_BANK_REJECT_NO_RIGHTS = 5313, /** - * The wire transfer cannot be done because the debitor would - * reach a unallowed debit. + * The POSTed JSON at /reject was invalid. To be returned along + * HTTP 400 Bad Request. */ - TALER_EC_BANK_TRANSFER_DEBIT = 5101, + TALER_EC_BANK_REJECT_JSON_INVALID = 5306, /** - * The wire transfer cannot be done because the credit and - * debit account are the same. + * A URL parameter for /history was missing. To be returned along + * HTTP 400 Bad Request. */ - TALER_EC_BANK_TRANSFER_SAME_ACCOUNT = 5102, + TALER_EC_BANK_HISTORY_PARAMETER_MISSING = 5208, /** - * Authentication failed for the /history request. - * Returned with a status code of MHD_HTTP_FORBIDDEN. + * A URL parameter for /history was malformed. To be returned along + * HTTP 400 Bad Request. */ - TALER_EC_BANK_HISTORY_NOT_AUHTORIZED = 5200, + TALER_EC_BANK_HISTORY_PARAMETER_MALFORMED = 5209, /** - * The bank library had trouble obtaining a valid - * HTTP response. - * Returned with a status code of 0. + * The client failed to login for /history. To be returned along + * HTTP 401 Unauthorized. */ - TALER_EC_BANK_HISTORY_HTTP_FAILURE = 5201, + TALER_EC_BANK_HISTORY_LOGIN_FAILED = 5212, /** - * The bank could not find the wire transfer that was supposed to - * be rejected. - * Returned with a status code of MHD_HTTP_NOT_FOUND. + * The bank had trouble obtaining a valid HTTP response. To be returned + * along status code 0. */ - TALER_EC_BANK_REJECT_NOT_FOUND = 5300, + TALER_EC_BANK_HISTORY_HTTP_FAILURE = 5213, /** - * Authentication failed for the /reject request. - * Returned with a status code of MHD_HTTP_FORBIDDEN. + * The debit account for /admin/add/incoming is not known to the + * bank. To be returned along HTTP 404 Not Found. */ - TALER_EC_BANK_REJECT_NOT_AUTHORIZED = 5301, + TALER_EC_BANK_ADD_INCOMING_UNKNOWN_ACCOUNT = 5100, /** - * The client wants to reject a transaction where they are - * not the _credit_ party, impossible! + * The client specified the same bank account for both the credit + * and the debit account. The bank will not accomplish this operation. + * To be returned along HTTP 403 Forbidden. */ - TALER_EC_BANK_REJECT_NO_RIGHTS = 5302, + TALER_EC_BANK_ADD_INCOMING_SAME_ACCOUNT = 5102, + + /** + * The operation would put the client in a debit situation which is + * forbidden to them. To return along HTTP 403 Forbidden. + */ + TALER_EC_BANK_ADD_INCOMING_UNALLOWED_DEBIT = 5103, + + /** + * The client POSTed an invalid JSON. To be returned along HTTP + * 400 Bad Request. + */ + TALER_EC_BANK_ADD_INCOMING_JSON_INVALID = 5106, + + /** + * The client failed to login for /admin/add/incoming. To be returned + * along HTTP 401 Unauthorized. + */ + TALER_EC_BANK_ADD_INCOMING_LOGIN_FAILED = 5112, /** * End of error code range. -- cgit v1.2.3 From 4f2ad1051b1433974e5d598b1546202797729e31 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 1 Jan 2018 22:10:26 +0100 Subject: change 'f' to 'contribution' in /deposit --- src/exchange-lib/exchange_api_deposit.c | 2 +- src/exchange/taler-exchange-httpd_deposit.c | 6 +++++- src/include/taler_error_codes.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/exchange-lib/exchange_api_deposit.c b/src/exchange-lib/exchange_api_deposit.c index d90b1aa7..76e3e4da 100644 --- a/src/exchange-lib/exchange_api_deposit.c +++ b/src/exchange-lib/exchange_api_deposit.c @@ -454,7 +454,7 @@ TALER_EXCHANGE_deposit (struct TALER_EXCHANGE_Handle *exchange, " s:o," /* merchant_pub */ " s:o, s:o," /* refund_deadline, wire_deadline */ " s:o}", /* coin_sig */ - "f", TALER_JSON_from_amount (amount), + "contribution", TALER_JSON_from_amount (amount), "wire", wire_details, "H_wire", GNUNET_JSON_from_data_auto (&h_wire), "h_contract_terms", GNUNET_JSON_from_data_auto (h_contract_terms), diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index b7fb3452..542c56c9 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -149,6 +149,8 @@ deposit_transaction (void *cls, { struct TALER_Amount amount_without_fee; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "/deposit replay, accepting again!\n"); GNUNET_assert (GNUNET_OK == TALER_amount_subtract (&amount_without_fee, &deposit->amount_with_fee, @@ -191,6 +193,8 @@ deposit_transaction (void *cls, if (0 < TALER_amount_cmp (&spent, &dc->value)) { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Deposited coin has insufficient funds left!\n"); *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection, TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS, tl); @@ -376,7 +380,7 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh, struct GNUNET_HashCode my_h_wire; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_json ("wire", &wire), - TALER_JSON_spec_amount ("f", &deposit.amount_with_fee), + TALER_JSON_spec_amount ("contribution", &deposit.amount_with_fee), TALER_JSON_spec_denomination_public_key ("denom_pub", &deposit.coin.denom_pub), TALER_JSON_spec_denomination_signature ("ub_sig", &deposit.coin.denom_sig), GNUNET_JSON_spec_fixed_auto ("coin_pub", &deposit.coin.coin_pub), diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h index 759e6fc7..d90bd4f3 100644 --- a/src/include/taler_error_codes.h +++ b/src/include/taler_error_codes.h @@ -1001,6 +1001,7 @@ enum TALER_ErrorCode TALER_EC_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE = 2114, /** + * The request fails to provide coins for the payment. * This response is provided with HTTP status code * MHD_HTTP_BAD_REQUEST. -- cgit v1.2.3 From 2d08d612f6802f9a369755267bf4a113cdc8d871 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 1 Jan 2018 23:07:36 +0100 Subject: refuse endian conversion for invalid amounts --- src/util/amount.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/util/amount.c b/src/util/amount.c index 7765c74b..20d06424 100644 --- a/src/util/amount.c +++ b/src/util/amount.c @@ -207,6 +207,8 @@ void TALER_amount_hton (struct TALER_AmountNBO *res, const struct TALER_Amount *d) { + GNUNET_assert (GNUNET_YES == + TALER_amount_is_valid (d)); res->value = GNUNET_htonll (d->value); res->fraction = htonl (d->fraction); memcpy (res->currency, @@ -230,6 +232,8 @@ TALER_amount_ntoh (struct TALER_Amount *res, memcpy (res->currency, dn->currency, TALER_CURRENCY_LEN); + GNUNET_assert (GNUNET_YES == + TALER_amount_is_valid (res)); } -- cgit v1.2.3 From ff0d4bb6eb99d446a891b97b9fa48027b3b553b2 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 1 Jan 2018 23:26:34 +0100 Subject: add TALER_EXCHANGE_refund2, make sure fee test initializes all amounts; update ChangeLog --- ChangeLog | 10 +++++ src/exchange-lib/exchange_api_refund.c | 73 ++++++++++++++++++++++++++++++---- src/exchangedb/test_exchangedb_fees.c | 6 +++ src/include/taler_exchange_service.h | 44 ++++++++++++++++++++ 4 files changed, 125 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/ChangeLog b/ChangeLog index 2948a76c..2c18ef5a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +Mon Jan 1 23:15:37 CET 2018 + Add TALER_EXCHANGE_refund2() API call to libtalerexchange. -CG + +Tue Dec 14 23:15:37 CET 2018 + Eliminated /admin/add/incoming. + Add taler-bank-transfer tool. -CG + +Sun Dec 10 19:03:11 CET 2018 + Implement support for optimized refresh protocol. -CG + Thu Nov 2 17:39:40 CET 2017 Limit amount values to 2^53 as we always wanted (#5167). -CG diff --git a/src/exchange-lib/exchange_api_refund.c b/src/exchange-lib/exchange_api_refund.c index a39dd23a..ef1d66b9 100644 --- a/src/exchange-lib/exchange_api_refund.c +++ b/src/exchange-lib/exchange_api_refund.c @@ -243,12 +243,8 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange, TALER_EXCHANGE_RefundResultCallback cb, void *cb_cls) { - struct TALER_EXCHANGE_RefundHandle *rh; - struct GNUNET_CURL_Context *ctx; struct TALER_RefundRequestPS rr; struct TALER_MerchantSignatureP merchant_sig; - json_t *refund_obj; - CURL *eh; GNUNET_assert (GNUNET_YES == MAH_handle_is_ready (exchange)); @@ -267,7 +263,68 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange, GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv, &rr.purpose, &merchant_sig.eddsa_sig)); - refund_obj = json_pack ("{s:o, s:o," /* amount/fee */ + return TALER_EXCHANGE_refund2 (exchange, + amount, + refund_fee, + h_contract_terms, + coin_pub, + rtransaction_id, + &rr.merchant, + &merchant_sig, + cb, + cb_cls); +} + + +/** + * Submit a refund request to the exchange and get the exchange's + * response. This API is used by a merchant. Note that + * while we return the response verbatim to the caller for further + * processing, we do already verify that the response is well-formed + * (i.e. that signatures included in the response are all valid). If + * the exchange's reply is not well-formed, we return an HTTP status code + * of zero to @a cb. + * + * The @a exchange must be ready to operate (i.e. have + * finished processing the /keys reply). If this check fails, we do + * NOT initiate the transaction with the exchange and instead return NULL. + * + * @param exchange the exchange handle; the exchange must be ready to operate + * @param amount the amount to be refunded; must be larger than the refund fee + * (as that fee is still being subtracted), and smaller than the amount + * (with deposit fee) of the original deposit contribution of this coin + * @param refund_fee fee applicable to this coin for the refund + * @param h_contract_terms hash of the contact of the merchant with the customer that is being refunded + * @param coin_pub coin’s public key of the coin from the original deposit operation + * @param rtransaction_id transaction id for the transaction between merchant and customer (of refunding operation); + * this is needed as we may first do a partial refund and later a full refund. If both + * refunds are also over the same amount, we need the @a rtransaction_id to make the disjoint + * refund requests different (as requests are idempotent and otherwise the 2nd refund might not work). + * @param merchant_pub public key of the merchant + * @param merchant_sig signature affirming the refund from the merchant + * @param cb the callback to call when a reply for this request is available + * @param cb_cls closure for the above callback + * @return a handle for this request; NULL if the inputs are invalid (i.e. + * signatures fail to verify). In this case, the callback is not called. + */ +struct TALER_EXCHANGE_RefundHandle * +TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle *exchange, + const struct TALER_Amount *amount, + const struct TALER_Amount *refund_fee, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + uint64_t rtransaction_id, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_MerchantSignatureP *merchant_sig, + TALER_EXCHANGE_RefundResultCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_RefundHandle *rh; + struct GNUNET_CURL_Context *ctx; + json_t *refund_obj; + CURL *eh; + +refund_obj = json_pack ("{s:o, s:o," /* amount/fee */ " s:o, s:o," /* h_contract_terms, coin_pub */ " s:I," /* rtransaction id */ " s:o, s:o}", /* merchant_pub, merchant_sig */ @@ -276,8 +333,8 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange, "h_contract_terms", GNUNET_JSON_from_data_auto (h_contract_terms), "coin_pub", GNUNET_JSON_from_data_auto (coin_pub), "rtransaction_id", (json_int_t) rtransaction_id, - "merchant_pub", GNUNET_JSON_from_data_auto (&rr.merchant), - "merchant_sig", GNUNET_JSON_from_data_auto (&merchant_sig) + "merchant_pub", GNUNET_JSON_from_data_auto (merchant_pub), + "merchant_sig", GNUNET_JSON_from_data_auto (merchant_sig) ); if (NULL == refund_obj) { @@ -294,7 +351,7 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange, rh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND); rh->depconf.h_contract_terms = *h_contract_terms; rh->depconf.coin_pub = *coin_pub; - rh->depconf.merchant = rr.merchant; + rh->depconf.merchant = *merchant_pub; rh->depconf.rtransaction_id = GNUNET_htonll (rtransaction_id); TALER_amount_hton (&rh->depconf.refund_amount, amount); diff --git a/src/exchangedb/test_exchangedb_fees.c b/src/exchangedb/test_exchangedb_fees.c index 2bee7745..0c9eceaf 100644 --- a/src/exchangedb/test_exchangedb_fees.c +++ b/src/exchangedb/test_exchangedb_fees.c @@ -86,6 +86,9 @@ main (int argc, GNUNET_assert (GNUNET_OK == TALER_string_to_amount ("EUR:1.0", &af->wire_fee)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:1.0", + &af->closing_fee)); sign_af (af, priv); n = GNUNET_new (struct TALER_EXCHANGEDB_AggregateFees); @@ -94,6 +97,9 @@ main (int argc, GNUNET_assert (GNUNET_OK == TALER_string_to_amount ("EUR:0.1", &n->wire_fee)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:0.1", + &n->closing_fee)); sign_af (n, priv); af->next = n; diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index da39a179..f1af114c 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -716,6 +716,50 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange, void *cb_cls); +/** + * Submit a refund request to the exchange and get the exchange's + * response. This API is used by a merchant. Note that + * while we return the response verbatim to the caller for further + * processing, we do already verify that the response is well-formed + * (i.e. that signatures included in the response are all valid). If + * the exchange's reply is not well-formed, we return an HTTP status code + * of zero to @a cb. + * + * The @a exchange must be ready to operate (i.e. have + * finished processing the /keys reply). If this check fails, we do + * NOT initiate the transaction with the exchange and instead return NULL. + * + * @param exchange the exchange handle; the exchange must be ready to operate + * @param amount the amount to be refunded; must be larger than the refund fee + * (as that fee is still being subtracted), and smaller than the amount + * (with deposit fee) of the original deposit contribution of this coin + * @param refund_fee fee applicable to this coin for the refund + * @param h_contract_terms hash of the contact of the merchant with the customer that is being refunded + * @param coin_pub coin’s public key of the coin from the original deposit operation + * @param rtransaction_id transaction id for the transaction between merchant and customer (of refunding operation); + * this is needed as we may first do a partial refund and later a full refund. If both + * refunds are also over the same amount, we need the @a rtransaction_id to make the disjoint + * refund requests different (as requests are idempotent and otherwise the 2nd refund might not work). + * @param merchant_pub public key of the merchant + * @param merchant_sig signature affirming the refund from the merchant + * @param cb the callback to call when a reply for this request is available + * @param cb_cls closure for the above callback + * @return a handle for this request; NULL if the inputs are invalid (i.e. + * signatures fail to verify). In this case, the callback is not called. + */ +struct TALER_EXCHANGE_RefundHandle * +TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle *exchange, + const struct TALER_Amount *amount, + const struct TALER_Amount *refund_fee, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + uint64_t rtransaction_id, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_MerchantSignatureP *merchant_sig, + TALER_EXCHANGE_RefundResultCallback cb, + void *cb_cls); + + /** * Cancel a refund permission request. This function cannot be used * on a request handle if a response is already served for it. If -- cgit v1.2.3 From dad63db5f91b95a5ebb4acc69d3b218aef1411b5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 2 Jan 2018 14:05:03 +0100 Subject: extending test logic to detect refund issue (commented out for now) --- src/exchange-lib/test_exchange_api.c | 60 ++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'src') diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c index 6fd9ad69..a1aa38c7 100644 --- a/src/exchange-lib/test_exchange_api.c +++ b/src/exchange-lib/test_exchange_api.c @@ -3562,7 +3562,67 @@ run (void *cls) .details.refund.fee = "EUR:0.01", .details.refund.deposit_ref = "deposit-refund-2", }, + { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, + .label = "check-empty-after-refund" }, + +#if 0 + /* Test refunded coins are never executed, even past + refund deadline */ + { .oc = OC_ADMIN_ADD_INCOMING, + .label = "create-reserve-rb", + .expected_response_code = MHD_HTTP_OK, + .details.admin_add_incoming.debit_account_no = 42, + .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, + .details.admin_add_incoming.auth_username = "user42", + .details.admin_add_incoming.auth_password = "pass42", + .details.admin_add_incoming.amount = "EUR:5.01" }, + /* Run wirewatch to observe /admin/add/incoming */ + { .oc = OC_RUN_WIREWATCH, + .label = "wirewatch-3b" }, + /* Withdraw a 5 EUR coin, at fee of 1 ct */ + { .oc = OC_WITHDRAW_SIGN, + .label = "withdraw-coin-rb", + .expected_response_code = MHD_HTTP_OK, + .details.reserve_withdraw.reserve_reference = "create-reserve-rb", + .details.reserve_withdraw.amount = "EUR:5" }, + /* Spend 5 EUR of the 5 EUR coin (in full) + (merchant would receive EUR:4.99 due to 1 ct deposit fee) */ + { .oc = OC_DEPOSIT, + .label = "deposit-refund-1b", + .expected_response_code = MHD_HTTP_OK, + .details.deposit.amount = "EUR:5", + .details.deposit.coin_ref = "withdraw-coin-rb", + .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost:8082/\", \"account_number\":42 }", + .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:5\" } ] }", + .details.deposit.refund_deadline = { 0 }, + }, + { .oc = OC_CHECK_BANK_TRANSFER, + .label = "check_bank_transfer-aai-3b", + .details.check_bank_transfer.exchange_base_url = "https://exchange.com/", + .details.check_bank_transfer.amount = "EUR:5.01", + .details.check_bank_transfer.account_debit = 42, + .details.check_bank_transfer.account_credit = 2 + }, + /* Trigger refund (before aggregator had a chance to execute + deposit, even though refund deadline was zero) */ + { .oc = OC_REFUND, + .label = "refund-ok-fast", + .expected_response_code = MHD_HTTP_OK, + .details.refund.amount = "EUR:5", + .details.refund.fee = "EUR:0.01", + .details.refund.deposit_ref = "deposit-refund-1b", + }, + /* Run transfers. This will do the transfer as refund deadline + was 0, except of course because the refund succeeded, the + transfer should no longer be done. */ + { .oc = OC_RUN_AGGREGATOR, + .label = "run-aggregator-3b" }, + /* check that aggregator didn't do anything, as expected */ + { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, + .label = "check-refund-fast-not-run" }, +#endif + /* ************** End of refund API testing************* */ /* ************** Test /payback API ************* */ -- cgit v1.2.3 From 41b5dde70945b6cb468e412caf6ab7ecd7f56651 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 2 Jan 2018 14:43:15 +0100 Subject: implement select_refunds_by_coin in exchangedb plugin --- src/exchangedb/plugin_exchangedb_postgres.c | 123 ++++++++++++++++++++++++++++ src/include/taler_exchangedb_plugin.h | 40 +++++++++ 2 files changed, 163 insertions(+) (limited to 'src') diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 36ae3e54..9c91259b 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -3032,6 +3032,128 @@ postgres_insert_refund (void *cls, } +/** + * Closure for #get_refunds_cb(). + */ +struct SelectRefundContext +{ + /** + * Function to call on each result. + */ + TALER_EXCHANGEDB_RefundCoinCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Set to #GNUNET_SYSERR on error. + */ + int status; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure of type `struct SelectRefundContext *` + * @param result the postgres result + * @param num_result the number of results in @a result + */ +static void +get_refunds_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct SelectRefundContext *srctx = cls; + + for (unsigned int i=0;istatus = GNUNET_SYSERR; + return; + } + if (GNUNET_OK != + srctx->cb (srctx->cb_cls, + &merchant_pub, + &merchant_sig, + &h_contract, + rtransaction_id, + &amount_with_fee, + &refund_fee)) + return; + } +} + + +/** + * Select refunds by @a coin_pub. + * + * @param cls closure of plugin + * @param session database handle to use + * @param coin_pub coin to get refunds for + * @param cb function to call for each refund found + * @param cb_cls closure for @a cb + * @return query result status + */ +static enum GNUNET_DB_QueryStatus +postgres_select_refunds_by_coin (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + TALER_EXCHANGEDB_RefundCoinCallback cb, + void *cb_cls) +{ + enum GNUNET_DB_QueryStatus qs; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (coin_pub), + GNUNET_PQ_query_param_end + }; + struct SelectRefundContext srctx = { + .cb = cb, + .cb_cls = cb_cls, + .status = GNUNET_OK + }; + + qs = GNUNET_PQ_eval_prepared_multi_select (session->conn, + "get_refunds_by_coin", + params, + &get_refunds_cb, + &srctx); + if (GNUNET_SYSERR == srctx.status) + return GNUNET_DB_STATUS_HARD_ERROR; + return qs; +} + + /** * Lookup refresh melt commitment data under the given @a rc. * @@ -6236,6 +6358,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) plugin->iterate_matching_deposits = &postgres_iterate_matching_deposits; plugin->insert_deposit = &postgres_insert_deposit; plugin->insert_refund = &postgres_insert_refund; + plugin->select_refunds_by_coin = &postgres_select_refunds_by_coin; plugin->insert_melt = &postgres_insert_melt; plugin->get_melt = &postgres_get_melt; plugin->insert_refresh_reveal = &postgres_insert_refresh_reveal; diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index e64b0ad4..c531d838 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -732,6 +732,29 @@ typedef int const struct TALER_RefreshCommitmentP *rc); +/** + * Callback invoked with information about refunds applicable + * to a particular coin. + * + * @param cls closure + * @param merchant_pub public key of merchant who authorized refund + * @param merchant_sig signature of merchant authorizing refund + * @param h_contract hash of contract being refunded + * @param rtransaction_id refund transaction ID + * @param amount_with_fee amount being refunded + * @param refund_fee fee the exchange keeps for the refund processing + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +typedef int +(*TALER_EXCHANGEDB_RefundCoinCallback)(void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_MerchantSignatureP *merchant_sig, + const struct GNUNET_HashCode *h_contract, + uint64_t rtransaction_id, + const struct TALER_Amount *amount_with_fee, + const struct TALER_Amount *refund_fee); + + /** * Information about a coin that was revealed to the exchange * during /refresh/reveal. @@ -1358,6 +1381,23 @@ struct TALER_EXCHANGEDB_Plugin struct TALER_EXCHANGEDB_Session *session, const struct TALER_EXCHANGEDB_Refund *refund); + /** + * Select refunds by @a coin_pub. + * + * @param cls closure of plugin + * @param session database handle to use + * @param coin_pub coin to get refunds for + * @param cb function to call for each refund found + * @param cb_cls closure for @a cb + * @return query result status + */ + enum GNUNET_DB_QueryStatus + (*select_refunds_by_coin)(void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + TALER_EXCHANGEDB_RefundCoinCallback cb, + void *cb_cls); + /** * Mark a deposit as tiny, thereby declaring that it cannot be -- cgit v1.2.3 From d9f16db0efca71d47455ee2efab7eabf4003a0a0 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 3 Jan 2018 01:10:23 +0100 Subject: add testcase for 'select_refunds_by_coin' --- src/exchangedb/plugin_exchangedb_postgres.c | 2 +- src/exchangedb/test_exchangedb.c | 73 ++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 9c91259b..7e1ef54e 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -3082,7 +3082,7 @@ get_refunds_cb (void *cls, &merchant_pub), GNUNET_PQ_result_spec_auto_from_type ("merchant_sig", &merchant_sig), - GNUNET_PQ_result_spec_auto_from_type ("h_contract", + GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", &h_contract), GNUNET_PQ_result_spec_uint64 ("rtransaction_id", &rtransaction_id), diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index 5891a08a..62ff2a74 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -1393,6 +1393,72 @@ wire_missing_cb (void *cls, } +/** + * Callback invoked with information about refunds applicable + * to a particular coin. + * + * @param cls closure with the `struct TALER_EXCHANGEDB_Refund *` we expect to get + * @param merchant_pub public key of merchant who authorized refund + * @param merchant_sig signature of merchant authorizing refund + * @param h_contract hash of contract being refunded + * @param rtransaction_id refund transaction ID + * @param amount_with_fee amount being refunded + * @param refund_fee fee the exchange keeps for the refund processing + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static int +check_refund_cb (void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_MerchantSignatureP *merchant_sig, + const struct GNUNET_HashCode *h_contract, + uint64_t rtransaction_id, + const struct TALER_Amount *amount_with_fee, + const struct TALER_Amount *refund_fee) +{ + const struct TALER_EXCHANGEDB_Refund *refund = cls; + + if (0 != memcmp (merchant_pub, + &refund->merchant_pub, + sizeof (struct TALER_MerchantPublicKeyP))) + { + GNUNET_break (0); + result = 66; + } + if (0 != memcmp (merchant_sig, + &refund->merchant_sig, + sizeof (struct TALER_MerchantSignatureP))) + { + GNUNET_break (0); + result = 66; + } + if (0 != memcmp (h_contract, + &refund->h_contract_terms, + sizeof (struct GNUNET_HashCode))) + { + GNUNET_break (0); + result = 66; + } + if (rtransaction_id != refund->rtransaction_id) + { + GNUNET_break (0); + result = 66; + } + if (0 != TALER_amount_cmp (amount_with_fee, + &refund->refund_amount)) + { + GNUNET_break (0); + result = 66; + } + if (0 != TALER_amount_cmp (refund_fee, + &refund->refund_fee)) + { + GNUNET_break (0); + result = 66; + } + return GNUNET_OK; +} + + /** * Main function that will be run by the scheduler. * @@ -1897,7 +1963,12 @@ run (void *cls) plugin->insert_refund (plugin->cls, session, &refund)); - + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->select_refunds_by_coin (plugin->cls, + session, + &refund.coin.coin_pub, + &check_refund_cb, + &refund)); /* test payback / revocation */ RND_BLK (&master_sig); -- cgit v1.2.3 From 5587732f5276621ada83a53d1fb0ee8b6f80032b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 4 Jan 2018 00:41:19 +0100 Subject: fix auditor to properly verify wire fee signatures and more gracefully handle arithmetic amount issue --- src/auditor/taler-auditor.c | 14 +++++++++-- src/exchange-lib/test_exchange_api.c | 2 +- src/exchange/taler-exchange-aggregator.c | 1 + src/exchange/taler-exchange-httpd_track_transfer.c | 2 ++ src/exchangedb/plugin_exchangedb_postgres.c | 27 ++++++++++++++++++++-- src/exchangedb/test_exchangedb.c | 23 ++++++++++++++---- src/include/taler_exchangedb_plugin.h | 4 ++++ 7 files changed, 64 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c index 870a81a4..89f04460 100644 --- a/src/auditor/taler-auditor.c +++ b/src/auditor/taler-auditor.c @@ -1664,10 +1664,15 @@ struct WireFeeInfo struct GNUNET_TIME_Absolute end_date; /** - * How high is the fee. + * How high is the wire fee. */ struct TALER_Amount wire_fee; + /** + * How high is the closing fee. + */ + struct TALER_Amount closing_fee; + }; @@ -1815,6 +1820,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, struct TALER_Amount spent; struct TALER_Amount value; struct TALER_Amount merchant_loss; + struct TALER_Amount merchant_delta; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking transaction history of coin %s\n", @@ -2043,7 +2049,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, /* Finally, update @a merchant_gain by subtracting what he "lost" from refunds */ if (GNUNET_SYSERR == - TALER_amount_subtract (merchant_gain, + TALER_amount_subtract (&merchant_delta, merchant_gain, &merchant_loss)) { @@ -2055,6 +2061,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, 0); return GNUNET_SYSERR; } + *merchant_gain = merchant_delta; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Coin %s contributes %s to contract %s\n", TALER_B2S (coin_pub), @@ -2276,6 +2283,7 @@ get_wire_fee (struct AggregationContext *ac, &wfi->start_date, &wfi->end_date, &wfi->wire_fee, + &wfi->closing_fee, &master_sig)) { GNUNET_break (0); @@ -2299,6 +2307,8 @@ get_wire_fee (struct AggregationContext *ac, wp.end_date = GNUNET_TIME_absolute_hton (wfi->end_date); TALER_amount_hton (&wp.wire_fee, &wfi->wire_fee); + TALER_amount_hton (&wp.closing_fee, + &wfi->closing_fee); if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES, &wp.purpose, diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c index a1aa38c7..3a9cfd1a 100644 --- a/src/exchange-lib/test_exchange_api.c +++ b/src/exchange-lib/test_exchange_api.c @@ -3566,7 +3566,7 @@ run (void *cls) .label = "check-empty-after-refund" }, -#if 0 +#if 1 /* Test refunded coins are never executed, even past refund deadline */ { .oc = OC_ADMIN_ADD_INCOMING, diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c index 3de5630d..c9ff958f 100644 --- a/src/exchange/taler-exchange-aggregator.c +++ b/src/exchange/taler-exchange-aggregator.c @@ -374,6 +374,7 @@ update_fees (struct WirePlugin *wp, p->start_date, p->end_date, &p->wire_fee, + &p->closing_fee, &p->master_sig); if (qs < 0) { diff --git a/src/exchange/taler-exchange-httpd_track_transfer.c b/src/exchange/taler-exchange-httpd_track_transfer.c index 4d28096b..38c6c29e 100644 --- a/src/exchange/taler-exchange-httpd_track_transfer.c +++ b/src/exchange/taler-exchange-httpd_track_transfer.c @@ -352,6 +352,7 @@ track_transfer_transaction (void *cls, struct GNUNET_TIME_Absolute wire_fee_start_date; struct GNUNET_TIME_Absolute wire_fee_end_date; struct TALER_MasterSignatureP wire_fee_master_sig; + struct TALER_Amount closing_fee; ctx->is_valid = GNUNET_NO; ctx->wdd_head = NULL; @@ -393,6 +394,7 @@ track_transfer_transaction (void *cls, &wire_fee_start_date, &wire_fee_end_date, &ctx->wire_fee, + &closing_fee, &wire_fee_master_sig); if (0 >= qs) { diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 7e1ef54e..84774641 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -383,6 +383,9 @@ postgres_create_tables (void *cls) ",wire_fee_val INT8 NOT NULL" ",wire_fee_frac INT4 NOT NULL" ",wire_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" + ",closing_fee_val INT8 NOT NULL" + ",closing_fee_frac INT4 NOT NULL" + ",closing_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" ",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)" ",PRIMARY KEY (wire_method, start_date)" /* this combo must be unique */ ");"), @@ -1170,6 +1173,9 @@ postgres_prepare (PGconn *db_conn) ",wire_fee_val" ",wire_fee_frac" ",wire_fee_curr" + ",closing_fee_val" + ",closing_fee_frac" + ",closing_fee_curr" ",master_sig" " FROM wire_fee" " WHERE wire_method=$1" @@ -1185,10 +1191,13 @@ postgres_prepare (PGconn *db_conn) ",wire_fee_val" ",wire_fee_frac" ",wire_fee_curr" + ",closing_fee_val" + ",closing_fee_frac" + ",closing_fee_curr" ",master_sig" ") VALUES " - "($1, $2, $3, $4, $5, $6, $7);", - 7), + "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);", + 19), /* Used in #postgres_store_wire_transfer_out */ GNUNET_PQ_make_prepare ("insert_wire_out", "INSERT INTO wire_out " @@ -4356,6 +4365,7 @@ postgres_insert_aggregation_tracking (void *cls, * @param[out] start_date when does the fee go into effect * @param[out] end_date when does the fee end being valid * @param[out] wire_fee how high is the wire transfer fee + * @param[out] closing_fee how high is the closing fee * @param[out] master_sig signature over the above by the exchange master key * @return status of the transaction */ @@ -4367,6 +4377,7 @@ postgres_get_wire_fee (void *cls, struct GNUNET_TIME_Absolute *start_date, struct GNUNET_TIME_Absolute *end_date, struct TALER_Amount *wire_fee, + struct TALER_Amount *closing_fee, struct TALER_MasterSignatureP *master_sig) { struct GNUNET_PQ_QueryParam params[] = { @@ -4378,6 +4389,7 @@ postgres_get_wire_fee (void *cls, TALER_PQ_result_spec_absolute_time ("start_date", start_date), TALER_PQ_result_spec_absolute_time ("end_date", end_date), TALER_PQ_result_spec_amount ("wire_fee", wire_fee), + TALER_PQ_result_spec_amount ("closing_fee", closing_fee), GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig), GNUNET_PQ_result_spec_end }; @@ -4398,6 +4410,7 @@ postgres_get_wire_fee (void *cls, * @param start_date when does the fee go into effect * @param end_date when does the fee end being valid * @param wire_fee how high is the wire transfer fee + * @param closing_fee how high is the closing fee * @param master_sig signature over the above by the exchange master key * @return transaction status code */ @@ -4408,6 +4421,7 @@ postgres_insert_wire_fee (void *cls, struct GNUNET_TIME_Absolute start_date, struct GNUNET_TIME_Absolute end_date, const struct TALER_Amount *wire_fee, + const struct TALER_Amount *closing_fee, const struct TALER_MasterSignatureP *master_sig) { struct GNUNET_PQ_QueryParam params[] = { @@ -4415,10 +4429,12 @@ postgres_insert_wire_fee (void *cls, TALER_PQ_query_param_absolute_time (&start_date), TALER_PQ_query_param_absolute_time (&end_date), TALER_PQ_query_param_amount (wire_fee), + TALER_PQ_query_param_amount (closing_fee), GNUNET_PQ_query_param_auto_from_type (master_sig), GNUNET_PQ_query_param_end }; struct TALER_Amount wf; + struct TALER_Amount cf; struct TALER_MasterSignatureP sig; struct GNUNET_TIME_Absolute sd; struct GNUNET_TIME_Absolute ed; @@ -4431,6 +4447,7 @@ postgres_insert_wire_fee (void *cls, &sd, &ed, &wf, + &cf, &sig); if (qs < 0) return qs; @@ -4449,6 +4466,12 @@ postgres_insert_wire_fee (void *cls, GNUNET_break (0); return GNUNET_DB_STATUS_HARD_ERROR; } + if (0 != TALER_amount_cmp (closing_fee, + &cf)) + { + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + } if ( (sd.abs_value_us != start_date.abs_value_us) || (ed.abs_value_us != end_date.abs_value_us) ) { diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index 62ff2a74..a0eb50f0 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -1062,10 +1062,12 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session) struct GNUNET_TIME_Absolute start_date; struct GNUNET_TIME_Absolute end_date; struct TALER_Amount wire_fee; + struct TALER_Amount closing_fee; struct TALER_MasterSignatureP master_sig; struct GNUNET_TIME_Absolute sd; struct GNUNET_TIME_Absolute ed; struct TALER_Amount fee; + struct TALER_Amount fee2; struct TALER_MasterSignatureP ms; start_date = GNUNET_TIME_absolute_get (); @@ -1075,6 +1077,9 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session) GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":1.424242", &wire_fee)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":2.424242", + &closing_fee)); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &master_sig, sizeof (master_sig)); @@ -1085,6 +1090,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session) start_date, end_date, &wire_fee, + &closing_fee, &master_sig)) { GNUNET_break (0); @@ -1097,6 +1103,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session) start_date, end_date, &wire_fee, + &closing_fee, &master_sig)) { GNUNET_break (0); @@ -1112,6 +1119,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session) &sd, &ed, &fee, + &fee2, &ms)) { GNUNET_break (0); @@ -1125,6 +1133,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session) &sd, &ed, &fee, + &fee2, &ms)) { GNUNET_break (0); @@ -1134,6 +1143,8 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session) (ed.abs_value_us != end_date.abs_value_us) || (0 != TALER_amount_cmp (&fee, &wire_fee)) || + (0 != TALER_amount_cmp (&fee2, + &closing_fee)) || (0 != memcmp (&ms, &master_sig, sizeof (ms))) ) @@ -1956,7 +1967,8 @@ run (void *cls) refund.merchant_pub = deposit.merchant_pub; RND_BLK (&refund.merchant_sig); refund.h_contract_terms = deposit.h_contract_terms; - refund.rtransaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); + refund.rtransaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT64_MAX); refund.refund_amount = deposit.amount_with_fee; refund.refund_fee = fee_refund; FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != @@ -2198,9 +2210,11 @@ main (int argc, NULL); plugin_name++; (void) GNUNET_asprintf (&testname, - "test-exchange-db-%s", plugin_name); + "test-exchange-db-%s", + plugin_name); (void) GNUNET_asprintf (&config_filename, - "%s.conf", testname); + "%s.conf", + testname); cfg = GNUNET_CONFIGURATION_create (); if (GNUNET_OK != GNUNET_CONFIGURATION_parse (cfg, @@ -2211,7 +2225,8 @@ main (int argc, GNUNET_free (testname); return 2; } - GNUNET_SCHEDULER_run (&run, cfg); + GNUNET_SCHEDULER_run (&run, + cfg); GNUNET_CONFIGURATION_destroy (cfg); GNUNET_free (config_filename); GNUNET_free (testname); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index c531d838..ae38856a 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -1698,6 +1698,7 @@ struct TALER_EXCHANGEDB_Plugin * @param start_date when does the fee go into effect * @param end_date when does the fee end being valid * @param wire_fee how high is the wire transfer fee + * @param closing_fee how high is the closing fee * @param master_sig signature over the above by the exchange master key * @return transaction status code */ @@ -1708,6 +1709,7 @@ struct TALER_EXCHANGEDB_Plugin struct GNUNET_TIME_Absolute start_date, struct GNUNET_TIME_Absolute end_date, const struct TALER_Amount *wire_fee, + const struct TALER_Amount *closing_fee, const struct TALER_MasterSignatureP *master_sig); @@ -1721,6 +1723,7 @@ struct TALER_EXCHANGEDB_Plugin * @param[out] start_date when does the fee go into effect * @param[out] end_date when does the fee end being valid * @param[out] wire_fee how high is the wire transfer fee + * @param[out] closing_fee how high is the closing fee * @param[out] master_sig signature over the above by the exchange master key * @return query status of the transaction */ @@ -1732,6 +1735,7 @@ struct TALER_EXCHANGEDB_Plugin struct GNUNET_TIME_Absolute *start_date, struct GNUNET_TIME_Absolute *end_date, struct TALER_Amount *wire_fee, + struct TALER_Amount *closing_fee, struct TALER_MasterSignatureP *master_sig); -- cgit v1.2.3 From 7d60d3a61823594a9f6df9f884d9237741a4576c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 4 Jan 2018 09:22:33 +0100 Subject: nicer formatting, some DCE --- contrib/auditor-report.tex.j2 | 20 ++++++++++---------- src/auditor/taler-auditor.c | 5 ----- 2 files changed, 10 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/contrib/auditor-report.tex.j2 b/contrib/auditor-report.tex.j2 index e4f5c8dc..bad7397d 100644 --- a/contrib/auditor-report.tex.j2 +++ b/contrib/auditor-report.tex.j2 @@ -209,7 +209,7 @@ the financial damage done to the customer). {% if data.amount_arithmetic_inconsistencies|length() == 0 %} {\bf No arithmetic problems detected.} {% else %} - \begin{longtable}{p{4.5cm}|l|rl|rl} + \begin{longtable}{p{3.5cm}|l|rl|rl} {\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\ \hline \hline \endfirsthead @@ -218,11 +218,11 @@ the financial damage done to the customer). \hline \hline {\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\ \endfoot - \hline - {\bf Total} & & - {{ data.total_arithmetic_delta_plus.value }}.{{ data.total_arithmetic_delta_plus.fraction }} & + \hline \hline + \multicolumn{2}{l|}{ {\bf $\sum$ Deltas (Auditor-Exchange)} } & + + {{ data.total_arithmetic_delta_plus.value }}.{{ data.total_arithmetic_delta_plus.fraction }} & {{ data.total_arithmetic_delta_plus.currency }} & - {{ data.total_arithmetic_delta_minus.value }}.{{ data.total_arithmetic_delta_minus.fraction }} & + - {{ data.total_arithmetic_delta_minus.value }}.{{ data.total_arithmetic_delta_minus.fraction }} & {{ data.total_arithmetic_delta_minus.currency }} \\ \caption{Arithmetic inconsistencies.} \label{table:amount:arithmetic:inconsistencies} @@ -327,14 +327,14 @@ any effects on its own balance, those entries are excluded from the total. {% if data.coin_inconsistencies|length() == 0 %} {\bf All coin histories were unproblematic.} {% else %} - \begin{longtable}{l|p{5.5cm}|rl|rl} - {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\ + \begin{longtable}{p{1.8cm}|p{3cm}|rl|rl} + {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\ \hline \hline \endfirsthead - {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\ \hline \hline + {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\ \hline \hline \endhead \hline \hline - {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\ + {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\ \endfoot \hline {\bf Total} & & @@ -696,7 +696,7 @@ impact. \hline \hline {\bf Table} & {\bf Row} & {\bf Diagnostic} \\ \endfoot - \hline + \hline \hline {\bf Table} & {\bf Row} & {\bf Diagnostic} \\ \caption{Other issues found (by table and row).} \label{table:misc} diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c index 89f04460..7ce2032d 100644 --- a/src/auditor/taler-auditor.c +++ b/src/auditor/taler-auditor.c @@ -1702,11 +1702,6 @@ struct AggregationContext */ struct WireFeeInfo *fee_tail; - /** - * How much did we make in aggregation fees. - */ - struct TALER_Amount total_aggregation_feesX; - /** * Final result status. */ -- cgit v1.2.3 From 0906696ec2949e2c9fa7a168e3d270fae1c3a905 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 4 Jan 2018 10:38:16 +0100 Subject: bugfixes in auditor and rendering --- contrib/auditor-report.tex.j2 | 2 +- src/auditor/taler-auditor.c | 78 ++++++++++++------------------ src/exchange/taler-exchange-httpd_refund.c | 1 - 3 files changed, 32 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/contrib/auditor-report.tex.j2 b/contrib/auditor-report.tex.j2 index bad7397d..4c979f7e 100644 --- a/contrib/auditor-report.tex.j2 +++ b/contrib/auditor-report.tex.j2 @@ -337,7 +337,7 @@ any effects on its own balance, those entries are excluded from the total. {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\ \endfoot \hline - {\bf Total} & & + \multicolumn{2}{l|}{ $\sum$ {\bf Delta (Auditor-Exchange)} } & {{ data.total_coin_delta_plus.value }}.{{ data.total_coin_delta_plus.fraction }} & {{ data.total_coin_delta_plus.currency }} & - {{ data.total_coin_delta_minus.value }}.{{ data.total_coin_delta_minus.fraction }} & diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c index 7ce2032d..e807378f 100644 --- a/src/auditor/taler-auditor.c +++ b/src/auditor/taler-auditor.c @@ -391,7 +391,7 @@ report_amount_arithmetic_inconsistency (const char *operation, "profitable", (json_int_t) profitable)); if (0 != profitable) { - target = profitable + target = (1 == profitable) ? &total_arithmetic_delta_plus : &total_arithmetic_delta_minus; GNUNET_break (GNUNET_OK == @@ -452,7 +452,7 @@ report_coin_arithmetic_inconsistency (const char *operation, "profitable", (json_int_t) profitable)); if (0 != profitable) { - target = profitable + target = (1 == profitable) ? &total_coin_delta_plus : &total_coin_delta_minus; GNUNET_break (GNUNET_OK == @@ -1798,7 +1798,6 @@ struct WireCheckContext * @param dki denomination information about the coin * @param tl_head head of transaction history to verify * @param[out] merchant_gain amount the coin contributes to the wire transfer to the merchant - * @param[out] merchant_fees fees the exchange charged the merchant for the transaction(s) * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ static int @@ -1807,8 +1806,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki, const struct TALER_EXCHANGEDB_TransactionList *tl_head, - struct TALER_Amount *merchant_gain, - struct TALER_Amount *merchant_fees) + struct TALER_Amount *merchant_gain) { struct TALER_Amount expenditures; struct TALER_Amount refunds; @@ -1816,6 +1814,8 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, struct TALER_Amount value; struct TALER_Amount merchant_loss; struct TALER_Amount merchant_delta; + const struct TALER_Amount *deposit_fee; + int refund_deposit_fee; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking transaction history of coin %s\n", @@ -1831,9 +1831,6 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, GNUNET_assert (GNUNET_OK == TALER_amount_get_zero (currency, merchant_gain)); - GNUNET_assert (GNUNET_OK == - TALER_amount_get_zero (currency, - merchant_fees)); GNUNET_assert (GNUNET_OK == TALER_amount_get_zero (currency, &merchant_loss)); @@ -1841,6 +1838,8 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, know the order, so instead of subtracting we compute positive (deposit, melt) and negative (refund) values separately here, and then subtract the negative from the positive after the loop. */ + refund_deposit_fee = GNUNET_NO; + deposit_fee = NULL; for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head;NULL != tl;tl = tl->next) { const struct TALER_Amount *amount_with_fee; @@ -1872,7 +1871,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, { struct TALER_Amount amount_without_fee; - if (GNUNET_OK != + if (GNUNET_OK != TALER_amount_subtract (&amount_without_fee, amount_with_fee, fee)) @@ -1891,14 +1890,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Detected applicable deposit of %s\n", TALER_amount2s (&amount_without_fee)); - if (GNUNET_OK != - TALER_amount_add (merchant_fees, - merchant_fees, - fee)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } + deposit_fee = fee; } /* Check that the fees given in the transaction list and in dki match */ TALER_amount_ntoh (&tmp, @@ -1976,14 +1968,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Detected applicable refund of %s\n", TALER_amount2s (amount_with_fee)); - if (GNUNET_OK != - TALER_amount_add (merchant_fees, - merchant_fees, - fee)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } + refund_deposit_fee = GNUNET_YES; } /* Check that the fees given in the transaction list and in dki match */ TALER_amount_ntoh (&tmp, @@ -2009,9 +1994,19 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, } break; } - } /* for 'tl' */ + if ( (GNUNET_YES == refund_deposit_fee) && + (NULL != deposit_fee) ) + { + /* We had a /deposit operation AND a /refund operation, + and should thus not charge the merchant the /deposit fee */ + GNUNET_assert (GNUNET_OK == + TALER_amount_add (merchant_gain, + merchant_gain, + deposit_fee)); + } + /* Calculate total balance change, i.e. expenditures minus refunds */ if (GNUNET_SYSERR == TALER_amount_subtract (&spent, @@ -2023,7 +2018,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, coin_pub, &expenditures, &refunds, - 0); + 1); return GNUNET_SYSERR; } @@ -2042,7 +2037,8 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, return GNUNET_SYSERR; } - /* Finally, update @a merchant_gain by subtracting what he "lost" from refunds */ + /* Finally, update @a merchant_gain by subtracting what he "lost" + from refunds */ if (GNUNET_SYSERR == TALER_amount_subtract (&merchant_delta, merchant_gain, @@ -2053,7 +2049,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, coin_pub, merchant_gain, &merchant_loss, - 0); + 1); return GNUNET_SYSERR; } *merchant_gain = merchant_delta; @@ -2079,7 +2075,8 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, * @param h_contract_terms which proposal was this payment about * @param coin_pub which public key was this payment about * @param coin_value amount contributed by this coin in total (with fee) - * @param coin_fee applicable fee for this coin + * @param deposit_fee applicable deposit fee for this coin, actual + * fees charged may differ if coin was refunded */ static void wire_transfer_information_cb (void *cls, @@ -2091,12 +2088,11 @@ wire_transfer_information_cb (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_Amount *coin_value, - const struct TALER_Amount *coin_fee) + const struct TALER_Amount *deposit_fee) { struct WireCheckContext *wcc = cls; const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki; struct TALER_Amount computed_value; - struct TALER_Amount computed_fees; struct TALER_Amount coin_value_without_fee; struct TALER_EXCHANGEDB_TransactionList *tl; const struct TALER_CoinPublicInfo *coin; @@ -2158,18 +2154,17 @@ wire_transfer_information_cb (void *cls, merchant_pub, dki, tl, - &computed_value, - &computed_fees); + &computed_value); if (GNUNET_SYSERR == TALER_amount_subtract (&coin_value_without_fee, coin_value, - coin_fee)) + deposit_fee)) { wcc->qs = GNUNET_DB_STATUS_HARD_ERROR; report_amount_arithmetic_inconsistency ("aggregation (fee structure)", rowid, coin_value, - coin_fee, + deposit_fee, -1); return; } @@ -2184,17 +2179,6 @@ wire_transfer_information_cb (void *cls, &computed_value, -1); } - if (0 != - TALER_amount_cmp (&computed_fees, - coin_fee)) - { - wcc->qs = GNUNET_DB_STATUS_HARD_ERROR; - report_amount_arithmetic_inconsistency ("aggregation (fee)", - rowid, - coin_fee, - &computed_fees, - 1); - } edb->free_coin_transaction_list (edb->cls, tl); diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c index f0aaa65c..986c9d31 100644 --- a/src/exchange/taler-exchange-httpd_refund.c +++ b/src/exchange/taler-exchange-httpd_refund.c @@ -285,7 +285,6 @@ refund_transaction (void *cls, } /* check if we already send the money for the /deposit */ - // FIXME: DB API... qs = TEH_plugin->test_deposit_done (TEH_plugin->cls, session, dep); -- cgit v1.2.3 From 77a58673ef432296c0ac9a8778cadfb6d5982061 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 4 Jan 2018 10:39:13 +0100 Subject: comment out extended test again --- src/exchange-lib/test_exchange_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c index 3a9cfd1a..a1aa38c7 100644 --- a/src/exchange-lib/test_exchange_api.c +++ b/src/exchange-lib/test_exchange_api.c @@ -3566,7 +3566,7 @@ run (void *cls) .label = "check-empty-after-refund" }, -#if 1 +#if 0 /* Test refunded coins are never executed, even past refund deadline */ { .oc = OC_ADMIN_ADD_INCOMING, -- cgit v1.2.3 From 164dd0ff1ee701477f4b7064a3169495c5577c42 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 4 Jan 2018 11:56:45 +0100 Subject: fix #5234 --- ChangeLog | 4 ++ src/exchange/taler-exchange-aggregator.c | 80 ++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) (limited to 'src') diff --git a/ChangeLog b/ChangeLog index 2c18ef5a..ff4b6d71 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Thu Jan 4 11:55:41 CET 2018 + Fix issue #5234 (aggregator ignoring refunds). + Misc. minor fixes to the auditor. -CG + Mon Jan 1 23:15:37 CET 2018 Add TALER_EXCHANGE_refund2() API call to libtalerexchange. -CG diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c index c9ff958f..d5d43052 100644 --- a/src/exchange/taler-exchange-aggregator.c +++ b/src/exchange/taler-exchange-aggregator.c @@ -123,6 +123,11 @@ struct AggregationUnit */ struct GNUNET_HashCode h_wire; + /** + * Hash code of contract we are currently looking into. + */ + const struct GNUNET_HashCode *h_contract; + /** * Wire transfer identifier we use. */ @@ -568,6 +573,53 @@ exchange_serve_process_config () } +/** + * Callback invoked with information about refunds applicable + * to a particular coin. Subtract refunded amount(s) from + * the aggregation unit's total amount. + * + * @param cls closure with a `struct AggregationUnit *` + * @param merchant_pub public key of merchant who authorized refund + * @param merchant_sig signature of merchant authorizing refund + * @param h_contract hash of contract being refunded + * @param rtransaction_id refund transaction ID + * @param amount_with_fee amount being refunded + * @param refund_fee fee the exchange keeps for the refund processing + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static int +refund_by_coin_cb (void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_MerchantSignatureP *merchant_sig, + const struct GNUNET_HashCode *h_contract, + uint64_t rtransaction_id, + const struct TALER_Amount *amount_with_fee, + const struct TALER_Amount *refund_fee) +{ + struct AggregationUnit *au = cls; + + /* TODO: potential optimization: include these conditions + in the SELECT! */ + if (0 != memcmp (merchant_pub, + &au->merchant_pub, + sizeof (struct TALER_MerchantPublicKeyP))) + return GNUNET_OK; /* different merchant */ + if (0 != memcmp (h_contract, + au->h_contract, + sizeof (struct GNUNET_HashCode))) + return GNUNET_OK; /* different contract */ + if (GNUNET_OK != + TALER_amount_subtract (&au->total_amount, + &au->total_amount, + amount_with_fee)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + /** * Function called with details about deposits that have been made, * with the goal of executing the corresponding wire transaction. @@ -610,6 +662,20 @@ deposit_cb (void *cls, return GNUNET_DB_STATUS_HARD_ERROR; } au->row_id = row_id; + + au->h_contract = h_contract_terms; + qs = db_plugin->select_refunds_by_coin (db_plugin->cls, + au->session, + coin_pub, + &refund_by_coin_cb, + au); + au->h_contract = NULL; + if (0 > qs) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return qs; + } + GNUNET_assert (NULL == au->wire); au->wire = json_incref ((json_t *) wire); if (GNUNET_OK != @@ -731,6 +797,20 @@ aggregate_cb (void *cls, /* Skip this one, but keep going! */ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } + + au->h_contract = h_contract_terms; + qs = db_plugin->select_refunds_by_coin (db_plugin->cls, + au->session, + coin_pub, + &refund_by_coin_cb, + au); + au->h_contract = NULL; + if (0 > qs) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return qs; + } + if (au->rows_offset >= aggregation_limit) { /* Bug: we asked for at most #aggregation_limit results! */ -- cgit v1.2.3 From 57eb85976bf2e291a4a944244413ed157128ba90 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 4 Jan 2018 11:57:11 +0100 Subject: enable test for #5234 --- src/exchange-lib/test_exchange_api.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c index a1aa38c7..bccd4f99 100644 --- a/src/exchange-lib/test_exchange_api.c +++ b/src/exchange-lib/test_exchange_api.c @@ -3566,7 +3566,6 @@ run (void *cls) .label = "check-empty-after-refund" }, -#if 0 /* Test refunded coins are never executed, even past refund deadline */ { .oc = OC_ADMIN_ADD_INCOMING, @@ -3621,7 +3620,7 @@ run (void *cls) /* check that aggregator didn't do anything, as expected */ { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, .label = "check-refund-fast-not-run" }, -#endif + /* ************** End of refund API testing************* */ -- cgit v1.2.3 From acc3a41df812b59a1775d3fc0697a0b73d847963 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 4 Jan 2018 13:47:03 +0100 Subject: add pay session signature --- src/include/taler_signatures.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'src') diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 6355303a..c281d21f 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -178,6 +178,12 @@ */ #define TALER_SIGNATURE_MERCHANT_REFUND_OK 1105 +/** + * Signature where the merchant confirms that the user replayed + * a payment for a browser session. + */ +#define TALER_SIGNATURE_MERCHANT_PAY_SESSION 1106 + /*********************/ /* Wallet signatures */ @@ -1291,6 +1297,31 @@ struct TALER_MerchantRefundConfirmationPS }; +/** + * Used by the merchant to confirm to the frontend that + * the user did a payment replay with the current browser session. + */ +struct TALER_MerchantPaySessionSigPS +{ + /** + * Set to #TALER_SIGNATURE_MERCHANT_PAY_SESSION. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Hashed order id. + * Hashed without the 0-termination. + */ + struct GNUNET_HashCode h_order_id GNUNET_PACKED; + + /** + * Hashed session id. + * Hashed without the 0-termination. + */ + struct GNUNET_HashCode h_session_id GNUNET_PACKED; + +}; + GNUNET_NETWORK_STRUCT_END #endif -- cgit v1.2.3