diff options
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-orders-ID-track-UNSPEC.c | 1250 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-orders-ID-track-UNSPEC.h | 47 |
2 files changed, 0 insertions, 1297 deletions
diff --git a/src/backend/taler-merchant-httpd_private-post-orders-ID-track-UNSPEC.c b/src/backend/taler-merchant-httpd_private-post-orders-ID-track-UNSPEC.c deleted file mode 100644 index 6fc8e167..00000000 --- a/src/backend/taler-merchant-httpd_private-post-orders-ID-track-UNSPEC.c +++ /dev/null @@ -1,1250 +0,0 @@ -/* - This file is part of TALER - (C) 2014-2020 Taler Systems SA - - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-merchant-httpd_track-transaction.c - * @brief implement API for tracking deposits and wire transfers - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include <jansson.h> -#include <taler/taler_signatures.h> -#include <taler/taler_json_lib.h> -#include "taler-merchant-httpd.h" -#include "taler_merchant_service.h" -#include "taler-merchant-httpd_mhd.h" -#include "taler-merchant-httpd_auditors.h" -#include "taler-merchant-httpd_exchanges.h" -#include "taler-merchant-httpd_track-transaction.h" - - -/** - * Information about a wire transfer for a /track/transaction response. - */ -struct TransactionWireTransfer -{ - - /** - * Wire transfer identifier this struct is about. - */ - struct TALER_WireTransferIdentifierRawP wtid; - - /** - * When was this wire transfer executed? - */ - struct GNUNET_TIME_Absolute execution_time; - - /** - * Number of coins of the selected transaction that - * is covered by this wire transfer. - */ - unsigned int num_coins; - - /** - * Information about the coins of the selected transaction - * that are part of the wire transfer. - */ - struct TALER_MERCHANT_CoinWireTransfer *coins; - - /** - * URL of the exchange that executed the wire transfer. - */ - char *exchange_url; -}; - - -/** - * Map containing all the known merchant instances - */ -extern struct GNUNET_CONTAINER_MultiHashMap *by_id_map; - -/** - * How long to wait before giving up processing with the exchange? - */ -#define TRACK_TIMEOUT (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \ - 30)) - -/** - * How often do we retry the simple INSERT database transaction? - */ -#define MAX_RETRIES 3 - - -/** - * Generate /track/transaction response. - * - * @param num_transfers how many wire transfers make up the transaction - * @param transfers data on each wire transfer - * @return MHD response object - */ -static struct MHD_Response * -make_track_transaction_ok (unsigned int num_transfers, - const struct TransactionWireTransfer *transfers) -{ - json_t *j_transfers; - - j_transfers = json_array (); - for (unsigned int i = 0; i<num_transfers; i++) - { - const struct TransactionWireTransfer *transfer = &transfers[i]; - struct TALER_Amount sum; - - sum = transfer->coins[0].amount_with_fee; - for (unsigned int j = 1; j<transfer->num_coins; j++) - { - const struct TALER_MERCHANT_CoinWireTransfer *coin = &transfer->coins[j]; - - GNUNET_assert (0 <= - TALER_amount_add (&sum, - &sum, - &coin->amount_with_fee)); - } - - GNUNET_assert (0 == - json_array_append_new ( - j_transfers, - json_pack ( - "{s:s, s:o, s:o, s:o}", - "exchange", - transfer->exchange_url, - "wtid", - GNUNET_JSON_from_data_auto (&transfer->wtid), - "execution_time", - GNUNET_JSON_from_time_abs (transfer->execution_time), - "amount", - TALER_JSON_from_amount (&sum)))); - } - { - struct MHD_Response *ret; - - ret = TALER_MHD_make_json (j_transfers); - json_decref (j_transfers); - return ret; - } -} - - -/** - * Context for a /track/transaction operation. - */ -struct TrackTransactionContext; - -/** - * Merchant instance being tracked - */ -struct MerchantInstance; - -/** - * Information we keep for each coin in a /track/transaction operation. - */ -struct TrackCoinContext -{ - /** - * Kept in a DLL. - */ - struct TrackCoinContext *next; - - /** - * Kept in a DLL. - */ - struct TrackCoinContext *prev; - - /** - * Our context for a /track/transaction operation. - */ - struct TrackTransactionContext *tctx; - - /** - * Public key of the coin. - */ - struct TALER_CoinSpendPublicKeyP coin_pub; - - /** - * Exchange that was used for the transaction. - */ - char *exchange_url; - - /** - * Handle for the request to resolve the WTID for this coin. - */ - struct TALER_EXCHANGE_DepositGetHandle *dwh; - - /** - * Wire transfer identifier for this coin. - */ - struct TALER_WireTransferIdentifierRawP wtid; - - /** - * Execution time of the wire transfer @e wtid. - */ - struct GNUNET_TIME_Absolute execution_time; - - /** - * Value of the coin including deposit fee. - */ - struct TALER_Amount amount_with_fee; - - /** - * Deposit fee for the coin. - */ - struct TALER_Amount deposit_fee; - - /** - * Have we obtained the WTID for this coin yet? - */ - int have_wtid; - -}; - - -/** - * Context for a /track/transaction operation. - */ -struct TrackTransactionContext -{ - - /** - * This field MUST be first. - */ - struct TM_HandlerContext hc; - - /** - * HTTP request we are handling. - */ - struct MHD_Connection *connection; - - /** - * Kept in a DLL. - */ - struct TrackCoinContext *tcc_head; - - /** - * Kept in a DLL. - */ - struct TrackCoinContext *tcc_tail; - - /** - * Task run on timeout. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * Handle for operation to lookup /keys (and auditors) from - * the exchange used for this transaction; NULL if no operation is - * pending. - */ - struct TMH_EXCHANGES_FindOperation *fo; - - /** - * Handle to our exchange, once we found it. - */ - struct TALER_EXCHANGE_Handle *eh; - - /** - * URL of the exchange we currently have in @e eh. - */ - const char *current_exchange; - - /** - * Handle we use to resolve transactions for a given WTID. - */ - struct TALER_EXCHANGE_TransfersGetHandle *wdh; - - /** - * Response to return upon resume. - */ - struct MHD_Response *response; - - /** - * Wire transfer identifier we are currently looking up in @e wdh. - */ - struct TALER_WireTransferIdentifierRawP current_wtid; - - /** - * Execution time of the wire transfer we are currently looking up in @e wdh. - */ - struct GNUNET_TIME_Absolute current_execution_time; - - /** - * Hash of wire details for the transaction. - */ - struct GNUNET_HashCode h_wire; - - /** - * Timestamp of the transaction. - */ - struct GNUNET_TIME_Absolute timestamp; - - /** - * Refund deadline for the transaction. - */ - struct GNUNET_TIME_Absolute refund_deadline; - - /** - * Total value of the transaction. - */ - struct TALER_Amount total_amount; - - /** - * Transaction this request is about. - */ - const char *transaction_id; - - /** - * Proposal's hashcode. - */ - struct GNUNET_HashCode h_contract_terms; - - /** - * Response code to return upon resume. - */ - unsigned int response_code; - - /** - * Which merchant instance is being tracked - */ - struct MerchantInstance *mi; - - /** - * Set to negative values in #coin_cb() if we encounter - * a database problem. - */ - enum GNUNET_DB_QueryStatus qs; - -}; - - -/** - * Free the @a tctx. - * - * @param tctx data to free - */ -static void -free_tctx (struct TrackTransactionContext *tctx) -{ - struct TrackCoinContext *tcc; - - while (NULL != (tcc = tctx->tcc_head)) - { - GNUNET_CONTAINER_DLL_remove (tctx->tcc_head, - tctx->tcc_tail, - tcc); - if (NULL != tcc->dwh) - { - TALER_EXCHANGE_deposits_get_cancel (tcc->dwh); - tcc->dwh = NULL; - } - if (NULL != tcc->exchange_url) - { - GNUNET_free (tcc->exchange_url); - tcc->exchange_url = NULL; - } - GNUNET_free (tcc); - } - if (NULL != tctx->wdh) - { - TALER_EXCHANGE_transfers_get_cancel (tctx->wdh); - tctx->wdh = NULL; - } - if (NULL != tctx->fo) - { - TMH_EXCHANGES_find_exchange_cancel (tctx->fo); - tctx->fo = NULL; - } - if (NULL != tctx->timeout_task) - { - GNUNET_SCHEDULER_cancel (tctx->timeout_task); - tctx->timeout_task = NULL; - } - GNUNET_free (tctx); -} - - -/** - * Custom cleanup routine for a `struct TrackTransactionContext`. - * - * @param hc the `struct PayContext` to clean up. - */ -static void -track_transaction_cleanup (struct TM_HandlerContext *hc) -{ - struct TrackTransactionContext *tctx = (struct TrackTransactionContext *) hc; - - free_tctx (tctx); -} - - -/** - * Resume the given /track/transaction operation and send the given - * response. Stores the response in the @a tctx and signals MHD to - * resume the connection. Also ensures MHD runs immediately. - * - * @param tctx transaction tracking context - * @param response_code response code to use - * @param response response data to send back - */ -static void -resume_track_transaction_with_response (struct TrackTransactionContext *tctx, - unsigned int response_code, - struct MHD_Response *response) -{ - tctx->response_code = response_code; - tctx->response = response; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resuming /track/transaction handling as exchange interaction is done (%u)\n", - response_code); - if (NULL != tctx->timeout_task) - { - GNUNET_SCHEDULER_cancel (tctx->timeout_task); - tctx->timeout_task = NULL; - } - MHD_resume_connection (tctx->connection); - TMH_trigger_daemon (); /* we resumed, kick MHD */ -} - - -/** - * This function is called to trace the wire transfers for - * all of the coins of the transaction of the @a tctx. Once - * we have traced all coins, we build the response. - * - * @param tctx track context with established connection to exchange - */ -static void -trace_coins (struct TrackTransactionContext *tctx); - - -/** - * Function called with detailed wire transfer data, including all - * of the coin transactions that were combined into the wire transfer. - * - * We now store this information. Then we check if we still have - * any coins of the original wire transfer not taken care of. - * - * @param cls closure - * @param hr HTTP response details - * @param exchange_pub public key of the exchange used for signing - * @param execution_time time when the exchange claims to have performed the wire transfer - * @param wtid extracted wire transfer identifier, or NULL if the exchange could - * not provide any (set only if @a http_status is #MHD_HTTP_OK) - * @param total_amount total amount of the wire transfer, or NULL if the exchange could - * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK) - * @param wire_fee wire fee that was charged by the exchange - * @param details_length length of the @a details array - * @param details array with details about the combined transactions - */ -static void -wire_deposits_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - const struct TALER_ExchangePublicKeyP *exchange_pub, - const struct GNUNET_HashCode *h_wire, - struct GNUNET_TIME_Absolute execution_time, - const struct TALER_Amount *total_amount, - const struct TALER_Amount *wire_fee, - unsigned int details_length, - const struct TALER_TrackTransferDetails *details) -{ - struct TrackTransactionContext *tctx = cls; - enum GNUNET_DB_QueryStatus qs; - - tctx->wdh = NULL; - if (MHD_HTTP_OK != hr->http_status) - { - GNUNET_break_op (0); - resume_track_transaction_with_response ( - tctx, - MHD_HTTP_FAILED_DEPENDENCY, - TALER_MHD_make_json_pack ( - "{s:I, s:I, s:I, s:O}", - "code", - (json_int_t) TALER_EC_TRACK_TRANSACTION_WIRE_TRANSFER_TRACE_ERROR, - "exchange_http_status", - (json_int_t) hr->http_status, - "exchange_code", - (json_int_t) hr->ec, - "exchange_reply", - hr->reply)); - return; - } - for (unsigned int i = 0; i<MAX_RETRIES; i++) - { - db->preflight (db->cls); - qs = db->store_transfer_to_proof (db->cls, - tctx->current_exchange, - &tctx->current_wtid, - tctx->current_execution_time, - exchange_pub, - hr->reply); - if (GNUNET_DB_STATUS_SOFT_ERROR != qs) - break; - } - if (0 > qs) - { - /* Not good, but not fatal either, log error and continue */ - /* Special report if retries insufficient */ - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - /* Always report on hard error as well to enable diagnostics */ - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to store transfer-to-proof mapping in DB\n"); - } - for (struct TrackCoinContext *tcc = tctx->tcc_head; - NULL != tcc; - tcc = tcc->next) - { - if (GNUNET_YES == tcc->have_wtid) - continue; - for (unsigned int d = 0; d<details_length; d++) - { - if (0 == GNUNET_memcmp (&details[d].coin_pub, - &tcc->coin_pub)) - { - tcc->wtid = tctx->current_wtid; - tcc->execution_time = tctx->current_execution_time; - tcc->have_wtid = GNUNET_YES; - } - - for (unsigned int i = 0; i<MAX_RETRIES; i++) - { - db->preflight (db->cls); - qs = db->store_coin_to_transfer (db->cls, - &details[d].h_contract_terms, - &details[d].coin_pub, - &tctx->current_wtid); - if (GNUNET_DB_STATUS_SOFT_ERROR != qs) - break; - } - if (0 > qs) - { - /* Not good, but not fatal either, log error and continue */ - /* Special report if retries insufficient */ - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - /* Always report on hard error as well to enable diagnostics */ - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to store coin-to-transfer mapping in DB\n"); - } - } - } - /* Continue tracing (will also handle case that we are done) */ - trace_coins (tctx); -} - - -/** - * Closure for #proof_cb(). - */ -struct ProofCheckContext -{ - /** - * Proof returned from #proof_cb. The reference counter was - * increased for this reference and it must thus be freed. - * NULL if we did not find any proof. The JSON should - * match the `TrackTransferResponse` of the exchange API - * (https://api.taler.net/api-exchange.html#tracktransferresponse) - */ - json_t *p_ret; - -}; - - -/** - * Function called with information about a wire transfer identifier. - * We actually never expect this to be called. - * - * @param cls closure with a `struct ProofCheckContext` - * @param proof proof from exchange about what the wire transfer was for - */ -static void -proof_cb (void *cls, - const json_t *proof) -{ - struct ProofCheckContext *pcc = cls; - - GNUNET_break (NULL == pcc->p_ret); - pcc->p_ret = json_incref ((json_t *) proof); -} - - -/** - * This function takes the wtid from the coin being tracked - * and _track_ it against the exchange. This way, we know - * all the other coins which were aggregated together with - * this one. This way we save further HTTP requests to track - * the other coins. - * - * @param cls closure with a `struct TrackCoinContext` - * @param hr HTTP response details - * @param exchange_pub public key of the exchange used for signing @a json - * @param wtid wire transfer identifier used by the exchange, NULL if exchange did not - * yet execute the transaction - * @param execution_time actual or planned execution time for the wire transfer - * @param coin_contribution contribution to the @a total_amount of the deposited coin (may be NULL) - */ -static void -wtid_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - const struct TALER_ExchangePublicKeyP *exchange_pub, - const struct TALER_WireTransferIdentifierRawP *wtid, - struct GNUNET_TIME_Absolute execution_time, - const struct TALER_Amount *coin_contribution) -{ - struct TrackCoinContext *tcc = cls; - struct TrackTransactionContext *tctx = tcc->tctx; - struct ProofCheckContext pcc; - enum GNUNET_DB_QueryStatus qs; - - tcc->dwh = NULL; - if (MHD_HTTP_OK != hr->http_status) - { - if (MHD_HTTP_ACCEPTED == hr->http_status) - { - resume_track_transaction_with_response ( - tcc->tctx, - MHD_HTTP_ACCEPTED, - /* Return verbatim what the exchange said. */ - TALER_MHD_make_json (hr->reply)); - return; - } - - /* Transaction not resolved for one of the - coins, report error! */ - resume_track_transaction_with_response ( - tcc->tctx, - MHD_HTTP_FAILED_DEPENDENCY, - TALER_MHD_make_json_pack ( - "{s:I, s:I, s:I, s:O}", - "code", - (json_int_t) TALER_EC_TRACK_TRANSACTION_COIN_TRACE_ERROR, - "exchange_http_status", - (json_int_t) hr->http_status, - "exchange_code", - (json_int_t) hr->ec, - "exchange_reply", - hr->reply)); - return; - } - tctx->current_wtid = *wtid; - tctx->current_execution_time = execution_time; - - pcc.p_ret = NULL; - /* attempt to find this wtid's track from our database, - Will make pcc.p_ret point to a "proof", if one exists. */ - db->preflight (db->cls); - qs = db->find_proof_by_wtid (db->cls, - tctx->current_exchange, - wtid, - &proof_cb, - &pcc); - if (0 > qs) - { - /* Simple select queries should not - cause serialization issues */ - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - /* Always report on hard error as well to enable diagnostics */ - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - resume_track_transaction_with_response ( - tcc->tctx, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_MHD_make_error ( - TALER_EC_TRACK_TRANSACTION_DB_FETCH_TRANSACTION_ERROR, - "Fail to query database about proofs")); - return; - } - - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - { - /* How come this wtid was already stored into the - database and _not all_ of its coins were already - tracked? Inconsistent state (! At least regarding - what the exchange tells us) */ - GNUNET_break_op (0); - resume_track_transaction_with_response ( - tcc->tctx, - MHD_HTTP_FAILED_DEPENDENCY, - TALER_MHD_make_json_pack ( - "{s:I, s:s, s:O, s:o, s:o}", - "code", (json_int_t) TALER_EC_TRACK_TRANSACTION_CONFLICTING_REPORTS, - "hint", "conflicting transfer data from exchange", - "transaction_tracking_claim", hr->reply, - "wtid_tracking_claim", pcc.p_ret, - "coin_pub", GNUNET_JSON_from_data_auto (&tcc->coin_pub))); - return; - } - - tctx->wdh = TALER_EXCHANGE_transfers_get (tctx->eh, - wtid, - &wire_deposits_cb, - tctx); -} - - -/** - * We have obtained all WTIDs, now prepare the response - * - * @param tctx handle for the operation - */ -static void -generate_response (struct TrackTransactionContext *tctx) -{ - unsigned int num_wtid = 0; - - /* count how many disjoint wire transfer identifiers there are; - note that there should only usually be one, so while this - is worst-case O(n^2), in pracitce this is O(n) */ - for (struct TrackCoinContext *tcc = tctx->tcc_head; - NULL != tcc; - tcc = tcc->next) - { - int found = GNUNET_NO; - - for (struct TrackCoinContext *tcc2 = tctx->tcc_head; - tcc2 != tcc; - tcc2 = tcc2->next) - { - if (0 == GNUNET_memcmp (&tcc->wtid, - &tcc2->wtid)) - { - found = GNUNET_YES; - break; - } - } - if (GNUNET_NO == found) - num_wtid++; - } - - { - /* on-stack allocation is fine, as the number of coins and the - number of wire-transfers per-transaction is expected to be tiny. */ - struct TransactionWireTransfer wts[num_wtid]; - unsigned int wtid_off; - - wtid_off = 0; - for (struct TrackCoinContext *tcc = tctx->tcc_head; - NULL != tcc; - tcc = tcc->next) - { - int found = GNUNET_NO; - - for (struct TrackCoinContext *tcc2 = tctx->tcc_head; - tcc2 != tcc; - tcc2 = tcc2->next) - { - if (0 == GNUNET_memcmp (&tcc->wtid, - &tcc2->wtid)) - { - found = GNUNET_YES; - break; - } - } - if (GNUNET_NO == found) - { - unsigned int num_coins; - struct TransactionWireTransfer *wt; - - wt = &wts[wtid_off++]; - wt->wtid = tcc->wtid; - wt->exchange_url = tcc->exchange_url; - wt->execution_time = tcc->execution_time; - /* count number of coins with this wtid */ - num_coins = 0; - for (struct TrackCoinContext *tcc2 = tctx->tcc_head; - NULL != tcc2; - tcc2 = tcc2->next) - { - if (0 == GNUNET_memcmp (&wt->wtid, - &tcc2->wtid)) - num_coins++; - } - /* initialize coins array */ - wt->num_coins = num_coins; - wt->coins = GNUNET_new_array (num_coins, - struct TALER_MERCHANT_CoinWireTransfer); - num_coins = 0; - for (struct TrackCoinContext *tcc2 = tctx->tcc_head; - NULL != tcc2; - tcc2 = tcc2->next) - { - if (0 == GNUNET_memcmp (&wt->wtid, - &tcc2->wtid)) - { - struct TALER_MERCHANT_CoinWireTransfer *coin = - &wt->coins[num_coins++]; - - coin->coin_pub = tcc2->coin_pub; - coin->amount_with_fee = tcc2->amount_with_fee; - coin->deposit_fee = tcc2->deposit_fee; - } - } - } /* GNUNET_NO == found */ - } /* for all tcc */ - GNUNET_assert (wtid_off == num_wtid); - - { - struct MHD_Response *resp; - - resp = make_track_transaction_ok (num_wtid, - wts); - for (wtid_off = 0; wtid_off < num_wtid; wtid_off++) - GNUNET_free (wts[wtid_off].coins); - resume_track_transaction_with_response (tctx, - MHD_HTTP_OK, - resp); - } - } /* end of scope for 'wts' and 'resp' */ -} - - -/** - * Find the exchange to trace the next coin(s). - * - * @param tctx operation context - */ -static void -find_exchange (struct TrackTransactionContext *tctx); - - -/** - * This function is called to 'trace the wire transfers' - * (true?) for all of the coins of the transaction of the @a tctx. - * Once we have traced all coins, we build the response. - * - * @param tctx track context with established connection to exchange - */ -static void -trace_coins (struct TrackTransactionContext *tctx) -{ - struct TrackCoinContext *tcc; - - /* Make sure we are connected to the exchange. */ - GNUNET_assert (NULL != tctx->eh); - - for (tcc = tctx->tcc_head; NULL != tcc; tcc = tcc->next) - - /* How come one doesn't have wtid? */ - if (GNUNET_YES != tcc->have_wtid) - break; - - if (NULL != tcc) - { - if (0 != strcmp (tcc->exchange_url, - tctx->current_exchange)) - { - /* exchange changed, find matching one first! */ - tctx->eh = NULL; - tctx->current_exchange = NULL; - find_exchange (tctx); - return; - } - /* we are not done requesting WTIDs from the current - exchange; do the next one */ - tcc->dwh = TALER_EXCHANGE_deposits_get (tctx->eh, - &tctx->mi->privkey, - &tctx->h_wire, - &tctx->h_contract_terms, - &tcc->coin_pub, - &wtid_cb, - tcc); - return; - } - tctx->current_exchange = NULL; - tctx->eh = NULL; - generate_response (tctx); -} - - -/** - * Function called with the result of our exchange lookup. - * Merely provide the execution context to the routine actually - * tracking the coin. - * - * @param cls the `struct TrackTransactionContext` - * @param hr HTTP response details - * @param eh NULL if exchange was not found to be acceptable - * @param wire_fee NULL (we did not specify a wire method) - * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config - */ -static void -process_track_transaction_with_exchange (void *cls, - const struct - TALER_EXCHANGE_HttpResponse *hr, - struct TALER_EXCHANGE_Handle *eh, - const struct TALER_Amount *wire_fee, - int exchange_trusted) -{ - struct TrackTransactionContext *tctx = cls; - - tctx->fo = NULL; - if (MHD_HTTP_OK != hr->http_status) - { - /* The request failed somehow */ - GNUNET_break_op (0); - resume_track_transaction_with_response ( - tctx, - MHD_HTTP_FAILED_DEPENDENCY, - TALER_MHD_make_json_pack ( - (NULL != hr->reply) - ? "{s:s, s:I, s:I, s:I, s:O}" - : "{s:s, s:I, s:I, s:I}", - "hint", "failed to obtain meta-data from exchange", - "code", (json_int_t) TALER_EC_TRACK_TRANSACTION_EXCHANGE_KEYS_FAILURE, - "exchange_http_status", (json_int_t) hr->http_status, - "exchange_code", (json_int_t) hr->ec, - "exchange_reply", hr->reply)); - return; - } - tctx->eh = eh; - trace_coins (tctx); -} - - -/** - * Handle a timeout for the processing of the track transaction request. - * - * @param cls closure - */ -static void -handle_track_transaction_timeout (void *cls) -{ - struct TrackTransactionContext *tctx = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resuming /track/transaction with error after timeout\n"); - tctx->timeout_task = NULL; - if (NULL != tctx->fo) - { - TMH_EXCHANGES_find_exchange_cancel (tctx->fo); - tctx->fo = NULL; - } - resume_track_transaction_with_response (tctx, - MHD_HTTP_SERVICE_UNAVAILABLE, - TALER_MHD_make_error ( - TALER_EC_PAY_EXCHANGE_TIMEOUT, - "exchange not reachable")); -} - - -/** - * Information about the wire transfer corresponding to - * a deposit operation. Note that it is in theory possible - * that we have a @a transaction_id and @a coin_pub in the - * result that do not match a deposit that we know about, - * for example because someone else deposited funds into - * our account. - * - * @param cls closure - * @param transaction_id ID of the contract - * @param coin_pub public key of the coin - * @param wtid identifier of the wire transfer in which the exchange - * send us the money for the coin deposit - * @param execution_time when was the wire transfer executed? - * @param exchange_proof proof from exchange about what the deposit was for - * NULL if we have not asked for this signature - */ -static void -transfer_cb (void *cls, - const struct GNUNET_HashCode *h_contract_terms, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_WireTransferIdentifierRawP *wtid, - struct GNUNET_TIME_Absolute execution_time, - const json_t *exchange_proof) -{ - struct TrackCoinContext *tcc = cls; - - if (0 != GNUNET_memcmp (coin_pub, - &tcc->coin_pub)) - return; - tcc->wtid = *wtid; - tcc->execution_time = execution_time; - tcc->have_wtid = GNUNET_YES; -} - - -/** - * Responsible to get the current coin wtid and store it into its state. - * - * @param cls closure - * @param transaction_id of the contract - * @param coin_pub public key of the coin - * @param exchange_url URL of exchange that issued @a coin_pub - * @param amount_with_fee amount the exchange will deposit for this coin - * @param deposit_fee fee the exchange will charge for this coin - * @param refund_fee fee the exchange will charge for refunding this coin - * @param exchange_proof proof from exchange that coin was accepted - */ -static void -coin_cb (void *cls, - const struct GNUNET_HashCode *h_contract_terms, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const char *exchange_url, - const struct TALER_Amount *amount_with_fee, - const struct TALER_Amount *deposit_fee, - const struct TALER_Amount *refund_fee, - const struct TALER_Amount *wire_fee, - const json_t *exchange_proof) -{ - struct TrackTransactionContext *tctx = cls; - struct TrackCoinContext *tcc; - - tcc = GNUNET_new (struct TrackCoinContext); - tcc->tctx = tctx; - tcc->coin_pub = *coin_pub; - tcc->exchange_url = GNUNET_strdup (exchange_url); - tcc->amount_with_fee = *amount_with_fee; - tcc->deposit_fee = *deposit_fee; - GNUNET_CONTAINER_DLL_insert (tctx->tcc_head, - tctx->tcc_tail, - tcc); - - /* find all those <coin, wtid> pairs associated to - this contract term's hash code. The callback - will then set the wtid for the "current coin" - context. */ - { - enum GNUNET_DB_QueryStatus qs; - - qs = db->find_transfers_by_hash (db->cls, - h_contract_terms, - &transfer_cb, - tcc); - if (0 > qs) - { - GNUNET_break (0); - tctx->qs = qs; - } - } -} - - -/** - * Find the exchange to trace the next coin(s). - * - * @param tctx operation context - */ -static void -find_exchange (struct TrackTransactionContext *tctx) -{ - struct TrackCoinContext *tcc = tctx->tcc_head; - - while ( (NULL != tcc) && - (GNUNET_YES == tcc->have_wtid) ) - tcc = tcc->next; - if (NULL != tcc) - { - tctx->current_exchange = tcc->exchange_url; - tctx->fo = TMH_EXCHANGES_find_exchange ( - tctx->current_exchange, - NULL, - GNUNET_NO, - &process_track_transaction_with_exchange, - tctx); - - } - else - { - generate_response (tctx); - } -} - - -/** - * Handle a "/track/transaction" request. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @param mi merchant backend instance, never NULL - * @return MHD result code - */ -MHD_RESULT -MH_handler_track_transaction (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size, - struct MerchantInstance *mi) -{ - struct TrackTransactionContext *tctx; - const char *order_id; - enum GNUNET_DB_QueryStatus qs; - struct json_t *contract_terms; - - if (NULL == *connection_cls) - { - tctx = GNUNET_new (struct TrackTransactionContext); - tctx->hc.cc = &track_transaction_cleanup; - tctx->connection = connection; - *connection_cls = tctx; - } - else - { - /* not first call, recover state */ - tctx = *connection_cls; - } - - if (0 != tctx->response_code) - { - MHD_RESULT ret; - - /* We are *done* processing the request, just queue the response (!) */ - if (UINT_MAX == tctx->response_code) - { - GNUNET_break (0); - return MHD_NO; /* hard error */ - } - ret = MHD_queue_response (connection, - tctx->response_code, - tctx->response); - if (NULL != tctx->response) - { - MHD_destroy_response (tctx->response); - tctx->response = NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Queueing response (%u) for /track/transaction (%s).\n", - (unsigned int) tctx->response_code, - ret ? "OK" : "FAILED"); - return ret; - } - if ( (NULL != tctx->fo) || - (NULL != tctx->eh) ) - { - /* likely old MHD version */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Not sure why we are here, should be suspended\n"); - return MHD_YES; /* still work in progress */ - } - order_id = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "order_id"); - if (NULL == order_id) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_PARAMETER_MISSING, - "order_id"); - - tctx->mi = mi; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Tracking on behalf of instance '%s'\n", - mi->id); - - - /* Map order id to contract terms; the objective is to get - the contract term's hashcode so as to retrieve all the - coins which have been deposited for it. */ - db->preflight (db->cls); - qs = db->find_contract_terms (db->cls, - &contract_terms, - order_id, - &tctx->mi->pubkey); - if (0 > qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_TRACK_TRANSACTION_DB_FETCH_TRANSACTION_ERROR, - "Database error finding contract terms"); - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND, - "Given order_id doesn't map to any proposal"); - - if (GNUNET_OK != - TALER_JSON_contract_hash (contract_terms, - &tctx->h_contract_terms)) - { - json_decref (contract_terms); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_INTERNAL_LOGIC_ERROR, - "Failed to hash contract terms"); - } - - { - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_absolute_time ("refund_deadline", - &tctx->refund_deadline), - TALER_JSON_spec_absolute_time ("timestamp", - &tctx->timestamp), - TALER_JSON_spec_amount ("amount", - &tctx->total_amount), - GNUNET_JSON_spec_fixed_auto ("h_wire", - &tctx->h_wire), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_YES != - GNUNET_JSON_parse (contract_terms, - spec, - NULL, NULL)) - { - GNUNET_break (0); - GNUNET_JSON_parse_free (spec); - json_decref (contract_terms); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_INTERNAL_LOGIC_ERROR, - "Failed to parse contract terms from DB"); - } - json_decref (contract_terms); - } - - tctx->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; - db->preflight (db->cls); - - /* Find coins which have been deposited for this contract, - and retrieve the wtid for each one. */ - qs = db->find_payments (db->cls, - &tctx->h_contract_terms, - &tctx->mi->pubkey, - &coin_cb, - tctx); - if ( (0 > qs) || - (0 > tctx->qs) ) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != tctx->qs); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_TRACK_TRANSACTION_DB_FETCH_PAYMENT_ERROR, - "Database error: failed to find payment data"); - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_TRACK_TRANSACTION_DB_NO_DEPOSITS_ERROR, - "deposit data not found"); - } - *connection_cls = tctx; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Suspending /track/transaction handling while working with the exchange\n"); - MHD_suspend_connection (connection); - tctx->timeout_task - = GNUNET_SCHEDULER_add_delayed (TRACK_TIMEOUT, - &handle_track_transaction_timeout, - tctx); - find_exchange (tctx); - return MHD_YES; -} - - -/* end of taler-merchant-httpd_track-transaction.c */ diff --git a/src/backend/taler-merchant-httpd_private-post-orders-ID-track-UNSPEC.h b/src/backend/taler-merchant-httpd_private-post-orders-ID-track-UNSPEC.h deleted file mode 100644 index 02b9a7b5..00000000 --- a/src/backend/taler-merchant-httpd_private-post-orders-ID-track-UNSPEC.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - This file is part of TALER - (C) 2014-2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-merchant-httpd_track-transaction.h - * @brief headers for /track/transaction handler - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#ifndef TALER_MERCHANT_HTTPD_TRACK_TRANSACTION_H -#define TALER_MERCHANT_HTTPD_TRACK_TRANSACTION_H -#include <microhttpd.h> -#include "taler-merchant-httpd.h" - -/** - * Handle a "/track/transaction" request. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @param mi merchant backend instance, never NULL - * @return MHD result code - */ -MHD_RESULT -MH_handler_track_transaction (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size, - struct MerchantInstance *mi); - - -#endif |