diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c index bfe2112c4..bed2a7fb7 100644 --- a/src/exchange/taler-exchange-httpd_db.c +++ b/src/exchange/taler-exchange-httpd_db.c @@ -1209,417 +1209,4 @@ TEH_DB_execute_admin_add_incoming (struct MHD_Connection *connection, } -/** - * 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; -} - - -/** - * Closure for #handle_wtid_data. - */ -struct DepositWtidContext -{ - - /** - * Where should we send the reply? - */ - struct MHD_Connection *connection; - - /** - * Hash of the proposal data we are looking up. - */ - struct GNUNET_HashCode h_contract_terms; - - /** - * Hash of the wire transfer details we are looking up. - */ - struct GNUNET_HashCode h_wire; - - /** - * Public key we are looking up. - */ - struct TALER_CoinSpendPublicKeyP coin_pub; - - /** - * MHD result code to return. - */ - int res; -}; - - -/** - * Function called with the results of the lookup of the - * wire transfer identifier information. - * - * @param cls our context for transmission - * @param wtid raw wire transfer identifier, NULL - * if the transaction was not yet done - * @param coin_contribution how much did the coin we asked about - * contribute to the total transfer value? (deposit value including fee) - * @param coin_fee how much did the exchange charge for the deposit fee - * @param execution_time when was the transaction done, or - * when we expect it to be done (if @a wtid was NULL); - * #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown - * to the exchange - */ -static void -handle_wtid_data (void *cls, - const struct TALER_WireTransferIdentifierRawP *wtid, - const struct TALER_Amount *coin_contribution, - const struct TALER_Amount *coin_fee, - struct GNUNET_TIME_Absolute execution_time) -{ - struct DepositWtidContext *ctx = cls; - struct TALER_Amount coin_delta; - - if (NULL == wtid) - { - ctx->res = TEH_RESPONSE_reply_transfer_pending (ctx->connection, - execution_time); - } - else - { - if (GNUNET_SYSERR == - TALER_amount_subtract (&coin_delta, - coin_contribution, - coin_fee)) - { - GNUNET_break (0); - ctx->res = TEH_RESPONSE_reply_internal_db_error (ctx->connection, - TALER_EC_TRACK_TRANSACTION_DB_FEE_INCONSISTENT); - } - else - { - ctx->res = TEH_RESPONSE_reply_track_transaction (ctx->connection, - &ctx->h_contract_terms, - &ctx->h_wire, - &ctx->coin_pub, - &coin_delta, - wtid, - execution_time); - } - } -} - - -/** - * Execute a "/track/transaction". Returns the transfer information - * associated with the given deposit. - * - * @param connection the MHD connection to handle - * @param h_contract_terms hash of the proposal data - * @param h_wire hash of the wire details - * @param coin_pub public key of the coin to link - * @param merchant_pub public key of the merchant - * @return MHD result code - */ -int -TEH_DB_execute_track_transaction (struct MHD_Connection *connection, - const struct GNUNET_HashCode *h_contract_terms, - const struct GNUNET_HashCode *h_wire, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_MerchantPublicKeyP *merchant_pub) -{ - int ret; - struct DepositWtidContext ctx; - struct TALER_EXCHANGEDB_Session *session; - - 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.connection = connection; - ctx.h_contract_terms = *h_contract_terms; - ctx.h_wire = *h_wire; - ctx.coin_pub = *coin_pub; - ctx.res = GNUNET_SYSERR; - ret = TEH_plugin->wire_lookup_deposit_wtid (TEH_plugin->cls, - session, - h_contract_terms, - h_wire, - coin_pub, - merchant_pub, - &handle_wtid_data, - &ctx); - if (GNUNET_SYSERR == ret) - { - GNUNET_break (0); - GNUNET_break (GNUNET_SYSERR == ctx.res); - return TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_TRACK_TRANSACTION_DB_FETCH_FAILED); - } - if (GNUNET_NO == ret) - { - GNUNET_break (GNUNET_SYSERR == ctx.res); - return TEH_RESPONSE_reply_transaction_unknown (connection, - TALER_EC_TRACK_TRANSACTION_NOT_FOUND); - } - if (GNUNET_SYSERR == ctx.res) - { - GNUNET_break (0); - return TEH_RESPONSE_reply_internal_error (connection, - TALER_EC_TRACK_TRANSACTION_WTID_RESOLUTION_ERROR, - "bug resolving deposit wtid"); - } - return ctx.res; -} - - /* end of taler-exchange-httpd_db.c */ diff --git a/src/exchange/taler-exchange-httpd_db.h b/src/exchange/taler-exchange-httpd_db.h index 5e2a27a31..662f034de 100644 --- a/src/exchange/taler-exchange-httpd_db.h +++ b/src/exchange/taler-exchange-httpd_db.h @@ -197,60 +197,5 @@ TEH_DB_execute_admin_add_incoming (struct MHD_Connection *connection, const json_t *transfer_details); -/** - * 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); - - -/** - * Execute a "/track/transaction". Returns the transfer information - * associated with the given deposit. - * - * @param connection the MHD connection to handle - * @param h_contract_terms hash of the contract - * @param h_wire hash of the wire details - * @param coin_pub public key of the coin to link - * @param merchant_pub public key of the merchant - * @return MHD result code - */ -int -TEH_DB_execute_track_transaction (struct MHD_Connection *connection, - const struct GNUNET_HashCode *h_contract_terms, - const struct GNUNET_HashCode *h_wire, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_MerchantPublicKeyP *merchant_pub); - - -/** - * Execute a "/payback". The validity of the coin and signature have - * already been checked. The database must now check that the coin is - * not (double) spent, and execute the transaction (record details, - * generate success or failure response). - * - * @param connection the MHD connection to handle - * @param coin information about the coin - * @param value how much are coins of the @a coin's denomination worth? - * @param h_blind blinded coin to use for the lookup - * @param coin_blind blinding factor used (for later verification by the auditor) - * @param coin_sig signature of the coin - * @return MHD result code - */ -int -TEH_DB_execute_payback (struct MHD_Connection *connection, - const struct TALER_CoinPublicInfo *coin, - const struct TALER_Amount *value, - const struct GNUNET_HashCode *h_blind, - const struct TALER_DenominationBlindingKeyP *coin_blind, - const struct TALER_CoinSpendSignatureP *coin_sig); - - #endif /* TALER_EXCHANGE_HTTPD_DB_H */ diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 7dab61ed6..286572fea 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -1163,135 +1163,4 @@ TEH_RESPONSE_reply_transfer_pending (struct MHD_Connection *connection, } -/** - * A merchant asked for details about a deposit. Provide - * them. Generates the 200 reply. - * - * @param connection connection to the client - * @param h_contract_terms hash of the contract - * @param h_wire hash of wire account details - * @param coin_pub public key of the coin - * @param coin_contribution how much did the coin we asked about - * contribute to the total transfer value? (deposit value minus fee) - * @param wtid raw wire transfer identifier - * @param exec_time execution time of the wire transfer - * @return MHD result code - */ -int -TEH_RESPONSE_reply_track_transaction (struct MHD_Connection *connection, - const struct GNUNET_HashCode *h_contract_terms, - const struct GNUNET_HashCode *h_wire, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_Amount *coin_contribution, - const struct TALER_WireTransferIdentifierRawP *wtid, - struct GNUNET_TIME_Absolute exec_time) -{ - struct TALER_ConfirmWirePS cw; - struct TALER_ExchangePublicKeyP pub; - struct TALER_ExchangeSignatureP sig; - - cw.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE); - cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS)); - cw.h_wire = *h_wire; - cw.h_contract_terms = *h_contract_terms; - cw.wtid = *wtid; - cw.coin_pub = *coin_pub; - cw.execution_time = GNUNET_TIME_absolute_hton (exec_time); - TALER_amount_hton (&cw.coin_contribution, - coin_contribution); - TEH_KS_sign (&cw.purpose, - &pub, - &sig); - return TEH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_OK, - "{s:o, s:o, s:o, s:o, s:o}", - "wtid", GNUNET_JSON_from_data_auto (wtid), - "execution_time", GNUNET_JSON_from_time_abs (exec_time), - "coin_contribution", TALER_JSON_from_amount (coin_contribution), - "exchange_sig", GNUNET_JSON_from_data_auto (&sig), - "exchange_pub", GNUNET_JSON_from_data_auto (&pub)); -} - - -/** - * 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)); -} - - - /* end of taler-exchange-httpd_responses.c */ diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h index 8a51d8d53..5a52be38a 100644 --- a/src/exchange/taler-exchange-httpd_responses.h +++ b/src/exchange/taler-exchange-httpd_responses.h @@ -300,90 +300,6 @@ TEH_RESPONSE_reply_transfer_pending (struct MHD_Connection *connection, struct GNUNET_TIME_Absolute planned_exec_time); -/** - * A merchant asked for details about a deposit. Provide - * them. Generates the 200 reply. - * - * @param connection connection to the client - * @param h_contract_terms hash of the proposal data - * @param h_wire hash of wire account details - * @param coin_pub public key of the coin - * @param coin_contribution contribution of this coin to the total amount transferred - * @param wtid raw wire transfer identifier - * @param exec_time execution time of the wire transfer - * @return MHD result code - */ -int -TEH_RESPONSE_reply_track_transaction (struct MHD_Connection *connection, - const struct GNUNET_HashCode *h_contract_terms, - const struct GNUNET_HashCode *h_wire, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_Amount *coin_contribution, - const struct TALER_WireTransferIdentifierRawP *wtid, - struct GNUNET_TIME_Absolute exec_time); - - -/** - * 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); - - /** * Send a confirmation response to a "/refresh/melt" request. * diff --git a/src/exchange/taler-exchange-httpd_track_transaction.c b/src/exchange/taler-exchange-httpd_track_transaction.c index 9b2aaeca5..b617021eb 100644 --- a/src/exchange/taler-exchange-httpd_track_transaction.c +++ b/src/exchange/taler-exchange-httpd_track_transaction.c @@ -25,10 +25,214 @@ #include #include "taler_signatures.h" #include "taler-exchange-httpd_parsing.h" +#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_track_transaction.h" #include "taler-exchange-httpd_responses.h" +/** + * A merchant asked for details about a deposit. Provide + * them. Generates the 200 reply. + * + * @param connection connection to the client + * @param h_contract_terms hash of the contract + * @param h_wire hash of wire account details + * @param coin_pub public key of the coin + * @param coin_contribution how much did the coin we asked about + * contribute to the total transfer value? (deposit value minus fee) + * @param wtid raw wire transfer identifier + * @param exec_time execution time of the wire transfer + * @return MHD result code + */ +int +TEH_RESPONSE_reply_track_transaction (struct MHD_Connection *connection, + const struct GNUNET_HashCode *h_contract_terms, + const struct GNUNET_HashCode *h_wire, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *coin_contribution, + const struct TALER_WireTransferIdentifierRawP *wtid, + struct GNUNET_TIME_Absolute exec_time) +{ + struct TALER_ConfirmWirePS cw; + struct TALER_ExchangePublicKeyP pub; + struct TALER_ExchangeSignatureP sig; + + cw.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE); + cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS)); + cw.h_wire = *h_wire; + cw.h_contract_terms = *h_contract_terms; + cw.wtid = *wtid; + cw.coin_pub = *coin_pub; + cw.execution_time = GNUNET_TIME_absolute_hton (exec_time); + TALER_amount_hton (&cw.coin_contribution, + coin_contribution); + TEH_KS_sign (&cw.purpose, + &pub, + &sig); + return TEH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:o, s:o, s:o, s:o, s:o}", + "wtid", GNUNET_JSON_from_data_auto (wtid), + "execution_time", GNUNET_JSON_from_time_abs (exec_time), + "coin_contribution", TALER_JSON_from_amount (coin_contribution), + "exchange_sig", GNUNET_JSON_from_data_auto (&sig), + "exchange_pub", GNUNET_JSON_from_data_auto (&pub)); +} + + +/** + * Closure for #handle_wtid_data. + */ +struct DepositWtidContext +{ + + /** + * Where should we send the reply? + */ + struct MHD_Connection *connection; + + /** + * Hash of the proposal data we are looking up. + */ + struct GNUNET_HashCode h_contract_terms; + + /** + * Hash of the wire transfer details we are looking up. + */ + struct GNUNET_HashCode h_wire; + + /** + * Public key we are looking up. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * MHD result code to return. + */ + int res; +}; + + +/** + * Function called with the results of the lookup of the + * wire transfer identifier information. + * + * @param cls our context for transmission + * @param wtid raw wire transfer identifier, NULL + * if the transaction was not yet done + * @param coin_contribution how much did the coin we asked about + * contribute to the total transfer value? (deposit value including fee) + * @param coin_fee how much did the exchange charge for the deposit fee + * @param execution_time when was the transaction done, or + * when we expect it to be done (if @a wtid was NULL); + * #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown + * to the exchange + */ +static void +handle_wtid_data (void *cls, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *coin_fee, + struct GNUNET_TIME_Absolute execution_time) +{ + struct DepositWtidContext *ctx = cls; + struct TALER_Amount coin_delta; + + if (NULL == wtid) + { + ctx->res = TEH_RESPONSE_reply_transfer_pending (ctx->connection, + execution_time); + } + else + { + if (GNUNET_SYSERR == + TALER_amount_subtract (&coin_delta, + coin_contribution, + coin_fee)) + { + GNUNET_break (0); + ctx->res = TEH_RESPONSE_reply_internal_db_error (ctx->connection, + TALER_EC_TRACK_TRANSACTION_DB_FEE_INCONSISTENT); + } + else + { + ctx->res = TEH_RESPONSE_reply_track_transaction (ctx->connection, + &ctx->h_contract_terms, + &ctx->h_wire, + &ctx->coin_pub, + &coin_delta, + wtid, + execution_time); + } + } +} + + +/** + * Execute a "/track/transaction". Returns the transfer information + * associated with the given deposit. + * + * @param connection the MHD connection to handle + * @param h_contract_terms hash of the proposal data + * @param h_wire hash of the wire details + * @param coin_pub public key of the coin to link + * @param merchant_pub public key of the merchant + * @return MHD result code + */ +int +TEH_DB_execute_track_transaction (struct MHD_Connection *connection, + const struct GNUNET_HashCode *h_contract_terms, + const struct GNUNET_HashCode *h_wire, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_MerchantPublicKeyP *merchant_pub) +{ + int ret; + struct DepositWtidContext ctx; + struct TALER_EXCHANGEDB_Session *session; + + 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.connection = connection; + ctx.h_contract_terms = *h_contract_terms; + ctx.h_wire = *h_wire; + ctx.coin_pub = *coin_pub; + ctx.res = GNUNET_SYSERR; + ret = TEH_plugin->wire_lookup_deposit_wtid (TEH_plugin->cls, + session, + h_contract_terms, + h_wire, + coin_pub, + merchant_pub, + &handle_wtid_data, + &ctx); + if (GNUNET_SYSERR == ret) + { + GNUNET_break (0); + GNUNET_break (GNUNET_SYSERR == ctx.res); + return TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_TRACK_TRANSACTION_DB_FETCH_FAILED); + } + if (GNUNET_NO == ret) + { + GNUNET_break (GNUNET_SYSERR == ctx.res); + return TEH_RESPONSE_reply_transaction_unknown (connection, + TALER_EC_TRACK_TRANSACTION_NOT_FOUND); + } + if (GNUNET_SYSERR == ctx.res) + { + GNUNET_break (0); + return TEH_RESPONSE_reply_internal_error (connection, + TALER_EC_TRACK_TRANSACTION_WTID_RESOLUTION_ERROR, + "bug resolving deposit wtid"); + } + return ctx.res; +} + + /** * Check the merchant signature, and if it is valid, * return the wire transfer identifier. 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. *