diff --git a/contrib/gana b/contrib/gana index f2babbbdd..0e7707e44 160000 --- a/contrib/gana +++ b/contrib/gana @@ -1 +1 @@ -Subproject commit f2babbbdd477eeafb17292e16f335226ea02cb6a +Subproject commit 0e7707e441874b8aca9801b389d47003ac7a8f73 diff --git a/src/exchange/taler-exchange-httpd_purses_merge.c b/src/exchange/taler-exchange-httpd_purses_merge.c index 602463373..526238221 100644 --- a/src/exchange/taler-exchange-httpd_purses_merge.c +++ b/src/exchange/taler-exchange-httpd_purses_merge.c @@ -157,7 +157,9 @@ reply_merge_success (struct MHD_Connection *connection, &pcc->target_amount, &pcc->wf->wad)) { - GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Purse merged, balance of %s benefits exchange as it is below wad fee.\n", + TALER_amount2s (&pcc->target_amount)); return TALER_MHD_reply_with_ec ( connection, TALER_EC_EXCHANGE_PURSE_MERGE_WAD_FEE_EXCEEDS_PURSE_VALUE, @@ -217,13 +219,19 @@ merge_transaction (void *cls, struct PurseMergeContext *pcc = cls; enum GNUNET_DB_QueryStatus qs; bool in_conflict = true; + bool no_balance = true; + bool no_partner = true; qs = TEH_plugin->do_purse_merge (TEH_plugin->cls, pcc->purse_pub, &pcc->merge_sig, pcc->merge_timestamp, + &pcc->reserve_sig, pcc->provider_url, - &pcc->reserve_pub); + &pcc->reserve_pub, + &no_partner, + &no_balance, + &in_conflict); if (qs < 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -237,26 +245,65 @@ merge_transaction (void *cls, "purse merge"); return qs; } - - - qs = TEH_plugin->do_account_merge (TEH_plugin->cls, - pcc->purse_pub, - &pcc->reserve_pub, - &pcc->reserve_sig); - if (qs < 0) + if (no_partner) { - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - return qs; - TALER_LOG_WARNING ( - "Failed to store account purse information in database\n"); *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "account merge"); - return qs; + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_MERGE_PURSE_PARTNER_UNKNOWN, + pcc->provider_url); + return GNUNET_DB_STATUS_HARD_ERROR; } + if (no_balance) + { + *mhd_ret = + TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_EXCHANGE_PURSE_NOT_FULL, + NULL); + return GNUNET_DB_STATUS_HARD_ERROR; + } + if (in_conflict) + { + struct TALER_PurseMergeSignatureP merge_sig; + struct GNUNET_TIME_Timestamp merge_timestamp; + char *partner_url = NULL; + struct TALER_ReservePublicKeyP reserve_pub; + qs = TEH_plugin->select_purse_merge (TEH_plugin->cls, + pcc->purse_pub, + &merge_sig, + &merge_timestamp, + &partner_url, + &reserve_pub); + if (qs < 0) + { + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + return qs; + TALER_LOG_WARNING ( + "Failed to fetch merge purse information from database\n"); + *mhd_ret = + TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "select purse merge"); + return qs; + } + *mhd_ret = TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_CONFLICT, + GNUNET_JSON_pack_timestamp ("merge_timestamp", + merge_timestamp), + GNUNET_JSON_pack_data_auto ("merge_sig", + &merge_sig), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("partner_base_url", + partner_url)), + GNUNET_JSON_pack_data_auto ("reserve_pub", + &reserve_pub)); + GNUNET_free (partner_url); + return GNUNET_DB_STATUS_HARD_ERROR; + } return qs; } diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index b79c8dd89..9d3171c2e 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -3509,16 +3509,18 @@ prepare_statements (struct PostgresClosure *pg) /* Used in #postgres_do_purse_merge() */ GNUNET_PQ_make_prepare ( "call_purse_merge", - "SELECT 1" + "SELECT" + " out_no_partner AS no_partner" + ",out_no_balance AS no_balance" + ",out_conflict AS conflict" " FROM exchange_do_purse_merge" - " ($1, $2, $3, $4, $5);", - 5), + " ($1, $2, $3, $4, $5, $6);", + 6), /* Used in #postgres_select_purse_merge */ GNUNET_PQ_make_prepare ( "select_purse_merge", "SELECT " " reserve_pub" - ",purse_pub" ",merge_sig" ",merge_timestamp" ",partner_base_url" @@ -13467,9 +13469,13 @@ postgres_get_purse_deposit ( * @param purse_pub purse to merge * @param merge_sig signature affirming the merge * @param merge_timestamp time of the merge + * @param reserve_sig signature of the reserve affirming the merge * @param partner_url URL of the partner exchange, can be NULL if the reserves lives with us * @param reserve_pub public key of the reserve to credit - * @return transaction status code + * @param[out] no_partner set to true if @a partner_url is unknown + * @param[out] no_balance set to true if the @a purse_pub is not paid up yet + * @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already + * @return transaction status code */ static enum GNUNET_DB_QueryStatus postgres_do_purse_merge ( @@ -13477,11 +13483,37 @@ postgres_do_purse_merge ( const struct TALER_PurseContractPublicKeyP *purse_pub, const struct TALER_PurseMergeSignatureP *merge_sig, const struct GNUNET_TIME_Timestamp merge_timestamp, + const struct TALER_ReserveSignatureP *reserve_sig, const char *partner_url, - const struct TALER_ReservePublicKeyP *reserve_pub) + const struct TALER_ReservePublicKeyP *reserve_pub, + bool *no_partner, + bool *no_balance, + bool *in_conflict) { - GNUNET_break (0); // FIXME - return GNUNET_DB_STATUS_HARD_ERROR; + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (purse_pub), + GNUNET_PQ_query_param_auto_from_type (merge_sig), + GNUNET_PQ_query_param_timestamp (&merge_timestamp), + GNUNET_PQ_query_param_auto_from_type (reserve_sig), + GNUNET_PQ_query_param_string (partner_url), + GNUNET_PQ_query_param_auto_from_type (reserve_pub), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("no_partner", + no_partner), + GNUNET_PQ_result_spec_bool ("no_balance", + no_balance), + GNUNET_PQ_result_spec_bool ("conflict", + in_conflict), + GNUNET_PQ_result_spec_end + }; + + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "call_purse_merge", + params, + rs); } @@ -13506,8 +13538,31 @@ postgres_select_purse_merge ( char **partner_url, struct TALER_ReservePublicKeyP *reserve_pub) { - GNUNET_break (0); // FIXME - return GNUNET_DB_STATUS_HARD_ERROR; + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (purse_pub), + GNUNET_PQ_query_param_end + }; + bool is_null; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("merge_sig", + merge_sig), + GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", + reserve_pub), + GNUNET_PQ_result_spec_timestamp ("merge_timestamp", + merge_timestamp), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("partner_base_url", + partner_url), + &is_null), + GNUNET_PQ_result_spec_end + }; + + *partner_url = NULL; + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "select_purse_merge", + params, + rs); } diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 39fa2ecdf..18f2fd924 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -4628,14 +4628,19 @@ struct TALER_EXCHANGEDB_Plugin /** * Function called to approve merging a purse into a - * reserve by the respective purse merge key. + * reserve by the respective purse merge key. The purse + * must not have been merged into a different reserve. * * @param cls the @e cls of this struct with the plugin-specific state * @param purse_pub purse to merge * @param merge_sig signature affirming the merge * @param merge_timestamp time of the merge + * @param reserve_sig signature of the reserve affirming the merge * @param partner_url URL of the partner exchange, can be NULL if the reserves lives with us * @param reserve_pub public key of the reserve to credit + * @param[out] no_partner set to true if @a partner_url is unknown + * @param[out] no_balance set to true if the @a purse_pub is not paid up yet + * @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already * @return transaction status code */ enum GNUNET_DB_QueryStatus @@ -4644,8 +4649,12 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_PurseContractPublicKeyP *purse_pub, const struct TALER_PurseMergeSignatureP *merge_sig, const struct GNUNET_TIME_Timestamp merge_timestamp, + const struct TALER_ReserveSignatureP *reserve_sig, const char *partner_url, - const struct TALER_ReservePublicKeyP *reserve_pub); + const struct TALER_ReservePublicKeyP *reserve_pub, + bool *no_partner, + bool *no_balance, + bool *in_conflict); /**