Implement new traits-based tests.

This commit is contained in:
Marcello Stanisci 2018-01-23 10:28:24 +01:00
parent b198bb3867
commit fe6960cce8
No known key found for this signature in database
GPG Key ID: 8D526861953F4C0F
38 changed files with 7425 additions and 450 deletions

2
.gitignore vendored
View File

@ -36,6 +36,7 @@ src/auditor/taler-auditor
src/auditor/taler-auditor-sign
src/bank-lib/test_bank_api
src/bank-lib/test_bank_api_with_fakebank
src/exchange-lib/test_exchange_api_new
src/exchange-lib/test_exchange_api
src/exchange-lib/test_exchange_api_home/.local/share/taler/exchange/live-keys/
src/exchange-lib/test_exchange_api_home/.local/share/taler/exchange/wirefees/
@ -86,6 +87,7 @@ doc/manual/manual.vr
contrib/taler-exchange.tag
doxygen-doc/
src/exchange-lib/test_exchange_api_keys_cherry_picking
src/exchange-lib/test_exchange_api_keys_cherry_picking_new
src/auditor/taler-wire-auditor
contrib/auditor-report.aux
contrib/auditor-report.log

View File

@ -34,14 +34,26 @@ libtalerexchange_la_LIBADD = \
-ljansson \
$(XLIB)
libtalertesting_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtalertesting_la_SOURCES = \
testing_api_cmd_exec_aggregator.c \
testing_api_cmd_exec_wirewatch.c \
testing_api_cmd_exec_keyup.c \
testing_api_cmd_exec_auditor-sign.c \
testing_api_cmd_fakebank_transfer.c \
testing_api_cmd_withdraw.c \
testing_api_cmd_wire.c \
testing_api_cmd_refund.c \
testing_api_cmd_status.c \
testing_api_cmd_deposit.c \
testing_api_cmd_refresh.c \
testing_api_cmd_track.c \
testing_api_cmd_bank_check.c \
testing_api_cmd_payback.c \
testing_api_cmd_signal.c \
testing_api_cmd_check_keys.c \
testing_api_helpers.c \
testing_api_loop.c \
testing_api_traits.c \
@ -50,7 +62,12 @@ libtalertesting_la_SOURCES = \
testing_api_trait_denom_pub.c \
testing_api_trait_denom_sig.c \
testing_api_trait_process.c \
testing_api_trait_reserve_priv.c
testing_api_trait_reserve_priv.c \
testing_api_trait_number.c \
testing_api_trait_fresh_coin.c \
testing_api_trait_string.c \
testing_api_trait_key_peer.c \
testing_api_trait_wtid.c
libtalertesting_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
@ -71,13 +88,17 @@ endif
endif
check_PROGRAMS = \
test_exchange_api_keys_cherry_picking_new \
test_exchange_api_new \
test_exchange_api \
test_exchange_api_keys_cherry_picking \
test_exchange_api_new
test_exchange_api_keys_cherry_picking
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
# FIXME: uncomment those.
TESTS = \
test_exchange_api_keys_cherry_picking_new \
test_exchange_api_new \
test_exchange_api \
test_exchange_api_keys_cherry_picking
@ -108,6 +129,19 @@ test_exchange_api_new_LDADD = \
-lgnunetutil \
-ljansson
test_exchange_api_keys_cherry_picking_new_SOURCES = \
test_exchange_api_keys_cherry_picking_new.c
test_exchange_api_keys_cherry_picking_new_LDADD = \
libtalertesting.la \
libtalerexchange.la \
$(LIBGCRYPT_LIBS) \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/bank-lib/libtalerbank.la \
-lgnunetcurl \
-lgnunetutil \
-ljansson
test_exchange_api_keys_cherry_picking_SOURCES = \
test_exchange_api_keys_cherry_picking.c
test_exchange_api_keys_cherry_picking_LDADD = \

View File

@ -1,7 +1,7 @@
# This file is in the public domain.
#
[PATHS]
# Persistant data storage for the testcase
# Persistent data storage for the testcase
TALER_TEST_HOME = test_exchange_api_home/
[taler]

View File

@ -0,0 +1,135 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/test_exchange_api_keys_cherry_picking_new.c
* @brief testcase to test exchange's /keys cherry picking ability
* @author Marcello Stanisci
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
#include "taler_exchange_service.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
#include "taler_bank_service.h"
#include "taler_fakebank_lib.h"
#include "taler_testing_lib.h"
/**
* Configuration file we use. One (big) configuration is used
* for the various components for this test.
*/
#define CONFIG_FILE "test_exchange_api_keys_cherry_picking.conf"
/**
* Used to increase the number of denomination keys.
*/
#define CONFIG_FILE_EXTENDED \
"test_exchange_api_keys_cherry_picking_extended.conf"
/**
* Main function that will tell the interpreter what commands to
* run.
*
* @param cls closure
*/
static void
run (void *cls,
struct TALER_TESTING_Interpreter *is)
{
struct TALER_TESTING_Command commands[] = {
/* Send signal to the exchange to see if it reacts */
TALER_TESTING_cmd_signal ("signal-reaction-1",
is->exchanged,
SIGUSR1),
TALER_TESTING_cmd_check_keys ("check-keys-1",
1, 4,
is->exchange),
TALER_TESTING_cmd_exec_keyup ("keyup-2", /* 1st keyup happens at start-up */
CONFIG_FILE_EXTENDED),
TALER_TESTING_cmd_exec_auditor_sign ("sign-keys-1",
CONFIG_FILE),
TALER_TESTING_cmd_signal ("trigger-keys-reload-1",
is->exchanged,
SIGUSR1),
TALER_TESTING_cmd_check_keys ("check-keys-2",
2, 8,
is->exchange),
/**
* End the suite. Fixme: better to have a label for this
* too, as it shows "(null)" in logs.
*/
TALER_TESTING_cmd_end ()
};
TALER_TESTING_run (is, commands);
}
int
main (int argc,
char * const *argv)
{
/* These environment variables get in the way... */
unsetenv ("XDG_DATA_HOME");
unsetenv ("XDG_CONFIG_HOME");
GNUNET_log_setup ("test-exchange-api-cherry-picking-new",
"DEBUG", NULL);
TALER_TESTING_cleanup_files (CONFIG_FILE);
/* @helpers. Run keyup, create tables, ... Note: it
* fetches the port number from config in order to see
* if it's available. */
switch (TALER_TESTING_prepare_exchange (CONFIG_FILE))
{
case GNUNET_SYSERR:
GNUNET_break (0);
return 1;
case GNUNET_NO:
return 77;
case GNUNET_OK:
if (GNUNET_OK !=
/* Set up event loop and reschedule context, plus
* start/stop the exchange. It calls TALER_TESTING_setup
* which creates the 'is' object.
*/
TALER_TESTING_setup_with_exchange (&run,
NULL,
CONFIG_FILE))
return 1;
break;
default:
GNUNET_break (0);
return 1;
}
return 0;
}
/* end of test_exchange_api_keys_cherry_picking_new.c */

View File

