Merge branch 'master' of git+ssh://taler.net/var/git/mint

This commit is contained in:
Christian Grothoff 2016-01-24 15:17:36 +01:00
commit d89c91e64a
18 changed files with 1927 additions and 169 deletions

View File

@ -1059,5 +1059,165 @@ void
TALER_MINT_admin_add_incoming_cancel (struct TALER_MINT_AdminAddIncomingHandle *aai);
/* ********************* /wire/deposits *********************** */
/**
* @brief A /wire/deposits Handle
*/
struct TALER_MINT_WireDepositsHandle;
/**
* Details for one of the /deposit operations that the
* mint combined into a single wire transfer.
*/
struct TALER_WireDepositDetails
{
/**
* Hash of the contract.
*/
struct GNUNET_HashCode h_contract;
/**
* Which coin was deposited?
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* Value of the deposit (including fee).
*/
struct TALER_Amount coin_value;
/**
* Fee charged by the mint for the deposit.
*/
struct TALER_Amount coin_fee;
/**
* Merchant's transaction identifier.
*/
uint64_t transaction_id;
};
/**
* Function called with detailed wire transfer data, including all
* of the coin transactions that were combined into the wire transfer.
*
* @param cls closure
* @param http_status HTTP status code we got, 0 on mint protocol violation
* @param json original json reply (may include signatures, those have then been
* validated already)
* @param wtid extracted wire transfer identifier, or NULL if the mint could
* not provide any (set only if @a http_status is #MHD_HTTP_OK)
* @param total_amount total amount of the wire transfer, or NULL if the mint could
* not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
* @param details_length length of the @a details array
* @param details array with details about the combined transactions
*/
typedef void
(*TALER_MINT_WireDepositsCallback)(void *cls,
unsigned int http_status,
json_t *json,
const struct GNUNET_HashCode *h_wire,
const struct TALER_Amount *total_amount,
unsigned int details_length,
const struct TALER_WireDepositDetails *details);
/**
* Query the mint about which transactions were combined
* to create a wire transfer.
*
* @param mint mint to query
* @param wtid raw wire transfer identifier to get information about
* @param cb callback to call
* @param cb_cls closure for @a cb
* @return handle to cancel operation
*/
struct TALER_MINT_WireDepositsHandle *
TALER_MINT_wire_deposits (struct TALER_MINT_Handle *mint,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_MINT_WireDepositsCallback cb,
void *cb_cls);
/**
* Cancel wire deposits request. This function cannot be used on a request
* handle if a response is already served for it.
*
* @param wdh the wire deposits request handle
*/
void
TALER_MINT_wire_deposits_cancel (struct TALER_MINT_WireDepositsHandle *wdh);
/* ********************* /deposit/wtid *********************** */
/**
* @brief A /deposit/wtid Handle
*/
struct TALER_MINT_DepositWtidHandle;
/**
* Function called with detailed wire transfer data.
*
* @param cls closure
* @param http_status HTTP status code we got, 0 on mint protocol violation
* @param json original json reply (may include signatures, those have then been
* validated already)
* @param wtid wire transfer identifier used by the mint, NULL if mint did not
* yet execute the transaction
* @param execution_time actual or planned execution time for the wire transfer
* @param coin_contribution contribution to the @a total_amount of the deposited coin (may be NULL)
* @param total_amount total amount of the wire transfer, or NULL if the mint could
* not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
*/
typedef void
(*TALER_MINT_DepositWtidCallback)(void *cls,
unsigned int http_status,
json_t *json,
const struct TALER_WireTransferIdentifierRawP *wtid,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *coin_contribution,
const struct TALER_Amount *total_amount);
/**
* Obtain the wire transfer details for a given deposit.
*
* @param mint the mint to query
* @param merchant_priv the merchant's private key
* @param h_wire hash of merchant's wire transfer details
* @param h_contract hash of the contract
* @param coin_pub public key of the coin
* @param transaction_id transaction identifier
* @param cb function to call with the result
* @param cb_cls closure for @a cb
* @return handle to abort request
*/
struct TALER_MINT_DepositWtidHandle *
TALER_MINT_deposit_wtid (struct TALER_MINT_Handle *mint,
const struct TALER_MerchantPrivateKeyP *merchant_priv,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t transaction_id,
TALER_MINT_DepositWtidCallback cb,
void *cb_cls);
/**
* Cancel deposit wtid request. This function cannot be used on a request
* handle if a response is already served for it.
*
* @param dwh the wire deposits request handle
*/
void
TALER_MINT_deposit_wtid_cancel (struct TALER_MINT_DepositWtidHandle *dwh);
#endif /* _TALER_MINT_SERVICE_H */

View File

@ -570,23 +570,24 @@ typedef void
/**
* Function called with the results of the lookup of the
* wire transfer identifier information.
* wire transfer identifier information. Only called if
* we are at least aware of the transaction existing.
*
* @param cls closure
* @param wtid 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 minus fee)
* contribute to the total transfer value? (deposit value including fee)
* @param coin_fee how much did the mint charge for the deposit fee
* @param total_amount how much was the total wire transfer?
* @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 mint
* when we expect it to be done (if @a wtid was NULL)
*/
typedef void
(*TALER_MINTDB_DepositWtidCallback)(void *cls,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *coin_contribution,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *total_amount,
struct GNUNET_TIME_Absolute execution_time);
@ -601,18 +602,20 @@ typedef void
* @param h_contract which contract was this payment about
* @param transaction_id merchant's transaction ID for the payment
* @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 mint for this coin
* @param coin_value amount contributed by this coin in total (with fee)
* @param coin_fee applicable fee for this coin
* @param transfer_value total amount of the wire transfer
*/
typedef void
(*TALER_MINTDB_TransactionDataCallback)(void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
uint64_t transaction_id,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *deposit_value,
const struct TALER_Amount *deposit_fee);
(*TALER_MINTDB_WireTransferDataCallback)(void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
uint64_t transaction_id,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_value,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *transfer_value);
/**
@ -1229,16 +1232,19 @@ struct TALER_MINTDB_Plugin
* into a wire transfer by the respective @a raw_wtid.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session database connection
* @param wtid the raw wire transfer identifier we used
* @param cb function to call on each transaction found
* @param cb_cls closure for @a cb
* @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors
* @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors,
* #GNUNET_NO if we found no results
*/
int
(*lookup_wire_transactions) (void *cls,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_MINTDB_TransactionDataCallback cb,
void *cb_cls);
(*lookup_wire_transfer) (void *cls,
struct TALER_MINTDB_Session *session,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_MINTDB_WireTransferDataCallback cb,
void *cb_cls);
/**
@ -1247,6 +1253,7 @@ struct TALER_MINTDB_Plugin
* to be executed.
*
* @param cls closure
* @param session database connection
* @param h_contract hash of the contract
* @param h_wire hash of merchant wire details
* @param coin_pub public key of deposited coin
@ -1254,10 +1261,12 @@ struct TALER_MINTDB_Plugin
* @param transaction_id transaction identifier
* @param cb function to call with the result
* @param cb_cls closure to pass to @a cb
* @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
* @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors,
* #GNUNET_NO if nothing was found
*/
int
(*wire_lookup_deposit_wtid)(void *cls,
struct TALER_MINTDB_Session *session,
const struct GNUNET_HashCode *h_contract,
const struct GNUNET_HashCode *h_wire,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -1271,26 +1280,32 @@ struct TALER_MINTDB_Plugin
* Function called to insert aggregation information into the DB.
*
* @param cls closure
* @param session database connection
* @param wtid the raw wire transfer identifier we used
* @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
* @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
* @param h_contract which contract was this payment about
* @param transaction_id merchant's transaction ID for the payment
* @param execution_time when did we execute the transaction
* @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 mint for this coin
* @param coin_value amount contributed by this coin in total
* @param coin_fee deposit fee charged by mint for this coin
* @param transfer_value total amount of the wire transfer
* @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
*/
int
(*insert_aggregation_tracking)(void *cls,
struct TALER_MINTDB_Session *session,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
uint64_t transaction_id,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *deposit_value,
const struct TALER_Amount *deposit_fee);
const struct TALER_Amount *coin_value,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *transfer_value);
};

View File

@ -20,10 +20,12 @@ libtalermint_la_SOURCES = \
mint_api_handle.c mint_api_handle.h \
mint_api_admin.c \
mint_api_deposit.c \
mint_api_deposit_wtid.c \
mint_api_refresh.c \
mint_api_refresh_link.c \
mint_api_reserve.c \
mint_api_wire.c
mint_api_wire.c \
mint_api_wire_deposits.c
libtalermint_la_LIBADD = \
-lgnunetutil \

View File

@ -288,7 +288,7 @@ TALER_MINT_perform (struct TALER_MINT_Context *ctx)
GNUNET_assert (CURLE_OK ==
curl_easy_getinfo (cmsg->easy_handle,
CURLINFO_PRIVATE,
(char *) &job));
(char **) &job));
GNUNET_assert (job->ctx == ctx);
job->jcc (job->jcc_cls,
cmsg->easy_handle);

View File

