aboutsummaryrefslogtreecommitdiff
path: root/src/backend/taler-merchant-httpd_private-post-orders-ID-track-UNSPEC.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/taler-merchant-httpd_private-post-orders-ID-track-UNSPEC.c')
-rw-r--r--src/backend/taler-merchant-httpd_private-post-orders-ID-track-UNSPEC.c1250
1 files changed, 0 insertions, 1250 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 */