diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/Makefile.am | 6 | ||||
-rw-r--r-- | src/lib/merchant_api_context.c | 20 | ||||
-rw-r--r-- | src/lib/merchant_api_pay.c | 292 | ||||
-rw-r--r-- | src/lib/test-mint-home/config/mint-common.conf | 30 | ||||
-rw-r--r-- | src/lib/test-mint-home/config/mint-keyup.conf | 86 | ||||
-rw-r--r-- | src/lib/test-mint-home/master.priv | 1 | ||||
-rw-r--r-- | src/lib/test-mint-home/sepa.json | 6 | ||||
-rw-r--r-- | src/lib/test_merchant.conf | 55 | ||||
-rw-r--r-- | src/lib/test_merchant.priv | 1 | ||||
-rw-r--r-- | src/lib/test_merchant_api.c | 201 |
10 files changed, 567 insertions, 131 deletions
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 8696db40..e43630de 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -42,6 +42,10 @@ test_merchant_api_SOURCES = \ test_merchant_api_LDADD = \ libtalermerchant.la \ $(LIBGCRYPT_LIBS) \ - $(top_builddir)/src/util/libtalerutil.la \ + -ltalermint \ + -ltalerutil \ -lgnunetutil \ -ljansson + +EXTRA_DIST = \ + test_merchant.conf diff --git a/src/lib/merchant_api_context.c b/src/lib/merchant_api_context.c index 5b8b9e4f..dd363b05 100644 --- a/src/lib/merchant_api_context.c +++ b/src/lib/merchant_api_context.c @@ -327,21 +327,29 @@ TALER_MERCHANT_perform (struct TALER_MERCHANT_Context *ctx) */ void TALER_MERCHANT_get_select_info (struct TALER_MERCHANT_Context *ctx, - fd_set *read_fd_set, - fd_set *write_fd_set, - fd_set *except_fd_set, - int *max_fd, - long *timeout) + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *except_fd_set, + int *max_fd, + long *timeout) { + long to; + GNUNET_assert (CURLM_OK == curl_multi_fdset (ctx->multi, read_fd_set, write_fd_set, except_fd_set, max_fd)); + to = *timeout; GNUNET_assert (CURLM_OK == curl_multi_timeout (ctx->multi, - timeout)); + &to)); + /* Only if what we got back from curl is smaller than what we + already had (-1 == infinity!), then update timeout */ + if ( (to < *timeout) && + (-1 != to) ) + *timeout = to; if ( (-1 == (*timeout)) && (NULL != ctx->jobs_head) ) *timeout = 1000 * 60 * 5; /* curl is not always good about giving timeouts */ diff --git a/src/lib/merchant_api_pay.c b/src/lib/merchant_api_pay.c index e33da423..40818e99 100644 --- a/src/lib/merchant_api_pay.c +++ b/src/lib/merchant_api_pay.c @@ -66,6 +66,10 @@ struct TALER_MERCHANT_Pay */ struct MAC_DownloadBuffer db; + /** + * Reference to the merchant. + */ + struct TALER_MERCHANT_Context *merchant; }; @@ -81,6 +85,7 @@ static void handle_pay_finished (void *cls, CURL *eh) { + /* FIXME: this function is not yet implemented!!! */ struct TALER_MERCHANT_Pay *ph = cls; long response_code; json_t *json; @@ -125,6 +130,7 @@ handle_pay_finished (void *cls, } ph->cb (ph->cb_cls, response_code, + "FIXME-redirect-URI", json); json_decref (json); TALER_MERCHANT_pay_cancel (ph); @@ -145,12 +151,15 @@ handle_pay_finished (void *cls, * @param num_coins number of coins used to pay * @param coins array of coins we use to pay * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s private key. + * @param max_fee maximum fee covered by the merchant (according to the contract) + * @param amount total value of the contract to be paid to the merchant * @param pay_cb the callback to call when a reply for this request is available * @param pay_cb_cls closure for @a pay_cb * @return a handle for this request */ struct TALER_MERCHANT_Pay * TALER_MERCHANT_pay_wallet (struct TALER_MERCHANT_Context *merchant, + const char *merchant_uri, const char *mint_uri, const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_contract, @@ -159,18 +168,15 @@ TALER_MERCHANT_pay_wallet (struct TALER_MERCHANT_Context *merchant, const struct TALER_MerchantPublicKeyP *merchant_pub, struct GNUNET_TIME_Absolute refund_deadline, unsigned int num_coins, - const struct TLAER_MERCHANT_PayCoin *coins, + const struct TALER_MERCHANT_PayCoin *coins, + const struct TALER_Amount *max_fee, + const struct TALER_Amount *amount, TALER_MERCHANT_PayCallback pay_cb, void *pay_cb_cls) { - struct TALER_MERCHANT_Pay *ph; - json_t *pay_obj; - json_t *j_coins; - CURL *eh; - struct GNUNET_HashCode h_wire; - struct TALER_Amount amount_without_fee; unsigned int i; struct TALER_DepositRequestPS dr; + struct TALER_MERCHANT_PaidCoin pc[num_coins]; dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS)); @@ -180,53 +186,220 @@ TALER_MERCHANT_pay_wallet (struct TALER_MERCHANT_Context *merchant, dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); dr.transaction_id = GNUNET_htonll (transaction_id); dr.merchant = *merchant_pub; - j_coins = json_array (); for (i=0;i<num_coins;i++) { - json_t *j_coin; - const struct TALER_MERCHANT_PayCoin *pc = &coins[i]; - struct TALER_CoinSpendSignatureP coin_sig; + const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; + struct TALER_MERCHANT_PaidCoin *p = &pc[i]; struct TALER_Amount fee; /* prepare 'dr' for this coin to generate coin signature */ - GNUNET_CRYPTO_ecdhe_key_get_public (&pc->coin_priv.edche_priv, - &dr.coin_pub.ecdhe_pub); + GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv, + &dr.coin_pub.eddsa_pub); TALER_amount_hton (&dr.amount_with_fee, &pc->amount_with_fee); if (GNUNET_SYSERR == TALER_amount_subtract (&fee, &pc->amount_with_fee, - &pc->fee)) + &pc->amount_without_fee)) { /* Integer underflow, fee larger than total amount? This should not happen (client violated API!) */ GNUNET_break (0); - json_decref (j_coins); return NULL; } TALER_amount_hton (&dr.deposit_fee, &fee); - GNUNET_CRYPTO_eddsa_sign (&pc->coin_priv.eddsa_priv, + GNUNET_CRYPTO_eddsa_sign (&coin->coin_priv.eddsa_priv, &dr.purpose, - &coin_sig.eddsa_sig); + &p->coin_sig.eddsa_signature); + p->denom_pub = coin->denom_pub; + p->denom_sig = coin->denom_sig; + p->coin_pub = dr.coin_pub; + p->amount_with_fee = pc->amount_with_fee; + p->amount_without_fee = pc->amount_without_fee; + } + return TALER_MERCHANT_pay_frontend (merchant, + merchant_uri, + mint_uri, + h_wire, + h_contract, + timestamp, + transaction_id, + merchant_pub, + refund_deadline, + GNUNET_TIME_UNIT_ZERO_ABS, + num_coins, + pc, + max_fee, + amount, + pay_cb, + pay_cb_cls); +} + + +/** + * Pay a merchant. API for frontends talking to backends. Here, + * the frontend does not have the coin's private keys, but just + * the public keys and signatures. Note the sublte difference + * in the type of @a coins compared to #TALER_MERCHANT_pay(). + * + * @param merchant the merchant context + * @param mint_uri URI of the mint that the coins belong to + * @param h_wire hash of the merchant’s account details + * @param h_contract hash of the contact of the merchant with the customer + * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant + * @param transaction_id transaction id for the transaction between merchant and customer + * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests) + * @param refund_deadline date until which the merchant can issue a refund to the customer via the merchant (can be zero if refunds are not allowed) + * @param execution_deadline date by which the merchant would like the mint to execute the transaction (can be zero if there is no specific date desired by the frontend) + * @param num_coins number of coins used to pay + * @param coins array of coins we use to pay + * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s private key. + * @param max_fee maximum fee covered by the merchant (according to the contract) + * @param amount total value of the contract to be paid to the merchant + * @param pay_cb the callback to call when a reply for this request is available + * @param pay_cb_cls closure for @a pay_cb + * @return a handle for this request + */ +struct TALER_MERCHANT_Pay * +TALER_MERCHANT_pay_frontend (struct TALER_MERCHANT_Context *merchant, + const char *merchant_uri, + const char *mint_uri, + const struct GNUNET_HashCode *h_wire, + const struct GNUNET_HashCode *h_contract, + struct GNUNET_TIME_Absolute timestamp, + uint64_t transaction_id, + const struct TALER_MerchantPublicKeyP *merchant_pub, + struct GNUNET_TIME_Absolute refund_deadline, + struct GNUNET_TIME_Absolute execution_deadline, + unsigned int num_coins, + const struct TALER_MERCHANT_PaidCoin *coins, + const struct TALER_Amount *max_fee, + const struct TALER_Amount *amount, + TALER_MERCHANT_PayCallback pay_cb, + void *pay_cb_cls) +{ + struct TALER_MERCHANT_Pay *ph; + json_t *pay_obj; + json_t *j_coins; + CURL *eh; + struct TALER_Amount total_fee; + struct TALER_Amount total_amount; + unsigned int i; + + if (0 == num_coins) + { + GNUNET_break (0); + return NULL; + } + j_coins = json_array (); + for (i=0;i<num_coins;i++) + { + json_t *j_coin; + const struct TALER_MERCHANT_PaidCoin *pc = &coins[i]; + struct TALER_Amount fee; + + if (GNUNET_SYSERR == + TALER_amount_subtract (&fee, + &pc->amount_with_fee, + &pc->amount_without_fee)) + { + /* Integer underflow, fee larger than total amount? + This should not happen (client violated API!) */ + GNUNET_break (0); + json_decref (j_coins); + return NULL; + } + if (0 == i) + { + total_fee = fee; + total_amount = pc->amount_with_fee; + } + else + { + if ( (GNUNET_OK != + TALER_amount_add (&total_fee, + &total_fee, + &fee)) || + (GNUNET_OK != + TALER_amount_add (&total_amount, + &total_amount, + &pc->amount_with_fee)) ) + { + /* integer overflow */ + GNUNET_break (0); + json_decref (j_coins); + return NULL; + } + } /* create JSON for this coin */ j_coin = json_pack ("{s:o, s:o," /* f/coin_pub */ " s:o, s:o," /* denom_pub / ub_sig */ " s:o}", /* coin_sig */ "f", TALER_json_from_amount (&pc->amount_with_fee), - "coin_pub", TALER_json_from_data (&dr.coin_pub, + "coin_pub", TALER_json_from_data (&pc->coin_pub, sizeof (struct TALER_CoinSpendPublicKeyP)), "denom_pub", TALER_json_from_rsa_public_key (pc->denom_pub.rsa_public_key), "ub_sig", TALER_json_from_rsa_signature (pc->denom_sig.rsa_signature), - "coin_sig", TALER_json_from_data (&coin_sig, - sizeof (coin_sig)) + "coin_sig", TALER_json_from_data (&pc->coin_sig, + sizeof (struct TALER_CoinSpendSignatureP)) ); json_array_append (j_coins, j_coin); } - + { /* Sanity check that total_amount and total_fee + match amount/max_fee requirements */ + struct TALER_Amount fee_left; + + if (GNUNET_OK == + TALER_amount_subtract (&fee_left, + &total_fee, + max_fee)) + { + /* Wallet must cover part of the fee! */ + struct TALER_Amount new_amount; + + if (GNUNET_OK != + TALER_amount_add (&new_amount, + &fee_left, + amount)) + { + /* integer overflow */ + GNUNET_break (0); + json_decref (j_coins); + return NULL; + } + if (1 == + TALER_amount_cmp (&new_amount, + &total_amount)) + { + /* new_amount > total_amount: all of the coins (total_amount) + do not add up to at least the new_amount owed to the + merchant, this request is bogus, abort */ + GNUNET_break (0); + json_decref (j_coins); + return NULL; + } + } + else + { + /* Full fee covered by merchant, but our total + must at least cover the total contract amount */ + if (1 == + TALER_amount_cmp (amount, + &total_amount)) + { + /* amount > total_amount: all of the coins (total_amount) do + not add up to at least the amount owed to the merchant, + this request is bogus, abort */ + GNUNET_break (0); + json_decref (j_coins); + return NULL; + } + } + } /* end of sanity check */ pay_obj = json_pack ("{s:o, s:o," /* H_wire/H_contract */ " s:I, s:o," /* transaction id, timestamp */ " s:o, s:s," /* refund_deadline, mint */ @@ -245,30 +418,19 @@ TALER_MERCHANT_pay_wallet (struct TALER_MERCHANT_Context *merchant, "amount", TALER_json_from_amount (amount) ); - // optionally: add edate! "edate", TALER_json_from_abs (wire_deadline), + if (0 != execution_deadline.abs_value_us) + { + /* Frontend did have an execution date in mind, add it */ + json_object_set_new (pay_obj, + "edate", + TALER_json_from_abs (execution_deadline)); + } ph = GNUNET_new (struct TALER_MERCHANT_Pay); -#if 0 ph->merchant = merchant; - ph->cb = cb; - ph->cb_cls = cb_cls; - ph->url = MAH_path_to_url (merchant, "/pay"); - ph->depconf.purpose.size = htonl (sizeof (struct TALER_PayConfirmationPS)); - ph->depconf.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONFIRM_PAY); - ph->depconf.h_contract = *h_contract; - ph->depconf.h_wire = h_wire; - ph->depconf.transaction_id = GNUNET_htonll (transaction_id); - ph->depconf.timestamp = GNUNET_TIME_absolute_hton (timestamp); - ph->depconf.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); - TALER_amount_subtract (&amount_without_fee, - amount, - &dki->fee_pay); - TALER_amount_hton (&ph->depconf.amount_without_fee, - &amount_without_fee); - ph->depconf.coin_pub = *coin_pub; - ph->depconf.merchant = *merchant_pub; - ph->amount_with_fee = *amount; - ph->coin_value = dki->value; + ph->cb = pay_cb; + ph->cb_cls = pay_cb_cls; + ph->url = GNUNET_strdup (merchant_uri); eh = curl_easy_init (); GNUNET_assert (NULL != (ph->json_enc = @@ -295,56 +457,12 @@ TALER_MERCHANT_pay_wallet (struct TALER_MERCHANT_Context *merchant, curl_easy_setopt (eh, CURLOPT_WRITEDATA, &ph->db)); - ctx = MAH_handle_to_context (merchant); - ph->job = MAC_job_add (ctx, + ph->job = MAC_job_add (merchant, eh, GNUNET_YES, &handle_pay_finished, ph); return ph; -#endif - return NULL; -} - - - -/** - * Pay a merchant. API for frontends talking to backends. Here, - * the frontend does not have the coin's private keys, but just - * the public keys and signatures. Note the sublte difference - * in the type of @a coins compared to #TALER_MERCHANT_pay(). - * - * @param merchant the merchant context - * @param mint_uri URI of the mint that the coins belong to - * @param h_wire hash of the merchant’s account details - * @param h_contract hash of the contact of the merchant with the customer - * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant - * @param transaction_id transaction id for the transaction between merchant and customer - * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests) - * @param refund_deadline date until which the merchant can issue a refund to the customer via the merchant (can be zero if refunds are not allowed) - * @param num_coins number of coins used to pay - * @param coins array of coins we use to pay - * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s private key. - * @param pay_cb the callback to call when a reply for this request is available - * @param pay_cb_cls closure for @a pay_cb - * @return a handle for this request - */ -struct TALER_MERCHANT_Pay * -TALER_MERCHANT_pay_frontend (struct TALER_MERCHANT_Context *merchant, - const char *mint_uri, - const struct GNUNET_HashCode *h_wire, - const struct GNUNET_HashCode *h_contract, - struct GNUNET_TIME_Absolute timestamp, - uint64_t transaction_id, - const struct TALER_MerchantPublicKeyP *merchant_pub, - struct GNUNET_TIME_Absolute refund_deadline, - unsigned int num_coins, - const struct TALER_MERCHANT_PaidCoin *coins, - TALER_MERCHANT_PayCallback pay_cb, - void *pay_cb_cls) -{ - GNUNET_break (0); // FIXME: not implemented! - return NULL; } diff --git a/src/lib/test-mint-home/config/mint-common.conf b/src/lib/test-mint-home/config/mint-common.conf new file mode 100644 index 00000000..b2b94826 --- /dev/null +++ b/src/lib/test-mint-home/config/mint-common.conf @@ -0,0 +1,30 @@ +[mint] +# Currency supported by the mint (can only be one) +CURRENCY = EUR + +# Wire format supported by the mint +# We use 'test' for testing of the actual +# coin operations, and 'sepa' to test SEPA-specific routines. +WIREFORMAT = test sepa + +# HTTP port the mint listens to +PORT = 8081 + +# Master public key used to sign the mint's various keys +MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG + +# How to access our database +DB = postgres + +# Is this is a testcase, use transient DB actions? +TESTRUN = YES + +[mintdb-postgres] + +DB_CONN_STR = "postgres:///talercheck" + +[mint-wire-sepa] +SEPA_RESPONSE_FILE = "test-mint-home/sepa.json" + +[mint-wire-test] +REDIRECT_URL = "http://www.taler.net/" diff --git a/src/lib/test-mint-home/config/mint-keyup.conf b/src/lib/test-mint-home/config/mint-keyup.conf new file mode 100644 index 00000000..8ad1f3bb --- /dev/null +++ b/src/lib/test-mint-home/config/mint-keyup.conf @@ -0,0 +1,86 @@ +[mint_keys] + +# how long is one signkey valid? +signkey_duration = 4 weeks + +# how long are the signatures with the signkey valid? +legal_duration = 2 years + +# how long do we generate denomination and signing keys +# ahead of time? +lookahead_sign = 32 weeks 1 day + +# how long do we provide to clients denomination and signing keys +# ahead of time? +lookahead_provide = 4 weeks 1 day + + +# Coin definitions are detected because the section +# name begins with "coin_". The rest of the +# name is free, but of course following the convention +# of "coin_$CURRENCY[_$SUBUNIT]_$VALUE" make sense. +[coin_eur_ct_1] +value = EUR:0.01 +duration_overlap = 5 minutes +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.00 +fee_deposit = EUR:0.00 +fee_refresh = EUR:0.01 +rsa_keysize = 1024 + +[coin_eur_ct_10] +value = EUR:0.10 +duration_overlap = 5 minutes +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +rsa_keysize = 1024 + +[coin_eur_1] +value = EUR:1 +duration_overlap = 5 minutes +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +rsa_keysize = 1024 + +[coin_eur_5] +value = EUR:5 +duration_overlap = 5 minutes +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +rsa_keysize = 1024 + +[coin_eur_10] +value = EUR:10 +duration_overlap = 5 minutes +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +rsa_keysize = 1024 + +[coin_eur_1000] +value = EUR:1000 +duration_overlap = 5 minutes +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +rsa_keysize = 2048 diff --git a/src/lib/test-mint-home/master.priv b/src/lib/test-mint-home/master.priv new file mode 100644 index 00000000..39492693 --- /dev/null +++ b/src/lib/test-mint-home/master.priv @@ -0,0 +1 @@ +p^-33XX!\0qmU_
\ No newline at end of file diff --git a/src/lib/test-mint-home/sepa.json b/src/lib/test-mint-home/sepa.json new file mode 100644 index 00000000..36d12f66 --- /dev/null +++ b/src/lib/test-mint-home/sepa.json @@ -0,0 +1,6 @@ +{ + "receiver_name": "Max Mustermann", + "iban": "DE89370400440532013000", + "bic": "COBADEFF370", + "sig": "8M5YJXM68PRAXKH76HYEBCJW657B23JA0RFGNDMZK2379YZMT626H1BN89KC0M1KJBWGYEN5Z763Q0Y7MCTZQ6BPPT7D9KFCTW60C10" +}
\ No newline at end of file diff --git a/src/lib/test_merchant.conf b/src/lib/test_merchant.conf new file mode 100644 index 00000000..04d0e20c --- /dev/null +++ b/src/lib/test_merchant.conf @@ -0,0 +1,55 @@ +# Sample configuration file for a merchant. +[merchant] + +# Which port do we run the backend on? (HTTP server) +PORT = 8082 + +# FIXME: is this one used? +HOSTNAME = localhost + +# Where is our private key? +KEYFILE = test_merchant.priv + +# What currency does this backend accept? +CURRENCY = KUDOS + +# FIXME: to be revised +TRUSTED_MINTS = taler + +# How quickly do we want the mint to send us our money? +# Used only if the frontend does not specify a value. +# FIXME: EDATE is a bit short, 'execution_delay'? +EDATE = 3 week + +# Which plugin (backend) do we use for the DB. +DB = postgres + +[mint-taler] +URI = http://localhost:8081/ +MASTER_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG + +# Auditors must be in sections "auditor-", the rest of the section +# name could be anything. +[auditor-ezb] +# Informal name of the auditor. Just for the user. +NAME = European Central Bank + +# URI of the auditor (especially for in the future, when the +# auditor offers an automated issue reporting system). +# Not really used today. +URI = http://taler.ezb.eu/ + +# This is the important bit: the signing key of the auditor. +PUBLIC_KEY = 9QXF7XY7E9VPV47B5Z806NDFSX2VJ79SVHHD29QEQ3BG31ANHZ60 + +# This specifies which database we use. +[merchantdb-postgres] +CONFIG = postgres:///talercheck + +# "wire-" sections include wire details, here for SEPA. +[wire-sepa] +IBAN = DE67830654080004822650 +NAME = GNUNET E.V +BIC = GENODEF1SRL +SALT = 17919252168512238964 +ADDRESS = "Garching" diff --git a/src/lib/test_merchant.priv b/src/lib/test_merchant.priv new file mode 100644 index 00000000..9c18c358 --- /dev/null +++ b/src/lib/test_merchant.priv @@ -0,0 +1 @@ +`&-./ jxGݢO:6l,ζXT4
\ No newline at end of file diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c index c264f9a3..82439432 100644 --- a/src/lib/test_merchant_api.c +++ b/src/lib/test_merchant_api.c @@ -19,14 +19,24 @@ * @author Christian Grothoff */ #include "platform.h" -#include "taler_util.h" -#include "taler_signatures.h" -#include "taler_mint_service.h" +#include <taler/taler_util.h> +#include <taler/taler_signatures.h> +#include <taler/taler_mint_service.h> #include "taler_merchant_service.h" #include <gnunet/gnunet_util_lib.h> #include <microhttpd.h> /** + * URI under which the merchant is reachable during the testcase. + */ +#define MERCHANT_URI "http://localhost:8082/" + +/** + * URI under which the mint is reachable during the testcase. + */ +#define MINT_URI "http://localhost:8081" + +/** * Main execution context for the main loop of the mint. */ static struct TALER_MINT_Context *ctx; @@ -285,6 +295,11 @@ struct Command const char *amount; /** + * Maximum fee covered by merchant. + */ + const char *max_fee; + + /** * Reference to a reserve_withdraw operation for a coin to * be used for the /deposit operation. */ @@ -318,15 +333,9 @@ struct Command struct GNUNET_TIME_Relative refund_deadline; /** - * Set (by the interpreter) to a fresh private key of the merchant, - * if @e refund_deadline is non-zero. - */ - struct TALER_MerchantPrivateKeyP merchant_priv; - - /** * Deposit handle while operation is running. */ - struct TALER_MINT_DepositHandle *dh; + struct TALER_MERCHANT_Pay *ph; } pay; @@ -733,7 +742,7 @@ pay_cb (void *cls, struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; - cmd->details.deposit.dh = NULL; + cmd->details.pay.ph = NULL; if (cmd->expected_response_code != http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -977,10 +986,102 @@ interpreter_run (void *cls, return; case OC_PAY: { - GNUNET_break (0); // FIXME: not implemented! - trigger_context_task (); + struct TALER_MERCHANT_PayCoin pc; + struct TALER_Amount amount; + struct TALER_Amount max_fee; + json_t *wire; + json_t *contract; + struct GNUNET_HashCode h_wire; + struct GNUNET_HashCode h_contract; + + /* get amount */ + if (GNUNET_OK != + TALER_string_to_amount (cmd->details.pay.amount, + &amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %u\n", + cmd->details.pay.amount, + is->ip); + fail (is); + return; + } + + /* get max_fee */ + if (GNUNET_OK != + TALER_string_to_amount (cmd->details.pay.max_fee, + &max_fee)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse max_fee `%s' at %u\n", + cmd->details.pay.max_fee, + is->ip); + fail (is); + return; + } + + /* parse wire details */ + wire = json_loads (cmd->details.pay.wire_details, + JSON_REJECT_DUPLICATES, + NULL); + if (NULL == wire) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse wire details `%s' at %u\n", + cmd->details.pay.wire_details, + is->ip); + fail (is); + return; + } + TALER_hash_json (wire, + &h_wire); + json_decref (wire); + + /* parse contract */ + contract = json_loads (cmd->details.pay.contract, + JSON_REJECT_DUPLICATES, + NULL); + if (NULL == contract) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse contract details `%s' at %u\n", + cmd->details.pay.contract, + is->ip); + fail (is); + return; + } + TALER_hash_json (contract, + &h_contract); + json_decref (contract); + + /* FIXME: fill in rest of arguments properly, + in particular merchant_pub and refund_deadline are + not correct right now... */ + cmd->details.pay.ph + = TALER_MERCHANT_pay_wallet (merchant, + MERCHANT_URI, + MINT_URI, + &h_wire, + &h_contract, + GNUNET_TIME_absolute_get (), + cmd->details.pay.transaction_id, + NULL /* FIXME: merchant_pub */, + GNUNET_TIME_UNIT_ZERO_ABS /* refund dead */, + 1 /* num_coins */, + &pc /* coins */, + &max_fee, + &amount, + &pay_cb, + is); + } + if (NULL == cmd->details.pay.ph) + { + GNUNET_break (0); + fail (is); return; } + trigger_context_task (); + return; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown instruction %d at %u (%s)\n", @@ -1062,7 +1163,15 @@ do_shutdown (void *cls, } break; case OC_PAY: - GNUNET_break (0); // FIXME: not implemented + if (NULL != cmd->details.pay.ph) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_MERCHANT_pay_cancel (cmd->details.pay.ph); + cmd->details.pay.ph = NULL; + } break; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -1089,6 +1198,11 @@ do_shutdown (void *cls, TALER_MINT_disconnect (mint); mint = NULL; } + if (NULL != merchant) + { + TALER_MERCHANT_fini (merchant); + merchant = NULL; + } if (NULL != ctx) { TALER_MINT_fini (ctx); @@ -1152,6 +1266,7 @@ context_task (void *cls, ctx_task = NULL; TALER_MINT_perform (ctx); + TALER_MERCHANT_perform (merchant); max_fd = -1; timeout = -1; FD_ZERO (&read_fd_set); @@ -1163,6 +1278,12 @@ context_task (void *cls, &except_fd_set, &max_fd, &timeout); + TALER_MERCHANT_get_select_info (merchant, + &read_fd_set, + &write_fd_set, + &except_fd_set, + &max_fd, + &timeout); if (timeout >= 0) delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, timeout); @@ -1225,41 +1346,41 @@ run (void *cls, { .oc = OC_PAY, .label = "deposit-simple", .expected_response_code = MHD_HTTP_OK, - .details.deposit.amount = "EUR:5", - .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }", - .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }", - .details.deposit.transaction_id = 1 }, + .details.pay.amount = "EUR:5", + .details.pay.coin_ref = "withdraw-coin-1", + .details.pay.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }", + .details.pay.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }", + .details.pay.transaction_id = 1 }, /* Try to double-spend the 5 EUR coin with different wire details */ { .oc = OC_PAY, .label = "deposit-double-1", .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.deposit.amount = "EUR:5", - .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":43 }", - .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }", - .details.deposit.transaction_id = 1 }, + .details.pay.amount = "EUR:5", + .details.pay.coin_ref = "withdraw-coin-1", + .details.pay.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":43 }", + .details.pay.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }", + .details.pay.transaction_id = 1 }, /* Try to double-spend the 5 EUR coin at the same merchant (but different transaction ID) */ { .oc = OC_PAY, .label = "deposit-double-2", .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.deposit.amount = "EUR:5", - .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }", - .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }", - .details.deposit.transaction_id = 2 }, + .details.pay.amount = "EUR:5", + .details.pay.coin_ref = "withdraw-coin-1", + .details.pay.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }", + .details.pay.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }", + .details.pay.transaction_id = 2 }, /* Try to double-spend the 5 EUR coin at the same merchant (but different contract) */ { .oc = OC_PAY, .label = "deposit-double-3", .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.deposit.amount = "EUR:5", - .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }", - .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":2 } }", - .details.deposit.transaction_id = 1 }, + .details.pay.amount = "EUR:5", + .details.pay.coin_ref = "withdraw-coin-1", + .details.pay.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }", + .details.pay.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":2 } }", + .details.pay.transaction_id = 1 }, { .oc = OC_END } }; @@ -1269,10 +1390,12 @@ run (void *cls, ctx = TALER_MINT_init (); GNUNET_assert (NULL != ctx); + merchant = TALER_MERCHANT_init (); + GNUNET_assert (NULL != merchant); ctx_task = GNUNET_SCHEDULER_add_now (&context_task, ctx); mint = TALER_MINT_connect (ctx, - "http://localhost:8081", + MINT_URI, &cert_cb, is, TALER_MINT_OPTION_END); GNUNET_assert (NULL != mint); @@ -1322,7 +1445,7 @@ main (int argc, NULL, NULL, NULL, "taler-merchant-httpd", "taler-merchant-httpd", - "-c", "test-merchant-home", + "-c", "test_merchant.conf", NULL); /* give child time to start and bind against the socket */ fprintf (stderr, "Waiting for taler-mint-httpd to be ready"); @@ -1331,10 +1454,14 @@ main (int argc, fprintf (stderr, "."); sleep (1); } - while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o /dev/null -O /dev/null")); + while (0 != system ("wget -q -t 1 -T 1 " MINT_URI "/keys -o /dev/null -O /dev/null")); fprintf (stderr, "\n"); result = GNUNET_SYSERR; GNUNET_SCHEDULER_run (&run, NULL); + GNUNET_OS_process_kill (merchantd, + SIGTERM); + GNUNET_OS_process_wait (merchantd); + GNUNET_OS_process_destroy (merchantd); GNUNET_OS_process_kill (mintd, SIGTERM); GNUNET_OS_process_wait (mintd); |