summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/Makefile.am6
-rw-r--r--src/lib/merchant_api_context.c20
-rw-r--r--src/lib/merchant_api_pay.c292
-rw-r--r--src/lib/test-mint-home/config/mint-common.conf30
-rw-r--r--src/lib/test-mint-home/config/mint-keyup.conf86
-rw-r--r--src/lib/test-mint-home/master.priv1
-rw-r--r--src/lib/test-mint-home/sepa.json6
-rw-r--r--src/lib/test_merchant.conf55
-rw-r--r--src/lib/test_merchant.priv1
-rw-r--r--src/lib/test_merchant_api.c201
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);