@ -2,23 +2,29 @@
This file is part of TALER
Copyright (C) 2014-2018 Taler Systems SA
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 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.
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, see <http://www.gnu.org/licenses/>
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange/test_exchange_api_new.c
* @brief testcase to test exchange's HTTP API interface
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
* @author Christian Grothoff
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
@ -37,10 +43,17 @@
#define CONFIG_FILE "test_exchange_api.conf"
/**
* URL of the fakebank. Obtained from CONFIG_FILE's "exchange-wire-test:BANK_URL" option.
* URL of the fakebank. Obtained from CONFIG_FILE's
* "exchange-wire-test:BANK_URI" option.
*/
static char *fakebank_url;
/**
* FIXME: what about putting exchange_url also global
* here? Right now, the exchange port is being "bounced"
* between functions before exchange_url is constructed
* and TALER_EXCHANGE_connect() is called with that.
*/
/**
* Account number of the exchange at the bank.
@ -71,6 +84,15 @@ static char *fakebank_url;
#define CMD_EXEC_WIREWATCH(label) \
TALER_TESTING_cmd_exec_wirewatch (label, CONFIG_FILE)
/**
* Execute the taler-exchange-aggregator command with
* our configuration file.
*
* @param label label to use for the command.
*/
#define CMD_EXEC_AGGREGATOR(label) \
TALER_TESTING_cmd_exec_aggregator (label, CONFIG_FILE)
/**
* Run wire transfer of funds from some user's account to the
* exchange.
@ -83,9 +105,22 @@ static char *fakebank_url;
fakebank_url, USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, \
USER_LOGIN_NAME, USER_LOGIN_PASS)
/**
* Run wire transfer of funds from some user's account to the
* exchange.
*
* @param label label to use for the command.
* @param amount amount to transfer, i.e. "EUR:1"
*/
#define CMD_TRANSFER_TO_EXCHANGE_SUBJECT(label,amount,subject) \
TALER_TESTING_cmd_fakebank_transfer_with_subject \
(label, amount, fakebank_url, USER_ACCOUNT_NO, \
EXCHANGE_ACCOUNT_NO, USER_LOGIN_NAME, USER_LOGIN_PASS, \
subject)
/**
* Main function that will tell the interpreter what commands to run.
* Main function that will tell the interpreter what commands to
* run.
*
* @param cls closure
*/
@ -94,9 +129,609 @@ run (void *cls,
struct TALER_TESTING_Interpreter *is)
{
struct TALER_TESTING_Command commands[] = {
/****** Start of "wire" testing ******/
/**
* Move money to the exchange's bank account.
*/
CMD_TRANSFER_TO_EXCHANGE ("create-reserve-1",
"EUR:5.01"),
CMD_EXEC_WIREWATCH ("exec-wirewatch-1"),
/**
* Make a reserve exist, according to the previous
* transfer.
*/
CMD_EXEC_WIREWATCH ("wirewatch-1"),
/**
* Check if 'test' wire method is offered by the exchange.
*/
TALER_TESTING_cmd_wire ("wire-test-1",
is->exchange,
"test",
NULL,
MHD_HTTP_OK),
/**
* Check if 'sepa' wire method is offered by the exchange.
*/
TALER_TESTING_cmd_wire ("wire-sepa-1",
is->exchange,
"sepa",
NULL,
MHD_HTTP_OK),
/****** End of "wire" testing ******/
/****** Start of withdraw and spend testing ******/
/**
* Withdraw EUR:5.
*/
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1",
is->exchange,
"create-reserve-1",
"EUR:5",
MHD_HTTP_OK),
/**
* Check the reserve is depleted.
*/
TALER_TESTING_cmd_status ("status-1",
is->exchange,
"create-reserve-1",
"EUR:0",
MHD_HTTP_OK),
/**
* Spend the coin.
*/
TALER_TESTING_cmd_deposit
("deposit-simple", is->exchange, "withdraw-coin-1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK),
/**
* Try to overdraw.
*/
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
is->exchange,
"create-reserve-1",
"EUR:5",
MHD_HTTP_FORBIDDEN),
/**
* Try to double spend using different wire details.
*/
TALER_TESTING_cmd_deposit
("deposit-double-1", is->exchange, "withdraw-coin-1", 0,
TALER_TESTING_make_wire_details
("{\"type\":\"test\",\"account_number\":43}",
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
/**
* Try to double spend using a different transaction id.
* (copied verbatim from old exchange-lib tests.)
* FIXME: how can it get a different transaction id? There
* isn't such a thing actually, the exchange only knows about
* contract terms' hashes. So since the contract terms are
* exactly the same as the previous command, how can a different
* id be generated?
*/
TALER_TESTING_cmd_deposit
("deposit-double-1", is->exchange, "withdraw-coin-1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\", \"account_number\":43}",
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
/**
* Try to double spend with different proposal.
*/
TALER_TESTING_cmd_deposit
("deposit-double-2", is->exchange, "withdraw-coin-1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\", \"account_number\":43}",
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\"value\":2}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
/****** End of withdraw and spend testing ******/
/****** Start of refresh testing ******/
/**
* Fill reserve with EUR:5, 1ct is for fees. NOTE: the old
* test-suite gave a account number of _424_ to the user at
* this step; to type less, here the _42_ number is reused.
* Does this change the tests semantics?
*/
CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-1",
"EUR:5.01"),
/**
* Make previous command effective.
*/
CMD_EXEC_WIREWATCH ("wirewatch-2"),
/**
* Withdraw EUR:5.
*/
TALER_TESTING_cmd_withdraw_amount
("refresh-withdraw-coin-1",
is->exchange,
"refresh-create-reserve-1",
"EUR:5",
MHD_HTTP_OK),
/**
* Try to partially spend (deposit) 1 EUR of the 5 EUR coin
* (in full) (merchant would receive EUR:0.99 due to 1 ct
* deposit fee)
*/
TALER_TESTING_cmd_deposit
("refresh-deposit-partial", is->exchange,
"refresh-withdraw-coin-1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\
\"value\":\"EUR:1\"}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK),
/**
* Melt the rest of the coin's value
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
TALER_TESTING_cmd_refresh_melt
("refresh-melt-1", is->exchange, "EUR:4",
"refresh-withdraw-coin-1", MHD_HTTP_OK),
/**
* Complete (successful) melt operation, and
* withdraw the coins
*/
TALER_TESTING_cmd_refresh_reveal
("refresh-reveal-1", is->exchange,
"refresh-melt-1", MHD_HTTP_OK),
/**
* Do it again to check idempotency
*/
TALER_TESTING_cmd_refresh_reveal
("refresh-reveal-1-idempotency",
is->exchange, "refresh-melt-1", MHD_HTTP_OK),
/**
* Test that /refresh/link works
*/
TALER_TESTING_cmd_refresh_link
("refresh-link-1", is->exchange,
"refresh-reveal-1", MHD_HTTP_OK),
/**
* Try to spend a refreshed EUR:1 coin
*/
TALER_TESTING_cmd_deposit
("refresh-deposit-refreshed-1a", is->exchange,
"refresh-reveal-1-idempotency", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\
\"value\":3}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK),
/**
* Try to spend a refreshed EUR:0.1 coin
*/
TALER_TESTING_cmd_deposit
("refresh-deposit-refreshed-1b", is->exchange,
"refresh-reveal-1", 4,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":43}",
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\
\"value\":3}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:0.1", MHD_HTTP_OK),
/* Test running a failing melt operation (same operation
* again must fail) */
TALER_TESTING_cmd_refresh_melt
("refresh-melt-failing", is->exchange, "EUR:4",
"refresh-withdraw-coin-1", MHD_HTTP_FORBIDDEN),
/* 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 exchange knows about the deposit, but has no WTID yet.
*/
TALER_TESTING_cmd_track_transaction
("deposit-wtid-found", is->exchange,
"deposit-simple", 0, MHD_HTTP_ACCEPTED, NULL),
/**
* Try resolving a deposit's WTID for a failed deposit.
* As the deposit failed, the answer should be that the
* exchange does NOT know about the deposit.
*/
TALER_TESTING_cmd_track_transaction
("deposit-wtid-failing", is->exchange,
"deposit-double-2", 0, MHD_HTTP_NOT_FOUND, NULL),
/**
* Try resolving an undefined (all zeros) WTID; this
* should fail as obviously the exchange didn't use that
* WTID value for any transaction.
*/
TALER_TESTING_cmd_track_transfer_empty
("wire-deposit-failing", is->exchange,
NULL, 0, MHD_HTTP_NOT_FOUND),
/**
* Run transfers. Note that _actual_ aggregation will NOT
* happen here, as each deposit operation is run with a
* fresh merchant public key! NOTE: this comment comes
* "verbatim" from the old test-suite, and IMO does not explain
* a lot!
*/
CMD_EXEC_AGGREGATOR ("run-aggregator"),
/**
* Check all the transfers took place.
*/
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-499c", "https://exchange.com/",
"EUR:4.98", 2, 42),
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-99c1", "https://exchange.com/",
"EUR:0.98", 2, 42),
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-99c2", "https://exchange.com/",
"EUR:0.98", 2, 42),
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-99c", "https://exchange.com/",
"EUR:0.08", 2, 43),
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-aai-1", "https://exchange.com/",
"EUR:5.01", 42, 2),
/**
* NOTE: the old test-suite had this "check bank transfer"
* command with debit account == 424.
*/
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-aai-2", "https://exchange.com/",
"EUR:5.01", 42, 2),
TALER_TESTING_cmd_check_bank_empty ("check_bank_empty"),
TALER_TESTING_cmd_track_transaction
("deposit-wtid-ok", is->exchange,
"deposit-simple", 0, MHD_HTTP_OK, "check_bank_transfer-499c"),
TALER_TESTING_cmd_track_transfer
("wire-deposit-success-bank", is->exchange,
"check_bank_transfer-99c1", 0, MHD_HTTP_OK, "EUR:0.98",
"EUR:0.01"),
TALER_TESTING_cmd_track_transfer
("wire-deposits-success-wtid", is->exchange,
"deposit-wtid-ok", 0, MHD_HTTP_OK, "EUR:4.98",
"EUR:0.01"),
/* **** End of test tracking API ***** */
/* **** test /refund API ***** */
/**
* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per
* config.
*/
CMD_TRANSFER_TO_EXCHANGE ("create-reserve-r1",
"EUR:5.01"),
/**
* Run wire-watch to trigger the reserve creation.
*/
CMD_EXEC_WIREWATCH ("wirewatch-3"),
/* Withdraw a 5 EUR coin, at fee of 1 ct */
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1",
is->exchange,
"create-reserve-r1",
"EUR:5",
MHD_HTTP_OK),
/**
* Spend 5 EUR of the 5 EUR coin (in full) (merchant would
* receive EUR:4.99 due to 1 ct deposit fee)
*/
TALER_TESTING_cmd_deposit
("deposit-refund-1", is->exchange, "withdraw-coin-r1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\", \"account_number\":42}",
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\","
"\"value\":\"EUR:5\"}]}",
GNUNET_TIME_UNIT_MINUTES, "EUR:5", MHD_HTTP_OK),
/**
* Run transfers. Should do nothing as refund deadline blocks
* it
*/
CMD_EXEC_AGGREGATOR ("run-aggregator-refund"),
/**
* Check that aggregator didn't do anything, as expected.
* Note, this operation takes two commands: one to "flush"
* the preliminary transfer (used to withdraw) from the
* fakebank and the second to actually check there are not
* other transfers around.
*/
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-pre-refund", "https://exchange.com/",
"EUR:5.01", 42, 2),
TALER_TESTING_cmd_check_bank_empty
("check_bank_transfer-pre-refund"),
TALER_TESTING_cmd_refund
("refund-ok", MHD_HTTP_OK,
"EUR:5", "EUR:0.01", "deposit-refund-1"),
/**
* Spend 4.99 EUR of the refunded 4.99 EUR coin (1ct gone
* due to refund) (merchant would receive EUR:4.98 due to
* 1 ct deposit fee) */
TALER_TESTING_cmd_deposit
("deposit-refund-2", is->exchange, "withdraw-coin-r1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\", \"account_number\":42}",
fakebank_url),
"{\"items\":[{\"name\":\"more ice cream\","
"\"value\":\"EUR:5\"}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:4.99", MHD_HTTP_OK),
/**
* Run transfers. This will do the transfer as refund deadline
* was 0
*/
CMD_EXEC_AGGREGATOR ("run-aggregator-3"),
/**
* Check that deposit did run.
*/
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-pre-refund", "https://exchange.com/",
"EUR:4.97", 2, 42),
/**
* Run failing refund, as past deadline & aggregation.
*/
TALER_TESTING_cmd_refund
("refund-fail", MHD_HTTP_GONE,
"EUR:4.99", "EUR:0.01", "deposit-refund-2"),
TALER_TESTING_cmd_check_bank_empty
("check-empty-after-refund"),
/**
* Test refunded coins are never executed, even past
* refund deadline
*/
CMD_TRANSFER_TO_EXCHANGE ("create-reserve-rb",
"EUR:5.01"),
CMD_EXEC_WIREWATCH ("wirewatch-rb"),
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-rb",
is->exchange,
"create-reserve-rb",
"EUR:5",
MHD_HTTP_OK),
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-aai-3b", "https://exchange.com/",
"EUR:5.01", 42, 2),
TALER_TESTING_cmd_deposit
("deposit-refund-1b", is->exchange, "withdraw-coin-rb", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\", \"account_number\":42}",
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\","
"\"value\":\"EUR:5\"}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK),
/**
* Trigger refund (before aggregator had a chance to execute
* deposit, even though refund deadline was zero).
*/
TALER_TESTING_cmd_refund
("refund-ok-fast", MHD_HTTP_OK,
"EUR:5", "EUR:0.01", "deposit-refund-1b"),
/**
* Run transfers. This will do the transfer as refund deadline
* was 0, except of course because the refund succeeded, the
* transfer should no longer be done.
*/
CMD_EXEC_AGGREGATOR ("run-aggregator-3b"),
/* check that aggregator didn't do anything, as expected */
TALER_TESTING_cmd_check_bank_empty
("check-refund-fast-not-run"),
/* ************** End of refund API testing ************* */
/* ************** Test /payback API ************* */
/**
* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per
* config.
*/
CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-1",
"EUR:5.01"),
/**
* Run wire-watch to trigger the reserve creation.
*/
CMD_EXEC_WIREWATCH ("wirewatch-4"),
/* Withdraw a 5 EUR coin, at fee of 1 ct */
TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-1",
is->exchange,
"payback-create-reserve-1",
"EUR:5",
MHD_HTTP_OK),
TALER_TESTING_cmd_revoke ("revoke-1", MHD_HTTP_OK,
"payback-withdraw-coin-1",
CONFIG_FILE),
TALER_TESTING_cmd_payback ("payback-1", MHD_HTTP_OK,
"payback-withdraw-coin-1", "EUR:5"),
/* Check the money is back with the reserve */
TALER_TESTING_cmd_status ("payback-reserve-status-1",
is->exchange,
"payback-create-reserve-1",
"EUR:5.0",
MHD_HTTP_OK),
/**
* Fill reserve with EUR:2.02, as withdraw fee is 1 ct per
* config, then withdraw two coin, partially spend one, and
* then have the rest paid back. Check deposit of other coin
* fails. (Do not use EUR:5 here as the EUR:5 coin was
* revoked and we did not bother to create a new one...)
*/
CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-2",
"EUR:2.02"),
/* Make previous command effective. */
CMD_EXEC_WIREWATCH ("wirewatch-5"),
/* Withdraw a 1 EUR coin, at fee of 1 ct */
TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-2a",
is->exchange,
"payback-create-reserve-2",
"EUR:1",
MHD_HTTP_OK),
/* Withdraw a 1 EUR coin, at fee of 1 ct */
TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-2b",
is->exchange,
"payback-create-reserve-2",
"EUR:1",
MHD_HTTP_OK),
TALER_TESTING_cmd_deposit
("payback-deposit-partial", is->exchange,
"payback-withdraw-coin-2a", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
fakebank_url),
"{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_OK),
TALER_TESTING_cmd_revoke ("revoke-2", MHD_HTTP_OK,
"payback-withdraw-coin-2a",
CONFIG_FILE),
TALER_TESTING_cmd_payback ("payback-2", MHD_HTTP_OK,
"payback-withdraw-coin-2a",
"EUR:0.5"),
TALER_TESTING_cmd_payback ("payback-2b", MHD_HTTP_FORBIDDEN,
"payback-withdraw-coin-2a",
"EUR:0.5"),
TALER_TESTING_cmd_deposit
("payback-deposit-revoked", is->exchange,
"payback-withdraw-coin-2b", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
fakebank_url),
"{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_NOT_FOUND),
/* Test deposit fails after payback, with proof in payback */
/* FIXME: #3887: right now, the exchange will never return the
* coin's transaction history with payback data, as we get a
* 404 on the DK! */
TALER_TESTING_cmd_deposit
("payback-deposit-partial-after-payback", is->exchange,
"payback-withdraw-coin-2a", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
fakebank_url),
"{\"items\":[{\"name\":\"extra ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_NOT_FOUND),
/* Test that revoked coins cannot be withdrawn */
CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-3",
"EUR:1.01"),
CMD_EXEC_WIREWATCH ("wirewatch-6"),
TALER_TESTING_cmd_withdraw_amount
("payback-withdraw-coin-3-revoked",
is->exchange,
"payback-create-reserve-3",
"EUR:1",
MHD_HTTP_NOT_FOUND),
/* check that we are empty before the rejection test */
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-pr1", "https://exchange.com/",
"EUR:5.01", 42, 2),
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-pr2", "https://exchange.com/",
"EUR:2.02", 42, 2),
TALER_TESTING_cmd_check_bank_transfer
("check_bank_transfer-pr3", "https://exchange.com/",
"EUR:1.01", 42, 2),
TALER_TESTING_cmd_check_bank_empty ("check-empty-again"),
/* Test rejection of bogus wire transfers */
CMD_TRANSFER_TO_EXCHANGE_SUBJECT ("bogus-subject",
"EUR:1.01",
"not a reserve public key"),
CMD_EXEC_WIREWATCH ("wirewatch-7"),
TALER_TESTING_cmd_check_bank_empty ("check-empty-from-reject"),
/* ************** End of /payback API ************* */
/**
* End the suite. Fixme: better to have a label for this
* too, as it shows a "(null)" token on logs.
*/
TALER_TESTING_cmd_end ()
};
@ -105,7 +740,6 @@ run (void *cls,
fakebank_url);
}
int
main (int argc,
char * const *argv)
@ -116,9 +750,15 @@ main (int argc,
GNUNET_log_setup ("test-exchange-api-new",
"INFO",
NULL);
if (NULL == (fakebank_url = TALER_TESTING_prepare_fakebank (CONFIG_FILE)))
if (NULL == (fakebank_url
/* Check fakebank port is available and config cares
* about bank url. */
= TALER_TESTING_prepare_fakebank (CONFIG_FILE)))
return 77;
TALER_TESTING_cleanup_files (CONFIG_FILE);
/* @helpers. Run keyup, create tables, ... Note: it
* fetches the port number from config in order to see
* if it's available. */
switch (TALER_TESTING_prepare_exchange (CONFIG_FILE))
{
case GNUNET_SYSERR:
@ -128,10 +768,15 @@ main (int argc,
return 77;
case GNUNET_OK:
if (GNUNET_OK !=
/* Set up event loop and reschedule context, plus
* start/stop the exchange. It calls TALER_TESTING_setup
* which creates the 'is' object.
*/
TALER_TESTING_setup_with_exchange (&run,
NULL,
CONFIG_FILE))
return 1;
break;
default:
GNUNET_break (0);
return 1;

View File

@ -0,0 +1,268 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_cmd_bank_check.c
* @brief command to check if a particular wire transfer took
* place.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_testing_lib.h"
#include "taler_fakebank_lib.h"
struct BankCheckState
{
/**
* Exchange base URL (Fixme: why?)
*/
const char *exchange_base_url;
/**
* Expected transferred amount.
*/
const char *amount;
/**
* Expected account number that gave money
*/
unsigned int debit_account;
/**
* Expected account number that received money
*/
unsigned int credit_account;
/**
* Wire transfer subject (set by fakebank-lib).
*/
char *subject;
/**
* Binary form of the transfer subject. Some commands expect
* it - via appropriate traits - to be in binary form.
*/
struct TALER_WireTransferIdentifierRawP wtid;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
};
/**
* Run the command.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command to execute, a /wire one.
* @param is the interpreter state.
*/
void
check_bank_transfer_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct BankCheckState *bcs = cls;
struct TALER_Amount amount;
if (GNUNET_OK !=
TALER_string_to_amount (bcs->amount,
&amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at %u\n",
bcs->amount,
is->ip);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK !=
TALER_FAKEBANK_check (is->fakebank,
&amount,
bcs->debit_account,
bcs->credit_account,
bcs->exchange_base_url,
&bcs->subject))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
TALER_TESTING_interpreter_next (is);
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
check_bank_transfer_cleanup
(void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct BankCheckState *bcs = cls;
GNUNET_free_non_null (bcs->subject);
GNUNET_free (bcs);
}
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
static int
check_bank_transfer_traits (void *cls,
void **ret,
const char *trait,
unsigned int index)
{
struct BankCheckState *bcs = cls;
GNUNET_assert (GNUNET_OK ==
GNUNET_STRINGS_string_to_data
(bcs->subject,
strlen (bcs->subject),
&bcs->wtid,
sizeof (struct TALER_WireTransferIdentifierRawP)));
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_transfer_subject (0, bcs->subject),
TALER_TESTING_make_trait_wtid (0, &bcs->wtid),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
/**
* Command to check whether a particular wire transfer has been
* made or not.
*
* @param label the command label
* @param exchange_base_url base url of the exchange (Fixme: why?)
* @param amount the amount expected to be transferred
* @param debit_account the account that gave money
* @param credit_account the account that received money
*
* @return the command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_check_bank_transfer
(const char *label,
const char *exchange_base_url,
const char *amount,
unsigned int debit_account,
unsigned int credit_account)
{
struct BankCheckState *bcs;
struct TALER_TESTING_Command cmd;
bcs = GNUNET_new (struct BankCheckState);
bcs->exchange_base_url = exchange_base_url;
bcs->amount = amount;
bcs->debit_account = debit_account;
bcs->credit_account = credit_account;
cmd.label = label;
cmd.cls = bcs;
cmd.run = &check_bank_transfer_run;
cmd.cleanup = &check_bank_transfer_cleanup;
// traits?
cmd.traits = &check_bank_transfer_traits;
return cmd;
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
check_bank_empty_cleanup
(void *cls,
const struct TALER_TESTING_Command *cmd)
{
return;
}
/**
* Run the command.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command to execute, a /wire one.
* @param is the interpreter state.
*/
void
check_bank_empty_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
if (GNUNET_OK != TALER_FAKEBANK_check_empty (is->fakebank))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
TALER_TESTING_interpreter_next (is);
}
/**
* Check bank's balance is zero.
*
* @param credit_account the account that received money
*
* @return the command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_check_bank_empty (const char *label)
{
struct TALER_TESTING_Command cmd;
cmd.label = label;
cmd.run = &check_bank_empty_run;
cmd.cleanup = &check_bank_empty_cleanup;
return cmd;
}

View File

@ -0,0 +1,153 @@
/*
This file is part of TALER
(C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_cmd_check_keys.c
* @brief Implementation of "check keys" test command.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_testing_lib.h"
struct CheckKeysState
{
/**
* FIXME
*/
unsigned int generation;
/**
* FIXME
*/
unsigned int num_denom_keys;
/**
* Connection to the exchange.
*/
struct TALER_EXCHANGE_Handle *exchange;
};
/**
* Run the command.
*
* @param cls closure, typically a #struct SignalState.
* @param cmd the command to execute, a /wire one.
* @param is the interpreter state.
*/
void
check_keys_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct CheckKeysState *cks = cls;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"cmd `%s', key generation: %d\n",
cmd->label, is->key_generation);
if (is->key_generation < cks->generation)
{
/* Go back to waiting for /keys signal! */
is->working = GNUNET_NO;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Triggering /keys dl, cmd `%s'\n",
cmd->label);
GNUNET_break (0 == TALER_EXCHANGE_check_keys_current
(cks->exchange, GNUNET_YES).abs_value_us);
return;
}
if (is->key_generation > cks->generation)
{
/* We got /keys too often, strange. Fatal. May theoretically
happen if somehow we were really unlucky and /keys expired
"naturally", but obviously with a sane configuration this
should also not be. */
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
/* /keys was updated, let's check they were OK! */
if (cks->num_denom_keys != is->keys->num_denom_keys)
{
/* Did not get the expected number of denomination keys! */
GNUNET_break (0);
fprintf (stderr, "Got %u keys in step %s\n",
is->keys->num_denom_keys, cmd->label);
TALER_TESTING_interpreter_fail (is);
return;
}
TALER_TESTING_interpreter_next (is);
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct SignalState.
* @param cmd the command which is being cleaned up.
*/
void
check_keys_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct CheckKeysState *cks = cls;
GNUNET_free (cks);
}
/**
* Make a "check keys" command.
*
* @param label command label
* @param generation FIXME
* @param num_denom_keys FIXME
*
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_check_keys
(const char *label,
unsigned int generation,
unsigned int num_denom_keys,
struct TALER_EXCHANGE_Handle *exchange)
{
struct CheckKeysState *cks;
struct TALER_TESTING_Command cmd;
cks = GNUNET_new (struct CheckKeysState);
cks->generation = generation;
cks->num_denom_keys = num_denom_keys;
cks->exchange = exchange;
cmd.cls = cks;
cmd.label = label;
cmd.run = &check_keys_run;
cmd.cleanup = &check_keys_cleanup;
return cmd;
}

View File

@ -0,0 +1,448 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_cmd_deposit.c
* @brief command for testing /deposit.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_testing_lib.h"
#include "taler_signatures.h"
struct DepositState
{
/**
* Amount to deposit.
*/
const char *amount;
/**
* Reference to any command that is able to provide a coin.
*/
const char *coin_reference;
/**
* If this @e coin_ref refers to an operation that generated
* an array of coins, this value determines which coin to pick.
*/
unsigned int coin_index;
/**
* JSON string describing the merchant's "wire details".
*/
char *wire_details;
/**
* JSON string describing what a proposal is about.
*/
const char *contract_terms;
/**
* 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.
*/
struct TALER_MerchantPrivateKeyP merchant_priv;
/**
* Deposit handle while operation is running.
*/
struct TALER_EXCHANGE_DepositHandle *dh;
/**
* Expected HTTP response code.
*/
unsigned int expected_response_code;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
/**
* Exchange connection.
*/
struct TALER_EXCHANGE_Handle *exchange;
};
/**
* 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 exchange's reply is bogus
* (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param exchange_pub public key the exchange used for signing
* @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,
enum TALER_ErrorCode ec,
const struct TALER_ExchangePublicKeyP *exchange_pub,
const json_t *obj)
{
struct DepositState *ds = cls;
ds->dh = NULL;
if (ds->expected_response_code != http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s\n",
http_status,
ds->is->commands[ds->is->ip].label);
json_dumpf (obj, stderr, 0);
TALER_TESTING_interpreter_fail (ds->is);
return;
}
TALER_TESTING_interpreter_next (ds->is);
}
/**
* Run the command.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command to execute, a /wire one.
* @param i the interpreter state.
*/
void
deposit_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct DepositState *ds = cls;
const struct TALER_TESTING_Command *coin_cmd;
struct TALER_TESTING_Command *this_cmd;
struct TALER_CoinSpendPrivateKeyP *coin_priv;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
struct TALER_DenominationSignature *denom_pub_sig;
struct TALER_CoinSpendSignatureP coin_sig;
struct GNUNET_TIME_Absolute refund_deadline;
struct GNUNET_TIME_Absolute wire_deadline;
struct GNUNET_TIME_Absolute timestamp;
struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv;
struct TALER_MerchantPublicKeyP merchant_pub;
struct GNUNET_HashCode h_contract_terms;
json_t *contract_terms;
json_t *wire;
struct TALER_Amount amount;
ds->is = is;
this_cmd = &is->commands[is->ip];
GNUNET_assert (ds->coin_reference);
coin_cmd = TALER_TESTING_interpreter_lookup_command
(is,
ds->coin_reference);
if (NULL == coin_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
/* Fixme: do prefer "interpreter fail" over assertions,
* as the former takes care of shutting down processes too */
GNUNET_assert (NULL != coin_cmd);
GNUNET_assert (GNUNET_OK
== TALER_TESTING_get_trait_coin_priv (coin_cmd,
ds->coin_index,
&coin_priv));
GNUNET_assert (GNUNET_OK
== TALER_TESTING_get_trait_denom_pub (coin_cmd,
ds->coin_index,
&denom_pub));
GNUNET_assert (GNUNET_OK
== TALER_TESTING_get_trait_denom_sig (coin_cmd,
ds->coin_index,
&denom_pub_sig));
if (GNUNET_OK !=
TALER_string_to_amount (ds->amount,
&amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at '%u/%s'\n",
ds->amount, is->ip, this_cmd->label);
TALER_TESTING_interpreter_fail (is);
return;
}
contract_terms = json_loads (ds->contract_terms,
JSON_REJECT_DUPLICATES,
NULL);
if (NULL == contract_terms)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse proposal data `%s' at %u/%s\n",
ds->contract_terms, is->ip, this_cmd->label);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_assert (GNUNET_OK ==
TALER_JSON_hash (contract_terms,
&h_contract_terms));
json_decref (contract_terms);
wire = json_loads (ds->wire_details,
JSON_REJECT_DUPLICATES,
NULL);
if (NULL == wire)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse wire details `%s' at %u/%s\n",
ds->wire_details,
is->ip,
this_cmd->label);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
&coin_pub.eddsa_pub);
merchant_priv = GNUNET_CRYPTO_eddsa_key_create ();
ds->merchant_priv.eddsa_priv = *merchant_priv;
GNUNET_free (merchant_priv);
if (0 != ds->refund_deadline.rel_value_us)
{
refund_deadline = GNUNET_TIME_relative_to_absolute
(ds->refund_deadline);
wire_deadline = GNUNET_TIME_relative_to_absolute
(GNUNET_TIME_relative_multiply
(ds->refund_deadline, 2));
}
else
{
refund_deadline = GNUNET_TIME_UNIT_ZERO_ABS;
wire_deadline = GNUNET_TIME_relative_to_absolute
(GNUNET_TIME_UNIT_ZERO);
}
GNUNET_CRYPTO_eddsa_key_get_public
(&ds->merchant_priv.eddsa_priv,
&merchant_pub.eddsa_pub);
timestamp = GNUNET_TIME_absolute_get ();
GNUNET_TIME_round_abs (&timestamp);
GNUNET_TIME_round_abs (&refund_deadline);
GNUNET_TIME_round_abs (&wire_deadline);
{
struct TALER_DepositRequestPS dr;
memset (&dr, 0, sizeof (dr));
dr.purpose.size = htonl
(sizeof (struct TALER_DepositRequestPS));
dr.purpose.purpose = htonl
(TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
dr.h_contract_terms = h_contract_terms;
GNUNET_assert (GNUNET_OK == TALER_JSON_hash
(wire, &dr.h_wire));
dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
dr.refund_deadline = GNUNET_TIME_absolute_hton
(refund_deadline);
TALER_amount_hton (&dr.amount_with_fee, &amount);
TALER_amount_hton
(&dr.deposit_fee, &denom_pub->fee_deposit);
dr.merchant = merchant_pub;
dr.coin_pub = coin_pub;
GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign
(&coin_priv->eddsa_priv,
&dr.purpose,
&coin_sig.eddsa_signature));
}
ds->dh = TALER_EXCHANGE_deposit
(ds->exchange,
&amount,
wire_deadline,
wire,
&h_contract_terms,
&coin_pub,
denom_pub_sig,
&denom_pub->key,
timestamp,
&merchant_pub,
refund_deadline,
&coin_sig,
&deposit_cb,
ds);
if (NULL == ds->dh)
{
GNUNET_break (0);
json_decref (wire);
TALER_TESTING_interpreter_fail (is);
return;
}
json_decref (wire);
return;
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
deposit_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct DepositState *ds = cls;
if (NULL != ds->dh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
ds->is->ip,
cmd->label);
TALER_EXCHANGE_deposit_cancel (ds->dh);
ds->dh = NULL;
}
GNUNET_free (ds->wire_details);
GNUNET_free (ds);
}
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
static int
deposit_traits (void *cls,
void **ret,
const char *trait,
unsigned int index)
{
struct DepositState *ds = cls;
const struct TALER_TESTING_Command *coin_cmd;
/* Will point to coin cmd internals. */
struct TALER_CoinSpendPrivateKeyP *coin_spent_priv;
coin_cmd = TALER_TESTING_interpreter_lookup_command
(ds->is, ds->coin_reference);
if (NULL == coin_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is);
return GNUNET_NO;
}
if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
(coin_cmd, ds->coin_index, &coin_spent_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is);
return GNUNET_NO;
}
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_coin_priv (0, coin_spent_priv),
TALER_TESTING_make_trait_wire_details (0, ds->wire_details),
TALER_TESTING_make_trait_contract_terms (0, ds->contract_terms),
TALER_TESTING_make_trait_peer_key
(0, &ds->merchant_priv.eddsa_priv),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
/**
* Create a deposit command.
*
* @param label command label
* @param exchange exchange connection
* @param coin_reference reference to any operation that can
* provide a coin
* @param coin_index if @a withdraw_reference offers an array of
* coins, this parameter selects which one in that array.
* This value is currently ignored, as only one-coin
* withdrawals are implemented.
* @param wire_details bank details of the merchant performing the
* deposit
* @param contract_terms contract terms to be signed over by the
* coin
* @param refund_deadline refund deadline, zero means 'no refunds'
* @param amount how much is going to be deposited
* @param expected_response_code which HTTP status code we expect
* in the response
*
* @return the deposit command to be run by the interpreter
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_deposit
(const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *coin_reference,
unsigned int coin_index,
char *wire_details,
const char *contract_terms,
struct GNUNET_TIME_Relative refund_deadline,
const char *amount,
unsigned int expected_response_code)
{
struct TALER_TESTING_Command cmd;
struct DepositState *ds;
ds = GNUNET_new (struct DepositState);
ds->exchange = exchange;
ds->coin_reference = coin_reference;
ds->coin_index = coin_index;
ds->wire_details = wire_details;
ds->contract_terms = contract_terms;
ds->refund_deadline = refund_deadline;
ds->amount = amount;
ds->expected_response_code = expected_response_code;
cmd.cls = ds;
cmd.label = label;
cmd.run = &deposit_run;
cmd.cleanup = &deposit_cleanup;
cmd.traits = &deposit_traits;
return cmd;
}

View File

@ -0,0 +1,162 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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,
see <http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_cmd_exec_aggregator.c
* @brief run the taler-exchange-aggregator command
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
struct AggregatorState
{
/**
* Process for the aggregator.
*/
struct GNUNET_OS_Process *aggregator_proc;
/**
* Which configuration file should we pass to the process?
*/
const char *config_filename;
};
/**
* Runs the command. Note that upon return, the interpreter
* will not automatically run the next command, as the command
* may continue asynchronously in other scheduler tasks. Thus,
* the command must ensure to eventually call
* #TALER_TESTING_interpreter_next() or
* #TALER_TESTING_interpreter_fail().
*
* @param is interpreter state
*/
static void
aggregator_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct AggregatorState *as = cls;
as->aggregator_proc
= GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-aggregator",
"taler-exchange-aggregator",
"-c", as->config_filename,
"-t", /* exit when done */
NULL);
if (NULL == as->aggregator_proc)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
TALER_TESTING_wait_for_sigchld (is);
}
/**
* Clean up after the command. Run during forced termination
* (CTRL-C) or test failure or test success.
*
* @param cls closure
*/
static void
aggregator_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct AggregatorState *as = cls;
if (NULL != as->aggregator_proc)
{
GNUNET_break (0 ==
GNUNET_OS_process_kill (as->aggregator_proc,
SIGKILL));
GNUNET_OS_process_wait (as->aggregator_proc);
GNUNET_OS_process_destroy (as->aggregator_proc);
as->aggregator_proc = NULL;
}
GNUNET_free (as);
}
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
static int
aggregator_traits (void *cls,
void **ret,
const char *trait,
unsigned int index)
{
struct AggregatorState *as = cls;
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_process (0, &as->aggregator_proc),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
/**
* Execute taler-exchange-wirewatch process.
*
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_exec_aggregator (const char *label,
const char *config_filename)
{
struct TALER_TESTING_Command cmd;
struct AggregatorState *as;
as = GNUNET_new (struct AggregatorState);
as->config_filename = config_filename;
cmd.cls = as;
cmd.label = label;
cmd.run = &aggregator_run;
cmd.cleanup = &aggregator_cleanup;
cmd.traits = &aggregator_traits;
return cmd;
}
/* end of testing_api_cmd_exec_aggregator.c */

View File

@ -0,0 +1,221 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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,
see <http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_cmd_exec_auditor-sign.c
* @brief run the taler-exchange-aggregator command
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
struct AuditorSignState
{
/**
* Process for the "auditor sign" command.
*/
struct GNUNET_OS_Process *auditor_sign_proc;
/**
* Which configuration file should we pass to the process?
*/
const char *config_filename;
};
/**
* Runs the command. Note that upon return, the interpreter
* will not automatically run the next command, as the command
* may continue asynchronously in other scheduler tasks. Thus,
* the command must ensure to eventually call
* #TALER_TESTING_interpreter_next() or
* #TALER_TESTING_interpreter_fail().
*
* @param is interpreter state
*/
static void
auditor_sign_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct AuditorSignState *ass = cls;
struct GNUNET_CONFIGURATION_Handle *cfg;
char *test_home_dir;
char *signed_keys_out;
char *exchange_master_pub;
cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK != GNUNET_CONFIGURATION_load
(cfg, ass->config_filename))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"paths",
"TALER_TEST_HOME",
&test_home_dir))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"paths",
"TALER_TEST_HOME");
GNUNET_CONFIGURATION_destroy (cfg);
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_asprintf (&signed_keys_out,
"%s/.local/share/taler/auditors/auditor.out",
test_home_dir);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"exchange",
"MASTER_PUBLIC_KEY",
&exchange_master_pub))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange",
"MASTER_PUBLIC_KEY");
GNUNET_CONFIGURATION_destroy (cfg);
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_CONFIGURATION_destroy (cfg);
ass->auditor_sign_proc = GNUNET_OS_start_process
(GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-auditor-sign",
"taler-auditor-sign",
"-c", ass->config_filename,
"-u", "http://auditor/",
"-m", exchange_master_pub,
"-r", "auditor.in",
"-o", signed_keys_out,
NULL);
if (NULL == ass->auditor_sign_proc)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
TALER_TESTING_wait_for_sigchld (is);
}
/**
* Clean up after the command. Run during forced termination
* (CTRL-C) or test failure or test success.
*
* @param cls closure
*/
static void
auditor_sign_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct AuditorSignState *ass = cls;
if (NULL != ass->auditor_sign_proc)
{
GNUNET_break (0 == GNUNET_OS_process_kill
(ass->auditor_sign_proc, SIGKILL));
GNUNET_OS_process_wait (ass->auditor_sign_proc);
GNUNET_OS_process_destroy (ass->auditor_sign_proc);
ass->auditor_sign_proc = NULL;
}
GNUNET_free (ass);
}
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
static int
auditor_sign_traits (void *cls,
void **ret,
const char *trait,
unsigned int index)
{
struct AuditorSignState *ass = cls;
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_process (0, &ass->auditor_sign_proc),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
/**
* Execute taler-auditor-sign process.
*
* @param label command label
* @param config_filename configuration filename
*
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_exec_auditor_sign (const char *label,
const char *config_filename)
{
struct TALER_TESTING_Command cmd;
struct AuditorSignState *ass;
ass = GNUNET_new (struct AuditorSignState);
ass->config_filename = config_filename;
cmd.cls = ass;
cmd.label = label;
cmd.run = &auditor_sign_run;
cmd.cleanup = &auditor_sign_cleanup;
cmd.traits = &auditor_sign_traits;
return cmd;
}
/* end of testing_api_cmd_exec_auditor-sign.c */

View File

@ -0,0 +1,168 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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,
see <http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_cmd_exec_keyup.c
* @brief run the taler-exchange-keyup command
* @author Marcello Stanisci
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
struct KeyupState
{
/**
* Process for the "keyup" command.
*/
struct GNUNET_OS_Process *keyup_proc;
/**
* Which configuration file should we pass to the process?
*/
const char *config_filename;
};
/**
* Runs the command. Note that upon return, the interpreter
* will not automatically run the next command, as the command
* may continue asynchronously in other scheduler tasks. Thus,
* the command must ensure to eventually call
* #TALER_TESTING_interpreter_next() or
* #TALER_TESTING_interpreter_fail().
*
* @param is interpreter state
*/
static void
keyup_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct KeyupState *ks = cls;
ks->keyup_proc = GNUNET_OS_start_process
(GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-keyup",
"taler-exchange-keyup",
"-c", ks->config_filename,
"-o", "auditor.in",
NULL);
if (NULL == ks->keyup_proc)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
TALER_TESTING_wait_for_sigchld (is);
}
/**
* Clean up after the command. Run during forced termination
* (CTRL-C) or test failure or test success.
*
* @param cls closure
*/
static void
keyup_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct KeyupState *ks = cls;
if (NULL != ks->keyup_proc)
{
GNUNET_break (0 ==
GNUNET_OS_process_kill (ks->keyup_proc,
SIGKILL));
GNUNET_OS_process_wait (ks->keyup_proc);
GNUNET_OS_process_destroy (ks->keyup_proc);
ks->keyup_proc = NULL;
}
GNUNET_free (ks);
}
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
static int
keyup_traits (void *cls,
void **ret,
const char *trait,
unsigned int index)
{
struct KeyupState *ks = cls;
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_process (0, &ks->keyup_proc),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
/**
* Execute taler-exchange-keyup process.
*
* @param label command label
* @param config_filename configuration filename
*
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_exec_keyup (const char *label,
const char *config_filename)
{
struct TALER_TESTING_Command cmd;
struct KeyupState *ks;
ks = GNUNET_new (struct KeyupState);
ks->config_filename = config_filename;
cmd.cls = ks;
cmd.label = label;
cmd.run = &keyup_run;
cmd.cleanup = &keyup_cleanup;
cmd.traits = &keyup_traits;
return cmd;
}
/* end of testing_api_cmd_exec_keyup.c */

View File

@ -122,11 +122,11 @@ static int
wirewatch_traits (void *cls,
void **ret,
const char *trait,
const char *selector)
unsigned int index)
{
struct WirewatchState *ws = cls;
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_process (NULL,
TALER_TESTING_make_trait_process (0,
&ws->wirewatch_proc),
TALER_TESTING_trait_end ()
};
@ -134,7 +134,7 @@ wirewatch_traits (void *cls,
return TALER_TESTING_get_trait (traits,
ret,
trait,
selector);
index);
}

View File

@ -2,18 +2,21 @@
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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 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.
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, see
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_cmd_fakebank_transfer.c
* @brief implementation of a fakebank wire transfer command
@ -104,14 +107,17 @@ struct FakebankTransferState
/**
* Function called upon completion of our /admin/add/incoming request.
* Function called upon completion of our /admin/add/incoming
* 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 exchange's reply is bogus (fails to follow the protocol)
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for
* successful status request; 0 if the exchange's reply is
* bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param serial_id unique ID of the wire transfer
* @param full_response full response from the exchange (for logging, in case of errors)
* @param full_response full response from the exchange (for
* logging, in case of errors)
*/
static void
add_incoming_cb (void *cls,
@ -167,8 +173,8 @@ fakebank_transfer_run (void *cls,
const struct TALER_TESTING_Command *ref;
struct TALER_ReservePrivateKeyP *reserve_priv;
ref = TALER_TESTING_interpreter_lookup_command (is,
fts->reserve_reference);
ref = TALER_TESTING_interpreter_lookup_command
(is, fts->reserve_reference);
if (NULL == ref)
{
GNUNET_break (0);
@ -177,7 +183,7 @@ fakebank_transfer_run (void *cls,
}
if (GNUNET_OK !=
TALER_TESTING_get_trait_reserve_priv (ref,
NULL,
0,
&reserve_priv))
{
GNUNET_break (0);
@ -193,27 +199,27 @@ fakebank_transfer_run (void *cls,
fts->reserve_priv.eddsa_priv = *priv;
GNUNET_free (priv);
}
GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv,
&reserve_pub.eddsa_pub);
subject = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub,
sizeof (reserve_pub));
GNUNET_CRYPTO_eddsa_key_get_public
(&fts->reserve_priv.eddsa_priv, &reserve_pub.eddsa_pub);
subject = GNUNET_STRINGS_data_to_string_alloc
(&reserve_pub, sizeof (reserve_pub));
}
auth.method = TALER_BANK_AUTH_BASIC;
auth.details.basic.username = (char *) fts->auth_username;
auth.details.basic.password = (char *) fts->auth_password;
fts->is = is;
fts->aih
= TALER_BANK_admin_add_incoming (TALER_TESTING_interpreter_get_context (is),
fts->bank_url,
&auth,
"https://exchange.com/", /* exchange URL: FIXME */
subject,
&fts->amount,
fts->debit_account_no,
fts->credit_account_no,
&add_incoming_cb,
fts);
fts->aih = TALER_BANK_admin_add_incoming
(TALER_TESTING_interpreter_get_context (is),
fts->bank_url,
&auth,
"https://exchange.com/", /* exchange URL: FIXME */
subject,
&fts->amount,
fts->debit_account_no,
fts->credit_account_no,
&add_incoming_cb,
fts);
GNUNET_free (subject);
if (NULL == fts->aih)
{
@ -263,11 +269,11 @@ static int
fakebank_transfer_traits (void *cls,
void **ret,
const char *trait,
const char *selector)
unsigned int index)
{
struct FakebankTransferState *fts = cls;
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_reserve_priv (NULL,
TALER_TESTING_make_trait_reserve_priv (0,
&fts->reserve_priv),
TALER_TESTING_trait_end ()
};
@ -275,12 +281,13 @@ fakebank_transfer_traits (void *cls,
if (NULL != fts->subject)
{
GNUNET_break (0);
return GNUNET_SYSERR; /* we do NOT create a reserve private key */
/* we do NOT create a reserve private key */
return GNUNET_SYSERR;
}
return TALER_TESTING_get_trait (traits,
ret,
trait,
selector);
index);
}
@ -330,14 +337,15 @@ TALER_TESTING_cmd_fakebank_transfer (const char *label,
*
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,
const char *amount,
const char *bank_url,
uint64_t debit_account_no,
uint64_t credit_account_no,
const char *auth_username,
const char *auth_password,
const char *subject)
TALER_TESTING_cmd_fakebank_transfer_with_subject
(const char *label,
const char *amount,
const char *bank_url,
uint64_t debit_account_no,
uint64_t credit_account_no,
const char *auth_username,
const char *auth_password,
const char *subject)
{
struct TALER_TESTING_Command cmd;
struct FakebankTransferState *fts;
@ -373,14 +381,15 @@ TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,
*
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label,
const char *amount,
const char *bank_url,
uint64_t debit_account_no,
uint64_t credit_account_no,
const char *auth_username,
const char *auth_password,
const char *ref)
TALER_TESTING_cmd_fakebank_transfer_with_ref
(const char *label,
const char *amount,
const char *bank_url,
uint64_t debit_account_no,
uint64_t credit_account_no,
const char *auth_username,
const char *auth_password,
const char *ref)
{
struct TALER_TESTING_Command cmd;
struct FakebankTransferState *fts;

View File

@ -0,0 +1,491 @@
/*
This file is part of TALER
Copyright (C) 2014-2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange/testing_api_cmd_payback.c
* @brief Implement the /revoke and /payback test commands.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_testing_lib.h"
struct RevokeState
{
/**
* Expected HTTP status code.
*/
unsigned int expected_response_code;
/**
* Command that offers a denomination to revoke.
*/
const char *coin_reference;
/**
* The interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
/**
* The revoke process handle.
*/
struct GNUNET_OS_Process *revoke_proc;
/**
* Configuration filename.
*/
const char *config_filename;
/**
* Encoding of the denomination (to revoke) public key hash.
*/
char *dhks;
};
struct PaybackState
{
/**
* Expected HTTP status code.
*/
unsigned int expected_response_code;
/**
* Command that offers a reserve private key plus a
* coin to be paid back.
*/
const char *coin_reference;
/**
* The interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
/**
* Amount expected to be paid back.
*/
const char *amount;
/**
* Connection to the exchange.
*/
struct TALER_EXCHANGE_Handle *exchange;
/**
* Handle to the ongoing operation.
*/
struct TALER_EXCHANGE_PaybackHandle *ph;
};
/**
* Check the result of the payback request.
*
* @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for
* successful status request; 0 if the exchange's reply is
* bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param amount amount the exchange will wire back for this coin
* @param timestamp what time did the exchange receive the
* /payback request
* @param reserve_pub public key of the reserve receiving the
* payback
* @param full_response full response from the exchange (for
* logging, in case of errors)
*/
static void
payback_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const struct TALER_Amount *amount,
struct GNUNET_TIME_Absolute timestamp,
const struct TALER_ReservePublicKeyP *reserve_pub,
const json_t *full_response)
{
struct PaybackState *ps = cls;
struct TALER_TESTING_Interpreter *is = ps->is;
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
const struct TALER_TESTING_Command *reserve_cmd;
struct TALER_ReservePrivateKeyP *reserve_priv;
struct TALER_ReservePublicKeyP rp;
struct TALER_Amount expected_amount;
ps->ph = NULL;
if (ps->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 (full_response, stderr, 0);
fprintf (stderr, "\n");
TALER_TESTING_interpreter_fail (is);
return;
}
reserve_cmd = TALER_TESTING_interpreter_lookup_command
(is, ps->coin_reference);
if (NULL == reserve_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv
(reserve_cmd, 0, &reserve_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
&rp.eddsa_pub);
switch (http_status)
{
case MHD_HTTP_OK:
if (GNUNET_OK != TALER_string_to_amount
(ps->amount, &expected_amount))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (0 != TALER_amount_cmp (amount, &expected_amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Total amount missmatch to command %s\n",
cmd->label);
json_dumpf (full_response, stderr, 0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (0 != memcmp (reserve_pub, &rp,
sizeof (struct TALER_ReservePublicKeyP)))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unmanaged HTTP status code.\n");
break;
}
TALER_TESTING_interpreter_next (is);
}
/**
* Run the command.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command to execute, a /wire one.
* @param is the interpreter state.
*/
void
payback_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct PaybackState *ps = cls;
const struct TALER_TESTING_Command *coin_cmd;
struct TALER_CoinSpendPrivateKeyP *coin_priv;
struct TALER_DenominationBlindingKeyP *blinding_key;
struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
struct TALER_DenominationSignature *coin_sig;
struct TALER_PlanchetSecretsP planchet;
ps->is = is;
ps->exchange = is->exchange;
coin_cmd = TALER_TESTING_interpreter_lookup_command
(is, ps->coin_reference);
if (NULL == coin_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
(coin_cmd, 0, &coin_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_blinding_key
(coin_cmd, 0, &blinding_key))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
planchet.coin_priv = *coin_priv;
planchet.blinding_key = *blinding_key;
if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub
(coin_cmd, 0, &denom_pub))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig
(coin_cmd, 0, &coin_sig))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Trying to get '%s..' paid back\n",
TALER_B2S (&denom_pub->h_key));
ps->ph = TALER_EXCHANGE_payback (ps->exchange,
denom_pub,
coin_sig,
&planchet,
payback_cb,
ps);
GNUNET_assert (NULL != ps->ph);
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
revoke_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct RevokeState *rs = cls;
if (NULL != rs->revoke_proc)
{
GNUNET_break (0 == GNUNET_OS_process_kill
(rs->revoke_proc, SIGKILL));
GNUNET_OS_process_wait (rs->revoke_proc);
GNUNET_OS_process_destroy (rs->revoke_proc);
rs->revoke_proc = NULL;
}
GNUNET_free (rs->dhks);
GNUNET_free (rs);
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
payback_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct PaybackState *ps = cls;
if (NULL != ps->ph)
{
TALER_EXCHANGE_payback_cancel (ps->ph);
ps->ph = NULL;
}
GNUNET_free (ps);
}
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
static int
revoke_traits (void *cls,
void **ret,
const char *trait,
unsigned int index)
{
struct RevokeState *rs = cls;
struct TALER_TESTING_Trait traits[] = {
/* Needed by the handler which waits the proc'
* death and calls the next command */
TALER_TESTING_make_trait_process (0, &rs->revoke_proc),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
/**
* Run the command.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command to execute, a /wire one.
* @param is the interpreter state.
*/
void
revoke_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct RevokeState *rs = cls;
const struct TALER_TESTING_Command *coin_cmd;
struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
rs->is = is;
/* Get denom pub from trait */
coin_cmd = TALER_TESTING_interpreter_lookup_command
(is, rs->coin_reference);
if (NULL == coin_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_denom_pub
(coin_cmd, 0, &denom_pub));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Trying to revoke denom '%s..'\n",
TALER_B2S (&denom_pub->h_key));
rs->dhks = GNUNET_STRINGS_data_to_string_alloc
(&denom_pub->h_key, sizeof (struct GNUNET_HashCode));
rs->revoke_proc = GNUNET_OS_start_process
(GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-keyup",
"taler-exchange-keyup",
"-c", rs->config_filename,
"-r", rs->dhks,
NULL);
if (NULL == rs->revoke_proc)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Revoke is ongoing..\n");
is->reload_keys = GNUNET_OK;
TALER_TESTING_wait_for_sigchld (is);
}
/**
* Make a /payback command.
*
* @param label the command label
* @param expected_response_code expected HTTP status code
* @param coin_reference reference to any command which
* offers a reserve private key
* @param amount denomination to pay back.
*
* @return a /revoke command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_payback (const char *label,
unsigned int expected_response_code,
const char *coin_reference,
const char *amount)
{
struct PaybackState *ps;
struct TALER_TESTING_Command cmd;
ps = GNUNET_new (struct PaybackState);
ps->expected_response_code = expected_response_code;
ps->coin_reference = coin_reference;
ps->amount = amount;
cmd.cls = ps;
cmd.label = label;
cmd.run = &payback_run;
cmd.cleanup = &payback_cleanup;
return cmd;
}
/**
* Make a /revoke command.
*
* @param label the command label
* @param expected_response_code expected HTTP status code
* @param coin_reference reference to any command which offers
* a coin trait
* @param config_filename configuration file name.
*
* @return a /revoke command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_revoke (const char *label,
unsigned int expected_response_code,
const char *coin_reference,
const char *config_filename)
{
struct RevokeState *rs;
struct TALER_TESTING_Command cmd;
rs = GNUNET_new (struct RevokeState);
rs->expected_response_code = expected_response_code;
rs->coin_reference = coin_reference;
rs->config_filename = config_filename;
cmd.cls = rs;
cmd.label = label;
cmd.run = &revoke_run;
cmd.cleanup = &revoke_cleanup;
cmd.traits = &revoke_traits;
return cmd;
}

View File

@ -0,0 +1,993 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_cmd_refresh.c
* @brief commands for testing all "refresh" features.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_testing_lib.h"
#include "taler_signatures.h"
/**
* Structure specifying details about a coin to be melted.
* Used in a NULL-terminated array as part of command
* specification.
*/
struct MeltDetails
{
/**
* Amount to melt (including fee).
*/
const char *amount;
/**
* Reference to reserve_withdraw operations for coin to
* be used for the /refresh/melt operation.
*/
const char *coin_reference;
};
/**
* State for a "refresh melt" operation.
*/
struct RefreshMeltState
{
/**
* Fixme: figure out this data purpose.
*/
const char *amount;
/**
* Information about coins to be melted.
*/
struct MeltDetails melted_coin;
/**
* Data used in the refresh operation.
*/
char *refresh_data;
/**
* Number of bytes in @e refresh_data.
*/
size_t refresh_data_length;
/**
* Reference to a previous melt command.
*/
const char *melt_reference;
/**
* Melt handle while operation is running.
*/
struct TALER_EXCHANGE_RefreshMeltHandle *rmh;
/**
* Connection to the exchange.
*/
struct TALER_EXCHANGE_Handle *exchange;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
/**
* Expected HTTP response code.
*/
unsigned int expected_response_code;
/**
* Array of the public keys corresponding to
* the @e fresh_amounts, set by the interpreter.
*/
struct TALER_EXCHANGE_DenomPublicKey *fresh_pks;
/**
* Set by the interpreter (upon completion) to the
* noreveal index selected by the exchange.
*/
uint16_t noreveal_index;
};
/**
* State for a "refresh reveal" operation.
*/
struct RefreshRevealState
{
/**
* Link to a "refresh melt" command.
*/
const char *melt_reference;
/**
* Reveal handle while operation is running.
*/
struct TALER_EXCHANGE_RefreshRevealHandle *rrh;
/**
* Number of fresh coins withdrawn, set by the interpreter.
* Length of the @e fresh_coins array.
*/
unsigned int num_fresh_coins;
/**
* Information about coins withdrawn, set by the interpreter.
*/
struct FreshCoin *fresh_coins;
/**
* Connection to the exchange.
*/
struct TALER_EXCHANGE_Handle *exchange;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
/**
* Expected HTTP response code.
*/
unsigned int expected_response_code;
};
/**
* State for a "refresh link" operation.
*/
struct RefreshLinkState
{
/**
* Link to a "refresh reveal" command.
*/
const char *reveal_reference;
/**
* Link handle while operation is running.
*/
struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
/**
* Connection to the exchange.
*/
struct TALER_EXCHANGE_Handle *exchange;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
/**
* Expected HTTP response code.
*/
unsigned int expected_response_code;
};
/**
* Function called with the result of the /refresh/reveal operation.
*
* @param cls closure with the interpreter state
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for
* successful status request. 0 if the exchange's reply is
* bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param num_coins number of fresh coins created, length of the
* @a sigs and @a coin_privs arrays, 0 if the operation
* failed
* @param coin_privs array of @a num_coins private keys for the
* coins that were created, NULL on error
* @param sigs array of signature over @a num_coins coins, NULL on
* error
* @param full_response full response from the exchange (for
* logging, in case of errors)
*/
static void
reveal_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
unsigned int num_coins,
const struct TALER_CoinSpendPrivateKeyP *coin_privs,
const struct TALER_DenominationSignature *sigs,
const json_t *full_response)
{
struct RefreshRevealState *rrs = cls;
const struct TALER_TESTING_Command *melt_cmd;
rrs->rrh = NULL;
if (rrs->expected_response_code != http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s\n",
http_status,
rrs->is->commands[rrs->is->ip].label);
json_dumpf (full_response, stderr, 0);
TALER_TESTING_interpreter_fail (rrs->is);
return;
}
melt_cmd = TALER_TESTING_interpreter_lookup_command
(rrs->is, rrs->melt_reference);
if (NULL == melt_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rrs->is);
return;
}
rrs->num_fresh_coins = num_coins;
switch (http_status)
{
case MHD_HTTP_OK:
rrs->fresh_coins = GNUNET_new_array
(num_coins, struct FreshCoin);
struct TALER_EXCHANGE_DenomPublicKey *fresh_pks;
unsigned int i;
if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub
(melt_cmd, 0, &fresh_pks))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rrs->is);
return;
}
for (i=0; i<num_coins; i++)
{
struct FreshCoin *fc = &rrs->fresh_coins[i];
fc->pk = &fresh_pks[i];
fc->coin_priv = coin_privs[i];
fc->sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup
(sigs[i].rsa_signature);
}
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unknown HTTP status %d\n",
http_status);
}
TALER_TESTING_interpreter_next (rrs->is);
}
/**
* Run the command.
*
* @param cls closure.
* @param cmd the command to execute, a /wire one.
* @param is the interpreter state.
*/
void
refresh_reveal_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct RefreshRevealState *rrs = cls;
struct RefreshMeltState *rms;
const struct TALER_TESTING_Command *melt_cmd;
rrs->is = is;
melt_cmd = TALER_TESTING_interpreter_lookup_command
(is, rrs->melt_reference);
if (NULL == melt_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rrs->is);
return;
}
rms = melt_cmd->cls;
rrs->rrh = TALER_EXCHANGE_refresh_reveal
(rrs->exchange,
rms->refresh_data_length,
rms->refresh_data,
rms->noreveal_index,
&reveal_cb, rrs);
if (NULL == rrs->rrh)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
}
/**
* Cleanup the state.
*
* @param cls closure.
* @param cmd the command which is being cleaned up.
*/
void
refresh_reveal_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct RefreshRevealState *rrs = cls;
if (NULL != rrs->rrh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
rrs->is->ip,
cmd->label);
TALER_EXCHANGE_refresh_reveal_cancel (rrs->rrh);
rrs->rrh = NULL;
}
{ /* Not sure why block-ing this */
unsigned int j;
for (j=0; j < rrs->num_fresh_coins; j++)
GNUNET_CRYPTO_rsa_signature_free
(rrs->fresh_coins[j].sig.rsa_signature);
}
GNUNET_free_non_null (rrs->fresh_coins);
rrs->fresh_coins = NULL;
rrs->num_fresh_coins = 0;
}
/**
* Function called with the result of a /refresh/link operation.
*
* @param cls closure with the interpreter state
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for
* successful status request 0 if the exchange's reply is
* bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param num_coins number of fresh coins created, length of the
* @a sigs and @a coin_privs arrays, 0 if the operation
* failed
* @param coin_privs array of @a num_coins private keys for the
* coins that were created, NULL on error
* @param sigs array of signature over @a num_coins coins, NULL on
* error
* @param pubs array of public keys for the @a sigs, NULL on error
* @param full_response full response from the exchange (for
* logging, in case of errors)
*/
static void
link_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
unsigned int num_coins,
const struct TALER_CoinSpendPrivateKeyP *coin_privs,
const struct TALER_DenominationSignature *sigs,
const struct TALER_DenominationPublicKey *pubs,
const json_t *full_response)
{
struct RefreshLinkState *rls = cls;
const struct TALER_TESTING_Command *reveal_cmd;
struct TALER_TESTING_Command *link_cmd
= &rls->is->commands[rls->is->ip];
unsigned int found;
unsigned int *num_fresh_coins;
rls->rlh = NULL;
if (rls->expected_response_code != http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s\n",
http_status,
link_cmd->label);
json_dumpf (full_response, stderr, 0);
TALER_TESTING_interpreter_fail (rls->is);
return;
}
reveal_cmd = TALER_TESTING_interpreter_lookup_command
(rls->is, rls->reveal_reference);
if (NULL == reveal_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rls->is);
return;
}
switch (http_status)
{
case MHD_HTTP_OK:
/* check that number of coins returned matches */
if (GNUNET_OK != TALER_TESTING_get_trait_uint
(reveal_cmd, 0, &num_fresh_coins))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rls->is);
return;
}
if (num_coins != *num_fresh_coins)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected number of fresh coins: %d vs %d\n",
num_coins, *num_fresh_coins);
TALER_TESTING_interpreter_fail (rls->is);
return;
}
/* check that the coins match */
for (unsigned int i=0;i<num_coins;i++)
for (unsigned int j=i+1;j<num_coins;j++)
if (0 == memcmp
(&coin_privs[i], &coin_privs[j],
sizeof (struct TALER_CoinSpendPrivateKeyP)))
GNUNET_break (0);
/* Note: coins might be legitimately permutated in here... */
found = 0;
/* Will point to the pointer inside the cmd state. */
struct FreshCoin *fc = NULL;
if (GNUNET_OK != TALER_TESTING_get_trait_fresh_coins
(reveal_cmd, 0, &fc))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rls->is);
return;
}
for (unsigned int i=0;i<num_coins;i++)
for (unsigned int j=0;j<num_coins;j++)
{
if ( (0 == memcmp
(&coin_privs[i], &fc[i].coin_priv,
sizeof (struct TALER_CoinSpendPrivateKeyP))) &&
(0 == GNUNET_CRYPTO_rsa_signature_cmp
(fc[i].sig.rsa_signature,
sigs[i].rsa_signature)) &&
(0 == GNUNET_CRYPTO_rsa_public_key_cmp
(fc[i].pk->key.rsa_public_key,
pubs[i].rsa_public_key)) )
{
found++;
break;
}
}
if (found != num_coins)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Only %u/%u coins match expectations\n",
found, num_coins);
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rls->is);
return;
}
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unknown HTTP response code %u.\n",
http_status);
}
TALER_TESTING_interpreter_next (rls->is);
}
/**
* Run the command.
*
* @param cls closure
* @param cmd the command to execute, a /wire one.
* @param i the interpreter state.
*/
void
refresh_link_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct RefreshLinkState *rls = cls;
struct RefreshRevealState *rrs;
struct RefreshMeltState *rms;
const struct TALER_TESTING_Command *reveal_cmd;
const struct TALER_TESTING_Command *melt_cmd;
const struct TALER_TESTING_Command *coin_cmd;
rls->is = is;
reveal_cmd = TALER_TESTING_interpreter_lookup_command
(rls->is, rls->reveal_reference);
if (NULL == reveal_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rls->is);
return;
}
rrs = reveal_cmd->cls;
melt_cmd = TALER_TESTING_interpreter_lookup_command
(rls->is, rrs->melt_reference);
if (NULL == melt_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rls->is);
return;
}
/* find reserve_withdraw command */
{
const struct MeltDetails *md;
rms = melt_cmd->cls;
md = &rms->melted_coin;
coin_cmd = TALER_TESTING_interpreter_lookup_command
(rls->is, md->coin_reference);
if (NULL == coin_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rls->is);
return;
}
}
struct TALER_CoinSpendPrivateKeyP *coin_priv;
if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
(coin_cmd, 0, &coin_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rls->is);
return;
}
/* finally, use private key from withdraw sign command */
rls->rlh = TALER_EXCHANGE_refresh_link
(rls->exchange, coin_priv, &link_cb, rls);
if (NULL == rls->rlh)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rls->is);
return;
}
}
/**
* Cleanup the state.
*
* @param cls closure
* @param cmd the command which is being cleaned up.
*/
void
refresh_link_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct RefreshLinkState *rls = cls;
if (NULL != rls->rlh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
rls->is->ip,
cmd->label);
TALER_EXCHANGE_refresh_link_cancel (rls->rlh);
rls->rlh = NULL;
}
}
/**
* Function called with the result of the /refresh/melt operation.
*
* @param cls closure with the interpreter state
* @param http_status HTTP response code, never #MHD_HTTP_OK (200)
* as for successful intermediate response this callback is
* skipped. 0 if the exchange's reply is bogus (fails to
* follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param noreveal_index choice by the exchange in the
* cut-and-choose protocol, UINT16_MAX on error
* @param exchange_pub public key the exchange used for signing
* @param full_response full response from the exchange (for
* logging, in case of errors)
*/
static void
melt_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
uint32_t noreveal_index,
const struct TALER_ExchangePublicKeyP *exchange_pub,
const json_t *full_response)
{
struct RefreshMeltState *rms = cls;
rms->rmh = NULL;
if (rms->expected_response_code != http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s\n",
http_status,
rms->is->commands[rms->is->ip].label);
json_dumpf (full_response, stderr, 0);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
rms->noreveal_index = noreveal_index;
TALER_TESTING_interpreter_next (rms->is);
}
/**
* Run the command.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command to execute, a /wire one.
* @param i the interpreter state.
*/
void
refresh_melt_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct RefreshMeltState *rms = cls;
unsigned int num_fresh_coins;
const struct TALER_TESTING_Command *coin_command;
const char *melt_fresh_amounts[] = {
/* with 0.01 withdraw fees (except for 1ct coins),
this totals up to exactly EUR:3.97, and with
the 0.03 refresh fee, to EUR:4.0 */
"EUR:1", "EUR:1", "EUR:1", "EUR:0.1", "EUR:0.1", "EUR:0.1",
"EUR:0.1", "EUR:0.1", "EUR:0.1", "EUR:0.1", "EUR:0.1",
"EUR:0.01", "EUR:0.01", "EUR:0.01", "EUR:0.01", "EUR:0.01",
"EUR:0.01", NULL};
const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk;
rms->is = is;
rms->noreveal_index = UINT16_MAX;
for (num_fresh_coins=0;
NULL != melt_fresh_amounts[num_fresh_coins];
num_fresh_coins++) ;
rms->fresh_pks = GNUNET_new_array
(num_fresh_coins,
struct TALER_EXCHANGE_DenomPublicKey);
{
struct TALER_CoinSpendPrivateKeyP *melt_priv;
struct TALER_Amount melt_amount;
struct TALER_Amount fresh_amount;
struct TALER_DenominationSignature *melt_sig;
struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub;
unsigned int i;
const struct MeltDetails *md = &rms->melted_coin;
if (NULL == (coin_command
= TALER_TESTING_interpreter_lookup_command
(is, md->coin_reference)))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
(coin_command, 0, &melt_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
if (GNUNET_OK !=
TALER_string_to_amount (md->amount,
&melt_amount))
{
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at %u\n",
md->amount,
is->ip);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig
(coin_command, 0, &melt_sig))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub
(coin_command, 0, &melt_denom_pub))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
for (i=0;i<num_fresh_coins;i++)
{
if (GNUNET_OK != TALER_string_to_amount
(melt_fresh_amounts[i], &fresh_amount))
{
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at index %u\n",
melt_fresh_amounts[i], i);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
fresh_pk = TALER_TESTING_find_pk
(TALER_EXCHANGE_get_keys (rms->exchange), &fresh_amount);
if (NULL == fresh_pk)
{
GNUNET_break (0);
/* Subroutine logs specific error */
TALER_TESTING_interpreter_fail (rms->is);
return;
}
rms->fresh_pks[i] = *fresh_pk;
}
rms->refresh_data = TALER_EXCHANGE_refresh_prepare
(melt_priv, &melt_amount, melt_sig, melt_denom_pub,
GNUNET_YES, num_fresh_coins, rms->fresh_pks,
&rms->refresh_data_length);
if (NULL == rms->refresh_data)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
rms->rmh = TALER_EXCHANGE_refresh_melt
(rms->exchange, rms->refresh_data_length,
rms->refresh_data, &melt_cb, rms);
if (NULL == rms->rmh)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
}
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct RefreshMeltState.
* @param cmd the command which is being cleaned up.
*/
void
refresh_melt_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct RefreshMeltState *rms = cls;
if (NULL != rms->rmh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
rms->is->ip, rms->is->commands[rms->is->ip].label);
TALER_EXCHANGE_refresh_melt_cancel (rms->rmh);
rms->rmh = NULL;
}
GNUNET_free_non_null (rms->fresh_pks);
rms->fresh_pks = NULL;
GNUNET_free_non_null (rms->refresh_data);
rms->refresh_data = NULL;
rms->refresh_data_length = 0;
}
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
static int
refresh_melt_traits (void *cls,
void **ret,
const char *trait,
unsigned int index)
{
struct RefreshMeltState *rms = cls;
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_denom_pub (0, rms->fresh_pks),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
/**
* Create a "refresh melt" command.
*
* @param label command label
* @param exchange connection to the exchange
* @param amount Fixme
* @param coin_reference reference to a command that will provide
* a coin to refresh
* @param expected_response_code expected HTTP code
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_refresh_melt
(const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *amount,
const char *coin_reference,
unsigned int expected_response_code)
{
struct RefreshMeltState *rms;
struct MeltDetails md;
struct TALER_TESTING_Command cmd;
md.coin_reference = coin_reference;
md.amount = amount;
rms = GNUNET_new (struct RefreshMeltState);
rms->amount = amount;
rms->melted_coin = md;
rms->expected_response_code = expected_response_code;
rms->exchange = exchange;
cmd.label = label;
cmd.cls = rms;
cmd.run = &refresh_melt_run;
cmd.cleanup = &refresh_melt_cleanup;
cmd.traits = &refresh_melt_traits;
return cmd;
}
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
static int
refresh_reveal_traits (void *cls,
void **ret,
const char *trait,
unsigned int index)
{
struct RefreshRevealState *rrs = cls;
unsigned int num_coins = rrs->num_fresh_coins;
#define NUM_TRAITS (num_coins * 3) + 3
struct TALER_TESTING_Trait traits[NUM_TRAITS];
unsigned int i;
/* Making coin privs traits */
for (i=0; i<num_coins; i++)
traits[i] = TALER_TESTING_make_trait_coin_priv
(i, &rrs->fresh_coins[i].coin_priv);
/* Making denom pubs traits */
for (i=0; i<num_coins; i++)
traits[num_coins + i]
= TALER_TESTING_make_trait_denom_pub
(i, rrs->fresh_coins[i].pk);
/* Making denom sigs traits */
for (i=0; i<num_coins; i++)
traits[(num_coins * 2) + i]
= TALER_TESTING_make_trait_denom_sig
(i, &rrs->fresh_coins[i].sig);
/* number of fresh coins */
traits[(num_coins * 3)] = TALER_TESTING_make_trait_uint
(0, &rrs->num_fresh_coins);
/* whole array of fresh coins */
traits[(num_coins * 3) + 1]
= TALER_TESTING_make_trait_fresh_coins (0, rrs->fresh_coins),
/* end of traits */
traits[(num_coins * 3) + 2] = TALER_TESTING_trait_end ();
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
/**
* Create a "refresh reveal" command.
*
* @param label command label
* @param exchange connection to the exchange
* @param melt_reference reference to a "refresh melt" command
* @param expected_response_code expected HTTP response code
*
* @return the "refresh reveal" command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_refresh_reveal
(const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *melt_reference,
unsigned int expected_response_code)
{
struct RefreshRevealState *rrs;
struct TALER_TESTING_Command cmd;
rrs = GNUNET_new (struct RefreshRevealState);
rrs->melt_reference = melt_reference;
rrs->exchange = exchange;
rrs->expected_response_code = expected_response_code;
cmd.cls = rrs;
cmd.label = label;
cmd.run = &refresh_reveal_run;
cmd.cleanup = &refresh_reveal_cleanup;
cmd.traits = &refresh_reveal_traits;
return cmd;
}
/**
* Create a "refresh link" command.
*
* @param label command label
* @param exchange connection to the exchange
* @param melt_reference reference to a "refresh melt" command
* @param expected_response_code expected HTTP response code
*
* @return the "refresh link" command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_refresh_link
(const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *reveal_reference,
unsigned int expected_response_code)
{
struct RefreshLinkState *rrs;
struct TALER_TESTING_Command cmd;
rrs = GNUNET_new (struct RefreshLinkState);
rrs->reveal_reference = reveal_reference;
rrs->exchange = exchange;
rrs->expected_response_code = expected_response_code;
cmd.cls = rrs;
cmd.label = label;
cmd.run = &refresh_link_run;
cmd.cleanup = &refresh_link_cleanup;
return cmd;
}

View File

@ -0,0 +1,295 @@
/*
This file is part of TALER
Copyright (C) 2014-2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange/testing_api_cmd_refund.c
* @brief Implement the /refund test command, plus other
* corollary commands (?).
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_testing_lib.h"
struct RefundState
{
/**
* Expected HTTP response code.
*/
unsigned int expected_response_code;
/**
* Amount to be refunded.
*/
const char *refund_amount;
/**
* Expected refund fee.
*/
const char *refund_fee;
/**
* Reference to any command that can provide a coin to refund.
*/
const char *coin_reference;
/**
* Refund transaction identifier. Left un-initialized in the
* old test-suite. What's the best way to init it?
*/
uint64_t refund_transaction_id;
/**
* Connection to the exchange.
*/
struct TALER_EXCHANGE_Handle *exchange;
/**
* Handle to the refund operation.
*/
struct TALER_EXCHANGE_RefundHandle *rh;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
};
/**
* Check the result for the refund request.
*
* @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for
* successful deposit; 0 if the exchange's reply is bogus
* (fails to follow the protocol).
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param exchange_pub public key the exchange used for signing @a
* obj
* @param obj the received JSON reply, should be kept as proof
* (and, in particular, be forwarded to the customer)
*/
static void
refund_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const struct TALER_ExchangePublicKeyP *exchange_pub,
const json_t *obj)
{
struct RefundState *rs = cls;
struct TALER_TESTING_Command *refund_cmd;
refund_cmd = &rs->is->commands[rs->is->ip];
rs->rh = NULL;
if (rs->expected_response_code != http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s\n",
http_status,
refund_cmd->label);
json_dumpf (obj, stderr, 0);
TALER_TESTING_interpreter_fail (rs->is);
return;
}
switch (http_status)
{
case MHD_HTTP_OK:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Good /refund status code\n");
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unmanaged HTTP status code: %u, command: %s\n",
http_status, refund_cmd->label);
}
TALER_TESTING_interpreter_next (rs->is);
}
/**
* Run the command.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command to execute, a /wire one.
* @param i the interpreter state.
*/
void
refund_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct RefundState *rs = cls;
struct TALER_CoinSpendPrivateKeyP *coin_priv;
struct TALER_CoinSpendPublicKeyP coin;
const char *contract_terms;
struct GNUNET_HashCode h_contract_terms;
json_t *j_contract_terms;
struct TALER_Amount refund_fee;
struct TALER_Amount refund_amount;
const struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv;
const struct TALER_TESTING_Command *coin_cmd;
rs->exchange = is->exchange;
rs->is = is;
if (GNUNET_OK !=
TALER_string_to_amount (rs->refund_amount,
&refund_amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at %u/%s\n",
rs->refund_amount,
is->ip,
cmd->label);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK !=
TALER_string_to_amount (rs->refund_fee,
&refund_fee))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at %u/%s\n",
rs->refund_fee,
is->ip,
cmd->label);
TALER_TESTING_interpreter_fail (is);
return;
}
coin_cmd = TALER_TESTING_interpreter_lookup_command
(is, rs->coin_reference);
if (NULL == coin_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_contract_terms
(coin_cmd, 0, &contract_terms))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
j_contract_terms = json_loads
(contract_terms, JSON_REJECT_DUPLICATES, NULL);
/* Very unlikely to fail */
GNUNET_assert (NULL != j_contract_terms);
GNUNET_assert (GNUNET_OK == TALER_JSON_hash
(j_contract_terms, &h_contract_terms));
json_decref (j_contract_terms);
/* Hunting for a coin .. */
if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
(coin_cmd, 0, &coin_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
&coin.eddsa_pub);
if (GNUNET_OK != TALER_TESTING_get_trait_peer_key
(coin_cmd, 0, &merchant_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
rs->rh = TALER_EXCHANGE_refund
(rs->exchange, &refund_amount, &refund_fee, &h_contract_terms,
&coin, rs->refund_transaction_id,
(const struct TALER_MerchantPrivateKeyP *) merchant_priv,
&refund_cb, rs);
GNUNET_assert (NULL != rs->rh);
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
refund_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct RefundState *rs = cls;
if (NULL != rs->rh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
rs->is->ip,
cmd->label);
TALER_EXCHANGE_refund_cancel (rs->rh);
rs->rh = NULL;
}
GNUNET_free (rs);
}
/**
* Create a /refund test command.
*
* @param label command label
* @param expected_response_code expected HTTP status code
* @param refund_amount the amount to ask a refund for
* @param refund_fee expected refund fee
* @param coin_reference reference to a command that can
* provide a coin to be refunded.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_refund (const char *label,
unsigned int expected_response_code,
const char *refund_amount,
const char *refund_fee,
const char *coin_reference)
{
struct RefundState *rs;
struct TALER_TESTING_Command cmd;
rs = GNUNET_new (struct RefundState);
rs->expected_response_code = expected_response_code;
rs->refund_amount = refund_amount;
rs->refund_fee = refund_fee;
rs->coin_reference = coin_reference;
cmd.cls = rs;
cmd.label = label;
cmd.run = &refund_run;
cmd.cleanup = &refund_cleanup;
return cmd;
}

View File

@ -0,0 +1,112 @@
/*
This file is part of TALER
(C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_cmd_signal.c
* @brief command(s) to send signals to processes.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_testing_lib.h"
struct SignalState
{
/**
* The process to send the signal to.
*/
struct GNUNET_OS_Process *process;
/**
* The signal to send to the process.
*/
int signal;
};
/**
* Run the command.
*
* @param cls closure, typically a #struct SignalState.
* @param cmd the command to execute, a /wire one.
* @param is the interpreter state.
*/
void
signal_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct SignalState *ss = cls;
GNUNET_break (0 == GNUNET_OS_process_kill
(ss->process, ss->signal));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Signaling '%d'..\n",
ss->signal);
sleep (6);
TALER_TESTING_interpreter_next (is);
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct SignalState.
* @param cmd the command which is being cleaned up.
*/
void
signal_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct SignalState *ss = cls;
GNUNET_free (ss);
}
/**
* Send a signal to a process.
*
* @param process handle to the process
* @param signal signal to send
*
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_signal (const char *label,
struct GNUNET_OS_Process *process,
int signal)
{
struct SignalState *ss;
struct TALER_TESTING_Command cmd;
ss = GNUNET_new (struct SignalState);
ss->process = process;
ss->signal = signal;
cmd.cls = ss;
cmd.label = label;
cmd.run = &signal_run;
cmd.cleanup = &signal_cleanup;
return cmd;
}

View File

@ -0,0 +1,240 @@
/*
This file is part of TALER
Copyright (C) 2014-2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange/testing_api_cmd_status.c
* @brief Implement the /reserve/status test command.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_testing_lib.h"
struct StatusState
{
/**
* Label to the command which created the reserve to check,
* needed to resort the reserve key.
*/
const char *reserve_reference;
/**
* Handle to a /reserve/status operation.
*/
struct TALER_EXCHANGE_ReserveStatusHandle *rsh;
/**
* Expected reserve balance.
*/
const char *expected_balance;
/**
* Expected HTTP response code.
*/
unsigned int expected_response_code;
/**
* Handle to the exchange.
*/
struct TALER_EXCHANGE_Handle *exchange;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
};
/**
* Check exchange returned expected values.
*
* @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for
* successful status request 0 if the exchange's reply is
* bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @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
*/
void
reserve_status_cb
(void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const json_t *json,
const struct TALER_Amount *balance,
unsigned int history_length,
const struct TALER_EXCHANGE_ReserveHistory *history)
{
struct StatusState *ss = cls;
struct TALER_Amount eb;
ss->rsh = NULL;
if (ss->expected_response_code != http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected HTTP response code: %d\n",
http_status);
TALER_TESTING_interpreter_fail (ss->is);
return;
}
GNUNET_assert (GNUNET_OK == TALER_string_to_amount
(ss->expected_balance, &eb));
if (0 != TALER_amount_cmp (&eb, balance))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected amount in reserve\n");
TALER_TESTING_interpreter_fail (ss->is);
return;
}
/**
* Fixme: need a way to check if reserve history is consistent.
* Every command which relates to reserve 'x' should be added in
* a linked list of all commands that relate to the same reserve
* 'x'.
*
* API-wise, any command that relates to a reserve should offer a
* method called e.g. "compare_with_history" that takes an element
* of the array returned by "/reserve/status" and checks if that
* element correspond to itself (= the command exposing the check-
* method).
*/
TALER_TESTING_interpreter_next (ss->is);
}
/**
* Run the command.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command to execute, a /wire one.
* @param i the interpreter state.
*/
void
status_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct StatusState *ss = cls;
const struct TALER_TESTING_Command *create_reserve;
struct TALER_ReservePrivateKeyP *reserve_priv;
struct TALER_ReservePublicKeyP reserve_pub;
ss->is = is;
GNUNET_assert (NULL != ss->reserve_reference);
create_reserve
= TALER_TESTING_interpreter_lookup_command
(is, ss->reserve_reference);
if (NULL == create_reserve)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK !=
TALER_TESTING_get_trait_reserve_priv (create_reserve,
0,
&reserve_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
&reserve_pub.eddsa_pub);
ss->rsh
= TALER_EXCHANGE_reserve_status (ss->exchange,
&reserve_pub,
&reserve_status_cb,
ss);
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
status_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct StatusState *ss = cls;
if (NULL != ss->rsh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
ss->is->ip,
cmd->label);
TALER_EXCHANGE_reserve_status_cancel (ss->rsh);
ss->rsh = NULL;
}
GNUNET_free (ss);
}
/**
* Create a /reserve/status command.
*
* @param label the command label.
* @param exchange the exchange to connect to.
* @param reserve_reference reference to the reserve to check.
* @param expected_balance balance expected to be at the referenced reserve.
* @param expected_response_code expected HTTP response code.
*
* @return the command to be executed by the interpreter.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_status (const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *reserve_reference,
const char *expected_balance,
unsigned int expected_response_code)
{
struct TALER_TESTING_Command cmd;
struct StatusState *ss;
ss = GNUNET_new (struct StatusState);
ss->exchange = exchange;
ss->reserve_reference = reserve_reference;
ss->expected_balance = expected_balance;
ss->expected_response_code = expected_response_code;
cmd.cls = ss;
cmd.label = label;
cmd.run = &status_run;
cmd.cleanup = &status_cleanup;
return cmd;
}

View File

@ -0,0 +1,819 @@
/*
This file is part of TALER
Copyright (C) 2014-2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange/testing_api_cmd_status.c
* @brief Implement the /reserve/status test command.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_testing_lib.h"
struct TrackTransactionState
{
/**
* Which #OC_CHECK_BANK_TRANSFER wtid should this match? NULL
* for none.
*/
const char *bank_transfer_reference;
/**
* Wire transfer identifier, set if #MHD_HTTP_OK was the
* response code.
*/
struct TALER_WireTransferIdentifierRawP wtid;
/**
* Expected HTTP response code.
*/
unsigned int expected_response_code;
/**
* Reference to any operation that can provide a transaction.
* Tipically a /deposit operation.
*/
const char *transaction_reference;
/**
* Index of the coin involved in the transaction.
*/
unsigned int coin_index;
/**
* Handle to the deposit wtid request.
*/
struct TALER_EXCHANGE_TrackTransactionHandle *tth;
/**
* Handle to the exchange.
*/
struct TALER_EXCHANGE_Handle *exchange;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
};
struct TrackTransferState
{
/**
* Expected amount for this WTID.
*/
const char *expected_total_amount;
/**
* Expected fee for this WTID.
*/
const char *expected_wire_fee;
/**
* Expected HTTP response code.
*/
unsigned int expected_response_code;
/**
* Reference to any operation that can provide a WTID.
*/
const char *wtid_reference;
/**
* Reference to any operation that can provide wire details.
*/
const char *wire_details_reference;
/**
* Reference to any operation that can provide an amount.
*/
const char *total_amount_reference;
/**
* Index to the WTID to pick.
*/
unsigned int index;
/**
* Handle to the deposit wtid request.
*/
struct TALER_EXCHANGE_TrackTransferHandle *tth;
/**
* Handle to the exchange.
*/
struct TALER_EXCHANGE_Handle *exchange;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
};
/**
* Function called with detailed wire transfer data.
*
* @param cls closure
* @param http_status HTTP status code we got, 0 on exchange
* protocol violation
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param exchange_pub public key the exchange used for signing
* @param json original json reply (may include signatures, those
* have then been validated already)
* @param wtid wire transfer identifier used by the exchange, NULL
* if exchange 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 exchange 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,
enum TALER_ErrorCode ec,
const struct TALER_ExchangePublicKeyP *exchange_pub,
const json_t *json,
const struct TALER_WireTransferIdentifierRawP *wtid,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *coin_contribution)
{
struct TrackTransactionState *tts = cls;
struct TALER_TESTING_Interpreter *is = tts->is;
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
tts->tth = NULL;
if (tts->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);
TALER_TESTING_interpreter_fail (is);
return;
}
switch (http_status)
{
case MHD_HTTP_OK:
tts->wtid = *wtid;
if (NULL != tts->bank_transfer_reference)
{
const struct TALER_TESTING_Command *bank_transfer_cmd;
char *ws;
ws = GNUNET_STRINGS_data_to_string_alloc (wtid,
sizeof (*wtid));
bank_transfer_cmd = TALER_TESTING_interpreter_lookup_command
(is, tts->bank_transfer_reference);
if (NULL == bank_transfer_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
char *transfer_subject;
if (GNUNET_OK != TALER_TESTING_get_trait_transfer_subject
(bank_transfer_cmd, 0, &transfer_subject))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (0 != strcmp (ws, transfer_subject))
{
GNUNET_break (0);
GNUNET_free (ws);
TALER_TESTING_interpreter_fail (tts->is);
return;
}
GNUNET_free (ws);
}
break;
case MHD_HTTP_ACCEPTED:
/* allowed, nothing to check here */
break;
case MHD_HTTP_NOT_FOUND:
/* allowed, nothing to check here */
break;
default:
GNUNET_break (0);
break;
}
TALER_TESTING_interpreter_next (tts->is);
}
/**
* Run the command.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command to execute, a /track/transaction one.
* @param is the interpreter state.
*/
void
track_transaction_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct TrackTransactionState *tts = cls;
const struct TALER_TESTING_Command *transaction_cmd;
struct TALER_CoinSpendPrivateKeyP *coin_priv;
struct TALER_CoinSpendPublicKeyP coin_pub;
const char *wire_details;
const char *contract_terms;
json_t *j_wire_details;
json_t *j_contract_terms;
struct GNUNET_HashCode h_wire_details;
struct GNUNET_HashCode h_contract_terms;
const struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv;
tts->is = is;
transaction_cmd = TALER_TESTING_interpreter_lookup_command
(tts->is, tts->transaction_reference);
if (NULL == transaction_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (tts->is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
(transaction_cmd, tts->coin_index, &coin_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (tts->is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
&coin_pub.eddsa_pub);
/* Get the strings.. */
if (GNUNET_OK != TALER_TESTING_get_trait_wire_details
(transaction_cmd, 0, &wire_details))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (tts->is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_contract_terms
(transaction_cmd, 0, &contract_terms))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (tts->is);
return;
}
/* Parse them.. */
j_wire_details = json_loads
(wire_details, JSON_REJECT_DUPLICATES, NULL);
j_contract_terms = json_loads
(contract_terms, JSON_REJECT_DUPLICATES, NULL);
if ((NULL == j_wire_details) || (NULL == j_contract_terms))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (tts->is);
return;
}
/* Should not fail here, json has been parsed already */
GNUNET_assert
( (GNUNET_OK == TALER_JSON_hash (j_wire_details,
&h_wire_details)) &&
(GNUNET_OK == TALER_JSON_hash (j_contract_terms,
&h_contract_terms)) );
if (GNUNET_OK != TALER_TESTING_get_trait_peer_key
(transaction_cmd, 0, &merchant_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (tts->is);
return;
}
tts->tth = TALER_EXCHANGE_track_transaction
(tts->exchange,
(struct TALER_MerchantPrivateKeyP *) merchant_priv,
&h_wire_details,
&h_contract_terms,
&coin_pub,
&deposit_wtid_cb,
tts);
GNUNET_assert (NULL != tts->tth);
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
track_transaction_cleanup
(void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct TrackTransactionState *tts = cls;
if (NULL != tts->tth)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
tts->is->ip,
cmd->label);
TALER_EXCHANGE_track_transaction_cancel (tts->tth);
tts->tth = NULL;
}
GNUNET_free (tts);
}
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
static int
track_transaction_traits (void *cls,
void **ret,
const char *trait,
unsigned int index)
{
struct TrackTransactionState *tts = cls;
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_wtid (0, &tts->wtid),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
/**
* Create a /track/transaction command.
*
* @param label the command label.
* @param exchange the exchange to connect to.
* @param transaction_reference reference to a deposit operation.
* @param coin_index index of the coin involved in the transaction
* @param expected_response_code expected HTTP response code.
* @param bank_transfer_reference which #OC_CHECK_BANK_TRANSFER
* wtid should this match? NULL
* for none
*
* @return the command to be executed by the interpreter.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_track_transaction
(const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *transaction_reference,
unsigned int coin_index,
unsigned int expected_response_code,
const char *bank_transfer_reference)
{
struct TALER_TESTING_Command cmd;
struct TrackTransactionState *tts;
tts = GNUNET_new (struct TrackTransactionState);
tts->exchange = exchange;
tts->transaction_reference = transaction_reference;
tts->expected_response_code = expected_response_code;
tts->bank_transfer_reference = bank_transfer_reference;
tts->coin_index = coin_index;
cmd.cls = tts;
cmd.label = label;
cmd.run = &track_transaction_run;
cmd.cleanup = &track_transaction_cleanup;
cmd.traits = &track_transaction_traits;
return cmd;
}
/**
* Cleanup the state.
*
* @param cls closure.
* @param cmd the command which is being cleaned up.
*/
void
track_transfer_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct TrackTransferState *tts = cls;
if (NULL != tts->tth)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
tts->is->ip,
cmd->label);
TALER_EXCHANGE_track_transfer_cancel (tts->tth);
tts->tth = NULL;
}
GNUNET_free (tts);
}
/**
* 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 exchange
* protocol violation
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param exchange_pub public key the exchange used for signing
* @param json original json reply (may include signatures, those
* have then been validated already)
* @param h_wire hash of the wire transfer address the transfer
* went to, or NULL on error
* @param execution_time time when the exchange claims to have
* performed the wire transfer
* @param total_amount total amount of the wire transfer, or NULL
* if the exchange could not provide any @a wtid (set only
* if @a http_status is #MHD_HTTP_OK)
* @param wire_fee wire fee that was charged by the exchange
* @param details_length length of the @a details array
* @param details array with details about the combined
* transactions
*/
static void
track_transfer_cb
(void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const struct TALER_ExchangePublicKeyP *exchange_pub,
const json_t *json,
const struct GNUNET_HashCode *h_wire,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *total_amount,
const struct TALER_Amount *wire_fee,
unsigned int details_length,
const struct TALER_TrackTransferDetails *details)
{
struct TrackTransferState *tts = cls;
struct TALER_TESTING_Interpreter *is = tts->is;
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
const struct TALER_TESTING_Command *wtid_cmd;
struct TALER_Amount expected_amount;
tts->tth = NULL;
if (tts->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);
TALER_TESTING_interpreter_fail (is);
return;
}
switch (http_status)
{
if (
(NULL == tts->expected_total_amount) ||
(NULL == tts->expected_wire_fee))
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Expected amount and fee not specified, "
"likely to segfault...\n");
case MHD_HTTP_OK:
if (GNUNET_OK !=
TALER_string_to_amount (tts->expected_total_amount,
&expected_amount))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (0 != TALER_amount_cmp (total_amount,
&expected_amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Total amount missmatch to command %s - "
"%s vs %s\n",
cmd->label,
TALER_amount_to_string (total_amount),
TALER_amount_to_string (&expected_amount));
json_dumpf (json, stderr, 0);
fprintf (stderr, "\n");
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK !=
TALER_string_to_amount (tts->expected_wire_fee,
&expected_amount))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (0 != TALER_amount_cmp (wire_fee,
&expected_amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire fee missmatch to command %s\n",
cmd->label);
json_dumpf (json, stderr, 0);
TALER_TESTING_interpreter_fail (is);
return;
}
wtid_cmd = TALER_TESTING_interpreter_lookup_command
(is, tts->wtid_reference);
if (NULL == wtid_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
/**
* Optionally checking: (1) wire-details for this transfer
* match the ones from a referenced "deposit" operation -
* or any operation that could provide wire-details. (2)
* Total amount for this transfer matches the one from any
* referenced command that could provide one.
*/
if (NULL != tts->wire_details_reference)
{
const struct TALER_TESTING_Command *wire_details_cmd;
const char *wire_details;
json_t *j_wire_details;
struct GNUNET_HashCode h_wire_details;
if (NULL == (wire_details_cmd
= TALER_TESTING_interpreter_lookup_command
(is, tts->wire_details_reference)))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_wire_details
(wire_details_cmd, 0, &wire_details))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
j_wire_details = json_loads
(wire_details, JSON_REJECT_DUPLICATES, NULL);
GNUNET_assert (NULL != j_wire_details);
GNUNET_assert (GNUNET_OK == TALER_JSON_hash
(j_wire_details, &h_wire_details));
if (0 != memcmp (&h_wire_details,
h_wire,
sizeof (struct GNUNET_HashCode)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire hash missmath to command %s\n",
cmd->label);
json_dumpf (json, stderr, 0);
TALER_TESTING_interpreter_fail (is);
return;
}
}
if (NULL != tts->total_amount_reference)
{
const struct TALER_TESTING_Command *total_amount_cmd;
const char *total_amount_from_reference_str;
struct TALER_Amount total_amount_from_reference;
if (NULL == (total_amount_cmd
= TALER_TESTING_interpreter_lookup_command
(is, tts->total_amount_reference)))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_amount
(total_amount_cmd, 0, &total_amount_from_reference_str))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_assert (GNUNET_OK == TALER_string_to_amount
(total_amount_from_reference_str,
&total_amount_from_reference));
if (0 != TALER_amount_cmp (total_amount,
&total_amount_from_reference))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Amount missmath to command %s\n",
cmd->label);
json_dumpf (json, stderr, 0);
TALER_TESTING_interpreter_fail (is);
return;
}
}
}
TALER_TESTING_interpreter_next (is);
}
/**
* Run the command.
*
* @param cls closure.
* @param cmd the command to execute, a /track/transfer one.
* @param is the interpreter state.
*/
void
track_transfer_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
/* looking for a wtid to track .. */
struct TrackTransferState *tts = cls;
struct TALER_WireTransferIdentifierRawP wtid;
struct TALER_WireTransferIdentifierRawP *wtid_ptr;
/* If no reference is given, we'll use a all-zeros
* WTID */
memset (&wtid, 0, sizeof (wtid));
wtid_ptr = &wtid;
tts->is = is;
if (NULL != tts->wtid_reference)
{
const struct TALER_TESTING_Command *wtid_cmd;
wtid_cmd = TALER_TESTING_interpreter_lookup_command
(tts->is, tts->wtid_reference);
if (NULL == wtid_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (tts->is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_wtid
(wtid_cmd, tts->index, &wtid_ptr))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (tts->is);
return;
}
}
tts->tth = TALER_EXCHANGE_track_transfer (tts->exchange,
wtid_ptr,
&track_transfer_cb,
tts);
GNUNET_assert (NULL != tts->tth);
}
/**
* Make a /track/transfer command, expecting the transfer
* not being done (yet).
*
* @param label the command label
* @param exchange connection to the exchange
* @param wtid_reference reference to any command which can provide
* a wtid
* @param index in case there are multiple wtid offered, this
* parameter selects a particular one
* @param expected_response_code expected HTTP response code
*
* @return the command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_track_transfer_empty
(const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *wtid_reference,
unsigned int index,
unsigned int expected_response_code)
{
struct TrackTransferState *tts;
struct TALER_TESTING_Command cmd;
tts = GNUNET_new (struct TrackTransferState);
tts->wtid_reference = wtid_reference;
tts->index = index;
tts->expected_response_code = expected_response_code;
tts->exchange = exchange;
cmd.cls = tts;
cmd.label = label;
cmd.run = &track_transfer_run;
cmd.cleanup = &track_transfer_cleanup;
return cmd;
}
/**
* Make a /track/transfer command, specifying which amount and
* wire fee are expected.
*
* @param label the command label
* @param exchange connection to the exchange
* @param wtid_reference reference to any command which can provide
* a wtid
* @param index in case there are multiple wtid offered, this
* parameter selects a particular one
* @param expected_response_code expected HTTP response code
* @param expected_amount how much money we expect being
* moved with this wire-transfer.
* @param expected_wire_fee expected wire fee.
*
* @return the command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_track_transfer
(const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *wtid_reference,
unsigned int index,
unsigned int expected_response_code,
const char *expected_total_amount,
const char *expected_wire_fee)
{
struct TrackTransferState *tts;
struct TALER_TESTING_Command cmd;
tts = GNUNET_new (struct TrackTransferState);
tts->wtid_reference = wtid_reference;
tts->index = index;
tts->expected_response_code = expected_response_code;
tts->exchange = exchange;
tts->expected_total_amount = expected_total_amount;
tts->expected_wire_fee = expected_wire_fee;
cmd.cls = tts;
cmd.label = label;
cmd.run = &track_transfer_run;
cmd.cleanup = &track_transfer_cleanup;
return cmd;
}

View File

@ -0,0 +1,267 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_cmd_wire.c
* @brief command for testing /wire.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_testing_lib.h"
struct WireState
{
/**
* Handle to the /wire operation.
*/
struct TALER_EXCHANGE_WireHandle *wh;
/**
* Which wire-method we expect are offered by the exchange.
*/
const char *expected_method;
/**
* Flag indicating if the expected method is actually
* offered.
*/
unsigned int method_found;
/**
* Fee we expect is charged for this wire-transfer method.
*/
const char *expected_fee;
/**
* Expected HTTP response code.
*/
unsigned int expected_response_code;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
/**
* Connection to the exchange.
*/
struct TALER_EXCHANGE_Handle *exchange;
};
/**
* Check all the expected values have been returned by /wire.
*
* @param cls closure
* @param wire_method name of the wire method (i.e. "sepa")
* @param fees fee structure for this method
*/
static void
check_method_and_fee_cb
(void *cls,
const char *wire_method,
const struct TALER_EXCHANGE_WireAggregateFees *fees);
/**
* Callbacks called with the result(s) of a wire format inquiry
* request to the exchange.
*
* @param cls closure with the interpreter state
* @param http_status HTTP response code, #MHD_HTTP_OK (200)
* for successful request; 0 if the exchange's
* reply is bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param obj the received JSON reply, if successful this should
* be the wire format details as provided by /wire.
*/
static void
wire_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const json_t *obj)
{
struct WireState *ws = cls;
struct TALER_TESTING_Command *cmd
= &ws->is->commands[ws->is->ip];
/**
* The handle has been free'd by GNUnet curl-lib. FIXME:
* shouldn't GNUnet nullify it once it frees it?
*/
ws->wh = NULL;
if (ws->expected_response_code != http_status)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ws->is);
return;
}
if (GNUNET_OK != TALER_EXCHANGE_wire_get_fees
(&TALER_EXCHANGE_get_keys (ws->exchange)->master_pub,
obj,
// will check synchronously.
&check_method_and_fee_cb,
ws))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire fee extraction in command %s failed\n",
cmd->label);
json_dumpf (obj, stderr, 0);
TALER_TESTING_interpreter_fail (ws->is);
return;
}
if (ws->method_found != GNUNET_OK)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"/wire does not offer method '%s'\n",
ws->expected_method);
TALER_TESTING_interpreter_fail (ws->is);
return;
}
TALER_TESTING_interpreter_next (ws->is);
}
/**
* Check all the expected values have been returned by /wire.
*
* @param cls closure
* @param wire_method name of the wire method (i.e. "sepa")
* @param fees fee structure for this method
*/
static void
check_method_and_fee_cb
(void *cls,
const char *wire_method,
const struct TALER_EXCHANGE_WireAggregateFees *fees)
{
struct WireState *ws = cls;
struct TALER_TESTING_Command *cmd
= &ws->is->commands[ws->is->ip]; // ugly?
struct TALER_Amount expected_fee;
if (0 == strcmp (ws->expected_method, wire_method))
ws->method_found = GNUNET_OK;
if ( ws->expected_fee && (ws->method_found == GNUNET_OK) )
{
GNUNET_assert (GNUNET_OK == TALER_string_to_amount
(ws->expected_fee,
&expected_fee));
while (NULL != fees)
{
if (0 != TALER_amount_cmp (&fees->wire_fee,
&expected_fee))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire fee missmatch to command %s\n",
cmd->label);
TALER_TESTING_interpreter_fail (ws->is);
return;
}
fees = fees->next;
}
}
}
/**
* Run the command.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command to execute, a /wire one.
* @param i the interpreter state.
*/
void
wire_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *i)
{
struct WireState *ws = cls;
ws->is = i;
ws->wh = TALER_EXCHANGE_wire (ws->exchange,
&wire_cb,
ws);
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
wire_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct WireState *ws = cls;
if (NULL != ws->wh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
ws->is->ip,
cmd->label);
TALER_EXCHANGE_wire_cancel (ws->wh);
ws->wh = NULL;
}
GNUNET_free (ws);
}
/**
* Create a /wire command.
*
* @param label the command label.
* @param exchange the exchange to connect to.
* @param expected_method which wire-transfer method is expected
* to be offered by the exchange.
* @param expected_fee the fee the exchange should charge.
* @param expected_response_code the HTTP response the exchange
* should return.
*
* @return the command to be executed by the interpreter.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_wire (const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *expected_method,
const char *expected_fee,
unsigned int expected_response_code)
{
struct TALER_TESTING_Command cmd;
struct WireState *ws;
ws = GNUNET_new (struct WireState);
ws->exchange = exchange;
ws->expected_method = expected_method;
ws->expected_fee = expected_fee;
ws->expected_response_code = expected_response_code;
cmd.cls = ws;
cmd.label = label;
cmd.run = &wire_run;
cmd.cleanup = &wire_cleanup;
return cmd;
}

View File

@ -2,16 +2,18 @@
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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 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.
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, see
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
@ -85,14 +87,17 @@ struct WithdrawState
/**
* Function called upon completion of our /reserve/withdraw request.
* Function called upon completion of our /reserve/withdraw
* request.
*
* @param cls closure with the withdraw state
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
* 0 if the exchange's reply is bogus (fails to follow the protocol)
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for
* successful status request; 0 if the exchange's reply is
* bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param sig signature over the coin, NULL on error
* @param full_response full response from the exchange (for logging, in case of errors)
* @param full_response full response from the exchange (for
* logging, in case of errors)
*/
static void
reserve_withdraw_cb (void *cls,
@ -164,9 +169,8 @@ withdraw_run (void *cls,
struct TALER_ReservePrivateKeyP *rp;
const struct TALER_TESTING_Command *create_reserve;
create_reserve
= TALER_TESTING_interpreter_lookup_command (is,
ws->reserve_reference);
create_reserve = TALER_TESTING_interpreter_lookup_command
(is, ws->reserve_reference);
if (NULL == create_reserve)
{
GNUNET_break (0);
@ -175,7 +179,7 @@ withdraw_run (void *cls,
}
if (GNUNET_OK !=
TALER_TESTING_get_trait_reserve_priv (create_reserve,
NULL,
0,
&rp))
{
GNUNET_break (0);
@ -245,38 +249,72 @@ static int
withdraw_traits (void *cls,
void **ret,
const char *trait,
const char *selector)
unsigned int index)
{
struct WithdrawState *ws = cls;
const struct TALER_TESTING_Command *reserve_cmd;
struct TALER_ReservePrivateKeyP *reserve_priv;
/* We offer the reserve key where these coins were withdrawn
* from. */
reserve_cmd = TALER_TESTING_interpreter_lookup_command
(ws->is, ws->reserve_reference);
if (NULL == reserve_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ws->is);
return GNUNET_SYSERR;
}
if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv
(reserve_cmd, 0, &reserve_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ws->is);
return GNUNET_SYSERR;
}
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_coin_priv (NULL /* only one coin */,
TALER_TESTING_make_trait_coin_priv (0 /* only one coin */,
&ws->ps.coin_priv),
TALER_TESTING_make_trait_blinding_key (NULL /* only one coin */,
TALER_TESTING_make_trait_blinding_key (0 /* only one coin */,
&ws->ps.blinding_key),
TALER_TESTING_make_trait_denom_pub (NULL /* only one coin */,
TALER_TESTING_make_trait_denom_pub (0 /* only one coin */,
ws->pk),
TALER_TESTING_make_trait_denom_sig (NULL /* only one coin */,
TALER_TESTING_make_trait_denom_sig (0 /* only one coin */,
&ws->sig),
TALER_TESTING_make_trait_reserve_priv (0,
reserve_priv),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
selector);
index);
}
/**
* Create withdraw command.
* Create a withdraw command.
*
* @param label command label, used by other commands to
* reference this.
* @param exchange handle to the exchange.
* @param amount how much we withdraw.
* @param expected_response_code which HTTP response code
* we expect from the exchange.
*
* @return the withdraw command to be executed by the interpreter.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_withdraw_amount (const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *reserve_reference,
const char *amount,
unsigned int expected_response_code)
TALER_TESTING_cmd_withdraw_amount
(const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *reserve_reference,
const char *amount,
unsigned int expected_response_code)
{
struct TALER_TESTING_Command cmd;
struct WithdrawState *ws;
@ -294,8 +332,9 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
label);
GNUNET_assert (0);
}
ws->pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (exchange),
&ws->amount);
ws->pk = TALER_TESTING_find_pk
(TALER_EXCHANGE_get_keys (exchange),
&ws->amount);
if (NULL == ws->pk)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@ -319,11 +358,12 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
*
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_withdraw_denomination (const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *reserve_reference,
const struct TALER_EXCHANGE_DenomPublicKey *dk,
unsigned int expected_response_code)
TALER_TESTING_cmd_withdraw_denomination
(const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *reserve_reference,
const struct TALER_EXCHANGE_DenomPublicKey *dk,
unsigned int expected_response_code)
{
struct TALER_TESTING_Command cmd;
struct WithdrawState *ws;
@ -349,7 +389,4 @@ TALER_TESTING_cmd_withdraw_denomination (const char *label,
}
/* end of testing_api_cmd_withdraw.c */

View File

@ -1,4 +1,3 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
@ -31,6 +30,8 @@
/**
* Remove files from previous runs
*
* @param config_name configuration filename.
*/
void
TALER_TESTING_cleanup_files (const char *config_name)
@ -65,7 +66,7 @@ TALER_TESTING_cleanup_files (const char *config_name)
/**
* Prepare launching an exchange. Checks that the configured
* port is available, runs taler-exchange-keyup,
* taler-auditor-sign and taler-exchange-dbinit. Does not
* taler-auditor-sign and taler-exchange-dbinit. Does NOT
* launch the exchange process itself.
*
* @param config_filename configuration file to use
@ -79,42 +80,16 @@ TALER_TESTING_prepare_exchange (const char *config_filename)
enum GNUNET_OS_ProcessStatusType type;
unsigned long code;
struct GNUNET_CONFIGURATION_Handle *cfg;
unsigned long long port;
cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK !=
GNUNET_CONFIGURATION_load (cfg,
config_filename))
return GNUNET_NO;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (cfg,
"exchange",
"PORT",
&port))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange",
"PORT");
GNUNET_CONFIGURATION_destroy (cfg);
return GNUNET_NO;
}
GNUNET_CONFIGURATION_destroy (cfg);
if (GNUNET_OK !=
GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
(uint16_t) port))
{
fprintf (stderr,
"Required port %llu not available, skipping.\n",
port);
return GNUNET_NO;
}
char *test_home_dir;
char *signed_keys_out;
char *exchange_master_pub;
proc = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-keyup",
"taler-exchange-keyup",
"-c", "test_exchange_api.conf",
"-c", config_filename,
"-o", "auditor.in",
NULL);
if (NULL == proc)
@ -126,21 +101,58 @@ TALER_TESTING_prepare_exchange (const char *config_filename)
GNUNET_OS_process_wait (proc);
GNUNET_OS_process_destroy (proc);
cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK != GNUNET_CONFIGURATION_load
(cfg, config_filename))
return GNUNET_SYSERR;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"paths",
"TALER_TEST_HOME",
&test_home_dir))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"paths",
"TALER_TEST_HOME");
GNUNET_CONFIGURATION_destroy (cfg);
return GNUNET_SYSERR;
}
GNUNET_asprintf (&signed_keys_out,
"%s/.local/share/taler/auditors/auditor.out",
test_home_dir);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"exchange",
"MASTER_PUBLIC_KEY",
&exchange_master_pub))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange",
"MASTER_PUBLIC_KEY");
GNUNET_CONFIGURATION_destroy (cfg);
return GNUNET_SYSERR;
}
GNUNET_CONFIGURATION_destroy (cfg);
proc = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-auditor-sign",
"taler-auditor-sign",
"-c", "test_exchange_api.conf",
"-c", config_filename,
"-u", "http://auditor/",
"-m", "98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG",
"-m", exchange_master_pub,
"-r", "auditor.in",
"-o", "test_exchange_api_home/.local/share/taler/auditors/auditor.out",
"-o", signed_keys_out,
NULL);
if (NULL == proc)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to run `taler-exchange-keyup`, is your PATH correct?\n");
"Failed to run `taler-auditor-sign`, is your PATH correct?\n");
return GNUNET_NO;
}
GNUNET_OS_process_wait (proc);
@ -151,7 +163,7 @@ TALER_TESTING_prepare_exchange (const char *config_filename)
NULL, NULL, NULL,
"taler-exchange-dbinit",
"taler-exchange-dbinit",
"-c", "test_exchange_api.conf",
"-c", config_filename,
"-r",
NULL);
if (NULL == proc)
@ -245,22 +257,60 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
* Initialize scheduler loop and curl context for the testcase
* including starting and stopping the exchange using the given
* configuration file.
*
* @param main_cb routine containing all the commands to run.
* @param main_cb_cls closure for @a main_cb, typically NULL.
* @param config_file configuration file for the test-suite.
*
* @return FIXME: depends on what TALER_TESTING_setup returns.
*/
int
TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb,
void *main_cb_cls,
const char *config_file)
const char *config_filename)
{
int result;
unsigned int iter;
struct GNUNET_OS_Process *exchanged;
struct GNUNET_CONFIGURATION_Handle *cfg;
unsigned long long port;
cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK !=
GNUNET_CONFIGURATION_load (cfg,
config_filename))
return GNUNET_NO;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (cfg,
"exchange",
"PORT",
&port))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange",
"PORT");
GNUNET_CONFIGURATION_destroy (cfg);
return GNUNET_NO;
}
GNUNET_CONFIGURATION_destroy (cfg);
if (GNUNET_OK !=
GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
(uint16_t) port))
{
fprintf (stderr,
"Required port %llu not available, skipping.\n",
port);
return GNUNET_NO;
}
exchanged = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-httpd",
"taler-exchange-httpd",
"-c", config_file,
"-c", config_filename,
"-i",
NULL);
/* give child time to start and bind against the socket */
@ -287,7 +337,9 @@ TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb,
fprintf (stderr, "\n");
result = TALER_TESTING_setup (main_cb,
main_cb_cls);
main_cb_cls,
config_filename,
exchanged);
GNUNET_break (0 ==
GNUNET_OS_process_kill (exchanged,
SIGTERM));
@ -325,10 +377,32 @@ TALER_TESTING_url_port_free (const char *url)
return GNUNET_OK;
}
/**
* Allocate and return a piece of wire-details. Mostly, it adds
* the bank_url to the JSON.
*
* @param template the wire-details template.
* @param bank_url the bank_url
*
* @return the filled out and stringified wire-details. To
* be manually free'd.
*/
char *
TALER_TESTING_make_wire_details (const char *template,
const char *bank_url)
{
json_t *jtemplate;
GNUNET_assert (NULL != (jtemplate = json_loads
(template, JSON_REJECT_DUPLICATES, NULL)));
GNUNET_assert (0 == json_object_set
(jtemplate, "bank_url", json_string (bank_url)));
return json_dumps (jtemplate, JSON_COMPACT);
}
/**
* Prepare launching a fakebank. Check that the configuration
* file has the right option, and that the port is avaiable.
* file has the right option, and that the port is available.
* If everything is OK, return the configured URL of the fakebank.
*
* @param config_filename configuration file to use

View File

@ -2,18 +2,21 @@
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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 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.
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, see
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_loop.c
* @brief main interpreter loop for testcases
@ -28,73 +31,12 @@
#include "taler_testing_lib.h"
#include "taler_fakebank_lib.h"
/**
* Global state of the interpreter, used by a command
* to access information about other commands.
*/
struct TALER_TESTING_Interpreter
{
/**
* Commands the interpreter will run.
*/
struct TALER_TESTING_Command *commands;
/**
* Interpreter task (if one is scheduled).
*/
struct GNUNET_SCHEDULER_Task *task;
/**
* ID of task called whenever we get a SIGCHILD.
* Used for #TALER_TESTING_wait_for_sigchld().
*/
struct GNUNET_SCHEDULER_Task *child_death_task;
/**
* Main execution context for the main loop.
*/
struct GNUNET_CURL_Context *ctx;
/**
* Context for running the CURL event loop.
*/
struct GNUNET_CURL_RescheduleContext *rc;
/**
* Handle to our fakebank, if #TALER_TESTING_run_with_fakebank() was used.
* Otherwise NULL.
*/
struct TALER_FAKEBANK_Handle *fakebank;
/**
* Task run on timeout.
*/
struct GNUNET_SCHEDULER_Task *timeout_task;
/**
* Instruction pointer. Tells #interpreter_run() which
* instruction to run next.
*/
unsigned int ip;
/**
* Result of the testcases, #GNUNET_OK on success
*/
int result;
};
/**
* Pipe used to communicate child death via signal.
* Must be global, as used in signal handler!
*/
static struct GNUNET_DISK_PipeHandle *sigpipe;
/**
* Lookup command by label.
*
@ -103,8 +45,9 @@ static struct GNUNET_DISK_PipeHandle *sigpipe;
* @return NULL if command was not found
*/
const struct TALER_TESTING_Command *
TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
const char *label)
TALER_TESTING_interpreter_lookup_command
(struct TALER_TESTING_Interpreter *is,
const char *label)
{
const struct TALER_TESTING_Command *cmd;
@ -131,23 +74,35 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
* Obtain main execution context for the main loop.
*/
struct GNUNET_CURL_Context *
TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is)
TALER_TESTING_interpreter_get_context
(struct TALER_TESTING_Interpreter *is)
{
return is->ctx;
}
struct TALER_FAKEBANK_Handle *
TALER_TESTING_interpreter_get_fakebank (struct TALER_TESTING_Interpreter *is)
TALER_TESTING_interpreter_get_fakebank
(struct TALER_TESTING_Interpreter *is)
{
return is->fakebank;
}
/**
* Run tests starting the "fakebank" first. The "fakebank"
* is a C minimalist version of the human-oriented Python bank,
* which is also part of the Taler project.
*
* @param is pointer to the interpreter state
* @param commands the list of commands to execute
* @param bank_url the url the fakebank is supposed to run on
*/
void
TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is,
struct TALER_TESTING_Command *commands,
const char *bank_url)
TALER_TESTING_run_with_fakebank
(struct TALER_TESTING_Interpreter *is,
struct TALER_TESTING_Command *commands,
const char *bank_url)
{
const char *port;
long pnum;
@ -159,7 +114,7 @@ TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is,
else
pnum = strtol (port + 1, NULL, 10);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Staring Fakebank on port %u (%s)\n",
"Starting Fakebank on port %u (%s)\n",
(unsigned int) pnum,
bank_url);
is->fakebank = TALER_FAKEBANK_start ((uint16_t) pnum);
@ -192,8 +147,7 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *i)
if (GNUNET_SYSERR == i->result)
return; /* ignore, we already failed! */
i->ip++;
i->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
i);
i->task = GNUNET_SCHEDULER_add_now (&interpreter_run, i);
}
@ -201,21 +155,25 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *i)
* Current command failed, clean up and fail the test case.
*/
void
TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *i)
TALER_TESTING_interpreter_fail
(struct TALER_TESTING_Interpreter *is)
{
i->result = GNUNET_SYSERR;
// FIXME: disconnect from the exchange.
is->result = GNUNET_SYSERR;
// this cleans up too.
GNUNET_SCHEDULER_shutdown ();
}
/**
* Create command array terminator.
*
* @return a end-command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_end (void)
{
static struct TALER_TESTING_Command cmd;
return cmd;
}
@ -224,7 +182,8 @@ TALER_TESTING_cmd_end (void)
* Obtain current label.
*/
const char *
TALER_TESTING_interpreter_get_current_label (struct TALER_TESTING_Interpreter *is)
TALER_TESTING_interpreter_get_current_label
(struct TALER_TESTING_Interpreter *is)
{
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
@ -278,6 +237,10 @@ do_shutdown (void *cls)
for (unsigned int j=0;NULL != (cmd = &is->commands[j])->label;j++)
cmd->cleanup (cmd->cls,
cmd);
if (NULL != is->exchange)
{
TALER_EXCHANGE_disconnect (is->exchange);
}
if (NULL != is->task)
{
GNUNET_SCHEDULER_cancel (is->task);
@ -351,7 +314,7 @@ maint_child_death (void *cls)
sizeof (c)));
if (GNUNET_OK !=
TALER_TESTING_get_trait_process (cmd,
NULL,
0,
&processp))
{
GNUNET_break (0);
@ -361,6 +324,14 @@ maint_child_death (void *cls)
GNUNET_OS_process_wait (*processp);
GNUNET_OS_process_destroy (*processp);
*processp = NULL;
if (GNUNET_OK == is->reload_keys)
{
GNUNET_break (0 == GNUNET_OS_process_kill
(is->exchanged, SIGUSR1));
sleep (5); /* make sure signal was received and processed */
}
TALER_TESTING_interpreter_next (is);
}
@ -372,7 +343,8 @@ maint_child_death (void *cls)
* with the next command.
*/
void
TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is)
TALER_TESTING_wait_for_sigchld
(struct TALER_TESTING_Interpreter *is)
{
const struct GNUNET_DISK_FileHandle *pr;
@ -384,42 +356,66 @@ TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is)
pr,
&maint_child_death,
is);
}
/**
* Run the testsuite. FIXME: explain why the commands are
* copied into the state.
*
* @param is the interpreter state
* @param commands the list of command to execute
*/
void
TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,
struct TALER_TESTING_Command *commands)
{
unsigned int i;
/* get the number of commands */
for (i=0;NULL != commands[i].label;i++) ;
is->commands = GNUNET_new_array (i + 1,
struct TALER_TESTING_Command);
memcpy (is->commands,
commands,
sizeof (struct TALER_TESTING_Command) * i);
is->timeout_task
= GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS, 300),
&do_timeout,
is);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
is);
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
is->timeout_task = GNUNET_SCHEDULER_add_delayed
(GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300),
&do_timeout, is);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is);
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, is);
}
/**
* Information used by the wrapper around the main
* "run" method.
*/
struct MainContext
{
/**
* Main "run" method.
*/
TALER_TESTING_Main main_cb;
/**
* Closure for "run".
*/
void *main_cb_cls;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
/**
* Configuration filename. The wrapper uses it to fetch
* the exchange port number; We could have passed the port
* number here, but having the config filename seems more
* generic.
*/
const char *config_filename;
};
@ -433,58 +429,170 @@ sighandler_child_death ()
static char c;
int old_errno = errno; /* back-up errno */
GNUNET_break (1 ==
GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
(sigpipe,
GNUNET_DISK_PIPE_END_WRITE),
&c, sizeof (c)));
GNUNET_break (1 == GNUNET_DISK_file_write
(GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
&c, sizeof (c)));
errno = old_errno; /* restore errno */
}
/**
* Called once a connection to the exchange has been
* established.
*
* @param cls closure, typically, the "run" method containing
* all the commands to be run, and a closure for it.
* @param keys the exchange's keys.
* @param compat protocol compatibility information.
*/
void
cert_cb (void *cls,
const struct TALER_EXCHANGE_Keys *keys,
enum TALER_EXCHANGE_VersionCompatibility compat)
{
struct MainContext *main_ctx = cls;
if (NULL == keys)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Got NULL response for /keys\n");
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got %d DK from /keys\n",
keys->num_denom_keys);
main_ctx->is->key_generation++;
main_ctx->is->keys = keys;
/* /keys has been called for some reason and
* the interpreter is already running. */
if (GNUNET_YES == main_ctx->is->working)
return;
main_ctx->is->working = GNUNET_YES;
/* Very first start of tests, call "run()" */
if (1 == main_ctx->is->key_generation)
{
main_ctx->main_cb (main_ctx->main_cb_cls,
main_ctx->is);
return;
}
/* Tests already started, just trigger the
* next command. */
GNUNET_SCHEDULER_add_now (&interpreter_run,
main_ctx->is);
}
/**
* Initialize scheduler loop and curl context for the testcase,
* and responsible to run the "run" method.
*
* @param cls closure, typically the "run" method, the
* interpreter state and a closure for "run".
*/
static void
main_wrapper (void *cls)
{
struct MainContext *main_ctx = cls;
struct TALER_TESTING_Interpreter *is = main_ctx->is;
struct GNUNET_CONFIGURATION_Handle *cfg;
char *exchange_url;
long long unsigned int exchange_port;
is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&is->rc);
cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK != GNUNET_CONFIGURATION_load
(cfg, main_ctx->config_filename))
return;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (cfg,
"exchange",
"PORT",
&exchange_port))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange",
"PORT");
GNUNET_CONFIGURATION_destroy (cfg);
return;
}
GNUNET_asprintf (&exchange_url,
"http://localhost:%llu/",
exchange_port);
is->ctx = GNUNET_CURL_init
(&GNUNET_CURL_gnunet_scheduler_reschedule, &is->rc);
GNUNET_assert (NULL != is->ctx);
is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx);
main_ctx->main_cb (main_ctx->main_cb_cls,
main_ctx->is);
GNUNET_assert ( NULL !=
(is->exchange = TALER_EXCHANGE_connect (is->ctx,
exchange_url,
cert_cb,
main_ctx)) );
GNUNET_free (exchange_url);
GNUNET_CONFIGURATION_destroy (cfg);
}
/**
* Initialize scheduler loop and curl context for the testcase.
* Install signal handlers plus schedules the main wrapper
* around the "run" method.
*
* @param main_cb the "run" method which coontains all the
* commands.
* @param main_cb_cls a closure for "run", typically NULL.
* @param config_filename configuration filename.
* @param exchanged exchange process handle: will be put in the
* state as some commands - e.g. revoke - need to send
* signal to it, for example to let it know to reload the
* key state..
*
* @return FIXME: not sure what 'is.result' is at this stage.
*/
int
TALER_TESTING_setup (TALER_TESTING_Main main_cb,
void *main_cb_cls)
void *main_cb_cls,
const char *config_filename,
struct GNUNET_OS_Process *exchanged)
{
struct TALER_TESTING_Interpreter is;
struct MainContext main_ctx = {
.main_cb = main_cb,
.main_cb_cls = main_cb_cls,
.is = &is
/* needed to init the curl ctx */
.is = &is,
/* needed to read values like exchange port
* number and construct the exchange url. The
* port number _could_ have been passed here, but
* we prefer to stay "general" as other values might
* need to be passed around in the future. */
.config_filename = config_filename
};
struct GNUNET_SIGNAL_Context *shc_chld;
/* zero-ing the state */
memset (&is,
0,
sizeof (is));
is.exchanged = exchanged;
sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
GNUNET_NO, GNUNET_NO);
GNUNET_assert (NULL != sigpipe);
shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
&sighandler_child_death);
shc_chld = GNUNET_SIGNAL_handler_install
(GNUNET_SIGCHLD, &sighandler_child_death);
GNUNET_SCHEDULER_run (&main_wrapper,
&main_ctx);
GNUNET_SIGNAL_handler_uninstall (shc_chld);
GNUNET_DISK_pipe_close (sigpipe);
sigpipe = NULL;
/*FIXME: ?? */
return is.result;
}

View File

@ -40,22 +40,22 @@
*/
int
TALER_TESTING_get_trait_blinding_key (const struct TALER_TESTING_Command *cmd,
const char *selector,
unsigned int index,
struct TALER_DenominationBlindingKeyP **blinding_key)
{
return cmd->traits (cmd->cls,
(void **) blinding_key,
TALER_TESTING_TRAIT_BLINDING_KEY,
selector);
index);
}
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_blinding_key (const char *selector,
TALER_TESTING_make_trait_blinding_key (unsigned int index,
const struct TALER_DenominationBlindingKeyP *blinding_key)
{
struct TALER_TESTING_Trait ret = {
.selector = selector,
.index = index,
.trait_name = TALER_TESTING_TRAIT_BLINDING_KEY,
.ptr = (const void *) blinding_key
};

View File

@ -2,18 +2,21 @@
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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 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.
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, see
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_trait_coin_priv.c
* @brief main interpreter loop for testcases
@ -34,28 +37,31 @@
* Obtain a coin private key from a @a cmd.
*
* @param cmd command to extract trait from
* @param selector which coin to pick if @a cmd has multiple on offer
* @param selector which coin to pick if @a cmd has multiple on
* offer
* @param coin_priv[out] set to the private key of the coin
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_coin_priv (const struct TALER_TESTING_Command *cmd,
const char *selector,
struct TALER_CoinSpendPrivateKeyP **coin_priv)
TALER_TESTING_get_trait_coin_priv
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
struct TALER_CoinSpendPrivateKeyP **coin_priv)
{
return cmd->traits (cmd->cls,
(void **) coin_priv,
TALER_TESTING_TRAIT_COIN_PRIVATE_KEY,
selector);
index);
}
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_coin_priv (const char *selector,
const struct TALER_CoinSpendPrivateKeyP *coin_priv)
TALER_TESTING_make_trait_coin_priv
(unsigned int index,
const struct TALER_CoinSpendPrivateKeyP *coin_priv)
{
struct TALER_TESTING_Trait ret = {
.selector = selector,
.index = index,
.trait_name = TALER_TESTING_TRAIT_COIN_PRIVATE_KEY,
.ptr = (const void *) coin_priv
};

View File

@ -2,16 +2,18 @@
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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 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.
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, see
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
@ -34,28 +36,39 @@
* Obtain a denomination public key from a @a cmd.
*
* @param cmd command to extract trait from
* @param selector which coin to pick if @a cmd has multiple on offer
* @param selector which coin to pick if @a cmd has multiple on
* offer
* @param denom_pub[out] set to the blinding key of the coin
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_denom_pub (const struct TALER_TESTING_Command *cmd,
const char *selector,
struct TALER_EXCHANGE_DenomPublicKey **denom_pub)
TALER_TESTING_get_trait_denom_pub
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
struct TALER_EXCHANGE_DenomPublicKey **denom_pub)
{
return cmd->traits (cmd->cls,
(void **) denom_pub,
TALER_TESTING_TRAIT_DENOM_PUB,
selector);
index);
}
/**
* Make a trait for a denomination public key.
*
* @param selector in case the trait provides multiple
* objects, this parameter extracts a particular one.
* @param denom_pub pointer to the data to be returned from
* this trait
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_denom_pub (const char *selector,
const struct TALER_EXCHANGE_DenomPublicKey *denom_pub)
TALER_TESTING_make_trait_denom_pub
(unsigned int index,
const struct TALER_EXCHANGE_DenomPublicKey *denom_pub)
{
struct TALER_TESTING_Trait ret = {
.selector = selector,
.index = index,
.trait_name = TALER_TESTING_TRAIT_DENOM_PUB,
.ptr = (const void *) denom_pub
};
@ -63,5 +76,4 @@ TALER_TESTING_make_trait_denom_pub (const char *selector,
return ret;
}
/* end of testing_api_trait_denom_pub.c */

View File

@ -40,22 +40,22 @@
*/
int
TALER_TESTING_get_trait_denom_sig (const struct TALER_TESTING_Command *cmd,
const char *selector,
unsigned int index,
struct TALER_DenominationSignature **denom_sig)
{
return cmd->traits (cmd->cls,
(void **) denom_sig,
TALER_TESTING_TRAIT_DENOM_SIG,
selector);
index);
}
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_denom_sig (const char *selector,
TALER_TESTING_make_trait_denom_sig (unsigned int index,
const struct TALER_DenominationSignature *denom_sig)
{
struct TALER_TESTING_Trait ret = {
.selector = selector,
.index = index,
.trait_name = TALER_TESTING_TRAIT_DENOM_SIG,
.ptr = (const void *) denom_sig
};

View File

@ -0,0 +1,74 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_fresh_coin.c
* @brief traits to offer fresh conins (after "melt" operations)
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_FRESH_COINS "fresh-coins"
/**
* Obtain a "number" value from @a cmd.
*
* @param cmd command to extract trait from
* @param selector which coin to pick if @a cmd has multiple on
* offer
* @param fresh_coins[out] will point to array of fresh coins
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_fresh_coins
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
struct FreshCoin **fresh_coins)
{
return cmd->traits (cmd->cls,
(void **) fresh_coins,
TALER_TESTING_TRAIT_FRESH_COINS,
index);
}
/**
* @param selector associate the object with this "tag"
* @param fresh_coins the array of fresh coins to offer
*
* @return the trait, to be put in the traits array of the command
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_fresh_coins
(unsigned int index,
struct FreshCoin *fresh_coins)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_FRESH_COINS,
.ptr = (const void *) fresh_coins
};
return ret;
}
/* end of testing_api_trait_fresh_coin.c */

View File

@ -0,0 +1,82 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_trait_key_peer.c
* @brief traits to offer peer's (private) keys
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
/**
* NOTE: calling it "peer" key to make clear it is _not a coin_
* key.
*/
#define TALER_TESTING_TRAIT_KEY_PEER "key-peer"
/**
* Obtain a private key from a "peer". Used e.g. to obtain
* a merchant's priv to sign a /track request.
*
* @param index (tipically zero) which key to return if they
* exist in an array.
* @param selector which coin to pick if @a cmd has multiple on
* offer
* @param priv[out] set to the key coming from @a cmd.
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_peer_key
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
const struct GNUNET_CRYPTO_EddsaPrivateKey **priv)
{
return cmd->traits (cmd->cls,
(void **) priv,
TALER_TESTING_TRAIT_KEY_PEER,
index);
}
/**
* @param index (tipically zero) which key to return if they
* exist in an array.
* @param priv which object should be returned
*
* @return the trait, to be put in the traits array of the command
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_peer_key
(unsigned int index,
struct GNUNET_CRYPTO_EddsaPrivateKey *priv)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_KEY_PEER,
.ptr = (const void *) priv
};
return ret;
}
/* end of testing_api_trait_key_peer.c */

View File

@ -0,0 +1,74 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_trait_number.c
* @brief traits to offer numbers
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_NUMBER "number"
/**
* Obtain a "number" value from @a cmd.
*
* @param cmd command to extract trait from
* @param selector which coin to pick if @a cmd has multiple on
* offer
* @param n[out] set to the number coming from @a cmd.
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_uint
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
unsigned int **n)
{
return cmd->traits (cmd->cls,
(void **) n,
TALER_TESTING_TRAIT_NUMBER,
index);
}
/**
* @param selector associate the object with this "tag"
* @param n which object should be returned
*
* @return the trait, to be put in the traits array of the command
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_uint
(unsigned int index,
const unsigned int *n)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_NUMBER,
.ptr = (const void *) n
};
return ret;
}
/* end of testing_api_trait_number.c */

View File

@ -40,22 +40,22 @@
*/
int
TALER_TESTING_get_trait_process (const struct TALER_TESTING_Command *cmd,
const char *selector,
unsigned int index,
struct GNUNET_OS_Process ***processp)
{
return cmd->traits (cmd->cls,
(void **) processp,
TALER_TESTING_TRAIT_PROCESS,
selector);
index);
}
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_process (const char *selector,
TALER_TESTING_make_trait_process (unsigned int index,
struct GNUNET_OS_Process **processp)
{
struct TALER_TESTING_Trait ret = {
.selector = selector,
.index = index,
.trait_name = TALER_TESTING_TRAIT_PROCESS,
.ptr = (const void *) processp
};

View File

@ -39,22 +39,22 @@
*/
int
TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd,
const char *selector,
unsigned int index,
struct TALER_ReservePrivateKeyP **reserve_priv)
{
return cmd->traits (cmd->cls,
(void **) reserve_priv,
TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY,
selector);
index);
}
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_reserve_priv (const char *selector,
TALER_TESTING_make_trait_reserve_priv (unsigned int index,
const struct TALER_ReservePrivateKeyP *reserve_priv)
{
struct TALER_TESTING_Trait ret = {
.selector = selector,
.index = index,
.trait_name = TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY,
.ptr = (const void *) reserve_priv
};

View File

@ -0,0 +1,209 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_trait_string.c
* @brief offers strings traits. Mostly used to offer
* stringified JSONs.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_WIRE_DETAILS "wire-details"
#define TALER_TESTING_TRAIT_CONTRACT_TERMS "contract-terms"
#define TALER_TESTING_TRAIT_TRANSFER_SUBJECT "transfer-subject"
#define TALER_TESTING_TRAIT_AMOUNT "amount"
/**
* Obtain contract terms from @a cmd.
*
* @param cmd command to extract trait from
* @param index always (?) zero, as one command sticks
* to one bank account
* @param contract_terms[out] where to write the contract
* terms.
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_contract_terms
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
const char **contract_terms)
{
return cmd->traits (cmd->cls,
(void **) contract_terms,
TALER_TESTING_TRAIT_CONTRACT_TERMS,
index);
}
/**
* @param index always (?) zero, as one command sticks
* to one bank account
* @param contract_terms contract terms to offer
* @return the trait, to be put in the traits array of the command
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_contract_terms
(unsigned int index,
const char *contract_terms)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_CONTRACT_TERMS,
.ptr = (const void *) contract_terms
};
return ret;
}
/**
* Obtain wire details from @a cmd.
*
* @param cmd command to extract trait from
* @param index always (?) zero, as one command sticks
* to one bank account
* @param wire_details[out] where to write the wire details.
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_wire_details
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
const char **wire_details)
{
return cmd->traits (cmd->cls,
(void **) wire_details,
TALER_TESTING_TRAIT_WIRE_DETAILS,
index);
}
/**
* Offer wire details in a trait.
*
* @param index always (?) zero, as one command sticks
* to one bank account
* @param wire_details wire details to offer
* @return the trait, to be put in the traits array of the command
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_wire_details
(unsigned int index,
const char *wire_details)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_WIRE_DETAILS,
.ptr = (const void *) wire_details
};
return ret;
}
/**
* Obtain a transfer subject from @a cmd.
*
* @param cmd command to extract trait from
* @param index always (?) zero, as one command sticks
* to one bank transfer
* @param transfer_subject[out] where to write the wire details.
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_transfer_subject
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
char **transfer_subject)
{
return cmd->traits (cmd->cls,
(void **) transfer_subject,
TALER_TESTING_TRAIT_TRANSFER_SUBJECT,
index);
}
/**
* Offer wire details in a trait.
*
* @param index always (?) zero, as one command sticks
* to one bank account
* @param wire_details wire details to offer
* @return the trait, to be put in the traits array of the command
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_transfer_subject
(unsigned int index,
char *transfer_subject)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_TRANSFER_SUBJECT,
.ptr = (const void *) transfer_subject
};
return ret;
}
/**
* Obtain an amount from @a cmd.
*
* @param cmd command to extract trait from
* @param index which amount is to be picked, in case
* multiple are offered.
* @param amount[out] where to write the wire details.
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_amount
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
const char **amount)
{
return cmd->traits (cmd->cls,
(void **) amount,
TALER_TESTING_TRAIT_AMOUNT,
index);
}
/**
* Offer amount in a trait.
*
* @param index which amount is to be picked, in case
* multiple are offered.
* @param amount the amount to offer
* @return the trait, to be put in the traits array of the command
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_amount
(unsigned int index,
const char *amount)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_AMOUNT,
.ptr = (const void *) amount
};
return ret;
}
/* end of testing_api_trait_string.c */

View File

@ -0,0 +1,74 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_trait_number.c
* @brief traits to offer numbers
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_WTID "wtid"
/**
* Obtain a WTID value from @a cmd.
*
* @param cmd command to extract trait from
* @param index which WTID to pick if @a cmd has multiple on
* offer
* @param wtid[out] set to the wanted WTID.
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_wtid
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
struct TALER_WireTransferIdentifierRawP **wtid)
{
return cmd->traits (cmd->cls,
(void **) wtid,
TALER_TESTING_TRAIT_WTID,
index);
}
/**
* @param index associate the object with this index
* @param wtid which object should be returned
*
* @return the trait, to be put in the traits array of the command
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_wtid
(unsigned int index,
struct TALER_WireTransferIdentifierRawP *wtid)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_WTID,
.ptr = (const void *) wtid
};
return ret;
}
/* end of testing_api_trait_number.c */

View File

@ -2,16 +2,18 @@
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
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 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.
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, see
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
@ -28,11 +30,15 @@
#include "taler_testing_lib.h"
/**
* End a trait array. Usually, commands offer several traits,
* and put them in arrays.
*/
struct TALER_TESTING_Trait
TALER_TESTING_trait_end ()
{
struct TALER_TESTING_Trait end = {
.selector = NULL,
.index = 0,
.trait_name = NULL,
.ptr = NULL
};
@ -40,31 +46,35 @@ TALER_TESTING_trait_end ()
return end;
}
/**
* Pick the chosen trait from the traits array.
*
* @param traits the traits array
* @param ret where to store the result
* @param selector which particular object in the trait should be
* returned
*
* @return GNUNET_OK if no error occurred, GNUNET_SYSERR otherwise
*/
int
TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
void **ret,
const char *trait,
const char *selector)
unsigned int index)
{
for (unsigned int i=0;
NULL != traits[i].trait_name;
i++)
for (unsigned int i=0; NULL != traits[i].trait_name; i++)
{
if ( (0 == strcmp (trait,
traits[i].trait_name)) &&
( (NULL == selector) ||
(0 == strcasecmp (selector,
traits[i].selector) ) ) )
if ( (0 == strcmp (trait, traits[i].trait_name)) &&
(index == traits[i].index) )
{
*ret = (void *) traits[i].ptr;
return GNUNET_OK;
}
}
/* FIXME: log */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Trait %s/%u not found.\n",
trait, index);
return GNUNET_SYSERR;
}
/* end of testing_api_traits.c */

View File

@ -1124,6 +1124,9 @@ exchange_keys_revoke_by_dki (void *cls,
rc->ok = GNUNET_SYSERR;
return GNUNET_SYSERR;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Successfully revoking denom '%s..'\n",
TALER_B2S (rc->hc));
return GNUNET_NO;
}

View File

@ -1682,6 +1682,10 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state,
GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
&hc);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Looking for denom: '%s..'\n",
TALER_B2S (&hc));
return TEH_KS_denomination_key_lookup_by_hash (key_state,
&hc,
use);

File diff suppressed because it is too large Load Diff