diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-transfers.c | 277 | ||||
-rw-r--r-- | src/backenddb/merchant-0001.sql | 2 | ||||
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 325 | ||||
-rw-r--r-- | src/include/taler_merchantdb_plugin.h | 96 |
4 files changed, 524 insertions, 176 deletions
diff --git a/src/backend/taler-merchant-httpd_private-post-transfers.c b/src/backend/taler-merchant-httpd_private-post-transfers.c index fd9752d8..519e69f5 100644 --- a/src/backend/taler-merchant-httpd_private-post-transfers.c +++ b/src/backend/taler-merchant-httpd_private-post-transfers.c @@ -299,11 +299,12 @@ check_transfer (void *cls, 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 ( "{s:I, s:s, s:s, s:o, s:o," " s:I, s:o, s:o, s:o, s:o," - " s:o, s:o, s:o }", + " s:o, s:o, s:o, s:o, s:o }", "code", (json_int_t) TALER_EC_POST_TRANSFERS_CONFLICTING_REPORTS, "hint", @@ -330,6 +331,10 @@ check_transfer (void *cls, GNUNET_JSON_from_data_auto (&ttd->h_contract_terms), "amount_with_fee", TALER_JSON_from_amount (amount_with_fee), + "coin_value", + TALER_JSON_from_amount (&ttd->coin_value), + "coin_fee", + TALER_JSON_from_amount (&ttd->coin_fee), "deposit_fee", TALER_JSON_from_amount (deposit_fee)); return; @@ -339,16 +344,14 @@ check_transfer (void *cls, /** - * 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. + * 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 json response from the exchange * @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 @@ -356,7 +359,6 @@ check_transfer (void *cls, */ static int check_wire_fee (struct PostTransfersContext *ptc, - const json_t *json, struct GNUNET_TIME_Absolute execution_time, const struct TALER_Amount *wire_fee) { @@ -397,11 +399,14 @@ check_wire_fee (struct PostTransfersContext *ptc, return GNUNET_OK; /* expected_fee >= wire_fee */ } /* Wire fee check failed, export proof to client */ - resume_transfer_with_response ( - ptc, - MHD_HTTP_FAILED_DEPENDENCY, + /* FIXME: This is not actually the *full* proof, as we are + not including the exchange's bogus response with the + signature claiming a different wire fee. Also, this + error is not described in the API docs! */ + ptc->response_code = MHD_HTTP_ACCEPTED; + ptc->response = TALER_MHD_make_json_pack ( - "{s:I, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:O}", + "{s:I, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}", "code", (json_int_t) TALER_EC_POST_TRANSFERS_JSON_BAD_WIRE_FEE, "wire_fee", TALER_JSON_from_amount (wire_fee), "execution_time", GNUNET_JSON_from_time_abs (execution_time), @@ -410,8 +415,7 @@ check_wire_fee (struct PostTransfersContext *ptc, "start_date", GNUNET_JSON_from_time_abs (start_date), "end_date", GNUNET_JSON_from_time_abs (end_date), "master_sig", GNUNET_JSON_from_data_auto (&master_sig), - "master_pub", GNUNET_JSON_from_data_auto (&ptc->master_pub), - "json", json)); + "master_pub", GNUNET_JSON_from_data_auto (&ptc->master_pub)); GNUNET_free (wire_method); return GNUNET_SYSERR; } @@ -452,85 +456,6 @@ wire_transfer_cb (void *cls, return; } - if (GNUNET_SYSERR == - check_wire_fee (ptc, - hr->reply, - td->execution_time, - &td->wire_fee)) - return; - - /* 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_! - * - * details_length is how many (Taler coin) deposits have been - * aggregated into _this_ wire transfer. - */// - for (unsigned int i = 0; i < td->details_length; i++) - { - const struct TALER_TrackTransferDetails *ttd = &td->details[i]; - - ptc->current_offset = i; - ptc->current_detail = ttd; - /* Set the coin as "never seen" before. */ - ptc->check_transfer_result = GNUNET_NO; - TMH_db->preflight (TMH_db->cls); - qs = TMH_db->lookup_deposits_by_contract_and_coin (TMH_db->cls, - instance_id, - &ttd->h_contract_terms, - &ttd->coin_pub, - &check_transfer, - ptc); - if (0 > qs) - { - /* single, read-only SQL statements should never cause - serialization problems */ - 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_transfer_with_error ( - ptc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_POST_TRANSFERS_DB_FETCH_DEPOSIT_ERROR, - "failed to obtain deposit data from local database"); - return; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - /* 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; - } - if (GNUNET_NO == ptc->check_transfer_result) - { - /* Internal error: how can we have called #check_transfer() - but still have no result? */ - GNUNET_break (0); - resume_transfer_with_error (ptc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_POST_TRANSFERS_DB_INTERNAL_LOGIC_ERROR, - "internal logic error"); - return; - } - if (GNUNET_SYSERR == ptc->check_transfer_result) - { - /* #check_transfer() failed, report conflict! */ - GNUNET_break_op (0); - GNUNET_assert (NULL != ptc->response); - resume_transfer_with_response (ptc, - MHD_HTTP_FAILED_DEPENDENCY, - ptc->response); - ptc->response = NULL; - return; - } - } - - /* Response is consistent with the /deposit we made, - remember it for future reference */ for (unsigned int r = 0; r<MAX_RETRIES; r++) { TMH_db->preflight (TMH_db->cls); @@ -674,6 +599,81 @@ process_transfer_with_exchange (void *cls, /** + * 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 */ + ptc->current_offset = current_offset; + ptc->current_detail = ttd; + /* Set the coin as "never seen" before. */ + ptc->check_transfer_result = GNUNET_NO; + TMH_db->preflight (TMH_db->cls); + 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); + if (0 > qs) + { + /* single, read-only SQL statements should never cause + serialization problems */ + 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); + ptc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + ptc->response + = TALER_MHD_make_error (TALER_EC_POST_TRANSFERS_DB_FETCH_DEPOSIT_ERROR, + "failed to obtain deposit data from local database"); + return; + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + /* 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; + } + if (GNUNET_NO == ptc->check_transfer_result) + { + /* 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_POST_TRANSFERS_DB_INTERNAL_LOGIC_ERROR, + "internal logic error"); + return; + } + if (GNUNET_SYSERR == ptc->check_transfer_result) + { + /* #check_transfer() failed, report conflict! */ + GNUNET_break_op (0); + GNUNET_assert (NULL != ptc->response); + return; + } +} + + +/** * 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 @@ -710,7 +710,7 @@ struct Entry * @param deposit_fee the fee charged for @a deposit_value */ static void -transfer_details_cb (void *cls, +transfer_summary_cb (void *cls, const char *order_id, const struct TALER_Amount *deposit_value, const struct TALER_Amount *deposit_fee) @@ -846,6 +846,7 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh, hc->cc = &transfer_cleanup; } +queue: if (0 != ptc->response_code) { MHD_RESULT ret; @@ -910,43 +911,86 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh, /* Check if transfer data is in database! */ { - struct GNUNET_CONTAINER_MultiHashMap *map; struct GNUNET_TIME_Absolute execution_time; struct TALER_Amount total_amount; struct TALER_Amount wire_fee; + bool verified; TMH_db->preflight (TMH_db->cls); - map = GNUNET_CONTAINER_multihashmap_create (16, - GNUNET_NO); - qs = TMH_db->lookup_transfer_details (TMH_db->cls, - hc->instance->settings.id, - ptc->exchange_url, - ptc->payto_uri, - &ptc->wtid, - &total_amount, - &wire_fee, - execution_time, - &transfer_details_cb, - map); + qs = TMH_db->lookup_transfer (TMH_db->cls, + ptc->exchange_url, + &ptc->wtid, + &total_amount, + &wire_fee, + &execution_time, + &verified); 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); - GNUNET_CONTAINER_multihashmap_iterate (map, - &hashmap_free, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (map); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_POST_TRANSFERS_DB_LOOKUP_ERROR, "Failed to query database about transfer details"); } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs) + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + goto fetch; + if (! verified) + { + if (GNUNET_SYSERR == + check_wire_fee (ptc, + execution_time, + &wire_fee)) + { + GNUNET_assert (0 != ptc->response_code); + goto queue; + } + + qs = TMH_db->lookup_transfer_details (TMH_db->cls, + ptc->exchange_url, + &ptc->wtid, + &verify_exchange_claim_cb, + ptc); + if (0 != ptc->response_code) + goto queue; + verified = true; + qs = TMH_db->set_transfer_status_to_verified (TMH_db->cls, + ptc->exchange_url, + &ptc->wtid); + GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); + } + + /* Short version: we already verified, 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); + 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); + GNUNET_CONTAINER_multihashmap_iterate (map, + &hashmap_free, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (map); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_POST_TRANSFERS_DB_LOOKUP_ERROR, + "Failed to query database about transfer details"); + } + deposit_sums = json_array (); GNUNET_assert (NULL != deposit_sums); GNUNET_CONTAINER_multihashmap_iterate (map, @@ -961,11 +1005,12 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh, "wire_fee", TALER_JSON_from_amount (&wire_fee), "execution_time", GNUNET_JSON_from_time_abs (execution_time), "deposit_sums", deposit_sums); - } - } + } /* end of 'verified == true' */ + } /* end of 'transfer data in database' */ /* reply not in database, ensure the POST is in the database, and start work to obtain the reply from the exchange */ +fetch: qs = TMH_db->insert_transfer (TMH_db->cls, ptc->hc->instance->settings.id, ptc->exchange_url, diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql index 754b47f3..9eb60e5c 100644 --- a/src/backenddb/merchant-0001.sql +++ b/src/backenddb/merchant-0001.sql @@ -370,6 +370,8 @@ CREATE TABLE IF NOT EXISTS merchant_transfer_signatures REFERENCES merchant_transfers (credit_serial) ON DELETE CASCADE ,signkey_serial BIGINT NOT NULL REFERENCES merchant_exchange_signing_keys (signkey_serial) ON DELETE CASCADE + ,wire_fee_val INT8 NOT NULL + ,wire_fee_frac INT4 NOT NULL ,execution_time INT8 NOT NULL ,exchange_sig BYTEA NOT NULL CHECK (LENGTH(exchange_sig)=64) ); diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index c196790c..8e402d31 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -2848,6 +2848,7 @@ RETRY: { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&credit_serial), + TALER_PQ_query_param_amount (&td->wire_fee), GNUNET_PQ_query_param_absolute_time (&td->execution_time), GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig), GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub), @@ -3118,14 +3119,109 @@ postgres_lookup_deposits_by_contract_and_coin ( /** - * Closure for #lookup_transfer_details_cb(). + * Lookup transfer status. + * + * @param cls closure + * @param exchange_url the exchange that made the transfer + * @param wtid wire transfer subject + * @param[out] total_amount amount that was transferred (in total, minus @a wire_fee) + * @param[out] wire_fee the wire fee the exchange charged + * @param[out] execution_time when the transfer was executed by the exchange + * @param[out] verified did we confirm the transfer was OK + * @return transaction status */ -struct LookupTransferDetailsContext +static enum GNUNET_DB_QueryStatus +postgres_lookup_transfer ( + void *cls, + const char *exchange_url, + const struct TALER_WireTransferIdentifierRawP *wtid, + struct TALER_Amount *total_amount, + struct TALER_Amount *wire_fee, + struct GNUNET_TIME_Absolute *execution_time, + bool *verified) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (exchange_url), + GNUNET_PQ_query_param_auto_from_type (wtid), + GNUNET_PQ_query_param_end + }; + uint8_t verified8; + struct TALER_Amount credit_amount; + struct GNUNET_PQ_ResultSpec rs[] = { + TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount", + &credit_amount), + TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee", + wire_fee), + GNUNET_PQ_result_spec_absolute_time ("execution_time", + execution_time), + GNUNET_PQ_result_spec_auto_from_type ("verified", + &verified8), + GNUNET_PQ_result_spec_end + }; + enum GNUNET_DB_QueryStatus qs; + + check_connection (pg); + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "lookup_transfer", + params, + rs); + if (qs > 0) + { + *verified = (0 != verified8); + // FIXME: unclear if table stores 'total' including or excluding fee :-(. + // Check and update DOCS and taler_exchange_service.h header! + if (0 > + TALER_amount_add (total_amount, + &credit_amount, + wire_fee)) + { + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + } + } + return qs; +} + + +/** + * 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 { /** * Function to call for each order that was aggregated. */ - TALER_MERCHANTDB_TransferDetailsCallback cb; + TALER_MERCHANTDB_TransferSummaryCallback cb; /** * Closure for @e cb. @@ -3148,16 +3244,16 @@ struct LookupTransferDetailsContext * Function to be called with the results of a SELECT statement * that has returned @a num_results results. * - * @param cls of type `struct LookupTransferDetailsContext *` + * @param cls of type `struct LookupTransferSummaryContext *` * @param result the postgres result * @param num_result the number of results in @a result */ static void -lookup_transfer_details_cb (void *cls, +lookup_transfer_summary_cb (void *cls, PGresult *result, unsigned int num_results) { - struct LookupTransferDetailsContext *ltdc = cls; + struct LookupTransferSummaryContext *ltdc = cls; struct PostgresClosure *pg = ltdc->pg; for (unsigned int i = 0; i<num_results; i++) @@ -3195,16 +3291,134 @@ lookup_transfer_details_cb (void *cls, /** + * Lookup transfer summary. + * + * @param cls closure + * @param exchange_url the exchange that made the transfer + * @param wtid wire transfer subject + * @param cb function to call with detailed transfer data + * @param cb_cls closure for @a cb + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +postgres_lookup_transfer_summary ( + void *cls, + const char *exchange_url, + const struct TALER_WireTransferIdentifierRawP *wtid, + TALER_MERCHANTDB_TransferSummaryCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (exchange_url), + GNUNET_PQ_query_param_auto_from_type (wtid), + GNUNET_PQ_query_param_end + }; + struct LookupTransferSummaryContext ltdc = { + .cb = cb, + .cb_cls = cb_cls, + .pg = pg + }; + enum GNUNET_DB_QueryStatus qs; + + check_connection (pg); + qs = GNUNET_PQ_eval_prepared_multi_select ( + pg->conn, + "lookup_transfer_summary", + params, + &lookup_transfer_summary_cb, + <dc); + if (0 >= qs) + return qs; + return ltdc.qs; +} + + +/** + * Closure for #lookup_transfer_details_cb(). + */ +struct LookupTransferDetailsContext +{ + /** + * Function to call for each order that was aggregated. + */ + TALER_MERCHANTDB_TransferDetailsCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Plugin context. + */ + struct PostgresClosure *pg; + + /** + * Transaction result. + */ + enum GNUNET_DB_QueryStatus qs; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls of type `struct LookupTransferDetailsContext *` + * @param result the postgres result + * @param num_result the number of results in @a result + */ +static void +lookup_transfer_details_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct LookupTransferDetailsContext *ltdc = cls; + struct PostgresClosure *pg = ltdc->pg; + + for (unsigned int i = 0; i<num_results; i++) + { + uint32_t current_offset; + struct TALER_TrackTransferDetails ttd; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint32 ("offset_in_exchange_list", + ¤t_offset), + GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", + &ttd.h_contract_terms), + GNUNET_PQ_result_spec_auto_from_type ("coin_pub", + &ttd.coin_pub), + TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value", + &ttd.coin_value), + TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee", + &ttd.coin_fee), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return; + } + ltdc->qs = i + 1; + ltdc->cb (ltdc->cb_cls, + (unsigned int) current_offset, + &ttd); + GNUNET_PQ_cleanup_result (rs); + } +} + + +/** * Lookup transfer details. * * @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 total_amount amount that was transferred (in total, minus @a wire_fee) - * @param wire_fee the wire fee the exchange charged - * @param execution_time when the transfer was executed by the exchange * @param cb function to call with detailed transfer data * @param cb_cls closure for @a cb * @return transaction status @@ -3212,25 +3426,15 @@ lookup_transfer_details_cb (void *cls, static enum GNUNET_DB_QueryStatus postgres_lookup_transfer_details ( void *cls, - const char *instance_id, const char *exchange_url, - const char *payto_uri, const struct TALER_WireTransferIdentifierRawP *wtid, - const struct TALER_Amount *total_amount, - const struct TALER_Amount *wire_fee, - struct GNUNET_TIME_Absolute execution_time, TALER_MERCHANTDB_TransferDetailsCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; - struct TALER_Amount transfer_amount; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (exchange_url), - GNUNET_PQ_query_param_string (payto_uri), GNUNET_PQ_query_param_auto_from_type (wtid), - TALER_PQ_query_param_amount (&transfer_amount), - GNUNET_PQ_query_param_absolute_time (&execution_time), GNUNET_PQ_query_param_end }; struct LookupTransferDetailsContext ltdc = { @@ -3240,14 +3444,6 @@ postgres_lookup_transfer_details ( }; enum GNUNET_DB_QueryStatus qs; - if (0 > - TALER_amount_subtract (&transfer_amount, - total_amount, - wire_fee)) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, @@ -5687,14 +5883,16 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) "INSERT INTO merchant_transfer_signatures" "(credit_serial" ",signkey_serial" + ",wire_fee_val" + ",wire_fee_frac" ",execution_time" ",exchange_sig) " - "SELECT $1, signkey_serial, $2, $3" + "SELECT $1, signkey_serial, $2, $3, $4, $5" " FROM merchant_exchange_signing_keys" - " WHERE exchange_pub=$4" + " WHERE exchange_pub=$6" " ORDER BY start_date DESC" " LIMIT 1", - 4), + 6), /* for postgres_insert_transfer_details() */ GNUNET_PQ_make_prepare ("insert_transfer_to_coin_mapping", "INSERT INTO merchant_transfer_to_coin" @@ -5755,8 +5953,30 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) " FROM merchant_instances" " WHERE merchant_id=$1)", 3), - /* for postgres_lookup_transfer_details() */ - GNUNET_PQ_make_prepare ("lookup_transfer_details", + /* for postgres_lookup_transfer() */ + GNUNET_PQ_make_prepare ("lookup_transfer", + "SELECT" + " credit_amount_val" + ",credit_amount_frac" + ",wire_fee_val" + ",wire_fee_frac" + ",execution_time" + ",verified" + " FROM merchant_transfers" + " JOIN merchant_transfer_signatures USING (credit_serial)" + " JOIN merchant_accounts USING (account_serial)" + " WHERE wtid=$2" + " AND exchange_url=$1", + 2), + /* 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", + 2), + /* for postgres_lookup_transfer_summary() */ + GNUNET_PQ_make_prepare ("lookup_transfer_summary", "SELECT" " order_id" ",exchange_deposit_value_val" @@ -5764,22 +5984,29 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) ",exchange_deposit_fee_val" ",exchange_deposit_fee_frac" " FROM merchant_transfers" - " JOIN merchant_accounts USING (account_serial)" " JOIN merchant_transfer_to_coin USING (credit_serial)" - " JOIN merchant_transfer_signatures USING (credit_serial)" " JOIN merchant_deposits USING (deposit_serial)" " JOIN merchant_contract_terms USING (order_serial)" - " WHERE wtid=$4" - " AND merchant_transfers.exchange_url=$2" - " AND credit_amount_val=$5" - " AND credit_amount_frac=$6" - " AND payto_uri=$3" - " AND execution_time=$7" - " AND merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)", - 7), + " WHERE wtid=$2" + " AND merchant_transfers.exchange_url=$1", + 2), + /* for postgres_lookup_transfer_details() */ + GNUNET_PQ_make_prepare ("lookup_transfer_details", + "SELECT" + " merchant_contract_terms.h_contract_terms" + ",merchant_transfer_to_coin.offset_in_exchange_list" + ",merchant_deposits.coin_pub" + ",exchange_deposit_value_val" + ",exchange_deposit_value_frac" + ",exchange_deposit_fee_val" + ",exchange_deposit_fee_frac" + " FROM merchant_transfer_to_coin" + " JOIN merchant_deposits USING (deposit_serial)" + " JOIN merchant_contract_terms USING (order_serial)" + " JOIN merchant_transfers USING (credit_serial)" + " WHERE merchant_transfers.wtid=$2" + " AND merchant_transfers.exchange_url=$1", + 2), /* OLD API: */ #if 0 @@ -6101,6 +6328,10 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) plugin->lookup_wire_fee = &postgres_lookup_wire_fee; 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->lookup_transfer_summary = &postgres_lookup_transfer_summary; plugin->lookup_transfer_details = &postgres_lookup_transfer_details; /* OLD API: */ diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index ec584bff..a9816a0f 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -403,13 +403,28 @@ enum TALER_MERCHANTDB_RefundStatus * @param deposit_fee the fee charged for @a deposit_value */ typedef void -(*TALER_MERCHANTDB_TransferDetailsCallback)( +(*TALER_MERCHANTDB_TransferSummaryCallback)( void *cls, const char *order_id, const struct TALER_Amount *deposit_value, const struct TALER_Amount *deposit_fee); +/** + * Function called with detailed information about a wire transfer and + * the underlying deposits that are being aggregated. + * + * @param cls closure + * @param current_offset offset in the exchange reply we are at + * @param ttd details about the transfer at @a current_offset + */ +typedef void +(*TALER_MERCHANTDB_TransferDetailsCallback)( + void *cls, + unsigned int current_offset, + const struct TALER_TrackTransferDetails *ttd); + + /* **************** OLD: ******************** */ /** @@ -1328,16 +1343,76 @@ struct TALER_MERCHANTDB_Plugin /** - * Lookup transfer details. + * Lookup transfer status. * * @param cls closure * @param instance_id instance to lookup payments for - * @param exchange_url - * @param payto_uri - * @param wtid - * @param total_amount - * @param wire_fee - * @param execution_time + * @param exchange_url the exchange that made the transfer + * @param payto_uri account that received the transfer + * @param wtid wire transfer subject + * @param[out] total_amount amount that was transferred (in total, minus @a wire_fee) + * @param[out] wire_fee the wire fee the exchange charged + * @param[out] execution_time when the transfer was executed by the exchange + * @param[out] verified did we confirm the transfer was OK + * @return transaction status + */ + enum GNUNET_DB_QueryStatus + (*lookup_transfer)( + void *cls, + const char *exchange_url, + const struct TALER_WireTransferIdentifierRawP *wtid, + struct TALER_Amount *total_amount, + struct TALER_Amount *wire_fee, + struct GNUNET_TIME_Absolute *execution_time, + bool *verified); + + + /** + * Set transfer status to verified. + * + * @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 + * @return transaction status + */ + enum GNUNET_DB_QueryStatus + (*set_transfer_status_to_verified)( + void *cls, + const char *exchange_url, + const struct TALER_WireTransferIdentifierRawP *wtid); + + + /** + * Lookup transfer summary (used if we already verified the details). + * + * @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 cb function to call with detailed transfer data + * @param cb_cls closure for @a cb + * @return transaction status + */ + enum GNUNET_DB_QueryStatus + (*lookup_transfer_summary)( + void *cls, + const char *exchange_url, + const struct TALER_WireTransferIdentifierRawP *wtid, + TALER_MERCHANTDB_TransferSummaryCallback cb, + void *cb_cls); + + + /** + * Lookup transfer details. Used if we still need to verify the details. + * + * @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 cb function to call with detailed transfer data * @param cb_cls closure for @a cb * @return transaction status @@ -1345,13 +1420,8 @@ struct TALER_MERCHANTDB_Plugin enum GNUNET_DB_QueryStatus (*lookup_transfer_details)( void *cls, - const char *instance_id, const char *exchange_url, - const char *payto_uri, const struct TALER_WireTransferIdentifierRawP *wtid, - const struct TALER_Amount *total_amount, - const struct TALER_Amount *wire_fee, - struct GNUNET_TIME_Absolute execution_time, TALER_MERCHANTDB_TransferDetailsCallback cb, void *cb_cls); |