From beea8eb383a4292b976c6c5d7356e6863e1adcbe Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 19 Jun 2017 18:06:51 +0200 Subject: refactor /track logic towards new structure --- src/exchange/taler-exchange-httpd_track_transfer.c | 379 +++++++++++++++++++++ 1 file changed, 379 insertions(+) (limited to 'src/exchange/taler-exchange-httpd_track_transfer.c') diff --git a/src/exchange/taler-exchange-httpd_track_transfer.c b/src/exchange/taler-exchange-httpd_track_transfer.c index f54df282d..1b9dd9fad 100644 --- a/src/exchange/taler-exchange-httpd_track_transfer.c +++ b/src/exchange/taler-exchange-httpd_track_transfer.c @@ -25,10 +25,389 @@ #include #include "taler_signatures.h" #include "taler-exchange-httpd_parsing.h" +#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_track_transfer.h" #include "taler-exchange-httpd_responses.h" +/** + * Detail for /wire/deposit response. + */ +struct TEH_TrackTransferDetail +{ + + /** + * We keep deposit details in a DLL. + */ + struct TEH_TrackTransferDetail *next; + + /** + * We keep deposit details in a DLL. + */ + struct TEH_TrackTransferDetail *prev; + + /** + * Hash of the proposal data. + */ + struct GNUNET_HashCode h_contract_terms; + + /** + * Coin's public key. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * Total value of the coin. + */ + struct TALER_Amount deposit_value; + + /** + * Fees charged by the exchange for the deposit. + */ + struct TALER_Amount deposit_fee; +}; + + +/** + * A merchant asked for transaction details about a wire transfer. + * Provide them. Generates the 200 reply. + * + * @param connection connection to the client + * @param total total amount that was transferred + * @param merchant_pub public key of the merchant + * @param h_wire destination account + * @param wire_fee wire fee that was charged + * @param exec_time execution time of the wire transfer + * @param wdd_head linked list with details about the combined deposits + * @return MHD result code + */ +int +TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection, + const struct TALER_Amount *total, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_wire, + const struct TALER_Amount *wire_fee, + struct GNUNET_TIME_Absolute exec_time, + const struct TEH_TrackTransferDetail *wdd_head) +{ + const struct TEH_TrackTransferDetail *wdd_pos; + json_t *deposits; + struct TALER_WireDepositDetailP dd; + struct GNUNET_HashContext *hash_context; + struct TALER_WireDepositDataPS wdp; + struct TALER_ExchangePublicKeyP pub; + struct TALER_ExchangeSignatureP sig; + + GNUNET_TIME_round_abs (&exec_time); + deposits = json_array (); + hash_context = GNUNET_CRYPTO_hash_context_start (); + for (wdd_pos = wdd_head; NULL != wdd_pos; wdd_pos = wdd_pos->next) + { + dd.h_contract_terms = wdd_pos->h_contract_terms; + dd.execution_time = GNUNET_TIME_absolute_hton (exec_time); + dd.coin_pub = wdd_pos->coin_pub; + TALER_amount_hton (&dd.deposit_value, + &wdd_pos->deposit_value); + TALER_amount_hton (&dd.deposit_fee, + &wdd_pos->deposit_fee); + GNUNET_CRYPTO_hash_context_read (hash_context, + &dd, + sizeof (struct TALER_WireDepositDetailP)); + GNUNET_assert (0 == + json_array_append_new (deposits, + json_pack ("{s:o, s:o, s:o, s:o}", + "h_contract_terms", GNUNET_JSON_from_data_auto (&wdd_pos->h_contract_terms), + "coin_pub", GNUNET_JSON_from_data_auto (&wdd_pos->coin_pub), + "deposit_value", TALER_JSON_from_amount (&wdd_pos->deposit_value), + "deposit_fee", TALER_JSON_from_amount (&wdd_pos->deposit_fee)))); + } + wdp.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT); + wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS)); + TALER_amount_hton (&wdp.total, + total); + TALER_amount_hton (&wdp.wire_fee, + wire_fee); + wdp.merchant_pub = *merchant_pub; + wdp.h_wire = *h_wire; + GNUNET_CRYPTO_hash_context_finish (hash_context, + &wdp.h_details); + TEH_KS_sign (&wdp.purpose, + &pub, + &sig); + return TEH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}", + "total", TALER_JSON_from_amount (total), + "wire_fee", TALER_JSON_from_amount (wire_fee), + "merchant_pub", GNUNET_JSON_from_data_auto (merchant_pub), + "H_wire", GNUNET_JSON_from_data_auto (h_wire), + "execution_time", GNUNET_JSON_from_time_abs (exec_time), + "deposits", deposits, + "exchange_sig", GNUNET_JSON_from_data_auto (&sig), + "exchange_pub", GNUNET_JSON_from_data_auto (&pub)); +} + + +/** + * Closure for #handle_transaction_data. + */ +struct WtidTransactionContext +{ + + /** + * Total amount of the wire transfer, as calculated by + * summing up the individual amounts. To be rounded down + * to calculate the real transfer amount at the end. + * Only valid if @e is_valid is #GNUNET_YES. + */ + struct TALER_Amount total; + + /** + * Public key of the merchant, only valid if @e is_valid + * is #GNUNET_YES. + */ + struct TALER_MerchantPublicKeyP merchant_pub; + + /** + * Which method was used to wire the funds? + */ + char *wire_method; + + /** + * Hash of the wire details of the merchant (identical for all + * deposits), only valid if @e is_valid is #GNUNET_YES. + */ + struct GNUNET_HashCode h_wire; + + /** + * Execution time of the wire transfer + */ + struct GNUNET_TIME_Absolute exec_time; + + /** + * Head of DLL with details for /wire/deposit response. + */ + struct TEH_TrackTransferDetail *wdd_head; + + /** + * Head of DLL with details for /wire/deposit response. + */ + struct TEH_TrackTransferDetail *wdd_tail; + + /** + * JSON array with details about the individual deposits. + */ + json_t *deposits; + + /** + * Initially #GNUNET_NO, if we found no deposits so far. Set to + * #GNUNET_YES if we got transaction data, and the database replies + * remained consistent with respect to @e merchant_pub and @e h_wire + * (as they should). Set to #GNUNET_SYSERR if we encountered an + * internal error. + */ + int is_valid; + +}; + + +/** + * Function called with the results of the lookup of the + * transaction data for the given wire transfer identifier. + * + * @param cls our context for transmission + * @param rowid which row in the DB is the information from (for diagnostics) + * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) + * @param wire_method which wire plugin was used + * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls) + * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls) + * @param h_contract_terms which proposal was this payment about + * @param coin_pub which public key was this payment about + * @param deposit_value amount contributed by this coin in total + * @param deposit_fee deposit fee charged by exchange for this coin + */ +static void +handle_transaction_data (void *cls, + uint64_t rowid, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const char *wire_method, + const struct GNUNET_HashCode *h_wire, + struct GNUNET_TIME_Absolute exec_time, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *deposit_value, + const struct TALER_Amount *deposit_fee) +{ + struct WtidTransactionContext *ctx = cls; + struct TALER_Amount delta; + struct TEH_TrackTransferDetail *wdd; + + if (GNUNET_SYSERR == ctx->is_valid) + return; + if (GNUNET_NO == ctx->is_valid) + { + ctx->merchant_pub = *merchant_pub; + ctx->h_wire = *h_wire; + ctx->exec_time = exec_time; + ctx->wire_method = GNUNET_strdup (wire_method); + ctx->is_valid = GNUNET_YES; + if (GNUNET_OK != + TALER_amount_subtract (&ctx->total, + deposit_value, + deposit_fee)) + { + GNUNET_break (0); + ctx->is_valid = GNUNET_SYSERR; + return; + } + } + else + { + if ( (0 != memcmp (&ctx->merchant_pub, + merchant_pub, + sizeof (struct TALER_MerchantPublicKeyP))) || + (0 != strcmp (wire_method, + ctx->wire_method)) || + (0 != memcmp (&ctx->h_wire, + h_wire, + sizeof (struct GNUNET_HashCode))) ) + { + GNUNET_break (0); + ctx->is_valid = GNUNET_SYSERR; + return; + } + if (GNUNET_OK != + TALER_amount_subtract (&delta, + deposit_value, + deposit_fee)) + { + GNUNET_break (0); + ctx->is_valid = GNUNET_SYSERR; + return; + } + if (GNUNET_OK != + TALER_amount_add (&ctx->total, + &ctx->total, + &delta)) + { + GNUNET_break (0); + ctx->is_valid = GNUNET_SYSERR; + return; + } + } + wdd = GNUNET_new (struct TEH_TrackTransferDetail); + wdd->deposit_value = *deposit_value; + wdd->deposit_fee = *deposit_fee; + wdd->h_contract_terms = *h_contract_terms; + wdd->coin_pub = *coin_pub; + GNUNET_CONTAINER_DLL_insert (ctx->wdd_head, + ctx->wdd_tail, + wdd); +} + + +/** + * Execute a "/track/transfer". Returns the transaction information + * associated with the given wire transfer identifier. + * + * @param connection the MHD connection to handle + * @param wtid wire transfer identifier to resolve + * @return MHD result code + */ +int +TEH_DB_execute_track_transfer (struct MHD_Connection *connection, + const struct TALER_WireTransferIdentifierRawP *wtid) +{ + int ret; + struct WtidTransactionContext ctx; + struct TALER_EXCHANGEDB_Session *session; + struct TEH_TrackTransferDetail *wdd; + struct GNUNET_TIME_Absolute wire_fee_start_date; + struct GNUNET_TIME_Absolute wire_fee_end_date; + struct TALER_Amount wire_fee; + struct TALER_MasterSignatureP wire_fee_master_sig; + + if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls))) + { + GNUNET_break (0); + return TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_DB_SETUP_FAILED); + } + ctx.is_valid = GNUNET_NO; + ctx.wdd_head = NULL; + ctx.wdd_tail = NULL; + ctx.wire_method = NULL; + ret = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls, + session, + wtid, + &handle_transaction_data, + &ctx); + if (GNUNET_SYSERR == ret) + { + GNUNET_break (0); + ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED); + goto cleanup; + } + if (GNUNET_SYSERR == ctx.is_valid) + { + GNUNET_break (0); + ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_TRACK_TRANSFER_DB_INCONSISTENT); + goto cleanup; + } + if (GNUNET_NO == ctx.is_valid) + { + ret = TEH_RESPONSE_reply_arg_unknown (connection, + TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND, + "wtid"); + goto cleanup; + } + if (GNUNET_OK != + TEH_plugin->get_wire_fee (TEH_plugin->cls, + session, + ctx.wire_method, + ctx.exec_time, + &wire_fee_start_date, + &wire_fee_end_date, + &wire_fee, + &wire_fee_master_sig)) + { + GNUNET_break (0); + ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND); + goto cleanup; + } + if (GNUNET_OK != + TALER_amount_subtract (&ctx.total, + &ctx.total, + &wire_fee)) + { + GNUNET_break (0); + ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT); + goto cleanup; + } + ret = TEH_RESPONSE_reply_track_transfer_details (connection, + &ctx.total, + &ctx.merchant_pub, + &ctx.h_wire, + &wire_fee, + ctx.exec_time, + ctx.wdd_head); + cleanup: + while (NULL != (wdd = ctx.wdd_head)) + { + GNUNET_CONTAINER_DLL_remove (ctx.wdd_head, + ctx.wdd_tail, + wdd); + GNUNET_free (wdd); + } + GNUNET_free_non_null (ctx.wire_method); + return ret; +} + + /** * Handle a "/track/transfer" request. * -- cgit v1.2.3