@ -0,0 +1,386 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015, 2016 GNUnet e.V.
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
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file mint-lib/mint_api_deposit_wtid.c
* @brief Implementation of the /deposit/wtid request of the mint's HTTP API
* @author Christian Grothoff
*/
#include "platform.h"
#include <curl/curl.h>
#include <jansson.h>
#include <microhttpd.h> /* just for HTTP status codes */
#include <gnunet/gnunet_util_lib.h>
#include "taler_mint_service.h"
#include "mint_api_common.h"
#include "mint_api_json.h"
#include "mint_api_context.h"
#include "mint_api_handle.h"
#include "taler_signatures.h"
/**
* @brief A Deposit Wtid Handle
*/
struct TALER_MINT_DepositWtidHandle
{
/**
* The connection to mint this request handle will use
*/
struct TALER_MINT_Handle *mint;
/**
* The url for this request.
*/
char *url;
/**
* JSON encoding of the request to POST.
*/
char *json_enc;
/**
* Handle for the request.
*/
struct MAC_Job *job;
/**
* Function to call with the result.
*/
TALER_MINT_DepositWtidCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Download buffer
*/
struct MAC_DownloadBuffer db;
/**
* Information the mint should sign in response.
* (with pre-filled fields from the request).
*/
struct TALER_ConfirmWirePS depconf;
};
/**
* Verify that the signature on the "200 OK" response
* from the mint is valid.
*
* @param dwh deposit wtid handle
* @param json json reply with the signature
* @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
*/
static int
verify_deposit_wtid_signature_ok (const struct TALER_MINT_DepositWtidHandle *dwh,
json_t *json)
{
struct TALER_MintSignatureP mint_sig;
struct TALER_MintPublicKeyP mint_pub;
const struct TALER_MINT_Keys *key_state;
struct MAJ_Specification spec[] = {
MAJ_spec_fixed_auto ("mint_sig", &mint_sig),
MAJ_spec_fixed_auto ("mint_pub", &mint_pub),
MAJ_spec_end
};
if (GNUNET_OK !=
MAJ_parse_json (json,
spec))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
key_state = TALER_MINT_get_keys (dwh->mint);
if (GNUNET_OK !=
TALER_MINT_test_signing_key (key_state,
&mint_pub))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_CONFIRM_WIRE,
&dwh->depconf.purpose,
&mint_sig.eddsa_signature,
&mint_pub.eddsa_pub))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/**
* Function called when we're done processing the
* HTTP /deposit/wtid request.
*
* @param cls the `struct TALER_MINT_DepositWtidHandle`
* @param eh the curl request handle
*/
static void
handle_deposit_wtid_finished (void *cls,
CURL *eh)
{
struct TALER_MINT_DepositWtidHandle *dwh = cls;
long response_code;
json_t *json;
const struct TALER_WireTransferIdentifierRawP *wtid = NULL;
struct GNUNET_TIME_Absolute execution_time = GNUNET_TIME_UNIT_FOREVER_ABS;
const struct TALER_Amount *coin_contribution = NULL;
const struct TALER_Amount *total_amount = NULL;
struct TALER_Amount coin_contribution_s;
struct TALER_Amount total_amount_s;
dwh->job = NULL;
json = MAC_download_get_result (&dwh->db,
eh,
&response_code);
switch (response_code)
{
case 0:
break;
case MHD_HTTP_OK:
{
struct MAJ_Specification spec[] = {
MAJ_spec_fixed_auto ("wtid", &dwh->depconf.wtid),
MAJ_spec_absolute_time ("execution_time", &execution_time),
MAJ_spec_amount ("coin_contribution", &coin_contribution_s),
MAJ_spec_amount ("total_amount", &total_amount_s),
MAJ_spec_end
};
if (GNUNET_OK !=
MAJ_parse_json (json,
spec))
{
GNUNET_break_op (0);
response_code = 0;
break;
}
wtid = &dwh->depconf.wtid;
dwh->depconf.execution_time = GNUNET_TIME_absolute_hton (execution_time);
TALER_amount_hton (&dwh->depconf.coin_contribution,
&coin_contribution_s);
coin_contribution = &coin_contribution_s;
TALER_amount_hton (&dwh->depconf.total_amount,
&total_amount_s);
total_amount = &total_amount_s;
if (GNUNET_OK !=
verify_deposit_wtid_signature_ok (dwh,
json))
{
GNUNET_break_op (0);
response_code = 0;
}
}
break;
case MHD_HTTP_ACCEPTED:
{
/* Transaction known, but not executed yet */
struct MAJ_Specification spec[] = {
MAJ_spec_absolute_time ("execution_time", &execution_time),
MAJ_spec_end
};
if (GNUNET_OK !=
MAJ_parse_json (json,
spec))
{
GNUNET_break_op (0);
response_code = 0;
break;
}
}
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the mint is buggy
(or API version conflict); just pass JSON reply to the application */
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, mint says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
break;
case MHD_HTTP_NOT_FOUND:
/* Mint does not know about transaction;
we should pass the reply to the application */
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
response_code);
GNUNET_break (0);
response_code = 0;
break;
}
dwh->cb (dwh->cb_cls,
response_code,
json,
wtid,
execution_time,
coin_contribution,
total_amount);
json_decref (json);
TALER_MINT_deposit_wtid_cancel (dwh);
}
/**
* Obtain wire transfer details about an existing deposit operation.
*
* @param mint the mint to query
* @param merchant_priv the merchant's private key
* @param h_wire hash of merchant's wire transfer details
* @param h_contract hash of the contract
* @param coin_pub public key of the coin
* @param transaction_id transaction identifier
* @param cb function to call with the result
* @param cb_cls closure for @a cb
* @return handle to abort request
*/
struct TALER_MINT_DepositWtidHandle *
TALER_MINT_deposit_wtid (struct TALER_MINT_Handle *mint,
const struct TALER_MerchantPrivateKeyP *merchant_priv,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t transaction_id,
TALER_MINT_DepositWtidCallback cb,
void *cb_cls)
{
struct TALER_DepositTrackPS dtp;
struct TALER_MerchantSignatureP merchant_sig;
struct TALER_MINT_DepositWtidHandle *dwh;
struct TALER_MINT_Context *ctx;
json_t *deposit_wtid_obj;
CURL *eh;
if (GNUNET_YES !=
MAH_handle_is_ready (mint))
{
GNUNET_break (0);
return NULL;
}
dtp.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_DEPOSIT_WTID);
dtp.purpose.size = htonl (sizeof (dtp));
dtp.h_contract = *h_contract;
dtp.h_wire = *h_wire;
dtp.transaction_id = GNUNET_htonll (transaction_id);
GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
&dtp.merchant.eddsa_pub);
dtp.coin_pub = *coin_pub;
GNUNET_assert (GNUNET_OK ==
GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv,
&dtp.purpose,
&merchant_sig.eddsa_sig));
deposit_wtid_obj = json_pack ("{s:o, s:o," /* H_wire, H_contract */
" s:o, s:I," /* coin_pub, transaction_id */
" s:o, s:o}", /* merchant_pub, merchant_sig */
"H_wire", TALER_json_from_data (h_wire,
sizeof (struct GNUNET_HashCode)),
"H_contract", TALER_json_from_data (h_contract,
sizeof (struct GNUNET_HashCode)),
"coin_pub", TALER_json_from_data (coin_pub,
sizeof (*coin_pub)),
"transaction_id", (json_int_t) transaction_id,
"merchant_pub", TALER_json_from_data (&dtp.merchant,
sizeof (struct TALER_MerchantPublicKeyP)),
"merchant_sig", TALER_json_from_data (&merchant_sig,
sizeof (merchant_sig)));
dwh = GNUNET_new (struct TALER_MINT_DepositWtidHandle);
dwh->mint = mint;
dwh->cb = cb;
dwh->cb_cls = cb_cls;
dwh->url = MAH_path_to_url (mint, "/deposit/wtid");
dwh->depconf.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
dwh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_WIRE);
dwh->depconf.h_wire = *h_wire;
dwh->depconf.h_contract = *h_contract;
dwh->depconf.coin_pub = *coin_pub;
dwh->depconf.transaction_id = GNUNET_htonll (transaction_id);
eh = curl_easy_init ();
GNUNET_assert (NULL != (dwh->json_enc =
json_dumps (deposit_wtid_obj,
JSON_COMPACT)));
json_decref (deposit_wtid_obj);
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_URL,
dwh->url));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_POSTFIELDS,
dwh->json_enc));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_POSTFIELDSIZE,
strlen (dwh->json_enc)));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEFUNCTION,
&MAC_download_cb));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEDATA,
&dwh->db));
ctx = MAH_handle_to_context (mint);
dwh->job = MAC_job_add (ctx,
eh,
GNUNET_YES,
&handle_deposit_wtid_finished,
dwh);
return dwh;
}
/**
* Cancel deposit wtid request. This function cannot be used on a request
* handle if a response is already served for it.
*
* @param dwh the wire deposits request handle
*/
void
TALER_MINT_deposit_wtid_cancel (struct TALER_MINT_DepositWtidHandle *dwh)
{
if (NULL != dwh->job)
{
MAC_job_cancel (dwh->job);
dwh->job = NULL;
}
GNUNET_free_non_null (dwh->db.buf);
GNUNET_free (dwh->url);
GNUNET_free (dwh->json_enc);
GNUNET_free (dwh);
}
/* end of mint_api_deposit_wtid.c */

View File

