diff options
author | Christian Grothoff <christian@grothoff.org> | 2023-05-03 23:13:51 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2023-05-03 23:13:51 +0200 |
commit | 8535f1e81939e87258ad9d2503ad25df9cfb021d (patch) | |
tree | 6a01388ace0fcd23f57eb3bf19ea7d756b8401b7 | |
parent | 63960574154ca1615fb095a71b958ec5d00df4ae (diff) |
simplify POST /private/transfers implementation to match new situation with taler-merchant-exchange (incomplete)
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 1 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-transfers.c | 1368 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-transfers.h | 9 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-webhooks.c | 5 | ||||
-rw-r--r-- | src/backenddb/Makefile.am | 1 | ||||
-rw-r--r-- | src/backenddb/merchant-0001.sql | 6 | ||||
-rw-r--r-- | src/backenddb/pg_set_transfer_status_to_confirmed.c | 66 | ||||
-rw-r--r-- | src/backenddb/pg_set_transfer_status_to_confirmed.h | 48 | ||||
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 46 | ||||
-rw-r--r-- | src/backenddb/test_merchantdb.c | 30 | ||||
-rw-r--r-- | src/include/taler_merchant_service.h | 57 | ||||
-rw-r--r-- | src/include/taler_merchantdb_plugin.h | 10 | ||||
-rw-r--r-- | src/lib/merchant_api_post_transfers.c | 91 | ||||
-rw-r--r-- | src/testing/test_kyc_api.c | 4 | ||||
-rw-r--r-- | src/testing/test_merchant_api.c | 6 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_get_transfers.c | 23 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_post_transfers.c | 151 |
17 files changed, 242 insertions, 1680 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index a676a009..15f67d04 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -289,7 +289,6 @@ do_shutdown (void *cls) TMH_force_kyc_resume (); TMH_force_rc_resume (); TMH_force_gorc_resume (); - TMH_force_post_transfers_resume (); TMH_force_tip_pickup_resume (); TMH_force_wallet_get_order_resume (); TMH_force_wallet_refund_order_resume (); diff --git a/src/backend/taler-merchant-httpd_private-post-transfers.c b/src/backend/taler-merchant-httpd_private-post-transfers.c index af1ca844..2d99f35e 100644 --- a/src/backend/taler-merchant-httpd_private-post-transfers.c +++ b/src/backend/taler-merchant-httpd_private-post-transfers.c @@ -28,951 +28,53 @@ #include "taler-merchant-httpd_helper.h" #include "taler-merchant-httpd_private-post-transfers.h" -/** - * How long to wait before giving up processing with the exchange? - */ -#define TRANSFER_GENERIC_TIMEOUT (GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_SECONDS, \ - 15)) /** * How often do we retry the simple INSERT database transaction? */ #define MAX_RETRIES 3 -/** - * Context used for handing POST /private/transfers requests. - */ -struct PostTransfersContext -{ - - /** - * Kept in a DLL. - */ - struct PostTransfersContext *next; - - /** - * Kept in a DLL. - */ - struct PostTransfersContext *prev; - - /** - * Argument for the /wire/transfers request. - */ - struct TALER_WireTransferIdentifierRawP wtid; - - /** - * Amount of the wire transfer. - */ - struct TALER_Amount amount; - - /** - * URL of the exchange. - */ - const char *exchange_url; - - /** - * payto:// URI used for the transfer. - */ - const char *payto_uri; - - /** - * Master public key of the exchange at @e exchange_url. - */ - struct TALER_MasterPublicKeyP master_pub; - - /** - * Handle for the /wire/transfers request. - */ - struct TALER_EXCHANGE_TransfersGetHandle *wdh; - - /** - * For which merchant instance is this tracking request? - */ - struct TMH_HandlerContext *hc; - - /** - * HTTP connection we are handling. - */ - struct MHD_Connection *connection; - - /** - * Response to return upon resume. - */ - 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; - - /** - * Pointer to the detail that we are currently - * checking in #check_transfer(). - */ - const struct TALER_TrackTransferDetails *current_detail; - - /** - * Which transaction detail are we currently looking at? - */ - unsigned int current_offset; - - /** - * Response code to return. - */ - unsigned int response_code; - - /** - * #GNUNET_NO if we did not find a matching coin. - * #GNUNET_SYSERR if we found a matching coin, but the amounts do not match. - * #GNUNET_OK if we did find a matching coin. - */ - enum GNUNET_GenericReturnValue check_transfer_result; - - /** - * Did we suspend @a connection and are thus in - * the #ptc_head DLL (#GNUNET_YES). Set to - * #GNUNET_NO if we are not suspended, and to - * #GNUNET_SYSERR if we should close the connection - * without a response due to shutdown. - */ - enum GNUNET_GenericReturnValue suspended; - - /** - * Should we retry the transaction due to a serialization error? - */ - bool soft_retry; - - /** - * Did we just download the exchange reply? - */ - bool downloaded; - -}; - - -/** - * Head of list of suspended requests. - */ -static struct PostTransfersContext *ptc_head; - -/** - * Tail of list of suspended requests. - */ -static struct PostTransfersContext *ptc_tail; - - -void -TMH_force_post_transfers_resume () -{ - struct PostTransfersContext *ptc; - - while (NULL != (ptc = ptc_head)) - { - GNUNET_CONTAINER_DLL_remove (ptc_head, - ptc_tail, - ptc); - ptc->suspended = GNUNET_SYSERR; - MHD_resume_connection (ptc->connection); - if (NULL != ptc->timeout_task) - { - GNUNET_SCHEDULER_cancel (ptc->timeout_task); - ptc->timeout_task = NULL; - } - } -} - - -/** - * Resume the given /track/transfer operation and send the given response. - * Stores the response in the @a ptc and signals MHD to resume - * the connection. Also ensures MHD runs immediately. - * - * @param ptc transfer tracking context - * @param response_code response code to use - * @param response response data to send back - */ -static void -resume_transfer_with_response (struct PostTransfersContext *ptc, - unsigned int response_code, - struct MHD_Response *response) -{ - ptc->response_code = response_code; - ptc->response = response; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resuming POST /transfers handling as exchange interaction is done (%u)\n", - response_code); - if (NULL != ptc->timeout_task) - { - GNUNET_SCHEDULER_cancel (ptc->timeout_task); - ptc->timeout_task = NULL; - } - GNUNET_CONTAINER_DLL_remove (ptc_head, - ptc_tail, - ptc); - ptc->suspended = GNUNET_NO; - MHD_resume_connection (ptc->connection); - TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ -} - - -/** - * Resume the given POST /transfers operation with an error. - * - * @param ptc transfer tracking context - * @param response_code response code to use - * @param ec error code to use - * @param hint hint text to provide - */ -static void -resume_transfer_with_error (struct PostTransfersContext *ptc, - unsigned int response_code, - enum TALER_ErrorCode ec, - const char *hint) -{ - resume_transfer_with_response (ptc, - response_code, - TALER_MHD_make_error (ec, - hint)); -} - - -/** - * Custom cleanup routine for a `struct PostTransfersContext`. - * - * @param cls the `struct PostTransfersContext` to clean up. - */ -static void -transfer_cleanup (void *cls) -{ - struct PostTransfersContext *ptc = cls; - - if (NULL != ptc->fo) - { - TMH_EXCHANGES_find_exchange_cancel (ptc->fo); - ptc->fo = NULL; - } - if (NULL != ptc->timeout_task) - { - GNUNET_SCHEDULER_cancel (ptc->timeout_task); - ptc->timeout_task = NULL; - } - if (NULL != ptc->wdh) - { - TALER_EXCHANGE_transfers_get_cancel (ptc->wdh); - ptc->wdh = NULL; - } - if (NULL != ptc->response) - { - MHD_destroy_response (ptc->response); - ptc->response = NULL; - } - GNUNET_free (ptc); -} - - -/** - * This function checks that the information about the coin which - * was paid back by _this_ wire transfer matches what _we_ (the merchant) - * knew about this coin. - * - * @param cls closure with our `struct PostTransfersContext *` - * @param exchange_url URL of the exchange that issued @a coin_pub - * @param amount_with_fee amount the exchange will transfer 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 wire_fee paid wire fee - * @param h_wire hash of merchant's wire details - * @param deposit_timestamp when did the exchange receive the deposit - * @param refund_deadline until when are refunds allowed - * @param exchange_sig signature by the exchange - * @param exchange_pub exchange signing key used for @a exchange_sig - */ -static void -check_transfer (void *cls, - 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 struct TALER_MerchantWireHashP *h_wire, - struct GNUNET_TIME_Timestamp deposit_timestamp, - struct GNUNET_TIME_Timestamp refund_deadline, - const struct TALER_ExchangeSignatureP *exchange_sig, - const struct TALER_ExchangePublicKeyP *exchange_pub) -{ - struct PostTransfersContext *ptc = cls; - const struct TALER_TrackTransferDetails *ttd = ptc->current_detail; - - if (GNUNET_SYSERR == ptc->check_transfer_result) - return; /* already had a serious issue; odd that we're called more than once as well... */ - if ( (0 != TALER_amount_cmp (amount_with_fee, - &ttd->coin_value)) || - (0 != TALER_amount_cmp (deposit_fee, - &ttd->coin_fee)) ) - { - /* Disagreement between the exchange and us about how much this - coin is worth! */ - GNUNET_break_op (0); - ptc->check_transfer_result = GNUNET_SYSERR; - /* Build the `TrackTransferConflictDetails` */ - ptc->response_code = MHD_HTTP_ACCEPTED; - ptc->response - = TALER_MHD_MAKE_JSON_PACK ( - TALER_JSON_pack_ec ( - TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_REPORTS), - GNUNET_JSON_pack_string ("exchange_url", - exchange_url), - GNUNET_JSON_pack_timestamp ("deposit_timestamp", - deposit_timestamp), - GNUNET_JSON_pack_timestamp ("refund_deadline", - refund_deadline), - GNUNET_JSON_pack_uint64 ("conflict_offset", - ptc->current_offset), - GNUNET_JSON_pack_data_auto ("coin_pub", - &ttd->coin_pub), - GNUNET_JSON_pack_data_auto ("h_wire", - h_wire), - GNUNET_JSON_pack_data_auto ("deposit_exchange_sig", - exchange_sig), - GNUNET_JSON_pack_data_auto ("deposit_exchange_pub", - exchange_pub), - GNUNET_JSON_pack_data_auto ("h_contract_terms", - &ttd->h_contract_terms), - TALER_JSON_pack_amount ("amount_with_fee", - amount_with_fee), - TALER_JSON_pack_amount ("coin_value", - &ttd->coin_value), - TALER_JSON_pack_amount ("coin_fee", - &ttd->coin_fee), - TALER_JSON_pack_amount ("deposit_fee", - deposit_fee)); - return; - } - ptc->check_transfer_result = GNUNET_OK; -} - - -/** - * Check that the given @a wire_fee is what the @a exchange_pub should charge - * at the @a execution_time. If the fee is correct (according to our - * database), return #GNUNET_OK. If we do not have the fee structure in our - * DB, we just accept it and return #GNUNET_NO; if we have proof that the fee - * is bogus, we respond with the proof to the client and return - * #GNUNET_SYSERR. - * - * @param ptc context of the transfer to respond to - * @param execution_time time of the wire transfer - * @param wire_fee fee claimed by the exchange - * @return #GNUNET_SYSERR if we returned hard proof of - * missbehavior from the exchange to the client - */ -static enum GNUNET_GenericReturnValue -check_wire_fee (struct PostTransfersContext *ptc, - struct GNUNET_TIME_Timestamp execution_time, - const struct TALER_Amount *wire_fee) -{ - struct TALER_WireFeeSet fees; - struct TALER_MasterSignatureP master_sig; - struct GNUNET_TIME_Timestamp start_date; - struct GNUNET_TIME_Timestamp end_date; - enum GNUNET_DB_QueryStatus qs; - char *wire_method; - - wire_method = TALER_payto_get_method (ptc->payto_uri); - qs = TMH_db->lookup_wire_fee (TMH_db->cls, - &ptc->master_pub, - wire_method, - execution_time, - &fees, - &start_date, - &end_date, - &master_sig); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - ptc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - ptc->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED, - "lookup_wire_fee"); - return GNUNET_SYSERR; - case GNUNET_DB_STATUS_SOFT_ERROR: - ptc->soft_retry = true; - return GNUNET_NO; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to find wire fee for `%s' and method `%s' at %s in DB, accepting blindly that the fee is %s\n", - TALER_B2S (&ptc->master_pub), - wire_method, - GNUNET_TIME_timestamp2s (execution_time), - TALER_amount2s (wire_fee)); - GNUNET_free (wire_method); - return GNUNET_NO; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - if (0 <= TALER_amount_cmp (&fees.wire, - wire_fee)) - { - GNUNET_free (wire_method); - return GNUNET_OK; /* expected_fee >= wire_fee */ - } - /* Wire fee check failed, export proof to client */ - ptc->response_code = MHD_HTTP_ACCEPTED; - ptc->response = - TALER_MHD_MAKE_JSON_PACK ( - TALER_JSON_pack_ec ( - TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_BAD_WIRE_FEE), - TALER_JSON_pack_amount ("wire_fee", - wire_fee), - GNUNET_JSON_pack_timestamp ("execution_time", - execution_time), - TALER_JSON_pack_amount ("expected_wire_fee", - &fees.wire), - TALER_JSON_pack_amount ("expected_closing_fee", - &fees.closing), - GNUNET_JSON_pack_timestamp ("start_date", - start_date), - GNUNET_JSON_pack_timestamp ("end_date", - end_date), - GNUNET_JSON_pack_data_auto ("master_sig", - &master_sig), - GNUNET_JSON_pack_data_auto ("master_pub", - &ptc->master_pub)); - GNUNET_free (wire_method); - return GNUNET_SYSERR; -} - - -/** - * Function called with detailed wire transfer data, including all - * of the coin transactions that were combined into the wire transfer. - * - * @param cls closure - * @param tgr response details - */ -static void -wire_transfer_cb (void *cls, - const struct TALER_EXCHANGE_TransfersGetResponse *tgr) -{ - struct PostTransfersContext *ptc = cls; - const struct TALER_EXCHANGE_HttpResponse *hr = &tgr->hr; - const char *instance_id = ptc->hc->instance->settings.id; - enum GNUNET_DB_QueryStatus qs; - - ptc->wdh = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Got response code %u from exchange for GET /transfers/$WTID\n", - hr->http_status); - switch (hr->http_status) - { - case MHD_HTTP_OK: - break; - case MHD_HTTP_NOT_FOUND: - resume_transfer_with_response ( - ptc, - MHD_HTTP_BAD_GATEWAY, - TALER_MHD_MAKE_JSON_PACK ( - TALER_JSON_pack_ec ( - TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_EXCHANGE_UNKNOWN), - TMH_pack_exchange_reply (hr))); - return; - default: - resume_transfer_with_response ( - ptc, - MHD_HTTP_BAD_GATEWAY, - TALER_MHD_MAKE_JSON_PACK ( - TALER_JSON_pack_ec ( - TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS), - TMH_pack_exchange_reply (hr))); - return; - } - TMH_db->preflight (TMH_db->cls); - /* Ok, exchange answer is acceptable, store it */ - qs = TMH_db->insert_transfer_details (TMH_db->cls, - instance_id, - ptc->exchange_url, - ptc->payto_uri, - &ptc->wtid, - &tgr->details.ok.td); - if (0 > qs) - { - /* Always report on DB error as well to enable diagnostics */ - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - resume_transfer_with_error ( - ptc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - (GNUNET_DB_STATUS_HARD_ERROR == qs) - ? TALER_EC_GENERIC_DB_COMMIT_FAILED - : TALER_EC_GENERIC_DB_SOFT_FAILURE, - NULL); - return; - } - if (0 == qs) - { - GNUNET_break (0); - resume_transfer_with_error ( - ptc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert-transfer-details"); - return; - } - if (0 != - TALER_amount_cmp (&tgr->details.ok.td.total_amount, - &ptc->amount)) - { - resume_transfer_with_error ( - ptc, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_TRANSFERS, - NULL); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Transfer details inserted, resuming request...\n"); - /* resume processing, main function will build the response */ - resume_transfer_with_response (ptc, - 0, - NULL); -} - - -/** - * Function called with the result of our exchange lookup. - * - * @param cls the `struct PostTransfersContext` - * @param hr HTTP response details - * @param eh NULL if exchange was not found to be acceptable - * @param ih internal handle to the exchange - */ -static void -process_transfer_with_exchange (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - struct TALER_EXCHANGE_Handle *eh, - struct TMH_Exchange *ih) -{ - struct PostTransfersContext *ptc = cls; - - (void) ih; - ptc->fo = NULL; - if (NULL == hr) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Exchange failed to respond!\n"); - resume_transfer_with_response ( - ptc, - MHD_HTTP_GATEWAY_TIMEOUT, - TALER_MHD_MAKE_JSON_PACK ( - TALER_JSON_pack_ec (TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT))); - return; - } - if (NULL == eh) - { - /* The request failed somehow */ - GNUNET_break_op (0); - resume_transfer_with_response ( - ptc, - MHD_HTTP_BAD_GATEWAY, - TALER_MHD_MAKE_JSON_PACK ( - TALER_JSON_pack_ec ( - TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE), - TMH_pack_exchange_reply (hr))); - return; - } - - /* keep master key for later */ - { - const struct TALER_EXCHANGE_Keys *keys; - - keys = TALER_EXCHANGE_get_keys (eh); - if (NULL == keys) - { - GNUNET_break (0); - resume_transfer_with_error (ptc, - MHD_HTTP_BAD_GATEWAY, - TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE, - NULL); - return; - } - ptc->master_pub = keys->master_pub; - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Requesting transfer details from exchange\n"); - ptc->wdh = TALER_EXCHANGE_transfers_get (eh, - &ptc->wtid, - &wire_transfer_cb, - ptc); - if (NULL == ptc->wdh) - { - GNUNET_break (0); - resume_transfer_with_error (ptc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_REQUEST_ERROR, - "failed to run GET /transfers/ on exchange"); - } -} - - -/** - * Now we want to double-check that any (Taler coin) deposit which is - * accounted into _this_ wire transfer, does exist into _our_ database. This - * is the rationale: if the exchange paid us for it, we must have received it - * _beforehands_! - * - * @param cls a `struct PostTransfersContext` - * @param current_offset at which offset in the exchange's reply are the @a ttd - * @param ttd details about an aggregated transfer (to check) - */ -static void -verify_exchange_claim_cb (void *cls, - unsigned int current_offset, - const struct TALER_TrackTransferDetails *ttd) -{ - struct PostTransfersContext *ptc = cls; - enum GNUNET_DB_QueryStatus qs; - - if (0 != ptc->response_code) - return; /* already encountered an error */ - if (ptc->soft_retry) - return; /* already encountered an error */ - ptc->current_offset = current_offset; - ptc->current_detail = ttd; - /* Set the coin as "never seen" before. */ - ptc->check_transfer_result = GNUNET_NO; - qs = TMH_db->lookup_deposits_by_contract_and_coin ( - TMH_db->cls, - ptc->hc->instance->settings.id, - &ttd->h_contract_terms, - &ttd->coin_pub, - &check_transfer, - ptc); - switch (qs) - { - case GNUNET_DB_STATUS_SOFT_ERROR: - ptc->soft_retry = true; - return; - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - ptc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - ptc->response - = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED, - "deposit by contract and coin"); - return; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - /* The exchange says we made this deposit, but WE do not - recall making it (corrupted / unreliable database?)! - Well, let's say thanks and accept the money! */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to find payment data in DB\n"); - ptc->check_transfer_result = GNUNET_OK; - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - switch (ptc->check_transfer_result) - { - case GNUNET_NO: - /* Internal error: how can we have called #check_transfer() - but still have no result? */ - GNUNET_break (0); - ptc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - ptc->response = - TALER_MHD_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "check_transfer_result must not be GNUNET_NO"); - return; - case GNUNET_SYSERR: - /* #check_transfer() failed, report conflict! */ - GNUNET_break_op (0); - GNUNET_assert (NULL != ptc->response); - return; - case GNUNET_OK: - break; - } -} - - -/** - * Represents an entry in the table used to sum up - * individual deposits for each h_contract_terms/order_id - * (as the exchange gives us per coin, and we return - * per order). - */ -struct Entry -{ - - /** - * Order of the entry. - */ - char *order_id; - - /** - * Sum accumulator for deposited value. - */ - struct TALER_Amount deposit_value; - - /** - * Sum accumulator for deposit fee. - */ - struct TALER_Amount deposit_fee; - -}; - - -/** - * Function called with information about a wire transfer identifier. - * Generate a response array based on the given information. - * - * @param cls closure, a hashmap to update - * @param order_id the order to which the deposits belong - * @param deposit_value the amount deposited under @a order_id - * @param deposit_fee the fee charged for @a deposit_value - */ -static void -transfer_summary_cb (void *cls, - const char *order_id, - const struct TALER_Amount *deposit_value, - const struct TALER_Amount *deposit_fee) -{ - struct GNUNET_CONTAINER_MultiHashMap *map = cls; - struct Entry *current_entry; - struct GNUNET_HashCode h_key; - - GNUNET_CRYPTO_hash (order_id, - strlen (order_id), - &h_key); - current_entry = GNUNET_CONTAINER_multihashmap_get (map, - &h_key); - if (NULL != current_entry) - { - /* The map already knows this order, do aggregation */ - GNUNET_assert ( (0 <= - TALER_amount_add (¤t_entry->deposit_value, - ¤t_entry->deposit_value, - deposit_value)) && - (0 <= - TALER_amount_add (¤t_entry->deposit_fee, - ¤t_entry->deposit_fee, - deposit_fee)) ); - } - else - { - /* First time in the map for this h_contract_terms*/ - current_entry = GNUNET_new (struct Entry); - current_entry->deposit_value = *deposit_value; - current_entry->deposit_fee = *deposit_fee; - current_entry->order_id = GNUNET_strdup (order_id); - GNUNET_assert (GNUNET_SYSERR != - GNUNET_CONTAINER_multihashmap_put (map, - &h_key, - current_entry, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - } -} - - -/** - * Callback that frees all the elements in the hashmap, and @a cls - * is non-NULL, appends them as JSON to the array - * - * @param cls closure, NULL or a `json_t *` array - * @param key current key - * @param value a `struct Entry` - * @return #GNUNET_YES if the iteration should continue, - * #GNUNET_NO otherwise. - */ -static int -hashmap_update_and_free (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - json_t *ja = cls; - struct Entry *entry = value; - - (void) key; - if (NULL != ja) - { - GNUNET_assert ( - 0 == - json_array_append_new ( - ja, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("order_id", - entry->order_id), - TALER_JSON_pack_amount ("deposit_value", - &entry->deposit_value), - TALER_JSON_pack_amount ("deposit_fee", - &entry->deposit_fee)))); - } - GNUNET_free (entry->order_id); - GNUNET_free (entry); - return GNUNET_YES; -} - - -/** - * Handle a timeout for the processing of the track transfer request. - * - * @param cls closure - */ -static void -handle_transfer_timeout (void *cls) -{ - struct PostTransfersContext *ptc = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resuming POST /private/transfers with error after timeout\n"); - ptc->timeout_task = NULL; - if (NULL != ptc->fo) - { - TMH_EXCHANGES_find_exchange_cancel (ptc->fo); - ptc->fo = NULL; - } - if (NULL != ptc->wdh) - { - TALER_EXCHANGE_transfers_get_cancel (ptc->wdh); - ptc->wdh = NULL; - } - resume_transfer_with_error (ptc, - MHD_HTTP_GATEWAY_TIMEOUT, - TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT, - NULL); -} - - -/** - * We are *done* processing the request, just queue the response (!) - * - * @param ptc request context - */ -static MHD_RESULT -queue (struct PostTransfersContext *ptc) -{ - MHD_RESULT ret; - - GNUNET_assert (0 != ptc->response_code); - if (UINT_MAX == ptc->response_code) - { - GNUNET_break (0); - return MHD_NO; /* hard error */ - } - ret = MHD_queue_response (ptc->connection, - ptc->response_code, - ptc->response); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Queueing response (%u) for POST /private/transfers (%s).\n", - (unsigned int) ptc->response_code, - ret ? "OK" : "FAILED"); - return ret; -} - - -/** - * Download transfer data from the exchange. - * - * @param ptc request context - */ -static void -download (struct PostTransfersContext *ptc) -{ - ptc->downloaded = true; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Suspending POST /private/transfers handling while working with exchange\n"); - MHD_suspend_connection (ptc->connection); - ptc->suspended = GNUNET_YES; - GNUNET_CONTAINER_DLL_insert (ptc_head, - ptc_tail, - ptc); - ptc->fo = TMH_EXCHANGES_find_exchange (ptc->exchange_url, - false, - &process_transfer_with_exchange, - ptc); - ptc->timeout_task - = GNUNET_SCHEDULER_add_delayed (TRANSFER_GENERIC_TIMEOUT, - &handle_transfer_timeout, - ptc); -} - MHD_RESULT TMH_private_post_transfers (const struct TMH_RequestHandler *rh, struct MHD_Connection *connection, struct TMH_HandlerContext *hc) { - struct PostTransfersContext *ptc = hc->ctx; + const char *payto_uri; + const char *exchange_url; + struct TALER_WireTransferIdentifierRawP wtid; + struct TALER_Amount amount; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_amount ("credit_amount", + TMH_currency, + &amount), + GNUNET_JSON_spec_fixed_auto ("wtid", + &wtid), + GNUNET_JSON_spec_string ("payto_uri", + &payto_uri), + GNUNET_JSON_spec_string ("exchange_url", + &exchange_url), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; enum GNUNET_DB_QueryStatus qs; - if (NULL == ptc) - { - ptc = GNUNET_new (struct PostTransfersContext); - ptc->connection = connection; - ptc->hc = hc; - hc->ctx = ptc; - hc->cc = &transfer_cleanup; - } - if (GNUNET_SYSERR == ptc->suspended) - return MHD_NO; /* we are in shutdown */ - /* resume logic: did we get resumed after a reply was built? */ - if (0 != ptc->response_code) - return queue (ptc); - if ( (NULL != ptc->fo) || - (NULL != ptc->wdh) ) - { - /* likely old MHD version causing spurious wake-up */ - GNUNET_break (GNUNET_NO == ptc->suspended); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Not sure why we are here, should be suspended\n"); - return MHD_YES; /* still work in progress */ - } - if (NULL == ptc->exchange_url) - { - /* First request, parse it! */ - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount ("credit_amount", - TMH_currency, - &ptc->amount), - GNUNET_JSON_spec_fixed_auto ("wtid", - &ptc->wtid), - GNUNET_JSON_spec_string ("payto_uri", - &ptc->payto_uri), - GNUNET_JSON_spec_string ("exchange_url", - &ptc->exchange_url), - GNUNET_JSON_spec_end () - }; - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (connection, - hc->request_body, - spec); - if (GNUNET_OK != res) - return (GNUNET_NO == res) - ? MHD_YES - : MHD_NO; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "New inbound wire transfer over %s to %s from %s\n", - TALER_amount2s (&ptc->amount), - ptc->payto_uri, - ptc->exchange_url); - } + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New inbound wire transfer over %s to %s from %s\n", + TALER_amount2s (&amount), + payto_uri, + exchange_url); /* Check if transfer data is in database, if not, add it. */ for (unsigned int retry = 0; retry<MAX_RETRIES; retry++) { - struct GNUNET_TIME_Timestamp execution_time; - struct TALER_Amount total_amount; - struct TALER_Amount exchange_amount; - struct TALER_Amount wire_fee; - bool verified; - bool have_exchange_sig; - TMH_db->preflight (TMH_db->cls); if (GNUNET_OK != TMH_db->start (TMH_db->cls, @@ -984,16 +86,19 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh, TALER_EC_GENERIC_DB_START_FAILED, "transfer"); } - qs = TMH_db->lookup_transfer (TMH_db->cls, - ptc->hc->instance->settings.id, - ptc->exchange_url, - &ptc->wtid, - &total_amount, - &wire_fee, - &exchange_amount, - &execution_time, - &have_exchange_sig, - &verified); + qs = TMH_db->insert_transfer (TMH_db->cls, + hc->instance->settings.id, + exchange_url, + &wtid, + &amount, + payto_uri, + true /* confirmed! */); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == 0) + qs = TMH_db->set_transfer_status_to_confirmed (TMH_db->cls, + hc->instance->settings.id, + exchange_url, + &wtid, + &amount); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -1001,357 +106,58 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh, TMH_db->rollback (TMH_db->cls); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "transfer"); + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_transfer"); + case GNUNET_DB_STATUS_SOFT_ERROR: + TMH_db->rollback (TMH_db->cls); + continue; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* Could not set to confirmed, must differ by amount! */ + TMH_db->rollback (TMH_db->cls); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_SUBMISSION, + NULL); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; + } + { + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_MERCHANT_WIRE_TRANSFER_CONFIRMED) + }; + + TMH_db->event_notify (TMH_db->cls, + &es, + NULL, + 0); + } + qs = TMH_db->commit (TMH_db->cls); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + TMH_db->rollback (TMH_db->cls); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_COMMIT_FAILED, + NULL); case GNUNET_DB_STATUS_SOFT_ERROR: TMH_db->rollback (TMH_db->cls); continue; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - /* Transfer so far unknown; try to persist the wire transfer information - we have received in the database (it is not yet present). Upon - success, try to download the transfer details from the exchange. */ - { - uint64_t account_serial; - - /* Make sure the bank account is configured. */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Transfer is not yet known\n"); - qs = TMH_db->lookup_account (TMH_db->cls, - ptc->hc->instance->settings.id, - ptc->payto_uri, - &account_serial); - switch (qs) - { - case GNUNET_DB_STATUS_SOFT_ERROR: - TMH_db->rollback (TMH_db->cls); - continue; - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - TMH_db->rollback (TMH_db->cls); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "lookup_account"); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Bank account `%s' not configured for instance `%s'\n", - ptc->payto_uri, - ptc->hc->instance->settings.id); - TMH_db->rollback (TMH_db->cls); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_ACCOUNT_NOT_FOUND, - ptc->payto_uri); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Bank account `%s' is configured at row %llu\n", - ptc->payto_uri, - (unsigned long long) account_serial); - break; - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Inserting new transfer\n"); - qs = TMH_db->insert_transfer (TMH_db->cls, - ptc->hc->instance->settings.id, - ptc->exchange_url, - &ptc->wtid, - &ptc->amount, - ptc->payto_uri, - true /* confirmed! */); - switch (qs) - { - case GNUNET_DB_STATUS_SOFT_ERROR: - TMH_db->rollback (TMH_db->cls); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Soft error, retrying...\n"); - continue; - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - TMH_db->rollback (TMH_db->cls); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "transfer"); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - TMH_db->rollback (TMH_db->cls); - /* Should not happen: we checked earlier! */ - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_CONFLICT, - TALER_EC_GENERIC_DB_STORE_FAILED, - "not unique"); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - { - struct GNUNET_DB_EventHeaderP es = { - .size = htons (sizeof (es)), - .type = htons (TALER_DBEVENT_MERCHANT_WIRE_TRANSFER_CONFIRMED) - }; - - TMH_db->event_notify (TMH_db->cls, - &es, - NULL, - 0); - } - break; - } - - qs = TMH_db->commit (TMH_db->cls); - switch (qs) - { - case GNUNET_DB_STATUS_SOFT_ERROR: - TMH_db->rollback (TMH_db->cls); - continue; - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - TMH_db->rollback (TMH_db->cls); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_COMMIT_FAILED, - NULL); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "post-transfer committed successfully\n"); - break; - } - download (ptc); - return MHD_YES; /* download() always suspends */ - } case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* Transfer exists */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Transfer exists in DB (verified: %s, exchange signature: %s)\n", - verified ? "true" : "false", - have_exchange_sig ? "true" : "false"); - if (! verified) - { - if ( (! ptc->downloaded) && - (! have_exchange_sig) ) - { - /* We may have previously attempted and failed to - download the exchange data, do it again! */ - TMH_db->rollback (TMH_db->cls); - download (ptc); - return MHD_YES; /* download always suspends */ - } - if (! have_exchange_sig) - { - /* We tried to download and still failed to get - an exchange signture. Still, that should have - been handled there. */ - TMH_db->rollback (TMH_db->cls); - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "download but no exchange signature and no error"); - } - /* verify */ - if (GNUNET_SYSERR == - check_wire_fee (ptc, - execution_time, - &wire_fee)) - { - TMH_db->rollback (TMH_db->cls); - return queue (ptc); /* generate error */ - } - if (ptc->soft_retry) - { - /* DB serialization failure */ - ptc->soft_retry = false; - TMH_db->rollback (TMH_db->cls); - continue; - } - qs = TMH_db->lookup_transfer_details (TMH_db->cls, - ptc->exchange_url, - &ptc->wtid, - &verify_exchange_claim_cb, - ptc); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - TMH_db->rollback (TMH_db->cls); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "lookup_transfer_details"); - case GNUNET_DB_STATUS_SOFT_ERROR: - TMH_db->rollback (TMH_db->cls); - continue; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - default: - break; - } - if (0 != ptc->response_code) - { - TMH_db->rollback (TMH_db->cls); - return queue (ptc); /* generate error */ - } - if (ptc->soft_retry) - { - /* DB serialization failure */ - ptc->soft_retry = false; - TMH_db->rollback (TMH_db->cls); - continue; - } - - { - struct TALER_Amount delta; - - if (0 > - TALER_amount_subtract (&delta, - &total_amount, - &wire_fee)) - { - GNUNET_break (0); - TMH_db->rollback (TMH_db->cls); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - NULL); - } - if (0 != - TALER_amount_cmp (&exchange_amount, - &delta)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Amount of expected was %s\n", - TALER_amount2s (&delta)); - TMH_db->rollback (TMH_db->cls); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_TRANSFERS, - TALER_amount2s (&exchange_amount)); - } - if ( (GNUNET_OK != - TALER_amount_cmp_currency (&ptc->amount, - &delta)) || - (0 != - TALER_amount_cmp (&ptc->amount, - &delta)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Amount submitted was %s\n", - TALER_amount2s (&ptc->amount)); - TMH_db->rollback (TMH_db->cls); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_SUBMISSION, - TALER_amount2s (&exchange_amount)); - } - } - verified = true; - qs = TMH_db->set_transfer_status_to_verified (TMH_db->cls, - ptc->exchange_url, - &ptc->wtid); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - TMH_db->rollback (TMH_db->cls); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "set_transfer_status_to_verified"); - case GNUNET_DB_STATUS_SOFT_ERROR: - TMH_db->rollback (TMH_db->cls); - continue; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_assert (0); - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - } /* end of 'if (! verified)' */ - - /* Short version: we verified that the exchange reply and - our own accounting match; generate the summary response */ - GNUNET_assert (verified); - { - struct GNUNET_CONTAINER_MultiHashMap *map; - json_t *deposit_sums; - - map = GNUNET_CONTAINER_multihashmap_create (16, - GNUNET_NO); - qs = TMH_db->lookup_transfer_summary (TMH_db->cls, - ptc->exchange_url, - &ptc->wtid, - &transfer_summary_cb, - map); - switch (qs) - { - case GNUNET_DB_STATUS_SOFT_ERROR: - TMH_db->rollback (TMH_db->cls); - continue; - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - TMH_db->rollback (TMH_db->cls); - GNUNET_CONTAINER_multihashmap_iterate (map, - &hashmap_update_and_free, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (map); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "transfer summary"); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - default: - break; - } - - qs = TMH_db->commit (TMH_db->cls); - switch (qs) - { - case GNUNET_DB_STATUS_SOFT_ERROR: - TMH_db->rollback (TMH_db->cls); - continue; - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - TMH_db->rollback (TMH_db->cls); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_COMMIT_FAILED, - NULL); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "post-transfer committed uselessly\n"); - break; - } - - deposit_sums = json_array (); - GNUNET_assert (NULL != deposit_sums); - GNUNET_CONTAINER_multihashmap_iterate (map, - &hashmap_update_and_free, - deposit_sums); - GNUNET_CONTAINER_multihashmap_destroy (map); - return TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_OK, - TALER_JSON_pack_amount ("total", - &total_amount), - TALER_JSON_pack_amount ("wire_fee", - &wire_fee), - GNUNET_JSON_pack_timestamp ("execution_time", - execution_time), - GNUNET_JSON_pack_array_steal ("deposit_sums", - deposit_sums)); - } /* end of 'verified == true' (not an 'if'!) */ - } /* end of 'switch (qs)' */ - GNUNET_assert (0); - } /* end of 'for(retries...) */ - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_SOFT_FAILURE, - "post-transfers"); + "post-transfer committed successfully\n"); + break; + } + } + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); } diff --git a/src/backend/taler-merchant-httpd_private-post-transfers.h b/src/backend/taler-merchant-httpd_private-post-transfers.h index a83a3449..8a411d2c 100644 --- a/src/backend/taler-merchant-httpd_private-post-transfers.h +++ b/src/backend/taler-merchant-httpd_private-post-transfers.h @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2014-2020 Taler Systems SA + (C) 2014-2023 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 @@ -26,13 +26,6 @@ /** - * We are shutting down, force resume of all POST /transfers requests. - */ -void -TMH_force_post_transfers_resume (void); - - -/** * Manages a POST /private/transfers call. It calls the GET /transfers/$WTID * offered by the exchange in order to obtain the set of transfers * (of coins) associated with a given wire transfer. diff --git a/src/backend/taler-merchant-httpd_private-post-webhooks.c b/src/backend/taler-merchant-httpd_private-post-webhooks.c index a7b90b0d..1115811e 100644 --- a/src/backend/taler-merchant-httpd_private-post-webhooks.c +++ b/src/backend/taler-merchant-httpd_private-post-webhooks.c @@ -87,11 +87,11 @@ TMH_private_post_webhooks (const struct TMH_RequestHandler *rh, GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("header_template", (const char **) &wb.header_template), - NULL), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("body_template", (const char **) &wb.body_template), - NULL), + NULL), GNUNET_JSON_spec_end () }; @@ -112,7 +112,6 @@ TMH_private_post_webhooks (const struct TMH_RequestHandler *rh, } - /* finally, interact with DB until no serialization error */ for (unsigned int i = 0; i<MAX_RETRIES; i++) { diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am index 2ed54ef7..ab46a59e 100644 --- a/src/backenddb/Makefile.am +++ b/src/backenddb/Makefile.am @@ -62,6 +62,7 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \ pg_update_transfer_status.h pg_update_transfer_status.c \ pg_delete_exchange_accounts.h pg_delete_exchange_accounts.c \ pg_select_accounts_by_exchange.h pg_select_accounts_by_exchange.c \ + pg_set_transfer_status_to_confirmed.h pg_set_transfer_status_to_confirmed.c \ pg_insert_exchange_account.h pg_insert_exchange_account.c \ pg_lookup_reserves.h pg_lookup_reserves.c \ plugin_merchantdb_postgres.c pg_helper.h diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql index cd4ae9f3..77792735 100644 --- a/src/backenddb/merchant-0001.sql +++ b/src/backenddb/merchant-0001.sql @@ -380,13 +380,13 @@ COMMENT ON TABLE merchant_refund_proofs -------------------- Wire transfers --------------------------- CREATE TABLE IF NOT EXISTS merchant_transfers - (credit_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY + (credit_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ,exchange_url VARCHAR NOT NULL ,wtid BYTEA CHECK (LENGTH(wtid)=32) ,credit_amount_val INT8 NOT NULL ,credit_amount_frac INT4 NOT NULL - ,account_serial BIGINT NOT NULL - REFERENCES merchant_accounts (account_serial) ON DELETE CASCADE + ,account_serial INT8 NOT NULL + REFERENCES merchant_accounts (account_serial) ON DELETE CASCADE ,verified BOOLEAN NOT NULL DEFAULT FALSE ,confirmed BOOLEAN NOT NULL DEFAULT FALSE ,UNIQUE (wtid, exchange_url, account_serial) diff --git a/src/backenddb/pg_set_transfer_status_to_confirmed.c b/src/backenddb/pg_set_transfer_status_to_confirmed.c new file mode 100644 index 00000000..700965a1 --- /dev/null +++ b/src/backenddb/pg_set_transfer_status_to_confirmed.c @@ -0,0 +1,66 @@ +/* + This file is part of TALER + Copyright (C) 2023 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 backenddb/pg_set_transfer_status_to_confirmed.c + * @brief Implementation of the set_transfer_status_to_confirmed function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_set_transfer_status_to_confirmed.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TMH_PG_set_transfer_status_to_confirmed ( + void *cls, + const char *instance_id, + const char *exchange_url, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *amount) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_auto_from_type (wtid), + GNUNET_PQ_query_param_string (exchange_url), + TALER_PQ_query_param_amount (amount), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + PREPARE (pg, + "set_transfer_status_to_confirmed", + "UPDATE merchant_transfers SET" + " confirmed=TRUE" + " WHERE wtid=$2" + " AND credit_amount_val=$4" + " AND credit_amount_frac=$5" + " AND exchange_url=$3" + " AND account_serial IN" + " (SELECT account_serial" + " FROM merchant_accounts" + " WHERE merchant_serial =" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1));"); + return GNUNET_PQ_eval_prepared_non_select ( + pg->conn, + "set_transfer_status_to_confirmed", + params); +} diff --git a/src/backenddb/pg_set_transfer_status_to_confirmed.h b/src/backenddb/pg_set_transfer_status_to_confirmed.h new file mode 100644 index 00000000..859ebb26 --- /dev/null +++ b/src/backenddb/pg_set_transfer_status_to_confirmed.h @@ -0,0 +1,48 @@ +/* + This file is part of TALER + Copyright (C) 2022 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 backenddb/pg_set_transfer_status_to_confirmed.h + * @brief implementation of the set_transfer_status_to_confirmed function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_SET_TRANSFER_STATUS_TO_CONFIRMED_H +#define PG_SET_TRANSFER_STATUS_TO_CONFIRMED_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Set transfer status to confirmed. + * + * @param cls closure + * @param instance_id merchant instance with the update + * @param exchange_url the exchange that made the transfer + * @param wtid wire transfer subject + * @param amount confirmed amount of the wire transfer + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +TMH_PG_set_transfer_status_to_confirmed ( + void *cls, + const char *instance_id, + const char *exchange_url, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *amount); + + +#endif diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index ca26c01d..710fbdb3 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -42,6 +42,7 @@ #include "pg_insert_exchange_account.h" #include "pg_lookup_reserves.h" #include "pg_update_transfer_status.h" +#include "pg_set_transfer_status_to_confirmed.h" /** @@ -3609,13 +3610,12 @@ postgres_insert_transfer ( bool confirmed) { struct PostgresClosure *pg = cls; - uint8_t confirmed8 = confirmed; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (exchange_url), GNUNET_PQ_query_param_auto_from_type (wtid), TALER_PQ_query_param_amount (credit_amount), GNUNET_PQ_query_param_string (payto_uri), - GNUNET_PQ_query_param_auto_from_type (&confirmed8), + GNUNET_PQ_query_param_bool (confirmed), GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_end }; @@ -4227,35 +4227,6 @@ postgres_lookup_transfer ( /** - * Set transfer status to verified. - * - * @param cls closure - * @param exchange_url the exchange that made the transfer - * @param wtid wire transfer subject - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_set_transfer_status_to_verified ( - void *cls, - const char *exchange_url, - const struct TALER_WireTransferIdentifierRawP *wtid) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (wtid), - GNUNET_PQ_query_param_string (exchange_url), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select ( - pg->conn, - "set_transfer_status_to_verified", - params); -} - - -/** * Closure for #lookup_transfer_summary_cb(). */ struct LookupTransferSummaryContext @@ -8637,7 +8608,8 @@ postgres_connect (void *cls) " AND merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" - " WHERE merchant_id=$7)"), + " WHERE merchant_id=$7)" + " ON CONFLICT DO NOTHING;"), /* for postgres_delete_transfer() */ GNUNET_PQ_make_prepare ("delete_transfer", "DELETE FROM merchant_transfers" @@ -8805,12 +8777,6 @@ postgres_connect (void *cls) " WHERE wtid=$2" " AND exchange_url=$1" " AND merchant_id=$3;"), - /* for postgres_set_transfer_status_to_verified() */ - GNUNET_PQ_make_prepare ("set_transfer_status_to_verified", - "UPDATE merchant_transfers SET" - " verified=TRUE" - " WHERE wtid=$1" - " AND exchange_url=$2"), /* for postgres_lookup_transfer_summary() */ GNUNET_PQ_make_prepare ("lookup_transfer_summary", "SELECT" @@ -9555,8 +9521,8 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) plugin->lookup_deposits_by_contract_and_coin = &postgres_lookup_deposits_by_contract_and_coin; plugin->lookup_transfer = &postgres_lookup_transfer; - plugin->set_transfer_status_to_verified = - &postgres_set_transfer_status_to_verified; + plugin->set_transfer_status_to_confirmed = + &TMH_PG_set_transfer_status_to_confirmed; plugin->lookup_transfer_summary = &postgres_lookup_transfer_summary; plugin->lookup_transfer_details = &postgres_lookup_transfer_details; plugin->lookup_instances diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c index c678f484..c3a2d3ad 100644 --- a/src/backenddb/test_merchantdb.c +++ b/src/backenddb/test_merchantdb.c @@ -3809,11 +3809,12 @@ test_lookup_transfer_details_by_order ( memset (results_matching, 0, sizeof (unsigned int) * transfers_length); - if (transfers_length != plugin->lookup_transfer_details_by_order (plugin->cls, - order_serial, - & - lookup_transfer_details_order_cb, - &cmp)) + if (transfers_length != + plugin->lookup_transfer_details_by_order ( + plugin->cls, + order_serial, + &lookup_transfer_details_order_cb, + &cmp)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Lookup transfer details by order failed\n"); @@ -4352,15 +4353,16 @@ run_test_transfers (struct TestTransfers_Closure *cls) true)); TEST_RET_ON_FAIL (test_lookup_transfer (&cls->instance, &cls->transfers[0])); - TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == - plugin->set_transfer_status_to_verified (plugin->cls, - cls->deposit. - exchange_url, - &cls-> - transfers[0]. - wtid), - "Set transfer status to verified failed\n"); - cls->transfers[0].verified = true; + TEST_COND_RET_ON_FAIL ( + GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == + plugin->set_transfer_status_to_confirmed ( + plugin->cls, + cls->instance.instance.id, + cls->deposit.exchange_url, + &cls->transfers[0].wtid, + &cls->deposit.amount_with_fee), + "Set transfer status to confirmed failed\n"); + cls->transfers[0].confirmed = true; TEST_RET_ON_FAIL (test_lookup_transfer (&cls->instance, &cls->transfers[0])); TEST_RET_ON_FAIL (test_lookup_transfer_summary (cls->deposit.exchange_url, diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h index 1135cb38..15d83fce 100644 --- a/src/include/taler_merchant_service.h +++ b/src/include/taler_merchant_service.h @@ -2930,31 +2930,6 @@ TALER_MERCHANT_wallet_post_order_refund_cancel ( */ struct TALER_MERCHANT_PostTransfersHandle; -/** - * Information about the _total_ amount that was paid back - * by the exchange for a given h_contract_terms, by _one_ wire - * transfer. - */ -struct TALER_MERCHANT_TrackTransferDetail -{ - - /** - * Total amount paid back by the exchange. - */ - struct TALER_Amount deposit_value; - - /** - * Total amount of deposit fees. - */ - struct TALER_Amount deposit_fee; - - /** - * Order ID associated whit this payment. - */ - const char *order_id; - -}; - /** * @brief Response to a POST /transfers operation from a merchant's backend. @@ -2973,38 +2948,6 @@ struct TALER_MERCHANT_PostTransfersResponse { /** - * Details in case of success (#MHD_HTTP_OK). - */ - struct - { - - /** - * when did the transfer happen (according to the exchange) - */ - struct GNUNET_TIME_Timestamp execution_time; - - /** - * total amount of the wire transfer - */ - struct TALER_Amount total_amount; - - /** - * how much did the exchange charge in terms of wire fees - */ - struct TALER_Amount wire_fee; - - /** - * Length of the @e details array - */ - unsigned int details_length; - - /** - * array with details about the combined transactions - */ - const struct TALER_MERCHANT_TrackTransferDetail *details; - } success; - - /** * Details if we got an #MHD_HTTP_BAD_GATEWAY. */ struct diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index 7a15ddee..d07b92f1 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -2341,20 +2341,22 @@ struct TALER_MERCHANTDB_Plugin /** - * Set transfer status to verified. + * Set transfer status to confirmed. * * @param cls closure * @param instance_id instance to lookup payments for * @param exchange_url the exchange that made the transfer - * @param payto_uri account that received the transfer * @param wtid wire transfer subject + * @param amount confirmed amount of the wire transfer * @return transaction status */ enum GNUNET_DB_QueryStatus - (*set_transfer_status_to_verified)( + (*set_transfer_status_to_confirmed)( void *cls, + const char *instance_id, const char *exchange_url, - const struct TALER_WireTransferIdentifierRawP *wtid); + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *amount); /** diff --git a/src/lib/merchant_api_post_transfers.c b/src/lib/merchant_api_post_transfers.c index 2a1a3f68..4a4ad2ac 100644 --- a/src/lib/merchant_api_post_transfers.c +++ b/src/lib/merchant_api_post_transfers.c @@ -96,96 +96,7 @@ handle_post_transfers_finished (void *cls, case 0: ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; - case MHD_HTTP_OK: - { - json_t *deposit_sums; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any ( - "total", - &ptr.details.success.total_amount), - TALER_JSON_spec_amount_any ( - "wire_fee", - &ptr.details.success.wire_fee), - GNUNET_JSON_spec_timestamp ( - "execution_time", - &ptr.details.success.execution_time), - GNUNET_JSON_spec_json ("deposit_sums", - &deposit_sums), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (ptr.hr.reply, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ptr.hr.http_status = 0; - ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (! json_is_array (deposit_sums)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - ptr.hr.http_status = 0; - ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - { - struct TALER_MERCHANT_TrackTransferDetail *details; - unsigned int i; - bool ok; - json_t *deposit_sum; - - ptr.details.success.details_length - = json_array_size (deposit_sums); - details = GNUNET_new_array (ptr.details.success.details_length, - struct TALER_MERCHANT_TrackTransferDetail); - ptr.details.success.details = details; - ok = true; - json_array_foreach (deposit_sums, i, deposit_sum) { - struct TALER_MERCHANT_TrackTransferDetail *d - = &details[i]; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("order_id", - &d->order_id), - TALER_JSON_spec_amount_any ("deposit_value", - &d->deposit_value), - TALER_JSON_spec_amount_any ("deposit_fee", - &d->deposit_fee), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (deposit_sum, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - ok = false; - break; - } - } - - if (! ok) - { - GNUNET_break_op (0); - GNUNET_free (details); - GNUNET_JSON_parse_free (spec); - ptr.hr.http_status = 0; - ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - pth->cb (pth->cb_cls, - &ptr); - GNUNET_free (details); - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_transfers_post_cancel (pth); - return; - } - } - case MHD_HTTP_ACCEPTED: + case MHD_HTTP_NO_CONTENT: break; case MHD_HTTP_UNAUTHORIZED: ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c index e000778f..90b0def3 100644 --- a/src/testing/test_kyc_api.c +++ b/src/testing/test_kyc_api.c @@ -255,7 +255,7 @@ run (void *cls, merchant_payto, merchant_url, "EUR:4.98", - MHD_HTTP_OK, + MHD_HTTP_NO_CONTENT, "deposit-simple", NULL), TALER_TESTING_cmd_run_tme ("run taler-merchant-exchange-1", @@ -368,7 +368,7 @@ run (void *cls, merchant_payto, merchant_url, "EUR:4.98", - MHD_HTTP_OK, + MHD_HTTP_NO_CONTENT, "deposit-simple", NULL), TALER_TESTING_cmd_run_tme ("run taler-merchant-exchange-2-aml", diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c index 551f824c..89bce513 100644 --- a/src/testing/test_merchant_api.c +++ b/src/testing/test_merchant_api.c @@ -492,13 +492,11 @@ run (void *cls, PAYTO_I1, merchant_url, "EUR:4.98", - MHD_HTTP_OK, + MHD_HTTP_NO_CONTENT, "deposit-simple", NULL), TALER_TESTING_cmd_run_tme ("run taler-merchant-exchange-1", config_file), - /* FIXME: with the new API, the following will no longer - make sense (probably should just be removed): */ TALER_TESTING_cmd_merchant_post_transfer2 ("post-transfer-bad", merchant_url, PAYTO_I1, @@ -508,7 +506,7 @@ run (void *cls, so we are sure to not get any reply*/ "http://192.0.2.1/404/", - MHD_HTTP_GATEWAY_TIMEOUT), + MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_get_transfers ("get-transfers-1", merchant_url, PAYTO_I1, diff --git a/src/testing/testing_api_cmd_get_transfers.c b/src/testing/testing_api_cmd_get_transfers.c index fe50c349..33c2d462 100644 --- a/src/testing/testing_api_cmd_get_transfers.c +++ b/src/testing/testing_api_cmd_get_transfers.c @@ -224,29 +224,6 @@ get_transfers_cb ( return; } } - { - const struct GNUNET_TIME_Timestamp *execution_time; - - if (GNUNET_OK != - TALER_TESTING_get_trait_timestamp (transfer_cmd, - 0, - &execution_time)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not fetch wire transfer execution time\n"); - TALER_TESTING_interpreter_fail (gts->is); - return; - } - if (GNUNET_TIME_timestamp_cmp (*execution_time, - !=, - transfers[i].execution_time)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire transfer execution time does not match\n"); - TALER_TESTING_interpreter_fail (gts->is); - return; - } - } } break; default: diff --git a/src/testing/testing_api_cmd_post_transfers.c b/src/testing/testing_api_cmd_post_transfers.c index b73c6b15..4971d952 100644 --- a/src/testing/testing_api_cmd_post_transfers.c +++ b/src/testing/testing_api_cmd_post_transfers.c @@ -115,10 +115,6 @@ struct PostTransfersState */ unsigned int deposits_length; - /** - * When the exchange executed the transfer. - */ - struct GNUNET_TIME_Timestamp execution_time; }; @@ -147,150 +143,7 @@ transfers_cb (void *cls, } switch (ptr->hr.http_status) { - case MHD_HTTP_OK: - { - pts->execution_time = ptr->details.success.execution_time; - pts->wire_fee = ptr->details.success.wire_fee; - fprintf (stderr, - "FIXME"); - json_dumpf (ptr->hr.reply, - stderr, - 0); -#if FIXME_WRITE_PROPPER_CHECK_OF_RETURNED_DATA_HERE - /* this code is some legacy logic that is close to what we - need but needs to be updated to the current API */ - struct TALER_Amount total; - - if (0 > - TALER_amount_subtract (&total, - total_amount, - wire_fee)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (pts->is); - return; - } - if (0 != - TALER_amount_cmp (&total, - &pts->credit_amount)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (pts->is); - return; - } - TALER_amount_set_zero (total.currency, - &total); - for (unsigned int i = 0; i<details_length; i++) - { - const struct TALER_MERCHANT_TrackTransferDetail *tdd = &details[i]; - struct TALER_Amount sum; - struct TALER_Amount fees; - - TALER_amount_set_zero (tdd->deposit_value.currency, - &sum); - TALER_amount_set_zero (tdd->deposit_fee.currency, - &fees); - for (unsigned int j = 0; j<pts->deposits_length; j++) - { - const char *label = pts->deposits[j]; - const struct TALER_TESTING_Command *cmd; - const json_t *contract_terms; - const struct TALER_Amount *deposit_value; - const struct TALER_Amount *deposit_fee; - const char *order_id; - - cmd = TALER_TESTING_interpreter_lookup_command (pts->is, - label); - if (NULL == cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (pts->is); - return; - } - if ( (GNUNET_OK != - TALER_TESTING_get_trait_contract_terms (cmd, - 0, - &contract_terms)) || - (GNUNET_OK != - TALER_TESTING_get_trait_amount_obj (cmd, - TALER_TESTING_CMD_DEPOSIT_TRAIT_IDX_DEPOSIT_VALUE, - &deposit_value)) || - (GNUNET_OK != - TALER_TESTING_get_trait_amount_obj (cmd, - TALER_TESTING_CMD_DEPOSIT_TRAIT_IDX_DEPOSIT_FEE, - &deposit_fee)) ) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (pts->is); - return; - } - order_id = json_string_value (json_object_get (contract_terms, - "order_id")); - if (NULL == order_id) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (pts->is); - return; - } - if (0 != strcmp (tdd->order_id, - order_id)) - continue; - if (0 > - TALER_amount_add (&sum, - &sum, - deposit_value)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (pts->is); - return; - } - if (0 > - TALER_amount_add (&fees, - &fees, - deposit_fee)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (pts->is); - return; - } - } - if (0 != - TALER_amount_cmp (&sum, - &tdd->deposit_value)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (pts->is); - return; - } - if (0 != - TALER_amount_cmp (&fees, - &tdd->deposit_fee)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (pts->is); - return; - } - GNUNET_assert (0 <= - TALER_amount_add (&total, - &total, - &tdd->deposit_value)); - GNUNET_assert (0 <= - TALER_amount_subtract (&total, - &total, - &tdd->deposit_fee)); - } - if (0 != - TALER_amount_cmp (&total, - &pts->credit_amount)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (pts->is); - return; - } -#endif - break; - } - case MHD_HTTP_ACCEPTED: + case MHD_HTTP_NO_CONTENT: break; case MHD_HTTP_UNAUTHORIZED: break; @@ -334,8 +187,6 @@ post_transfers_traits (void *cls, TALER_TESTING_make_trait_fee (&pts->wire_fee), TALER_TESTING_make_trait_exchange_url ( (const char **) &pts->exchange_url), - TALER_TESTING_make_trait_timestamp (0, - &pts->execution_time), TALER_TESTING_make_trait_bank_row (&pts->serial), TALER_TESTING_trait_end (), }; |