-modify C API to future-proof it for returning more details as required for KYC implementation

This commit is contained in:
Christian Grothoff 2021-10-13 18:52:59 +02:00
parent 9e25e39b80
commit acbadd5c6e
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
12 changed files with 390 additions and 244 deletions

View File

@ -472,7 +472,7 @@ echo "===========4: deposit wire target wrong================="
# Original target bank account was 43, changing to 44 # Original target bank account was 43, changing to 44
SERIAL=`echo "SELECT deposit_serial_id FROM deposits WHERE amount_with_fee_val=3 AND amount_with_fee_frac=0 ORDER BY deposit_serial_id LIMIT 1" | psql $DB -Aqt` SERIAL=`echo "SELECT deposit_serial_id FROM deposits WHERE amount_with_fee_val=3 AND amount_with_fee_frac=0 ORDER BY deposit_serial_id LIMIT 1" | psql $DB -Aqt`
OLD_WIRE=`echo "SELECT wire FROM deposits WHERE deposit_serial_id=${SERIAL};" | psql $DB -Aqt` OLD_WIRE=`echo "SELECT wire FROM deposits WHERE deposit_serial_id=${SERIAL};" | psql $DB -Aqt`
echo "UPDATE deposits SET wire='{\"payto_uri\":\"payto://x-taler-bank/localhost:8082/44\",\"salt\":\"test-salt\"}' WHERE deposit_serial_id=${SERIAL}" | psql -Aqt $DB echo "UPDATE deposits SET wire='{\"payto_uri\":\"payto://x-taler-bank/localhost:8082/44\",\"salt\":\"9PE256FT5N3YX8H3F1QCHXVNGWAGG3MHDN4J360TSVWTA6WSSMNB8MY4GN5HQAP89TDZH8ANKEG1FB1FJZMN7ZC6NH6QRB0CDDR4TJ8\"}' WHERE deposit_serial_id=${SERIAL}" | psql -Aqt $DB
run_audit run_audit
@ -1500,7 +1500,7 @@ echo "===========26: deposit wire target malformed ================="
# Expects 'payto_uri', not 'url' (also breaks signature, but we cannot even check that). # Expects 'payto_uri', not 'url' (also breaks signature, but we cannot even check that).
SERIAL=`echo "SELECT deposit_serial_id FROM deposits WHERE amount_with_fee_val=3 AND amount_with_fee_frac=0 ORDER BY deposit_serial_id LIMIT 1" | psql $DB -Aqt` SERIAL=`echo "SELECT deposit_serial_id FROM deposits WHERE amount_with_fee_val=3 AND amount_with_fee_frac=0 ORDER BY deposit_serial_id LIMIT 1" | psql $DB -Aqt`
OLD_WIRE=`echo "SELECT wire FROM deposits WHERE deposit_serial_id=${SERIAL};" | psql $DB -Aqt` OLD_WIRE=`echo "SELECT wire FROM deposits WHERE deposit_serial_id=${SERIAL};" | psql $DB -Aqt`
echo "UPDATE deposits SET wire='{\"url\":\"payto://x-taler-bank/localhost:8082/44\",\"salt\":\"test-salt\"}' WHERE deposit_serial_id=${SERIAL}" | psql -Aqt $DB echo "UPDATE deposits SET wire='{\"url\":\"payto://x-taler-bank/localhost:8082/44\",\"salt\":\"9PE256FT5N3YX8H3F1QCHXVNGWAGG3MHDN4J360TSVWTA6WSSMNB8MY4GN5HQAP89TDZH8ANKEG1FB1FJZMN7ZC6NH6QRB0CDDR4TJ8\"}' WHERE deposit_serial_id=${SERIAL}" | psql -Aqt $DB
run_audit run_audit

View File