@ -757,7 +757,7 @@ TALER_MINT_connect (struct TALER_MINT_Context *ctx,
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (c,
CURLOPT_VERBOSE,
1));
0));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (c,
CURLOPT_STDERR,

View File

@ -232,6 +232,20 @@ parse_json (json_t *root,
}
break;
case MAJ_CMD_UINT64:
{
json_int_t val;
if (! json_is_integer (pos))
{
GNUNET_break_op (0);
return i;
}
val = json_integer_value (pos);
*spec[i].details.u64 = (uint64_t) val;
}
break;
case MAJ_CMD_JSON_OBJECT:
{
if (! (json_is_object (pos) || json_is_array (pos)) )
@ -428,6 +442,26 @@ MAJ_spec_uint16 (const char *name,
}
/**
* 64-bit integer.
*
* @param name name of the JSON field
* @param[out] u64 where to store the integer found under @a name
*/
struct MAJ_Specification
MAJ_spec_uint64 (const char *name,
uint64_t *u64)
{
struct MAJ_Specification ret =
{
.cmd = MAJ_CMD_UINT64,
.field = name,
.details.u64 = u64
};
return ret;
}
/**
* JSON object.
*

View File

@ -78,6 +78,11 @@ enum MAJ_Command
*/
MAJ_CMD_UINT16,
/**
* Parse `uint64_t` integer at the current position.
*/
MAJ_CMD_UINT64,
/**
* Parse JSON object at the current position.
*/
@ -191,6 +196,11 @@ struct MAJ_Specification
*/
uint16_t *u16;
/**
* Where to store 64-bit integer.
*/
uint64_t *u64;
/**
* Where to store a JSON object.
*/
@ -282,6 +292,17 @@ MAJ_spec_uint16 (const char *name,
uint16_t *u16);
/**
* 64-bit integer.
*
* @param name name of the JSON field
* @param[out] u64 where to store the integer found under @a name
*/
struct MAJ_Specification
MAJ_spec_uint64 (const char *name,
uint64_t *u64);
/**
* JSON object.
*

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2015 GNUnet e.V.
Copyright (C) 2015, 2016 GNUnet e.V.
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
@ -185,6 +185,17 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
return GNUNET_SYSERR;
}
num_coins = 0;
/* Theoretically, a coin may have been melted repeatedly
into different sessions; so the response is an array
which contains information by melting session. That
array contains another array. However, our API returns
a single 1d array, so we flatten the 2d array that is
returned into a single array. Note that usually a coin
is melted at most once, and so we'll only run this
loop once for 'session=0' in most cases.
num_coins tracks the size of the 1d array we return,
whilst 'i' and 'session' track the 2d array. */
for (session=0;session<json_array_size (json); session++)
{
json_t *jsona;
@ -212,13 +223,17 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
num_coins += json_array_size (jsona);
MAJ_parse_free (spec);
}
/* Now that we know how big the 1d array is, allocate
and fill it. */
{
unsigned int off_coin;
unsigned int off_coin; /* index into 1d array */
unsigned int i;
struct TALER_CoinSpendPrivateKeyP coin_privs[num_coins];
struct TALER_DenominationSignature sigs[num_coins];
struct TALER_DenominationPublicKey pubs[num_coins];
memset (sigs, 0, sizeof (sigs));
memset (pubs, 0, sizeof (pubs));
off_coin = 0;
for (session=0;session<json_array_size (json); session++)
{
@ -265,6 +280,7 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
}
}
/* check if we really got all, then invoke callback */
off_coin += i;
if (i != json_array_size (jsona))
{
GNUNET_break_op (0);
@ -272,7 +288,6 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
MAJ_parse_free (spec);
break;
}
off_coin += json_array_size (jsona);
MAJ_parse_free (spec);
} /* end of for (session) */
@ -295,9 +310,13 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
}
/* clean up */
for (i=0;i<num_coins;i++)
for (i=0;i<off_coin;i++)
{
if (NULL != sigs[i].rsa_signature)
GNUNET_CRYPTO_rsa_signature_free (sigs[i].rsa_signature);
if (NULL != pubs[i].rsa_public_key)
GNUNET_CRYPTO_rsa_public_key_free (pubs[i].rsa_public_key);
}
}
return ret;
}

View File

