diff options
Diffstat (limited to 'src/mint-lib/test_mint_api.c')
-rw-r--r-- | src/mint-lib/test_mint_api.c | 716 |
1 files changed, 708 insertions, 8 deletions
diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index 29ccd1e5d..4b1b0f228 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -81,7 +81,72 @@ enum OpCode /** * Deposit a coin (pay with it). */ - OC_DEPOSIT + OC_DEPOSIT, + + /** + * Melt a (set of) coins. + */ + OC_REFRESH_MELT, + + /** + * Complete melting session by withdrawing melted coins. + */ + OC_REFRESH_REVEAL, + + /** + * Verify mint's /refresh/link by linking original private key to + * results from #OC_REFRESH_REVEAL step. + */ + OC_REFRESH_LINK + +}; + + +/** + * 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 withdraw_sign operations for coin to + * be used for the /refresh/melt operation. + */ + const char *coin_ref; + +}; + + +/** + * Information about a fresh coin generated by the refresh operation. + */ +struct FreshCoin +{ + + /** + * If @e amount is NULL, this specifies the denomination key to + * use. Otherwise, this will be set (by the interpreter) to the + * denomination PK matching @e amount. + */ + const struct TALER_MINT_DenomPublicKey *pk; + + /** + * Set (by the interpreter) to the mint's signature over the + * coin's public key. + */ + struct TALER_DenominationSignature sig; + + /** + * Set (by the interpreter) to the coin's private key. + */ + struct TALER_CoinSpendPrivateKeyP coin_priv; }; @@ -112,6 +177,9 @@ struct Command union { + /** + * Information for a #OC_ADMIN_ADD_INCOMING command. + */ struct { @@ -145,6 +213,9 @@ struct Command } admin_add_incoming; + /** + * Information for a #OC_WITHDRAW_STATUS command. + */ struct { @@ -166,8 +237,12 @@ struct Command } withdraw_status; + /** + * Information for a #OC_WITHDRAW_SIGN command. + */ struct { + /** * Which reserve should we withdraw from? */ @@ -210,6 +285,9 @@ struct Command } withdraw_sign; + /** + * Information for a #OC_DEPOSIT command. + */ struct { @@ -225,6 +303,12 @@ struct Command const char *coin_ref; /** + * If this @e coin_ref refers to an operation that generated + * an array of coins, this value determines which coin to use. + */ + unsigned int coin_idx; + + /** * JSON string describing the merchant's "wire details". */ const char *wire_details; @@ -258,6 +342,103 @@ struct Command } deposit; + /** + * Information for a #OC_REFRESH_MELT command. + */ + struct + { + + /** + * Information about coins to be melted. + */ + struct MeltDetails *melted_coins; + + /** + * Denominations of the fresh coins to withdraw. + */ + const char **fresh_amounts; + + /** + * Array of the public keys corresponding to + * the @e fresh_amounts, set by the interpreter. + */ + const struct TALER_MINT_DenomPublicKey **fresh_pks; + + /** + * Melt handle while operation is running. + */ + struct TALER_MINT_RefreshMeltHandle *rmh; + + /** + * Data used in the refresh operation, set by the interpreter. + */ + char *refresh_data; + + /** + * Number of bytes in @e refresh_data, set by the interpreter. + */ + size_t refresh_data_length; + + /** + * Set by the interpreter (upon completion) to the noreveal + * index selected by the mint. + */ + uint16_t noreveal_index; + + } refresh_melt; + + /** + * Information for a #OC_REFRESH_REVEAL command. + */ + struct + { + + /** + * Melt operation this is the matching reveal for. + */ + const char *melt_ref; + + /** + * Reveal handle while operation is running. + */ + struct TALER_MINT_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; + + } refresh_reveal; + + /** + * Information for a #OC_REFRESH_LINK command. + */ + struct + { + + /** + * Reveal operation this is the matching link for. + */ + const char *reveal_ref; + + /** + * Link handle while operation is running. + */ + struct TALER_MINT_RefreshLinkHandle *rlh; + + /** + * Which of the melted coins should be used for the linkage? + */ + unsigned int coin_idx; + + } refresh_link; + } details; }; @@ -671,7 +852,182 @@ deposit_cb (void *cls, is->ip++; is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, is); +} + + +/** + * 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 mint's reply is bogus (fails to follow the protocol) + * @param noreveal_index choice by the mint in the cut-and-choose protocol, + * UINT16_MAX on error + * @param full_response full response from the mint (for logging, in case of errors) + */ +static void +melt_cb (void *cls, + unsigned int http_status, + uint16_t noreveal_index, + json_t *full_response) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + + cmd->details.refresh_melt.rmh = NULL; + if (cmd->expected_response_code != http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u to command %s\n", + http_status, + cmd->label); + fail (is); + return; + } + cmd->details.refresh_melt.noreveal_index = noreveal_index; + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); +} + +/** + * 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 mint's reply is bogus (fails to follow the protocol) + * @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 mint (for logging, in case of errors) + */ +static void +reveal_cb (void *cls, + unsigned int http_status, + unsigned int num_coins, + const struct TALER_CoinSpendPrivateKeyP *coin_privs, + const struct TALER_DenominationSignature *sigs, + json_t *full_response) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + const struct Command *ref; + unsigned int i; + + cmd->details.refresh_reveal.rrh = NULL; + if (cmd->expected_response_code != http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u to command %s\n", + http_status, + cmd->label); + fail (is); + return; + } + ref = find_command (is, + cmd->details.refresh_reveal.melt_ref); + cmd->details.refresh_reveal.num_fresh_coins = num_coins; + switch (http_status) + { + case MHD_HTTP_OK: + cmd->details.refresh_reveal.fresh_coins + = GNUNET_new_array (num_coins, + struct FreshCoin); + for (i=0;i<num_coins;i++) + { + struct FreshCoin *fc = &cmd->details.refresh_reveal.fresh_coins[i]; + + fc->pk = ref->details.refresh_melt.fresh_pks[i]; + fc->coin_priv = coin_privs[i]; + fc->sig.rsa_signature + = GNUNET_CRYPTO_rsa_signature_dup (sigs[i].rsa_signature); + } + break; + default: + break; + } + + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); +} + + +/** + * 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 mint's reply is bogus (fails to follow the protocol) + * @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 mint (for logging, in case of errors) + */ +static void +link_cb (void *cls, + unsigned int http_status, + unsigned int num_coins, + const struct TALER_CoinSpendPrivateKeyP *coin_privs, + const struct TALER_DenominationSignature *sigs, + const struct TALER_DenominationPublicKey *pubs, + json_t *full_response) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + const struct Command *ref; + unsigned int i; + + cmd->details.refresh_link.rlh = NULL; + if (cmd->expected_response_code != http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u to command %s\n", + http_status, + cmd->label); + fail (is); + return; + } + ref = find_command (is, + cmd->details.refresh_link.reveal_ref); + switch (http_status) + { + case MHD_HTTP_OK: + /* check that number of coins returned matches */ + if (num_coins != ref->details.refresh_reveal.num_fresh_coins) + { + GNUNET_break (0); + fail (is); + return; + } + /* check that the coins match */ + for (i=0;i<num_coins;i++) + { + const struct FreshCoin *fc; + + fc = &ref->details.refresh_reveal.fresh_coins[i]; + if ( (0 != memcmp (&coin_privs[i], + &fc->coin_priv, + sizeof (struct TALER_CoinSpendPrivateKeyP))) || + (0 != GNUNET_CRYPTO_rsa_signature_cmp (fc->sig.rsa_signature, + sigs[i].rsa_signature)) || + (0 != GNUNET_CRYPTO_rsa_public_key_cmp (fc->pk->key.rsa_public_key, + pubs[i].rsa_public_key)) ) + { + GNUNET_break (0); + fail (is); + return; + } + } + break; + default: + break; + } + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); } @@ -904,6 +1260,9 @@ interpreter_run (void *cls, case OC_DEPOSIT: { struct GNUNET_HashCode h_contract; + const struct TALER_CoinSpendPrivateKeyP *coin_priv; + const struct TALER_MINT_DenomPublicKey *coin_pk; + const struct TALER_DenominationSignature *coin_pk_sig; struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendSignatureP coin_sig; struct GNUNET_TIME_Absolute refund_deadline; @@ -916,7 +1275,30 @@ interpreter_run (void *cls, ref = find_command (is, cmd->details.deposit.coin_ref); GNUNET_assert (NULL != ref); - GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc); + switch (ref->oc) + { + case OC_WITHDRAW_SIGN: + coin_priv = &ref->details.withdraw_sign.coin_priv; + coin_pk = ref->details.withdraw_sign.pk; + coin_pk_sig = &ref->details.withdraw_sign.sig; + break; + case OC_REFRESH_REVEAL: + { + const struct FreshCoin *fc; + unsigned int idx; + + idx = cmd->details.deposit.coin_idx; + GNUNET_assert (idx < ref->details.refresh_reveal.num_fresh_coins); + fc = &ref->details.refresh_reveal.fresh_coins[idx]; + + coin_priv = &fc->coin_priv; + coin_pk = fc->pk; + coin_pk_sig = &fc->sig; + } + break; + default: + GNUNET_assert (0); + } if (GNUNET_OK != TALER_string_to_amount (cmd->details.deposit.amount, &amount)) @@ -943,7 +1325,8 @@ interpreter_run (void *cls, fail (is); return; } - GNUNET_CRYPTO_eddsa_key_get_public (&ref->details.withdraw_sign.coin_priv.eddsa_priv, + + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, &coin_pub.eddsa_pub); if (0 != cmd->details.deposit.refund_deadline.rel_value_us) @@ -975,11 +1358,11 @@ interpreter_run (void *cls, TALER_amount_hton (&dr.amount_with_fee, &amount); TALER_amount_hton (&dr.deposit_fee, - &ref->details.withdraw_sign.pk->fee_deposit); + &coin_pk->fee_deposit); dr.merchant = merchant_pub; dr.coin_pub = coin_pub; GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign (&ref->details.withdraw_sign.coin_priv.eddsa_priv, + GNUNET_CRYPTO_eddsa_sign (&coin_priv->eddsa_priv, &dr.purpose, &coin_sig.eddsa_signature)); @@ -990,8 +1373,8 @@ interpreter_run (void *cls, wire, &h_contract, &coin_pub, - &ref->details.withdraw_sign.sig, - &ref->details.withdraw_sign.pk->key, + coin_pk_sig, + &coin_pk->key, timestamp, cmd->details.deposit.transaction_id, &merchant_pub, @@ -1009,6 +1392,157 @@ interpreter_run (void *cls, trigger_context_task (); return; } + case OC_REFRESH_MELT: + { + unsigned int num_melted_coins; + unsigned int num_fresh_coins; + + cmd->details.refresh_melt.noreveal_index = UINT16_MAX; + for (num_melted_coins=0; + NULL != cmd->details.refresh_melt.melted_coins[num_melted_coins].amount; + num_melted_coins++) ; + for (num_fresh_coins=0; + NULL != cmd->details.refresh_melt.fresh_amounts[num_fresh_coins]; + num_fresh_coins++) ; + + cmd->details.refresh_melt.fresh_pks + = GNUNET_new_array (num_fresh_coins, + const struct TALER_MINT_DenomPublicKey *); + { + struct TALER_CoinSpendPrivateKeyP melt_privs[num_melted_coins]; + struct TALER_Amount melt_amounts[num_melted_coins]; + struct TALER_DenominationSignature melt_sigs[num_melted_coins]; + struct TALER_MINT_DenomPublicKey melt_pks[num_melted_coins]; + struct TALER_MINT_DenomPublicKey fresh_pks[num_fresh_coins]; + unsigned int i; + + for (i=0;i<num_melted_coins;i++) + { + const struct MeltDetails *md = &cmd->details.refresh_melt.melted_coins[i]; + ref = find_command (is, + md->coin_ref); + GNUNET_assert (NULL != ref); + GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc); + + melt_privs[i] = ref->details.withdraw_sign.coin_priv; + if (GNUNET_OK != + TALER_string_to_amount (md->amount, + &melt_amounts[i])) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %u\n", + md->amount, + is->ip); + fail (is); + return; + } + melt_sigs[i] = ref->details.withdraw_sign.sig; + melt_pks[i] = *ref->details.withdraw_sign.pk; + } + for (i=0;i<num_fresh_coins;i++) + { + if (GNUNET_OK != + TALER_string_to_amount (cmd->details.refresh_melt.fresh_amounts[i], + &amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %u\n", + cmd->details.withdraw_sign.amount, + is->ip); + fail (is); + return; + } + cmd->details.refresh_melt.fresh_pks[i] + = find_pk (is->keys, + &amount); + fresh_pks[i] = *cmd->details.refresh_melt.fresh_pks[i]; + } + cmd->details.refresh_melt.refresh_data + = TALER_MINT_refresh_prepare (num_melted_coins, + melt_privs, + melt_amounts, + melt_sigs, + melt_pks, + GNUNET_YES, + num_fresh_coins, + fresh_pks, + &cmd->details.refresh_melt.refresh_data_length); + if (NULL == cmd->details.refresh_melt.refresh_data) + { + GNUNET_break (0); + fail (is); + return; + } + cmd->details.refresh_melt.rmh + = TALER_MINT_refresh_melt (mint, + cmd->details.refresh_melt.refresh_data_length, + cmd->details.refresh_melt.refresh_data, + &melt_cb, + is); + if (NULL == cmd->details.refresh_melt.rmh) + { + GNUNET_break (0); + fail (is); + return; + } + } + } + trigger_context_task (); + return; + case OC_REFRESH_REVEAL: + ref = find_command (is, + cmd->details.refresh_reveal.melt_ref); + cmd->details.refresh_reveal.rrh + = TALER_MINT_refresh_reveal (mint, + ref->details.refresh_melt.refresh_data_length, + ref->details.refresh_melt.refresh_data, + ref->details.refresh_melt.noreveal_index, + &reveal_cb, + is); + if (NULL == cmd->details.refresh_reveal.rrh) + { + GNUNET_break (0); + fail (is); + return; + } + trigger_context_task (); + return; + case OC_REFRESH_LINK: + /* find reveal command */ + ref = find_command (is, + cmd->details.refresh_link.reveal_ref); + /* find melt command */ + ref = find_command (is, + ref->details.refresh_reveal.melt_ref); + /* find withdraw_sign command */ + { + unsigned int idx; + const struct MeltDetails *md; + unsigned int num_melted_coins; + + for (num_melted_coins=0; + NULL != ref->details.refresh_melt.melted_coins[num_melted_coins].amount; + num_melted_coins++) ; + idx = cmd->details.refresh_link.coin_idx; + GNUNET_assert (idx < num_melted_coins); + md = &ref->details.refresh_melt.melted_coins[idx]; + ref = find_command (is, + md->coin_ref); + } + /* finally, use private key from withdraw sign command */ + cmd->details.refresh_link.rlh + = TALER_MINT_refresh_link (mint, + &ref->details.withdraw_sign.coin_priv, + &link_cb, + is); + if (NULL == cmd->details.refresh_link.rlh) + { + GNUNET_break (0); + fail (is); + return; + } + trigger_context_task (); + return; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown instruction %d at %u (%s)\n", @@ -1100,6 +1634,55 @@ do_shutdown (void *cls, cmd->details.deposit.dh = NULL; } break; + case OC_REFRESH_MELT: + if (NULL != cmd->details.refresh_melt.rmh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_MINT_refresh_melt_cancel (cmd->details.refresh_melt.rmh); + cmd->details.refresh_melt.rmh = NULL; + } + GNUNET_free_non_null (cmd->details.refresh_melt.fresh_pks); + cmd->details.refresh_melt.fresh_pks = NULL; + GNUNET_free_non_null (cmd->details.refresh_melt.refresh_data); + cmd->details.refresh_melt.refresh_data = NULL; + cmd->details.refresh_melt.refresh_data_length = 0; + break; + case OC_REFRESH_REVEAL: + if (NULL != cmd->details.refresh_reveal.rrh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_MINT_refresh_reveal_cancel (cmd->details.refresh_reveal.rrh); + cmd->details.refresh_reveal.rrh = NULL; + } + { + unsigned int j; + struct FreshCoin *fresh_coins; + + fresh_coins = cmd->details.refresh_reveal.fresh_coins; + for (j=0;j<cmd->details.refresh_reveal.num_fresh_coins;j++) + GNUNET_CRYPTO_rsa_signature_free (fresh_coins[j].sig.rsa_signature); + } + GNUNET_free_non_null (cmd->details.refresh_reveal.fresh_coins); + cmd->details.refresh_reveal.fresh_coins = NULL; + cmd->details.refresh_reveal.num_fresh_coins = 0; + break; + case OC_REFRESH_LINK: + if (NULL != cmd->details.refresh_link.rlh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_MINT_refresh_link_cancel (cmd->details.refresh_link.rlh); + cmd->details.refresh_link.rlh = NULL; + } + break; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown instruction %d at %u (%s)\n", @@ -1236,6 +1819,34 @@ run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct InterpreterState *is; + static struct MeltDetails melt_coins_1[] = { + { .amount = "EUR:4", + .coin_ref = "refresh-withdraw-coin-1" }, + { NULL, NULL } + }; + static const char *melt_fresh_amounts_1[] = { + "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", + /* 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*/ + NULL + }; static struct Command commands[] = { /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */ @@ -1273,6 +1884,7 @@ run (void *cls, .expected_response_code = MHD_HTTP_PAYMENT_REQUIRED, .details.withdraw_sign.reserve_reference = "create-reserve-1", .details.withdraw_sign.amount = "EUR:5" }, + /* Try to double-spend the 5 EUR coin with different wire details */ { .oc = OC_DEPOSIT, .label = "deposit-double-1", @@ -1303,6 +1915,87 @@ run (void *cls, .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":2 } }", .details.deposit.transaction_id = 1 }, + /* ***************** /refresh testing ******************** */ + + /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct */ + { .oc = OC_ADMIN_ADD_INCOMING, + .label = "refresh-create-reserve-1", + .expected_response_code = MHD_HTTP_OK, + .details.admin_add_incoming.wire = "{ \"type\":\"TEST\", \"bank\":\"source bank\", \"account\":424 }", + .details.admin_add_incoming.amount = "EUR:5.01" }, + /* Withdraw a 5 EUR coin, at fee of 1 ct */ + { .oc = OC_WITHDRAW_SIGN, + .label = "refresh-withdraw-coin-1", + .expected_response_code = MHD_HTTP_OK, + .details.withdraw_sign.reserve_reference = "refresh-create-reserve-1", + .details.withdraw_sign.amount = "EUR:5" }, + /* 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) */ + { .oc = OC_DEPOSIT, + .label = "refresh-deposit-partial", + .expected_response_code = MHD_HTTP_OK, + .details.deposit.amount = "EUR:1", + .details.deposit.coin_ref = "refresh-withdraw-coin-1", + .details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }", + .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\"EUR:1 } }", + .details.deposit.transaction_id = 42421 }, + + /* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */ + + { .oc = OC_REFRESH_MELT, + .label = "refresh-melt-1", + .expected_response_code = MHD_HTTP_OK, + .details.refresh_melt.melted_coins = melt_coins_1, + .details.refresh_melt.fresh_amounts = melt_fresh_amounts_1 }, + +#if TEST_REFRESH + + /* Complete (successful) melt operation, and withdraw the coins */ + { .oc = OC_REFRESH_REVEAL, + .label = "refresh-reveal-1", + .expected_response_code = MHD_HTTP_OK, + .details.refresh_reveal.melt_ref = "refresh-melt-1" }, + + + /* Test that /refresh/link works */ + { .oc = OC_REFRESH_LINK, + .label = "refresh-link-1", + .expected_response_code = MHD_HTTP_OK, + .details.refresh_link.reveal_ref = "refresh-reveal-1" }, + + /* Test successfully spending coins from the refresh operation: + first EUR:1 */ + { .oc = OC_DEPOSIT, + .label = "refresh-deposit-refreshed-1", + .expected_response_code = MHD_HTTP_OK, + .details.deposit.amount = "EUR:1", + .details.deposit.coin_ref = "refresh-reveal-1a", + .details.deposit.coin_idx = 0, + .details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }", + .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":3 } }", + .details.deposit.transaction_id = 2 }, + /* Test successfully spending coins from the refresh operation: + finally EUR:0.1 */ + { .oc = OC_DEPOSIT, + .label = "refresh-deposit-refreshed-1b", + .expected_response_code = MHD_HTTP_OK, + .details.deposit.amount = "EUR:0.1", + .details.deposit.coin_ref = "refresh-reveal-1b", + .details.deposit.coin_idx = 4, + .details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }", + .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":3 } }", + .details.deposit.transaction_id = 2 }, + + /* Test running a failing melt operation (same operation again must fail) */ + { .oc = OC_REFRESH_MELT, + .label = "refresh-melt-failing", + .expected_response_code = MHD_HTTP_FORBIDDEN, + .details.refresh_melt.melted_coins = melt_coins_1, + .details.refresh_melt.fresh_amounts = melt_fresh_amounts_1 }, + + /* *************** end of /refresh testing ************** */ +#endif + { .oc = OC_END } }; @@ -1359,7 +2052,14 @@ main (int argc, "-d", "test-mint-home", NULL); /* give child time to start and bind against the socket */ - sleep (2); + fprintf (stderr, "Waiting for taler-mint-httpd to be ready"); + do + { + fprintf (stderr, "."); + sleep (1); + } + while (0 != system ("wget -q -t 1 http://localhost:8081/agpl -o /dev/null -O /dev/null")); + fprintf (stderr, "\n"); result = GNUNET_SYSERR; GNUNET_SCHEDULER_run (&run, NULL); GNUNET_OS_process_kill (mintd, |