@ -1303,12 +1303,16 @@ test_wire_out (const struct TALER_EXCHANGEDB_Deposit *deposit)
putting in the WTID into the wire_out table */ putting in the WTID into the wire_out table */
{ {
json_t *wire_out_account; json_t *wire_out_account;
struct TALER_WireSalt salt;
memset (&salt,
44,
sizeof (salt));
wire_out_account = GNUNET_JSON_PACK ( wire_out_account = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("payto_uri", GNUNET_JSON_pack_string ("payto_uri",
"payto://x-taler-bank/localhost:8080/1"), "payto://x-taler-bank/localhost:8080/1"),
GNUNET_JSON_pack_string ("salt", GNUNET_JSON_pack_data_auto ("salt",
"this-is-my-salt")); &salt));
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->store_wire_transfer_out (plugin->cls, plugin->store_wire_transfer_out (plugin->cls,
wire_out_date, wire_out_date,
@ -1513,15 +1517,19 @@ run (void *cls)
uint64_t rr; uint64_t rr;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
struct GNUNET_TIME_Absolute now; struct GNUNET_TIME_Absolute now;
struct TALER_WireSalt salt;
dkp = NULL; dkp = NULL;
rh = NULL; rh = NULL;
deposit.coin.denom_sig.rsa_signature = NULL; deposit.coin.denom_sig.rsa_signature = NULL;
memset (&salt,
45,
sizeof (salt));
wire = GNUNET_JSON_PACK ( wire = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("payto_uri", GNUNET_JSON_pack_string ("payto_uri",
"payto://iban/DE67830654080004822650?receiver-name=Test"), "payto://iban/DE67830654080004822650?receiver-name=Test"),
GNUNET_JSON_pack_string ("salt", GNUNET_JSON_pack_data_auto ("salt",
"this-is-a-salt-value")); &salt));
ZR_BLK (&cbc); ZR_BLK (&cbc);
ZR_BLK (&cbc2); ZR_BLK (&cbc2);
if (NULL == if (NULL ==

View File

@ -793,23 +793,78 @@ TALER_EXCHANGE_deposit_permission_sign (
struct TALER_EXCHANGE_DepositHandle; struct TALER_EXCHANGE_DepositHandle;
/**
* Structure with information about a deposit
* operation's result.
*/
struct TALER_EXCHANGE_DepositResult
{
/**
* HTTP response data
*/
struct TALER_EXCHANGE_HttpResponse hr;
union
{
/**
* Information returned if the HTTP status is
* #MHD_HTTP_OK.
*/
struct
{
/**
* Time when the exchange generated the deposit confirmation
*/
struct GNUNET_TIME_Absolute deposit_timestamp;
/**
* signature provided by the exchange
*/
const struct TALER_ExchangeSignatureP *exchange_sig;
/**
* exchange key used to sign @a exchange_sig.
*/
const struct TALER_ExchangePublicKeyP *exchange_pub;
/**
* Base URL for looking up wire transfers, or
* NULL to use the default base URL.
*/
const char *transaction_base_url;
/**
* Payment target that the merchant should use
* to check for its KYC status.
*/
uint64_t payment_target_uuid;
} success;
/**
* Information returned if the HTTP status is
* #MHD_HTTP_CONFLICT.
*/
struct
{
/* TODO: returning full details is not implemented */
} conflict;
} details;
};
/** /**
* Callbacks of this type are used to serve the result of submitting a * Callbacks of this type are used to serve the result of submitting a
* deposit permission request to a exchange. * deposit permission request to a exchange.
* *
* @param cls closure * @param cls closure
* @param hr HTTP response data * @param dr deposit response details
* @param deposit_timestamp time when the exchange generated the deposit confirmation
* @param exchange_sig signature provided by the exchange
* @param exchange_pub exchange key used to sign @a obj, or NULL
*/ */
typedef void typedef void
(*TALER_EXCHANGE_DepositResultCallback) ( (*TALER_EXCHANGE_DepositResultCallback) (
void *cls, void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr, const struct TALER_EXCHANGE_DepositResult *dr);
struct GNUNET_TIME_Absolute deposit_timestamp,
const struct TALER_ExchangeSignatureP *exchange_sig,
const struct TALER_ExchangePublicKeyP *exchange_pub);
/** /**
@ -1200,19 +1255,75 @@ TALER_EXCHANGE_reserves_get_cancel (
struct TALER_EXCHANGE_WithdrawHandle; struct TALER_EXCHANGE_WithdrawHandle;
/**
* Details about a response for a withdraw request.
*/
struct TALER_EXCHANGE_WithdrawResponse
{
/**
* HTTP response data.
*/
struct TALER_EXCHANGE_HttpResponse hr;
/**
* Details about the response.
*/
union
{
/**
* Details if the status is #MHD_HTTP_OK.
*/
struct
{
/**
* Signature over the coin.
*/
struct TALER_DenominationSignature sig;
} success;
/**
* Details if the status is #MHD_HTTP_ACCEPTED.
*/
struct
{
/**
* Payment target that the merchant should use
* to check for its KYC status.
*/
uint64_t payment_target_uuid;
} accepted;
/**
* Details if the status is #MHD_HTTP_CONFLICT.
*/
struct
{
/* TODO: returning full details is not implemented */
} conflict;
/**
* Details if the status is #MHD_HTTP_GONE.
*/
struct
{
/* TODO: returning full details is not implemented */
} gone;
} details;
};
/** /**
* Callbacks of this type are used to serve the result of submitting a * Callbacks of this type are used to serve the result of submitting a
* withdraw request to a exchange. * withdraw request to a exchange.
* *
* @param cls closure * @param cls closure
* @param hr HTTP response data * @param wr response details
* @param sig signature over the coin, NULL on error
*/ */
typedef void typedef void
(*TALER_EXCHANGE_WithdrawCallback) ( (*TALER_EXCHANGE_WithdrawCallback) (
void *cls, void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr, const struct TALER_EXCHANGE_WithdrawResponse *wr);
const struct TALER_DenominationSignature *sig);
/** /**
@ -1684,41 +1795,82 @@ struct TALER_EXCHANGE_DepositGetHandle;
/** /**
* Data returned for a successful GET /deposits/ request. Note that * Data returned for a successful GET /deposits/ request.
* most fields are only set if the status is #MHD_HTTP_OK. Only
* the @e execution_time is available if the status is #MHD_HTTP_ACCEPTED.
*/ */
struct TALER_EXCHANGE_DepositData struct TALER_EXCHANGE_GetDepositResponse
{ {
/** /**
* exchange key used to sign, all zeros if exchange did not * HTTP response data.
* yet execute the transaction
*/ */
struct TALER_ExchangePublicKeyP exchange_pub; struct TALER_EXCHANGE_HttpResponse hr;
/** /**
* signature from the exchange over the deposit data, all zeros if exchange did not * Details about the response.
* yet execute the transaction
*/ */
struct TALER_ExchangeSignatureP exchange_sig; union
{
/** /**
* wire transfer identifier used by the exchange, all zeros if exchange did not * Response if the status was #MHD_HTTP_OK
* yet execute the transaction */
*/ struct TALER_EXCHANGE_DepositData
struct TALER_WireTransferIdentifierRawP wtid; {
/**
* exchange key used to sign, all zeros if exchange did not
* yet execute the transaction
*/
struct TALER_ExchangePublicKeyP exchange_pub;
/** /**
* actual or planned execution time for the wire transfer * signature from the exchange over the deposit data, all zeros if exchange did not
*/ * yet execute the transaction
struct GNUNET_TIME_Absolute execution_time; */
struct TALER_ExchangeSignatureP exchange_sig;
/** /**
* contribution to the total amount by this coin, all zeros if exchange did not * wire transfer identifier used by the exchange, all zeros if exchange did not
* yet execute the transaction * yet execute the transaction
*/ */
struct TALER_Amount coin_contribution; struct TALER_WireTransferIdentifierRawP wtid;
/**
* actual execution time for the wire transfer
*/
struct GNUNET_TIME_Absolute execution_time;
/**
* contribution to the total amount by this coin, all zeros if exchange did not
* yet execute the transaction
*/
struct TALER_Amount coin_contribution;
/**
* Payment target that the merchant should use
* to check for its KYC status.
*/
uint64_t payment_target_uuid;
} success;
/**
* Response if the status was #MHD_HTTP_ACCEPTED
*/
struct
{
/**
* planned execution time for the wire transfer
*/
struct GNUNET_TIME_Absolute execution_time;
/**
* Payment target that the merchant should use
* to check for its KYC status.
*/
uint64_t payment_target_uuid;
} accepted;
} details;
}; };
@ -1732,8 +1884,7 @@ struct TALER_EXCHANGE_DepositData
typedef void typedef void
(*TALER_EXCHANGE_DepositGetCallback)( (*TALER_EXCHANGE_DepositGetCallback)(
void *cls, void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr, const struct TALER_EXCHANGE_GetDepositResponse *dr);
const struct TALER_EXCHANGE_DepositData *dd);
/** /**

View File

@ -324,19 +324,17 @@ handle_deposit_finished (void *cls,
struct TALER_EXCHANGE_DepositHandle *dh = cls; struct TALER_EXCHANGE_DepositHandle *dh = cls;
struct TALER_ExchangeSignatureP exchange_sig; struct TALER_ExchangeSignatureP exchange_sig;
struct TALER_ExchangePublicKeyP exchange_pub; struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP *es = NULL;
struct TALER_ExchangePublicKeyP *ep = NULL;
const json_t *j = response; const json_t *j = response;
struct TALER_EXCHANGE_HttpResponse hr = { struct TALER_EXCHANGE_DepositResult dr = {
.reply = j, .hr.reply = j,
.http_status = (unsigned int) response_code .hr.http_status = (unsigned int) response_code
}; };
dh->job = NULL; dh->job = NULL;
switch (response_code) switch (response_code)
{ {
case 0: case 0:
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break; break;
case MHD_HTTP_OK: case MHD_HTTP_OK:
if (GNUNET_OK != if (GNUNET_OK !=
@ -346,31 +344,35 @@ handle_deposit_finished (void *cls,
&exchange_pub)) &exchange_pub))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
hr.http_status = 0; dr.hr.http_status = 0;
hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE; dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
} }
else else
{ {
es = &exchange_sig; dr.details.success.exchange_sig = &exchange_sig;
ep = &exchange_pub; dr.details.success.exchange_pub = &exchange_pub;
dr.details.success.deposit_timestamp
= GNUNET_TIME_absolute_ntoh (dh->depconf.exchange_timestamp);
dr.details.success.transaction_base_url; // FIXME
dr.details.success.payment_target_uuid; // FIXME
} }
break; break;
case MHD_HTTP_BAD_REQUEST: case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy /* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */ (or API version conflict); just pass JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
break; break;
case MHD_HTTP_FORBIDDEN: case MHD_HTTP_FORBIDDEN:
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
/* Nothing really to verify, exchange says one of the signatures is /* Nothing really to verify, exchange says one of the signatures is
invalid; as we checked them, this should never happen, we invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */ should pass the JSON reply to the application */
break; break;
case MHD_HTTP_NOT_FOUND: case MHD_HTTP_NOT_FOUND:
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
/* Nothing really to verify, this should never /* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */ happen, we should pass the JSON reply to the application */
break; break;
@ -381,13 +383,13 @@ handle_deposit_finished (void *cls,
j)) j))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
hr.http_status = 0; dr.hr.http_status = 0;
hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE; dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
} }
else else
{ {
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
} }
break; break;
case MHD_HTTP_GONE: case MHD_HTTP_GONE:
@ -395,31 +397,28 @@ handle_deposit_finished (void *cls,
/* Note: one might want to check /keys for revocation /* Note: one might want to check /keys for revocation
signature here, alas tricky in case our /keys signature here, alas tricky in case our /keys
is outdated => left to clients */ is outdated => left to clients */
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
break; break;
case MHD_HTTP_INTERNAL_SERVER_ERROR: case MHD_HTTP_INTERNAL_SERVER_ERROR:
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
/* Server had an internal issue; we should retry, but this API /* Server had an internal issue; we should retry, but this API
leaves this to the application */ leaves this to the application */
break; break;
default: default:
/* unexpected response code */ /* unexpected response code */
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange deposit\n", "Unexpected response code %u/%d for exchange deposit\n",
(unsigned int) response_code, (unsigned int) response_code,
hr.ec); dr.hr.ec);
GNUNET_break_op (0); GNUNET_break_op (0);
break; break;
} }
dh->cb (dh->cb_cls, dh->cb (dh->cb_cls,
&hr, &dr);
GNUNET_TIME_absolute_ntoh (dh->depconf.exchange_timestamp),
es,
ep);
TALER_EXCHANGE_deposit_cancel (dh); TALER_EXCHANGE_deposit_cancel (dh);
} }

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2020 Taler Systems SA Copyright (C) 2014-2021 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software terms of the GNU General Public License as published by the Free Software
@ -88,7 +88,7 @@ struct TALER_EXCHANGE_DepositGetHandle
* @param exchange_sig the exchange's signature * @param exchange_sig the exchange's signature
* @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
*/ */
static int static enum GNUNET_GenericReturnValue
verify_deposit_wtid_signature_ok ( verify_deposit_wtid_signature_ok (
const struct TALER_EXCHANGE_DepositGetHandle *dwh, const struct TALER_EXCHANGE_DepositGetHandle *dwh,
const json_t *json, const json_t *json,
@ -133,26 +133,30 @@ handle_deposit_wtid_finished (void *cls,
{ {
struct TALER_EXCHANGE_DepositGetHandle *dwh = cls; struct TALER_EXCHANGE_DepositGetHandle *dwh = cls;
const json_t *j = response; const json_t *j = response;
struct TALER_EXCHANGE_HttpResponse hr = { struct TALER_EXCHANGE_GetDepositResponse dr = {
.reply = j, .hr.reply = j,
.http_status = (unsigned int) response_code .hr.http_status = (unsigned int) response_code
}; };
dwh->job = NULL; dwh->job = NULL;
switch (response_code) switch (response_code)
{ {
case 0: case 0:
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break; break;
case MHD_HTTP_OK: case MHD_HTTP_OK:
{ {
struct TALER_EXCHANGE_DepositData dd;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("wtid", &dwh->depconf.wtid), GNUNET_JSON_spec_fixed_auto ("wtid",
TALER_JSON_spec_absolute_time ("execution_time", &dd.execution_time), &dr.details.success.wtid),
TALER_JSON_spec_amount_any ("coin_contribution", &dd.coin_contribution), TALER_JSON_spec_absolute_time ("execution_time",
GNUNET_JSON_spec_fixed_auto ("exchange_sig", &dd.exchange_sig), &dr.details.success.execution_time),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", &dd.exchange_pub), TALER_JSON_spec_amount_any ("coin_contribution",
&dr.details.success.coin_contribution),
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
&dr.details.success.exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
&dr.details.success.exchange_pub),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
@ -162,41 +166,37 @@ handle_deposit_wtid_finished (void *cls,
NULL, NULL)) NULL, NULL))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
hr.http_status = 0; dr.hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
} }
dwh->depconf.execution_time = GNUNET_TIME_absolute_hton ( dwh->depconf.execution_time = GNUNET_TIME_absolute_hton (
dd.execution_time); dr.details.success.execution_time);
dwh->depconf.wtid = dr.details.success.wtid;
TALER_amount_hton (&dwh->depconf.coin_contribution, TALER_amount_hton (&dwh->depconf.coin_contribution,
&dd.coin_contribution); &dr.details.success.coin_contribution);
if (GNUNET_OK != if (GNUNET_OK !=
verify_deposit_wtid_signature_ok (dwh, verify_deposit_wtid_signature_ok (dwh,
j, j,
&dd.exchange_pub, &dr.details.success.exchange_pub,
&dd.exchange_sig)) &dr.details.success.exchange_sig))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
hr.http_status = 0; dr.hr.http_status = 0;
hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE; dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE;
} break;
else
{
dd.wtid = dwh->depconf.wtid;
dwh->cb (dwh->cb_cls,
&hr,
&dd);
TALER_EXCHANGE_deposits_get_cancel (dwh);
return;
} }
dwh->cb (dwh->cb_cls,
&dr);
TALER_EXCHANGE_deposits_get_cancel (dwh);
return;
} }
break;
case MHD_HTTP_ACCEPTED: case MHD_HTTP_ACCEPTED:
{ {
/* Transaction known, but not executed yet */ /* Transaction known, but not executed yet */
struct GNUNET_TIME_Absolute execution_time;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_absolute_time ("execution_time", &execution_time), TALER_JSON_spec_absolute_time ("execution_time",
&dr.details.accepted.execution_time),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
@ -206,63 +206,54 @@ handle_deposit_wtid_finished (void *cls,
NULL, NULL)) NULL, NULL))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
hr.http_status = 0; dr.hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
} }
else dr.details.accepted.payment_target_uuid; // FIXME
{ dwh->cb (dwh->cb_cls,
struct TALER_EXCHANGE_DepositData dd = { &dr);
.execution_time = execution_time TALER_EXCHANGE_deposits_get_cancel (dwh);
}; return;
dwh->cb (dwh->cb_cls,
&hr,
&dd);
TALER_EXCHANGE_deposits_get_cancel (dwh);
return;
}
} }
break;
case MHD_HTTP_BAD_REQUEST: case MHD_HTTP_BAD_REQUEST:
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
/* This should never happen, either us or the exchange is buggy /* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */ (or API version conflict); just pass JSON reply to the application */
break; break;
case MHD_HTTP_FORBIDDEN: case MHD_HTTP_FORBIDDEN:
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
/* Nothing really to verify, exchange says one of the signatures is /* Nothing really to verify, exchange says one of the signatures is
invalid; as we checked them, this should never happen, we invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */ should pass the JSON reply to the application */
break; break;
case MHD_HTTP_NOT_FOUND: case MHD_HTTP_NOT_FOUND:
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
/* Exchange does not know about transaction; /* Exchange does not know about transaction;
we should pass the reply to the application */ we should pass the reply to the application */
break; break;
case MHD_HTTP_INTERNAL_SERVER_ERROR: case MHD_HTTP_INTERNAL_SERVER_ERROR:
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
/* Server had an internal issue; we should retry, but this API /* Server had an internal issue; we should retry, but this API
leaves this to the application */ leaves this to the application */
break; break;
default: default:
/* unexpected response code */ /* unexpected response code */
hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange GET deposits\n", "Unexpected response code %u/%d for exchange GET deposits\n",
(unsigned int) response_code, (unsigned int) response_code,
(int) hr.ec); (int) dr.hr.ec);
GNUNET_break_op (0); GNUNET_break_op (0);
break; break;
} }
dwh->cb (dwh->cb_cls, dwh->cb (dwh->cb_cls,
&hr, &dr);
NULL);
TALER_EXCHANGE_deposits_get_cancel (dwh); TALER_EXCHANGE_deposits_get_cancel (dwh);
} }

View File

@ -91,44 +91,41 @@ handle_reserve_withdraw_finished (
const struct GNUNET_CRYPTO_RsaSignature *blind_sig) const struct GNUNET_CRYPTO_RsaSignature *blind_sig)
{ {
struct TALER_EXCHANGE_WithdrawHandle *wh = cls; struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
struct TALER_EXCHANGE_WithdrawResponse wr = {
.hr = *hr
};
wh->wh2 = NULL; wh->wh2 = NULL;
if (MHD_HTTP_OK != hr->http_status) switch (hr->http_status)
{ {
wh->cb (wh->cb_cls, case MHD_HTTP_OK:
hr,
NULL);
}
else
{
struct TALER_FreshCoin fc;
if (GNUNET_OK !=
TALER_planchet_to_coin (&wh->pk.key,
blind_sig,
&wh->ps,
&wh->c_hash,
&fc))
{ {
struct TALER_EXCHANGE_HttpResponse hrx = { struct TALER_FreshCoin fc;
.reply = hr->reply,
.http_status = 0,
.ec = TALER_EC_EXCHANGE_WITHDRAW_UNBLIND_FAILURE
};
wh->cb (wh->cb_cls, if (GNUNET_OK !=
&hrx, TALER_planchet_to_coin (&wh->pk.key,
NULL); blind_sig,
&wh->ps,
&wh->c_hash,
&fc))
{
wr.hr.http_status = 0;
wr.hr.ec = TALER_EC_EXCHANGE_WITHDRAW_UNBLIND_FAILURE;
break;
}
wr.details.success.sig = fc.sig;
break;
} }
else case MHD_HTTP_ACCEPTED:
{ wr.details.accepted.payment_target_uuid; // FIXME
wh->cb (wh->cb_cls, break;
hr, default:
&fc.sig); break;
GNUNET_CRYPTO_rsa_signature_free (fc.sig.rsa_signature);
}
} }
wh->cb (wh->cb_cls,
&wr);
if (MHD_HTTP_OK == hr->http_status)
GNUNET_CRYPTO_rsa_signature_free (wr.details.success.sig.rsa_signature);
TALER_EXCHANGE_withdraw_cancel (wh); TALER_EXCHANGE_withdraw_cancel (wh);
} }

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2018-2020 Taler Systems SA Copyright (C) 2018-2021 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by under the terms of the GNU General Public License as published by
@ -207,38 +207,30 @@ do_retry (void *cls)
* check if the response code is acceptable. * check if the response code is acceptable.
* *
* @param cls closure. * @param cls closure.
* @param hr HTTP response details * @param dr deposit response details
* @param exchange_timestamp when did the exchange receive the deposit permission
* @param exchange_sig signature provided by the exchange
* (NULL on errors)
* @param exchange_pub public key of the exchange,
* used for signing the response.
*/ */
static void static void
deposit_cb (void *cls, deposit_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr, const struct TALER_EXCHANGE_DepositResult *dr)
const struct GNUNET_TIME_Absolute exchange_timestamp,
const struct TALER_ExchangeSignatureP *exchange_sig,
const struct TALER_ExchangePublicKeyP *exchange_pub)
{ {
struct DepositState *ds = cls; struct DepositState *ds = cls;
ds->dh = NULL; ds->dh = NULL;
if (ds->expected_response_code != hr->http_status) if (ds->expected_response_code != dr->hr.http_status)
{ {
if (0 != ds->do_retry) if (0 != ds->do_retry)
{ {
ds->do_retry--; ds->do_retry--;
if ( (0 == hr->http_status) || if ( (0 == dr->hr.http_status) ||
(TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) || (TALER_EC_GENERIC_DB_SOFT_FAILURE == dr->hr.ec) ||
(MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) ) (MHD_HTTP_INTERNAL_SERVER_ERROR == dr->hr.http_status) )
{ {
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Retrying deposit failed with %u/%d\n", "Retrying deposit failed with %u/%d\n",
hr->http_status, dr->hr.http_status,
(int) hr->ec); (int) dr->hr.ec);
/* on DB conflicts, do not use backoff */ /* on DB conflicts, do not use backoff */
if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) if (TALER_EC_GENERIC_DB_SOFT_FAILURE == dr->hr.ec)
ds->backoff = GNUNET_TIME_UNIT_ZERO; ds->backoff = GNUNET_TIME_UNIT_ZERO;
else else
ds->backoff = GNUNET_TIME_randomized_backoff (ds->backoff, ds->backoff = GNUNET_TIME_randomized_backoff (ds->backoff,
@ -253,22 +245,22 @@ deposit_cb (void *cls,
} }
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s in %s:%u\n", "Unexpected response code %u to command %s in %s:%u\n",
hr->http_status, dr->hr.http_status,
ds->is->commands[ds->is->ip].label, ds->is->commands[ds->is->ip].label,
__FILE__, __FILE__,
__LINE__); __LINE__);
json_dumpf (hr->reply, json_dumpf (dr->hr.reply,
stderr, stderr,
0); 0);
TALER_TESTING_interpreter_fail (ds->is); TALER_TESTING_interpreter_fail (ds->is);
return; return;
} }
if (MHD_HTTP_OK == hr->http_status) if (MHD_HTTP_OK == dr->hr.http_status)
{ {
ds->deposit_succeeded = GNUNET_YES; ds->deposit_succeeded = GNUNET_YES;
ds->exchange_timestamp = exchange_timestamp; ds->exchange_timestamp = dr->details.success.deposit_timestamp;
ds->exchange_pub = *exchange_pub; ds->exchange_pub = *dr->details.success.exchange_pub;
ds->exchange_sig = *exchange_sig; ds->exchange_sig = *dr->details.success.exchange_sig;
} }
TALER_TESTING_interpreter_next (ds->is); TALER_TESTING_interpreter_next (ds->is);
} }

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2020 Taler Systems SA Copyright (C) 2014-2021 Taler Systems SA
TALER is free software; you can redistribute it and/or modify TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as it under the terms of the GNU General Public License as
@ -80,38 +80,36 @@ struct TrackTransactionState
* line matches our expectations. * line matches our expectations.
* *
* @param cls closure. * @param cls closure.
* @param hr HTTP response details * @param dr GET deposit response details
* @param dd data about the wire transfer associated with the deposit
*/ */
static void static void
deposit_wtid_cb (void *cls, deposit_wtid_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr, const struct TALER_EXCHANGE_GetDepositResponse *dr)
const struct TALER_EXCHANGE_DepositData *dd)
{ {
struct TrackTransactionState *tts = cls; struct TrackTransactionState *tts = cls;
struct TALER_TESTING_Interpreter *is = tts->is; struct TALER_TESTING_Interpreter *is = tts->is;
struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
tts->tth = NULL; tts->tth = NULL;
if (tts->expected_response_code != hr->http_status) if (tts->expected_response_code != dr->hr.http_status)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d to command %s in %s:%u\n", "Unexpected response code %u/%d to command %s in %s:%u\n",
hr->http_status, dr->hr.http_status,
(int) hr->ec, (int) dr->hr.ec,
cmd->label, cmd->label,
__FILE__, __FILE__,
__LINE__); __LINE__);
json_dumpf (hr->reply, json_dumpf (dr->hr.reply,
stderr, stderr,
0); 0);
TALER_TESTING_interpreter_fail (is); TALER_TESTING_interpreter_fail (is);
return; return;
} }
switch (hr->http_status) switch (dr->hr.http_status)
{ {
case MHD_HTTP_OK: case MHD_HTTP_OK:
tts->wtid = dd->wtid; tts->wtid = dr->details.success.wtid;
if (NULL != tts->bank_transfer_reference) if (NULL != tts->bank_transfer_reference)
{ {
const struct TALER_TESTING_Command *bank_transfer_cmd; const struct TALER_TESTING_Command *bank_transfer_cmd;
@ -139,7 +137,7 @@ deposit_wtid_cb (void *cls,
} }
/* Compare that expected and gotten subjects match. */ /* Compare that expected and gotten subjects match. */
if (0 != GNUNET_memcmp (&dd->wtid, if (0 != GNUNET_memcmp (&dr->details.success.wtid,
wtid_want)) wtid_want))
{ {
GNUNET_break (0); GNUNET_break (0);
@ -147,8 +145,6 @@ deposit_wtid_cb (void *cls,
return; return;
} }
} }
break; break;
case MHD_HTTP_ACCEPTED: case MHD_HTTP_ACCEPTED:
/* allowed, nothing to check here */ /* allowed, nothing to check here */

View File

@ -200,14 +200,18 @@ insert_deposit_run (void *cls,
&hc); &hc);
{ {
char *str; char *str;
struct TALER_WireSalt salt;
GNUNET_asprintf (&str, GNUNET_asprintf (&str,
"payto://x-taler-bank/localhost/%s", "payto://x-taler-bank/localhost/%s",
ids->merchant_account); ids->merchant_account);
memset (&salt,
46,
sizeof (salt));
deposit.receiver_wire_account deposit.receiver_wire_account
= GNUNET_JSON_PACK ( = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("salt", GNUNET_JSON_pack_data_auto ("salt",
"this-is-a-salt-value"), &salt),
GNUNET_JSON_pack_string ("payto_uri", GNUNET_JSON_pack_string ("payto_uri",
str)); str));
GNUNET_free (str); GNUNET_free (str);

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2018-2020 Taler Systems SA Copyright (C) 2018-2021 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by under the terms of the GNU General Public License as published by
@ -178,38 +178,36 @@ do_retry (void *cls)
* in the state. * in the state.
* *
* @param cls closure. * @param cls closure.
* @param hr HTTP response details * @param wr withdraw response details
* @param sig signature over the coin, NULL on error.
*/ */
static void static void
reserve_withdraw_cb (void *cls, reserve_withdraw_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr, const struct TALER_EXCHANGE_WithdrawResponse *wr)
const struct TALER_DenominationSignature *sig)
{ {
struct WithdrawState *ws = cls; struct WithdrawState *ws = cls;
struct TALER_TESTING_Interpreter *is = ws->is; struct TALER_TESTING_Interpreter *is = ws->is;
ws->wsh = NULL; ws->wsh = NULL;
if (ws->expected_response_code != hr->http_status) if (ws->expected_response_code != wr->hr.http_status)
{ {
if (0 != ws->do_retry) if (0 != ws->do_retry)
{ {
if (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN != hr->ec) if (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN != wr->hr.ec)
ws->do_retry--; /* we don't count reserve unknown as failures here */ ws->do_retry--; /* we don't count reserve unknown as failures here */
if ( (0 == hr->http_status) || if ( (0 == wr->hr.http_status) ||
(TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) || (TALER_EC_GENERIC_DB_SOFT_FAILURE == wr->hr.ec) ||
(TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS == hr->ec) || (TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS == wr->hr.ec) ||
(TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN == hr->ec) || (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN == wr->hr.ec) ||
(MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) ) (MHD_HTTP_INTERNAL_SERVER_ERROR == wr->hr.http_status) )
{ {
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Retrying withdraw failed with %u/%d\n", "Retrying withdraw failed with %u/%d\n",
hr->http_status, wr->hr.http_status,
(int) hr->ec); (int) wr->hr.ec);
/* on DB conflicts, do not use backoff */ /* on DB conflicts, do not use backoff */
if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) if (TALER_EC_GENERIC_DB_SOFT_FAILURE == wr->hr.ec)
ws->backoff = GNUNET_TIME_UNIT_ZERO; ws->backoff = GNUNET_TIME_UNIT_ZERO;
else if (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN != hr->ec) else if (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN != wr->hr.ec)
ws->backoff = EXCHANGE_LIB_BACKOFF (ws->backoff); ws->backoff = EXCHANGE_LIB_BACKOFF (ws->backoff);
else else
ws->backoff = GNUNET_TIME_relative_max (UNKNOWN_MIN_BACKOFF, ws->backoff = GNUNET_TIME_relative_max (UNKNOWN_MIN_BACKOFF,
@ -227,29 +225,23 @@ reserve_withdraw_cb (void *cls,
} }
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d to command %s in %s:%u\n", "Unexpected response code %u/%d to command %s in %s:%u\n",
hr->http_status, wr->hr.http_status,
(int) hr->ec, (int) wr->hr.ec,
TALER_TESTING_interpreter_get_current_label (is), TALER_TESTING_interpreter_get_current_label (is),
__FILE__, __FILE__,
__LINE__); __LINE__);
json_dumpf (hr->reply, json_dumpf (wr->hr.reply,
stderr, stderr,
0); 0);
GNUNET_break (0); GNUNET_break (0);
TALER_TESTING_interpreter_fail (is); TALER_TESTING_interpreter_fail (is);
return; return;
} }
switch (hr->http_status) switch (wr->hr.http_status)
{ {
case MHD_HTTP_OK: case MHD_HTTP_OK:
if (NULL == sig)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
ws->sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup ( ws->sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup (
sig->rsa_signature); wr->details.success.sig.rsa_signature);
if (0 != ws->total_backoff.rel_value_us) if (0 != ws->total_backoff.rel_value_us)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -259,23 +251,27 @@ reserve_withdraw_cb (void *cls,
GNUNET_YES)); GNUNET_YES));
} }
break; break;
case MHD_HTTP_ACCEPTED:
/* nothing to check */
/* TODO: trait for returned uuid! */
break;
case MHD_HTTP_FORBIDDEN: case MHD_HTTP_FORBIDDEN:
/* nothing to check */ /* nothing to check */
break; break;
case MHD_HTTP_NOT_FOUND:
/* nothing to check */
break;
case MHD_HTTP_CONFLICT: case MHD_HTTP_CONFLICT:
/* nothing to check */ /* nothing to check */
break; break;
case MHD_HTTP_GONE: case MHD_HTTP_GONE:
/* theoretically could check that the key was actually */ /* theoretically could check that the key was actually */
break; break;
case MHD_HTTP_NOT_FOUND:
/* nothing to check */
break;
default: default:
/* Unsupported status code (by test harness) */ /* Unsupported status code (by test harness) */
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Withdraw test command does not support status code %u\n", "Withdraw test command does not support status code %u\n",
hr->http_status); wr->hr.http_status);
GNUNET_break (0); GNUNET_break (0);
break; break;
} }

View File

@ -670,11 +670,17 @@ TALER_TESTING_prepare_fakebank (const char *config_filename,
json_t * json_t *
TALER_TESTING_make_wire_details (const char *payto) TALER_TESTING_make_wire_details (const char *payto)
{ {
struct TALER_WireSalt salt;
/* salt must be constant for aggregation tests! */
memset (&salt,
47,
sizeof (salt));
return GNUNET_JSON_PACK ( return GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("payto_uri", GNUNET_JSON_pack_string ("payto_uri",
payto), payto),
GNUNET_JSON_pack_string ("salt", GNUNET_JSON_pack_data_auto ("salt",
"test-salt (must be constant for aggregation tests)")); &salt));
} }

View File

@ -158,21 +158,24 @@ static int
test_merchant_sigs () test_merchant_sigs ()
{ {
const char *pt = "payto://x-taler-bank/localhost/Account"; const char *pt = "payto://x-taler-bank/localhost/Account";
const char *salt = "my test salt"; struct TALER_WireSalt salt;
struct TALER_MerchantPrivateKeyP priv; struct TALER_MerchantPrivateKeyP priv;
struct TALER_MerchantPublicKeyP pub; struct TALER_MerchantPublicKeyP pub;
struct TALER_MerchantSignatureP sig; struct TALER_MerchantSignatureP sig;
GNUNET_CRYPTO_eddsa_key_create (&priv.eddsa_priv); GNUNET_CRYPTO_eddsa_key_create (&priv.eddsa_priv);
memset (&salt,
42,
sizeof (salt));
TALER_merchant_wire_signature_make (pt, TALER_merchant_wire_signature_make (pt,
salt, &salt,
&priv, &priv,
&sig); &sig);
GNUNET_CRYPTO_eddsa_key_get_public (&priv.eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&priv.eddsa_priv,
&pub.eddsa_pub); &pub.eddsa_pub);
if (GNUNET_OK != if (GNUNET_OK !=
TALER_merchant_wire_signature_check (pt, TALER_merchant_wire_signature_check (pt,
salt, &salt,
&pub, &pub,
&sig)) &sig))
{ {
@ -182,16 +185,19 @@ test_merchant_sigs ()
if (GNUNET_OK == if (GNUNET_OK ==
TALER_merchant_wire_signature_check ( TALER_merchant_wire_signature_check (
"payto://x-taler-bank/localhost/Other", "payto://x-taler-bank/localhost/Other",
salt, &salt,
&pub, &pub,
&sig)) &sig))
{ {
GNUNET_break (0); GNUNET_break (0);
return 1; return 1;
} }
memset (&salt,
43,
sizeof (salt));
if (GNUNET_OK == if (GNUNET_OK ==
TALER_merchant_wire_signature_check (pt, TALER_merchant_wire_signature_check (pt,
"other salt", &salt,
&pub, &pub,
&sig)) &sig))
{ {