@ -0,0 +1,284 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015, 2016 GNUnet e.V.
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
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file mint-lib/mint_api_wire_deposits.c
* @brief Implementation of the /wire/deposits request of the mint's HTTP API
* @author Christian Grothoff
*/
#include "platform.h"
#include <curl/curl.h>
#include <jansson.h>
#include <microhttpd.h> /* just for HTTP status codes */
#include <gnunet/gnunet_util_lib.h>
#include "taler_mint_service.h"
#include "mint_api_common.h"
#include "mint_api_json.h"
#include "mint_api_context.h"
#include "mint_api_handle.h"
#include "taler_signatures.h"
/**
* @brief A /wire/deposits Handle
*/
struct TALER_MINT_WireDepositsHandle
{
/**
* The connection to mint this request handle will use
*/
struct TALER_MINT_Handle *mint;
/**
* The url for this request.
*/
char *url;
/**
* Handle for the request.
*/
struct MAC_Job *job;
/**
* Function to call with the result.
*/
TALER_MINT_WireDepositsCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Download buffer
*/
struct MAC_DownloadBuffer db;
};
/**
* Function called when we're done processing the
* HTTP /wire/deposits request.
*
* @param cls the `struct TALER_MINT_WireDepositsHandle`
* @param eh the curl request handle
*/
static void
handle_wire_deposits_finished (void *cls,
CURL *eh)
{
struct TALER_MINT_WireDepositsHandle *wdh = cls;
long response_code;
json_t *json;
wdh->job = NULL;
json = MAC_download_get_result (&wdh->db,
eh,
&response_code);
switch (response_code)
{
case 0:
break;
case MHD_HTTP_OK:
{
json_t *details_j;
struct GNUNET_HashCode h_wire;
struct TALER_Amount total_amount;
struct TALER_MerchantPublicKeyP merchant_pub;
unsigned int num_details;
struct MAJ_Specification spec[] = {
MAJ_spec_fixed_auto ("H_wire", &h_wire),
MAJ_spec_fixed_auto ("merchant_pub", &merchant_pub),
MAJ_spec_amount ("total_amount", &total_amount),
MAJ_spec_json ("details", &details_j),
MAJ_spec_end
};
if (GNUNET_OK !=
MAJ_parse_json (json,
spec))
{
GNUNET_break_op (0);
response_code = 0;
break;
}
num_details = json_array_size (details_j);
{
struct TALER_WireDepositDetails details[num_details];
unsigned int i;
for (i=0;i<num_details;i++)
{
struct TALER_WireDepositDetails *detail = &details[i];
struct json_t *detail_j = json_array_get (details_j, i);
struct MAJ_Specification spec_detail[] = {
MAJ_spec_fixed_auto ("H_contract", &detail->h_contract),
MAJ_spec_amount ("deposit_value", &detail->coin_value),
MAJ_spec_amount ("deposit_fee", &detail->coin_fee),
MAJ_spec_uint64 ("transaction_id", &detail->transaction_id),
MAJ_spec_fixed_auto ("coin_pub", &detail->coin_pub),
MAJ_spec_end
};
if (GNUNET_OK !=
MAJ_parse_json (detail_j,
spec_detail))
{
GNUNET_break_op (0);
response_code = 0;
break;
}
}
if (0 == response_code)
break;
wdh->cb (wdh->cb_cls,
response_code,
json,
&h_wire,
&total_amount,
num_details,
details);
json_decref (json);
TALER_MINT_wire_deposits_cancel (wdh);
return;
}
}
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the mint is buggy
(or API version conflict); just pass JSON reply to the application */
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, mint says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
break;
case MHD_HTTP_NOT_FOUND:
/* Mint does not know about transaction;
we should pass the reply to the application */
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
response_code);
GNUNET_break (0);
response_code = 0;
break;
}
wdh->cb (wdh->cb_cls,
response_code,
json,
NULL, NULL, 0, NULL);
json_decref (json);
TALER_MINT_wire_deposits_cancel (wdh);
}
/**
* Query the mint about which transactions were combined
* to create a wire transfer.
*
* @param mint mint to query
* @param wtid raw wire transfer identifier to get information about
* @param cb callback to call
* @param cb_cls closure for @a cb
* @return handle to cancel operation
*/
struct TALER_MINT_WireDepositsHandle *
TALER_MINT_wire_deposits (struct TALER_MINT_Handle *mint,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_MINT_WireDepositsCallback cb,
void *cb_cls)
{
struct TALER_MINT_WireDepositsHandle *wdh;
struct TALER_MINT_Context *ctx;
char *buf;
char *path;
CURL *eh;
if (GNUNET_YES !=
MAH_handle_is_ready (mint))
{
GNUNET_break (0);
return NULL;
}
wdh = GNUNET_new (struct TALER_MINT_WireDepositsHandle);
wdh->mint = mint;
wdh->cb = cb;
wdh->cb_cls = cb_cls;
buf = GNUNET_STRINGS_data_to_string_alloc (wtid,
sizeof (struct TALER_WireTransferIdentifierRawP));
GNUNET_asprintf (&path,
"/wire/deposits?wtid=%s",
buf);
wdh->url = MAH_path_to_url (wdh->mint,
path);
GNUNET_free (buf);
GNUNET_free (path);
eh = curl_easy_init ();
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_URL,
wdh->url));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEFUNCTION,
&MAC_download_cb));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEDATA,
&wdh->db));
ctx = MAH_handle_to_context (mint);
wdh->job = MAC_job_add (ctx,
eh,
GNUNET_YES,
&handle_wire_deposits_finished,
wdh);
return wdh;
}
/**
* Cancel wire deposits request. This function cannot be used on a request
* handle if a response is already served for it.
*
* @param wdh the wire deposits request handle
*/
void
TALER_MINT_wire_deposits_cancel (struct TALER_MINT_WireDepositsHandle *wdh)
{
if (NULL != wdh->job)
{
MAC_job_cancel (wdh->job);
wdh->job = NULL;
}
GNUNET_free_non_null (wdh->db.buf);
GNUNET_free (wdh->url);
GNUNET_free (wdh);
}
/* end of mint_api_wire_deposits.c */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 GNUnet e.V.
Copyright (C) 2014, 2015, 2016 GNUnet e.V.
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
@ -111,7 +111,17 @@ enum OpCode
/**
* Verify the mint's /wire-method.
*/
OC_WIRE
OC_WIRE,
/**
* Verify mint's /wire/deposits method.
*/
OC_WIRE_DEPOSITS,
/**
* Verify mint's /deposit/wtid method.
*/
OC_DEPOSIT_WTID
};
@ -470,6 +480,60 @@ struct Command
} wire;
/**
* Information for the /wire/deposits's command.
*/
struct {
/**
* Handle to the wire deposits request.
*/
struct TALER_MINT_WireDepositsHandle *wdh;
/**
* Reference to a /deposit/wtid command. If set, we use the
* WTID from that command.
*/
const char *wtid_ref;
/**
* WTID to use (used if @e wtid_ref is NULL).
*/
struct TALER_WireTransferIdentifierRawP wtid;
/* TODO: may want to add list of deposits we expected
to see aggregated here in the future. */
} wire_deposits;
/**
* Information for the /deposit/wtid command.
*/
struct {
/**
* Handle to the deposit wtid request.
*/
struct TALER_MINT_DepositWtidHandle *dwh;
/**
* Which /deposit operation should we obtain WTID data for?
*/
const char *deposit_ref;
/**
* What is the expected total amount? Only used if
* @e expected_response_code was #MHD_HTTP_OK.
*/
struct TALER_Amount total_amount_expected;
/**
* Wire transfer identifier, set if #MHD_HTTP_OK was the response code.
*/
struct TALER_WireTransferIdentifierRawP wtid;
} deposit_wtid;
} details;
};
@ -1219,6 +1283,158 @@ wire_cb (void *cls,
}
/**
* Function called with detailed wire transfer data, including all
* of the coin transactions that were combined into the wire transfer.
*
* @param cls closure
* @param http_status HTTP status code we got, 0 on mint protocol violation
* @param json original json reply (may include signatures, those have then been
* validated already)
* @param wtid extracted wire transfer identifier, or NULL if the mint could
* not provide any (set only if @a http_status is #MHD_HTTP_OK)
* @param total_amount total amount of the wire transfer, or NULL if the mint could
* not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
* @param details_length length of the @a details array
* @param details array with details about the combined transactions
*/
static void
wire_deposits_cb (void *cls,
unsigned int http_status,
json_t *json,
const struct GNUNET_HashCode *h_wire,
const struct TALER_Amount *total_amount,
unsigned int details_length,
const struct TALER_WireDepositDetails *details)
{
struct InterpreterState *is = cls;
struct Command *cmd = &is->commands[is->ip];
const struct Command *ref;
cmd->details.wire_deposits.wdh = NULL;
ref = find_command (is,
cmd->details.wire_deposits.wtid_ref);
if (cmd->expected_response_code != http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s\n",
http_status,
cmd->label);
json_dumpf (json, stderr, 0);
fail (is);
return;
}
switch (http_status)
{
case MHD_HTTP_OK:
if (0 != TALER_amount_cmp (total_amount,
&ref->details.deposit_wtid.total_amount_expected))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Total amount missmatch to command %s\n",
http_status,
cmd->label);
json_dumpf (json, stderr, 0);
fail (is);
return;
}
if (NULL != ref->details.deposit_wtid.deposit_ref)
{
const struct Command *dep;
struct GNUNET_HashCode hw;
dep = find_command (is,
ref->details.deposit_wtid.deposit_ref);
GNUNET_CRYPTO_hash (dep->details.deposit.wire_details,
strlen (dep->details.deposit.wire_details),
&hw);
if (0 != memcmp (&hw,
h_wire,
sizeof (struct GNUNET_HashCode)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire hash missmatch to command %s\n",
cmd->label);
json_dumpf (json, stderr, 0);
fail (is);
return;
}
}
break;
default:
break;
}
/* move to next command */
is->ip++;
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
}
/**
* Function called with detailed wire transfer data.
*
* @param cls closure
* @param http_status HTTP status code we got, 0 on mint protocol violation
* @param json original json reply (may include signatures, those have then been
* validated already)
* @param wtid wire transfer identifier used by the mint, NULL if mint did not
* yet execute the transaction
* @param execution_time actual or planned execution time for the wire transfer
* @param coin_contribution contribution to the @a total_amount of the deposited coin (may be NULL)
* @param total_amount total amount of the wire transfer, or NULL if the mint could
* not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
*/
static void
deposit_wtid_cb (void *cls,
unsigned int http_status,
json_t *json,
const struct TALER_WireTransferIdentifierRawP *wtid,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *coin_contribution,
const struct TALER_Amount *total_amount)
{
struct InterpreterState *is = cls;
struct Command *cmd = &is->commands[is->ip];
cmd->details.deposit_wtid.dwh = NULL;
if (cmd->expected_response_code != http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s\n",
http_status,
cmd->label);
json_dumpf (json, stderr, 0);
fail (is);
return;
}
switch (http_status)
{
case MHD_HTTP_OK:
cmd->details.deposit_wtid.wtid = *wtid;
if (0 != TALER_amount_cmp (total_amount,
&cmd->details.deposit_wtid.total_amount_expected))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Total amount missmatch to command %s\n",
cmd->label);
json_dumpf (json, stderr, 0);
fail (is);
return;
}
break;
default:
break;
}
/* move to next command */
is->ip++;
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
}
/**
* Run the main interpreter loop that performs mint operations.
*
@ -1401,7 +1617,9 @@ interpreter_run (void *cls,
struct GNUNET_TIME_Absolute refund_deadline;
struct GNUNET_TIME_Absolute wire_deadline;
struct GNUNET_TIME_Absolute timestamp;
struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
struct TALER_MerchantPublicKeyP merchant_pub;
json_t *contract;
json_t *wire;
GNUNET_assert (NULL !=
@ -1444,37 +1662,51 @@ interpreter_run (void *cls,
fail (is);
return;
}
GNUNET_CRYPTO_hash (cmd->details.deposit.contract,
strlen (cmd->details.deposit.contract),
&h_contract);
contract = json_loads (cmd->details.deposit.contract,
JSON_REJECT_DUPLICATES,
NULL);
if (NULL == contract)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse contract details `%s' at %u/%s\n",
cmd->details.deposit.contract,
is->ip,
cmd->label);
fail (is);
return;
}
TALER_hash_json (contract,
&h_contract);
wire = json_loads (cmd->details.deposit.wire_details,
JSON_REJECT_DUPLICATES,
NULL);
if (NULL == wire)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse wire details `%s' at %u\n",
"Failed to parse wire details `%s' at %u/%s\n",
cmd->details.deposit.wire_details,
is->ip);
is->ip,
cmd->label);
fail (is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
&coin_pub.eddsa_pub);
priv = GNUNET_CRYPTO_eddsa_key_create ();
cmd->details.deposit.merchant_priv.eddsa_priv = *priv;
GNUNET_free (priv);
if (0 != cmd->details.deposit.refund_deadline.rel_value_us)
{
struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
priv = GNUNET_CRYPTO_eddsa_key_create ();
cmd->details.deposit.merchant_priv.eddsa_priv = *priv;
GNUNET_free (priv);
refund_deadline = GNUNET_TIME_relative_to_absolute (cmd->details.deposit.refund_deadline);
}
else
{
refund_deadline = GNUNET_TIME_UNIT_ZERO_ABS;
}
GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.deposit.merchant_priv.eddsa_priv,
&merchant_pub.eddsa_pub);
wire_deadline = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
timestamp = GNUNET_TIME_absolute_get ();
TALER_round_abs_time (&timestamp);
@ -1686,6 +1918,85 @@ interpreter_run (void *cls,
is);
trigger_context_task ();
return;
case OC_WIRE_DEPOSITS:
if (NULL != cmd->details.wire_deposits.wtid_ref)
{
ref = find_command (is,
cmd->details.wire_deposits.wtid_ref);
GNUNET_assert (NULL != ref);
cmd->details.wire_deposits.wtid = ref->details.deposit_wtid.wtid;
}
cmd->details.wire_deposits.wdh
= TALER_MINT_wire_deposits (mint,
&cmd->details.wire_deposits.wtid,
&wire_deposits_cb,
is);
trigger_context_task ();
return;
case OC_DEPOSIT_WTID:
{
struct GNUNET_HashCode h_wire;
struct GNUNET_HashCode h_contract;
json_t *wire;
json_t *contract;
const struct Command *coin;
struct TALER_CoinSpendPublicKeyP coin_pub;
ref = find_command (is,
cmd->details.deposit_wtid.deposit_ref);
GNUNET_assert (NULL != ref);
coin = find_command (is,
ref->details.deposit.coin_ref);
GNUNET_assert (NULL != coin);
switch (coin->oc)
{
case OC_WITHDRAW_SIGN:
GNUNET_CRYPTO_eddsa_key_get_public (&coin->details.reserve_withdraw.coin_priv.eddsa_priv,
&coin_pub.eddsa_pub);
break;
case OC_REFRESH_REVEAL:
{
const struct FreshCoin *fc;
unsigned int idx;
idx = ref->details.deposit.coin_idx;
GNUNET_assert (idx < coin->details.refresh_reveal.num_fresh_coins);
fc = &coin->details.refresh_reveal.fresh_coins[idx];
GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
&coin_pub.eddsa_pub);
}
break;
default:
GNUNET_assert (0);
}
wire = json_loads (ref->details.deposit.wire_details,
JSON_REJECT_DUPLICATES,
NULL);
GNUNET_assert (NULL != wire);
TALER_hash_json (wire,
&h_wire);
json_decref (wire);
contract = json_loads (ref->details.deposit.contract,
JSON_REJECT_DUPLICATES,
NULL);
GNUNET_assert (NULL != contract);
TALER_hash_json (contract,
&h_contract);
json_decref (contract);
cmd->details.deposit_wtid.dwh
= TALER_MINT_deposit_wtid (mint,
&ref->details.deposit.merchant_priv,
&h_wire,
&h_contract,
&coin_pub,
ref->details.deposit.transaction_id,
&deposit_wtid_cb,
is);
trigger_context_task ();
}
return;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unknown instruction %d at %u (%s)\n",
@ -1695,8 +2006,6 @@ interpreter_run (void *cls,
fail (is);
return;
}
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
}
@ -1829,10 +2138,36 @@ do_shutdown (void *cls,
case OC_WIRE:
if (NULL != cmd->details.wire.wh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
i,
cmd->label);
TALER_MINT_wire_cancel (cmd->details.wire.wh);
cmd->details.wire.wh = NULL;
}
break;
case OC_WIRE_DEPOSITS:
if (NULL != cmd->details.wire_deposits.wdh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
i,
cmd->label);
TALER_MINT_wire_deposits_cancel (cmd->details.wire_deposits.wdh);
cmd->details.wire_deposits.wdh = NULL;
}
break;
case OC_DEPOSIT_WTID:
if (NULL != cmd->details.deposit_wtid.dwh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
i,
cmd->label);
TALER_MINT_deposit_wtid_cancel (cmd->details.deposit_wtid.dwh);
cmd->details.deposit_wtid.dwh = NULL;
}
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unknown instruction %d at %u (%s)\n",
@ -2047,7 +2382,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }",
.details.deposit.transaction_id = 1 },
/* Try to overdraw funds ... */
@ -2064,7 +2399,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":43 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }",
.details.deposit.transaction_id = 1 },
/* Try to double-spend the 5 EUR coin at the same merchant (but different
transaction ID) */
@ -2074,7 +2409,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }",
.details.deposit.transaction_id = 2 },
/* Try to double-spend the 5 EUR coin at the same merchant (but different
contract) */
@ -2084,7 +2419,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":2 } }",
.details.deposit.contract = "{ \"items\":[{ \"name\":\"ice cream\", \"value\":2 } ] }",
.details.deposit.transaction_id = 1 },
/* ***************** /refresh testing ******************** */
@ -2109,7 +2444,7 @@ run (void *cls,
.details.deposit.amount = "EUR:1",
.details.deposit.coin_ref = "refresh-withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\"EUR:1 } }",
.details.deposit.contract = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:1\" } ] }",
.details.deposit.transaction_id = 42421 },
/* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
@ -2143,7 +2478,7 @@ run (void *cls,
.details.deposit.coin_ref = "refresh-reveal-1",
.details.deposit.coin_idx = 0,
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":3 } }",
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }",
.details.deposit.transaction_id = 2 },
/* Test successfully spending coins from the refresh operation:
@ -2155,7 +2490,7 @@ run (void *cls,
.details.deposit.coin_ref = "refresh-reveal-1",
.details.deposit.coin_idx = 4,
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":3 } }",
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }",
.details.deposit.transaction_id = 2 },
/* Test running a failing melt operation (same operation again must fail) */
@ -2168,6 +2503,35 @@ run (void *cls,
// FIXME: also test with coin that was already melted
// (signature differs from coin that was deposited...)
/* *************** end of /refresh testing ************** */
/* ************** Test tracking API ******************** */
/* Try resolving a deposit's WTID, as we never triggered
execution of transactions, the answer should be that
the mint knows about the deposit, but has no WTID yet. */
{ .oc = OC_DEPOSIT_WTID,
.label = "deposit-wtid-found",
.expected_response_code = MHD_HTTP_ACCEPTED,
.details.deposit_wtid.deposit_ref = "deposit-simple" },
/* Try resolving a deposit's WTID for a failed deposit.
As the deposit failed, the answer should be that
the mint does NOT know about the deposit. */
{ .oc = OC_DEPOSIT_WTID,
.label = "deposit-wtid-failing",
.expected_response_code = MHD_HTTP_NOT_FOUND,
.details.deposit_wtid.deposit_ref = "deposit-double-2" },
/* Try resolving an undefined (all zeros) WTID; this
should fail as obviously the mint didn't use that
WTID value for any transaction. */
{ .oc = OC_WIRE_DEPOSITS,
.label = "wire-deposit-failing",
.expected_response_code = MHD_HTTP_NOT_FOUND },
/* TODO: trigger aggregation logic and then check the
cases where tracking succeeds! */
/* ************** End of tracking API testing************* */
#endif
{ .oc = OC_END }

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 GNUnet e.V.
Copyright (C) 2014, 2015, 2016 GNUnet e.V.
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
@ -1565,6 +1565,12 @@ struct WtidTransactionContext
*/
struct TALER_Amount total;
/**
* Value we find in the DB for the @e total; only valid if @e is_valid
* is #GNUNET_YES.
*/
struct TALER_Amount db_transaction_value;
/**
* Public key of the merchant, only valid if @e is_valid
* is #GNUNET_YES.
@ -1606,6 +1612,7 @@ struct WtidTransactionContext
* @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 mint for this coin
* @param transaction_value total value of the wire transaction
*/
static void
handle_transaction_data (void *cls,
@ -1615,7 +1622,8 @@ handle_transaction_data (void *cls,
uint64_t transaction_id,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *deposit_value,
const struct TALER_Amount *deposit_fee)
const struct TALER_Amount *deposit_fee,
const struct TALER_Amount *transaction_value)
{
struct WtidTransactionContext *ctx = cls;
struct TALER_Amount delta;
@ -1626,6 +1634,7 @@ handle_transaction_data (void *cls,
{
ctx->merchant_pub = *merchant_pub;
ctx->h_wire = *h_wire;
ctx->db_transaction_value = *transaction_value;
ctx->is_valid = GNUNET_YES;
if (GNUNET_OK !=
TALER_amount_subtract (&ctx->total,
@ -1644,7 +1653,9 @@ handle_transaction_data (void *cls,
sizeof (struct TALER_MerchantPublicKeyP))) ||
(0 != memcmp (&ctx->h_wire,
h_wire,
sizeof (struct GNUNET_HashCode))) )
sizeof (struct GNUNET_HashCode))) ||
(0 != TALER_amount_cmp (transaction_value,
&ctx->db_transaction_value)) )
{
GNUNET_break (0);
ctx->is_valid = GNUNET_SYSERR;
@ -1693,17 +1704,25 @@ handle_transaction_data (void *cls,
*/
int
TMH_DB_execute_wire_deposits (struct MHD_Connection *connection,
const struct TALER_WireTransferIdentifierP *wtid)
const struct TALER_WireTransferIdentifierRawP *wtid)
{
int ret;
struct WtidTransactionContext ctx;
struct TALER_MINTDB_Session *session;
if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
TMH_test_mode)))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
ctx.is_valid = GNUNET_NO;
ctx.deposits = json_array ();
ret = TMH_plugin->lookup_wire_transactions (TMH_plugin->cls,
&wtid->raw,
&handle_transaction_data,
&ctx);
ret = TMH_plugin->lookup_wire_transfer (TMH_plugin->cls,
session,
wtid,
&handle_transaction_data,
&ctx);
if (GNUNET_SYSERR == ret)
{
GNUNET_break (0);
@ -1722,8 +1741,15 @@ TMH_DB_execute_wire_deposits (struct MHD_Connection *connection,
return TMH_RESPONSE_reply_arg_unknown (connection,
"wtid");
}
if (0 != TALER_amount_cmp (&ctx.total,
&ctx.db_transaction_value))
{
/* FIXME: this CAN actually differ, due to rounding
down. But we should still check that the values
do match after rounding 'total' down! */
}
return TMH_RESPONSE_reply_wire_deposit_details (connection,
&ctx.total,
&ctx.db_transaction_value,
&ctx.merchant_pub,
&ctx.h_wire,
ctx.deposits);
@ -1776,7 +1802,8 @@ struct DepositWtidContext
* @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 minus fee)
* contribute to the total transfer value? (deposit value including fee)
* @param coin_fee how much did the mint charge for the deposit fee
* @param total_amount how much was the total wire transfer?
* @param execution_time when was the transaction done, or
* when we expect it to be done (if @a wtid was NULL);
@ -1787,31 +1814,40 @@ static void
handle_wtid_data (void *cls,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *coin_contribution,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *total_amount,
struct GNUNET_TIME_Absolute execution_time)
{
struct DepositWtidContext *ctx = cls;
struct TALER_Amount coin_delta;
if (NULL == wtid)
{
if (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us ==
execution_time.abs_value_us)
ctx->res = TMH_RESPONSE_reply_deposit_unknown (ctx->connection);
else
ctx->res = TMH_RESPONSE_reply_deposit_pending (ctx->connection,
execution_time);
ctx->res = TMH_RESPONSE_reply_deposit_pending (ctx->connection,
execution_time);
}
else
{
ctx->res = TMH_RESPONSE_reply_deposit_wtid (ctx->connection,
&ctx->h_contract,
&ctx->h_wire,
&ctx->coin_pub,
coin_contribution,
total_amount,
ctx->transaction_id,
wtid,
execution_time);
if (GNUNET_SYSERR ==
TALER_amount_subtract (&coin_delta,
coin_contribution,
coin_fee))
{
GNUNET_break (0);
ctx->res = TMH_RESPONSE_reply_internal_db_error (ctx->connection);
}
else
{
ctx->res = TMH_RESPONSE_reply_deposit_wtid (ctx->connection,
&ctx->h_contract,
&ctx->h_wire,
&ctx->coin_pub,
&coin_delta,
total_amount,
ctx->transaction_id,
wtid,
execution_time);
}
}
}
@ -1838,14 +1874,22 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection,
{
int ret;
struct DepositWtidContext ctx;
struct TALER_MINTDB_Session *session;
if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
TMH_test_mode)))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
ctx.connection = connection;
ctx.h_contract = *h_contract;
ctx.h_wire = *h_wire;
ctx.coin_pub = *coin_pub;
ctx.transaction_id = transaction_id;
ctx.res = MHD_NO; /* this value should never be read... */
ctx.res = GNUNET_SYSERR;
ret = TMH_plugin->wire_lookup_deposit_wtid (TMH_plugin->cls,
session,
h_contract,
h_wire,
coin_pub,
@ -1856,8 +1900,20 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection,
if (GNUNET_SYSERR == ret)
{
GNUNET_break (0);
GNUNET_break (GNUNET_SYSERR == ctx.res);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
if (GNUNET_NO == ret)
{
GNUNET_break (GNUNET_SYSERR == ctx.res);
return TMH_RESPONSE_reply_deposit_unknown (connection);
}
if (GNUNET_SYSERR == ctx.res)
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_error (connection,
"bug resolving deposit wtid");
}
return ctx.res;
}

