summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------contrib/merchant-backoffice0
-rw-r--r--src/backend/taler-merchant-httpd_private-delete-transfers-ID.c2
-rw-r--r--src/backend/taler-merchant-httpd_private-post-transfers.c154
-rw-r--r--src/backenddb/merchant-0001.sql2
-rw-r--r--src/backenddb/merchant-0002.sql22
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c152
-rw-r--r--src/backenddb/test_merchantdb.c4
-rw-r--r--src/include/taler_merchantdb_plugin.h12
-rwxr-xr-xsrc/testing/initialize_taler_system.sh13
-rwxr-xr-xsrc/testing/test_merchant_order_creation.sh121
10 files changed, 389 insertions, 93 deletions
diff --git a/contrib/merchant-backoffice b/contrib/merchant-backoffice
-Subproject fc9a68c973386afe4c83cf196555b6d01f10a4f
+Subproject 1732185ac1d1dcc783b8f2489f2ce333b5254d9
diff --git a/src/backend/taler-merchant-httpd_private-delete-transfers-ID.c b/src/backend/taler-merchant-httpd_private-delete-transfers-ID.c
index fd5c968a..93656f4a 100644
--- a/src/backend/taler-merchant-httpd_private-delete-transfers-ID.c
+++ b/src/backend/taler-merchant-httpd_private-delete-transfers-ID.c
@@ -24,7 +24,7 @@
/**
- * Handle a DELETE "/transfers/$ID" request.
+ * Handle a DELETE "/private/transfers/$ID" request.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
diff --git a/src/backend/taler-merchant-httpd_private-post-transfers.c b/src/backend/taler-merchant-httpd_private-post-transfers.c
index 4fd0819d..5ac4ec9d 100644
--- a/src/backend/taler-merchant-httpd_private-post-transfers.c
+++ b/src/backend/taler-merchant-httpd_private-post-transfers.c
@@ -146,6 +146,10 @@ struct PostTransfersContext
*/
bool downloaded;
+ /**
+ * Are we currently suspended?
+ */
+ bool suspended;
};
@@ -170,6 +174,7 @@ TMH_force_post_transfers_resume ()
GNUNET_CONTAINER_DLL_remove (ptc_head,
ptc_tail,
ptc);
+ ptc->suspended = false;
MHD_resume_connection (ptc->connection);
if (NULL != ptc->timeout_task)
{
@@ -207,6 +212,7 @@ resume_transfer_with_response (struct PostTransfersContext *ptc,
GNUNET_CONTAINER_DLL_remove (ptc_head,
ptc_tail,
ptc);
+ ptc->suspended = false;
MHD_resume_connection (ptc->connection);
TMH_trigger_daemon (); /* we resumed, kick MHD */
}
@@ -468,8 +474,22 @@ wire_transfer_cb (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got response code %u from exchange for GET /transfers/$WTID\n",
hr->http_status);
- if (MHD_HTTP_OK != 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 (
+ "{s:I, s:I, s:I}",
+ "code",
+ (json_int_t) TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_EXCHANGE_UNKNOWN,
+ "exchange_code", (json_int_t) hr->ec,
+ "exchange_http_status", (json_int_t) hr->http_status));
+ return;
+ default:
resume_transfer_with_response (
ptc,
MHD_HTTP_BAD_GATEWAY,
@@ -482,7 +502,6 @@ wire_transfer_cb (void *cls,
"exchange_reply", hr->reply));
return;
}
-
TMH_db->preflight (TMH_db->cls);
/* Ok, exchange answer is acceptable, store it */
qs = TMH_db->insert_transfer_details (TMH_db->cls,
@@ -504,6 +523,29 @@ wire_transfer_cb (void *cls,
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 (&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,
@@ -536,6 +578,8 @@ process_transfer_with_exchange (void *cls,
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,
@@ -584,6 +628,8 @@ process_transfer_with_exchange (void *cls,
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,
@@ -664,7 +710,7 @@ verify_exchange_claim_cb (void *cls,
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 NULL");
+ "check_transfer_result must not be GNUNET_NO");
return;
case GNUNET_SYSERR:
/* #check_transfer() failed, report conflict! */
@@ -864,6 +910,7 @@ download (struct PostTransfersContext *ptc)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Suspending POST /private/transfers handling while working with exchange\n");
MHD_suspend_connection (ptc->connection);
+ ptc->suspended = true;
GNUNET_CONTAINER_DLL_insert (ptc_head,
ptc_tail,
ptc);
@@ -895,12 +942,14 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh,
hc->ctx = ptc;
hc->cc = &transfer_cleanup;
}
+ /* 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 (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 */
@@ -939,6 +988,11 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh,
TALER_EC_GENERIC_CURRENCY_MISMATCH,
TMH_currency);
}
+ 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);
}
/* Check if transfer data is in database, if not, add it. */
@@ -946,8 +1000,10 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh,
{
struct GNUNET_TIME_Absolute 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 !=
@@ -966,7 +1022,9 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh,
&ptc->wtid,
&total_amount,
&wire_fee,
+ &exchange_amount,
&execution_time,
+ &have_exchange_sig,
&verified);
switch (qs)
{
@@ -982,7 +1040,9 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh,
TMH_db->rollback (TMH_db->cls);
continue;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- /* Transfer so far unknown */
+ /* 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;
@@ -1019,6 +1079,8 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh,
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,
@@ -1030,6 +1092,8 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh,
{
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);
@@ -1039,12 +1103,13 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh,
TALER_EC_GENERIC_DB_STORE_FAILED,
"transfer");
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- /* transfer present, but transfer _details_
- missing (hence the SELECT returned an empty
- set after intersection via USING part of the
- query). Fine, we still need to talk to the
- exchange! */
- break;
+ 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:
break;
}
@@ -1069,21 +1134,36 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh,
break;
}
download (ptc);
- return MHD_YES;
+ 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\n");
+ "Transfer exists in DB (verified: %s, exchange signature: %s)\n",
+ verified ? "true" : "false",
+ have_exchange_sig ? "true" : "false");
if (! verified)
{
- if (! ptc->downloaded)
+ 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;
+ 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 ==
@@ -1135,6 +1215,52 @@ TMH_private_post_transfers (const struct TMH_RequestHandler *rh,
TMH_db->rollback (TMH_db->cls);
continue;
}
+
+ {
+ struct TALER_Amount delta;
+
+ if (0 >
+ TALER_amount_subtract (&delta,
+ &total_amount,
+ &wire_fee))
+ {
+ GNUNET_break (0);
+ 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));
+ 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));
+ 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,
diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql
index 6e79c7ff..74144e86 100644
--- a/src/backenddb/merchant-0001.sql
+++ b/src/backenddb/merchant-0001.sql
@@ -368,7 +368,7 @@ COMMENT ON COLUMN merchant_transfers.verified
COMMENT ON COLUMN merchant_transfers.confirmed
IS 'true once the merchant confirmed that this transfer was received';
COMMENT ON COLUMN merchant_transfers.credit_amount_val
- IS 'actual value of the (aggregated) wire transfer, excluding the wire fee';
+ IS 'actual value of the (aggregated) wire transfer, excluding the wire fee, according to the merchant';
CREATE TABLE IF NOT EXISTS merchant_transfer_signatures
(credit_serial BIGINT PRIMARY KEY
diff --git a/src/backenddb/merchant-0002.sql b/src/backenddb/merchant-0002.sql
index 13c1f797..46428dd9 100644
--- a/src/backenddb/merchant-0002.sql
+++ b/src/backenddb/merchant-0002.sql
@@ -31,6 +31,7 @@ COMMENT ON COLUMN merchant_instances.auth_salt
IS 'salt to use when hashing Authorization header before comparing with auth_hash';
+
-- need to preserve payto_uri for extended reserve API (easier than to reconstruct)
ALTER TABLE merchant_tip_reserve_keys
ADD COLUMN payto_uri VARCHAR;
@@ -38,5 +39,26 @@ COMMENT ON COLUMN merchant_tip_reserve_keys.payto_uri
IS 'payto:// URI used to fund the reserve, may be NULL once reserve is funded';
+-- need serial IDs on various tables for exchange-auditor replication
+ALTER TABLE merchant_transfer_signatures
+ ADD COLUMN credit_amount_val INT8,
+ ADD COLUMN credit_amount_frac INT4;
+COMMENT ON COLUMN merchant_transfers.credit_amount_val
+ IS 'actual value of the (aggregated) wire transfer, excluding the wire fee, according to the exchange';
+
+
+-- support different amounts claimed by exchange and merchant about wire transfers,
+-- add column to tell when this happens; but "believe" existing amounts match, as
+-- otherwise earlier version of the code would have failed hard.
+UPDATE merchant_transfer_signatures
+ SET credit_amount_val=mt.credit_amount_val,
+ credit_amount_frac=mt.credit_amount_frac
+ FROM merchant_transfer_signatures mts
+ INNER JOIN merchant_transfers mt USING(credit_serial);
+ALTER TABLE merchant_transfer_signatures
+ ALTER COLUMN credit_amount_val SET NOT NULL,
+ ALTER COLUMN credit_amount_frac SET NOT NULL;
+
+
-- Complete transaction
COMMIT;
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
index d7621224..e8ba1b44 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -3525,7 +3525,9 @@ postgres_lookup_account (void *cls,
* @param payto_uri what is the merchant's bank account that received the transfer
* @param wtid identifier of the wire transfer
* @param td transfer details to store
- * @return transaction status
+ * @return transaction status,
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the @a wtid and @a exchange_uri are not known for this @a instance_id
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT on success
*/
static enum GNUNET_DB_QueryStatus
postgres_insert_transfer_details (
@@ -3561,7 +3563,6 @@ RETRY:
GNUNET_PQ_query_param_string (payto_uri),
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_auto_from_type (wtid),
- TALER_PQ_query_param_amount (&td->total_amount), /* excludes wire fee */
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@@ -3580,12 +3581,19 @@ RETRY:
postgres_rollback (pg);
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'lookup_credit_serial' for account %s and amount %s failed with status %d\n",
+ payto_uri,
+ TALER_amount2s (&td->total_amount),
+ qs);
return qs;
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
- /* account does not exists, fail! */
postgres_rollback (pg);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'lookup_credit_serial' for account %s failed with transfer unknown\n",
+ payto_uri);
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
}
@@ -3594,6 +3602,7 @@ RETRY:
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&credit_serial),
+ TALER_PQ_query_param_amount (&td->total_amount),
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),
@@ -3610,11 +3619,24 @@ RETRY:
postgres_rollback (pg);
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'insert_transfer_signature' failed with status %d\n",
+ qs);
return qs;
}
+ if (0 == qs)
+ {
+ postgres_rollback (pg);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'insert_transfer_signature' failed with status %d\n",
+ qs);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
}
/* Update transfer-coin association table */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Updating transfer-coin association table\n");
for (unsigned int i = 0; i<td->details_length; i++)
{
const struct TALER_TrackTransferDetails *d = &td->details[i];
@@ -3638,6 +3660,9 @@ RETRY:
postgres_rollback (pg);
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'insert_transfer_to_coin_mapping' failed with status %d\n",
+ qs);
return qs;
}
}
@@ -3645,6 +3670,8 @@ RETRY:
that were wired, set the respective order's "wired" status to
true, *if* all other deposited coins associated with that order
have also been wired (this time or earlier) */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Updating contract terms 'wired' status\n");
for (unsigned int i = 0; i<td->details_length; i++)
{
const struct TALER_TrackTransferDetails *d = &td->details[i];
@@ -3662,9 +3689,14 @@ RETRY:
postgres_rollback (pg);
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'update_wired_by_coin_pub' failed with status %d\n",
+ qs);
return qs;
}
}
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Committing transaction...\n");
qs = postgres_commit (pg);
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
@@ -3898,8 +3930,10 @@ postgres_lookup_deposits_by_contract_and_coin (
* @param[out] total_amount amount that was debited from our
* aggregate balance at the exchange (in total, sum of
* the wire transfer amount and the @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] wire_fee the wire fee the exchange charged (only set if @a have_exchange_sig is true)
+ * @param[out] exchange_amount the amount the exchange claims was transferred (only set if @a have_exchange_sig is true)
+ * @param[out] execution_time when the transfer was executed by the exchange (only set if @a have_exchange_sig is true)
+ * @param[out] have_exchange_sig do we have a response from the exchange about this transfer
* @param[out] verified did we confirm the transfer was OK
* @return transaction status
*/
@@ -3911,7 +3945,9 @@ postgres_lookup_transfer (
const struct TALER_WireTransferIdentifierRawP *wtid,
struct TALER_Amount *total_amount,
struct TALER_Amount *wire_fee,
+ struct TALER_Amount *exchange_amount,
struct GNUNET_TIME_Absolute *execution_time,
+ bool *have_exchange_sig,
bool *verified)
{
struct PostgresClosure *pg = cls;
@@ -3923,14 +3959,23 @@ postgres_lookup_transfer (
};
uint8_t verified8;
/** Amount we got actually credited, _excludes_ the wire fee */
+ bool no_sig;
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_allow_null (
+ TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
+ wire_fee),
+ &no_sig),
+ GNUNET_PQ_result_spec_allow_null (
+ TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_amount",
+ exchange_amount),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_absolute_time ("execution_time",
+ execution_time),
+ NULL),
GNUNET_PQ_result_spec_auto_from_type ("verified",
&verified8),
GNUNET_PQ_result_spec_end
@@ -3938,22 +3983,33 @@ postgres_lookup_transfer (
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
+ *execution_time = GNUNET_TIME_UNIT_ZERO_ABS;
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_transfer",
params,
rs);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Lookup transfer returned %d\n",
+ qs);
if (qs > 0)
{
+ *have_exchange_sig = ! no_sig;
*verified = (0 != verified8);
- if (0 >
- TALER_amount_add (total_amount,
- &credit_amount,
- wire_fee))
+ if ( (! no_sig) &&
+ (0 >
+ TALER_amount_add (total_amount,
+ &credit_amount,
+ wire_fee)) )
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
+ else
+ {
+ *verified = false;
+ *have_exchange_sig = false;
+ }
return qs;
}
@@ -7814,8 +7870,6 @@ postgres_connect (void *cls)
" FROM merchant_transfers"
" WHERE exchange_url=$1"
" AND wtid=$4"
- " AND credit_amount_val=$5"
- " AND credit_amount_frac=$6"
" AND account_serial="
" (SELECT account_serial"
" FROM merchant_accounts"
@@ -7825,22 +7879,24 @@ postgres_connect (void *cls)
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$3))",
- 2),
+ 4),
/* for postgres_insert_transfer_details() */
GNUNET_PQ_make_prepare ("insert_transfer_signature",
"INSERT INTO merchant_transfer_signatures"
"(credit_serial"
",signkey_serial"
+ ",credit_amount_val"
+ ",credit_amount_frac"
",wire_fee_val"
",wire_fee_frac"
",execution_time"
",exchange_sig) "
- "SELECT $1, signkey_serial, $2, $3, $4, $5"
+ "SELECT $1, signkey_serial, $2, $3, $4, $5, $6, $7"
" FROM merchant_exchange_signing_keys"
- " WHERE exchange_pub=$6"
+ " WHERE exchange_pub=$8"
" ORDER BY start_date DESC"
" LIMIT 1",
- 6),
+ 8),
/* for postgres_insert_transfer_details() */
GNUNET_PQ_make_prepare ("insert_transfer_to_coin_mapping",
"INSERT INTO merchant_transfer_to_coin"
@@ -7925,16 +7981,18 @@ postgres_connect (void *cls)
/* for postgres_lookup_transfer() */
GNUNET_PQ_make_prepare ("lookup_transfer",
"SELECT"
- " credit_amount_val"
- ",credit_amount_frac"
+ " mt.credit_amount_val AS credit_amount_val"
+ ",mt.credit_amount_frac AS credit_amount_frac"
+ ",mts.credit_amount_val AS exchange_amount_val"
+ ",mts.credit_amount_frac AS exchange_amount_frac"
",wire_fee_val"
",wire_fee_frac"
",execution_time"
",verified"
- " FROM merchant_transfers"
- " JOIN merchant_transfer_signatures USING (credit_serial)"
+ " FROM merchant_transfers mt"
" JOIN merchant_accounts USING (account_serial)"
" JOIN merchant_instances USING (merchant_serial)"
+ " LEFT JOIN merchant_transfer_signatures mts USING (credit_serial)"
" WHERE wtid=$2"
" AND exchange_url=$1"
" AND merchant_id=$3;",
@@ -7981,8 +8039,8 @@ postgres_connect (void *cls)
/* for postgres_lookup_transfers() */
GNUNET_PQ_make_prepare ("lookup_transfers_time_payto_asc",
"SELECT"
- " credit_amount_val"
- ",credit_amount_frac"
+ " mt.credit_amount_val"
+ ",mt.credit_amount_frac"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -7990,7 +8048,7 @@ postgres_connect (void *cls)
",merchant_transfer_signatures.execution_time"
",verified"
",confirmed"
- " FROM merchant_transfers"
+ " FROM merchant_transfers mt"
" JOIN merchant_accounts USING (account_serial)"
" JOIN merchant_transfer_signatures USING (credit_serial)"
" WHERE execution_time < $2"
@@ -8007,8 +8065,8 @@ postgres_connect (void *cls)
/* for postgres_lookup_transfers() */
GNUNET_PQ_make_prepare ("lookup_transfers_time_asc",
"SELECT"
- " credit_amount_val"
- ",credit_amount_frac"
+ " mt.credit_amount_val"
+ ",mt.credit_amount_frac"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -8016,7 +8074,7 @@ postgres_connect (void *cls)
",merchant_transfer_signatures.execution_time"
",verified"
",confirmed"
- " FROM merchant_transfers"
+ " FROM merchant_transfers mt"
" JOIN merchant_accounts USING (account_serial)"
" JOIN merchant_transfer_signatures USING (credit_serial)"
" WHERE execution_time < $2"
@@ -8032,8 +8090,8 @@ postgres_connect (void *cls)
/* for postgres_lookup_transfers() */
GNUNET_PQ_make_prepare ("lookup_transfers_payto_asc",
"SELECT"
- " credit_amount_val"
- ",credit_amount_frac"
+ " mt.credit_amount_val"
+ ",mt.credit_amount_frac"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -8044,7 +8102,7 @@ postgres_connect (void *cls)
" END AS execution_time"
",verified"
",confirmed"
- " FROM merchant_transfers"
+ " FROM merchant_transfers mt"
" JOIN merchant_accounts USING (account_serial)"
" LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
" WHERE credit_serial > $2"
@@ -8059,8 +8117,8 @@ postgres_connect (void *cls)
/* for postgres_lookup_transfers() */
GNUNET_PQ_make_prepare ("lookup_transfers_asc",
"SELECT"
- " credit_amount_val"
- ",credit_amount_frac"
+ " mt.credit_amount_val"
+ ",mt.credit_amount_frac"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -8071,7 +8129,7 @@ postgres_connect (void *cls)
" END AS execution_time"
",verified"
",confirmed"
- " FROM merchant_transfers"
+ " FROM merchant_transfers mt"
" JOIN merchant_accounts USING (account_serial)"
" LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
" WHERE credit_serial > $2"
@@ -8085,8 +8143,8 @@ postgres_connect (void *cls)
/* for postgres_lookup_transfers() */
GNUNET_PQ_make_prepare ("lookup_transfers_time_payto_desc",
"SELECT"
- " credit_amount_val"
- ",credit_amount_frac"
+ " mt.credit_amount_val"
+ ",mt.credit_amount_frac"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -8094,7 +8152,7 @@ postgres_connect (void *cls)
",merchant_transfer_signatures.execution_time"
",verified"
",confirmed"
- " FROM merchant_transfers"
+ " FROM merchant_transfers mt"
" JOIN merchant_accounts USING (account_serial)"
" JOIN merchant_transfer_signatures USING (credit_serial)"
" WHERE execution_time < $2"
@@ -8111,8 +8169,8 @@ postgres_connect (void *cls)
/* for postgres_lookup_transfers() */
GNUNET_PQ_make_prepare ("lookup_transfers_time_desc",
"SELECT"
- " credit_amount_val"
- ",credit_amount_frac"
+ " mt.credit_amount_val"
+ ",mt.credit_amount_frac"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -8120,7 +8178,7 @@ postgres_connect (void *cls)
",merchant_transfer_signatures.execution_time"
",verified"
",confirmed"
- " FROM merchant_transfers"
+ " FROM merchant_transfers mt"
" JOIN merchant_accounts USING (account_serial)"
" JOIN merchant_transfer_signatures USING (credit_serial)"
" WHERE execution_time < $2"
@@ -8136,8 +8194,8 @@ postgres_connect (void *cls)
/* for postgres_lookup_transfers() */
GNUNET_PQ_make_prepare ("lookup_transfers_payto_desc",
"SELECT"
- " credit_amount_val"
- ",credit_amount_frac"
+ " mt.credit_amount_val"
+ ",mt.credit_amount_frac"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -8148,7 +8206,7 @@ postgres_connect (void *cls)
" END AS execution_time"
",verified"
",confirmed"
- " FROM merchant_transfers"
+ " FROM merchant_transfers mt"
" JOIN merchant_accounts USING (account_serial)"
" LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
" WHERE credit_serial < $2"
@@ -8163,8 +8221,8 @@ postgres_connect (void *cls)
/* for postgres_lookup_transfers() */
GNUNET_PQ_make_prepare ("lookup_transfers_desc",
"SELECT"
- " credit_amount_val"
- ",credit_amount_frac"
+ " mt.credit_amount_val"
+ ",mt.credit_amount_frac"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -8175,7 +8233,7 @@ postgres_connect (void *cls)
" END AS execution_time"
",verified"
",confirmed"
- " FROM merchant_transfers"
+ " FROM merchant_transfers mt"
" JOIN merchant_accounts USING (account_serial)"
" LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
" WHERE credit_serial < $2"
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index aeb4fca3..b3b43062 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -3382,7 +3382,9 @@ test_lookup_transfer (
struct TALER_Amount total_with_fee;
struct TALER_Amount total;
struct TALER_Amount fee;
+ struct TALER_Amount exchange_amount;
struct GNUNET_TIME_Absolute time;
+ bool esig;
bool verified;
if (1 != plugin->lookup_transfer (plugin->cls,
@@ -3391,7 +3393,9 @@ test_lookup_transfer (
&transfer->wtid,
&total,
&fee,
+ &exchange_amount,
&time,
+ &esig,
&verified))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h
index 594e3876..beeee462 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -1613,7 +1613,9 @@ struct TALER_MERCHANTDB_Plugin
* @param payto_uri what is the merchant's bank account that received the transfer
* @param wtid identifier of the wire transfer
* @param td transfer details to store
- * @return transaction status
+ * @return transaction status,
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the @a wtid and @a exchange_uri are not known for this @a instance_id
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT on success
*/
enum GNUNET_DB_QueryStatus
(*insert_transfer_details)(
@@ -1686,8 +1688,10 @@ struct TALER_MERCHANTDB_Plugin
* @param[out] total_amount amount that was debited from our
* aggregate balance at the exchange (in total, sum of
* the wire transfer amount and the @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] wire_fee the wire fee the exchange charged (only set if @a have_exchange_sig is true)
+ * @param[out] exchange_amount the amount the exchange claims was transferred (only set if @a have_exchange_sig is true)
+ * @param[out] execution_time when the transfer was executed by the exchange (only set if @a have_exchange_sig is true)
+ * @param[out] have_exchange_sig do we have a response from the exchange about this transfer
* @param[out] verified did we confirm the transfer was OK
* @return transaction status
*/
@@ -1699,7 +1703,9 @@ struct TALER_MERCHANTDB_Plugin
const struct TALER_WireTransferIdentifierRawP *wtid,
struct TALER_Amount *total_amount,
struct TALER_Amount *wire_fee,
+ struct TALER_Amount *exchange_amount,
struct GNUNET_TIME_Absolute *execution_time,
+ bool *have_exchange_sig,
bool *verified);
diff --git a/src/testing/initialize_taler_system.sh b/src/testing/initialize_taler_system.sh
index 277d55e6..c4127684 100755
--- a/src/testing/initialize_taler_system.sh
+++ b/src/testing/initialize_taler_system.sh
@@ -20,6 +20,9 @@ function exit_fail() {
# Cleanup to run whenever we exit
function cleanup()
{
+ # kill main HTTP servers first
+ kill ${MERCHANT_HTTPD_PID:-X} &> /dev/null || true
+ kill ${EXCHANGE_HTTPD_PID:-X} &> /dev/null || true
for n in `jobs -p`
do
kill $n 2> /dev/null || true
@@ -108,10 +111,12 @@ echo " OK"
# Launch services
echo -n "Launching taler services ..."
taler-bank-manage-testing $CONF postgres:///$TALER_DB serve > taler-bank.log 2> taler-bank.err &
-taler-exchange-secmod-eddsa -c $CONF 2> taler-exchange-secmod-eddsa.log &
-taler-exchange-secmod-rsa -c $CONF 2> taler-exchange-secmod-rsa.log &
-taler-exchange-httpd -c $CONF 2> taler-exchange-httpd.log &
-taler-merchant-httpd -c $CONF -L INFO 2> taler-merchant-httpd.log &
+taler-exchange-secmod-eddsa -c $CONF -L DEBUG 2> taler-exchange-secmod-eddsa.log &
+taler-exchange-secmod-rsa -c $CONF -L DEBUG 2> taler-exchange-secmod-rsa.log &
+taler-exchange-httpd -c $CONF -L DEBUG 2> taler-exchange-httpd.log &
+EXCHANGE_HTTPD_PID=$!
+taler-merchant-httpd -c $CONF -L DEBUG 2> taler-merchant-httpd.log &
+MERCHANT_HTTPD_PID=$!
taler-exchange-wirewatch -c $CONF 2> taler-exchange-wirewatch.log &
taler-auditor-httpd -L INFO -c $CONF 2> taler-auditor-httpd.log &
diff --git a/src/testing/test_merchant_order_creation.sh b/src/testing/test_merchant_order_creation.sh
index bbababf0..f596666d 100755
--- a/src/testing/test_merchant_order_creation.sh
+++ b/src/testing/test_merchant_order_creation.sh
@@ -234,11 +234,11 @@ TO_SLEEP=`echo $(( ($WIRE_DEADLINE /1000) - $NOW ))`
echo waiting $TO_SLEEP secs for wire transfer
echo -n "Perform wire transfers ..."
-taler-exchange-aggregator -c $CONF -T ${TO_SLEEP}000000 -t -L INFO
-taler-exchange-transfer -c $CONF -t -L INFO
+taler-exchange-aggregator -c $CONF -T ${TO_SLEEP}000000 -t -L INFO &> aggregator.log
+taler-exchange-transfer -c $CONF -t -L INFO &> transfer.log
echo " DONE"
-echo -n "Notifying merchant of wire transfer ..."
+echo -n "Obtaining wire transfer details from bank..."
# First, extract the wire transfer data from the bank.
# As there is no "nice" API, we do this by dumping the
@@ -260,20 +260,85 @@ then
exit_fail "Wrong exchange URL in subject '$SUBJECT', expected $EXCHANGE_URL"
fi
+echo " OK"
+
set +e
+export TARGET_PAYTO
+export WURL
+export WTID
+export CREDIT_AMOUNT
+export LAST_RESPONSE
+
+echo -n "Notifying merchant of bogus wire transfer ..."
+
STATUS=$(curl 'http://localhost:9966/instances/default/private/transfers' \
-d '{"credit_amount":"'$CREDIT_AMOUNT'1","wtid":"'$WTID'","payto_uri":"'$TARGET_PAYTO'","exchange_url":"'$WURL'"}' \
-m 3 \
-w "%{http_code}" -s -o $LAST_RESPONSE)
-set -e
+if [ "$STATUS" != "409" ]
+then
+ jq . < $LAST_RESPONSE
+ exit_fail "Expected to fail since the amount is not valid. got: $STATUS"
+fi
+
+echo "OK"
+echo -n "Notifying merchant of bogus wire transfer AGAIN ..."
+
+STATUS=$(curl 'http://localhost:9966/instances/default/private/transfers' \
+ -d '{"credit_amount":"'$CREDIT_AMOUNT'1","wtid":"'$WTID'","payto_uri":"'$TARGET_PAYTO'","exchange_url":"'$WURL'"}' \
+ -m 3 \
+ -w "%{http_code}" -s -o $LAST_RESPONSE)
-if [ "$STATUS" != "000" ]
+if [ "$STATUS" != "409" ]
then
+ jq . < $LAST_RESPONSE
exit_fail "Expected to fail since the amount is not valid. got: $STATUS"
fi
+echo " OK"
+echo -n "Notifying merchant of correct wire transfer (conflicting with old data)..."
+
+STATUS=$(curl 'http://localhost:9966/instances/default/private/transfers' \
+ -d '{"credit_amount":"'$CREDIT_AMOUNT'","wtid":"'$WTID'","payto_uri":"'$TARGET_PAYTO'","exchange_url":"'$WURL'"}' \
+ -m 3 \
+ -w "%{http_code}" -s -o $LAST_RESPONSE)
+
+if [ "$STATUS" != "409" ]
+then
+ jq . < $LAST_RESPONSE
+ exit_fail "Expected response conflict, after providing conflicting transfer data. got: $STATUS"
+fi
+
+echo " OK"
+
+echo -n "Deleting bogus wire transfer ..."
+
+TID=`curl -s http://localhost:9966/instances/default/private/transfers | jq -r .transfers[0].transfer_serial_id`
+STATUS=$(curl -H "Content-Type: application/json" -X DELETE \
+ "http://localhost:9966/instances/default/private/transfers/$TID" \
+ -w "%{http_code}" -s -o $LAST_RESPONSE)
+
+if [ "$STATUS" != "204" ]
+then
+ jq . < $LAST_RESPONSE
+ exit_fail "Expected response 204 No Content, after deleting valid TID. got: $STATUS"
+fi
+
+STATUS=$(curl -H "Content-Type: application/json" -X DELETE \
+ "http://localhost:9966/instances/default/private/transfers/$TID" \
+ -w "%{http_code}" -s -o $LAST_RESPONSE)
+if [ "$STATUS" != "404" ]
+then
+ jq . < $LAST_RESPONSE
+ exit_fail "Expected response 404 Not found, after deleting TID again. got: $STATUS"
+fi
+
+echo " OK"
+
+echo -n "Notifying merchant of correct wire transfer (now working)..."
+
STATUS=$(curl 'http://localhost:9966/instances/default/private/transfers' \
-d '{"credit_amount":"'$CREDIT_AMOUNT'","wtid":"'$WTID'","payto_uri":"'$TARGET_PAYTO'","exchange_url":"'$WURL'"}' \
-m 3 \
@@ -281,10 +346,14 @@ STATUS=$(curl 'http://localhost:9966/instances/default/private/transfers' \
if [ "$STATUS" != "200" ]
then
- echo `cat $LAST_RESPONSE`
+ jq . < $LAST_RESPONSE
exit_fail "Expected response ok, after providing transfer data. got: $STATUS"
fi
+echo " OK"
+echo -n "Testing idempotence ..."
+set -e
+
# Test idempotence: do it again!
STATUS=$(curl 'http://localhost:9966/instances/default/private/transfers' \
@@ -293,10 +362,12 @@ STATUS=$(curl 'http://localhost:9966/instances/default/private/transfers' \
if [ "$STATUS" != "200" ]
then
- echo `cat $LAST_RESPONSE`
+ jq . < $LAST_RESPONSE
exit_fail "Expected response ok, after providing transfer data. got: $STATUS"
fi
+echo " OK"
+echo -n "Sending bogus WTID ..."
#
# CHECK TRANSFER API
#
@@ -305,44 +376,44 @@ STATUS=$(curl 'http://localhost:9966/instances/default/private/transfers' \
-d '{"credit_amount":"'$CREDIT_AMOUNT'","wtid":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","payto_uri":"'$TARGET_PAYTO'","exchange_url":"'$WURL'"}' \
-w "%{http_code}" -s -o $LAST_RESPONSE)
-jq . < $LAST_RESPONSE
-
if [ "$STATUS" != "502" ]
then
- echo `cat $LAST_RESPONSE`
+ jq . < $LAST_RESPONSE
exit_fail "Expected response invalid since the WTID is fake. got: $STATUS"
fi
+echo "OK"
+echo -n "Fetching wire transfers ..."
+
STATUS=$(curl 'http://localhost:9966/instances/default/private/transfers' \
-w "%{http_code}" -s -o $LAST_RESPONSE)
-jq . < $LAST_RESPONSE
-
if [ "$STATUS" != "200" ]
then
- echo `cat $LAST_RESPONSE`
- exit_fail "Expected response ok. got: $STATUS"
+ jq . < $LAST_RESPONSE
+ exit_fail "Expected response 200 Ok. got: $STATUS"
fi
TRANSFERS_LIST_SIZE=`jq -r '.transfers | length' < $LAST_RESPONSE`
if [ "$TRANSFERS_LIST_SIZE" != "2" ]
then
- echo `cat $LAST_RESPONSE`
+ jq . < $LAST_RESPONSE
exit_fail "Expected response ok. got: $STATUS"
fi
+echo "OK"
+echo -n "Fetching wire transfer details of bogus WTID ..."
+
# Test for #6854: use a bogus WTID, causing the exchange to fail to
# find the WTID.
STATUS=$(curl 'http://localhost:9966/instances/default/private/transfers' \
-d '{"credit_amount":"'$CREDIT_AMOUNT'","wtid":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","payto_uri":"'$TARGET_PAYTO'","exchange_url":"'$WURL'"}' \
-w "%{http_code}" -s -o $LAST_RESPONSE)
-jq . < $LAST_RESPONSE
-
if [ "$STATUS" != "502" ]
then
- echo `cat $LAST_RESPONSE`
+ jq . < $LAST_RESPONSE
exit_fail "Expected response invalid since the WTID is fake. got: $STATUS"
fi
@@ -353,8 +424,11 @@ echo -n "Checking order status ..."
STATUS=$(curl "http://localhost:9966/instances/default/private/orders/${ORDER_ID}?transfer=YES" \
-w "%{http_code}" -s -o $LAST_RESPONSE)
+
+
if [ "$STATUS" != "200" ]
then
+ jq . < $LAST_RESPONSE
exit_fail 'should response ok, after order inquiry. got:' $STATUS `cat $LAST_RESPONSE`
exit 1
fi
@@ -364,13 +438,12 @@ DEPOSIT_TOTAL=`jq -r .deposit_total < $LAST_RESPONSE`
if [ "$DEPOSIT_TOTAL" == "TESTKUDOS:0" ]
then
echo 'deposit total is zero, expected greater than zero. got:' $DEPOSIT_TOTAL `cat $LAST_RESPONSE`
- bash
exit 1
fi
echo " OK"
-echo Removing password from account 43
+echo -n "Removing password from account 43 ..."
taler-bank-manage -c $CONF --with-db postgres:///$TALER_DB django changepassword_unsafe 43 x >/dev/null 2>/dev/null
ACCOUNT_PASSWORD="43:x"
@@ -381,7 +454,8 @@ STATUS=$(curl "http://$ACCOUNT_PASSWORD@$BANK_HOST/accounts/43" \
if [ "$STATUS" != "200" ]
then
- echo 'should response ok, getting account status. got:' $STATUS `cat $LAST_RESPONSE`
+ jq . < $LAST_RESPONSE
+ echo "Expected response 200 Ok, getting account status. Got: $STATUS"
exit 1
fi
@@ -389,10 +463,11 @@ BALANCE=`jq -r .balance.amount < $LAST_RESPONSE`
if [ "$BALANCE" == "TESTKUDOS:0" ]
then
- echo 'Wire transfer did not happen. Got:' $BALANCE `cat $LAST_RESPONSE`
+ jq . < $LAST_RESPONSE
+ echo "Wire transfer did not happen. Got: $BALANCE"
exit 1
fi
-
+echo " OK"
exit 0