2015-01-08 18:37:20 +01:00
|
|
|
/*
|
|
|
|
This file is part of TALER
|
2015-06-18 11:39:53 +02:00
|
|
|
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
|
2015-01-08 18:37:20 +01:00
|
|
|
|
|
|
|
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/test_mint_api.c
|
|
|
|
* @brief testcase to test mint's HTTP API interface
|
|
|
|
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
|
2015-06-18 11:39:53 +02:00
|
|
|
* @author Christian Grothoff
|
2015-07-05 13:20:09 +02:00
|
|
|
*
|
|
|
|
* TODO:
|
|
|
|
* - enhance interpreter to allow for testing of failure conditions
|
|
|
|
* (i.e. double-spending, insufficient funds on withdraw)
|
2015-01-08 18:37:20 +01:00
|
|
|
*/
|
|
|
|
#include "platform.h"
|
|
|
|
#include "taler_util.h"
|
2015-07-05 16:55:01 +02:00
|
|
|
#include "taler_signatures.h"
|
2015-01-08 18:37:20 +01:00
|
|
|
#include "taler_mint_service.h"
|
2015-07-08 08:54:55 +02:00
|
|
|
#include <gnunet/gnunet_util_lib.h>
|
2015-07-04 18:45:51 +02:00
|
|
|
#include <microhttpd.h>
|
|
|
|
|
2015-01-08 18:37:20 +01:00
|
|
|
|
2015-06-18 11:39:53 +02:00
|
|
|
/**
|
|
|
|
* Main execution context for the main loop.
|
|
|
|
*/
|
2015-06-18 01:17:01 +02:00
|
|
|
static struct TALER_MINT_Context *ctx;
|
2015-01-08 18:37:20 +01:00
|
|
|
|
2015-06-18 11:39:53 +02:00
|
|
|
/**
|
|
|
|
* Handle to access the mint.
|
|
|
|
*/
|
2015-06-18 01:17:01 +02:00
|
|
|
static struct TALER_MINT_Handle *mint;
|
2015-01-08 18:37:20 +01:00
|
|
|
|
2015-06-18 11:39:53 +02:00
|
|
|
/**
|
|
|
|
* Task run on shutdown.
|
|
|
|
*/
|
2015-01-09 18:18:59 +01:00
|
|
|
static struct GNUNET_SCHEDULER_Task *shutdown_task;
|
2015-01-08 18:37:20 +01:00
|
|
|
|
2015-06-18 11:39:53 +02:00
|
|
|
/**
|
|
|
|
* Task that runs the main event loop.
|
|
|
|
*/
|
2015-06-18 01:17:01 +02:00
|
|
|
static struct GNUNET_SCHEDULER_Task *ctx_task;
|
|
|
|
|
2015-06-18 11:39:53 +02:00
|
|
|
/**
|
|
|
|
* Result of the testcases, #GNUNET_OK on success
|
|
|
|
*/
|
2015-01-08 18:37:20 +01:00
|
|
|
static int result;
|
|
|
|
|
|
|
|
|
2015-07-04 17:30:38 +02:00
|
|
|
/**
|
|
|
|
* Opcodes for the interpreter.
|
|
|
|
*/
|
|
|
|
enum OpCode
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Termination code, stops the interpreter loop (with success).
|
|
|
|
*/
|
|
|
|
OC_END = 0,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add funds to a reserve by (faking) incoming wire transfer.
|
|
|
|
*/
|
|
|
|
OC_ADMIN_ADD_INCOMING,
|
|
|
|
|
2015-07-09 11:36:13 +02:00
|
|
|
/**
|
|
|
|
* Check status of a reserve.
|
|
|
|
*/
|
|
|
|
OC_WITHDRAW_STATUS,
|
|
|
|
|
2015-07-04 17:30:38 +02:00
|
|
|
/**
|
|
|
|
* Withdraw a coin from a reserve.
|
|
|
|
*/
|
|
|
|
OC_WITHDRAW_SIGN,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deposit a coin (pay with it).
|
|
|
|
*/
|
|
|
|
OC_DEPOSIT
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Details for a mint operation to execute.
|
|
|
|
*/
|
|
|
|
struct Command
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Opcode of the command.
|
|
|
|
*/
|
|
|
|
enum OpCode oc;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Label for the command, can be NULL.
|
|
|
|
*/
|
|
|
|
const char *label;
|
|
|
|
|
2015-07-09 12:22:07 +02:00
|
|
|
/**
|
|
|
|
* Which response code do we expect for this command?
|
|
|
|
*/
|
|
|
|
unsigned int expected_response_code;
|
|
|
|
|
2015-07-04 17:30:38 +02:00
|
|
|
/**
|
|
|
|
* Details about the command.
|
|
|
|
*/
|
|
|
|
union
|
|
|
|
{
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Label to another admin_add_incoming command if we
|
|
|
|
* should deposit into an existing reserve, NULL if
|
|
|
|
* a fresh reserve should be created.
|
|
|
|
*/
|
|
|
|
const char *reserve_reference;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* String describing the amount to add to the reserve.
|
|
|
|
*/
|
|
|
|
const char *amount;
|
|
|
|
|
2015-07-04 18:45:51 +02:00
|
|
|
/**
|
|
|
|
* Wire details (JSON).
|
|
|
|
*/
|
|
|
|
const char *wire;
|
|
|
|
|
2015-07-04 17:30:38 +02:00
|
|
|
/**
|
|
|
|
* Set (by the interpreter) to the reserve's private key
|
|
|
|
* we used to fill the reserve.
|
|
|
|
*/
|
|
|
|
struct TALER_ReservePrivateKeyP reserve_priv;
|
|
|
|
|
2015-07-04 18:45:51 +02:00
|
|
|
/**
|
|
|
|
* Set to the API's handle during the operation.
|
|
|
|
*/
|
|
|
|
struct TALER_MINT_AdminAddIncomingHandle *aih;
|
|
|
|
|
2015-07-04 17:30:38 +02:00
|
|
|
} admin_add_incoming;
|
|
|
|
|
2015-07-09 11:36:13 +02:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Label to the #OC_ADMIN_ADD_INCOMING command which
|
|
|
|
* created the reserve.
|
|
|
|
*/
|
|
|
|
const char *reserve_reference;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set to the API's handle during the operation.
|
|
|
|
*/
|
|
|
|
struct TALER_MINT_WithdrawStatusHandle *wsh;
|
|
|
|
|
2015-07-09 12:11:01 +02:00
|
|
|
/**
|
|
|
|
* Expected reserve balance.
|
|
|
|
*/
|
|
|
|
const char *expected_balance;
|
|
|
|
|
2015-07-09 11:36:13 +02:00
|
|
|
} withdraw_status;
|
|
|
|
|
2015-07-04 17:30:38 +02:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Which reserve should we withdraw from?
|
|
|
|
*/
|
|
|
|
const char *reserve_reference;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* String describing the denomination value we should withdraw.
|
|
|
|
* A corresponding denomination key must exist in the mint's
|
|
|
|
* offerings. Can be NULL if @e pk is set instead.
|
|
|
|
*/
|
|
|
|
const char *amount;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If @e amount is NULL, this specifies the denomination key to
|
|
|
|
* use. Otherwise, this will be set (by the interpreter) to the
|
|
|
|
* denomination PK matching @e amount.
|
|
|
|
*/
|
|
|
|
const struct TALER_MINT_DenomPublicKey *pk;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set (by the interpreter) to the mint's signature over the
|
|
|
|
* coin's public key.
|
|
|
|
*/
|
|
|
|
struct TALER_DenominationSignature sig;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set (by the interpreter) to the coin's private key.
|
|
|
|
*/
|
|
|
|
struct TALER_CoinSpendPrivateKeyP coin_priv;
|
|
|
|
|
2015-07-04 22:35:30 +02:00
|
|
|
/**
|
|
|
|
* Blinding key used for the operation.
|
|
|
|
*/
|
|
|
|
struct TALER_DenominationBlindingKey blinding_key;
|
|
|
|
|
2015-07-04 22:00:29 +02:00
|
|
|
/**
|
|
|
|
* Withdraw handle (while operation is running).
|
|
|
|
*/
|
|
|
|
struct TALER_MINT_WithdrawSignHandle *wsh;
|
|
|
|
|
2015-07-04 17:30:38 +02:00
|
|
|
} withdraw_sign;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Amount to deposit.
|
|
|
|
*/
|
|
|
|
const char *amount;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reference to a withdraw_sign operation for a coin to
|
|
|
|
* be used for the /deposit operation.
|
|
|
|
*/
|
|
|
|
const char *coin_ref;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* JSON string describing the merchant's "wire details".
|
|
|
|
*/
|
|
|
|
const char *wire_details;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* JSON string describing the contract between the two parties.
|
|
|
|
*/
|
|
|
|
const char *contract;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transaction ID to use.
|
|
|
|
*/
|
|
|
|
uint64_t transaction_id;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Relative time (to add to 'now') to compute the refund deadline.
|
|
|
|
* Zero for no refunds.
|
|
|
|
*/
|
|
|
|
struct GNUNET_TIME_Relative refund_deadline;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set (by the interpreter) to a fresh private key of the merchant,
|
|
|
|
* if @e refund_deadline is non-zero.
|
|
|
|
*/
|
2015-07-05 14:32:26 +02:00
|
|
|
struct TALER_MerchantPrivateKeyP merchant_priv;
|
2015-07-04 17:30:38 +02:00
|
|
|
|
2015-07-04 22:00:29 +02:00
|
|
|
/**
|
|
|
|
* Deposit handle while operation is running.
|
|
|
|
*/
|
|
|
|
struct TALER_MINT_DepositHandle *dh;
|
|
|
|
|
2015-07-04 17:30:38 +02:00
|
|
|
} deposit;
|
|
|
|
|
|
|
|
} details;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* State of the interpreter loop.
|
|
|
|
*/
|
|
|
|
struct InterpreterState
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Keys from the mint.
|
|
|
|
*/
|
|
|
|
const struct TALER_MINT_Keys *keys;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Commands the interpreter will run.
|
|
|
|
*/
|
|
|
|
struct Command *commands;
|
|
|
|
|
2015-07-04 20:47:39 +02:00
|
|
|
/**
|
|
|
|
* Interpreter task (if one is scheduled).
|
|
|
|
*/
|
|
|
|
struct GNUNET_SCHEDULER_Task *task;
|
|
|
|
|
2015-07-04 17:30:38 +02:00
|
|
|
/**
|
|
|
|
* Instruction pointer. Tells #interpreter_run() which
|
|
|
|
* instruction to run next.
|
|
|
|
*/
|
|
|
|
unsigned int ip;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-07-04 20:47:39 +02:00
|
|
|
/**
|
|
|
|
* Task that runs the context's event loop with the GNUnet scheduler.
|
|
|
|
*
|
|
|
|
* @param cls unused
|
|
|
|
* @param tc scheduler context (unused)
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
context_task (void *cls,
|
|
|
|
const struct GNUNET_SCHEDULER_TaskContext *tc);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Run the context task, the working set has changed.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trigger_context_task ()
|
|
|
|
{
|
|
|
|
GNUNET_SCHEDULER_cancel (ctx_task);
|
|
|
|
ctx_task = GNUNET_SCHEDULER_add_now (&context_task,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-04 17:30:38 +02:00
|
|
|
/**
|
|
|
|
* The testcase failed, return with an error code.
|
|
|
|
*
|
|
|
|
* @param is interpreter state to clean up
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
fail (struct InterpreterState *is)
|
|
|
|
{
|
|
|
|
result = GNUNET_SYSERR;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-04 18:45:51 +02:00
|
|
|
/**
|
|
|
|
* Find a command by label.
|
|
|
|
*
|
|
|
|
* @param is interpreter state to search
|
|
|
|
* @param label label to look for
|
|
|
|
* @return NULL if command was not found
|
|
|
|
*/
|
|
|
|
static const struct Command *
|
|
|
|
find_command (const struct InterpreterState *is,
|
|
|
|
const char *label)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
const struct Command *cmd;
|
|
|
|
|
2015-07-04 20:47:39 +02:00
|
|
|
if (NULL == label)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
|
|
|
"Attempt to lookup command for empty label\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-07-04 18:45:51 +02:00
|
|
|
for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
|
|
|
|
if ( (NULL != cmd->label) &&
|
|
|
|
(0 == strcmp (cmd->label,
|
|
|
|
label)) )
|
|
|
|
return cmd;
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
|
|
|
"Command not found: %s\n",
|
|
|
|
label);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Run the main interpreter loop that performs mint operations.
|
|
|
|
*
|
|
|
|
* @param cls contains the `struct InterpreterState`
|
|
|
|
* @param tc scheduler context
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
interpreter_run (void *cls,
|
|
|
|
const struct GNUNET_SCHEDULER_TaskContext *tc);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-07-04 22:35:30 +02:00
|
|
|
* Function called upon completion of our /admin/add/incoming request.
|
2015-07-04 18:45:51 +02:00
|
|
|
*
|
|
|
|
* @param cls closure with the interpreter state
|
|
|
|
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
|
|
|
|
* 0 if the mint's reply is bogus (fails to follow the protocol)
|
|
|
|
* @param full_response full response from the mint (for logging, in case of errors)
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_incoming_cb (void *cls,
|
|
|
|
unsigned int http_status,
|
|
|
|
json_t *full_response)
|
|
|
|
{
|
|
|
|
struct InterpreterState *is = cls;
|
|
|
|
struct Command *cmd = &is->commands[is->ip];
|
|
|
|
|
|
|
|
cmd->details.admin_add_incoming.aih = NULL;
|
|
|
|
if (MHD_HTTP_OK != http_status)
|
|
|
|
{
|
2015-07-05 13:05:58 +02:00
|
|
|
GNUNET_break (0);
|
2015-07-04 18:45:51 +02:00
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
is->ip++;
|
2015-07-04 20:47:39 +02:00
|
|
|
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
|
|
|
|
is);
|
2015-07-04 18:45:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-09 12:02:01 +02:00
|
|
|
/**
|
|
|
|
* Check if the given historic event @a h corresponds to the given
|
|
|
|
* command @a cmd.
|
|
|
|
*
|
|
|
|
* @param h event in history
|
|
|
|
* @param cmd an #OC_ADMIN_ADD_INCOMING command
|
|
|
|
* @return #GNUNET_OK if they match, #GNUNET_SYSERR if not
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
compare_admin_add_incoming_history (const struct TALER_MINT_ReserveHistory *h,
|
|
|
|
const struct Command *cmd)
|
|
|
|
{
|
|
|
|
struct TALER_Amount amount;
|
|
|
|
|
|
|
|
if (TALER_MINT_RTT_DEPOSIT != h->type)
|
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
|
|
|
|
&amount));
|
|
|
|
if (0 != TALER_amount_cmp (&amount,
|
|
|
|
&h->amount))
|
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the given historic event @a h corresponds to the given
|
|
|
|
* command @a cmd.
|
|
|
|
*
|
|
|
|
* @param h event in history
|
|
|
|
* @param cmd an #OC_WITHDRAW_SIGN command
|
|
|
|
* @return #GNUNET_OK if they match, #GNUNET_SYSERR if not
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
compare_withdraw_sign_history (const struct TALER_MINT_ReserveHistory *h,
|
|
|
|
const struct Command *cmd)
|
|
|
|
{
|
|
|
|
struct TALER_Amount amount;
|
2015-07-09 12:11:01 +02:00
|
|
|
struct TALER_Amount amount_with_fee;
|
2015-07-09 12:02:01 +02:00
|
|
|
|
|
|
|
if (TALER_MINT_RTT_WITHDRAWAL != h->type)
|
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_string_to_amount (cmd->details.withdraw_sign.amount,
|
|
|
|
&amount));
|
2015-07-09 12:11:01 +02:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&amount_with_fee,
|
|
|
|
&amount,
|
|
|
|
&cmd->details.withdraw_sign.pk->fee_withdraw));
|
|
|
|
if (0 != TALER_amount_cmp (&amount_with_fee,
|
2015-07-09 12:02:01 +02:00
|
|
|
&h->amount))
|
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
2015-07-09 12:11:01 +02:00
|
|
|
return GNUNET_SYSERR;
|
2015-07-09 12:02:01 +02:00
|
|
|
}
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-09 11:36:13 +02:00
|
|
|
/**
|
|
|
|
* Function called with the result of a /withdraw/status request.
|
|
|
|
*
|
|
|
|
* @param cls closure with the interpreter state
|
|
|
|
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
|
|
|
|
* 0 if the mint's reply is bogus (fails to follow the protocol)
|
|
|
|
* @param[in] json original response in JSON format (useful only for diagnostics)
|
|
|
|
* @param balance current balance in the reserve, NULL on error
|
|
|
|
* @param history_length number of entries in the transaction history, 0 on error
|
|
|
|
* @param history detailed transaction history, NULL on error
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
withdraw_status_cb (void *cls,
|
|
|
|
unsigned int http_status,
|
|
|
|
json_t *json,
|
|
|
|
const struct TALER_Amount *balance,
|
|
|
|
unsigned int history_length,
|
|
|
|
const struct TALER_MINT_ReserveHistory *history)
|
|
|
|
{
|
|
|
|
struct InterpreterState *is = cls;
|
|
|
|
struct Command *cmd = &is->commands[is->ip];
|
2015-07-09 12:02:01 +02:00
|
|
|
struct Command *rel;
|
|
|
|
unsigned int i;
|
|
|
|
unsigned int j;
|
2015-07-09 12:11:01 +02:00
|
|
|
struct TALER_Amount amount;
|
2015-07-09 11:36:13 +02:00
|
|
|
|
|
|
|
cmd->details.withdraw_status.wsh = NULL;
|
2015-07-09 12:22:07 +02:00
|
|
|
if (cmd->expected_response_code != http_status)
|
2015-07-09 12:02:01 +02:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
2015-07-09 12:22:07 +02:00
|
|
|
switch (http_status)
|
2015-07-09 12:02:01 +02:00
|
|
|
{
|
2015-07-09 12:22:07 +02:00
|
|
|
case MHD_HTTP_OK:
|
|
|
|
/* FIXME: note that history events may come in a different
|
|
|
|
order than the commands right now... */
|
|
|
|
j = 0;
|
|
|
|
for (i=0;i<is->ip;i++)
|
2015-07-09 12:02:01 +02:00
|
|
|
{
|
2015-07-09 12:22:07 +02:00
|
|
|
switch ((rel = &is->commands[i])->oc)
|
2015-07-09 12:02:01 +02:00
|
|
|
{
|
2015-07-09 12:22:07 +02:00
|
|
|
case OC_ADMIN_ADD_INCOMING:
|
|
|
|
if ( ( (NULL != rel->label) &&
|
|
|
|
(0 == strcmp (cmd->details.withdraw_status.reserve_reference,
|
|
|
|
rel->label) ) ) ||
|
|
|
|
( (NULL != rel->details.admin_add_incoming.reserve_reference) &&
|
|
|
|
(0 == strcmp (cmd->details.withdraw_status.reserve_reference,
|
|
|
|
rel->details.admin_add_incoming.reserve_reference) ) ) )
|
2015-07-09 12:02:01 +02:00
|
|
|
{
|
2015-07-09 12:22:07 +02:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
compare_admin_add_incoming_history (&history[j],
|
|
|
|
rel))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
j++;
|
2015-07-09 12:02:01 +02:00
|
|
|
}
|
2015-07-09 12:22:07 +02:00
|
|
|
break;
|
|
|
|
case OC_WITHDRAW_SIGN:
|
|
|
|
if (0 == strcmp (cmd->details.withdraw_status.reserve_reference,
|
|
|
|
rel->details.withdraw_sign.reserve_reference))
|
2015-07-09 12:02:01 +02:00
|
|
|
{
|
2015-07-09 12:22:07 +02:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
compare_withdraw_sign_history (&history[j],
|
|
|
|
rel))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
j++;
|
2015-07-09 12:02:01 +02:00
|
|
|
}
|
2015-07-09 12:22:07 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* unreleated, just skip */
|
|
|
|
break;
|
2015-07-09 12:02:01 +02:00
|
|
|
}
|
|
|
|
}
|
2015-07-09 12:22:07 +02:00
|
|
|
if (j != history_length)
|
2015-07-09 12:11:01 +02:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
2015-07-09 12:22:07 +02:00
|
|
|
if (NULL != cmd->details.withdraw_status.expected_balance)
|
|
|
|
{
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_string_to_amount (cmd->details.withdraw_status.expected_balance,
|
|
|
|
&amount));
|
|
|
|
if (0 != TALER_amount_cmp (&amount,
|
|
|
|
balance))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* FIXME: support other status codes! */
|
|
|
|
default:
|
|
|
|
/* Unsupported status code (by test harness) */
|
|
|
|
GNUNET_break (0);
|
|
|
|
break;
|
2015-07-09 12:11:01 +02:00
|
|
|
}
|
2015-07-09 11:36:13 +02:00
|
|
|
is->ip++;
|
|
|
|
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
|
|
|
|
is);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-04 22:35:30 +02:00
|
|
|
/**
|
|
|
|
* Function called upon completion of our /withdraw/sign request.
|
|
|
|
*
|
|
|
|
* @param cls closure with the interpreter state
|
|
|
|
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
|
|
|
|
* 0 if the mint's reply is bogus (fails to follow the protocol)
|
|
|
|
* @param sig signature over the coin, NULL on error
|
|
|
|
* @param full_response full response from the mint (for logging, in case of errors)
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
withdraw_sign_cb (void *cls,
|
|
|
|
unsigned int http_status,
|
|
|
|
const struct TALER_DenominationSignature *sig,
|
|
|
|
json_t *full_response)
|
|
|
|
{
|
|
|
|
struct InterpreterState *is = cls;
|
|
|
|
struct Command *cmd = &is->commands[is->ip];
|
|
|
|
|
|
|
|
cmd->details.withdraw_sign.wsh = NULL;
|
2015-07-09 12:22:07 +02:00
|
|
|
if (cmd->expected_response_code != http_status)
|
2015-07-04 22:35:30 +02:00
|
|
|
{
|
2015-07-05 13:05:58 +02:00
|
|
|
GNUNET_break (0);
|
2015-07-04 22:35:30 +02:00
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
2015-07-09 12:22:07 +02:00
|
|
|
switch (http_status)
|
|
|
|
{
|
|
|
|
case MHD_HTTP_OK:
|
|
|
|
if (NULL == sig)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cmd->details.withdraw_sign.sig.rsa_signature
|
|
|
|
= GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature);
|
|
|
|
break;
|
|
|
|
/* FIXME: support other status codes here */
|
|
|
|
default:
|
|
|
|
/* Unsupported status code (by test harness) */
|
|
|
|
GNUNET_break (0);
|
|
|
|
break;
|
|
|
|
}
|
2015-07-04 22:35:30 +02:00
|
|
|
is->ip++;
|
|
|
|
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
|
|
|
|
is);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-05 14:32:26 +02:00
|
|
|
/**
|
|
|
|
* Function called with the result of a /deposit operation.
|
|
|
|
*
|
|
|
|
* @param cls closure with the interpreter state
|
|
|
|
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful deposit;
|
|
|
|
* 0 if the mint's reply is bogus (fails to follow the protocol)
|
|
|
|
* @param obj the received JSON reply, should be kept as proof (and, in case of errors,
|
|
|
|
* be forwarded to the customer)
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
deposit_cb (void *cls,
|
|
|
|
unsigned int http_status,
|
|
|
|
json_t *obj)
|
|
|
|
{
|
|
|
|
struct InterpreterState *is = cls;
|
|
|
|
struct Command *cmd = &is->commands[is->ip];
|
|
|
|
|
|
|
|
cmd->details.deposit.dh = NULL;
|
2015-07-09 12:22:07 +02:00
|
|
|
if (cmd->expected_response_code != http_status)
|
2015-07-05 14:32:26 +02:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
is->ip++;
|
|
|
|
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
|
|
|
|
is);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-04 22:35:30 +02:00
|
|
|
/**
|
|
|
|
* Find denomination key matching the given amount.
|
|
|
|
*
|
|
|
|
* @param keys array of keys to search
|
|
|
|
* @param amount coin value to look for
|
|
|
|
* @return NULL if no matching key was found
|
|
|
|
*/
|
|
|
|
static const struct TALER_MINT_DenomPublicKey *
|
|
|
|
find_pk (const struct TALER_MINT_Keys *keys,
|
|
|
|
const struct TALER_Amount *amount)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
struct GNUNET_TIME_Absolute now;
|
|
|
|
struct TALER_MINT_DenomPublicKey *pk;
|
|
|
|
char *str;
|
|
|
|
|
|
|
|
now = GNUNET_TIME_absolute_get ();
|
|
|
|
for (i=0;i<keys->num_denom_keys;i++)
|
|
|
|
{
|
|
|
|
pk = &keys->denom_keys[i];
|
|
|
|
if ( (0 == TALER_amount_cmp (amount,
|
|
|
|
&pk->value)) &&
|
|
|
|
(now.abs_value_us >= pk->valid_from.abs_value_us) &&
|
|
|
|
(now.abs_value_us < pk->withdraw_valid_until.abs_value_us) )
|
|
|
|
return pk;
|
2015-07-05 11:54:14 +02:00
|
|
|
}
|
|
|
|
/* do 2nd pass to check if expiration times are to blame for failure */
|
|
|
|
str = TALER_amount_to_string (amount);
|
|
|
|
for (i=0;i<keys->num_denom_keys;i++)
|
|
|
|
{
|
2015-07-04 22:41:05 +02:00
|
|
|
if ( (0 == TALER_amount_cmp (amount,
|
|
|
|
&pk->value)) &&
|
|
|
|
( (now.abs_value_us < pk->valid_from.abs_value_us) ||
|
|
|
|
(now.abs_value_us > pk->withdraw_valid_until.abs_value_us) ) )
|
2015-07-05 11:54:14 +02:00
|
|
|
{
|
2015-07-04 22:41:05 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
2015-07-05 11:54:14 +02:00
|
|
|
"Have denomination key for `%s', but with wrong expiration range %llu vs [%llu,%llu)\n",
|
|
|
|
str,
|
2015-07-04 22:41:05 +02:00
|
|
|
now.abs_value_us,
|
|
|
|
pk->valid_from.abs_value_us,
|
|
|
|
pk->withdraw_valid_until.abs_value_us);
|
2015-07-05 11:54:14 +02:00
|
|
|
GNUNET_free (str);
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-07-04 22:35:30 +02:00
|
|
|
}
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
|
|
|
"No denomination key for amount %s found\n",
|
|
|
|
str);
|
|
|
|
GNUNET_free (str);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-04 17:30:38 +02:00
|
|
|
/**
|
|
|
|
* Run the main interpreter loop that performs mint operations.
|
|
|
|
*
|
|
|
|
* @param cls contains the `struct InterpreterState`
|
|
|
|
* @param tc scheduler context
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
interpreter_run (void *cls,
|
|
|
|
const struct GNUNET_SCHEDULER_TaskContext *tc)
|
|
|
|
{
|
|
|
|
struct InterpreterState *is = cls;
|
2015-07-04 18:45:51 +02:00
|
|
|
struct Command *cmd = &is->commands[is->ip];
|
|
|
|
const struct Command *ref;
|
|
|
|
struct TALER_ReservePublicKeyP reserve_pub;
|
2015-07-04 22:35:30 +02:00
|
|
|
struct TALER_CoinSpendPublicKeyP coin_pub;
|
2015-07-04 18:45:51 +02:00
|
|
|
struct TALER_Amount amount;
|
2015-07-05 16:55:01 +02:00
|
|
|
struct GNUNET_TIME_Absolute execution_date;
|
2015-07-04 18:45:51 +02:00
|
|
|
json_t *wire;
|
2015-07-04 17:30:38 +02:00
|
|
|
|
2015-07-04 20:47:39 +02:00
|
|
|
is->task = NULL;
|
2015-07-04 17:30:38 +02:00
|
|
|
if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
|
|
|
|
{
|
|
|
|
fprintf (stderr,
|
|
|
|
"Test aborted by shutdown request\n");
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (cmd->oc)
|
|
|
|
{
|
|
|
|
case OC_END:
|
|
|
|
result = GNUNET_OK;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
case OC_ADMIN_ADD_INCOMING:
|
2015-07-04 20:47:39 +02:00
|
|
|
if (NULL !=
|
|
|
|
cmd->details.admin_add_incoming.reserve_reference)
|
2015-07-04 18:45:51 +02:00
|
|
|
{
|
2015-07-04 20:47:39 +02:00
|
|
|
ref = find_command (is,
|
|
|
|
cmd->details.admin_add_incoming.reserve_reference);
|
|
|
|
GNUNET_assert (NULL != ref);
|
2015-07-04 18:45:51 +02:00
|
|
|
GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
|
|
|
|
cmd->details.admin_add_incoming.reserve_priv
|
|
|
|
= ref->details.admin_add_incoming.reserve_priv;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
|
|
|
|
|
|
|
|
priv = GNUNET_CRYPTO_eddsa_key_create ();
|
|
|
|
cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *priv;
|
|
|
|
GNUNET_free (priv);
|
|
|
|
}
|
|
|
|
GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv,
|
|
|
|
&reserve_pub.eddsa_pub);
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
|
|
|
|
&amount))
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Failed to parse amount `%s' at %u\n",
|
|
|
|
cmd->details.admin_add_incoming.amount,
|
|
|
|
is->ip);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
wire = json_loads (cmd->details.admin_add_incoming.wire,
|
|
|
|
JSON_REJECT_DUPLICATES,
|
|
|
|
NULL);
|
|
|
|
if (NULL == wire)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Failed to parse wire details `%s' at %u\n",
|
2015-07-04 20:47:39 +02:00
|
|
|
cmd->details.admin_add_incoming.wire,
|
2015-07-04 18:45:51 +02:00
|
|
|
is->ip);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
2015-07-05 16:55:01 +02:00
|
|
|
execution_date = GNUNET_TIME_absolute_get ();
|
|
|
|
TALER_round_abs_time (&execution_date);
|
2015-07-04 18:45:51 +02:00
|
|
|
cmd->details.admin_add_incoming.aih
|
|
|
|
= TALER_MINT_admin_add_incoming (mint,
|
|
|
|
&reserve_pub,
|
|
|
|
&amount,
|
2015-07-05 16:55:01 +02:00
|
|
|
execution_date,
|
2015-07-04 18:45:51 +02:00
|
|
|
wire,
|
|
|
|
&add_incoming_cb,
|
|
|
|
is);
|
2015-07-05 16:55:01 +02:00
|
|
|
if (NULL == cmd->details.admin_add_incoming.aih)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
2015-07-04 20:47:39 +02:00
|
|
|
trigger_context_task ();
|
2015-07-04 18:45:51 +02:00
|
|
|
return;
|
2015-07-09 11:36:13 +02:00
|
|
|
case OC_WITHDRAW_STATUS:
|
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
cmd->details.withdraw_status.reserve_reference);
|
|
|
|
ref = find_command (is,
|
|
|
|
cmd->details.withdraw_status.reserve_reference);
|
|
|
|
GNUNET_assert (NULL != ref);
|
|
|
|
GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
|
|
|
|
GNUNET_CRYPTO_eddsa_key_get_public (&ref->details.admin_add_incoming.reserve_priv.eddsa_priv,
|
|
|
|
&reserve_pub.eddsa_pub);
|
|
|
|
cmd->details.withdraw_status.wsh
|
|
|
|
= TALER_MINT_withdraw_status (mint,
|
|
|
|
&reserve_pub,
|
|
|
|
&withdraw_status_cb,
|
|
|
|
is);
|
|
|
|
trigger_context_task ();
|
|
|
|
return;
|
2015-07-04 17:30:38 +02:00
|
|
|
case OC_WITHDRAW_SIGN:
|
2015-07-04 22:35:30 +02:00
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
cmd->details.withdraw_sign.reserve_reference);
|
|
|
|
ref = find_command (is,
|
|
|
|
cmd->details.withdraw_sign.reserve_reference);
|
|
|
|
GNUNET_assert (NULL != ref);
|
|
|
|
GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
|
|
|
|
if (NULL != cmd->details.withdraw_sign.amount)
|
|
|
|
{
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_string_to_amount (cmd->details.withdraw_sign.amount,
|
|
|
|
&amount))
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Failed to parse amount `%s' at %u\n",
|
|
|
|
cmd->details.withdraw_sign.amount,
|
|
|
|
is->ip);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cmd->details.withdraw_sign.pk = find_pk (is->keys,
|
|
|
|
&amount);
|
|
|
|
}
|
|
|
|
if (NULL == cmd->details.withdraw_sign.pk)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Failed to determine denomination key at %u\n",
|
|
|
|
is->ip);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create coin's private key */
|
|
|
|
{
|
|
|
|
struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
|
|
|
|
|
|
|
|
priv = GNUNET_CRYPTO_eddsa_key_create ();
|
|
|
|
cmd->details.withdraw_sign.coin_priv.eddsa_priv = *priv;
|
|
|
|
GNUNET_free (priv);
|
|
|
|
}
|
|
|
|
GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.withdraw_sign.coin_priv.eddsa_priv,
|
|
|
|
&coin_pub.eddsa_pub);
|
|
|
|
cmd->details.withdraw_sign.blinding_key.rsa_blinding_key
|
2015-07-09 11:44:54 +02:00
|
|
|
= GNUNET_CRYPTO_rsa_blinding_key_create (GNUNET_CRYPTO_rsa_public_key_len (cmd->details.withdraw_sign.pk->key.rsa_public_key));
|
2015-07-04 22:35:30 +02:00
|
|
|
|
|
|
|
cmd->details.withdraw_sign.wsh
|
|
|
|
= TALER_MINT_withdraw_sign (mint,
|
|
|
|
cmd->details.withdraw_sign.pk,
|
|
|
|
&ref->details.admin_add_incoming.reserve_priv,
|
|
|
|
&cmd->details.withdraw_sign.coin_priv,
|
|
|
|
&cmd->details.withdraw_sign.blinding_key,
|
|
|
|
&withdraw_sign_cb,
|
|
|
|
is);
|
2015-07-05 16:55:01 +02:00
|
|
|
if (NULL == cmd->details.withdraw_sign.wsh)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
2015-07-05 11:52:00 +02:00
|
|
|
trigger_context_task ();
|
2015-07-04 22:35:30 +02:00
|
|
|
return;
|
2015-07-04 17:30:38 +02:00
|
|
|
case OC_DEPOSIT:
|
2015-07-05 14:32:26 +02:00
|
|
|
{
|
|
|
|
struct GNUNET_HashCode h_contract;
|
|
|
|
struct TALER_CoinSpendPublicKeyP coin_pub;
|
|
|
|
struct TALER_CoinSpendSignatureP coin_sig;
|
|
|
|
struct GNUNET_TIME_Absolute refund_deadline;
|
|
|
|
struct GNUNET_TIME_Absolute timestamp;
|
|
|
|
struct TALER_MerchantPublicKeyP merchant_pub;
|
|
|
|
json_t *wire;
|
|
|
|
|
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
cmd->details.deposit.coin_ref);
|
|
|
|
ref = find_command (is,
|
|
|
|
cmd->details.deposit.coin_ref);
|
|
|
|
GNUNET_assert (NULL != ref);
|
|
|
|
GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc);
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_string_to_amount (cmd->details.deposit.amount,
|
|
|
|
&amount))
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Failed to parse amount `%s' at %u\n",
|
|
|
|
cmd->details.deposit.amount,
|
|
|
|
is->ip);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
GNUNET_CRYPTO_hash (cmd->details.deposit.contract,
|
|
|
|
strlen (cmd->details.deposit.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",
|
|
|
|
cmd->details.deposit.wire_details,
|
|
|
|
is->ip);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
GNUNET_CRYPTO_eddsa_key_get_public (&ref->details.withdraw_sign.coin_priv.eddsa_priv,
|
|
|
|
&coin_pub.eddsa_pub);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
timestamp = GNUNET_TIME_absolute_get ();
|
2015-07-05 16:55:01 +02:00
|
|
|
TALER_round_abs_time (×tamp);
|
|
|
|
{
|
|
|
|
struct TALER_DepositRequestPS dr;
|
|
|
|
|
|
|
|
dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
|
|
|
|
dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
|
|
|
|
dr.h_contract = h_contract;
|
|
|
|
TALER_hash_json (wire,
|
|
|
|
&dr.h_wire);
|
|
|
|
dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
|
|
|
|
dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
|
|
|
|
dr.transaction_id = GNUNET_htonll (cmd->details.deposit.transaction_id);
|
|
|
|
TALER_amount_hton (&dr.amount_with_fee,
|
|
|
|
&amount);
|
|
|
|
TALER_amount_hton (&dr.deposit_fee,
|
|
|
|
&ref->details.withdraw_sign.pk->fee_deposit);
|
|
|
|
dr.merchant = merchant_pub;
|
|
|
|
dr.coin_pub = coin_pub;
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
GNUNET_CRYPTO_eddsa_sign (&ref->details.withdraw_sign.coin_priv.eddsa_priv,
|
|
|
|
&dr.purpose,
|
|
|
|
&coin_sig.eddsa_signature));
|
2015-07-05 14:32:26 +02:00
|
|
|
|
2015-07-05 16:55:01 +02:00
|
|
|
}
|
2015-07-05 14:32:26 +02:00
|
|
|
cmd->details.deposit.dh
|
|
|
|
= TALER_MINT_deposit (mint,
|
|
|
|
&amount,
|
|
|
|
wire,
|
|
|
|
&h_contract,
|
|
|
|
&coin_pub,
|
|
|
|
&ref->details.withdraw_sign.sig,
|
|
|
|
&ref->details.withdraw_sign.pk->key,
|
|
|
|
timestamp,
|
|
|
|
cmd->details.deposit.transaction_id,
|
|
|
|
&merchant_pub,
|
|
|
|
refund_deadline,
|
|
|
|
&coin_sig,
|
|
|
|
&deposit_cb,
|
|
|
|
is);
|
|
|
|
if (NULL == cmd->details.deposit.dh)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
json_decref (wire);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
2015-07-05 16:55:01 +02:00
|
|
|
trigger_context_task ();
|
2015-07-05 14:32:26 +02:00
|
|
|
return;
|
|
|
|
}
|
2015-07-04 17:30:38 +02:00
|
|
|
default:
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Unknown instruction %d at %u (%s)\n",
|
|
|
|
cmd->oc,
|
2015-07-04 20:47:39 +02:00
|
|
|
is->ip,
|
2015-07-04 17:30:38 +02:00
|
|
|
cmd->label);
|
|
|
|
fail (is);
|
|
|
|
return;
|
|
|
|
}
|
2015-07-04 20:47:39 +02:00
|
|
|
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
|
|
|
|
is);
|
2015-07-04 17:30:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-18 11:39:53 +02:00
|
|
|
/**
|
|
|
|
* Function run when the test terminates (good or bad).
|
|
|
|
* Cleans up our state.
|
|
|
|
*
|
2015-07-04 20:47:39 +02:00
|
|
|
* @param cls the interpreter state.
|
2015-06-18 11:39:53 +02:00
|
|
|
* @param tc unused
|
|
|
|
*/
|
2015-01-08 18:37:20 +01:00
|
|
|
static void
|
2015-06-18 01:17:01 +02:00
|
|
|
do_shutdown (void *cls,
|
|
|
|
const struct GNUNET_SCHEDULER_TaskContext *tc)
|
2015-01-08 18:37:20 +01:00
|
|
|
{
|
2015-07-04 20:47:39 +02:00
|
|
|
struct InterpreterState *is = cls;
|
|
|
|
struct Command *cmd;
|
|
|
|
unsigned int i;
|
|
|
|
|
2015-01-09 18:18:59 +01:00
|
|
|
shutdown_task = NULL;
|
2015-07-04 20:47:39 +02:00
|
|
|
for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
|
|
|
|
{
|
|
|
|
switch (cmd->oc)
|
|
|
|
{
|
|
|
|
case OC_END:
|
|
|
|
GNUNET_assert (0);
|
|
|
|
break;
|
|
|
|
case OC_ADMIN_ADD_INCOMING:
|
|
|
|
if (NULL != cmd->details.admin_add_incoming.aih)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
|
|
|
"Command %u (%s) did not complete\n",
|
|
|
|
i,
|
|
|
|
cmd->label);
|
|
|
|
TALER_MINT_admin_add_incoming_cancel (cmd->details.admin_add_incoming.aih);
|
|
|
|
cmd->details.admin_add_incoming.aih = NULL;
|
|
|
|
}
|
|
|
|
break;
|
2015-07-09 11:36:13 +02:00
|
|
|
case OC_WITHDRAW_STATUS:
|
|
|
|
if (NULL != cmd->details.withdraw_status.wsh)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
|
|
|
"Command %u (%s) did not complete\n",
|
|
|
|
i,
|
|
|
|
cmd->label);
|
|
|
|
TALER_MINT_withdraw_status_cancel (cmd->details.withdraw_status.wsh);
|
|
|
|
cmd->details.withdraw_status.wsh = NULL;
|
|
|
|
}
|
|
|
|
break;
|
2015-07-04 20:47:39 +02:00
|
|
|
case OC_WITHDRAW_SIGN:
|
2015-07-04 22:00:29 +02:00
|
|
|
if (NULL != cmd->details.withdraw_sign.wsh)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
|
|
|
"Command %u (%s) did not complete\n",
|
|
|
|
i,
|
|
|
|
cmd->label);
|
|
|
|
TALER_MINT_withdraw_sign_cancel (cmd->details.withdraw_sign.wsh);
|
|
|
|
cmd->details.withdraw_sign.wsh = NULL;
|
|
|
|
}
|
2015-07-04 22:35:30 +02:00
|
|
|
if (NULL != cmd->details.withdraw_sign.sig.rsa_signature)
|
|
|
|
{
|
|
|
|
GNUNET_CRYPTO_rsa_signature_free (cmd->details.withdraw_sign.sig.rsa_signature);
|
|
|
|
cmd->details.withdraw_sign.sig.rsa_signature = NULL;
|
|
|
|
}
|
|
|
|
if (NULL != cmd->details.withdraw_sign.blinding_key.rsa_blinding_key)
|
|
|
|
{
|
|
|
|
GNUNET_CRYPTO_rsa_blinding_key_free (cmd->details.withdraw_sign.blinding_key.rsa_blinding_key);
|
|
|
|
cmd->details.withdraw_sign.blinding_key.rsa_blinding_key = NULL;
|
|
|
|
}
|
2015-07-04 20:47:39 +02:00
|
|
|
break;
|
|
|
|
case OC_DEPOSIT:
|
2015-07-04 22:00:29 +02:00
|
|
|
if (NULL != cmd->details.deposit.dh)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
|
|
|
"Command %u (%s) did not complete\n",
|
|
|
|
i,
|
|
|
|
cmd->label);
|
|
|
|
TALER_MINT_deposit_cancel (cmd->details.deposit.dh);
|
|
|
|
cmd->details.deposit.dh = NULL;
|
|
|
|
}
|
2015-07-04 20:47:39 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Unknown instruction %d at %u (%s)\n",
|
|
|
|
cmd->oc,
|
|
|
|
i,
|
|
|
|
cmd->label);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (NULL != is->task)
|
|
|
|
{
|
|
|
|
GNUNET_SCHEDULER_cancel (is->task);
|
|
|
|
is->task = NULL;
|
|
|
|
}
|
|
|
|
GNUNET_free (is);
|
2015-06-18 01:17:01 +02:00
|
|
|
if (NULL != ctx_task)
|
2015-01-08 18:37:20 +01:00
|
|
|
{
|
2015-06-18 01:17:01 +02:00
|
|
|
GNUNET_SCHEDULER_cancel (ctx_task);
|
|
|
|
ctx_task = NULL;
|
|
|
|
}
|
|
|
|
if (NULL != mint)
|
|
|
|
{
|
|
|
|
TALER_MINT_disconnect (mint);
|
|
|
|
mint = NULL;
|
|
|
|
}
|
|
|
|
if (NULL != ctx)
|
|
|
|
{
|
|
|
|
TALER_MINT_fini (ctx);
|
|
|
|
ctx = NULL;
|
2015-01-08 18:37:20 +01:00
|
|
|
}
|
|
|
|
}
|
2015-06-18 01:17:01 +02:00
|
|
|
|
|
|
|
|
2015-01-08 18:37:20 +01:00
|
|
|
/**
|
2015-06-18 01:17:01 +02:00
|
|
|
* Functions of this type are called to provide the retrieved signing and
|
|
|
|
* denomination keys of the mint. No TALER_MINT_*() functions should be called
|
|
|
|
* in this callback.
|
2015-01-08 18:37:20 +01:00
|
|
|
*
|
|
|
|
* @param cls closure
|
2015-06-18 01:17:01 +02:00
|
|
|
* @param keys information about keys of the mint
|
2015-01-08 18:37:20 +01:00
|
|
|
*/
|
|
|
|
static void
|
2015-06-18 01:17:01 +02:00
|
|
|
cert_cb (void *cls,
|
|
|
|
const struct TALER_MINT_Keys *keys)
|
2015-01-08 18:37:20 +01:00
|
|
|
{
|
2015-07-04 20:47:39 +02:00
|
|
|
struct InterpreterState *is = cls;
|
2015-07-04 17:30:38 +02:00
|
|
|
|
2015-07-05 13:21:53 +02:00
|
|
|
/* check that keys is OK */
|
2015-06-18 01:17:01 +02:00
|
|
|
#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); GNUNET_SCHEDULER_shutdown(); return; } while (0)
|
|
|
|
ERR (NULL == keys);
|
|
|
|
ERR (0 == keys->num_sign_keys);
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Read %u signing keys\n",
|
|
|
|
keys->num_sign_keys);
|
|
|
|
ERR (0 == keys->num_denom_keys);
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Read %u denomination keys\n",
|
|
|
|
keys->num_denom_keys);
|
|
|
|
#undef ERR
|
2015-07-04 17:30:38 +02:00
|
|
|
|
2015-07-05 13:21:53 +02:00
|
|
|
/* run actual tests via interpreter-loop */
|
2015-07-04 17:30:38 +02:00
|
|
|
is->keys = keys;
|
2015-07-04 20:47:39 +02:00
|
|
|
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
|
|
|
|
is);
|
2015-01-08 18:37:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-06-18 01:17:01 +02:00
|
|
|
* Task that runs the context's event loop with the GNUnet scheduler.
|
2015-01-08 18:37:20 +01:00
|
|
|
*
|
2015-06-18 01:17:01 +02:00
|
|
|
* @param cls unused
|
|
|
|
* @param tc scheduler context (unused)
|
2015-01-08 18:37:20 +01:00
|
|
|
*/
|
|
|
|
static void
|
2015-06-18 01:17:01 +02:00
|
|
|
context_task (void *cls,
|
|
|
|
const struct GNUNET_SCHEDULER_TaskContext *tc)
|
2015-01-08 18:37:20 +01:00
|
|
|
{
|
2015-06-18 01:17:01 +02:00
|
|
|
long timeout;
|
|
|
|
int max_fd;
|
|
|
|
fd_set read_fd_set;
|
|
|
|
fd_set write_fd_set;
|
|
|
|
fd_set except_fd_set;
|
|
|
|
struct GNUNET_NETWORK_FDSet *rs;
|
|
|
|
struct GNUNET_NETWORK_FDSet *ws;
|
|
|
|
struct GNUNET_TIME_Relative delay;
|
|
|
|
|
|
|
|
ctx_task = NULL;
|
|
|
|
TALER_MINT_perform (ctx);
|
|
|
|
max_fd = -1;
|
|
|
|
timeout = -1;
|
|
|
|
FD_ZERO (&read_fd_set);
|
|
|
|
FD_ZERO (&write_fd_set);
|
|
|
|
FD_ZERO (&except_fd_set);
|
|
|
|
TALER_MINT_get_select_info (ctx,
|
|
|
|
&read_fd_set,
|
|
|
|
&write_fd_set,
|
|
|
|
&except_fd_set,
|
|
|
|
&max_fd,
|
|
|
|
&timeout);
|
|
|
|
if (timeout >= 0)
|
|
|
|
delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
|
|
|
|
timeout);
|
|
|
|
else
|
|
|
|
delay = GNUNET_TIME_UNIT_FOREVER_REL;
|
|
|
|
rs = GNUNET_NETWORK_fdset_create ();
|
|
|
|
GNUNET_NETWORK_fdset_copy_native (rs,
|
|
|
|
&read_fd_set,
|
2015-07-04 21:27:28 +02:00
|
|
|
max_fd + 1);
|
2015-06-18 01:17:01 +02:00
|
|
|
ws = GNUNET_NETWORK_fdset_create ();
|
|
|
|
GNUNET_NETWORK_fdset_copy_native (ws,
|
|
|
|
&write_fd_set,
|
|
|
|
max_fd + 1);
|
|
|
|
ctx_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
|
|
|
|
delay,
|
|
|
|
rs,
|
|
|
|
ws,
|
|
|
|
&context_task,
|
|
|
|
cls);
|
|
|
|
GNUNET_NETWORK_fdset_destroy (rs);
|
|
|
|
GNUNET_NETWORK_fdset_destroy (ws);
|
2015-01-08 18:37:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Main function that will be run by the scheduler.
|
|
|
|
*
|
|
|
|
* @param cls closure
|
|
|
|
* @param args remaining command-line arguments
|
|
|
|
* @param cfgfile name of the configuration file used (for saving, can be NULL!)
|
|
|
|
* @param config configuration
|
|
|
|
*/
|
|
|
|
static void
|
2015-06-18 01:17:01 +02:00
|
|
|
run (void *cls,
|
2015-06-18 13:44:58 +02:00
|
|
|
const struct GNUNET_SCHEDULER_TaskContext *tc)
|
2015-01-08 18:37:20 +01:00
|
|
|
{
|
2015-07-04 20:47:39 +02:00
|
|
|
struct InterpreterState *is;
|
|
|
|
static struct Command commands[] =
|
|
|
|
{
|
2015-07-05 13:21:53 +02:00
|
|
|
/* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */
|
2015-07-04 20:47:39 +02:00
|
|
|
{ .oc = OC_ADMIN_ADD_INCOMING,
|
|
|
|
.label = "create-reserve-1",
|
2015-07-09 12:22:07 +02:00
|
|
|
.expected_response_code = MHD_HTTP_OK,
|
2015-07-05 16:55:01 +02:00
|
|
|
.details.admin_add_incoming.wire = "{ \"type\":\"TEST\", \"bank\":\"source bank\", \"account\":42 }",
|
2015-07-05 13:18:49 +02:00
|
|
|
.details.admin_add_incoming.amount = "EUR:5.01" },
|
2015-07-04 20:47:39 +02:00
|
|
|
{ .oc = OC_WITHDRAW_SIGN,
|
|
|
|
.label = "withdraw-coin-1",
|
2015-07-09 12:22:07 +02:00
|
|
|
.expected_response_code = MHD_HTTP_OK,
|
2015-07-04 20:47:39 +02:00
|
|
|
.details.withdraw_sign.reserve_reference = "create-reserve-1",
|
|
|
|
.details.withdraw_sign.amount = "EUR:5" },
|
2015-07-09 11:36:13 +02:00
|
|
|
{ .oc = OC_WITHDRAW_STATUS,
|
|
|
|
.label = "withdraw-status-1",
|
2015-07-09 12:22:07 +02:00
|
|
|
.expected_response_code = MHD_HTTP_OK,
|
2015-07-09 12:11:01 +02:00
|
|
|
.details.withdraw_status.reserve_reference = "create-reserve-1",
|
|
|
|
.details.withdraw_status.expected_balance = "EUR:0" },
|
2015-07-04 20:47:39 +02:00
|
|
|
{ .oc = OC_DEPOSIT,
|
|
|
|
.label = "deposit-simple",
|
2015-07-09 12:22:07 +02:00
|
|
|
.expected_response_code = MHD_HTTP_OK,
|
2015-07-04 20:47:39 +02:00
|
|
|
.details.deposit.amount = "EUR:5",
|
|
|
|
.details.deposit.coin_ref = "withdraw-coin-1",
|
2015-07-05 16:55:01 +02:00
|
|
|
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
|
2015-07-04 20:47:39 +02:00
|
|
|
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
|
|
|
|
.details.deposit.transaction_id = 1 },
|
|
|
|
{ .oc = OC_END }
|
|
|
|
};
|
|
|
|
|
|
|
|
is = GNUNET_new (struct InterpreterState);
|
|
|
|
is->commands = commands;
|
|
|
|
|
2015-01-08 18:37:20 +01:00
|
|
|
ctx = TALER_MINT_init ();
|
2015-06-18 01:17:01 +02:00
|
|
|
GNUNET_assert (NULL != ctx);
|
|
|
|
ctx_task = GNUNET_SCHEDULER_add_now (&context_task,
|
|
|
|
ctx);
|
|
|
|
mint = TALER_MINT_connect (ctx,
|
2015-06-18 13:44:58 +02:00
|
|
|
"http://localhost:8081",
|
2015-07-04 20:47:39 +02:00
|
|
|
&cert_cb, is,
|
2015-06-18 01:17:01 +02:00
|
|
|
TALER_MINT_OPTION_END);
|
2015-01-08 18:37:20 +01:00
|
|
|
GNUNET_assert (NULL != mint);
|
2015-07-04 20:47:39 +02:00
|
|
|
shutdown_task
|
|
|
|
= GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
|
2015-01-08 18:37:20 +01:00
|
|
|
(GNUNET_TIME_UNIT_SECONDS, 5),
|
2015-07-04 20:47:39 +02:00
|
|
|
&do_shutdown, is);
|
2015-01-08 18:37:20 +01:00
|
|
|
}
|
|
|
|
|
2015-06-18 01:17:01 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Main function for the testcase for the mint API.
|
|
|
|
*
|
|
|
|
* @param argc expected to be 1
|
|
|
|
* @param argv expected to only contain the program name
|
|
|
|
*/
|
2015-01-08 18:37:20 +01:00
|
|
|
int
|
2015-06-18 01:17:01 +02:00
|
|
|
main (int argc,
|
|
|
|
char * const *argv)
|
2015-01-08 18:37:20 +01:00
|
|
|
{
|
2015-06-18 14:25:09 +02:00
|
|
|
struct GNUNET_OS_Process *proc;
|
2015-06-18 13:44:58 +02:00
|
|
|
struct GNUNET_OS_Process *mintd;
|
|
|
|
|
|
|
|
GNUNET_log_setup ("test-mint-api",
|
|
|
|
"WARNING",
|
|
|
|
NULL);
|
2015-06-18 14:25:09 +02:00
|
|
|
proc = GNUNET_OS_start_process (GNUNET_NO,
|
|
|
|
GNUNET_OS_INHERIT_STD_ALL,
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
"taler-mint-keyup",
|
|
|
|
"taler-mint-keyup",
|
|
|
|
"-d", "test-mint-home",
|
|
|
|
"-m", "test-mint-home/master.priv",
|
|
|
|
NULL);
|
|
|
|
GNUNET_OS_process_wait (proc);
|
|
|
|
GNUNET_OS_process_destroy (proc);
|
2015-06-18 13:44:58 +02:00
|
|
|
mintd = GNUNET_OS_start_process (GNUNET_NO,
|
|
|
|
GNUNET_OS_INHERIT_STD_ALL,
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
"taler-mint-httpd",
|
|
|
|
"taler-mint-httpd",
|
|
|
|
"-d", "test-mint-home",
|
|
|
|
NULL);
|
2015-06-18 13:47:35 +02:00
|
|
|
/* give child time to start and bind against the socket */
|
2015-07-05 17:27:20 +02:00
|
|
|
sleep (2);
|
2015-01-08 18:37:20 +01:00
|
|
|
result = GNUNET_SYSERR;
|
2015-06-18 13:44:58 +02:00
|
|
|
GNUNET_SCHEDULER_run (&run, NULL);
|
|
|
|
GNUNET_OS_process_kill (mintd,
|
|
|
|
SIGTERM);
|
|
|
|
GNUNET_OS_process_wait (mintd);
|
|
|
|
GNUNET_OS_process_destroy (mintd);
|
2015-01-08 18:37:20 +01:00
|
|
|
return (GNUNET_OK == result) ? 0 : 1;
|
|
|
|
}
|
2015-06-18 11:39:53 +02:00
|
|
|
|
|
|
|
/* end of test_mint_api.c */
|