View File

@ -202,7 +202,7 @@ TMH_DB_execute_admin_add_incoming (struct MHD_Connection *connection,
*/
int
TMH_DB_execute_wire_deposits (struct MHD_Connection *connection,
const struct TALER_WireTransferIdentifierP *wtid);
const struct TALER_WireTransferIdentifierRawP *wtid);
/**

View File

@ -1081,7 +1081,7 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection,
struct GNUNET_TIME_Absolute planned_exec_time)
{
return TMH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_FOUND,
MHD_HTTP_ACCEPTED,
"{s:o}",
"execution_time", TALER_json_from_abs (planned_exec_time));
}
@ -1117,11 +1117,7 @@ TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection,
struct TALER_ConfirmWirePS cw;
struct TALER_MintPublicKeyP pub;
struct TALER_MintSignatureP sig;
struct TALER_WireTransferIdentifierP wtid_crc;
char *wtid_s;
int ret;
/* Create signature for the reply */
cw.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_WIRE);
cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS));
cw.h_wire = *h_wire;
@ -1137,24 +1133,18 @@ TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection,
TMH_KS_sign (&cw.purpose,
&pub,
&sig);
/* Compute checksum and crockford encoding if wire transfer subject */
wtid_crc.raw = *wtid;
wtid_crc.crc8 = GNUNET_CRYPTO_crc8_n (wtid,
sizeof (struct TALER_WireTransferIdentifierRawP));
wtid_s = GNUNET_STRINGS_data_to_string_alloc (&wtid_crc,
sizeof (wtid_crc));
ret = TMH_RESPONSE_reply_json_pack (connection,
return TMH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:s, s:o, s:o, s:o}",
"wtid", wtid_s,
"{s:o, s:o, s:o, s:o, s:o, s:o}",
"wtid", TALER_json_from_data (wtid,
sizeof (*wtid)),
"execution_time", TALER_json_from_abs (exec_time),
"coin_contribution", TALER_json_from_amount (coin_contribution),
"total_amount", TALER_json_from_amount (total_amount),
"mint_sig", TALER_json_from_data (&sig,
sizeof (sig)),
"mint_pub", TALER_json_from_data (&pub,
sizeof (pub)));
GNUNET_free (wtid_s);
return ret;
}

View File

@ -46,22 +46,17 @@ TMH_TRACKING_handler_wire_deposits (struct TMH_RequestHandler *rh,
const char *upload_data,
size_t *upload_data_size)
{
struct TALER_WireTransferIdentifierP wtid;
struct TALER_WireTransferIdentifierRawP wtid;
int res;
res = TMH_PARSE_mhd_request_arg_data (connection,
"wtid",
&wtid,
sizeof (struct TALER_WireTransferIdentifierP));
sizeof (struct TALER_WireTransferIdentifierRawP));
if (GNUNET_SYSERR == res)
return MHD_NO; /* internal error */
if (GNUNET_NO == res)
return MHD_YES; /* parse error */
if (wtid.crc8 !=
GNUNET_CRYPTO_crc8_n (&wtid.raw,
sizeof (wtid.raw)))
return TMH_RESPONSE_reply_arg_invalid (connection,
"wtid");
return TMH_DB_execute_wire_deposits (connection,
&wtid);
}
@ -126,13 +121,12 @@ TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh,
struct TALER_DepositTrackPS tps;
uint64_t transaction_id;
struct TALER_MerchantSignatureP merchant_sig;
struct TALER_MerchantPublicKeyP merchant_pub;
struct TMH_PARSE_FieldSpecification spec[] = {
TMH_PARSE_member_fixed ("H_wire", &tps.h_wire),
TMH_PARSE_member_fixed ("H_contract", &tps.h_contract),
TMH_PARSE_member_fixed ("coin_pub", &tps.coin_pub),
TMH_PARSE_member_uint64 ("transaction_id", &transaction_id),
TMH_PARSE_member_fixed ("merchant_pub", &merchant_pub),
TMH_PARSE_member_fixed ("merchant_pub", &tps.merchant),
TMH_PARSE_member_fixed ("merchant_sig", &merchant_sig),
TMH_PARSE_MEMBER_END
};
@ -159,7 +153,7 @@ TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh,
tps.transaction_id = GNUNET_htonll (transaction_id);
res = check_and_handle_deposit_wtid_request (connection,
&tps,
&merchant_pub,
&tps.merchant,
&merchant_sig,
transaction_id);
TMH_PARSE_release_data (spec);

View File

@ -67,9 +67,11 @@ PERF_TALER_MINTDB_denomination_init ()
properties.expire_withdraw = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_());
properties.expire_spend = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_());
properties.expire_legal = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_());
TALER_string_to_amount (CURRENCY ":1.1", &amount);
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1.1", &amount));
TALER_amount_hton (&properties.value, &amount);
TALER_string_to_amount (CURRENCY ":0.1", &amount);
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":0.1", &amount));
TALER_amount_hton (&properties.fee_withdraw, &amount);
TALER_amount_hton (&properties.fee_deposit, &amount);
TALER_amount_hton (&properties.fee_refresh, &amount);
@ -467,8 +469,8 @@ PERF_TALER_MINTDB_refresh_session_free (struct TALER_MINTDB_RefreshSession *refr
{
if (NULL == refresh_session)
return GNUNET_OK;
return GNUNET_OK;
GNUNET_free (refresh_session);
return GNUNET_OK;
}
@ -502,10 +504,12 @@ PERF_TALER_MINTDB_refresh_melt_init (struct GNUNET_HashCode *session,
&to_sign.purpose,
&coin_sig.eddsa_signature);
}
GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":1.1",
&amount));
GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":0.1",
&amount_with_fee));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1.1",
&amount));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":0.1",
&amount_with_fee));
melt = GNUNET_new (struct TALER_MINTDB_RefreshMelt);
melt->coin.coin_pub = coin->public_info.coin_pub;
melt->coin.denom_sig.rsa_signature =

View File

@ -451,8 +451,8 @@ postgres_create_tables (void *cls,
/* Table for the tracking API, mapping from wire transfer identifiers
to transactions and back */
SQLEXEC("CREATE TABLE IF NOT EXISTS aggregation_tracking "
"(h_contract BYTEA PRIMARY KEY CHECK (LENGTH(h_contract)=64)"
",h_wire BYTEA PRIMARY KEY CHECK (LENGTH(h_wire)=64)"
"(h_contract BYTEA CHECK (LENGTH(h_contract)=64)"
",h_wire BYTEA CHECK (LENGTH(h_wire)=64)"
",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)"
",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)"
",transaction_id INT8 NOT NULL"
@ -461,9 +461,12 @@ postgres_create_tables (void *cls,
",coin_amount_val INT8 NOT NULL"
",coin_amount_frac INT4 NOT NULL"
",coin_amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
",transaction_total_val INT8 NOT NULL"
",transaction_total_frac INT4 NOT NULL"
",transaction_total_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
",coin_fee_val INT8 NOT NULL"
",coin_fee_frac INT4 NOT NULL"
",coin_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
",transfer_total_val INT8 NOT NULL"
",transfer_total_frac INT4 NOT NULL"
",transfer_total_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
")");
/* Index for lookup_transactions statement on wtid */
SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index "
@ -708,7 +711,7 @@ postgres_prepare (PGconn *db_conn)
"SELECT"
" denom_pub"
",denom_sig"
" FROM known_coins "
" FROM known_coins"
" WHERE coin_pub=$1",
1, NULL);
/* Used in #postgres_insert_known_coin() to store
@ -875,6 +878,26 @@ postgres_prepare (PGconn *db_conn)
" (merchant_pub=$3)"
" )",
3, NULL);
/* Fetch an existing deposit request.
Used in #postgres_wire_lookup_deposit_wtid(). */
PREPARE ("get_deposit_for_wtid",
"SELECT"
" amount_with_fee_val"
",amount_with_fee_frac"
",amount_with_fee_curr"
",deposit_fee_val"
",deposit_fee_frac"
",deposit_fee_curr"
",wire_deadline"
" FROM deposits"
" WHERE ("
" (coin_pub=$1) AND"
" (transaction_id=$2) AND"
" (merchant_pub=$3) AND"
" (h_contract=$4) AND"
" (h_wire=$5)"
" )",
5, NULL);
/* Used in #postgres_iterate_deposits() */
PREPARE ("deposits_iterate",
@ -972,7 +995,7 @@ postgres_prepare (PGconn *db_conn)
" AND rm.oldcoin_index = rcl.oldcoin_index"
" AND rcl.cnc_index=rs.noreveal_index",
1, NULL);
/* Used in #postgres_lookup_wire_transactions */
/* Used in #postgres_lookup_wire_transfer */
PREPARE ("lookup_transactions",
"SELECT"
" h_contract"
@ -984,9 +1007,12 @@ postgres_prepare (PGconn *db_conn)
",coin_amount_val"
",coin_amount_frac"
",coin_amount_curr"
",transaction_total_val"
",transaction_total_frac"
",transaction_total_curr"
",coin_fee_val"
",coin_fee_frac"
",coin_fee_curr"
",transfer_total_val"
",transfer_total_frac"
",transfer_total_curr"
" FROM aggregation_tracking"
" WHERE wtid_raw=$1",
1, NULL);
@ -998,9 +1024,12 @@ postgres_prepare (PGconn *db_conn)
",coin_amount_val"
",coin_amount_frac"
",coin_amount_curr"
",transaction_total_val"
",transaction_total_frac"
",transaction_total_curr"
",coin_fee_val"
",coin_fee_frac"
",coin_fee_curr"
",transfer_total_val"
",transfer_total_frac"
",transfer_total_curr"
" FROM aggregation_tracking"
" WHERE"
" coin_pub=$1 AND"
@ -1022,12 +1051,15 @@ postgres_prepare (PGconn *db_conn)
",coin_amount_val"
",coin_amount_frac"
",coin_amount_curr"
",transaction_total_val"
",transaction_total_frac"
",transaction_total_curr"
",coin_fee_val"
",coin_fee_frac"
",coin_fee_curr"
",transfer_total_val"
",transfer_total_frac"
",transfer_total_curr"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)",
13, NULL);
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)",
16, NULL);
return GNUNET_OK;
#undef PREPARE
@ -3451,19 +3483,87 @@ postgres_get_coin_transactions (void *cls,
* into a wire transfer by the respective @a wtid.
*
* @param cls closure
* @param session database connection
* @param wtid the raw wire transfer identifier we used
* @param cb function to call on each transaction found
* @param cb_cls closure for @a cb
* @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors
* @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors,
* #GNUNET_NO if we found no results
*/
static int
postgres_lookup_wire_transactions (void *cls,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_MINTDB_TransactionDataCallback cb,
void *cb_cls)
postgres_lookup_wire_transfer (void *cls,
struct TALER_MINTDB_Session *session,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_MINTDB_WireTransferDataCallback cb,
void *cb_cls)
{
GNUNET_break (0); // not implemented!
return GNUNET_SYSERR;
PGresult *result;
struct TALER_PQ_QueryParam params[] = {
TALER_PQ_query_param_auto_from_type (wtid),
TALER_PQ_query_param_end
};
int nrows;
int i;
/* check if the melt record exists and get it */
result = TALER_PQ_exec_prepared (session->conn,
"lookup_transactions",
params);
if (PGRES_TUPLES_OK != PQresultStatus (result))
{
BREAK_DB_ERR (result);
PQclear (result);
return GNUNET_SYSERR;
}
nrows = PQntuples (result);
if (0 == nrows)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"lookup_wire_transfer() returned 0 matching rows\n");
PQclear (result);
return GNUNET_NO;
}
for (i=0;i<nrows;i++)
{
struct GNUNET_HashCode h_contract;
struct GNUNET_HashCode h_wire;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_MerchantPublicKeyP merchant_pub;
uint64_t transaction_id;
struct GNUNET_TIME_Absolute exec_time;
struct TALER_Amount coin_amount;
struct TALER_Amount coin_fee;
struct TALER_Amount transfer_amount;
struct TALER_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_auto_from_type ("h_contract", &h_contract),
TALER_PQ_result_spec_auto_from_type ("h_wire", &h_wire),
TALER_PQ_result_spec_auto_from_type ("coin_pub", &coin_pub),
TALER_PQ_result_spec_auto_from_type ("merchant_pub", &merchant_pub),
TALER_PQ_result_spec_uint64 ("transaction_id", &transaction_id),
TALER_PQ_result_spec_absolute_time ("execution_time", &exec_time),
TALER_PQ_result_spec_amount ("coin_amount", &coin_amount),
TALER_PQ_result_spec_amount ("coin_fee", &coin_fee),
TALER_PQ_result_spec_amount ("transfer_total", &transfer_amount),
TALER_PQ_result_spec_end
};
if (GNUNET_OK != TALER_PQ_extract_result (result, rs, i))
{
GNUNET_break (0);
PQclear (result);
return GNUNET_SYSERR;
}
cb (cb_cls,
&merchant_pub,
&h_wire,
&h_contract,
transaction_id,
&coin_pub,
&coin_amount,
&coin_fee,
&transfer_amount);
}
PQclear (result);
return GNUNET_OK;
}
@ -3473,6 +3573,7 @@ postgres_lookup_wire_transactions (void *cls,
* to be executed.
*
* @param cls closure
* @param session database connection
* @param h_contract hash of the contract
* @param h_wire hash of merchant wire details
* @param coin_pub public key of deposited coin
@ -3480,10 +3581,12 @@ postgres_lookup_wire_transactions (void *cls,
* @param transaction_id transaction identifier
* @param cb function to call with the result
* @param cb_cls closure to pass to @a cb
* @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
* @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors,
* #GNUNET_NO if nothing was found
*/
static int
postgres_wire_lookup_deposit_wtid (void *cls,
struct TALER_MINTDB_Session *session,
const struct GNUNET_HashCode *h_contract,
const struct GNUNET_HashCode *h_wire,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -3492,8 +3595,130 @@ postgres_wire_lookup_deposit_wtid (void *cls,
TALER_MINTDB_DepositWtidCallback cb,
void *cb_cls)
{
GNUNET_break (0); // not implemented
return GNUNET_SYSERR;
PGresult *result;
struct TALER_PQ_QueryParam params[] = {
TALER_PQ_query_param_auto_from_type (coin_pub),
TALER_PQ_query_param_auto_from_type (h_contract),
TALER_PQ_query_param_auto_from_type (h_wire),
TALER_PQ_query_param_uint64 (&transaction_id),
TALER_PQ_query_param_auto_from_type (merchant_pub),
TALER_PQ_query_param_end
};
int nrows;
/* check if the melt record exists and get it */
result = TALER_PQ_exec_prepared (session->conn,
"lookup_deposit_wtid",
params);
if (PGRES_TUPLES_OK != PQresultStatus (result))
{
BREAK_DB_ERR (result);
PQclear (result);
return GNUNET_SYSERR;
}
nrows = PQntuples (result);
if (0 == nrows)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"lookup_deposit_wtid returned 0 matching rows\n");
PQclear (result);
/* Check if transaction exists in deposits, so that we just
do not have a WTID yet, if so, do call the CB with a NULL wtid
and return GNUNET_YES! */
{
struct TALER_PQ_QueryParam params2[] = {
TALER_PQ_query_param_auto_from_type (coin_pub),
TALER_PQ_query_param_uint64 (&transaction_id),
TALER_PQ_query_param_auto_from_type (merchant_pub),
TALER_PQ_query_param_auto_from_type (h_contract),
TALER_PQ_query_param_auto_from_type (h_wire),
TALER_PQ_query_param_end
};
result = TALER_PQ_exec_prepared (session->conn,
"get_deposit_for_wtid",
params2);
if (PGRES_TUPLES_OK != PQresultStatus (result))
{
BREAK_DB_ERR (result);
PQclear (result);
return GNUNET_SYSERR;
}
}
nrows = PQntuples (result);
if (0 == nrows)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"get_deposit_for_wtid returned 0 matching rows\n");
PQclear (result);
return GNUNET_NO;
}
/* Ok, we're aware of the transaction, but it has not yet been
executed */
{
struct GNUNET_TIME_Absolute exec_time;
struct TALER_Amount coin_amount;
struct TALER_Amount coin_fee;
struct TALER_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_amount ("amount_with_fee", &coin_amount),
TALER_PQ_result_spec_amount ("deposit_fee", &coin_fee),
TALER_PQ_result_spec_absolute_time ("wire_deadline", &exec_time),
TALER_PQ_result_spec_end
};
if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0))
{
GNUNET_break (0);
PQclear (result);
return GNUNET_SYSERR;
}
cb (cb_cls,
NULL,
&coin_amount,
&coin_fee,
NULL,
exec_time);
PQclear (result);
return GNUNET_YES;
}
}
if (1 != nrows)
{
GNUNET_break (0);
PQclear (result);
return GNUNET_SYSERR;
}
{
struct TALER_WireTransferIdentifierRawP wtid;
struct GNUNET_TIME_Absolute exec_time;
struct TALER_Amount coin_amount;
struct TALER_Amount coin_fee;
struct TALER_Amount transaction_amount;
struct TALER_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_auto_from_type ("wtid_raw", &wtid),
TALER_PQ_result_spec_absolute_time ("execution_time", &exec_time),
TALER_PQ_result_spec_amount ("coin_amount", &coin_amount),
TALER_PQ_result_spec_amount ("coin_fee", &coin_fee),
TALER_PQ_result_spec_amount ("transfer_total", &transaction_amount),
TALER_PQ_result_spec_end
};
if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0))
{
GNUNET_break (0);
PQclear (result);
return GNUNET_SYSERR;
}
cb (cb_cls,
&wtid,
&coin_amount,
&coin_fee,
&transaction_amount,
exec_time);
}
PQclear (result);
return GNUNET_OK;
}
@ -3501,29 +3726,64 @@ postgres_wire_lookup_deposit_wtid (void *cls,
* Function called to insert aggregation information into the DB.
*
* @param cls closure
* @param session database connection
* @param wtid the raw wire transfer identifier we used
* @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
* @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
* @param h_contract which contract was this payment about
* @param transaction_id merchant's transaction ID for the payment
* @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 mint for this coin
* @param coin_value amount contributed by this coin in total
* @param coin_fee deposit fee charged by mint for this coin
* @param transfer_value total amount of the wire transfer
* @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
*/
static int
postgres_insert_aggregation_tracking (void *cls,
struct TALER_MINTDB_Session *session,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
uint64_t transaction_id,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *deposit_value,
const struct TALER_Amount *deposit_fee)
const struct TALER_Amount *coin_value,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *transfer_value)
{
GNUNET_break (0); // not implemented
return GNUNET_SYSERR;
struct TALER_PQ_QueryParam params[] = {
TALER_PQ_query_param_auto_from_type (h_contract),
TALER_PQ_query_param_auto_from_type (h_wire),
TALER_PQ_query_param_auto_from_type (coin_pub),
TALER_PQ_query_param_auto_from_type (merchant_pub),
TALER_PQ_query_param_uint64 (&transaction_id),
TALER_PQ_query_param_auto_from_type (wtid),
TALER_PQ_query_param_absolute_time (&execution_time),
TALER_PQ_query_param_amount (coin_value),
TALER_PQ_query_param_amount (coin_fee),
TALER_PQ_query_param_amount (transfer_value),
TALER_PQ_query_param_end
};
PGresult *result;
result = TALER_PQ_exec_prepared (session->conn,
"insert_aggregation_tracking",
params);
if (PGRES_COMMAND_OK != PQresultStatus (result))
{
BREAK_DB_ERR (result);
PQclear (result);
return GNUNET_SYSERR;
}
if (0 != strcmp ("1", PQcmdTuples (result)))
{
GNUNET_break (0);
PQclear (result);
return GNUNET_SYSERR;
}
PQclear (result);
return GNUNET_OK;
}
@ -3596,7 +3856,7 @@ libtaler_plugin_mintdb_postgres_init (void *cls)
plugin->get_transfer = &postgres_get_transfer;
plugin->get_coin_transactions = &postgres_get_coin_transactions;
plugin->free_coin_transaction_list = &common_free_coin_transaction_list;
plugin->lookup_wire_transactions = &postgres_lookup_wire_transactions;
plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer;
plugin->wire_lookup_deposit_wtid = &postgres_wire_lookup_deposit_wtid;
plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking;
return plugin;

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 GNUnet e.V.
Copyright (C) 2014, 2015, 2016 GNUnet e.V.
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
@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file mint/test_mintdb.c
* @file mintdb/test_mintdb.c
* @brief test cases for DB interaction functions
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
*/
@ -305,8 +305,10 @@ test_melting (struct TALER_MINTDB_Session *session)
RND_BLK (&refresh_session);
RND_BLK (&session_hash);
melts = NULL;
dkp = NULL;
new_dkp = NULL;
new_denom_pubs = NULL;
ret_denom_pubs = NULL;
/* create and test a refresh session */
refresh_session.num_oldcoins = MELT_OLD_COINS;
refresh_session.num_newcoins = 1;
@ -324,11 +326,11 @@ test_melting (struct TALER_MINTDB_Session *session)
sizeof (refresh_session)));
/* create a denomination (value: 1; fraction: 100) */
dkp = create_denom_key_pair(512, session,
&value,
&fee_withdraw,
&fee_deposit,
&fee_refresh);
dkp = create_denom_key_pair (512, session,
&value,
&fee_withdraw,
&fee_deposit,
&fee_refresh);
/* create MELT_OLD_COINS number of refresh melts */
melts = GNUNET_new_array (MELT_OLD_COINS, struct TALER_MINTDB_RefreshMelt);
for (cnt=0; cnt < MELT_OLD_COINS; cnt++)
@ -416,7 +418,8 @@ test_melting (struct TALER_MINTDB_Session *session)
ret = GNUNET_OK;
drop:
destroy_denom_key_pair (dkp);
if (NULL != dkp)
destroy_denom_key_pair (dkp);
if (NULL != melts)
{
for (cnt = 0; cnt < MELT_OLD_COINS; cnt++)
@ -439,6 +442,114 @@ test_melting (struct TALER_MINTDB_Session *session)
}
/**
* Callback that should never be called.
*/
static void
cb_wt_never (void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
uint64_t transaction_id,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_value,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *transfer_value)
{
GNUNET_assert (0); /* this statement should be unreachable */
}
/**
* Callback that should never be called.
*/
static void
cb_wtid_never (void *cls,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *coin_contribution,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *total_amount,
struct GNUNET_TIME_Absolute execution_time)
{
GNUNET_assert (0);
}
static struct TALER_MerchantPublicKeyP merchant_pub_wt;
static struct GNUNET_HashCode h_wire_wt;
static struct GNUNET_HashCode h_contract_wt;
static uint64_t transaction_id_wt;
static struct TALER_CoinSpendPublicKeyP coin_pub_wt;
static struct TALER_Amount coin_value_wt;
static struct TALER_Amount coin_fee_wt;
static struct TALER_Amount transfer_value_wt;
static struct GNUNET_TIME_Absolute execution_time_wt;
static struct TALER_WireTransferIdentifierRawP wtid_wt;
/**
* Callback that should be called with the WT data.
*/
static void
cb_wt_check (void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
uint64_t transaction_id,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_value,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *transfer_value)
{
GNUNET_assert (cls == &cb_wt_never);
GNUNET_assert (0 == memcmp (merchant_pub,
&merchant_pub_wt,
sizeof (struct TALER_MerchantPublicKeyP)));
GNUNET_assert (0 == memcmp (h_wire,
&h_wire_wt,
sizeof (struct GNUNET_HashCode)));
GNUNET_assert (0 == memcmp (h_contract,
&h_contract_wt,
sizeof (struct GNUNET_HashCode)));
GNUNET_assert (transaction_id == transaction_id_wt);
GNUNET_assert (0 == memcmp (coin_pub,
&coin_pub_wt,
sizeof (struct TALER_CoinSpendPublicKeyP)));
GNUNET_assert (0 == TALER_amount_cmp (coin_value,
&coin_value_wt));
GNUNET_assert (0 == TALER_amount_cmp (coin_fee,
&coin_fee_wt));
GNUNET_assert (0 == TALER_amount_cmp (transfer_value,
&transfer_value_wt));
}
/**
* Callback that should be called with the WT data.
*/
static void
cb_wtid_check (void *cls,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *coin_contribution,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *total_amount,
struct GNUNET_TIME_Absolute execution_time)
{
GNUNET_assert (cls == &cb_wtid_never);
GNUNET_assert (0 == memcmp (wtid,
&wtid_wt,
sizeof (struct TALER_WireTransferIdentifierRawP)));
GNUNET_assert (execution_time.abs_value_us ==
execution_time_wt.abs_value_us);
GNUNET_assert (0 == TALER_amount_cmp (coin_contribution,
&coin_value_wt));
GNUNET_assert (0 == TALER_amount_cmp (coin_fee,
&coin_fee_wt));
GNUNET_assert (0 == TALER_amount_cmp (total_amount,
&transfer_value_wt));
}
/**
* Main function that will be run by the scheduler.
*
@ -455,7 +566,6 @@ run (void *cls,
{
struct TALER_MINTDB_Session *session;
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_Amount amount;
struct DenomKeyPair *dkp;
struct TALER_MINTDB_CollectableBlindcoin cbc;
struct TALER_MINTDB_CollectableBlindcoin cbc2;
@ -465,6 +575,7 @@ run (void *cls,
struct TALER_MINTDB_CollectableBlindcoin *withdraw;
struct TALER_MINTDB_Deposit deposit;
struct TALER_MINTDB_Deposit deposit2;
struct TALER_WireTransferIdentifierRawP wtid;
json_t *wire;
json_t *just;
const char * const json_wire_str =
@ -563,11 +674,7 @@ run (void *cls,
= GNUNET_CRYPTO_rsa_sign (dkp->priv.rsa_private_key,
&cbc.h_coin_envelope,
sizeof (cbc.h_coin_envelope));
(void) memcpy (&cbc.reserve_pub,
&reserve_pub,
sizeof (reserve_pub));
amount.value--;
amount.fraction--;
cbc.reserve_pub = reserve_pub;
cbc.amount_with_fee = value;
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (CURRENCY, &cbc.withdraw_fee));
@ -652,9 +759,7 @@ run (void *cls,
plugin->have_deposit (plugin->cls,
session,
&deposit));
(void) memcpy (&deposit2,
&deposit,
sizeof (deposit));
deposit2 = deposit;
deposit2.transaction_id++; /* should fail if transaction id is different */
FAILIF (GNUNET_NO !=
plugin->have_deposit (plugin->cls,
@ -666,15 +771,79 @@ run (void *cls,
plugin->have_deposit (plugin->cls,
session,
&deposit2));
(void) memcpy (&deposit2.merchant_pub,
&deposit.merchant_pub,
sizeof (deposit.merchant_pub));
deposit2.merchant_pub = deposit.merchant_pub;
RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */
FAILIF (GNUNET_NO !=
plugin->have_deposit (plugin->cls,
session,
&deposit2));
FAILIF (GNUNET_OK != test_melting (session));
/* setup values for wire transfer aggregation data */
memset (&wtid, 42, sizeof (wtid));
memset (&merchant_pub_wt, 43, sizeof (merchant_pub_wt));
memset (&h_wire_wt, 44, sizeof (h_wire_wt));
memset (&h_contract_wt, 45, sizeof (h_contract_wt));
memset (&coin_pub_wt, 46, sizeof (coin_pub_wt));
transaction_id_wt = 47;
execution_time_wt = GNUNET_TIME_absolute_get ();
memset (&merchant_pub_wt, 48, sizeof (merchant_pub_wt));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY "KUDOS:1.000010",
&coin_value_wt));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY "KUDOS:0.000010",
&coin_fee_wt));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY "KUDOS:1.000000",
&transfer_value_wt));
FAILIF (GNUNET_NO !=
plugin->lookup_wire_transfer (plugin->cls,
session,
&wtid_wt,
&cb_wt_never,
NULL));
FAILIF (GNUNET_NO !=
plugin->wire_lookup_deposit_wtid (plugin->cls,
session,
&h_contract_wt,
&h_wire_wt,
&coin_pub_wt,
&merchant_pub_wt,
transaction_id_wt,
&cb_wtid_never,
NULL));
/* insert WT data */
FAILIF (GNUNET_OK !=
plugin->insert_aggregation_tracking (plugin->cls,
session,
&wtid_wt,
&merchant_pub_wt,
&h_wire_wt,
&h_contract_wt,
transaction_id_wt,
execution_time_wt,
&coin_pub_wt,
&coin_value_wt,
&coin_fee_wt,
&transfer_value_wt));
FAILIF (GNUNET_OK !=
plugin->lookup_wire_transfer (plugin->cls,
session,
&wtid_wt,
&cb_wt_check,
&cb_wt_never));
FAILIF (GNUNET_OK !=
plugin->wire_lookup_deposit_wtid (plugin->cls,
session,
&h_contract_wt,
&h_wire_wt,
&coin_pub_wt,
&merchant_pub_wt,
transaction_id_wt,
&cb_wtid_check,
&cb_wtid_never));
result = 0;
drop: