diff options
author | Christian Grothoff <christian@grothoff.org> | 2016-06-05 15:08:21 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2016-06-05 15:08:21 +0200 |
commit | a6734b3ebe3a721742a9f91cf55f78578e2111c5 (patch) | |
tree | cedf4652fc15983ce0a287519e3fc4a4af14dbd7 | |
parent | 148de70dd07591d40a5fa123dcbf4c3b1701cab2 (diff) | |
download | merchant-a6734b3ebe3a721742a9f91cf55f78578e2111c5.tar.gz merchant-a6734b3ebe3a721742a9f91cf55f78578e2111c5.tar.bz2 merchant-a6734b3ebe3a721742a9f91cf55f78578e2111c5.zip |
include exchange_uri with merchant_proofs
-rw-r--r-- | src/backend/taler-merchant-httpd_track-deposit.c | 316 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_track-transaction.c | 42 | ||||
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 49 | ||||
-rw-r--r-- | src/include/taler_merchantdb_plugin.h | 31 | ||||
-rw-r--r-- | src/lib/test_merchant_api.c | 10 |
5 files changed, 378 insertions, 70 deletions
diff --git a/src/backend/taler-merchant-httpd_track-deposit.c b/src/backend/taler-merchant-httpd_track-deposit.c index 970bd576..48b7e2eb 100644 --- a/src/backend/taler-merchant-httpd_track-deposit.c +++ b/src/backend/taler-merchant-httpd_track-deposit.c @@ -87,19 +87,123 @@ struct DepositTrackContext unsigned int response_code; /** - * + * Error message. */ - json_t *json; + const char *error; /** - * Error message. + * Response to return upon resume. */ - const char *error; + struct MHD_Response *response; + /** + * 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; + + /** + * Task run on timeout. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * URI of the exchange. + */ + char *uri; }; /** + * Free the @a rctx. + * + * @param rctx data to free + */ +static void +free_deposit_track_context (struct DepositTrackContext *rctx) +{ + if (NULL != rctx->fo) + { + TMH_EXCHANGES_find_exchange_cancel (rctx->fo); + rctx->fo = NULL; + } + if (NULL != rctx->timeout_task) + { + GNUNET_SCHEDULER_cancel (rctx->timeout_task); + rctx->timeout_task = NULL; + } + GNUNET_free (rctx); +} + + +/** + * Resume the given /track/deposit operation and send the given response. + * Stores the response in the @a rctx and signals MHD to resume + * the connection. Also ensures MHD runs immediately. + * + * @param rctx deposit tracking context + * @param response_code response code to use + * @param response response data to send back + */ +static void +resume_track_deposit_with_response (struct DepositTrackContext *rctx, + unsigned int response_code, + struct MHD_Response *response) +{ + rctx->response_code = response_code; + rctx->response = response; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resuming /track/transaction handling as exchange interaction is done (%u)\n", + response_code); + if (NULL != rctx->timeout_task) + { + GNUNET_SCHEDULER_cancel (rctx->timeout_task); + rctx->timeout_task = NULL; + } + MHD_resume_connection (rctx->connection); + TMH_trigger_daemon (); /* we resumed, kick MHD */ +} + + +/** + * Custom cleanup routine for a `struct DepositTrackContext`. + * + * @param hc the `struct DepositTrackContext` to clean up. + */ +static void +track_deposit_cleanup (struct TM_HandlerContext *hc) +{ + struct DepositTrackContext *rctx = (struct DepositTrackContext *) hc; + + free_deposit_track_context (rctx); +} + + +/** + * Function called with information about a coin that was deposited. + * Verify that it matches the information claimed by the exchange. + * + * @param cls closure FIXME! + * @param transaction_id of the contract + * @param coin_pub public key of the coin + * @param amount_with_fee amount the exchange will deposit for this coin + * @param deposit_fee fee the exchange will charge for this coin + * @param exchange_proof proof from exchange that coin was accepted + */ +static void +check_deposit (void *cls, + uint64_t transaction_id, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *amount_with_fee, + const struct TALER_Amount *deposit_fee, + const json_t *exchange_proof) +{ + GNUNET_break (0); // not implemented! +} + + +/** * Function called with detailed wire transfer data, including all * of the coin transactions that were combined into the wire transfer. * @@ -124,56 +228,136 @@ wire_deposit_cb (void *cls, const struct TALER_WireDepositDetails *details) { struct DepositTrackContext *rctx = cls; - // unsigned int i; + unsigned int i; + int ret; rctx->wdh = NULL; - if (NULL == total_amount) + if (MHD_HTTP_OK != http_status) { - rctx->error = "failed to obtain /wire/deposit response from exchange"; - rctx->json = json_incref ((json_t *) json); - rctx->response_code = http_status; - MHD_resume_connection (rctx->connection); + resume_track_deposit_with_response + (rctx, + MHD_HTTP_FAILED_DEPENDENCY, + TMH_RESPONSE_make_json_pack ("{s:I, s:O}", + "exchange_status", (json_int_t) http_status, + "details", json)); return; } + + if (GNUNET_OK != + db->store_transfer_to_proof (db->cls, + rctx->uri, + &rctx->wtid, + json)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to persist wire transfer proof in DB\n"); + } + rctx->details_length = details_length; rctx->details = GNUNET_new_array (details_length, struct TALER_WireDepositDetails); memcpy (rctx->details, details, details_length * sizeof (struct TALER_WireDepositDetails)); - GNUNET_break (0); - /* FIXME: now check that these details match what we have in - our database... */ + for (i=0;i<details_length;i++) + { + ret = db->find_payments_by_id (rctx, + details[i].transaction_id, + &check_deposit, + rctx); + if (GNUNET_SYSERR == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to verify existing payment data in DB\n"); + } + if (GNUNET_NO == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to find payment data in DB\n"); + } + /* FIXME: check result of "check_deposit"! */ + ret = db->store_coin_to_transfer (db->cls, + details[i].transaction_id, + &details[i].coin_pub, + &rctx->wtid); + if (GNUNET_OK != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to persist coin to wire transfer mapping in DB\n"); + } + } + /* FIXME: might want a more custom response here... */ + resume_track_deposit_with_response + (rctx, + MHD_HTTP_OK, + TMH_RESPONSE_make_json_pack ("{s:I, s:O}", + "exchange_status", (json_int_t) http_status, + "details", json)); +} +/** + * Function called with the result of our exchange lookup. + * + * @param cls the `struct DepositTrackContext` + * @param eh NULL if exchange was not found to be acceptable + * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config + */ +static void +process_track_deposit_with_exchange (void *cls, + struct TALER_EXCHANGE_Handle *eh, + int exchange_trusted) +{ + struct DepositTrackContext *rctx = cls; + + rctx->fo = NULL; + rctx->eh = eh; + rctx->wdh = TALER_EXCHANGE_wire_deposits (eh, + &rctx->wtid, + &wire_deposit_cb, + rctx); } /** - * Function called with information about who is auditing - * a particular exchange and what key the exchange is using. + * Handle a timeout for the processing of the track deposit request. * * @param cls closure - * @param keys information about the various keys used - * by the exchange, NULL if /keys failed */ static void -cert_cb (void *cls, - const struct TALER_EXCHANGE_Keys *keys) +handle_track_deposit_timeout (void *cls) { struct DepositTrackContext *rctx = cls; - if (NULL == keys) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resuming /track/deposit with error after timeout\n"); + rctx->timeout_task = NULL; + + if (NULL != rctx->fo) { - rctx->error = "failed to obtain /keys from exchange"; - rctx->response_code = MHD_HTTP_SERVICE_UNAVAILABLE; - MHD_resume_connection (rctx->connection); - return; + TMH_EXCHANGES_find_exchange_cancel (rctx->fo); + rctx->fo = NULL; } - rctx->wdh = TALER_EXCHANGE_wire_deposits (rctx->eh, - &rctx->wtid, - &wire_deposit_cb, - rctx); + resume_track_deposit_with_response (rctx, + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_internal_error ("exchange not reachable")); +} + + +/** + * Function called with information about a wire transfer identifier. + * Generate a response based on the given @a proof. + * + * @param cls closure + * @param proof proof from exchange about what the wire transfer was for + */ +static void +proof_cb (void *cls, + const json_t *proof) +{ + struct DepositTrackContext *rctx = cls; + + GNUNET_break (0); // not implemented! } @@ -199,18 +383,52 @@ MH_handler_track_deposit (struct TMH_RequestHandler *rh, struct DepositTrackContext *rctx; const char *str; const char *uri; + int ret; - rctx = *connection_cls; - if (NULL != rctx) + if (NULL == *connection_cls) { - // ... + rctx = GNUNET_new (struct DepositTrackContext); + rctx->hc.cc = &track_deposit_cleanup; + rctx->connection = connection; + *connection_cls = rctx; } + else + { + /* not first call, recover state */ + rctx = *connection_cls; + } + + if (0 != rctx->response_code) + { + /* We are *done* processing the request, just queue the response (!) */ + if (UINT_MAX == rctx->response_code) + { + GNUNET_break (0); + return MHD_NO; /* hard error */ + } + ret = MHD_queue_response (connection, + rctx->response_code, + rctx->response); + if (NULL != rctx->response) + { + MHD_destroy_response (rctx->response); + rctx->response = NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Queueing response (%u) for /track/deposit (%s).\n", + (unsigned int) rctx->response_code, + ret ? "OK" : "FAILED"); + return ret; + } + uri = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "exchange"); if (NULL == uri) return TMH_RESPONSE_reply_external_error (connection, "exchange argument missing"); + rctx->uri = GNUNET_strdup (uri); + str = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "wtid"); @@ -222,15 +440,39 @@ MH_handler_track_deposit (struct TMH_RequestHandler *rh, strlen (str), &rctx->wtid, sizeof (rctx->wtid))) + { return TMH_RESPONSE_reply_external_error (connection, "wtid argument malformed"); - rctx->eh = TALER_EXCHANGE_connect (NULL /* FIXME */, - uri, - &cert_cb, - rctx, - TALER_EXCHANGE_OPTION_END); + } + + /* FIXME: check if reply is already in database! */ + ret = db->find_proof_by_wtid (db->cls, + rctx->uri, + &rctx->wtid, + &proof_cb, + rctx); + if (0 != rctx->response_code) + { + ret = MHD_queue_response (connection, + rctx->response_code, + rctx->response); + if (NULL != rctx->response) + { + MHD_destroy_response (rctx->response); + rctx->response = NULL; + } + return ret; + } - GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Suspending /track/deposit handling while working with the exchange\n"); + MHD_suspend_connection (connection); + rctx->fo = TMH_EXCHANGES_find_exchange (uri, + &process_track_deposit_with_exchange, + rctx); + rctx->timeout_task = GNUNET_SCHEDULER_add_delayed (TRACK_TIMEOUT, + &handle_track_deposit_timeout, + rctx); return MHD_NO; } diff --git a/src/backend/taler-merchant-httpd_track-transaction.c b/src/backend/taler-merchant-httpd_track-transaction.c index 96c677dd..6744840f 100644 --- a/src/backend/taler-merchant-httpd_track-transaction.c +++ b/src/backend/taler-merchant-httpd_track-transaction.c @@ -253,11 +253,11 @@ track_transaction_cleanup (struct TM_HandlerContext *hc) /** - * Resume the given pay context and send the given response. - * Stores the response in the @a pc and signals MHD to resume - * the connection. Also ensures MHD runs immediately. + * 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 pc payment context + * @param tctx transaction tracking context * @param response_code response code to use * @param response response data to send back */ @@ -329,13 +329,14 @@ wire_deposits_cb (void *cls, resume_track_transaction_with_response (tctx, MHD_HTTP_FAILED_DEPENDENCY, - TMH_RESPONSE_make_json_pack ("{s:I, s:o}", + TMH_RESPONSE_make_json_pack ("{s:I, s:O}", "exchange_status", (json_int_t) http_status, "details", json)); return; } if (GNUNET_OK != db->store_transfer_to_proof (db->cls, + tctx->exchange_uri, &tctx->current_wtid, json)) { @@ -466,7 +467,7 @@ process_track_transaction_with_exchange (void *cls, /** - * Handle a timeout for the processing of the pay request. + * Handle a timeout for the processing of the track transaction request. * * @param cls closure */ @@ -576,18 +577,6 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, const char *str; int ret; - str = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "id"); - if (NULL == str) - return TMH_RESPONSE_reply_external_error (connection, - "id argument missing"); - if (1 != - sscanf (str, - "%llu", - &transaction_id)) - return TMH_RESPONSE_reply_external_error (connection, - "id argument must be a number"); if (NULL == *connection_cls) { tctx = GNUNET_new (struct TrackTransactionContext); @@ -600,6 +589,7 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, /* not first call, recover state */ tctx = *connection_cls; } + if (0 != tctx->response_code) { /* We are *done* processing the request, just queue the response (!) */ @@ -623,6 +613,19 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, return ret; } + str = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "id"); + if (NULL == str) + return TMH_RESPONSE_reply_external_error (connection, + "id argument missing"); + if (1 != + sscanf (str, + "%llu", + &transaction_id)) + return TMH_RESPONSE_reply_external_error (connection, + "id argument must be a number"); + ret = db->find_transaction_by_id (db->cls, transaction_id, &transaction_cb, @@ -631,7 +634,6 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, { /* FIXME: generate proper 404 */ GNUNET_break (0); - free_tctx (tctx); return TMH_RESPONSE_reply_external_error (connection, "Unknown transaction ID"); } @@ -640,7 +642,6 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, (NULL == tctx->exchange_uri) ) { GNUNET_break (0); - free_tctx (tctx); return TMH_RESPONSE_reply_internal_error (connection, "Database error"); } @@ -658,7 +659,6 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, { /* FIXME: generate proper 404 */ GNUNET_break (0); - free_tctx (tctx); return TMH_RESPONSE_reply_external_error (connection, "No deposits found for transaction ID"); } diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index bfd0c55f..be986532 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -180,8 +180,11 @@ postgres_initialize (void *cls) ");"); PG_EXEC (pg, "CREATE TABLE IF NOT EXISTS merchant_proofs (" - " wtid BYTEA PRIMARY KEY CHECK (LENGTH(wtid)=32)" - ",proof BYTEA NOT NULL);"); + " exchange_uri VARCHAR NOT NULL" + ",wtid BYTEA CHECK (LENGTH(wtid)=32)" + ",proof BYTEA NOT NULL" + " PRIMARY KEY(wtid,exchange_uri)" + ");"); /* Note that transaction_id + coin_pub may actually be unknown to us, e.g. someone else deposits something for us at the exchange. Hence those cannot be foreign keys into deposits/transactions! */ @@ -193,11 +196,11 @@ postgres_initialize (void *cls) ",PRIMARY KEY (transaction_id, coin_pub)" ");"); PG_EXEC_INDEX (pg, - "CREATE INDEX IF NOT EXISTS transfers_by_coin " - " ON transfers (transaction_id, coin_pub)"); + "CREATE INDEX IF NOT EXISTS merchant_transfers_by_coin " + " ON merchant_transfers (transaction_id, coin_pub)"); PG_EXEC_INDEX (pg, - "CREATE INDEX IF NOT EXISTS transfers_by_wtid " - " ON transfers (wtid)"); + "CREATE INDEX IF NOT EXISTS merchant_transfers_by_wtid " + " ON merchant_transfers (wtid)"); /* Setup prepared "INSERT" statements */ PG_PREPARE (pg, @@ -240,10 +243,11 @@ postgres_initialize (void *cls) PG_PREPARE (pg, "insert_proof", "INSERT INTO merchant_proofs" - "(wtid" + "(exchange_uri" + ",wtid" ",proof) VALUES " - "($1, $2)", - 2); + "($1, $2, $3)", + 3); /* Setup prepared "SELECT" statements */ PG_PREPARE (pg, @@ -460,12 +464,14 @@ postgres_store_coin_to_transfer (void *cls, * Insert wire transfer confirmation from the exchange into the database. * * @param cls closure + * @param exchange_uri URI of the exchange * @param wtid identifier of the wire transfer * @param exchange_proof proof from exchange about what the deposit was for * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error */ static int postgres_store_transfer_to_proof (void *cls, + const char *exchange_uri, const struct TALER_WireTransferIdentifierRawP *wtid, const json_t *exchange_proof) { @@ -474,6 +480,7 @@ postgres_store_transfer_to_proof (void *cls, int ret; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (exchange_uri), GNUNET_PQ_query_param_auto_from_type (wtid), TALER_PQ_query_param_json (exchange_proof), GNUNET_PQ_query_param_end @@ -833,6 +840,29 @@ postgres_find_deposits_by_wtid (void *cls, /** + * Lookup proof information about a wire transfer. + * + * @param cls closure + * @param merchant_uri from which merchant are we looking for proof + * @param wtid wire transfer identifier for the search + * @param cb function to call with proof data + * @param cb_cls closure for @a cb + * @return #GNUNET_OK on success, #GNUNET_NO if transaction Id is unknown, + * #GNUNET_SYSERR on hard errors + */ +static int +postgres_find_proof_by_wtid (void *cls, + const char *merchant_uri, + const struct TALER_WireTransferIdentifierRawP *wtid, + TALER_MERCHANTDB_ProofCallback cb, + void *cb_cls) +{ + GNUNET_break (0); + return GNUNET_SYSERR; +} + + +/** * Initialize Postgres database subsystem. * * @param cls a configuration instance @@ -881,6 +911,7 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) plugin->find_payments_by_id = &postgres_find_payments_by_id; plugin->find_transfers_by_id = &postgres_find_transfers_by_id; plugin->find_deposits_by_wtid = &postgres_find_deposits_by_wtid; + plugin->find_proof_by_wtid = &postgres_find_proof_by_wtid; return plugin; } diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index 1b3013dd..8ce021a1 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -98,6 +98,17 @@ typedef void /** + * Function called with information about a wire transfer identifier. + * + * @param cls closure + * @param proof proof from exchange about what the wire transfer was for + */ +typedef void +(*TALER_MERCHANTDB_ProofCallback)(void *cls, + const json_t *proof); + + +/** * Handle to interact with the database. */ struct TALER_MERCHANTDB_Plugin @@ -199,12 +210,14 @@ struct TALER_MERCHANTDB_Plugin * Insert wire transfer confirmation from the exchange into the database. * * @param cls closure + * @param exchange_uri from which exchange did we get the @a exchange_proof * @param wtid identifier of the wire transfer * @param exchange_proof proof from exchange about what the deposit was for * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error */ int (*store_transfer_to_proof) (void *cls, + const char *exchange_uri, const struct TALER_WireTransferIdentifierRawP *wtid, const json_t *exchange_proof); @@ -280,6 +293,24 @@ struct TALER_MERCHANTDB_Plugin TALER_MERCHANTDB_CoinDepositCallback cb, void *cb_cls); + + /** + * Lookup proof information about a wire transfer. + * + * @param cls closure + * @param exchange_uri from which exchange are we looking for proof + * @param wtid wire transfer identifier for the search + * @param cb function to call with proof data + * @param cb_cls closure for @a cb + * @return #GNUNET_OK on success, #GNUNET_NO if transaction Id is unknown, + * #GNUNET_SYSERR on hard errors + */ + int + (*find_proof_by_wtid) (void *cls, + const char *exchange_uri, + const struct TALER_WireTransferIdentifierRawP *wtid, + TALER_MERCHANTDB_ProofCallback cb, + void *cb_cls); }; diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c index a6dca8bc..720480f3 100644 --- a/src/lib/test_merchant_api.c +++ b/src/lib/test_merchant_api.c @@ -109,7 +109,7 @@ enum OpCode /** * Retrieve deposit permissions for a given wire transfer */ - OC_TRACK_DEPOSIT + OC_TRACK_DEPOSIT }; @@ -1162,7 +1162,7 @@ interpreter_run (void *cls) /* get amount */ ref = find_command (is, cmd->details.pay.contract_ref); - GNUNET_assert (NULL != ref); + GNUNET_assert (NULL != ref); merchant_sig = ref->details.contract.merchant_sig; GNUNET_assert (NULL != ref->details.contract.contract); { @@ -1265,12 +1265,16 @@ interpreter_run (void *cls) } return; case OC_TRACK_DEPOSIT: + GNUNET_break (0); + fail (is); +#if 0 TALER_MERCHANT_track_deposit (ctx, MERCHANT_URI "track/deposit", cmd->details.track_deposit.wtid, EXCHANGE_URI, - track_deposit_cb, + &track_deposit_cb, is); +#endif return; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |