From c1f28638c9929424f9ad395c7016e6ea2022d751 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 17 Jan 2016 18:19:09 +0100 Subject: working on #3888 --- src/include/taler_crypto_lib.h | 31 ++++++ src/include/taler_mintdb_plugin.h | 48 ++++++++- src/include/taler_signatures.h | 19 ++++ src/mint/taler-mint-httpd_db.c | 198 ++++++++++++++++++++++++++++++++-- src/mint/taler-mint-httpd_db.h | 14 +++ src/mint/taler-mint-httpd_responses.c | 67 ++++++++++-- src/mint/taler-mint-httpd_responses.h | 33 ++++-- src/mint/taler-mint-httpd_tracking.c | 24 ++++- src/mintdb/plugin_mintdb_postgres.c | 26 ++++- 9 files changed, 428 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 16240e5a..87020fbb 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -428,6 +428,37 @@ struct TALER_RefreshLinkDecrypted }; +/** + * Binary information encoded in Crockford's Base32 in wire transfer + * subjects of transfers from Taler to a merchant. The actual value + * is chosen by the mint and has no particular semantics, other than + * being unique so that the mint can lookup details about the wire + * transfer when needed. + */ +struct TALER_WireTransferIdentifierP +{ + + /** + * Raw value. Note that typical payment systems (SEPA, ACH) support + * at least two lines of 27 ASCII characters to encode a transaction + * subject or "details", for a total of 54 characters. (The payment + * system protocols often support more lines, but the forms presented + * to customers are usually limited to 54 characters.) + * + * With a Base32-encoding of 5 bit per character, this gives us 270 + * bits or (rounded down) 33 bytes. So we use the first 32 bytes to + * encode the actual value (i.e. a 256-bit / 32-byte public key or + * a hash code), and the last byte for a minimalistic checksum. + */ + uint8_t raw[32]; + + /** + * Checksum using CRC8 over the @e raw data. + */ + uint8_t crc8; +}; + + GNUNET_NETWORK_STRUCT_END diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index d9a1c6c8..a4a94faa 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -571,7 +571,7 @@ typedef void /** * Function called with the results of the lookup of the * wire transfer identifier information. - * + * * @param cls closure * @param wtid base32-encoded wire transfer identifier, NULL * if the transaction was not yet done @@ -585,6 +585,31 @@ typedef void const char *wtid, struct GNUNET_TIME_Absolute execution_time); + +/** + * Function called with the results of the lookup of the + * transaction data associated with a wire transfer identifier. + * + * @param cls closure + * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) + * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls) + * @param h_contract which contract was this payment about + * @param transaction_id merchant's transaction ID for the payment + * @param coin_pub which public key was this payment about + * @param deposit_value amount contributed by this coin in total + * @param deposit_fee deposit fee charged by mint for this coin + */ +typedef void +(*TALER_MINTDB_TransactionDataCallback)(void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_wire, + const struct GNUNET_HashCode *h_contract, + uint64_t transaction_id, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *deposit_value, + const struct TALER_Amount *deposit_fee); + + /** * @brief The plugin API, returned from the plugin's "init" function. * The argument given to "init" is simply a configuration handle. @@ -1194,11 +1219,30 @@ struct TALER_MINTDB_Plugin struct TALER_MINTDB_TransactionList *list); + /** + * Lookup the list of Taler transactions that was aggregated + * into a wire transfer by the respective @a raw_wtid. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param raw_wtid the raw wire transfer identifier we used + * @param raw_len number of bytes in @a raw_wtid (right now always 32) + * @param cb function to call on each transaction found + * @param cb_cls closure for @a cb + * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors + */ + int + (*lookup_wire_transactions) (void *cls, + const void *raw_wtid, + size_t raw_len, + TALER_MINTDB_TransactionDataCallback cb, + void *cb_cls); + + /** * Try to find the wire transfer details for a deposit operation. * If we did not execute the deposit yet, return when it is supposed * to be executed. - * + * * @param cls closure * @param h_contract hash of the contract * @param h_wire hash of merchant wire details diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index fb2916cf..ca8cf2de 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -106,6 +106,11 @@ */ #define TALER_SIGNATURE_MINT_WIRE_TYPES 1036 +/** + * Signature where the Mint confirms the /deposit/wtid response. + */ +#define TALER_SIGNATURE_MINT_CONFIRM_WIRE 1036 + /*********************/ /* Wallet signatures */ @@ -863,6 +868,20 @@ struct TALER_ContractPS }; +/** + * Details affirmed by the mint about a wire transfer the mint + * claims to have done with respect to a deposit operation. + */ +struct TALER_ConfirmWirePS +{ + /** + * Purpose header for the signature over the contract with + * purpose #TALER_SIGNATURE_MINT_CONFIRM_WIRE. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + +}; GNUNET_NETWORK_STRUCT_END diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 2b4ade59..1b0af585 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -1551,10 +1551,190 @@ TMH_DB_execute_admin_add_incoming (struct MHD_Connection *connection, } +/** + * Closure for #handle_transaction_data. + */ +struct WtidTransactionContext +{ + + /** + * Total amount of the wire transfer, as calculated by + * summing up the individual amounts. To be rounded down + * to calculate the real transfer amount at the end. + * Only valid if @e is_valid is #GNUNET_YES. + */ + struct TALER_Amount total; + + /** + * Public key of the merchant, only valid if @e is_valid + * is #GNUNET_YES. + */ + struct TALER_MerchantPublicKeyP merchant_pub; + + /** + * Hash of the wire details of the merchant (identical for all + * deposits), only valid if @e is_valid is #GNUNET_YES. + */ + struct GNUNET_HashCode h_wire; + + /** + * JSON array with details about the individual deposits. + */ + json_t *deposits; + + /** + * Initially #GNUNET_NO, if we found no deposits so far. Set to + * #GNUNET_YES if we got transaction data, and the database replies + * remained consistent with respect to @e merchant_pub and @e h_wire + * (as they should). Set to #GNUNET_SYSERR if we encountered an + * internal error. + */ + int is_valid; + +}; + + +/** + * Function called with the results of the lookup of the + * transaction data for the given wire transfer identifier. + * + * @param cls our context for transmission + * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) + * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls) + * @param h_contract which contract was this payment about + * @param transaction_id merchant's transaction ID for the payment + * @param coin_pub which public key was this payment about + * @param deposit_value amount contributed by this coin in total + * @param deposit_fee deposit fee charged by mint for this coin + */ +static void +handle_transaction_data (void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_wire, + const struct GNUNET_HashCode *h_contract, + uint64_t transaction_id, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *deposit_value, + const struct TALER_Amount *deposit_fee) +{ + struct WtidTransactionContext *ctx = cls; + struct TALER_Amount delta; + + if (GNUNET_SYSERR == ctx->is_valid) + return; + if (GNUNET_NO == ctx->is_valid) + { + ctx->merchant_pub = *merchant_pub; + ctx->h_wire = *h_wire; + ctx->is_valid = GNUNET_YES; + if (GNUNET_OK != + TALER_amount_subtract (&ctx->total, + deposit_value, + deposit_fee)) + { + GNUNET_break (0); + ctx->is_valid = GNUNET_SYSERR; + return; + } + } + else + { + if ( (0 != memcmp (&ctx->merchant_pub, + merchant_pub, + sizeof (struct TALER_MerchantPublicKeyP))) || + (0 != memcmp (&ctx->h_wire, + h_wire, + sizeof (struct GNUNET_HashCode))) ) + { + GNUNET_break (0); + ctx->is_valid = GNUNET_SYSERR; + return; + } + if (GNUNET_OK != + TALER_amount_subtract (&delta, + deposit_value, + deposit_fee)) + { + GNUNET_break (0); + ctx->is_valid = GNUNET_SYSERR; + return; + } + if (GNUNET_OK != + TALER_amount_add (&ctx->total, + &ctx->total, + &delta)) + { + GNUNET_break (0); + ctx->is_valid = GNUNET_SYSERR; + return; + } + } + /* NOTE: We usually keep JSON stuff out of the _DB file, and this + is also ugly if we ever add signatures over this data. (#4135) */ + json_array_append (ctx->deposits, + json_pack ("{s:o, s:o, s:o, s:I, s:o}", + "deposit_value", TALER_json_from_amount (deposit_value), + "deposit_fee", TALER_json_from_amount (deposit_fee), + "H_contract", TALER_json_from_data (h_contract, + sizeof (struct GNUNET_HashCode)), + "transaction_id", (json_int_t) transaction_id, + "coin_pub", TALER_json_from_data (coin_pub, + sizeof (struct TALER_CoinSpendPublicKeyP)))); +} + + +/** + * Execute a "/wire/deposits". Returns the transaction information + * associated with the given wire transfer identifier. + * + * @param connection the MHD connection to handle + * @param wtid wire transfer identifier to resolve + * @return MHD result code + */ +int +TMH_DB_execute_wire_deposits (struct MHD_Connection *connection, + const struct TALER_WireTransferIdentifierP *wtid) +{ + int ret; + struct WtidTransactionContext ctx; + + ctx.is_valid = GNUNET_NO; + ctx.deposits = json_array (); + ret = TMH_plugin->lookup_wire_transactions (TMH_plugin->cls, + &wtid->raw, + sizeof (wtid->raw), + &handle_transaction_data, + &ctx); + if (GNUNET_SYSERR == ret) + { + GNUNET_break (0); + json_decref (ctx.deposits); + return TMH_RESPONSE_reply_internal_db_error (connection); + } + if (GNUNET_SYSERR == ctx.is_valid) + { + GNUNET_break (0); + json_decref (ctx.deposits); + return TMH_RESPONSE_reply_internal_db_error (connection); + } + if (GNUNET_NO == ctx.is_valid) + { + json_decref (ctx.deposits); + return TMH_RESPONSE_reply_arg_unknown (connection, + "wtid"); + } + return TMH_RESPONSE_reply_wire_deposit_details (connection, + &ctx.total, + &ctx.merchant_pub, + &ctx.h_wire, + ctx.deposits); +} + + /** * Closure for #handle_wtid_data. */ -struct DepositWtidContext +struct DepositWtidContext { /** @@ -1572,7 +1752,7 @@ struct DepositWtidContext /** * Function called with the results of the lookup of the * wire transfer identifier information. - * + * * @param cls our context for transmission * @param wtid base32-encoded wire transfer identifier, NULL * if the transaction was not yet done @@ -1590,16 +1770,19 @@ handle_wtid_data (void *cls, if (NULL == wtid) { - if (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us == + if (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us == execution_time.abs_value_us) ctx->res = TMH_RESPONSE_reply_deposit_unknown (ctx->connection); else - ctx->res = TMH_RESPONSE_reply_deposit_pending (ctx->connection); + ctx->res = TMH_RESPONSE_reply_deposit_pending (ctx->connection, + execution_time); } else { - ctx->res = TMH_RESPONSE_reply_deposit_wtid (ctx->connection); - } + ctx->res = TMH_RESPONSE_reply_deposit_wtid (ctx->connection, + wtid, + execution_time); + } } @@ -1627,6 +1810,7 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection, struct DepositWtidContext ctx; ctx.connection = connection; + ctx.res = MHD_NO; /* this value should never be read... */ ret = TMH_plugin->wire_lookup_deposit_wtid (TMH_plugin->cls, h_contract, h_wire, @@ -1634,7 +1818,7 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection, merchant_pub, transaction_id, &handle_wtid_data, - connection); + &ctx); if (GNUNET_SYSERR == ret) { GNUNET_break (0); diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h index d9adba2d..0ee3d050 100644 --- a/src/mint/taler-mint-httpd_db.h +++ b/src/mint/taler-mint-httpd_db.h @@ -192,6 +192,19 @@ TMH_DB_execute_admin_add_incoming (struct MHD_Connection *connection, json_t *wire); +/** + * Execute a "/wire/deposits". Returns the transaction information + * associated with the given wire transfer identifier. + * + * @param connection the MHD connection to handle + * @param wtid wire transfer identifier to resolve + * @return MHD result code + */ +int +TMH_DB_execute_wire_deposits (struct MHD_Connection *connection, + const struct TALER_WireTransferIdentifierP *wtid); + + /** * Execute a "/deposit/wtid". Returns the transfer information * associated with the given deposit. @@ -212,5 +225,6 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection, const struct TALER_MerchantPublicKeyP *merchant_pub, uint64_t transaction_id); + #endif /* TALER_MINT_HTTPD_DB_H */ diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index f3498b46..9a55d5aa 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -1056,15 +1056,15 @@ TMH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection, * 404 reply. * * @param connection connection to the client - * @param * @return MHD result code */ int -TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection, - ...) +TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection) { - GNUNET_break (0); // FIXME: not implemented - return MHD_NO; + return TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", "Deposit unknown"); } @@ -1073,15 +1073,17 @@ TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection, * we did not execute the deposit yet. Generate a 202 reply. * * @param connection connection to the client - * @param + * @param planned_exec_time planned execution time * @return MHD result code */ int TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection, - ...) + struct GNUNET_TIME_Absolute planned_exec_time) { - GNUNET_break (0); // FIXME: not implemented - return MHD_NO; + return TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_FOUND, + "{s:o}", + "execution_time", TALER_json_from_abs (planned_exec_time)); } @@ -1090,15 +1092,58 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection, * them. Generates the 200 reply. * * @param connection connection to the client - * @param + * @param wtid wire transfer identifier (as 0-terminated string) + * @param exec_time execution time of the wire transfer * @return MHD result code */ int TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection, - ...) + const char *wtid, + struct GNUNET_TIME_Absolute exec_time) +{ + struct TALER_ConfirmWirePS cw; + struct TALER_MintPublicKeyP pub; + struct TALER_MintSignatureP sig; + + cw.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_WIRE); + cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS)); + // FIXME: fill in rest of 'cw'! + TMH_KS_sign (&cw.purpose, + &pub, + &sig); + return TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_FOUND, + "{s:s, s:o, s:o, s:o}", + "wtid", wtid, + "execution_time", TALER_json_from_abs (exec_time), + "mint_sig", TALER_json_from_data (&sig, + sizeof (sig)), + "mint_pub", TALER_json_from_data (&pub, + sizeof (pub))); +} + + +/** + * A merchant asked for transaction details about a wire transfer. + * Provide them. Generates the 200 reply. + * + * @param connection connection to the client + * @param total total amount that was transferred + * @param merchant_pub public key of the merchant + * @param h_wire destination account + * @param deposits details about the combined deposits + * @return MHD result code + */ +int +TMH_RESPONSE_reply_wire_deposit_details (struct MHD_Connection *connection, + const struct TALER_Amount *total, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_wire, + json_t *deposits) { GNUNET_break (0); // FIXME: not implemented return MHD_NO; } + /* end of taler-mint-httpd_responses.c */ diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h index 5d1523b4..6debbc93 100644 --- a/src/mint/taler-mint-httpd_responses.h +++ b/src/mint/taler-mint-httpd_responses.h @@ -253,12 +253,10 @@ TMH_RESPONSE_reply_deposit_insufficient_funds (struct MHD_Connection *connection * 404 reply. * * @param connection connection to the client - * @param * @return MHD result code */ int -TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection, - ...); +TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection); /** @@ -266,12 +264,12 @@ TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection, * we did not execute the deposit yet. Generate a 202 reply. * * @param connection connection to the client - * @param + * @param planned_exec_time planned execution time * @return MHD result code */ int TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection, - ...); + struct GNUNET_TIME_Absolute planned_exec_time); /** @@ -279,12 +277,33 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection, * them. Generates the 200 reply. * * @param connection connection to the client - * @param + * @param wtid wire transfer identifier (as 0-terminated string) + * @param exec_time execution time of the wire transfer * @return MHD result code */ int TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection, - ...); + const char *wtid, + struct GNUNET_TIME_Absolute exec_time); + + +/** + * A merchant asked for transaction details about a wire transfer. + * Provide them. Generates the 200 reply. + * + * @param connection connection to the client + * @param total total amount that was transferred + * @param merchant_pub public key of the merchant + * @param h_wire destination account + * @param deposits details about the combined deposits + * @return MHD result code + */ +int +TMH_RESPONSE_reply_wire_deposit_details (struct MHD_Connection *connection, + const struct TALER_Amount *total, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_wire, + json_t *deposits); /** diff --git a/src/mint/taler-mint-httpd_tracking.c b/src/mint/taler-mint-httpd_tracking.c index 59d02942..e61b4bae 100644 --- a/src/mint/taler-mint-httpd_tracking.c +++ b/src/mint/taler-mint-httpd_tracking.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014, 2015, 2016 GNUnet e.V. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -46,8 +46,24 @@ TMH_TRACKING_handler_wire_deposits (struct TMH_RequestHandler *rh, const char *upload_data, size_t *upload_data_size) { - GNUNET_break (0); // not implemented - return MHD_NO; + struct TALER_WireTransferIdentifierP wtid; + int res; + + res = TMH_PARSE_mhd_request_arg_data (connection, + "wtid", + &wtid, + sizeof (struct TALER_WireTransferIdentifierP)); + if (GNUNET_SYSERR == res) + return MHD_NO; /* internal error */ + if (GNUNET_NO == res) + return MHD_YES; /* parse error */ + if (wtid.crc8 != + GNUNET_CRYPTO_crc8_n (&wtid.raw, + sizeof (wtid.raw))) + return TMH_RESPONSE_reply_arg_invalid (connection, + "wtid"); + return TMH_DB_execute_wire_deposits (connection, + &wtid); } @@ -57,7 +73,7 @@ TMH_TRACKING_handler_wire_deposits (struct TMH_RequestHandler *rh, * * @param connection the MHD connection to handle * @param tps signed request to execute - * @param merchant_pub public key from the merchant + * @param merchant_pub public key from the merchant * @param merchant_sig signature from the merchant (to be checked) * @param transaction_id transaction ID (in host byte order) * @return MHD result code diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 0f32cfb8..2ebc8f0f 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -3364,11 +3364,34 @@ postgres_get_coin_transactions (void *cls, } +/** + * Lookup the list of Taler transactions that was aggregated + * into a wire transfer by the respective @a raw_wtid. + * + * @param cls closure + * @param raw_wtid the raw wire transfer identifier we used + * @param raw_len number of bytes in @a raw_wtid (right now always 32) + * @param cb function to call on each transaction found + * @param cb_cls closure for @a cb + * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors + */ +static int +postgres_lookup_wire_transactions (void *cls, + const void *raw_wtid, + size_t raw_len, + TALER_MINTDB_TransactionDataCallback cb, + void *cb_cls) +{ + GNUNET_break (0); // not implemented! + return GNUNET_SYSERR; +} + + /** * Try to find the wire transfer details for a deposit operation. * If we did not execute the deposit yet, return when it is supposed * to be executed. - * + * * @param cls closure * @param h_contract hash of the contract * @param h_wire hash of merchant wire details @@ -3465,6 +3488,7 @@ libtaler_plugin_mintdb_postgres_init (void *cls) plugin->get_transfer = &postgres_get_transfer; plugin->get_coin_transactions = &postgres_get_coin_transactions; plugin->free_coin_transaction_list = &common_free_coin_transaction_list; + plugin->lookup_wire_transactions = &postgres_lookup_wire_transactions; plugin->wire_lookup_deposit_wtid = &postgres_wire_lookup_deposit_wtid; return plugin; } -- cgit v1.2.3 From 0bdae896e71118674c1d0fb27b15870c21480e72 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 17 Jan 2016 18:21:32 +0100 Subject: remark on TODO --- src/include/taler_signatures.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index ca8cf2de..ef9486e4 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -880,6 +880,7 @@ struct TALER_ConfirmWirePS */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + // FIXME: add details (#3888:10056) }; -- cgit v1.2.3 From 1eea4abe07cbcde11f67df58be08a4f59803e7f1 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 18 Jan 2016 22:52:47 +0100 Subject: remove useless check, this is a fixed-size array, thus pointer is never NULL --- src/mint-lib/mint_api_refresh.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/mint-lib/mint_api_refresh.c b/src/mint-lib/mint_api_refresh.c index a779bdbc..9effa3d4 100644 --- a/src/mint-lib/mint_api_refresh.c +++ b/src/mint-lib/mint_api_refresh.c @@ -334,12 +334,9 @@ free_melt_data (struct MeltData *md) for (i=0;ifresh_coins) - { - for (j=0;jnum_fresh_coins;j++) - free_fresh_coin (&md->fresh_coins[i][j]); - GNUNET_free (md->fresh_coins[i]); - } + for (j=0;jnum_fresh_coins;j++) + free_fresh_coin (&md->fresh_coins[i][j]); + GNUNET_free (md->fresh_coins[i]); } /* Finally, clean up a bit... (NOTE: compilers might optimize this away, so this is @@ -774,7 +771,7 @@ deserialize_melt_data (const char *buf, &buf[off], buf_size - off, &ok); - if (off != buf_size) + if (off != buf_size) { GNUNET_break (0); ok = GNUNET_NO; -- cgit v1.2.3 From 1f2dee5ae8c902e10c8e2a00b57147b2169275a3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 18 Jan 2016 22:56:12 +0100 Subject: check return value from TALER_amount_subtract() --- src/mint-lib/mint_api_deposit.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/mint-lib/mint_api_deposit.c b/src/mint-lib/mint_api_deposit.c index 8f7b6db0..58436adf 100644 --- a/src/mint-lib/mint_api_deposit.c +++ b/src/mint-lib/mint_api_deposit.c @@ -434,6 +434,14 @@ TALER_MINT_deposit (struct TALER_MINT_Handle *mint, TALER_LOG_WARNING ("Denomination key unknown to mint\n"); return NULL; } + if (GNUNET_SYSERR == + TALER_amount_subtract (&amount_without_fee, + amount, + &dki->fee_deposit)) + { + GNUNET_break (0); + return NULL; + } if (GNUNET_OK != verify_signatures (dki, @@ -492,9 +500,6 @@ TALER_MINT_deposit (struct TALER_MINT_Handle *mint, dh->depconf.transaction_id = GNUNET_htonll (transaction_id); dh->depconf.timestamp = GNUNET_TIME_absolute_hton (timestamp); dh->depconf.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); - TALER_amount_subtract (&amount_without_fee, - amount, - &dki->fee_deposit); TALER_amount_hton (&dh->depconf.amount_without_fee, &amount_without_fee); dh->depconf.coin_pub = *coin_pub; -- cgit v1.2.3 From 80f6c4240ee26884ac2dfdc02ff6b8aac0951777 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 19 Jan 2016 14:39:00 +0100 Subject: -fix (C) notices --- src/include/taler_amount_lib.h | 2 +- src/include/taler_crypto_lib.h | 2 +- src/include/taler_json_lib.h | 2 +- src/include/taler_mint_service.h | 2 +- src/include/taler_mintdb_lib.h | 2 +- src/include/taler_mintdb_plugin.h | 2 +- src/include/taler_pq_lib.h | 2 +- src/include/taler_signatures.h | 2 +- src/include/taler_util.h | 2 +- src/include/taler_util_wallet.h | 2 +- src/mint-lib/mint_api_admin.c | 2 +- src/mint-lib/mint_api_common.c | 2 +- src/mint-lib/mint_api_common.h | 2 +- src/mint-lib/mint_api_context.c | 2 +- src/mint-lib/mint_api_context.h | 2 +- src/mint-lib/mint_api_deposit.c | 2 +- src/mint-lib/mint_api_handle.c | 2 +- src/mint-lib/mint_api_handle.h | 2 +- src/mint-lib/mint_api_refresh.c | 2 +- src/mint-lib/mint_api_refresh_link.c | 2 +- src/mint-lib/mint_api_reserve.c | 2 +- src/mint-lib/mint_api_wire.c | 2 +- src/mint-lib/test_mint_api.c | 2 +- src/mint-tools/taler-auditor-sign.c | 2 +- src/mint-tools/taler-mint-dbinit.c | 2 +- src/mint-tools/taler-mint-keycheck.c | 2 +- src/mint-tools/taler-mint-keyup.c | 2 +- src/mint-tools/taler-mint-reservemod.c | 2 +- src/mint-tools/taler-mint-sepa.c | 2 +- src/mint/taler-mint-httpd_db.c | 2 +- src/mint/taler-mint-httpd_db.h | 2 +- src/mintdb/mintdb_keyio.c | 2 +- src/mintdb/mintdb_plugin.c | 2 +- src/mintdb/perf_taler_mintdb.c | 2 +- src/mintdb/perf_taler_mintdb_init.c | 2 +- src/mintdb/perf_taler_mintdb_init.h | 2 +- src/mintdb/perf_taler_mintdb_interpreter.c | 2 +- src/mintdb/perf_taler_mintdb_interpreter.h | 2 +- src/mintdb/perf_taler_mintdb_values.h | 2 +- src/mintdb/plugin_mintdb_common.c | 2 +- src/mintdb/plugin_mintdb_postgres.c | 2 +- src/mintdb/test_mintdb.c | 2 +- src/mintdb/test_mintdb_deposits.c | 2 +- src/mintdb/test_perf_taler_mintdb.c | 2 +- src/pq/db_pq.c | 2 +- src/pq/pq_helper.c | 2 +- src/pq/test_pq.c | 2 +- src/util/amount.c | 2 +- src/util/crypto.c | 2 +- src/util/json.c | 2 +- src/util/os_installation.c | 2 +- src/util/test_amount.c | 2 +- src/util/test_crypto.c | 2 +- src/util/test_json.c | 2 +- src/util/test_wireformats.c | 2 +- src/util/util.c | 2 +- src/util/wireformats.c | 2 +- 57 files changed, 57 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/include/taler_amount_lib.h b/src/include/taler_amount_lib.h index 8661ed91..094b96f7 100644 --- a/src/include/taler_amount_lib.h +++ b/src/include/taler_amount_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 87020fbb..20cb434f 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index 8bdcf270..d9fa0518 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h index 78650edf..a7b6afd1 100644 --- a/src/include/taler_mint_service.h +++ b/src/include/taler_mint_service.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software diff --git a/src/include/taler_mintdb_lib.h b/src/include/taler_mintdb_lib.h index 7dfef8dc..70e314d9 100644 --- a/src/include/taler_mintdb_lib.h +++ b/src/include/taler_mintdb_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index a4a94faa..90319b96 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h index 2fe66c52..e0ca429e 100644 --- a/src/include/taler_pq_lib.h +++ b/src/include/taler_pq_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index ef9486e4..ac011983 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015, 2016 GNUnet e.V. 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 diff --git a/src/include/taler_util.h b/src/include/taler_util.h index b6dd9596..38090181 100644 --- a/src/include/taler_util.h +++ b/src/include/taler_util.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/include/taler_util_wallet.h b/src/include/taler_util_wallet.h index 4699a469..87efcdda 100644 --- a/src/include/taler_util_wallet.h +++ b/src/include/taler_util_wallet.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_admin.c b/src/mint-lib/mint_api_admin.c index 472b02e7..641e0690 100644 --- a/src/mint-lib/mint_api_admin.c +++ b/src/mint-lib/mint_api_admin.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_common.c b/src/mint-lib/mint_api_common.c index fd85fbdc..faba38c7 100644 --- a/src/mint-lib/mint_api_common.c +++ b/src/mint-lib/mint_api_common.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_common.h b/src/mint-lib/mint_api_common.h index d256fa42..10a20214 100644 --- a/src/mint-lib/mint_api_common.h +++ b/src/mint-lib/mint_api_common.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_context.c b/src/mint-lib/mint_api_context.c index e60e01b0..3a86efb6 100644 --- a/src/mint-lib/mint_api_context.c +++ b/src/mint-lib/mint_api_context.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_context.h b/src/mint-lib/mint_api_context.h index 79613cc8..181a4808 100644 --- a/src/mint-lib/mint_api_context.h +++ b/src/mint-lib/mint_api_context.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_deposit.c b/src/mint-lib/mint_api_deposit.c index 58436adf..40037292 100644 --- a/src/mint-lib/mint_api_deposit.c +++ b/src/mint-lib/mint_api_deposit.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_handle.c b/src/mint-lib/mint_api_handle.c index 49960cb1..077e42c2 100644 --- a/src/mint-lib/mint_api_handle.c +++ b/src/mint-lib/mint_api_handle.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_handle.h b/src/mint-lib/mint_api_handle.h index fae30a30..0dae58db 100644 --- a/src/mint-lib/mint_api_handle.h +++ b/src/mint-lib/mint_api_handle.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_refresh.c b/src/mint-lib/mint_api_refresh.c index 9effa3d4..cea16b15 100644 --- a/src/mint-lib/mint_api_refresh.c +++ b/src/mint-lib/mint_api_refresh.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_refresh_link.c b/src/mint-lib/mint_api_refresh_link.c index d4060bd1..17c96039 100644 --- a/src/mint-lib/mint_api_refresh_link.c +++ b/src/mint-lib/mint_api_refresh_link.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_reserve.c b/src/mint-lib/mint_api_reserve.c index a726eca4..1f8140cf 100644 --- a/src/mint-lib/mint_api_reserve.c +++ b/src/mint-lib/mint_api_reserve.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/mint_api_wire.c b/src/mint-lib/mint_api_wire.c index 5fc82f72..0947354a 100644 --- a/src/mint-lib/mint_api_wire.c +++ b/src/mint-lib/mint_api_wire.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index 024e080f..0e3b2bed 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-tools/taler-auditor-sign.c b/src/mint-tools/taler-auditor-sign.c index bd37e68d..7e6d3b12 100644 --- a/src/mint-tools/taler-auditor-sign.c +++ b/src/mint-tools/taler-auditor-sign.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-tools/taler-mint-dbinit.c b/src/mint-tools/taler-mint-dbinit.c index 3293a69a..2d9f7776 100644 --- a/src/mint-tools/taler-mint-dbinit.c +++ b/src/mint-tools/taler-mint-dbinit.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-tools/taler-mint-keycheck.c b/src/mint-tools/taler-mint-keycheck.c index c5ac86cb..4fa2707f 100644 --- a/src/mint-tools/taler-mint-keycheck.c +++ b/src/mint-tools/taler-mint-keycheck.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-tools/taler-mint-keyup.c b/src/mint-tools/taler-mint-keyup.c index e2c8d798..b82554b9 100644 --- a/src/mint-tools/taler-mint-keyup.c +++ b/src/mint-tools/taler-mint-keyup.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-tools/taler-mint-reservemod.c b/src/mint-tools/taler-mint-reservemod.c index 11d67ed5..38d27054 100644 --- a/src/mint-tools/taler-mint-reservemod.c +++ b/src/mint-tools/taler-mint-reservemod.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint-tools/taler-mint-sepa.c b/src/mint-tools/taler-mint-sepa.c index 21f4af9c..e66db541 100644 --- a/src/mint-tools/taler-mint-sepa.c +++ b/src/mint-tools/taler-mint-sepa.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2015 GNUnet e.V. 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 diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 1b0af585..14e03dfd 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h index 0ee3d050..e366112d 100644 --- a/src/mint/taler-mint-httpd_db.h +++ b/src/mint/taler-mint-httpd_db.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mintdb/mintdb_keyio.c b/src/mintdb/mintdb_keyio.c index 5bfe5bb1..89ac9055 100644 --- a/src/mintdb/mintdb_keyio.c +++ b/src/mintdb/mintdb_keyio.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mintdb/mintdb_plugin.c b/src/mintdb/mintdb_plugin.c index 2e8d206f..f1b2ad04 100644 --- a/src/mintdb/mintdb_plugin.c +++ b/src/mintdb/mintdb_plugin.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2015 GNUnet e.V. 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 diff --git a/src/mintdb/perf_taler_mintdb.c b/src/mintdb/perf_taler_mintdb.c index 73708a6c..fbaa2ce3 100644 --- a/src/mintdb/perf_taler_mintdb.c +++ b/src/mintdb/perf_taler_mintdb.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mintdb/perf_taler_mintdb_init.c b/src/mintdb/perf_taler_mintdb_init.c index 141ac1ab..97a1b4c9 100644 --- a/src/mintdb/perf_taler_mintdb_init.c +++ b/src/mintdb/perf_taler_mintdb_init.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mintdb/perf_taler_mintdb_init.h b/src/mintdb/perf_taler_mintdb_init.h index 063259f5..f94beef1 100644 --- a/src/mintdb/perf_taler_mintdb_init.h +++ b/src/mintdb/perf_taler_mintdb_init.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mintdb/perf_taler_mintdb_interpreter.c b/src/mintdb/perf_taler_mintdb_interpreter.c index 9084513f..293d5f35 100644 --- a/src/mintdb/perf_taler_mintdb_interpreter.c +++ b/src/mintdb/perf_taler_mintdb_interpreter.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mintdb/perf_taler_mintdb_interpreter.h b/src/mintdb/perf_taler_mintdb_interpreter.h index 722d1a07..3510e3dd 100644 --- a/src/mintdb/perf_taler_mintdb_interpreter.h +++ b/src/mintdb/perf_taler_mintdb_interpreter.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mintdb/perf_taler_mintdb_values.h b/src/mintdb/perf_taler_mintdb_values.h index 25202edd..35f87f5b 100644 --- a/src/mintdb/perf_taler_mintdb_values.h +++ b/src/mintdb/perf_taler_mintdb_values.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mintdb/plugin_mintdb_common.c b/src/mintdb/plugin_mintdb_common.c index bbe104f6..1f2fdc58 100644 --- a/src/mintdb/plugin_mintdb_common.c +++ b/src/mintdb/plugin_mintdb_common.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2015 GNUnet e.V. 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 diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 2ebc8f0f..057ea9fe 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mintdb/test_mintdb.c b/src/mintdb/test_mintdb.c index 5b9d1c2e..0cd660a4 100644 --- a/src/mintdb/test_mintdb.c +++ b/src/mintdb/test_mintdb.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/mintdb/test_mintdb_deposits.c b/src/mintdb/test_mintdb_deposits.c index 171fa2c6..3ce0a35a 100644 --- a/src/mintdb/test_mintdb_deposits.c +++ b/src/mintdb/test_mintdb_deposits.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014 Christian Grothoff (and other contributing authors) + Copyright (C) 2014 GNUnet e.V. 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 diff --git a/src/mintdb/test_perf_taler_mintdb.c b/src/mintdb/test_perf_taler_mintdb.c index 2830ef81..789a0dd4 100644 --- a/src/mintdb/test_perf_taler_mintdb.c +++ b/src/mintdb/test_perf_taler_mintdb.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/pq/db_pq.c b/src/pq/db_pq.c index e1a082e4..59a1db02 100644 --- a/src/pq/db_pq.c +++ b/src/pq/db_pq.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/pq/pq_helper.c b/src/pq/pq_helper.c index bd54447e..d245ffdb 100644 --- a/src/pq/pq_helper.c +++ b/src/pq/pq_helper.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c index bda22758..f0413d1f 100644 --- a/src/pq/test_pq.c +++ b/src/pq/test_pq.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2015 Christian Grothoff (and other contributing authors) + (C) 2015 GNUnet e.V. 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 diff --git a/src/util/amount.c b/src/util/amount.c index 7260b150..dc2d2e40 100644 --- a/src/util/amount.c +++ b/src/util/amount.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014 Christian Grothoff (and other contributing authors) + Copyright (C) 2014 GNUnet e.V. 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 diff --git a/src/util/crypto.c b/src/util/crypto.c index ebf6413d..9e689056 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/util/json.c b/src/util/json.c index cea0118e..6aca7548 100644 --- a/src/util/json.c +++ b/src/util/json.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 diff --git a/src/util/os_installation.c b/src/util/os_installation.c index ec1868a8..0eab118f 100644 --- a/src/util/os_installation.c +++ b/src/util/os_installation.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2006-2014 Christian Grothoff (and other contributing authors) + Copyright (C) 2006-2014 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published diff --git a/src/util/test_amount.c b/src/util/test_amount.c index 4741bcf3..3f33334b 100644 --- a/src/util/test_amount.c +++ b/src/util/test_amount.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2015 Christian Grothoff (and other contributing authors) + (C) 2015 GNUnet e.V. 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 diff --git a/src/util/test_crypto.c b/src/util/test_crypto.c index a5313195..59acd781 100644 --- a/src/util/test_crypto.c +++ b/src/util/test_crypto.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2015 Christian Grothoff (and other contributing authors) + (C) 2015 GNUnet e.V. 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 diff --git a/src/util/test_json.c b/src/util/test_json.c index c48fe68b..5e2f50fe 100644 --- a/src/util/test_json.c +++ b/src/util/test_json.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2015 Christian Grothoff (and other contributing authors) + (C) 2015 GNUnet e.V. 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 diff --git a/src/util/test_wireformats.c b/src/util/test_wireformats.c index 7e0e5bbc..b41abb80 100644 --- a/src/util/test_wireformats.c +++ b/src/util/test_wireformats.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2014 Christian Grothoff (and other contributing authors) + (C) 2014 GNUnet e.V. 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 diff --git a/src/util/util.c b/src/util/util.c index 08438cfa..addafacf 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014 Christian Grothoff (and other contributing authors) + Copyright (C) 2014 GNUnet e.V. 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 diff --git a/src/util/wireformats.c b/src/util/wireformats.c index dab7fb92..cd5a9c3d 100644 --- a/src/util/wireformats.c +++ b/src/util/wireformats.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 GNUnet e.V. 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 -- cgit v1.2.3 From a64b6053770339f3ae2de4f779bad42c7109cb0d Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 20 Jan 2016 16:21:32 +0100 Subject: indent, etc. --- src/mint-lib/mint_api_refresh_link.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/mint-lib/mint_api_refresh_link.c b/src/mint-lib/mint_api_refresh_link.c index d4060bd1..c8793259 100644 --- a/src/mint-lib/mint_api_refresh_link.c +++ b/src/mint-lib/mint_api_refresh_link.c @@ -194,7 +194,7 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh, }; if (GNUNET_OK != - MAJ_parse_json (json_array_get (json, + MAJ_parse_json (json_array_get (json, session), spec)) { @@ -218,7 +218,7 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh, struct TALER_CoinSpendPrivateKeyP coin_privs[num_coins]; struct TALER_DenominationSignature sigs[num_coins]; struct TALER_DenominationPublicKey pubs[num_coins]; - + off_coin = 0; for (session=0;sessionlink_cb (rlh->link_cb_cls, -- cgit v1.2.3 From d2917889767cf71ae0ad8d79ebe62ccf6b9a350e Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 20 Jan 2016 18:03:44 +0100 Subject: spelling out what information mint should sign for TALER_SIGNATURE_MINT_CONFIRM_WIRE --- src/include/taler_crypto_lib.h | 33 ++++++++++++++++++----- src/include/taler_signatures.h | 60 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 20cb434f..36ca6a02 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -429,13 +429,15 @@ struct TALER_RefreshLinkDecrypted /** - * Binary information encoded in Crockford's Base32 in wire transfer - * subjects of transfers from Taler to a merchant. The actual value - * is chosen by the mint and has no particular semantics, other than - * being unique so that the mint can lookup details about the wire - * transfer when needed. + * Length of the raw value in the Taler wire transfer identifier + * (in binary representation). */ -struct TALER_WireTransferIdentifierP +#define TALER_WIRE_TRANSFER_IDENTIFIER_LEN 32 + +/** + * Raw value of a wire transfer subjects, without the checksum. + */ +struct TALER_WireTransferIdentifierRawP { /** @@ -450,7 +452,24 @@ struct TALER_WireTransferIdentifierP * encode the actual value (i.e. a 256-bit / 32-byte public key or * a hash code), and the last byte for a minimalistic checksum. */ - uint8_t raw[32]; + uint8_t raw[TALER_WIRE_TRANSFER_IDENTIFIER_LEN]; +}; + + +/** + * Binary information encoded in Crockford's Base32 in wire transfer + * subjects of transfers from Taler to a merchant. The actual value + * is chosen by the mint and has no particular semantics, other than + * being unique so that the mint can lookup details about the wire + * transfer when needed. + */ +struct TALER_WireTransferIdentifierP +{ + + /** + * Raw value. + */ + struct TALER_WireTransferIdentifierRawP raw; /** * Checksum using CRC8 over the @e raw data. diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index ac011983..62e602a0 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -880,7 +880,65 @@ struct TALER_ConfirmWirePS */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - // FIXME: add details (#3888:10056) + /** + * Hash over the wiring information of the merchant. + */ + struct GNUNET_HashCode h_wire GNUNET_PACKED; + + /** + * Hash over the contract for which this deposit is made. + */ + struct GNUNET_HashCode h_contract GNUNET_PACKED; + + /** + * Raw value (binary encoding) of the wire transfer subject. + */ + struct TALER_WireTransferIdentifierRawP raw; + + /** + * The coin's public key. This is the value that must have been + * signed (blindly) by the Mint. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * Merchant-generated transaction ID to detect duplicate + * transactions. The merchant must communicate a merchant-unique ID + * to the customer for each transaction. Note that different coins + * that are part of the same transaction can use the same + * transaction ID. The transaction ID is useful for later disputes, + * and the merchant's contract offer (@e h_contract) with the + * customer should include the offer's term and transaction ID + * signed with a key from the merchant. + */ + uint64_t transaction_id GNUNET_PACKED; + + /** + * When did the mint execute this transfer? Note that the + * timestamp may not be exactly the same on the wire, i.e. + * because the wire has a different timezone or resolution. + */ + struct GNUNET_TIME_AbsoluteNBO execution_time; + + /** + * The contribution of @e coin_pub to the total transfer volume. + * This is the value of the deposit minus the fee. + */ + struct TALER_AmountNBO coin_contribution; + + /** + * The total amount the mint transferred in the transaction. + * Note that we may be aggregating multiple coin's @e coin_contribution + * values into a single wire transfer, so this value may be larger + * than that of @e coin_contribution. It may also be smaller, as + * @e coin_contribution may be say "1.123456" but the wire unit may + * be rounded down, i.e. to "1.12" (depending on the transfer method). + * + * Note that the mint books the deltas from rounding down as profit, + * so aggregating transfers is a good thing for the merchant (as it + * reduces rounding down expenses). + */ + struct TALER_AmountNBO total_amount; }; -- cgit v1.2.3 From 43e04f2ad105ff4712697b3480bbb75330f69ad3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 20 Jan 2016 18:50:19 +0100 Subject: work on #3888 --- src/include/taler_mintdb_plugin.h | 15 +++++---- src/include/taler_signatures.h | 16 +++++----- src/mint/taler-mint-httpd_db.c | 40 ++++++++++++++++++++++-- src/mint/taler-mint-httpd_responses.c | 59 ++++++++++++++++++++++++++++++----- src/mint/taler-mint-httpd_responses.h | 14 +++++++-- src/mintdb/plugin_mintdb_postgres.c | 8 ++--- 6 files changed, 120 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 90319b96..9e4f891c 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -573,8 +573,11 @@ typedef void * wire transfer identifier information. * * @param cls closure - * @param wtid base32-encoded wire transfer identifier, NULL + * @param wtid wire transfer identifier, NULL * if the transaction was not yet done + * @param coin_contribution how much did the coin we asked about + * contribute to the total transfer value? (deposit value minus fee) + * @param total_amount how much was the total wire transfer? * @param execution_time when was the transaction done, or * when we expect it to be done (if @a wtid was NULL); * #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown @@ -582,7 +585,9 @@ typedef void */ typedef void (*TALER_MINTDB_DepositWtidCallback)(void *cls, - const char *wtid, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *total_amount, struct GNUNET_TIME_Absolute execution_time); @@ -1224,16 +1229,14 @@ struct TALER_MINTDB_Plugin * into a wire transfer by the respective @a raw_wtid. * * @param cls the @e cls of this struct with the plugin-specific state - * @param raw_wtid the raw wire transfer identifier we used - * @param raw_len number of bytes in @a raw_wtid (right now always 32) + * @param wtid the raw wire transfer identifier we used * @param cb function to call on each transaction found * @param cb_cls closure for @a cb * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors */ int (*lookup_wire_transactions) (void *cls, - const void *raw_wtid, - size_t raw_len, + const struct TALER_WireTransferIdentifierRawP *wtid, TALER_MINTDB_TransactionDataCallback cb, void *cb_cls); diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 62e602a0..29008fc9 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -893,7 +893,7 @@ struct TALER_ConfirmWirePS /** * Raw value (binary encoding) of the wire transfer subject. */ - struct TALER_WireTransferIdentifierRawP raw; + struct TALER_WireTransferIdentifierRawP wtid; /** * The coin's public key. This is the value that must have been @@ -903,13 +903,13 @@ struct TALER_ConfirmWirePS /** * Merchant-generated transaction ID to detect duplicate - * transactions. The merchant must communicate a merchant-unique ID - * to the customer for each transaction. Note that different coins - * that are part of the same transaction can use the same - * transaction ID. The transaction ID is useful for later disputes, - * and the merchant's contract offer (@e h_contract) with the - * customer should include the offer's term and transaction ID - * signed with a key from the merchant. + * transactions, in big endian. The merchant must communicate a + * merchant-unique ID to the customer for each transaction. Note + * that different coins that are part of the same transaction can + * use the same transaction ID. The transaction ID is useful for + * later disputes, and the merchant's contract offer (@e h_contract) + * with the customer should include the offer's term and transaction + * ID signed with a key from the merchant. */ uint64_t transaction_id GNUNET_PACKED; diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 14e03dfd..d31dcd2b 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -1702,7 +1702,6 @@ TMH_DB_execute_wire_deposits (struct MHD_Connection *connection, ctx.deposits = json_array (); ret = TMH_plugin->lookup_wire_transactions (TMH_plugin->cls, &wtid->raw, - sizeof (wtid->raw), &handle_transaction_data, &ctx); if (GNUNET_SYSERR == ret) @@ -1742,6 +1741,26 @@ struct DepositWtidContext */ struct MHD_Connection *connection; + /** + * Hash of the contract we are looking up. + */ + struct GNUNET_HashCode h_contract; + + /** + * Hash of the wire transfer details we are looking up. + */ + struct GNUNET_HashCode h_wire; + + /** + * Public key we are looking up. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * Transaction ID we are looking up. + */ + uint64_t transaction_id; + /** * MHD result code to return. */ @@ -1754,8 +1773,11 @@ struct DepositWtidContext * wire transfer identifier information. * * @param cls our context for transmission - * @param wtid base32-encoded wire transfer identifier, NULL + * @param wtid raw wire transfer identifier, NULL * if the transaction was not yet done + * @param coin_contribution how much did the coin we asked about + * contribute to the total transfer value? (deposit value minus fee) + * @param total_amount how much was the total wire transfer? * @param execution_time when was the transaction done, or * when we expect it to be done (if @a wtid was NULL); * #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown @@ -1763,7 +1785,9 @@ struct DepositWtidContext */ static void handle_wtid_data (void *cls, - const char *wtid, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *total_amount, struct GNUNET_TIME_Absolute execution_time) { struct DepositWtidContext *ctx = cls; @@ -1780,6 +1804,12 @@ handle_wtid_data (void *cls, else { ctx->res = TMH_RESPONSE_reply_deposit_wtid (ctx->connection, + &ctx->h_contract, + &ctx->h_wire, + &ctx->coin_pub, + coin_contribution, + total_amount, + ctx->transaction_id, wtid, execution_time); } @@ -1810,6 +1840,10 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection, struct DepositWtidContext ctx; ctx.connection = connection; + ctx.h_contract = *h_contract; + ctx.h_wire = *h_wire; + ctx.coin_pub = *coin_pub; + ctx.transaction_id = transaction_id; ctx.res = MHD_NO; /* this value should never be read... */ ret = TMH_plugin->wire_lookup_deposit_wtid (TMH_plugin->cls, h_contract, diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index 9a55d5aa..d89b74c8 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -1092,34 +1092,69 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection, * them. Generates the 200 reply. * * @param connection connection to the client - * @param wtid wire transfer identifier (as 0-terminated string) + * @param h_contract hash of the contract + * @param h_wire hash of wire account details + * @param coin_pub public key of the coin + * @param coin_contribution how much did the coin we asked about + * contribute to the total transfer value? (deposit value minus fee) + * @param total_amount how much was the total wire transfer? + * @param transaction_id merchant transaction identifier + * @param wtid raw wire transfer identifier * @param exec_time execution time of the wire transfer * @return MHD result code */ int TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection, - const char *wtid, + const struct GNUNET_HashCode *h_contract, + const struct GNUNET_HashCode *h_wire, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *total_amount, + uint64_t transaction_id, + const struct TALER_WireTransferIdentifierRawP *wtid, struct GNUNET_TIME_Absolute exec_time) { struct TALER_ConfirmWirePS cw; struct TALER_MintPublicKeyP pub; struct TALER_MintSignatureP sig; + struct TALER_WireTransferIdentifierP wtid_crc; + char *wtid_s; + int ret; + /* Create signature for the reply */ cw.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_WIRE); cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS)); - // FIXME: fill in rest of 'cw'! + cw.h_wire = *h_wire; + cw.h_contract = *h_contract; + cw.wtid = *wtid; + cw.coin_pub = *coin_pub; + cw.transaction_id = GNUNET_htonll (transaction_id); + cw.execution_time = GNUNET_TIME_absolute_hton (exec_time); + TALER_amount_hton (&cw.coin_contribution, + coin_contribution); + TALER_amount_hton (&cw.total_amount, + total_amount); TMH_KS_sign (&cw.purpose, &pub, &sig); - return TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_FOUND, + /* Compute checksum and crockford encoding if wire transfer subject */ + wtid_crc.raw = *wtid; + wtid_crc.crc8 = GNUNET_CRYPTO_crc8_n (wtid, + sizeof (struct TALER_WireTransferIdentifierRawP)); + + wtid_s = GNUNET_STRINGS_data_to_string_alloc (&wtid_crc, + sizeof (wtid_crc)); + ret = TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_OK, "{s:s, s:o, s:o, s:o}", - "wtid", wtid, + "wtid", wtid_s, "execution_time", TALER_json_from_abs (exec_time), "mint_sig", TALER_json_from_data (&sig, sizeof (sig)), "mint_pub", TALER_json_from_data (&pub, sizeof (pub))); + GNUNET_free (wtid_s); + return ret; } @@ -1141,8 +1176,16 @@ TMH_RESPONSE_reply_wire_deposit_details (struct MHD_Connection *connection, const struct GNUNET_HashCode *h_wire, json_t *deposits) { - GNUNET_break (0); // FIXME: not implemented - return MHD_NO; + /* FIXME: #4135: signing not implemented here */ + return TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:o, s:o, s:o, s:o}", + "total", TALER_json_from_amount (total), + "merchant_pub", TALER_json_from_data (merchant_pub, + sizeof (struct TALER_MerchantPublicKeyP)), + "h_wire", TALER_json_from_data (h_wire, + sizeof (struct GNUNET_HashCode)), + "deposits", deposits); } diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h index 6debbc93..caad2904 100644 --- a/src/mint/taler-mint-httpd_responses.h +++ b/src/mint/taler-mint-httpd_responses.h @@ -277,13 +277,23 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection, * them. Generates the 200 reply. * * @param connection connection to the client - * @param wtid wire transfer identifier (as 0-terminated string) + * @param h_contract hash of the contract + * @param h_wire hash of wire account details + * @param coin_pub public key of the coin + * @param transaction_id merchant transaction identifier + * @param wtid raw wire transfer identifier * @param exec_time execution time of the wire transfer * @return MHD result code */ int TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection, - const char *wtid, + const struct GNUNET_HashCode *h_contract, + const struct GNUNET_HashCode *h_wire, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *total_amount, + uint64_t transaction_id, + const struct TALER_WireTransferIdentifierRawP *wtid, struct GNUNET_TIME_Absolute exec_time); diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 057ea9fe..ad0c9115 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -3366,19 +3366,17 @@ postgres_get_coin_transactions (void *cls, /** * Lookup the list of Taler transactions that was aggregated - * into a wire transfer by the respective @a raw_wtid. + * into a wire transfer by the respective @a wtid. * * @param cls closure - * @param raw_wtid the raw wire transfer identifier we used - * @param raw_len number of bytes in @a raw_wtid (right now always 32) + * @param wtid the raw wire transfer identifier we used * @param cb function to call on each transaction found * @param cb_cls closure for @a cb * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors */ static int postgres_lookup_wire_transactions (void *cls, - const void *raw_wtid, - size_t raw_len, + const struct TALER_WireTransferIdentifierRawP *wtid, TALER_MINTDB_TransactionDataCallback cb, void *cb_cls) { -- cgit v1.2.3 From 6d80541f70d071e6d4d98d2d30aa04d959431cda Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 20 Jan 2016 18:51:45 +0100 Subject: bump year --- src/mint/taler-mint-httpd_responses.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index d89b74c8..98c36283 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014, 2015, 2016 GNUnet e.V. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software -- cgit v1.2.3 From 8f071e2200f0421caf1969c71a16ed62b3356fb5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Jan 2016 08:44:45 +0100 Subject: adding aggregation_tracking table to postgres plugin --- src/include/taler_crypto_lib.h | 5 ++ src/include/taler_mintdb_plugin.h | 29 ++++++++- src/mintdb/plugin_mintdb_postgres.c | 119 ++++++++++++++++++++++++++++++++++-- 3 files changed, 148 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 36ca6a02..6056270f 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -434,6 +434,11 @@ struct TALER_RefreshLinkDecrypted */ #define TALER_WIRE_TRANSFER_IDENTIFIER_LEN 32 +/** + * #TALER_WIRE_TRANSFER_IDENTIFIER_LEN as a string. + */ +#define TALER_WIRE_TRANSFER_IDENTIFIER_LEN_STR "32" + /** * Raw value of a wire transfer subjects, without the checksum. */ diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 9e4f891c..baf58788 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014, 2015, 2016 GNUnet e.V. 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 @@ -1266,6 +1266,33 @@ struct TALER_MINTDB_Plugin TALER_MINTDB_DepositWtidCallback cb, void *cb_cls); + + /** + * Function called to insert aggregation information into the DB. + * + * @param cls closure + * @param wtid the raw wire transfer identifier we used + * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) + * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls) + * @param h_contract which contract was this payment about + * @param transaction_id merchant's transaction ID for the payment + * @param coin_pub which public key was this payment about + * @param deposit_value amount contributed by this coin in total + * @param deposit_fee deposit fee charged by mint for this coin + * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors + */ + int + (*insert_aggregation_tracking)(void *cls, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_wire, + const struct GNUNET_HashCode *h_contract, + uint64_t transaction_id, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *deposit_value, + const struct TALER_Amount *deposit_fee); + + }; diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index ad0c9115..ca8edcff 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014, 2015, 2016 GNUnet e.V. 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 @@ -445,9 +445,34 @@ postgres_create_tables (void *cls, ",coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)" ",wire TEXT NOT NULL" ")"); - /* Index for get_deposit statement on coin_pub, transactiojn_id and merchant_pub */ + /* Index for get_deposit statement on coin_pub, transaction_id and merchant_pub */ SQLEXEC_INDEX("CREATE INDEX deposits_coin_pub_index " "ON deposits(coin_pub, transaction_id, merchant_pub)"); + /* Table for the tracking API, mapping from wire transfer identifiers + to transactions and back */ + SQLEXEC("CREATE TABLE IF NOT EXISTS aggregation_tracking " + "(h_contract BYTEA PRIMARY KEY CHECK (LENGTH(h_contract)=64)" + ",h_wire BYTEA PRIMARY KEY CHECK (LENGTH(h_wire)=64)" + ",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)" + ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)" + ",transaction_id INT8 NOT NULL" + ",wtid_raw BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=" TALER_WIRE_TRANSFER_IDENTIFIER_LEN_STR ")" + ",execution_time INT8 NOT NULL" + ",coin_amount_val INT8 NOT NULL" + ",coin_amount_frac INT4 NOT NULL" + ",coin_amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" + ",transaction_total_val INT8 NOT NULL" + ",transaction_total_frac INT4 NOT NULL" + ",transaction_total_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" + ")"); + /* Index for lookup_transactions statement on wtid */ + SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index " + "ON aggregation_tracking(wtid_raw)"); + /* Index for lookup_deposit_wtid statement */ + SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_deposit_index " + "ON aggregation_tracking(coin_pub,h_contract,h_wire,transaction_id,merchant_pub)"); + + #undef SQLEXEC #undef SQLEXEC_INDEX @@ -947,6 +972,63 @@ postgres_prepare (PGconn *db_conn) " AND rm.oldcoin_index = rcl.oldcoin_index" " AND rcl.cnc_index=rs.noreveal_index", 1, NULL); + /* Used in #postgres_lookup_wire_transactions */ + PREPARE ("lookup_transactions", + "SELECT" + " h_contract" + ",h_wire" + ",coin_pub" + ",merchant_pub" + ",transaction_id" + ",execution_time" + ",coin_amount_val" + ",coin_amount_frac" + ",coin_amount_curr" + ",transaction_total_val" + ",transaction_total_frac" + ",transaction_total_curr" + " FROM aggregation_tracking" + " WHERE wtid_raw=$1", + 1, NULL); + /* Used in #postgres_wire_lookup_deposit_wtid */ + PREPARE ("lookup_deposit_wtid", + "SELECT" + " wtid_raw" + ",execution_time" + ",coin_amount_val" + ",coin_amount_frac" + ",coin_amount_curr" + ",transaction_total_val" + ",transaction_total_frac" + ",transaction_total_curr" + " FROM aggregation_tracking" + " WHERE" + " coin_pub=$1 AND" + " h_contract=$2 AND" + " h_wire=$3 AND" + " transaction_id=$4 AND" + " merchant_pub=$5", + 5, NULL); + /* Used in #postgres_insert_aggregation_tracking */ + PREPARE ("insert_aggregation_tracking", + "INSERT INTO aggregation_tracking " + "(h_contract" + ",h_wire" + ",coin_pub" + ",merchant_pub" + ",transaction_id" + ",wtid_raw" + ",execution_time" + ",coin_amount_val" + ",coin_amount_frac" + ",coin_amount_curr" + ",transaction_total_val" + ",transaction_total_frac" + ",transaction_total_curr" + ") VALUES " + "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)", + 13, NULL); + return GNUNET_OK; #undef PREPARE } @@ -3415,6 +3497,36 @@ postgres_wire_lookup_deposit_wtid (void *cls, } +/** + * Function called to insert aggregation information into the DB. + * + * @param cls closure + * @param wtid the raw wire transfer identifier we used + * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) + * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls) + * @param h_contract which contract was this payment about + * @param transaction_id merchant's transaction ID for the payment + * @param coin_pub which public key was this payment about + * @param deposit_value amount contributed by this coin in total + * @param deposit_fee deposit fee charged by mint for this coin + * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors + */ +static int +postgres_insert_aggregation_tracking (void *cls, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_wire, + const struct GNUNET_HashCode *h_contract, + uint64_t transaction_id, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *deposit_value, + const struct TALER_Amount *deposit_fee) +{ + GNUNET_break (0); // not implemented + return GNUNET_SYSERR; +} + + /** * Initialize Postgres database subsystem. * @@ -3466,7 +3578,6 @@ libtaler_plugin_mintdb_postgres_init (void *cls) plugin->have_deposit = &postgres_have_deposit; plugin->iterate_deposits = &postgres_iterate_deposits; plugin->insert_deposit = &postgres_insert_deposit; - plugin->get_refresh_session = &postgres_get_refresh_session; plugin->create_refresh_session = &postgres_create_refresh_session; plugin->insert_refresh_melt = &postgres_insert_refresh_melt; @@ -3477,7 +3588,6 @@ libtaler_plugin_mintdb_postgres_init (void *cls) plugin->get_refresh_commit_coins = &postgres_get_refresh_commit_coins; plugin->insert_refresh_commit_links = &postgres_insert_refresh_commit_links; plugin->get_refresh_commit_links = &postgres_get_refresh_commit_links; - plugin->get_melt_commitment = &postgres_get_melt_commitment; plugin->free_melt_commitment = &common_free_melt_commitment; plugin->insert_refresh_out = &postgres_insert_refresh_out; @@ -3488,6 +3598,7 @@ libtaler_plugin_mintdb_postgres_init (void *cls) plugin->free_coin_transaction_list = &common_free_coin_transaction_list; plugin->lookup_wire_transactions = &postgres_lookup_wire_transactions; plugin->wire_lookup_deposit_wtid = &postgres_wire_lookup_deposit_wtid; + plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking; return plugin; } -- cgit v1.2.3 From 9ccba0e77f1c388bbfb7e6bea1b650797d57d023 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Jan 2016 11:24:18 +0100 Subject: implementing insert function into aggregation table --- src/include/taler_mintdb_plugin.h | 16 ++++++++++---- src/mint-lib/mint_api_context.c | 2 +- src/mint/taler-mint-httpd_db.c | 16 ++++++++++++++ src/mintdb/plugin_mintdb_postgres.c | 43 +++++++++++++++++++++++++++++++++++-- 4 files changed, 70 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index baf58788..2ffde228 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -1229,6 +1229,7 @@ struct TALER_MINTDB_Plugin * into a wire transfer by the respective @a raw_wtid. * * @param cls the @e cls of this struct with the plugin-specific state + * @param session database connection * @param wtid the raw wire transfer identifier we used * @param cb function to call on each transaction found * @param cb_cls closure for @a cb @@ -1236,6 +1237,7 @@ struct TALER_MINTDB_Plugin */ int (*lookup_wire_transactions) (void *cls, + struct TALER_MINTDB_Session *session, const struct TALER_WireTransferIdentifierRawP *wtid, TALER_MINTDB_TransactionDataCallback cb, void *cb_cls); @@ -1247,6 +1249,7 @@ struct TALER_MINTDB_Plugin * to be executed. * * @param cls closure + * @param session database connection * @param h_contract hash of the contract * @param h_wire hash of merchant wire details * @param coin_pub public key of deposited coin @@ -1258,6 +1261,7 @@ struct TALER_MINTDB_Plugin */ int (*wire_lookup_deposit_wtid)(void *cls, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *h_contract, const struct GNUNET_HashCode *h_wire, const struct TALER_CoinSpendPublicKeyP *coin_pub, @@ -1271,26 +1275,30 @@ struct TALER_MINTDB_Plugin * Function called to insert aggregation information into the DB. * * @param cls closure + * @param session database connection * @param wtid the raw wire transfer identifier we used * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls) * @param h_contract which contract was this payment about * @param transaction_id merchant's transaction ID for the payment + * @param execution_time when did we execute the transaction * @param coin_pub which public key was this payment about - * @param deposit_value amount contributed by this coin in total - * @param deposit_fee deposit fee charged by mint for this coin + * @param coin_value amount contributed by this coin to the total + * @param transaction_value total amount of the wire transaction * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors */ int (*insert_aggregation_tracking)(void *cls, + struct TALER_MINTDB_Session *session, const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_MerchantPublicKeyP *merchant_pub, const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_contract, uint64_t transaction_id, + struct GNUNET_TIME_Absolute execution_time, const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_Amount *deposit_value, - const struct TALER_Amount *deposit_fee); + const struct TALER_Amount *coin_value, + const struct TALER_Amount *transaction_value); }; diff --git a/src/mint-lib/mint_api_context.c b/src/mint-lib/mint_api_context.c index 3a86efb6..2767906b 100644 --- a/src/mint-lib/mint_api_context.c +++ b/src/mint-lib/mint_api_context.c @@ -288,7 +288,7 @@ TALER_MINT_perform (struct TALER_MINT_Context *ctx) GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle, CURLINFO_PRIVATE, - (char *) &job)); + (char **) &job)); GNUNET_assert (job->ctx == ctx); job->jcc (job->jcc_cls, cmsg->easy_handle); diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index d31dcd2b..cc164220 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -1697,10 +1697,18 @@ TMH_DB_execute_wire_deposits (struct MHD_Connection *connection, { int ret; struct WtidTransactionContext ctx; + struct TALER_MINTDB_Session *session; + if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls, + TMH_test_mode))) + { + GNUNET_break (0); + return TMH_RESPONSE_reply_internal_db_error (connection); + } ctx.is_valid = GNUNET_NO; ctx.deposits = json_array (); ret = TMH_plugin->lookup_wire_transactions (TMH_plugin->cls, + session, &wtid->raw, &handle_transaction_data, &ctx); @@ -1838,7 +1846,14 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection, { int ret; struct DepositWtidContext ctx; + struct TALER_MINTDB_Session *session; + if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls, + TMH_test_mode))) + { + GNUNET_break (0); + return TMH_RESPONSE_reply_internal_db_error (connection); + } ctx.connection = connection; ctx.h_contract = *h_contract; ctx.h_wire = *h_wire; @@ -1846,6 +1861,7 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection, ctx.transaction_id = transaction_id; ctx.res = MHD_NO; /* this value should never be read... */ ret = TMH_plugin->wire_lookup_deposit_wtid (TMH_plugin->cls, + session, h_contract, h_wire, coin_pub, diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index ca8edcff..f3760216 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -3451,6 +3451,7 @@ postgres_get_coin_transactions (void *cls, * into a wire transfer by the respective @a wtid. * * @param cls closure + * @param session database connection * @param wtid the raw wire transfer identifier we used * @param cb function to call on each transaction found * @param cb_cls closure for @a cb @@ -3458,6 +3459,7 @@ postgres_get_coin_transactions (void *cls, */ static int postgres_lookup_wire_transactions (void *cls, + struct TALER_MINTDB_Session *session, const struct TALER_WireTransferIdentifierRawP *wtid, TALER_MINTDB_TransactionDataCallback cb, void *cb_cls) @@ -3473,6 +3475,7 @@ postgres_lookup_wire_transactions (void *cls, * to be executed. * * @param cls closure + * @param session database connection * @param h_contract hash of the contract * @param h_wire hash of merchant wire details * @param coin_pub public key of deposited coin @@ -3484,6 +3487,7 @@ postgres_lookup_wire_transactions (void *cls, */ static int postgres_wire_lookup_deposit_wtid (void *cls, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *h_contract, const struct GNUNET_HashCode *h_wire, const struct TALER_CoinSpendPublicKeyP *coin_pub, @@ -3501,6 +3505,7 @@ postgres_wire_lookup_deposit_wtid (void *cls, * Function called to insert aggregation information into the DB. * * @param cls closure + * @param session database connection * @param wtid the raw wire transfer identifier we used * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls) @@ -3513,15 +3518,49 @@ postgres_wire_lookup_deposit_wtid (void *cls, */ static int postgres_insert_aggregation_tracking (void *cls, + struct TALER_MINTDB_Session *session, const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_MerchantPublicKeyP *merchant_pub, const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_contract, uint64_t transaction_id, + struct GNUNET_TIME_Absolute execution_time, const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_Amount *deposit_value, - const struct TALER_Amount *deposit_fee) + const struct TALER_Amount *coin_value, + const struct TALER_Amount *transaction_value) { + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_auto_from_type (h_contract), + TALER_PQ_query_param_auto_from_type (h_wire), + TALER_PQ_query_param_auto_from_type (coin_pub), + TALER_PQ_query_param_auto_from_type (merchant_pub), + TALER_PQ_query_param_uint64 (&transaction_id), + TALER_PQ_query_param_auto_from_type (wtid), + TALER_PQ_query_param_absolute_time (&execution_time), + TALER_PQ_query_param_amount (coin_value), + TALER_PQ_query_param_amount (transaction_value), + TALER_PQ_query_param_end + }; + PGresult *result; + + result = TALER_PQ_exec_prepared (session->conn, + "insert_aggregation_tracking", + params); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + if (0 != strcmp ("1", PQcmdTuples (result))) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + return GNUNET_OK; + GNUNET_break (0); // not implemented return GNUNET_SYSERR; } -- cgit v1.2.3 From c12a899f32e1ce432181fa428ebe77bd72ce4394 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Jan 2016 12:09:17 +0100 Subject: finish implementation of DB tracing functions --- src/include/taler_mintdb_plugin.h | 57 ++++---- src/mint/taler-mint-httpd_db.c | 78 ++++++---- src/mintdb/plugin_mintdb_postgres.c | 277 ++++++++++++++++++++++++++++++++---- 3 files changed, 332 insertions(+), 80 deletions(-) (limited to 'src') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 2ffde228..e5cf6d6f 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -570,23 +570,24 @@ typedef void /** * Function called with the results of the lookup of the - * wire transfer identifier information. + * wire transfer identifier information. Only called if + * we are at least aware of the transaction existing. * * @param cls closure * @param wtid wire transfer identifier, NULL * if the transaction was not yet done * @param coin_contribution how much did the coin we asked about - * contribute to the total transfer value? (deposit value minus fee) + * contribute to the total transfer value? (deposit value including fee) + * @param coin_fee how much did the mint charge for the deposit fee * @param total_amount how much was the total wire transfer? * @param execution_time when was the transaction done, or - * when we expect it to be done (if @a wtid was NULL); - * #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown - * to the mint + * when we expect it to be done (if @a wtid was NULL) */ typedef void (*TALER_MINTDB_DepositWtidCallback)(void *cls, const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *coin_fee, const struct TALER_Amount *total_amount, struct GNUNET_TIME_Absolute execution_time); @@ -601,18 +602,20 @@ typedef void * @param h_contract which contract was this payment about * @param transaction_id merchant's transaction ID for the payment * @param coin_pub which public key was this payment about - * @param deposit_value amount contributed by this coin in total - * @param deposit_fee deposit fee charged by mint for this coin + * @param coin_value amount contributed by this coin in total (with fee) + * @param coin_fee applicable fee for this coin + * @param transfer_value total amount of the wire transfer */ typedef void -(*TALER_MINTDB_TransactionDataCallback)(void *cls, - const struct TALER_MerchantPublicKeyP *merchant_pub, - const struct GNUNET_HashCode *h_wire, - const struct GNUNET_HashCode *h_contract, - uint64_t transaction_id, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_Amount *deposit_value, - const struct TALER_Amount *deposit_fee); +(*TALER_MINTDB_WireTransferDataCallback)(void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_wire, + const struct GNUNET_HashCode *h_contract, + uint64_t transaction_id, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *coin_value, + const struct TALER_Amount *coin_fee, + const struct TALER_Amount *transfer_value); /** @@ -1233,14 +1236,15 @@ struct TALER_MINTDB_Plugin * @param wtid the raw wire transfer identifier we used * @param cb function to call on each transaction found * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors + * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors, + * #GNUNET_NO if we found no results */ int - (*lookup_wire_transactions) (void *cls, - struct TALER_MINTDB_Session *session, - const struct TALER_WireTransferIdentifierRawP *wtid, - TALER_MINTDB_TransactionDataCallback cb, - void *cb_cls); + (*lookup_wire_transfer) (void *cls, + struct TALER_MINTDB_Session *session, + const struct TALER_WireTransferIdentifierRawP *wtid, + TALER_MINTDB_WireTransferDataCallback cb, + void *cb_cls); /** @@ -1257,7 +1261,8 @@ struct TALER_MINTDB_Plugin * @param transaction_id transaction identifier * @param cb function to call with the result * @param cb_cls closure to pass to @a cb - * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors + * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors, + * #GNUNET_NO if nothing was found */ int (*wire_lookup_deposit_wtid)(void *cls, @@ -1283,8 +1288,9 @@ struct TALER_MINTDB_Plugin * @param transaction_id merchant's transaction ID for the payment * @param execution_time when did we execute the transaction * @param coin_pub which public key was this payment about - * @param coin_value amount contributed by this coin to the total - * @param transaction_value total amount of the wire transaction + * @param coin_value amount contributed by this coin in total + * @param coin_fee deposit fee charged by mint for this coin + * @param transfer_value total amount of the wire transfer * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors */ int @@ -1298,7 +1304,8 @@ struct TALER_MINTDB_Plugin struct GNUNET_TIME_Absolute execution_time, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_Amount *coin_value, - const struct TALER_Amount *transaction_value); + const struct TALER_Amount *coin_fee, + const struct TALER_Amount *transfer_value); }; diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index cc164220..fb4ee1b7 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -1565,6 +1565,12 @@ struct WtidTransactionContext */ struct TALER_Amount total; + /** + * Value we find in the DB for the @e total; only valid if @e is_valid + * is #GNUNET_YES. + */ + struct TALER_Amount db_transaction_value; + /** * Public key of the merchant, only valid if @e is_valid * is #GNUNET_YES. @@ -1606,6 +1612,7 @@ struct WtidTransactionContext * @param coin_pub which public key was this payment about * @param deposit_value amount contributed by this coin in total * @param deposit_fee deposit fee charged by mint for this coin + * @param transaction_value total value of the wire transaction */ static void handle_transaction_data (void *cls, @@ -1615,7 +1622,8 @@ handle_transaction_data (void *cls, uint64_t transaction_id, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_Amount *deposit_value, - const struct TALER_Amount *deposit_fee) + const struct TALER_Amount *deposit_fee, + const struct TALER_Amount *transaction_value) { struct WtidTransactionContext *ctx = cls; struct TALER_Amount delta; @@ -1626,6 +1634,7 @@ handle_transaction_data (void *cls, { ctx->merchant_pub = *merchant_pub; ctx->h_wire = *h_wire; + ctx->db_transaction_value = *transaction_value; ctx->is_valid = GNUNET_YES; if (GNUNET_OK != TALER_amount_subtract (&ctx->total, @@ -1644,7 +1653,9 @@ handle_transaction_data (void *cls, sizeof (struct TALER_MerchantPublicKeyP))) || (0 != memcmp (&ctx->h_wire, h_wire, - sizeof (struct GNUNET_HashCode))) ) + sizeof (struct GNUNET_HashCode))) || + (0 != TALER_amount_cmp (transaction_value, + &ctx->db_transaction_value)) ) { GNUNET_break (0); ctx->is_valid = GNUNET_SYSERR; @@ -1707,11 +1718,11 @@ TMH_DB_execute_wire_deposits (struct MHD_Connection *connection, } ctx.is_valid = GNUNET_NO; ctx.deposits = json_array (); - ret = TMH_plugin->lookup_wire_transactions (TMH_plugin->cls, - session, - &wtid->raw, - &handle_transaction_data, - &ctx); + ret = TMH_plugin->lookup_wire_transfer (TMH_plugin->cls, + session, + &wtid->raw, + &handle_transaction_data, + &ctx); if (GNUNET_SYSERR == ret) { GNUNET_break (0); @@ -1730,8 +1741,15 @@ TMH_DB_execute_wire_deposits (struct MHD_Connection *connection, return TMH_RESPONSE_reply_arg_unknown (connection, "wtid"); } + if (0 != TALER_amount_cmp (&ctx.total, + &ctx.db_transaction_value)) + { + /* FIXME: this CAN actually differ, due to rounding + down. But we should still check that the values + do match after rounding 'total' down! */ + } return TMH_RESPONSE_reply_wire_deposit_details (connection, - &ctx.total, + &ctx.db_transaction_value, &ctx.merchant_pub, &ctx.h_wire, ctx.deposits); @@ -1784,7 +1802,8 @@ struct DepositWtidContext * @param wtid raw wire transfer identifier, NULL * if the transaction was not yet done * @param coin_contribution how much did the coin we asked about - * contribute to the total transfer value? (deposit value minus fee) + * contribute to the total transfer value? (deposit value including fee) + * @param coin_fee how much did the mint charge for the deposit fee * @param total_amount how much was the total wire transfer? * @param execution_time when was the transaction done, or * when we expect it to be done (if @a wtid was NULL); @@ -1795,31 +1814,40 @@ static void handle_wtid_data (void *cls, const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *coin_fee, const struct TALER_Amount *total_amount, struct GNUNET_TIME_Absolute execution_time) { struct DepositWtidContext *ctx = cls; + struct TALER_Amount coin_delta; if (NULL == wtid) { - if (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us == - execution_time.abs_value_us) - ctx->res = TMH_RESPONSE_reply_deposit_unknown (ctx->connection); - else - ctx->res = TMH_RESPONSE_reply_deposit_pending (ctx->connection, - execution_time); + ctx->res = TMH_RESPONSE_reply_deposit_pending (ctx->connection, + execution_time); } else { - ctx->res = TMH_RESPONSE_reply_deposit_wtid (ctx->connection, - &ctx->h_contract, - &ctx->h_wire, - &ctx->coin_pub, - coin_contribution, - total_amount, - ctx->transaction_id, - wtid, - execution_time); + if (GNUNET_SYSERR == + TALER_amount_subtract (&coin_delta, + coin_contribution, + coin_fee)) + { + GNUNET_break (0); + ctx->res = TMH_RESPONSE_reply_internal_db_error (ctx->connection); + } + else + { + ctx->res = TMH_RESPONSE_reply_deposit_wtid (ctx->connection, + &ctx->h_contract, + &ctx->h_wire, + &ctx->coin_pub, + &coin_delta, + total_amount, + ctx->transaction_id, + wtid, + execution_time); + } } } @@ -1874,6 +1902,8 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection, GNUNET_break (0); return TMH_RESPONSE_reply_internal_db_error (connection); } + if (GNUNET_NO == ret) + return TMH_RESPONSE_reply_deposit_unknown (connection); return ctx.res; } diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index f3760216..38ce45eb 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -875,6 +875,26 @@ postgres_prepare (PGconn *db_conn) " (merchant_pub=$3)" " )", 3, NULL); + /* Fetch an existing deposit request. + Used in #postgres_wire_lookup_deposit_wtid(). */ + PREPARE ("get_deposit_for_wtid", + "SELECT" + " amount_with_fee_val" + ",amount_with_fee_frac" + ",amount_with_fee_curr" + ",deposit_fee_val" + ",deposit_fee_frac" + ",deposit_fee_curr" + ",wire_deadline" + " FROM deposits" + " WHERE (" + " (coin_pub=$1) AND" + " (transaction_id=$2) AND" + " (merchant_pub=$3) AND" + " (h_contract=$4) AND" + " (h_wire=$5)" + " )", + 5, NULL); /* Used in #postgres_iterate_deposits() */ PREPARE ("deposits_iterate", @@ -972,7 +992,7 @@ postgres_prepare (PGconn *db_conn) " AND rm.oldcoin_index = rcl.oldcoin_index" " AND rcl.cnc_index=rs.noreveal_index", 1, NULL); - /* Used in #postgres_lookup_wire_transactions */ + /* Used in #postgres_lookup_wire_transfer */ PREPARE ("lookup_transactions", "SELECT" " h_contract" @@ -984,9 +1004,12 @@ postgres_prepare (PGconn *db_conn) ",coin_amount_val" ",coin_amount_frac" ",coin_amount_curr" - ",transaction_total_val" - ",transaction_total_frac" - ",transaction_total_curr" + ",coin_fee_val" + ",coin_fee_frac" + ",coin_fee_curr" + ",transfer_total_val" + ",transfer_total_frac" + ",transfer_total_curr" " FROM aggregation_tracking" " WHERE wtid_raw=$1", 1, NULL); @@ -998,9 +1021,12 @@ postgres_prepare (PGconn *db_conn) ",coin_amount_val" ",coin_amount_frac" ",coin_amount_curr" - ",transaction_total_val" - ",transaction_total_frac" - ",transaction_total_curr" + ",coin_fee_val" + ",coin_fee_frac" + ",coin_fee_curr" + ",transfer_total_val" + ",transfer_total_frac" + ",transfer_total_curr" " FROM aggregation_tracking" " WHERE" " coin_pub=$1 AND" @@ -1022,12 +1048,15 @@ postgres_prepare (PGconn *db_conn) ",coin_amount_val" ",coin_amount_frac" ",coin_amount_curr" - ",transaction_total_val" - ",transaction_total_frac" - ",transaction_total_curr" + ",coin_fee_val" + ",coin_fee_frac" + ",coin_fee_curr" + ",transfer_total_val" + ",transfer_total_frac" + ",transfer_total_curr" ") VALUES " - "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)", - 13, NULL); + "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)", + 16, NULL); return GNUNET_OK; #undef PREPARE @@ -3455,17 +3484,83 @@ postgres_get_coin_transactions (void *cls, * @param wtid the raw wire transfer identifier we used * @param cb function to call on each transaction found * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors + * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors, + * #GNUNET_NO if we found no results */ static int -postgres_lookup_wire_transactions (void *cls, - struct TALER_MINTDB_Session *session, - const struct TALER_WireTransferIdentifierRawP *wtid, - TALER_MINTDB_TransactionDataCallback cb, - void *cb_cls) +postgres_lookup_wire_transfer (void *cls, + struct TALER_MINTDB_Session *session, + const struct TALER_WireTransferIdentifierRawP *wtid, + TALER_MINTDB_WireTransferDataCallback cb, + void *cb_cls) { - GNUNET_break (0); // not implemented! - return GNUNET_SYSERR; + PGresult *result; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_auto_from_type (wtid), + TALER_PQ_query_param_end + }; + int nrows; + int i; + + /* check if the melt record exists and get it */ + result = TALER_PQ_exec_prepared (session->conn, + "lookup_transactions", + params); + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + nrows = PQntuples (result); + if (0 == nrows) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "lookup_wire_transfer() returned 0 matching rows\n"); + PQclear (result); + return GNUNET_NO; + } + for (i=0;iconn, + "lookup_deposit_wtid", + params); + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + nrows = PQntuples (result); + if (0 == nrows) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "lookup_wire_transfer() returned 0 matching rows\n"); + PQclear (result); + + /* Check if transaction exists in deposits, so that we just + do not have a WTID yet, if so, do call the CB with a NULL wtid + and return GNUNET_YES! */ + { + struct TALER_PQ_QueryParam params2[] = { + TALER_PQ_query_param_auto_from_type (coin_pub), + TALER_PQ_query_param_uint64 (&transaction_id), + TALER_PQ_query_param_auto_from_type (merchant_pub), + TALER_PQ_query_param_auto_from_type (h_contract), + TALER_PQ_query_param_auto_from_type (h_wire), + TALER_PQ_query_param_end + }; + + result = TALER_PQ_exec_prepared (session->conn, + "get_deposit_for_wtid", + params2); + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + } + nrows = PQntuples (result); + if (0 == nrows) + { + PQclear (result); + return GNUNET_NO; + } + + /* Ok, we're aware of the transaction, but it has not yet been + executed */ + { + struct GNUNET_TIME_Absolute exec_time; + struct TALER_Amount coin_amount; + struct TALER_Amount coin_fee; + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_amount ("coin_amount", &coin_amount), + TALER_PQ_result_spec_amount ("deposit_fee", &coin_fee), + TALER_PQ_result_spec_absolute_time ("wire_deadline", &exec_time), + TALER_PQ_result_spec_end + }; + if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + cb (cb_cls, + NULL, + &coin_amount, + &coin_fee, + NULL, + exec_time); + PQclear (result); + return GNUNET_YES; + } + } + if (1 != nrows) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + { + struct TALER_WireTransferIdentifierRawP wtid; + struct GNUNET_TIME_Absolute exec_time; + struct TALER_Amount coin_amount; + struct TALER_Amount coin_fee; + struct TALER_Amount transaction_amount; + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_auto_from_type ("wtid_raw", &wtid), + TALER_PQ_result_spec_absolute_time ("execution_time", &exec_time), + TALER_PQ_result_spec_amount ("coin_amount", &coin_amount), + TALER_PQ_result_spec_amount ("coin_fee", &coin_fee), + TALER_PQ_result_spec_amount ("transfer_total", &transaction_amount), + TALER_PQ_result_spec_end + }; + if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + cb (cb_cls, + &wtid, + &coin_amount, + &coin_fee, + &transaction_amount, + exec_time); + } + PQclear (result); + return GNUNET_OK; } @@ -3512,8 +3727,9 @@ postgres_wire_lookup_deposit_wtid (void *cls, * @param h_contract which contract was this payment about * @param transaction_id merchant's transaction ID for the payment * @param coin_pub which public key was this payment about - * @param deposit_value amount contributed by this coin in total - * @param deposit_fee deposit fee charged by mint for this coin + * @param coin_value amount contributed by this coin in total + * @param coin_fee deposit fee charged by mint for this coin + * @param transfer_value total amount of the wire transfer * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors */ static int @@ -3527,7 +3743,8 @@ postgres_insert_aggregation_tracking (void *cls, struct GNUNET_TIME_Absolute execution_time, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_Amount *coin_value, - const struct TALER_Amount *transaction_value) + const struct TALER_Amount *coin_fee, + const struct TALER_Amount *transfer_value) { struct TALER_PQ_QueryParam params[] = { TALER_PQ_query_param_auto_from_type (h_contract), @@ -3538,7 +3755,8 @@ postgres_insert_aggregation_tracking (void *cls, TALER_PQ_query_param_auto_from_type (wtid), TALER_PQ_query_param_absolute_time (&execution_time), TALER_PQ_query_param_amount (coin_value), - TALER_PQ_query_param_amount (transaction_value), + TALER_PQ_query_param_amount (coin_fee), + TALER_PQ_query_param_amount (transfer_value), TALER_PQ_query_param_end }; PGresult *result; @@ -3560,9 +3778,6 @@ postgres_insert_aggregation_tracking (void *cls, } PQclear (result); return GNUNET_OK; - - GNUNET_break (0); // not implemented - return GNUNET_SYSERR; } @@ -3635,7 +3850,7 @@ libtaler_plugin_mintdb_postgres_init (void *cls) plugin->get_transfer = &postgres_get_transfer; plugin->get_coin_transactions = &postgres_get_coin_transactions; plugin->free_coin_transaction_list = &common_free_coin_transaction_list; - plugin->lookup_wire_transactions = &postgres_lookup_wire_transactions; + plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer; plugin->wire_lookup_deposit_wtid = &postgres_wire_lookup_deposit_wtid; plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking; return plugin; -- cgit v1.2.3 From c097b11052c8304f1090dc086b221cc3bba6a8d0 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Jan 2016 13:53:34 +0100 Subject: defined tracking API (not implemented) --- src/include/taler_mint_service.h | 163 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) (limited to 'src') diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h index a7b6afd1..dc653135 100644 --- a/src/include/taler_mint_service.h +++ b/src/include/taler_mint_service.h @@ -1059,5 +1059,168 @@ void TALER_MINT_admin_add_incoming_cancel (struct TALER_MINT_AdminAddIncomingHandle *aai); +/* ********************* /wire/deposits *********************** */ + +/** + * @brief A /wire/deposits Handle + */ +struct TALER_MINT_WireDepositsHandle; + + +/** + * Details for one of the /deposit operations that the + * mint combined into a single wire transfer. + */ +struct TALER_WireDepositDetails +{ + /** + * Hash of the contract. + */ + struct GNUNET_HashCode h_contract; + + /** + * Which coin was deposited? + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * Value of the deposit (including fee). + */ + struct TALER_Amount coin_contribution; + + /** + * Fee charged by the mint for the deposit. + */ + struct TALER_Amount coin_fee; + + /** + * Merchant's transaction identifier. + */ + uint64_t transaction_id; + +}; + + +/** + * 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 mint protocol violation + * @param json original json reply (may include signatures, those have then been + * validated already) + * @param wtid extracted wire transfer identifier, or NULL if the mint could + * not provide any (set only if @a http_status is #MHD_HTTP_OK) + * @param total_amount total amount of the wire transfer, or NULL if the mint could + * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK) + * @param details_length length of the @a details array + * @param details array with details about the combined transactions + */ +typedef void +(*TALER_MINT_WireDepositsCallback)(void *cls, + unsigned int http_status, + json_t *json, + const struct GNUNET_HashCode *h_wire, + const struct TALER_Amount *total_amount, + unsigned int details_length, + const struct TALER_WireDepositDetails *details); + + +/** + * Query the mint about which transactions were combined + * to create a wire transfer. + * + * @param mint mint to query + * @param wtid raw wire transfer identifier to get information about + * @param cb callback to call + * @param cb_cls closure for @a cb + * @return handle to cancel operation + */ +struct TALER_MINT_WireDepositsHandle * +TALER_MINT_wire_deposits (struct TALER_MINT_Handle *mint, + const struct TALER_WireTransferIdentifierRawP *wtid, + TALER_MINT_WireDepositsCallback cb, + void *cb_cls); + + +/** + * Cancel wire deposits request. This function cannot be used on a request + * handle if a response is already served for it. + * + * @param wdh the wire deposits request handle + */ +void +TALER_MINT_wire_deposits_cancel (struct TALER_MINT_WireDepositsHandle *wdh); + + +/* ********************* /deposit/wtid *********************** */ + + +/** + * @brief A /deposit/wtid Handle + */ +struct TALER_MINT_DepositWtidHandle; + + +/** + * Function called with detailed wire transfer data. + * + * @param cls closure + * @param http_status HTTP status code we got, 0 on mint protocol violation + * @param json original json reply (may include signatures, those have then been + * validated already) + * @param wtid wire transfer identifier used by the mint, NULL if mint did not + * yet execute the transaction + * @param execution_time actual or planned execution time for the wire transfer + * @param coin_contribution original value of the deposited coin (may be NULL) + * @param coin_fee fee of charged by the mint for the deposit (may be NULL) + * @param total_amount total amount of the wire transfer, or NULL if the mint could + * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK) + */ +typedef void +(*TALER_MINT_DepositWtidCallback)(void *cls, + unsigned int http_status, + json_t *json, + const struct TALER_WireTransferIdentifierRawP *wtid, + struct GNUNET_TIME_Absolute execution_time, + const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *coin_fee, + const struct TALER_Amount *total_amount); + + +/** + * Obtain the wire transfer details for a given transaction. + * + * @param mint the mint to query + * @param merchant_priv the merchant's private key + * @param h_wire hash of merchant's wire transfer details + * @param h_contract hash of the contract + * @param coin_pub public key of the coin + * @param transaction_id transaction identifier + * @param cb function to call with the result + * @param cb_cls closure for @a cb + * @return handle to abort request + */ +struct TALER_MINT_DepositWtidHandle * +TALER_MINT_deposit_wtid (struct TALER_MINT_Handle *mint, + const struct TALER_MerchantPrivateKeyP *merchant_priv, + const struct GNUNET_HashCode *h_wire, + const struct GNUNET_HashCode *h_contract, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + uint64_t transaction_id, + TALER_MINT_DepositWtidCallback cb, + void *cb_cls); + + +/** + * Cancel deposit wtid request. This function cannot be used on a request + * handle if a response is already served for it. + * + * @param dwh the wire deposits request handle + * + */ +void +TALER_MINT_deposit_wtid_cancel (struct TALER_MINT_DepositWtidHandle *dwh); + #endif /* _TALER_MINT_SERVICE_H */ -- cgit v1.2.3 From ce199e6e9590a4290c11cdbc1b40600f74ac41e6 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Jan 2016 14:46:17 +0100 Subject: adding API code for /deposit/wtid requests --- src/include/taler_mint_service.h | 7 +- src/mint-lib/Makefile.am | 1 + src/mint-lib/mint_api_deposit_wtid.c | 387 ++++++++++++++++++++++++++++++++++ src/mint/taler-mint-httpd_responses.c | 22 +- 4 files changed, 396 insertions(+), 21 deletions(-) create mode 100644 src/mint-lib/mint_api_deposit_wtid.c (limited to 'src') diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h index dc653135..070f77f0 100644 --- a/src/include/taler_mint_service.h +++ b/src/include/taler_mint_service.h @@ -1172,8 +1172,7 @@ struct TALER_MINT_DepositWtidHandle; * @param wtid wire transfer identifier used by the mint, NULL if mint did not * yet execute the transaction * @param execution_time actual or planned execution time for the wire transfer - * @param coin_contribution original value of the deposited coin (may be NULL) - * @param coin_fee fee of charged by the mint for the deposit (may be NULL) + * @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 mint could * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK) */ @@ -1184,12 +1183,11 @@ typedef void const struct TALER_WireTransferIdentifierRawP *wtid, struct GNUNET_TIME_Absolute execution_time, const struct TALER_Amount *coin_contribution, - const struct TALER_Amount *coin_fee, const struct TALER_Amount *total_amount); /** - * Obtain the wire transfer details for a given transaction. + * Obtain the wire transfer details for a given deposit. * * @param mint the mint to query * @param merchant_priv the merchant's private key @@ -1217,7 +1215,6 @@ TALER_MINT_deposit_wtid (struct TALER_MINT_Handle *mint, * handle if a response is already served for it. * * @param dwh the wire deposits request handle - * */ void TALER_MINT_deposit_wtid_cancel (struct TALER_MINT_DepositWtidHandle *dwh); diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am index ccea4ec5..3ee1f5a1 100644 --- a/src/mint-lib/Makefile.am +++ b/src/mint-lib/Makefile.am @@ -20,6 +20,7 @@ libtalermint_la_SOURCES = \ mint_api_handle.c mint_api_handle.h \ mint_api_admin.c \ mint_api_deposit.c \ + mint_api_deposit_wtid.c \ mint_api_refresh.c \ mint_api_refresh_link.c \ mint_api_reserve.c \ diff --git a/src/mint-lib/mint_api_deposit_wtid.c b/src/mint-lib/mint_api_deposit_wtid.c new file mode 100644 index 00000000..bb81d93e --- /dev/null +++ b/src/mint-lib/mint_api_deposit_wtid.c @@ -0,0 +1,387 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015, 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_deposit_wtid.c + * @brief Implementation of the /deposit/wtid request of the mint's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include "taler_mint_service.h" +#include "mint_api_common.h" +#include "mint_api_json.h" +#include "mint_api_context.h" +#include "mint_api_handle.h" +#include "taler_signatures.h" + + +/** + * @brief A Deposit Wtid Handle + */ +struct TALER_MINT_DepositWtidHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * JSON encoding of the request to POST. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with the result. + */ + TALER_MINT_DepositWtidCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + + /** + * Information the mint should sign in response. + * (with pre-filled fields from the request). + */ + struct TALER_ConfirmWirePS depconf; + +}; + + +/** + * Verify that the signature on the "200 OK" response + * from the mint is valid. + * + * @param dwh deposit wtid handle + * @param json json reply with the signature + * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not + */ +static int +verify_deposit_wtid_signature_ok (const struct TALER_MINT_DepositWtidHandle *dwh, + json_t *json) +{ + struct TALER_MintSignatureP mint_sig; + struct TALER_MintPublicKeyP mint_pub; + const struct TALER_MINT_Keys *key_state; + struct MAJ_Specification spec[] = { + MAJ_spec_fixed_auto ("mint_sig", &mint_sig), + MAJ_spec_fixed_auto ("mint_pub", &mint_pub), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + key_state = TALER_MINT_get_keys (dwh->mint); + if (GNUNET_OK != + TALER_MINT_test_signing_key (key_state, + &mint_pub)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_CONFIRM_WIRE, + &dwh->depconf.purpose, + &mint_sig.eddsa_signature, + &mint_pub.eddsa_pub)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /deposit/wtid request. + * + * @param cls the `struct TALER_MINT_DepositWtidHandle` + * @param eh the curl request handle + */ +static void +handle_deposit_wtid_finished (void *cls, + CURL *eh) +{ + struct TALER_MINT_DepositWtidHandle *dwh = cls; + long response_code; + json_t *json; + const struct TALER_WireTransferIdentifierRawP *wtid = NULL; + struct GNUNET_TIME_Absolute execution_time = GNUNET_TIME_UNIT_FOREVER_ABS; + const struct TALER_Amount *coin_contribution = NULL; + const struct TALER_Amount *total_amount = NULL; + struct TALER_Amount coin_contribution_s; + struct TALER_Amount total_amount_s; + + dwh->job = NULL; + json = MAC_download_get_result (&dwh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + { + struct MAJ_Specification spec[] = { + MAJ_spec_fixed_auto ("wtid", &dwh->depconf.wtid), + MAJ_spec_absolute_time ("execution_time", &execution_time), + MAJ_spec_amount ("coin_contribution", &coin_contribution_s), + MAJ_spec_amount ("total_amount", &total_amount_s), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + wtid = &dwh->depconf.wtid; + dwh->depconf.execution_time = GNUNET_TIME_absolute_hton (execution_time); + TALER_amount_hton (&dwh->depconf.coin_contribution, + &coin_contribution_s); + coin_contribution = &coin_contribution_s; + TALER_amount_hton (&dwh->depconf.total_amount, + &total_amount_s); + total_amount = &total_amount_s; + if (GNUNET_OK != + verify_deposit_wtid_signature_ok (dwh, + json)) + { + GNUNET_break_op (0); + response_code = 0; + } + } + break; + case MHD_HTTP_ACCEPTED: + { + /* Transaction known, but not executed yet */ + struct MAJ_Specification spec[] = { + MAJ_spec_absolute_time ("execution_time", &execution_time), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + } + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + /* Nothing really to verify, mint says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Mint does not know about transaction; + we should pass the reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + dwh->cb (dwh->cb_cls, + response_code, + json, + wtid, + execution_time, + coin_contribution, + total_amount); + json_decref (json); + TALER_MINT_deposit_wtid_cancel (dwh); +} + + +/** + * Obtain wire transfer details about an existing deposit operation. + * + * @param mint the mint to query + * @param merchant_priv the merchant's private key + * @param h_wire hash of merchant's wire transfer details + * @param h_contract hash of the contract + * @param coin_pub public key of the coin + * @param transaction_id transaction identifier + * @param cb function to call with the result + * @param cb_cls closure for @a cb + * @return handle to abort request + */ +struct TALER_MINT_DepositWtidHandle * +TALER_MINT_deposit_wtid (struct TALER_MINT_Handle *mint, + const struct TALER_MerchantPrivateKeyP *merchant_priv, + const struct GNUNET_HashCode *h_wire, + const struct GNUNET_HashCode *h_contract, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + uint64_t transaction_id, + TALER_MINT_DepositWtidCallback cb, + void *cb_cls) +{ + struct TALER_DepositTrackPS dtp; + struct TALER_MerchantSignatureP merchant_sig; + struct TALER_MINT_DepositWtidHandle *dwh; + struct TALER_MINT_Context *ctx; + json_t *deposit_wtid_obj; + CURL *eh; + + if (GNUNET_YES != + MAH_handle_is_ready (mint)) + { + GNUNET_break (0); + return NULL; + } + dtp.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_DEPOSIT_WTID); + dtp.purpose.size = htonl (sizeof (dtp)); + dtp.h_contract = *h_contract; + dtp.h_wire = *h_wire; + dtp.transaction_id = GNUNET_htonll (transaction_id); + GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv, + &dtp.merchant.eddsa_pub); + + dtp.coin_pub = *coin_pub; + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv, + &dtp.purpose, + &merchant_sig.eddsa_sig)); + + deposit_wtid_obj = json_pack ("{s:o, s:o," /* H_wire, H_contract */ + " s:o, s:I," /* coin_pub, transaction_id */ + " s:o, s:o}", /* merchant_pub, merchant_sig */ + "H_wire", TALER_json_from_data (h_wire, + sizeof (struct GNUNET_HashCode)), + "H_contract", TALER_json_from_data (h_contract, + sizeof (struct GNUNET_HashCode)), + "coin_pub", TALER_json_from_data (coin_pub, + sizeof (*coin_pub)), + "transaction_id", (json_int_t) transaction_id, + "merchant_pub", TALER_json_from_data (&dtp.merchant, + sizeof (struct TALER_MerchantPublicKeyP)), + "merchant_sig", TALER_json_from_data (&merchant_sig, + sizeof (merchant_sig))); + + dwh = GNUNET_new (struct TALER_MINT_DepositWtidHandle); + dwh->mint = mint; + dwh->cb = cb; + dwh->cb_cls = cb_cls; + dwh->url = MAH_path_to_url (mint, "/deposit/wtid"); + dwh->depconf.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS)); + dwh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_WIRE); + dwh->depconf.h_wire = *h_wire; + dwh->depconf.h_contract = *h_contract; + dwh->depconf.coin_pub = *coin_pub; + dwh->depconf.transaction_id = GNUNET_htonll (transaction_id); + + eh = curl_easy_init (); + GNUNET_assert (NULL != (dwh->json_enc = + json_dumps (deposit_wtid_obj, + JSON_COMPACT))); + json_decref (deposit_wtid_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + dwh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + dwh->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (dwh->json_enc))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &dwh->db)); + ctx = MAH_handle_to_context (mint); + dwh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_deposit_wtid_finished, + dwh); + return dwh; +} + + +/** + * Cancel deposit wtid request. This function cannot be used on a request + * handle if a response is already served for it. + * + * @param dwh the wire deposits request handle + */ +void +TALER_MINT_deposit_wtid_cancel (struct TALER_MINT_DepositWtidHandle *dwh) +{ + if (NULL != dwh->job) + { + MAC_job_cancel (dwh->job); + dwh->job = NULL; + } + GNUNET_free_non_null (dwh->db.buf); + GNUNET_free (dwh->url); + GNUNET_free (dwh->json_enc); + GNUNET_free (dwh); +} + + +/* end of mint_api_deposit_wtid.c */ diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index 98c36283..23aa9e8b 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -1117,11 +1117,7 @@ TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection, struct TALER_ConfirmWirePS cw; struct TALER_MintPublicKeyP pub; struct TALER_MintSignatureP sig; - struct TALER_WireTransferIdentifierP wtid_crc; - char *wtid_s; - int ret; - /* Create signature for the reply */ cw.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_WIRE); cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS)); cw.h_wire = *h_wire; @@ -1137,24 +1133,18 @@ TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection, TMH_KS_sign (&cw.purpose, &pub, &sig); - /* Compute checksum and crockford encoding if wire transfer subject */ - wtid_crc.raw = *wtid; - wtid_crc.crc8 = GNUNET_CRYPTO_crc8_n (wtid, - sizeof (struct TALER_WireTransferIdentifierRawP)); - - wtid_s = GNUNET_STRINGS_data_to_string_alloc (&wtid_crc, - sizeof (wtid_crc)); - ret = TMH_RESPONSE_reply_json_pack (connection, + return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_OK, - "{s:s, s:o, s:o, s:o}", - "wtid", wtid_s, + "{s:o, s:o, s:o, s:o, s:o, s:o}", + "wtid", TALER_json_from_data (wtid, + sizeof (*wtid)), "execution_time", TALER_json_from_abs (exec_time), + "coin_contribution", TALER_json_from_amount (coin_contribution), + "total_amount", TALER_json_from_amount (total_amount), "mint_sig", TALER_json_from_data (&sig, sizeof (sig)), "mint_pub", TALER_json_from_data (&pub, sizeof (pub))); - GNUNET_free (wtid_s); - return ret; } -- cgit v1.2.3 From d63447baf65bd1578f51595cc8e673f3312f8044 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Jan 2016 15:18:55 +0100 Subject: adding mint-lib logic to execute /wire/deposits requests --- src/include/taler_mint_service.h | 2 +- src/mint-lib/Makefile.am | 3 +- src/mint-lib/mint_api_json.c | 34 ++++ src/mint-lib/mint_api_json.h | 21 +++ src/mint-lib/mint_api_wire_deposits.c | 296 ++++++++++++++++++++++++++++++++++ src/mint/taler-mint-httpd_db.c | 4 +- src/mint/taler-mint-httpd_db.h | 2 +- src/mint/taler-mint-httpd_tracking.c | 9 +- 8 files changed, 359 insertions(+), 12 deletions(-) create mode 100644 src/mint-lib/mint_api_wire_deposits.c (limited to 'src') diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h index 070f77f0..b151cb00 100644 --- a/src/include/taler_mint_service.h +++ b/src/include/taler_mint_service.h @@ -1086,7 +1086,7 @@ struct TALER_WireDepositDetails /** * Value of the deposit (including fee). */ - struct TALER_Amount coin_contribution; + struct TALER_Amount coin_value; /** * Fee charged by the mint for the deposit. diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am index 3ee1f5a1..171a4246 100644 --- a/src/mint-lib/Makefile.am +++ b/src/mint-lib/Makefile.am @@ -24,7 +24,8 @@ libtalermint_la_SOURCES = \ mint_api_refresh.c \ mint_api_refresh_link.c \ mint_api_reserve.c \ - mint_api_wire.c + mint_api_wire.c \ + mint_api_wire_deposits.c libtalermint_la_LIBADD = \ -lgnunetutil \ diff --git a/src/mint-lib/mint_api_json.c b/src/mint-lib/mint_api_json.c index a728a549..7de33e5e 100644 --- a/src/mint-lib/mint_api_json.c +++ b/src/mint-lib/mint_api_json.c @@ -232,6 +232,20 @@ parse_json (json_t *root, } break; + case MAJ_CMD_UINT64: + { + json_int_t val; + + if (! json_is_integer (pos)) + { + GNUNET_break_op (0); + return i; + } + val = json_integer_value (pos); + *spec[i].details.u64 = (uint64_t) val; + } + break; + case MAJ_CMD_JSON_OBJECT: { if (! (json_is_object (pos) || json_is_array (pos)) ) @@ -428,6 +442,26 @@ MAJ_spec_uint16 (const char *name, } +/** + * 64-bit integer. + * + * @param name name of the JSON field + * @param[out] u64 where to store the integer found under @a name + */ +struct MAJ_Specification +MAJ_spec_uint64 (const char *name, + uint64_t *u64) +{ + struct MAJ_Specification ret = + { + .cmd = MAJ_CMD_UINT64, + .field = name, + .details.u64 = u64 + }; + return ret; +} + + /** * JSON object. * diff --git a/src/mint-lib/mint_api_json.h b/src/mint-lib/mint_api_json.h index 68809059..6bc3a557 100644 --- a/src/mint-lib/mint_api_json.h +++ b/src/mint-lib/mint_api_json.h @@ -78,6 +78,11 @@ enum MAJ_Command */ MAJ_CMD_UINT16, + /** + * Parse `uint64_t` integer at the current position. + */ + MAJ_CMD_UINT64, + /** * Parse JSON object at the current position. */ @@ -191,6 +196,11 @@ struct MAJ_Specification */ uint16_t *u16; + /** + * Where to store 64-bit integer. + */ + uint64_t *u64; + /** * Where to store a JSON object. */ @@ -282,6 +292,17 @@ MAJ_spec_uint16 (const char *name, uint16_t *u16); +/** + * 64-bit integer. + * + * @param name name of the JSON field + * @param[out] u64 where to store the integer found under @a name + */ +struct MAJ_Specification +MAJ_spec_uint64 (const char *name, + uint64_t *u64); + + /** * JSON object. * diff --git a/src/mint-lib/mint_api_wire_deposits.c b/src/mint-lib/mint_api_wire_deposits.c new file mode 100644 index 00000000..6fd4d75d --- /dev/null +++ b/src/mint-lib/mint_api_wire_deposits.c @@ -0,0 +1,296 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015, 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_wire_deposits.c + * @brief Implementation of the /wire/deposits request of the mint's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include "taler_mint_service.h" +#include "mint_api_common.h" +#include "mint_api_json.h" +#include "mint_api_context.h" +#include "mint_api_handle.h" +#include "taler_signatures.h" + + +/** + * @brief A /wire/deposits Handle + */ +struct TALER_MINT_WireDepositsHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * JSON encoding of the request to POST. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with the result. + */ + TALER_MINT_WireDepositsCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + +}; + + +/** + * Function called when we're done processing the + * HTTP /wire/deposits request. + * + * @param cls the `struct TALER_MINT_WireDepositsHandle` + * @param eh the curl request handle + */ +static void +handle_wire_deposits_finished (void *cls, + CURL *eh) +{ + struct TALER_MINT_WireDepositsHandle *wdh = cls; + long response_code; + json_t *json; + + wdh->job = NULL; + json = MAC_download_get_result (&wdh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + { + json_t *details_j; + struct GNUNET_HashCode h_wire; + struct TALER_Amount total_amount; + struct TALER_MerchantPublicKeyP merchant_pub; + unsigned int num_details; + struct MAJ_Specification spec[] = { + MAJ_spec_fixed_auto ("H_wire", &h_wire), + MAJ_spec_fixed_auto ("merchant_pub", &merchant_pub), + MAJ_spec_amount ("total_amount", &total_amount), + MAJ_spec_json ("details", &details_j), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + num_details = json_array_size (details_j); + { + struct TALER_WireDepositDetails details[num_details]; + unsigned int i; + + for (i=0;ih_contract), + MAJ_spec_amount ("deposit_value", &detail->coin_value), + MAJ_spec_amount ("deposit_fee", &detail->coin_fee), + MAJ_spec_uint64 ("transaction_id", &detail->transaction_id), + MAJ_spec_fixed_auto ("coin_pub", &detail->coin_pub), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (detail_j, + spec_detail)) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + } + if (0 == response_code) + break; + wdh->cb (wdh->cb_cls, + response_code, + json, + &h_wire, + &total_amount, + num_details, + details); + json_decref (json); + TALER_MINT_wire_deposits_cancel (wdh); + return; + } + } + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + /* Nothing really to verify, mint says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Mint does not know about transaction; + we should pass the reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + wdh->cb (wdh->cb_cls, + response_code, + json, + NULL, NULL, 0, NULL); + json_decref (json); + TALER_MINT_wire_deposits_cancel (wdh); +} + + +/** + * Query the mint about which transactions were combined + * to create a wire transfer. + * + * @param mint mint to query + * @param wtid raw wire transfer identifier to get information about + * @param cb callback to call + * @param cb_cls closure for @a cb + * @return handle to cancel operation + */ +struct TALER_MINT_WireDepositsHandle * +TALER_MINT_wire_deposits (struct TALER_MINT_Handle *mint, + const struct TALER_WireTransferIdentifierRawP *wtid, + TALER_MINT_WireDepositsCallback cb, + void *cb_cls) +{ + struct TALER_MINT_WireDepositsHandle *wdh; + struct TALER_MINT_Context *ctx; + json_t *wdh_obj; + CURL *eh; + + if (GNUNET_YES != + MAH_handle_is_ready (mint)) + { + GNUNET_break (0); + return NULL; + } + + wdh_obj = json_pack ("{s:o}", + "wtid", TALER_json_from_data (wtid, + sizeof (struct TALER_WireTransferIdentifierRawP))); + + wdh = GNUNET_new (struct TALER_MINT_WireDepositsHandle); + wdh->mint = mint; + wdh->cb = cb; + wdh->cb_cls = cb_cls; + wdh->url = MAH_path_to_url (mint, "/wire/deposits"); + + eh = curl_easy_init (); + GNUNET_assert (NULL != (wdh->json_enc = + json_dumps (wdh_obj, + JSON_COMPACT))); + json_decref (wdh_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + wdh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + wdh->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (wdh->json_enc))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &wdh->db)); + ctx = MAH_handle_to_context (mint); + wdh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_wire_deposits_finished, + wdh); + return wdh; +} + + +/** + * Cancel wire deposits request. This function cannot be used on a request + * handle if a response is already served for it. + * + * @param wdh the wire deposits request handle + */ +void +TALER_MINT_wire_deposits_cancel (struct TALER_MINT_WireDepositsHandle *wdh) +{ + if (NULL != wdh->job) + { + MAC_job_cancel (wdh->job); + wdh->job = NULL; + } + GNUNET_free_non_null (wdh->db.buf); + GNUNET_free (wdh->url); + GNUNET_free (wdh->json_enc); + GNUNET_free (wdh); +} + + +/* end of mint_api_wire_deposits.c */ diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index fb4ee1b7..19c21398 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -1704,7 +1704,7 @@ handle_transaction_data (void *cls, */ int TMH_DB_execute_wire_deposits (struct MHD_Connection *connection, - const struct TALER_WireTransferIdentifierP *wtid) + const struct TALER_WireTransferIdentifierRawP *wtid) { int ret; struct WtidTransactionContext ctx; @@ -1720,7 +1720,7 @@ TMH_DB_execute_wire_deposits (struct MHD_Connection *connection, ctx.deposits = json_array (); ret = TMH_plugin->lookup_wire_transfer (TMH_plugin->cls, session, - &wtid->raw, + wtid, &handle_transaction_data, &ctx); if (GNUNET_SYSERR == ret) diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h index e366112d..0327bef2 100644 --- a/src/mint/taler-mint-httpd_db.h +++ b/src/mint/taler-mint-httpd_db.h @@ -202,7 +202,7 @@ TMH_DB_execute_admin_add_incoming (struct MHD_Connection *connection, */ int TMH_DB_execute_wire_deposits (struct MHD_Connection *connection, - const struct TALER_WireTransferIdentifierP *wtid); + const struct TALER_WireTransferIdentifierRawP *wtid); /** diff --git a/src/mint/taler-mint-httpd_tracking.c b/src/mint/taler-mint-httpd_tracking.c index e61b4bae..76293803 100644 --- a/src/mint/taler-mint-httpd_tracking.c +++ b/src/mint/taler-mint-httpd_tracking.c @@ -46,22 +46,17 @@ TMH_TRACKING_handler_wire_deposits (struct TMH_RequestHandler *rh, const char *upload_data, size_t *upload_data_size) { - struct TALER_WireTransferIdentifierP wtid; + struct TALER_WireTransferIdentifierRawP wtid; int res; res = TMH_PARSE_mhd_request_arg_data (connection, "wtid", &wtid, - sizeof (struct TALER_WireTransferIdentifierP)); + sizeof (struct TALER_WireTransferIdentifierRawP)); if (GNUNET_SYSERR == res) return MHD_NO; /* internal error */ if (GNUNET_NO == res) return MHD_YES; /* parse error */ - if (wtid.crc8 != - GNUNET_CRYPTO_crc8_n (&wtid.raw, - sizeof (wtid.raw))) - return TMH_RESPONSE_reply_arg_invalid (connection, - "wtid"); return TMH_DB_execute_wire_deposits (connection, &wtid); } -- cgit v1.2.3 From d779c5ee0267c9b11d0b3eb5160bd727f38397b1 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Jan 2016 15:29:07 +0100 Subject: fix DB setup --- src/mintdb/plugin_mintdb_postgres.c | 13 ++++++++----- src/mintdb/test_mintdb.c | 17 ++++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 38ce45eb..922feb5d 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -451,8 +451,8 @@ postgres_create_tables (void *cls, /* Table for the tracking API, mapping from wire transfer identifiers to transactions and back */ SQLEXEC("CREATE TABLE IF NOT EXISTS aggregation_tracking " - "(h_contract BYTEA PRIMARY KEY CHECK (LENGTH(h_contract)=64)" - ",h_wire BYTEA PRIMARY KEY CHECK (LENGTH(h_wire)=64)" + "(h_contract BYTEA CHECK (LENGTH(h_contract)=64)" + ",h_wire BYTEA CHECK (LENGTH(h_wire)=64)" ",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)" ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)" ",transaction_id INT8 NOT NULL" @@ -461,9 +461,12 @@ postgres_create_tables (void *cls, ",coin_amount_val INT8 NOT NULL" ",coin_amount_frac INT4 NOT NULL" ",coin_amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" - ",transaction_total_val INT8 NOT NULL" - ",transaction_total_frac INT4 NOT NULL" - ",transaction_total_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" + ",coin_fee_val INT8 NOT NULL" + ",coin_fee_frac INT4 NOT NULL" + ",coin_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" + ",transfer_total_val INT8 NOT NULL" + ",transfer_total_frac INT4 NOT NULL" + ",transfer_total_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" ")"); /* Index for lookup_transactions statement on wtid */ SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index " diff --git a/src/mintdb/test_mintdb.c b/src/mintdb/test_mintdb.c index 0cd660a4..8be858c2 100644 --- a/src/mintdb/test_mintdb.c +++ b/src/mintdb/test_mintdb.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014, 2015, 2016 GNUnet e.V. 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 @@ -305,8 +305,10 @@ test_melting (struct TALER_MINTDB_Session *session) RND_BLK (&refresh_session); RND_BLK (&session_hash); melts = NULL; + dkp = NULL; new_dkp = NULL; new_denom_pubs = NULL; + ret_denom_pubs = NULL; /* create and test a refresh session */ refresh_session.num_oldcoins = MELT_OLD_COINS; refresh_session.num_newcoins = 1; @@ -324,11 +326,11 @@ test_melting (struct TALER_MINTDB_Session *session) sizeof (refresh_session))); /* create a denomination (value: 1; fraction: 100) */ - dkp = create_denom_key_pair(512, session, - &value, - &fee_withdraw, - &fee_deposit, - &fee_refresh); + dkp = create_denom_key_pair (512, session, + &value, + &fee_withdraw, + &fee_deposit, + &fee_refresh); /* create MELT_OLD_COINS number of refresh melts */ melts = GNUNET_new_array (MELT_OLD_COINS, struct TALER_MINTDB_RefreshMelt); for (cnt=0; cnt < MELT_OLD_COINS; cnt++) @@ -416,7 +418,8 @@ test_melting (struct TALER_MINTDB_Session *session) ret = GNUNET_OK; drop: - destroy_denom_key_pair (dkp); + if (NULL != dkp) + destroy_denom_key_pair (dkp); if (NULL != melts) { for (cnt = 0; cnt < MELT_OLD_COINS; cnt++) -- cgit v1.2.3 From ad1edd6c893bab2b2e4fecfe5b578fdfaefe68cf Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Jan 2016 15:50:42 +0100 Subject: -remove dead code --- src/mint-lib/test_mint_api.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index 0e3b2bed..c00a0341 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -1695,8 +1695,6 @@ interpreter_run (void *cls, fail (is); return; } - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); } -- cgit v1.2.3 From 7864e625f1188543cb9af7ef7431c11c80a0ccc9 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Jan 2016 15:52:10 +0100 Subject: -check rval --- src/mintdb/perf_taler_mintdb_init.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/mintdb/perf_taler_mintdb_init.c b/src/mintdb/perf_taler_mintdb_init.c index 97a1b4c9..a7e2e26b 100644 --- a/src/mintdb/perf_taler_mintdb_init.c +++ b/src/mintdb/perf_taler_mintdb_init.c @@ -67,9 +67,11 @@ PERF_TALER_MINTDB_denomination_init () properties.expire_withdraw = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_()); properties.expire_spend = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_()); properties.expire_legal = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_()); - TALER_string_to_amount (CURRENCY ":1.1", &amount); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":1.1", &amount)); TALER_amount_hton (&properties.value, &amount); - TALER_string_to_amount (CURRENCY ":0.1", &amount); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":0.1", &amount)); TALER_amount_hton (&properties.fee_withdraw, &amount); TALER_amount_hton (&properties.fee_deposit, &amount); TALER_amount_hton (&properties.fee_refresh, &amount); @@ -502,10 +504,12 @@ PERF_TALER_MINTDB_refresh_melt_init (struct GNUNET_HashCode *session, &to_sign.purpose, &coin_sig.eddsa_signature); } - GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":1.1", - &amount)); - GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":0.1", - &amount_with_fee)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":1.1", + &amount)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":0.1", + &amount_with_fee)); melt = GNUNET_new (struct TALER_MINTDB_RefreshMelt); melt->coin.coin_pub = coin->public_info.coin_pub; melt->coin.denom_sig.rsa_signature = -- cgit v1.2.3 From 014b901b2cf7a307ab5a16750be72a8f27ef8e89 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Jan 2016 16:12:05 +0100 Subject: fix clean up logic of mint_api_refresh_link --- src/mint-lib/mint_api_refresh_link.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/mint-lib/mint_api_refresh_link.c b/src/mint-lib/mint_api_refresh_link.c index 9ae55b2a..dcd2326c 100644 --- a/src/mint-lib/mint_api_refresh_link.c +++ b/src/mint-lib/mint_api_refresh_link.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015 GNUnet e.V. + Copyright (C) 2015, 2016 GNUnet e.V. 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 @@ -185,6 +185,17 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh, return GNUNET_SYSERR; } num_coins = 0; + /* Theoretically, a coin may have been melted repeatedly + into different sessions; so the response is an array + which contains information by melting session. That + array contains another array. However, our API returns + a single 1d array, so we flatten the 2d array that is + returned into a single array. Note that usually a coin + is melted at most once, and so we'll only run this + loop once for 'session=0' in most cases. + + num_coins tracks the size of the 1d array we return, + whilst 'i' and 'session' track the 2d array. */ for (session=0;session Date: Thu, 21 Jan 2016 16:14:40 +0100 Subject: remove dead update --- src/mintdb/test_mintdb.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/mintdb/test_mintdb.c b/src/mintdb/test_mintdb.c index 8be858c2..4ced5f7b 100644 --- a/src/mintdb/test_mintdb.c +++ b/src/mintdb/test_mintdb.c @@ -458,7 +458,6 @@ run (void *cls, { struct TALER_MINTDB_Session *session; struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_Amount amount; struct DenomKeyPair *dkp; struct TALER_MINTDB_CollectableBlindcoin cbc; struct TALER_MINTDB_CollectableBlindcoin cbc2; @@ -566,11 +565,7 @@ run (void *cls, = GNUNET_CRYPTO_rsa_sign (dkp->priv.rsa_private_key, &cbc.h_coin_envelope, sizeof (cbc.h_coin_envelope)); - (void) memcpy (&cbc.reserve_pub, - &reserve_pub, - sizeof (reserve_pub)); - amount.value--; - amount.fraction--; + cbc.reserve_pub = reserve_pub; cbc.amount_with_fee = value; GNUNET_assert (GNUNET_OK == TALER_amount_get_zero (CURRENCY, &cbc.withdraw_fee)); @@ -655,9 +650,7 @@ run (void *cls, plugin->have_deposit (plugin->cls, session, &deposit)); - (void) memcpy (&deposit2, - &deposit, - sizeof (deposit)); + deposit2 = deposit; deposit2.transaction_id++; /* should fail if transaction id is different */ FAILIF (GNUNET_NO != plugin->have_deposit (plugin->cls, @@ -669,9 +662,7 @@ run (void *cls, plugin->have_deposit (plugin->cls, session, &deposit2)); - (void) memcpy (&deposit2.merchant_pub, - &deposit.merchant_pub, - sizeof (deposit.merchant_pub)); + deposit2.merchant_pub = deposit.merchant_pub; RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */ FAILIF (GNUNET_NO != plugin->have_deposit (plugin->cls, -- cgit v1.2.3 From 440039da26fa0fda03aa49b7f7bc755514f63110 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Jan 2016 16:16:23 +0100 Subject: fix code order --- src/mintdb/perf_taler_mintdb_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/mintdb/perf_taler_mintdb_init.c b/src/mintdb/perf_taler_mintdb_init.c index a7e2e26b..ccfc6a05 100644 --- a/src/mintdb/perf_taler_mintdb_init.c +++ b/src/mintdb/perf_taler_mintdb_init.c @@ -469,8 +469,8 @@ PERF_TALER_MINTDB_refresh_session_free (struct TALER_MINTDB_RefreshSession *refr { if (NULL == refresh_session) return GNUNET_OK; - return GNUNET_OK; GNUNET_free (refresh_session); + return GNUNET_OK; } -- cgit v1.2.3 From 46a10b12b8723d21e9c101525c7fb2a41b5c0d16 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 22 Jan 2016 13:45:32 +0100 Subject: add testcase for aggregation API --- src/mintdb/plugin_mintdb_postgres.c | 2 +- src/mintdb/test_mintdb.c | 177 +++++++++++++++++++++++++++++++++++- 2 files changed, 177 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 922feb5d..6da873d5 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -711,7 +711,7 @@ postgres_prepare (PGconn *db_conn) "SELECT" " denom_pub" ",denom_sig" - " FROM known_coins " + " FROM known_coins" " WHERE coin_pub=$1", 1, NULL); /* Used in #postgres_insert_known_coin() to store diff --git a/src/mintdb/test_mintdb.c b/src/mintdb/test_mintdb.c index 4ced5f7b..07f5d078 100644 --- a/src/mintdb/test_mintdb.c +++ b/src/mintdb/test_mintdb.c @@ -14,7 +14,7 @@ TALER; see the file COPYING. If not, If not, see */ /** - * @file mint/test_mintdb.c + * @file mintdb/test_mintdb.c * @brief test cases for DB interaction functions * @author Sree Harsha Totakura */ @@ -442,6 +442,114 @@ test_melting (struct TALER_MINTDB_Session *session) } +/** + * Callback that should never be called. + */ +static void +cb_wt_never (void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_wire, + const struct GNUNET_HashCode *h_contract, + uint64_t transaction_id, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *coin_value, + const struct TALER_Amount *coin_fee, + const struct TALER_Amount *transfer_value) +{ + GNUNET_assert (0); /* this statement should be unreachable */ +} + + +/** + * Callback that should never be called. + */ +static void +cb_wtid_never (void *cls, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *coin_fee, + const struct TALER_Amount *total_amount, + struct GNUNET_TIME_Absolute execution_time) +{ + GNUNET_assert (0); +} + + +static struct TALER_MerchantPublicKeyP merchant_pub_wt; +static struct GNUNET_HashCode h_wire_wt; +static struct GNUNET_HashCode h_contract_wt; +static uint64_t transaction_id_wt; +static struct TALER_CoinSpendPublicKeyP coin_pub_wt; +static struct TALER_Amount coin_value_wt; +static struct TALER_Amount coin_fee_wt; +static struct TALER_Amount transfer_value_wt; +static struct GNUNET_TIME_Absolute execution_time_wt; +static struct TALER_WireTransferIdentifierRawP wtid_wt; + + +/** + * Callback that should be called with the WT data. + */ +static void +cb_wt_check (void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_wire, + const struct GNUNET_HashCode *h_contract, + uint64_t transaction_id, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *coin_value, + const struct TALER_Amount *coin_fee, + const struct TALER_Amount *transfer_value) +{ + GNUNET_assert (cls == &cb_wt_never); + GNUNET_assert (0 == memcmp (merchant_pub, + &merchant_pub_wt, + sizeof (struct TALER_MerchantPublicKeyP))); + GNUNET_assert (0 == memcmp (h_wire, + &h_wire_wt, + sizeof (struct GNUNET_HashCode))); + GNUNET_assert (0 == memcmp (h_contract, + &h_contract_wt, + sizeof (struct GNUNET_HashCode))); + GNUNET_assert (transaction_id == transaction_id_wt); + GNUNET_assert (0 == memcmp (coin_pub, + &coin_pub_wt, + sizeof (struct TALER_CoinSpendPublicKeyP))); + GNUNET_assert (0 == TALER_amount_cmp (coin_value, + &coin_value_wt)); + GNUNET_assert (0 == TALER_amount_cmp (coin_fee, + &coin_fee_wt)); + GNUNET_assert (0 == TALER_amount_cmp (transfer_value, + &transfer_value_wt)); +} + + +/** + * Callback that should be called with the WT data. + */ +static void +cb_wtid_check (void *cls, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *coin_fee, + const struct TALER_Amount *total_amount, + struct GNUNET_TIME_Absolute execution_time) +{ + GNUNET_assert (cls == &cb_wtid_never); + GNUNET_assert (0 == memcmp (wtid, + &wtid_wt, + sizeof (struct TALER_WireTransferIdentifierRawP))); + GNUNET_assert (execution_time.abs_value_us == + execution_time_wt.abs_value_us); + GNUNET_assert (0 == TALER_amount_cmp (coin_contribution, + &coin_value_wt)); + GNUNET_assert (0 == TALER_amount_cmp (coin_fee, + &coin_fee_wt)); + GNUNET_assert (0 == TALER_amount_cmp (total_amount, + &transfer_value_wt)); +} + + /** * Main function that will be run by the scheduler. * @@ -467,6 +575,7 @@ run (void *cls, struct TALER_MINTDB_CollectableBlindcoin *withdraw; struct TALER_MINTDB_Deposit deposit; struct TALER_MINTDB_Deposit deposit2; + struct TALER_WireTransferIdentifierRawP wtid; json_t *wire; json_t *just; const char * const json_wire_str = @@ -669,6 +778,72 @@ run (void *cls, session, &deposit2)); FAILIF (GNUNET_OK != test_melting (session)); + + /* setup values for wire transfer aggregation data */ + memset (&wtid, 42, sizeof (wtid)); + memset (&merchant_pub_wt, 43, sizeof (merchant_pub_wt)); + memset (&h_wire_wt, 44, sizeof (h_wire_wt)); + memset (&h_contract_wt, 45, sizeof (h_contract_wt)); + memset (&coin_pub_wt, 46, sizeof (coin_pub_wt)); + transaction_id_wt = 47; + execution_time_wt = GNUNET_TIME_absolute_get (); + memset (&merchant_pub_wt, 48, sizeof (merchant_pub_wt)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY "KUDOS:1.000010", + &coin_value_wt)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY "KUDOS:0.000010", + &coin_fee_wt)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY "KUDOS:1.000000", + &transfer_value_wt)); + + FAILIF (GNUNET_NO != + plugin->lookup_wire_transfer (plugin->cls, + session, + &wtid_wt, + &cb_wt_never, + NULL)); + FAILIF (GNUNET_NO != + plugin->wire_lookup_deposit_wtid (plugin->cls, + session, + &h_contract_wt, + &h_wire_wt, + &coin_pub_wt, + &merchant_pub_wt, + transaction_id_wt, + &cb_wtid_never, + NULL)); + /* insert WT data */ + FAILIF (GNUNET_OK != + plugin->insert_aggregation_tracking (plugin->cls, + session, + &wtid_wt, + &merchant_pub_wt, + &h_wire_wt, + &h_contract_wt, + transaction_id_wt, + execution_time_wt, + &coin_pub_wt, + &coin_value_wt, + &coin_fee_wt, + &transfer_value_wt)); + FAILIF (GNUNET_OK != + plugin->lookup_wire_transfer (plugin->cls, + session, + &wtid_wt, + &cb_wt_check, + &cb_wt_never)); + FAILIF (GNUNET_OK != + plugin->wire_lookup_deposit_wtid (plugin->cls, + session, + &h_contract_wt, + &h_wire_wt, + &coin_pub_wt, + &merchant_pub_wt, + transaction_id_wt, + &cb_wtid_check, + &cb_wtid_never)); result = 0; drop: -- cgit v1.2.3 From dbfb2f7163e1c14a26785f4f0fcb5db6152bff57 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 22 Jan 2016 15:29:31 +0100 Subject: extending test driver interpreter with commands to exercise aggregation API (not yet actually executed) --- src/mint-lib/test_mint_api.c | 309 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 308 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index c00a0341..d8da7509 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -111,7 +111,17 @@ enum OpCode /** * Verify the mint's /wire-method. */ - OC_WIRE + OC_WIRE, + + /** + * Verify mint's /wire/deposits method. + */ + OC_WIRE_DEPOSITS, + + /** + * Verify mint's /deposit/wtid method. + */ + OC_DEPOSIT_WTID }; @@ -470,6 +480,60 @@ struct Command } wire; + /** + * Information for the /wire/deposits's command. + */ + struct { + + /** + * Handle to the wire deposits request. + */ + struct TALER_MINT_WireDepositsHandle *wdh; + + /** + * Reference to a /deposit/wtid command. If set, we use the + * WTID from that command. + */ + const char *wtid_ref; + + /** + * WTID to use (used if @e wtid_ref is NULL). + */ + struct TALER_WireTransferIdentifierRawP wtid; + + /* TODO: may want to add list of deposits we expected + to see aggregated here in the future. */ + + } wire_deposits; + + /** + * Information for the /deposit/wtid command. + */ + struct { + + /** + * Handle to the deposit wtid request. + */ + struct TALER_MINT_DepositWtidHandle *dwh; + + /** + * Which /deposit operation should we obtain WTID data for? + */ + const char *deposit_ref; + + /** + * What is the expected total amount? Only used if + * @e expected_response_code was #MHD_HTTP_OK. + */ + struct TALER_Amount total_amount_expected; + + /** + * Wire transfer identifier, set if #MHD_HTTP_OK was the response code. + */ + struct TALER_WireTransferIdentifierRawP wtid; + + } deposit_wtid; + } details; }; @@ -1219,6 +1283,156 @@ wire_cb (void *cls, } +/** + * 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 mint protocol violation + * @param json original json reply (may include signatures, those have then been + * validated already) + * @param wtid extracted wire transfer identifier, or NULL if the mint could + * not provide any (set only if @a http_status is #MHD_HTTP_OK) + * @param total_amount total amount of the wire transfer, or NULL if the mint could + * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK) + * @param details_length length of the @a details array + * @param details array with details about the combined transactions + */ +static void +wire_deposits_cb (void *cls, + unsigned int http_status, + json_t *json, + const struct GNUNET_HashCode *h_wire, + const struct TALER_Amount *total_amount, + unsigned int details_length, + const struct TALER_WireDepositDetails *details) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + const struct Command *ref; + + ref = find_command (is, + cmd->details.wire_deposits.wtid_ref); + 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); + json_dumpf (json, stderr, 0); + fail (is); + return; + } + switch (http_status) + { + case MHD_HTTP_OK: + if (0 != TALER_amount_cmp (total_amount, + &ref->details.deposit_wtid.total_amount_expected)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Total amount missmatch to command %s\n", + http_status, + cmd->label); + json_dumpf (json, stderr, 0); + fail (is); + return; + } + if (NULL != ref->details.deposit_wtid.deposit_ref) + { + const struct Command *dep; + struct GNUNET_HashCode hw; + + dep = find_command (is, + ref->details.deposit_wtid.deposit_ref); + GNUNET_CRYPTO_hash (dep->details.deposit.wire_details, + strlen (dep->details.deposit.wire_details), + &hw); + if (0 != memcmp (&hw, + h_wire, + sizeof (struct GNUNET_HashCode))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Wire hash missmatch to command %s\n", + cmd->label); + json_dumpf (json, stderr, 0); + fail (is); + return; + } + } + break; + default: + break; + } + + /* move to next command */ + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); +} + + +/** + * Function called with detailed wire transfer data. + * + * @param cls closure + * @param http_status HTTP status code we got, 0 on mint protocol violation + * @param json original json reply (may include signatures, those have then been + * validated already) + * @param wtid wire transfer identifier used by the mint, NULL if mint 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 mint 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, + json_t *json, + const struct TALER_WireTransferIdentifierRawP *wtid, + struct GNUNET_TIME_Absolute execution_time, + const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *total_amount) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + + 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); + json_dumpf (json, stderr, 0); + fail (is); + return; + } + switch (http_status) + { + case MHD_HTTP_OK: + cmd->details.deposit_wtid.wtid = *wtid; + if (0 != TALER_amount_cmp (total_amount, + &cmd->details.deposit_wtid.total_amount_expected)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Total amount missmatch to command %s\n", + cmd->label); + json_dumpf (json, stderr, 0); + fail (is); + return; + } + break; + default: + break; + } + + /* move to next command */ + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); +} + + /** * Run the main interpreter loop that performs mint operations. * @@ -1686,6 +1900,85 @@ interpreter_run (void *cls, is); trigger_context_task (); return; + case OC_WIRE_DEPOSITS: + if (NULL != cmd->details.wire_deposits.wtid_ref) + { + ref = find_command (is, + cmd->details.wire_deposits.wtid_ref); + GNUNET_assert (NULL != ref); + cmd->details.wire_deposits.wtid = ref->details.deposit_wtid.wtid; + } + cmd->details.wire_deposits.wdh + = TALER_MINT_wire_deposits (mint, + &cmd->details.wire_deposits.wtid, + &wire_deposits_cb, + is); + trigger_context_task (); + return; + case OC_DEPOSIT_WTID: + { + struct GNUNET_HashCode h_wire; + struct GNUNET_HashCode h_contract; + json_t *wire; + json_t *contract; + const struct Command *coin; + struct TALER_CoinSpendPublicKeyP coin_pub; + + ref = find_command (is, + cmd->details.deposit_wtid.deposit_ref); + GNUNET_assert (NULL != ref); + coin = find_command (is, + ref->details.deposit.coin_ref); + GNUNET_assert (NULL != coin); + switch (coin->oc) + { + case OC_WITHDRAW_SIGN: + GNUNET_CRYPTO_eddsa_key_get_public (&coin->details.reserve_withdraw.coin_priv.eddsa_priv, + &coin_pub.eddsa_pub); + break; + case OC_REFRESH_REVEAL: + { + const struct FreshCoin *fc; + unsigned int idx; + + idx = ref->details.deposit.coin_idx; + GNUNET_assert (idx < coin->details.refresh_reveal.num_fresh_coins); + fc = &coin->details.refresh_reveal.fresh_coins[idx]; + + GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv, + &coin_pub.eddsa_pub); + } + break; + default: + GNUNET_assert (0); + } + + wire = json_loads (ref->details.deposit.wire_details, + JSON_REJECT_DUPLICATES, + NULL); + GNUNET_assert (NULL != wire); + TALER_hash_json (wire, + &h_wire); + json_decref (wire); + contract = json_loads (ref->details.deposit.contract, + JSON_REJECT_DUPLICATES, + NULL); + GNUNET_assert (NULL != contract); + TALER_hash_json (contract, + &h_contract); + json_decref (contract); + cmd->details.deposit_wtid.dwh + = TALER_MINT_deposit_wtid (mint, + &ref->details.deposit.merchant_priv, + &h_wire, + &h_contract, + &coin_pub, + ref->details.deposit.transaction_id, + &deposit_wtid_cb, + is); + trigger_context_task (); + } + return; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown instruction %d at %u (%s)\n", @@ -1831,6 +2124,20 @@ do_shutdown (void *cls, cmd->details.wire.wh = NULL; } break; + case OC_WIRE_DEPOSITS: + if (NULL != cmd->details.wire_deposits.wdh) + { + TALER_MINT_wire_deposits_cancel (cmd->details.wire_deposits.wdh); + cmd->details.wire_deposits.wdh = NULL; + } + break; + case OC_DEPOSIT_WTID: + if (NULL != cmd->details.deposit_wtid.dwh) + { + TALER_MINT_deposit_wtid_cancel (cmd->details.deposit_wtid.dwh); + cmd->details.deposit_wtid.dwh = NULL; + } + break; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown instruction %d at %u (%s)\n", -- cgit v1.2.3 From c2fe7e8ee9bc658b3934f6ea5a5c2b28f8b7053f Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 22 Jan 2016 15:50:45 +0100 Subject: sketched first part of testcase, currently failing (bad signature: 401) --- src/mint-lib/test_mint_api.c | 45 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index d8da7509..356441bf 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -1311,6 +1311,7 @@ wire_deposits_cb (void *cls, struct Command *cmd = &is->commands[is->ip]; const struct Command *ref; + cmd->details.wire_deposits.wdh = NULL; ref = find_command (is, cmd->details.wire_deposits.wtid_ref); if (cmd->expected_response_code != http_status) @@ -1397,6 +1398,7 @@ deposit_wtid_cb (void *cls, struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; + cmd->details.deposit_wtid.dwh = NULL; if (cmd->expected_response_code != http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -2352,7 +2354,7 @@ run (void *cls, .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.contract = "{ \"items\":[{ \"name\":\"ice cream\", \"value\":1 }]}", .details.deposit.transaction_id = 1 }, /* Try to overdraw funds ... */ @@ -2369,7 +2371,7 @@ run (void *cls, .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.contract = "{ \"items\":[{ \"name\":\"ice cream\", \"value\":1 } ] }", .details.deposit.transaction_id = 1 }, /* Try to double-spend the 5 EUR coin at the same merchant (but different transaction ID) */ @@ -2379,7 +2381,7 @@ run (void *cls, .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.contract = "{ \"items\":[\"name\":\"ice cream\", \"value\":1 }] }", .details.deposit.transaction_id = 2 }, /* Try to double-spend the 5 EUR coin at the same merchant (but different contract) */ @@ -2389,7 +2391,7 @@ run (void *cls, .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.contract = "{ \"items\":[{ \"name\":\"ice cream\", \"value\":2 } ] }", .details.deposit.transaction_id = 1 }, /* ***************** /refresh testing ******************** */ @@ -2414,7 +2416,7 @@ run (void *cls, .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.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) */ @@ -2448,7 +2450,7 @@ run (void *cls, .details.deposit.coin_ref = "refresh-reveal-1", .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.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }", .details.deposit.transaction_id = 2 }, /* Test successfully spending coins from the refresh operation: @@ -2460,7 +2462,7 @@ run (void *cls, .details.deposit.coin_ref = "refresh-reveal-1", .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.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }", .details.deposit.transaction_id = 2 }, /* Test running a failing melt operation (same operation again must fail) */ @@ -2473,6 +2475,35 @@ run (void *cls, // 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 mint knows about the deposit, but has no WTID yet. */ + { .oc = OC_DEPOSIT_WTID, + .label = "deposit-wtid-found", + .expected_response_code = MHD_HTTP_FOUND, + .details.deposit_wtid.deposit_ref = "deposit-simple" }, + /* Try resolving a deposit's WTID for a failed deposit. + As the deposit failed, the answer should be that + the mint does NOT know about the deposit. */ + { .oc = OC_DEPOSIT_WTID, + .label = "deposit-wtid-failing", + .expected_response_code = MHD_HTTP_NOT_FOUND, + .details.deposit_wtid.deposit_ref = "deposit-double-2" }, + /* Try resolving an undefined (all zeros) WTID; this + should fail as obviously the mint didn't use that + WTID value for any transaction. */ + { .oc = OC_WIRE_DEPOSITS, + .label = "wire-deposit-failing", + .expected_response_code = MHD_HTTP_NOT_FOUND }, + + /* TODO: trigger aggregation logic and then check the + cases where tracking succeeds! */ + + /* ************** End of tracking API testing************* */ + + #endif { .oc = OC_END } -- cgit v1.2.3 From 5601a81d4dbefcd62ff84b2d6b8802046dc8520a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 22 Jan 2016 16:52:52 +0100 Subject: do not have curl be verbose --- src/mint-lib/mint_api_deposit_wtid.c | 1 - src/mint-lib/mint_api_handle.c | 2 +- src/mint/taler-mint-httpd_tracking.c | 5 ++--- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/mint-lib/mint_api_deposit_wtid.c b/src/mint-lib/mint_api_deposit_wtid.c index bb81d93e..50f9c55d 100644 --- a/src/mint-lib/mint_api_deposit_wtid.c +++ b/src/mint-lib/mint_api_deposit_wtid.c @@ -300,7 +300,6 @@ TALER_MINT_deposit_wtid (struct TALER_MINT_Handle *mint, GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv, &dtp.purpose, &merchant_sig.eddsa_sig)); - deposit_wtid_obj = json_pack ("{s:o, s:o," /* H_wire, H_contract */ " s:o, s:I," /* coin_pub, transaction_id */ " s:o, s:o}", /* merchant_pub, merchant_sig */ diff --git a/src/mint-lib/mint_api_handle.c b/src/mint-lib/mint_api_handle.c index 077e42c2..c8898d5c 100644 --- a/src/mint-lib/mint_api_handle.c +++ b/src/mint-lib/mint_api_handle.c @@ -757,7 +757,7 @@ TALER_MINT_connect (struct TALER_MINT_Context *ctx, GNUNET_assert (CURLE_OK == curl_easy_setopt (c, CURLOPT_VERBOSE, - 1)); + 0)); GNUNET_assert (CURLE_OK == curl_easy_setopt (c, CURLOPT_STDERR, diff --git a/src/mint/taler-mint-httpd_tracking.c b/src/mint/taler-mint-httpd_tracking.c index 76293803..a6b41cf8 100644 --- a/src/mint/taler-mint-httpd_tracking.c +++ b/src/mint/taler-mint-httpd_tracking.c @@ -121,13 +121,12 @@ TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh, struct TALER_DepositTrackPS tps; uint64_t transaction_id; struct TALER_MerchantSignatureP merchant_sig; - struct TALER_MerchantPublicKeyP merchant_pub; struct TMH_PARSE_FieldSpecification spec[] = { TMH_PARSE_member_fixed ("H_wire", &tps.h_wire), TMH_PARSE_member_fixed ("H_contract", &tps.h_contract), TMH_PARSE_member_fixed ("coin_pub", &tps.coin_pub), TMH_PARSE_member_uint64 ("transaction_id", &transaction_id), - TMH_PARSE_member_fixed ("merchant_pub", &merchant_pub), + TMH_PARSE_member_fixed ("merchant_pub", &tps.merchant), TMH_PARSE_member_fixed ("merchant_sig", &merchant_sig), TMH_PARSE_MEMBER_END }; @@ -154,7 +153,7 @@ TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh, tps.transaction_id = GNUNET_htonll (transaction_id); res = check_and_handle_deposit_wtid_request (connection, &tps, - &merchant_pub, + &tps.merchant, &merchant_sig, transaction_id); TMH_PARSE_release_data (spec); -- cgit v1.2.3 From 0659100bdff687d87a2cb0242fc26a18d70c710b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 22 Jan 2016 17:21:14 +0100 Subject: use correct field name --- src/mintdb/plugin_mintdb_postgres.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 6da873d5..8da98671 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -3620,7 +3620,7 @@ postgres_wire_lookup_deposit_wtid (void *cls, if (0 == nrows) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "lookup_wire_transfer() returned 0 matching rows\n"); + "lookup_deposit_wtid returned 0 matching rows\n"); PQclear (result); /* Check if transaction exists in deposits, so that we just @@ -3649,6 +3649,8 @@ postgres_wire_lookup_deposit_wtid (void *cls, nrows = PQntuples (result); if (0 == nrows) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "get_deposit_for_wtid returned 0 matching rows\n"); PQclear (result); return GNUNET_NO; } @@ -3660,11 +3662,12 @@ postgres_wire_lookup_deposit_wtid (void *cls, struct TALER_Amount coin_amount; struct TALER_Amount coin_fee; struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_amount ("coin_amount", &coin_amount), + TALER_PQ_result_spec_amount ("amount_with_fee", &coin_amount), TALER_PQ_result_spec_amount ("deposit_fee", &coin_fee), TALER_PQ_result_spec_absolute_time ("wire_deadline", &exec_time), TALER_PQ_result_spec_end }; + if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)) { GNUNET_break (0); -- cgit v1.2.3 From 2fc6afe94693e826795f5cda768c6b3a9b11f2dd Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 22 Jan 2016 17:21:36 +0100 Subject: more robust error handling --- src/mint/taler-mint-httpd_db.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 19c21398..c39cbbcf 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014, 2015, 2016 GNUnet e.V. 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 @@ -1887,7 +1887,7 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection, ctx.h_wire = *h_wire; ctx.coin_pub = *coin_pub; ctx.transaction_id = transaction_id; - ctx.res = MHD_NO; /* this value should never be read... */ + ctx.res = GNUNET_SYSERR; ret = TMH_plugin->wire_lookup_deposit_wtid (TMH_plugin->cls, session, h_contract, @@ -1900,10 +1900,20 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection, if (GNUNET_SYSERR == ret) { GNUNET_break (0); + GNUNET_break (GNUNET_SYSERR == ctx.res); return TMH_RESPONSE_reply_internal_db_error (connection); } if (GNUNET_NO == ret) + { + GNUNET_break (GNUNET_SYSERR == ctx.res); return TMH_RESPONSE_reply_deposit_unknown (connection); + } + if (GNUNET_SYSERR == ctx.res) + { + GNUNET_break (0); + return TMH_RESPONSE_reply_internal_error (connection, + "bug resolving deposit wtid"); + } return ctx.res; } -- cgit v1.2.3 From dcf1a03a9387c4b1e98dac141da19a87204d07e7 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 22 Jan 2016 17:21:42 +0100 Subject: fix test logic --- src/mint-lib/test_mint_api.c | 58 ++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index 356441bf..60ef8c78 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014, 2015, 2016 GNUnet e.V. 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 @@ -1617,7 +1617,9 @@ interpreter_run (void *cls, struct GNUNET_TIME_Absolute refund_deadline; struct GNUNET_TIME_Absolute wire_deadline; struct GNUNET_TIME_Absolute timestamp; + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; struct TALER_MerchantPublicKeyP merchant_pub; + json_t *contract; json_t *wire; GNUNET_assert (NULL != @@ -1660,37 +1662,51 @@ interpreter_run (void *cls, fail (is); return; } - GNUNET_CRYPTO_hash (cmd->details.deposit.contract, - strlen (cmd->details.deposit.contract), - &h_contract); + contract = json_loads (cmd->details.deposit.contract, + JSON_REJECT_DUPLICATES, + NULL); + if (NULL == contract) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse contract details `%s' at %u/%s\n", + cmd->details.deposit.contract, + is->ip, + cmd->label); + fail (is); + return; + } + TALER_hash_json (contract, + &h_contract); wire = json_loads (cmd->details.deposit.wire_details, JSON_REJECT_DUPLICATES, NULL); if (NULL == wire) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse wire details `%s' at %u\n", + "Failed to parse wire details `%s' at %u/%s\n", cmd->details.deposit.wire_details, - is->ip); + is->ip, + cmd->label); fail (is); return; } GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, &coin_pub.eddsa_pub); + priv = GNUNET_CRYPTO_eddsa_key_create (); + cmd->details.deposit.merchant_priv.eddsa_priv = *priv; + GNUNET_free (priv); if (0 != cmd->details.deposit.refund_deadline.rel_value_us) { - struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - - priv = GNUNET_CRYPTO_eddsa_key_create (); - cmd->details.deposit.merchant_priv.eddsa_priv = *priv; - GNUNET_free (priv); refund_deadline = GNUNET_TIME_relative_to_absolute (cmd->details.deposit.refund_deadline); } else { refund_deadline = GNUNET_TIME_UNIT_ZERO_ABS; } + GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.deposit.merchant_priv.eddsa_priv, + &merchant_pub.eddsa_pub); + wire_deadline = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS); timestamp = GNUNET_TIME_absolute_get (); TALER_round_abs_time (×tamp); @@ -2122,6 +2138,10 @@ do_shutdown (void *cls, case OC_WIRE: if (NULL != cmd->details.wire.wh) { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); TALER_MINT_wire_cancel (cmd->details.wire.wh); cmd->details.wire.wh = NULL; } @@ -2129,6 +2149,10 @@ do_shutdown (void *cls, case OC_WIRE_DEPOSITS: if (NULL != cmd->details.wire_deposits.wdh) { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); TALER_MINT_wire_deposits_cancel (cmd->details.wire_deposits.wdh); cmd->details.wire_deposits.wdh = NULL; } @@ -2136,6 +2160,10 @@ do_shutdown (void *cls, case OC_DEPOSIT_WTID: if (NULL != cmd->details.deposit_wtid.dwh) { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); TALER_MINT_deposit_wtid_cancel (cmd->details.deposit_wtid.dwh); cmd->details.deposit_wtid.dwh = NULL; } @@ -2354,7 +2382,7 @@ run (void *cls, .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.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }", .details.deposit.transaction_id = 1 }, /* Try to overdraw funds ... */ @@ -2371,7 +2399,7 @@ run (void *cls, .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.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }", .details.deposit.transaction_id = 1 }, /* Try to double-spend the 5 EUR coin at the same merchant (but different transaction ID) */ @@ -2381,7 +2409,7 @@ run (void *cls, .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.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }", .details.deposit.transaction_id = 2 }, /* Try to double-spend the 5 EUR coin at the same merchant (but different contract) */ @@ -2416,7 +2444,7 @@ run (void *cls, .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.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) */ -- cgit v1.2.3 From e6432cd1ff4c66f1c3186c2acdb45b9cc1d599c5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 22 Jan 2016 17:24:08 +0100 Subject: use correct status code --- src/mint/taler-mint-httpd_responses.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index 23aa9e8b..041f694b 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -1081,7 +1081,7 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection, struct GNUNET_TIME_Absolute planned_exec_time) { return TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_FOUND, + MHD_HTTP_ACCEPTED, "{s:o}", "execution_time", TALER_json_from_abs (planned_exec_time)); } -- cgit v1.2.3 From d6553966f11e24f8f86aa21c0ce3760b6e966006 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 22 Jan 2016 17:32:46 +0100 Subject: /wire/deposits expects to use GET, not POST --- src/mint-lib/mint_api_wire_deposits.c | 36 ++++++++++++----------------------- src/mint-lib/test_mint_api.c | 2 +- 2 files changed, 13 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/mint-lib/mint_api_wire_deposits.c b/src/mint-lib/mint_api_wire_deposits.c index 6fd4d75d..f71c5b69 100644 --- a/src/mint-lib/mint_api_wire_deposits.c +++ b/src/mint-lib/mint_api_wire_deposits.c @@ -48,11 +48,6 @@ struct TALER_MINT_WireDepositsHandle */ char *url; - /** - * JSON encoding of the request to POST. - */ - char *json_enc; - /** * Handle for the request. */ @@ -217,7 +212,8 @@ TALER_MINT_wire_deposits (struct TALER_MINT_Handle *mint, { struct TALER_MINT_WireDepositsHandle *wdh; struct TALER_MINT_Context *ctx; - json_t *wdh_obj; + char *buf; + char *path; CURL *eh; if (GNUNET_YES != @@ -227,33 +223,26 @@ TALER_MINT_wire_deposits (struct TALER_MINT_Handle *mint, return NULL; } - wdh_obj = json_pack ("{s:o}", - "wtid", TALER_json_from_data (wtid, - sizeof (struct TALER_WireTransferIdentifierRawP))); - wdh = GNUNET_new (struct TALER_MINT_WireDepositsHandle); wdh->mint = mint; wdh->cb = cb; wdh->cb_cls = cb_cls; - wdh->url = MAH_path_to_url (mint, "/wire/deposits"); + + buf = GNUNET_STRINGS_data_to_string_alloc (wtid, + sizeof (struct TALER_WireTransferIdentifierRawP)); + GNUNET_asprintf (&path, + "/wire/deposits?wtid=%s", + buf); + wdh->url = MAH_path_to_url (wdh->mint, + path); + GNUNET_free (buf); + GNUNET_free (path); eh = curl_easy_init (); - GNUNET_assert (NULL != (wdh->json_enc = - json_dumps (wdh_obj, - JSON_COMPACT))); - json_decref (wdh_obj); GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, CURLOPT_URL, wdh->url)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_POSTFIELDS, - wdh->json_enc)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_POSTFIELDSIZE, - strlen (wdh->json_enc))); GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, CURLOPT_WRITEFUNCTION, @@ -288,7 +277,6 @@ TALER_MINT_wire_deposits_cancel (struct TALER_MINT_WireDepositsHandle *wdh) } GNUNET_free_non_null (wdh->db.buf); GNUNET_free (wdh->url); - GNUNET_free (wdh->json_enc); GNUNET_free (wdh); } diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index 60ef8c78..e41d0180 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -2510,7 +2510,7 @@ run (void *cls, the mint knows about the deposit, but has no WTID yet. */ { .oc = OC_DEPOSIT_WTID, .label = "deposit-wtid-found", - .expected_response_code = MHD_HTTP_FOUND, + .expected_response_code = MHD_HTTP_ACCEPTED, .details.deposit_wtid.deposit_ref = "deposit-simple" }, /* Try resolving a deposit's WTID for a failed deposit. As the deposit failed, the answer should be that -- cgit v1.2.3 From b4075653516167f96464afa366482e1618e4b06b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 24 Jan 2016 16:26:54 +0100 Subject: defining wire plugin API --- src/include/Makefile.am | 3 +- src/include/taler_wire_plugin.h | 178 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 src/include/taler_wire_plugin.h (limited to 'src') diff --git a/src/include/Makefile.am b/src/include/Makefile.am index bfdcbe7c..2f3973c4 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -20,7 +20,8 @@ talerinclude_HEADERS = \ taler_mintdb_lib.h \ taler_mintdb_plugin.h \ taler_pq_lib.h \ - taler_signatures.h + taler_signatures.h \ + taler_wire_plugin.h endif diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h new file mode 100644 index 00000000..fd86ca28 --- /dev/null +++ b/src/include/taler_wire_plugin.h @@ -0,0 +1,178 @@ +/* + This file is part of TALER + Copyright (C) 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file include/taler_wire_plugin.h + * @brief Plugin API for the handling of wire transactions + * @author Christian Grothoff + */ +#ifndef TALER_WIRE_PLUGIN_H +#define TALER_WIRE_PLUGIN_H + +#include + + +/** + * Callback with prepared transaction. + * + * @param cls closure + * @param buf transaction data to persist + * @param buf_size number of bytes in @a buf + */ +typedef void +(*TALER_WIRE_PrepareTransactionCallback) (void *cls, + const char *buf, + size_t buf_size); + + +/** + * Handle returned for cancelling a preparation step. + */ +struct TALER_WIRE_PrepareHandle; + + +/** + * Handle returned for cancelling an execution step. + */ +struct TALER_WIRE_ExecuteHandle; + + +/** + * Function called with the result from the execute step. + * + * @param cls closure + * @param success #GNUNET_OK on success, #GNUNET_SYSERR on failure + * @param emsg NULL on success, otherwise an error message + */ +typedef void +(*TALER_WIRE_ConfirmationCallback)(void *cls, + int success, + const char *emsg); + + +/** + * @brief The plugin API, returned from the plugin's "init" function. + * The argument given to "init" is simply a configuration handle. + */ +struct TALER_WIRE_Plugin +{ + + /** + * Closure for all callbacks. + */ + void *cls; + + /** + * Name of the library which generated this plugin. Set by the + * plugin loader. + */ + char *library_name; + + /** + * Round amount DOWN to the amount that can be transferred via the wire + * method. For example, Taler may support 0.000001 EUR as a unit of + * payment, but SEPA only supports 0.01 EUR. This function would + * round 0.125 EUR to 0.12 EUR in this case. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param[in,out] amount amount to round down + * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary, + * #GNUNET_SYSERR if the amount or currency was invalid + */ + int + (*amount_round) (void *cls, + struct TALER_Amount *amount); + + + /** + * Check if the given wire format JSON object is correctly formatted + * + * @param wire the JSON wire format object + * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not + */ + int + wire_validate (const json_t *wire); + + + /** + * Prepare for exeuction of a wire transfer. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param wire valid wire account information + * @param amount amount to transfer, already rounded + * @param wtid wire transfer identifier to use + * @param ptc function to call with the prepared data to persist + * @param ptc_cls closure for @a ptc + * @return NULL on failure + */ + struct TALER_WIRE_PrepareHandle * + (*prepare_wire_transfer) (void *cls, + const json_t *wire, + const struct TALER_Amount *amount, + const void *wtid, + TALER_WIRE_PrepareTransactionCallback ptc, + void *ptc_cls); + + /** + * Abort preparation of a wire transfer. For example, + * because we are shutting down. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param pth preparation to cancel + */ + void + (*prepare_wire_transfer_cancel) (void *cls, + struct TALER_WIRE_PrepareHandle *pth); + + + /** + * Execute a wire transfer. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param buf buffer with the prepared execution details + * @param buf_size number of bytes in @a buf + * @param cc function to call upon success + * @param cc_cls closure for @a cc + * @return NULL on error + */ + struct TALER_WIRE_ExecuteHandle * + (*execute_wire_transfer) (void *cls, + const char *buf, + size_t buf_size, + TALER_WIRE_ConfirmatinCallback cc, + void *cc_cls); + + + /** + * Abort execution of a wire transfer. For example, because we are + * shutting down. Note that if an execution is aborted, it may or + * may not still succeed. The caller MUST run @e + * execute_wire_transfer again for the same request as soon as + * possilbe, to ensure that the request either ultimately succeeds + * or ultimately fails. Until this has been done, the transaction is + * in limbo (i.e. may or may not have been committed). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param eh execution to cancel + */ + void + (*execute_wire_transfer_cancel) (void *cls, + struct TALER_WIRE_ExecuteHandle *eh); + + +}; + + +#endif /* TALER_WIRE_PLUGIN_H */ -- cgit v1.2.3 From 072d819e23e6a8fa336664f6e7a851bcd23f03e1 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 24 Jan 2016 16:34:03 +0100 Subject: fix error message and leak --- src/mintdb/plugin_mintdb_postgres.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 8da98671..e9a9466b 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -3806,6 +3806,7 @@ libtaler_plugin_mintdb_postgres_init (void *cls) &db_conn_destroy)) { TALER_LOG_ERROR ("Cannnot create pthread key.\n"); + GNUNET_free (pg); return NULL; } if (GNUNET_OK != @@ -3815,8 +3816,9 @@ libtaler_plugin_mintdb_postgres_init (void *cls) &pg->connection_cfg_str)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "mint", + "mintdb-postgres", "db_conn_str"); + GNUNET_free (pg); return NULL; } plugin = GNUNET_new (struct TALER_MINTDB_Plugin); -- cgit v1.2.3 From 9aa323ca7bcf1567f6ecf98cf73b5691ade1b816 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 24 Jan 2016 16:44:57 +0100 Subject: adding skeletons for plugins --- configure.ac | 1 + src/Makefile.am | 4 +- src/include/taler_wire_plugin.h | 6 +- src/wire/Makefile.am | 36 ++++++ src/wire/plugin_wire_template.c | 235 ++++++++++++++++++++++++++++++++++++++++ src/wire/plugin_wire_test.c | 235 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 513 insertions(+), 4 deletions(-) create mode 100644 src/wire/Makefile.am create mode 100644 src/wire/plugin_wire_template.c create mode 100644 src/wire/plugin_wire_test.c (limited to 'src') diff --git a/configure.ac b/configure.ac index ccb0c1f3..8f06b520 100644 --- a/configure.ac +++ b/configure.ac @@ -357,6 +357,7 @@ AC_CONFIG_FILES([Makefile src/include/Makefile src/util/Makefile src/pq/Makefile + src/wire/Makefile src/mintdb/Makefile src/mint/Makefile src/mint-tools/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 0d1d4572..986dcc5d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,10 +7,10 @@ if WALLET_ONLY SUBDIRS = include util else -SUBDIRS = include util $(PQ_DIR) mintdb mint mint-tools +SUBDIRS = include util $(PQ_DIR) wire mintdb mint mint-tools if HAVE_LIBCURL SUBDIRS += mint-lib -else +else if HAVE_LIBGNURL SUBDIRS += mint-lib endif diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h index fd86ca28..00bcc42e 100644 --- a/src/include/taler_wire_plugin.h +++ b/src/include/taler_wire_plugin.h @@ -22,6 +22,8 @@ #define TALER_WIRE_PLUGIN_H #include +#include +#include "taler_util.h" /** @@ -103,7 +105,7 @@ struct TALER_WIRE_Plugin * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not */ int - wire_validate (const json_t *wire); + (*wire_validate) (const json_t *wire); /** @@ -151,7 +153,7 @@ struct TALER_WIRE_Plugin (*execute_wire_transfer) (void *cls, const char *buf, size_t buf_size, - TALER_WIRE_ConfirmatinCallback cc, + TALER_WIRE_ConfirmationCallback cc, void *cc_cls); diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am new file mode 100644 index 00000000..a27a59c7 --- /dev/null +++ b/src/wire/Makefile.am @@ -0,0 +1,36 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + +plugindir = $(libdir)/taler + +plugin_LTLIBRARIES = \ + libtaler_plugin_wire_test.la + +noinst_LTLIBRARIES = \ + libtaler_plugin_wire_template.la + + +libtaler_plugin_wire_test_la_SOURCES = \ + plugin_wire_test.c +libtaler_plugin_wire_test_la_LIBADD = \ + $(LTLIBINTL) +libtaler_plugin_wire_test_la_LDFLAGS = \ + $(TALER_PLUGIN_LDFLAGS) \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetutil $(XLIB) + + + +libtaler_plugin_wire_template_la_SOURCES = \ + plugin_wire_template.c +libtaler_plugin_wire_template_la_LIBADD = \ + $(LTLIBINTL) +libtaler_plugin_wire_template_la_LDFLAGS = \ + $(TALER_PLUGIN_LDFLAGS) \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetutil $(XLIB) diff --git a/src/wire/plugin_wire_template.c b/src/wire/plugin_wire_template.c new file mode 100644 index 00000000..8abcca8c --- /dev/null +++ b/src/wire/plugin_wire_template.c @@ -0,0 +1,235 @@ +/* + This file is part of TALER + Copyright (C) 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file plugin_wire_template.c + * @brief template for wire plugins; replace "template" with real plugin name! + * @author Florian Dold + * @author Christian Grothoff + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include "taler_wire_plugin.h" + + +/** + * Type of the "cls" argument given to each of the functions in + * our API. + */ +struct TemplateClosure +{ + + /** + * URI of the bank for sending funds to the bank. + */ + char *bank_uri; + + /** + * Which currency do we support? + */ + char *currency; + +}; + + +/** + * Round amount DOWN to the amount that can be transferred via the wire + * method. For example, Taler may support 0.000001 EUR as a unit of + * payment, but SEPA only supports 0.01 EUR. This function would + * round 0.125 EUR to 0.12 EUR in this case. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param[in,out] amount amount to round down + * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary, + * #GNUNET_SYSERR if the amount or currency was invalid + */ +static int +template_amount_round (void *cls, + struct TALER_Amount *amount) +{ + GNUNET_break (0); + return GNUNET_SYSERR; +} + + +/** + * Check if the given wire format JSON object is correctly formatted + * + * @param wire the JSON wire format object + * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not + */ +static int +template_wire_validate (const json_t *wire) +{ + GNUNET_break (0); + return GNUNET_SYSERR; +} + + +/** + * Prepare for exeuction of a wire transfer. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param wire valid wire account information + * @param amount amount to transfer, already rounded + * @param wtid wire transfer identifier to use + * @param ptc function to call with the prepared data to persist + * @param ptc_cls closure for @a ptc + * @return NULL on failure + */ +static struct TALER_WIRE_PrepareHandle * +template_prepare_wire_transfer (void *cls, + const json_t *wire, + const struct TALER_Amount *amount, + const void *wtid, + TALER_WIRE_PrepareTransactionCallback ptc, + void *ptc_cls) +{ + GNUNET_break (0); + return NULL; +} + + +/** + * Abort preparation of a wire transfer. For example, + * because we are shutting down. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param pth preparation to cancel + */ +static void +template_prepare_wire_transfer_cancel (void *cls, + struct TALER_WIRE_PrepareHandle *pth) +{ + GNUNET_break (0); +} + + +/** + * Execute a wire transfer. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param buf buffer with the prepared execution details + * @param buf_size number of bytes in @a buf + * @param cc function to call upon success + * @param cc_cls closure for @a cc + * @return NULL on error + */ +static struct TALER_WIRE_ExecuteHandle * +template_execute_wire_transfer (void *cls, + const char *buf, + size_t buf_size, + TALER_WIRE_ConfirmationCallback cc, + void *cc_cls) +{ + GNUNET_break (0); + return NULL; +} + + +/** + * Abort execution of a wire transfer. For example, because we are + * shutting down. Note that if an execution is aborted, it may or + * may not still succeed. The caller MUST run @e + * execute_wire_transfer again for the same request as soon as + * possilbe, to ensure that the request either ultimately succeeds + * or ultimately fails. Until this has been done, the transaction is + * in limbo (i.e. may or may not have been committed). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param eh execution to cancel + */ +static void +template_execute_wire_transfer_cancel (void *cls, + struct TALER_WIRE_ExecuteHandle *eh) +{ + GNUNET_break (0); +} + + +/** + * Initialize template-wire subsystem. + * + * @param cls a configuration instance + * @return NULL on error, otherwise a `struct TALER_WIRE_Plugin` + */ +void * +libtaler_plugin_wire_template_init (void *cls) +{ + struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct TemplateClosure *tc; + struct TALER_WIRE_Plugin *plugin; + + tc = GNUNET_new (struct TemplateClosure); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "wire-template", + "bank_uri", + &tc->bank_uri)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "wire-template", + "bank_uri"); + GNUNET_free (tc); + return NULL; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "mint", + "CURRENCY", + &tc->currency)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint", + "CURRENCY"); + GNUNET_free (tc->bank_uri); + GNUNET_free (tc); + return NULL; + } + + plugin = GNUNET_new (struct TALER_WIRE_Plugin); + plugin->cls = tc; + plugin->amount_round = &template_amount_round; + plugin->wire_validate = &template_wire_validate; + plugin->prepare_wire_transfer = &template_prepare_wire_transfer; + plugin->prepare_wire_transfer_cancel = &template_prepare_wire_transfer_cancel; + plugin->execute_wire_transfer = &template_execute_wire_transfer; + plugin->execute_wire_transfer_cancel = &template_execute_wire_transfer_cancel; + return plugin; +} + + +/** + * Shutdown Template wire subsystem. + * + * @param cls a `struct TALER_WIRE_Plugin` + * @return NULL (always) + */ +void * +libtaler_plugin_wire_template_done (void *cls) +{ + struct TALER_WIRE_Plugin *plugin = cls; + struct TemplateClosure *tc = plugin->cls; + + GNUNET_free (tc->bank_uri); + GNUNET_free (tc->currency); + GNUNET_free (tc); + GNUNET_free (plugin); + return NULL; +} + +/* end of plugin_wire_template.c */ diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c new file mode 100644 index 00000000..72c87ef5 --- /dev/null +++ b/src/wire/plugin_wire_test.c @@ -0,0 +1,235 @@ +/* + This file is part of TALER + Copyright (C) 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file plugin_wire_test.c + * @brief plugin for the "test" wire method + * @author Florian Dold + * @author Christian Grothoff + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include "taler_wire_plugin.h" + + +/** + * Type of the "cls" argument given to each of the functions in + * our API. + */ +struct TestClosure +{ + + /** + * URI of the bank for sending funds to the bank. + */ + char *bank_uri; + + /** + * Which currency do we support? + */ + char *currency; + +}; + + +/** + * Round amount DOWN to the amount that can be transferred via the wire + * method. For example, Taler may support 0.000001 EUR as a unit of + * payment, but SEPA only supports 0.01 EUR. This function would + * round 0.125 EUR to 0.12 EUR in this case. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param[in,out] amount amount to round down + * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary, + * #GNUNET_SYSERR if the amount or currency was invalid + */ +static int +test_amount_round (void *cls, + struct TALER_Amount *amount) +{ + GNUNET_break (0); + return GNUNET_SYSERR; +} + + +/** + * Check if the given wire format JSON object is correctly formatted + * + * @param wire the JSON wire format object + * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not + */ +static int +test_wire_validate (const json_t *wire) +{ + GNUNET_break (0); + return GNUNET_SYSERR; +} + + +/** + * Prepare for exeuction of a wire transfer. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param wire valid wire account information + * @param amount amount to transfer, already rounded + * @param wtid wire transfer identifier to use + * @param ptc function to call with the prepared data to persist + * @param ptc_cls closure for @a ptc + * @return NULL on failure + */ +static struct TALER_WIRE_PrepareHandle * +test_prepare_wire_transfer (void *cls, + const json_t *wire, + const struct TALER_Amount *amount, + const void *wtid, + TALER_WIRE_PrepareTransactionCallback ptc, + void *ptc_cls) +{ + GNUNET_break (0); + return NULL; +} + + +/** + * Abort preparation of a wire transfer. For example, + * because we are shutting down. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param pth preparation to cancel + */ +static void +test_prepare_wire_transfer_cancel (void *cls, + struct TALER_WIRE_PrepareHandle *pth) +{ + GNUNET_break (0); +} + + +/** + * Execute a wire transfer. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param buf buffer with the prepared execution details + * @param buf_size number of bytes in @a buf + * @param cc function to call upon success + * @param cc_cls closure for @a cc + * @return NULL on error + */ +static struct TALER_WIRE_ExecuteHandle * +test_execute_wire_transfer (void *cls, + const char *buf, + size_t buf_size, + TALER_WIRE_ConfirmationCallback cc, + void *cc_cls) +{ + GNUNET_break (0); + return NULL; +} + + +/** + * Abort execution of a wire transfer. For example, because we are + * shutting down. Note that if an execution is aborted, it may or + * may not still succeed. The caller MUST run @e + * execute_wire_transfer again for the same request as soon as + * possilbe, to ensure that the request either ultimately succeeds + * or ultimately fails. Until this has been done, the transaction is + * in limbo (i.e. may or may not have been committed). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param eh execution to cancel + */ +static void +test_execute_wire_transfer_cancel (void *cls, + struct TALER_WIRE_ExecuteHandle *eh) +{ + GNUNET_break (0); +} + + +/** + * Initialize test-wire subsystem. + * + * @param cls a configuration instance + * @return NULL on error, otherwise a `struct TALER_WIRE_Plugin` + */ +void * +libtaler_plugin_wire_test_init (void *cls) +{ + struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct TestClosure *tc; + struct TALER_WIRE_Plugin *plugin; + + tc = GNUNET_new (struct TestClosure); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "wire-test", + "bank_uri", + &tc->bank_uri)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "wire-test", + "bank_uri"); + GNUNET_free (tc); + return NULL; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "mint", + "CURRENCY", + &tc->currency)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint", + "CURRENCY"); + GNUNET_free (tc->bank_uri); + GNUNET_free (tc); + return NULL; + } + + plugin = GNUNET_new (struct TALER_WIRE_Plugin); + plugin->cls = tc; + plugin->amount_round = &test_amount_round; + plugin->wire_validate = &test_wire_validate; + plugin->prepare_wire_transfer = &test_prepare_wire_transfer; + plugin->prepare_wire_transfer_cancel = &test_prepare_wire_transfer_cancel; + plugin->execute_wire_transfer = &test_execute_wire_transfer; + plugin->execute_wire_transfer_cancel = &test_execute_wire_transfer_cancel; + return plugin; +} + + +/** + * Shutdown Test wire subsystem. + * + * @param cls a `struct TALER_WIRE_Plugin` + * @return NULL (always) + */ +void * +libtaler_plugin_wire_test_done (void *cls) +{ + struct TALER_WIRE_Plugin *plugin = cls; + struct TestClosure *tc = plugin->cls; + + GNUNET_free (tc->bank_uri); + GNUNET_free (tc->currency); + GNUNET_free (tc); + GNUNET_free (plugin); + return NULL; +} + +/* end of plugin_wire_test.c */ -- cgit v1.2.3 From 5c58c43609609e6871c7105c7ca8fc3d794dca04 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 24 Jan 2016 16:52:39 +0100 Subject: more work on wire plugins --- src/include/taler_wire_plugin.h | 2 +- src/wire/plugin_wire_template.c | 12 ++++++++++-- src/wire/plugin_wire_test.c | 24 +++++++++++++++++++----- 3 files changed, 30 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h index 00bcc42e..62930450 100644 --- a/src/include/taler_wire_plugin.h +++ b/src/include/taler_wire_plugin.h @@ -123,7 +123,7 @@ struct TALER_WIRE_Plugin (*prepare_wire_transfer) (void *cls, const json_t *wire, const struct TALER_Amount *amount, - const void *wtid, + const struct TALER_WireTransferIdentifierRawP *wtid, TALER_WIRE_PrepareTransactionCallback ptc, void *ptc_cls); diff --git a/src/wire/plugin_wire_template.c b/src/wire/plugin_wire_template.c index 8abcca8c..baf0ee7d 100644 --- a/src/wire/plugin_wire_template.c +++ b/src/wire/plugin_wire_template.c @@ -60,7 +60,15 @@ static int template_amount_round (void *cls, struct TALER_Amount *amount) { - GNUNET_break (0); + struct TemplateClosure *tc = cls; + + if (0 != strcasecmp (amount->currency, + tc->currency)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_break (0); // not implemented return GNUNET_SYSERR; } @@ -94,7 +102,7 @@ static struct TALER_WIRE_PrepareHandle * template_prepare_wire_transfer (void *cls, const json_t *wire, const struct TALER_Amount *amount, - const void *wtid, + const struct TALER_WireTransferIdentifierRawP *wtid, TALER_WIRE_PrepareTransactionCallback ptc, void *ptc_cls) { diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c index 72c87ef5..1ea856fd 100644 --- a/src/wire/plugin_wire_test.c +++ b/src/wire/plugin_wire_test.c @@ -60,8 +60,21 @@ static int test_amount_round (void *cls, struct TALER_Amount *amount) { - GNUNET_break (0); - return GNUNET_SYSERR; + struct TestClosure *tc = cls; + uint32_t delta; + + if (0 != strcasecmp (amount->currency, + tc->currency)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + /* 'test' method supports 1/100 of the unit currency, i.e. 0.01 CUR */ + delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100); + if (0 == delta) + return GNUNET_NO; + amount->fraction -= delta; + return GNUNET_OK; } @@ -74,8 +87,9 @@ test_amount_round (void *cls, static int test_wire_validate (const json_t *wire) { - GNUNET_break (0); - return GNUNET_SYSERR; + GNUNET_break (0); /* FIXME: we still need to define the + proper wire format for 'test' */ + return GNUNET_YES; } @@ -94,7 +108,7 @@ static struct TALER_WIRE_PrepareHandle * test_prepare_wire_transfer (void *cls, const json_t *wire, const struct TALER_Amount *amount, - const void *wtid, + const struct TALER_WireTransferIdentifierRawP *wtid, TALER_WIRE_PrepareTransactionCallback ptc, void *ptc_cls) { -- cgit v1.2.3 From 941cb8182f052bbd6344ba7daf91f3aa792027bd Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 10:20:40 +0100 Subject: adding first version of thebank-lib --- src/bank-lib/Makefile.am | 47 + src/bank-lib/bank_api_admin.c | 240 ++++ src/bank-lib/bank_api_context.c | 570 +++++++++ src/bank-lib/bank_api_context.h | 181 +++ src/bank-lib/bank_api_json.c | 525 ++++++++ src/bank-lib/bank_api_json.h | 352 +++++ src/bank-lib/test_bank_api.c | 2610 ++++++++++++++++++++++++++++++++++++++ src/include/taler_bank_service.h | 160 +++ 8 files changed, 4685 insertions(+) create mode 100644 src/bank-lib/Makefile.am create mode 100644 src/bank-lib/bank_api_admin.c create mode 100644 src/bank-lib/bank_api_context.c create mode 100644 src/bank-lib/bank_api_context.h create mode 100644 src/bank-lib/bank_api_json.c create mode 100644 src/bank-lib/bank_api_json.h create mode 100644 src/bank-lib/test_bank_api.c create mode 100644 src/include/taler_bank_service.h (limited to 'src') diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am new file mode 100644 index 00000000..5b3b4d25 --- /dev/null +++ b/src/bank-lib/Makefile.am @@ -0,0 +1,47 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + +lib_LTLIBRARIES = \ + libtalerbank.la + +libtalerbank_la_LDFLAGS = \ + -version-info 0:0:0 \ + -no-undefined + +libtalerbank_la_SOURCES = \ + bank_api_context.c bank_api_context.h \ + bank_api_json.c bank_api_json.h \ + bank_api_admin.c + +libtalerbank_la_LIBADD = \ + -lgnunetutil \ + -ljansson \ + $(XLIB) + +if HAVE_LIBCURL +libtalerbank_la_LIBADD += -lcurl +else +if HAVE_LIBGNURL +libtalerbank_la_LIBADD += -lgnurl +endif +endif + +#check_PROGRAMS = \ +# test_bank_api + +#TESTS = \ +# $(check_PROGRAMS) + +#test_bank_api_SOURCES = \ +# test_bank_api.c +#test_bank_api_LDADD = \ +# libtalerbank.la \ +# $(LIBGCRYPT_LIBS) \ +# $(top_builddir)/src/util/libtalerutil.la \ +# -lgnunetutil \ +# -ljansson diff --git a/src/bank-lib/bank_api_admin.c b/src/bank-lib/bank_api_admin.c new file mode 100644 index 00000000..ed205eeb --- /dev/null +++ b/src/bank-lib/bank_api_admin.c @@ -0,0 +1,240 @@ +/* + This file is part of TALER + Copyright (C) 2015, 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file bank-lib/bank_api_admin.c + * @brief Implementation of the /admin/ requests of the bank's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include "taler_bank_service.h" +#include "bank_api_json.h" +#include "bank_api_context.h" +#include "taler_signatures.h" + + +/** + * @brief An admin/add/incoming Handle + */ +struct TALER_BANK_AdminAddIncomingHandle +{ + + /** + * The connection to bank this request handle will use + */ + struct TALER_BANK_Context *bank; + + /** + * The url for this request. + */ + char *url; + + /** + * JSON encoding of the request to POST. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct BAC_Job *job; + + /** + * HTTP headers for the request. + */ + struct curl_slist *headers; + + /** + * Function to call with the result. + */ + TALER_BANK_AdminAddIncomingResultCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Download buffer + */ + struct BAC_DownloadBuffer db; + +}; + + +/** + * Function called when we're done processing the + * HTTP /admin/add/incoming request. + * + * @param cls the `struct TALER_BANK_AdminAddIncomingHandle` + * @param eh the curl request handle + */ +static void +handle_admin_add_incoming_finished (void *cls, + CURL *eh) +{ + struct TALER_BANK_AdminAddIncomingHandle *aai = cls; + long response_code; + json_t *json; + + aai->job = NULL; + json = BAC_download_get_result (&aai->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the bank is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + /* Access denied */ + break; + case MHD_HTTP_UNAUTHORIZED: + /* Nothing really to verify, bank says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + aai->cb (aai->cb_cls, + response_code); + json_decref (json); + TALER_BANK_admin_add_incoming_cancel (aai); +} + + +/** + * Notify the bank that we have received an incoming transaction + * which fills a reserve. Note that this API is an administrative + * API and thus not accessible to typical bank clients, but only + * to the operators of the bank. + * + * @param bank the bank handle; the bank must be ready to operate + * @param reserve_pub public key of the reserve + * @param amount amount that was deposited + * @param execution_date when did we receive the amount + * @param wire wire details + * @param res_cb the callback to call when the final result for this request is available + * @param res_cb_cls closure for the above callback + * @return NULL + * if the inputs are invalid (i.e. invalid amount). + * In this case, the callback is not called. + */ +struct TALER_BANK_AdminAddIncomingHandle * +TALER_BANK_admin_add_incoming (struct TALER_BANK_Context *bank, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *amount, + const json_t *wire, + TALER_BANK_AdminAddIncomingResultCallback res_cb, + void *res_cb_cls) +{ + struct TALER_BANK_AdminAddIncomingHandle *aai; + json_t *admin_obj; + CURL *eh; + + admin_obj = json_pack ("{s:o, s:o," /* reserve_pub/amount */ + " s:O}", /* execution_Date/wire */ + "wtid", TALER_json_from_data (wtid, + sizeof (*wtid)), + "amount", TALER_json_from_amount (amount), + "wire", wire); + aai = GNUNET_new (struct TALER_BANK_AdminAddIncomingHandle); + aai->bank = bank; + aai->cb = res_cb; + aai->cb_cls = res_cb_cls; + aai->url = BAC_path_to_url (bank, "/admin/add/incoming"); + + eh = curl_easy_init (); + GNUNET_assert (NULL != (aai->json_enc = + json_dumps (admin_obj, + JSON_COMPACT))); + json_decref (admin_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + aai->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + aai->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (aai->json_enc))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &BAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &aai->db)); + aai->job = BAC_job_add (bank, + eh, + GNUNET_YES, + &handle_admin_add_incoming_finished, + aai); + return aai; +} + + +/** + * Cancel an add incoming. This function cannot be used on a request + * handle if a response is already served for it. + * + * @param aai the admin add incoming request handle + */ +void +TALER_BANK_admin_add_incoming_cancel (struct TALER_BANK_AdminAddIncomingHandle *aai) +{ + if (NULL != aai->job) + { + BAC_job_cancel (aai->job); + aai->job = NULL; + } + curl_slist_free_all (aai->headers); + GNUNET_free_non_null (aai->db.buf); + GNUNET_free (aai->url); + GNUNET_free (aai->json_enc); + GNUNET_free (aai); +} + + +/* end of bank_api_admin.c */ diff --git a/src/bank-lib/bank_api_context.c b/src/bank-lib/bank_api_context.c new file mode 100644 index 00000000..f54e9e70 --- /dev/null +++ b/src/bank-lib/bank_api_context.c @@ -0,0 +1,570 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file bank-lib/bank_api_context.c + * @brief Implementation of the context part of the bank's HTTP API + * @author Sree Harsha Totakura + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include "taler_bank_service.h" +#include "bank_api_context.h" + + +/** + * Log error related to CURL operations. + * + * @param type log level + * @param function which function failed to run + * @param code what was the curl error code + */ +#define CURL_STRERROR(type, function, code) \ + GNUNET_log (type, \ + "Curl function `%s' has failed at `%s:%d' with error: %s\n", \ + function, __FILE__, __LINE__, curl_easy_strerror (code)); + +/** + * Print JSON parsing related error information + */ +#define JSON_WARN(error) \ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \ + "JSON parsing failed at %s:%u: %s (%s)\n", \ + __FILE__, __LINE__, error.text, error.source) + + +/** + * Failsafe flag. Raised if our constructor fails to initialize + * the Curl library. + */ +static int TALER_BANK_curl_fail; + + +/** + * Jobs are CURL requests running within a `struct TALER_BANK_Context`. + */ +struct BAC_Job +{ + + /** + * We keep jobs in a DLL. + */ + struct BAC_Job *next; + + /** + * We keep jobs in a DLL. + */ + struct BAC_Job *prev; + + /** + * Easy handle of the job. + */ + CURL *easy_handle; + + /** + * Context this job runs in. + */ + struct TALER_BANK_Context *ctx; + + /** + * Function to call upon completion. + */ + BAC_JobCompletionCallback jcc; + + /** + * Closure for @e jcc. + */ + void *jcc_cls; + +}; + + +/** + * Context + */ +struct TALER_BANK_Context +{ + /** + * Curl multi handle + */ + CURLM *multi; + + /** + * Curl share handle + */ + CURLSH *share; + + /** + * We keep jobs in a DLL. + */ + struct BAC_Job *jobs_head; + + /** + * We keep jobs in a DLL. + */ + struct BAC_Job *jobs_tail; + + /** + * HTTP header "application/json", created once and used + * for all requests that need it. + */ + struct curl_slist *json_header; + + /** + * Base URL of the bank. + */ + char *url; + +}; + + +/** + * Initialise this library. This function should be called before using any of + * the following functions. + * + * @param url HTTP base URL for the bank + * @return library context + */ +struct TALER_BANK_Context * +TALER_BANK_init (const char *url) +{ + struct TALER_BANK_Context *ctx; + CURLM *multi; + CURLSH *share; + + if (TALER_BANK_curl_fail) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Curl was not initialised properly\n"); + return NULL; + } + if (NULL == (multi = curl_multi_init ())) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create a Curl multi handle\n"); + return NULL; + } + if (NULL == (share = curl_share_init ())) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create a Curl share handle\n"); + return NULL; + } + ctx = GNUNET_new (struct TALER_BANK_Context); + ctx->multi = multi; + ctx->share = share; + ctx->url = GNUNET_strdup (url); + GNUNET_assert (NULL != (ctx->json_header = + curl_slist_append (NULL, + "Content-Type: application/json"))); + return ctx; +} + + +/** + * Schedule a CURL request to be executed and call the given @a jcc + * upon its completion. Note that the context will make use of the + * CURLOPT_PRIVATE facility of the CURL @a eh. Applications can + * instead use #BAC_easy_to_closure to extract the @a jcc_cls argument + * from a valid @a eh afterwards. + * + * This function modifies the CURL handle to add the + * "Content-Type: application/json" header if @a add_json is set. + * + * @param ctx context to execute the job in + * @param eh curl easy handle for the request, will + * be executed AND cleaned up + * @param add_json add "application/json" content type header + * @param jcc callback to invoke upon completion + * @param jcc_cls closure for @a jcc + */ +struct BAC_Job * +BAC_job_add (struct TALER_BANK_Context *ctx, + CURL *eh, + int add_json, + BAC_JobCompletionCallback jcc, + void *jcc_cls) +{ + struct BAC_Job *job; + + if (GNUNET_YES == add_json) + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_HTTPHEADER, + ctx->json_header)); + + job = GNUNET_new (struct BAC_Job); + job->easy_handle = eh; + job->ctx = ctx; + job->jcc = jcc; + job->jcc_cls = jcc_cls; + GNUNET_CONTAINER_DLL_insert (ctx->jobs_head, + ctx->jobs_tail, + job); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_PRIVATE, + job)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_SHARE, + ctx->share)); + GNUNET_assert (CURLM_OK == + curl_multi_add_handle (ctx->multi, + eh)); + return job; +} + + +/** + * Obtain the `jcc_cls` argument from an `eh` that was + * given to #BAC_job_add(). + * + * @param eh easy handle that was used + * @return the `jcc_cls` that was given to #BAC_job_add(). + */ +void * +BAC_easy_to_closure (CURL *eh) +{ + struct BAC_Job *job; + + GNUNET_assert (CURLE_OK == + curl_easy_getinfo (eh, + CURLINFO_PRIVATE, + (char **) &job)); + return job->jcc_cls; +} + + +/** + * Cancel a job. Must only be called before the job completion + * callback is called for the respective job. + * + * @param job job to cancel + */ +void +BAC_job_cancel (struct BAC_Job *job) +{ + struct TALER_BANK_Context *ctx = job->ctx; + + GNUNET_CONTAINER_DLL_remove (ctx->jobs_head, + ctx->jobs_tail, + job); + GNUNET_assert (CURLM_OK == + curl_multi_remove_handle (ctx->multi, + job->easy_handle)); + curl_easy_cleanup (job->easy_handle); + GNUNET_free (job); +} + + +/** + * Run the main event loop for the Taler interaction. + * + * @param ctx the library context + */ +void +TALER_BANK_perform (struct TALER_BANK_Context *ctx) +{ + CURLMsg *cmsg; + struct BAC_Job *job; + int n_running; + int n_completed; + + (void) curl_multi_perform (ctx->multi, + &n_running); + while (NULL != (cmsg = curl_multi_info_read (ctx->multi, + &n_completed))) + { + /* Only documented return value is CURLMSG_DONE */ + GNUNET_break (CURLMSG_DONE == cmsg->msg); + GNUNET_assert (CURLE_OK == + curl_easy_getinfo (cmsg->easy_handle, + CURLINFO_PRIVATE, + (char **) &job)); + GNUNET_assert (job->ctx == ctx); + job->jcc (job->jcc_cls, + cmsg->easy_handle); + BAC_job_cancel (job); + } +} + + +/** + * Obtain the information for a select() call to wait until + * #TALER_BANK_perform() is ready again. Note that calling + * any other TALER_BANK-API may also imply that the library + * is again ready for #TALER_BANK_perform(). + * + * Basically, a client should use this API to prepare for select(), + * then block on select(), then call #TALER_BANK_perform() and then + * start again until the work with the context is done. + * + * This function will NOT zero out the sets and assumes that @a max_fd + * and @a timeout are already set to minimal applicable values. It is + * safe to give this API FD-sets and @a max_fd and @a timeout that are + * already initialized to some other descriptors that need to go into + * the select() call. + * + * @param ctx context to get the event loop information for + * @param read_fd_set will be set for any pending read operations + * @param write_fd_set will be set for any pending write operations + * @param except_fd_set is here because curl_multi_fdset() has this argument + * @param max_fd set to the highest FD included in any set; + * if the existing sets have no FDs in it, the initial + * value should be "-1". (Note that `max_fd + 1` will need + * to be passed to select().) + * @param timeout set to the timeout in milliseconds (!); -1 means + * no timeout (NULL, blocking forever is OK), 0 means to + * proceed immediately with #TALER_BANK_perform(). + */ +void +TALER_BANK_get_select_info (struct TALER_BANK_Context *ctx, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *except_fd_set, + int *max_fd, + long *timeout) +{ + long to; + int m; + + m = -1; + GNUNET_assert (CURLM_OK == + curl_multi_fdset (ctx->multi, + read_fd_set, + write_fd_set, + except_fd_set, + &m)); + to = *timeout; + *max_fd = GNUNET_MAX (m, *max_fd); + GNUNET_assert (CURLM_OK == + curl_multi_timeout (ctx->multi, + &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 = to; +} + + +/** + * Cleanup library initialisation resources. This function should be called + * after using this library to cleanup the resources occupied during library's + * initialisation. + * + * @param ctx the library context + */ +void +TALER_BANK_fini (struct TALER_BANK_Context *ctx) +{ + /* all jobs must have been cancelled at this time, assert this */ + GNUNET_assert (NULL == ctx->jobs_head); + curl_share_cleanup (ctx->share); + curl_multi_cleanup (ctx->multi); + curl_slist_free_all (ctx->json_header); + GNUNET_free (ctx->url); + GNUNET_free (ctx); +} + + +/** + * Obtain the URL to use for an API request. + * + * @param h the mint handle to query + * @param path Taler API path (i.e. "/reserve/withdraw") + * @return the full URI to use with cURL + */ +char * +MAH_path_to_url (struct TALER_BANK_Context *h, + const char *path) +{ + char *url; + + if ( ('/' == path[0]) && + (0 < strlen (h->url)) && + ('/' == h->url[strlen (h->url) - 1]) ) + path++; /* avoid generating URL with "//" from concat */ + GNUNET_asprintf (&url, + "%s%s", + h->url, + path); + return url; +} + + +/** + * Callback used when downloading the reply to an HTTP request. + * Just appends all of the data to the `buf` in the + * `struct BAC_DownloadBuffer` for further processing. The size of + * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if + * the download exceeds this size, we abort with an error. + * + * @param bufptr data downloaded via HTTP + * @param size size of an item in @a bufptr + * @param nitems number of items in @a bufptr + * @param cls the `struct KeysRequest` + * @return number of bytes processed from @a bufptr + */ +size_t +BAC_download_cb (char *bufptr, + size_t size, + size_t nitems, + void *cls) +{ + struct BAC_DownloadBuffer *db = cls; + size_t msize; + void *buf; + + if (0 == size * nitems) + { + /* Nothing (left) to do */ + return 0; + } + msize = size * nitems; + if ( (msize + db->buf_size) >= GNUNET_MAX_MALLOC_CHECKED) + { + db->eno = ENOMEM; + return 0; /* signals an error to curl */ + } + db->buf = GNUNET_realloc (db->buf, + db->buf_size + msize); + buf = db->buf + db->buf_size; + memcpy (buf, bufptr, msize); + db->buf_size += msize; + return msize; +} + + +/** + * Obtain information about the final result about the + * HTTP download. If the download was successful, parses + * the JSON in the @a db and returns it. Also returns + * the HTTP @a response_code. If the download failed, + * the return value is NULL. The response code is set + * in any case, on download errors to zero. + * + * Calling this function also cleans up @a db. + * + * @param db download buffer + * @param eh CURL handle (to get the response code) + * @param[out] response_code set to the HTTP response code + * (or zero if we aborted the download, i.e. + * because the response was too big, or if + * the JSON we received was malformed). + * @return NULL if downloading a JSON reply failed + */ +json_t * +BAC_download_get_result (struct BAC_DownloadBuffer *db, + CURL *eh, + long *response_code) +{ + json_t *json; + json_error_t error; + char *ct; + + if ( (CURLE_OK != + curl_easy_getinfo (eh, + CURLINFO_CONTENT_TYPE, + &ct)) || + (NULL == ct) || + (0 != strcasecmp (ct, + "application/json")) ) + { + /* No content type or explicitly not JSON, refuse to parse + (but keep response code) */ + if (CURLE_OK != + curl_easy_getinfo (eh, + CURLINFO_RESPONSE_CODE, + response_code)) + { + /* unexpected error... */ + GNUNET_break (0); + *response_code = 0; + } + return NULL; + } + + json = NULL; + if (0 == db->eno) + { + json = json_loadb (db->buf, + db->buf_size, + JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK, + &error); + if (NULL == json) + { + JSON_WARN (error); + *response_code = 0; + } + } + GNUNET_free_non_null (db->buf); + db->buf = NULL; + db->buf_size = 0; + if (NULL != json) + { + if (CURLE_OK != + curl_easy_getinfo (eh, + CURLINFO_RESPONSE_CODE, + response_code)) + { + /* unexpected error... */ + GNUNET_break (0); + *response_code = 0; + } + } + return json; +} + + +/** + * Initial global setup logic, specifically runs the Curl setup. + */ +__attribute__ ((constructor)) +void +TALER_BANK_constructor__ (void) +{ + CURLcode ret; + + if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT))) + { + CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR, + "curl_global_init", + ret); + TALER_BANK_curl_fail = 1; + } +} + + +/** + * Cleans up after us, specifically runs the Curl cleanup. + */ +__attribute__ ((destructor)) +void +TALER_BANK_destructor__ (void) +{ + if (TALER_BANK_curl_fail) + return; + curl_global_cleanup (); +} + +/* end of bank_api_context.c */ diff --git a/src/bank-lib/bank_api_context.h b/src/bank-lib/bank_api_context.h new file mode 100644 index 00000000..552cbe44 --- /dev/null +++ b/src/bank-lib/bank_api_context.h @@ -0,0 +1,181 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file bank-lib/bank_api_context.h + * @brief Internal interface to the context part of the bank's HTTP API + * @author Sree Harsha Totakura + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler_bank_service.h" +#include "taler_signatures.h" + + +/** + * Entry in the context's job queue. + */ +struct BAC_Job; + +/** + * Function to call upon completion of a job. + * + * @param cls closure + * @param eh original easy handle (for inspection) + */ +typedef void +(*BAC_JobCompletionCallback)(void *cls, + CURL *eh); + + +/** + * Schedule a CURL request to be executed and call the given @a jcc + * upon its completion. Note that the context will make use of the + * CURLOPT_PRIVATE facility of the CURL @a eh. Applications can + * instead use #BAC_easy_to_closure to extract the @a jcc_cls argument + * from a valid @a eh afterwards. + * + * This function modifies the CURL handle to add the + * "Content-Type: application/json" header if @a add_json is set. + * + * @param ctx context to execute the job in + * @param eh curl easy handle for the request, will + * be executed AND cleaned up + * @param add_json add "application/json" content type header + * @param jcc callback to invoke upon completion + * @param jcc_cls closure for @a jcc + */ +struct BAC_Job * +BAC_job_add (struct TALER_BANK_Context *ctx, + CURL *eh, + int add_json, + BAC_JobCompletionCallback jcc, + void *jcc_cls); + + +/** + * Obtain the `jcc_cls` argument from an `eh` that was + * given to #BAC_job_add(). + * + * @param eh easy handle that was used + * @return the `jcc_cls` that was given to #BAC_job_add(). + */ +void * +BAC_easy_to_closure (CURL *eh); + + +/** + * Cancel a job. Must only be called before the job completion + * callback is called for the respective job. + * + * @param job job to cancel + */ +void +BAC_job_cancel (struct BAC_Job *job); + + +/** + * @brief Buffer data structure we use to buffer the HTTP download + * before giving it to the JSON parser. + */ +struct BAC_DownloadBuffer +{ + + /** + * Download buffer + */ + void *buf; + + /** + * The size of the download buffer + */ + size_t buf_size; + + /** + * Error code (based on libc errno) if we failed to download + * (i.e. response too large). + */ + int eno; + +}; + + +/** + * Callback used when downloading the reply to an HTTP request. + * Just appends all of the data to the `buf` in the + * `struct BAC_DownloadBuffer` for further processing. The size of + * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if + * the download exceeds this size, we abort with an error. + * + * Should be used by the various routines as the + * CURLOPT_WRITEFUNCTION. A `struct BAC_DownloadBuffer` needs to be + * passed to the CURLOPT_WRITEDATA. + * + * Afterwards, `eno` needs to be checked to ensure that the download + * completed correctly. + * + * @param bufptr data downloaded via HTTP + * @param size size of an item in @a bufptr + * @param nitems number of items in @a bufptr + * @param cls the `struct KeysRequest` + * @return number of bytes processed from @a bufptr + */ +size_t +BAC_download_cb (char *bufptr, + size_t size, + size_t nitems, + void *cls); + + +/** + * Obtain information about the final result about the + * HTTP download. If the download was successful, parses + * the JSON in the @a db and returns it. Also returns + * the HTTP @a response_code. If the download failed, + * the return value is NULL. The response code is set + * in any case, on download errors to zero. + * + * Calling this function also cleans up @a db. + * + * @param db download buffer + * @param eh CURL handle (to get the response code) + * @param[out] response_code set to the HTTP response code + * (or zero if we aborted the download, i.e. + * because the response was too big, or if + * the JSON we received was malformed). + * @return NULL if downloading a JSON reply failed + */ +json_t * +BAC_download_get_result (struct BAC_DownloadBuffer *db, + CURL *eh, + long *response_code); + + +/** + * Obtain the URL to use for an API request. + * + * @param h the bank handle to query + * @param path Taler API path (i.e. "/reserve/withdraw") + * @return the full URI to use with cURL + */ +char * +BAC_path_to_url (struct TALER_BANK_Context *h, + const char *path); + + +/* end of bank_api_context.h */ diff --git a/src/bank-lib/bank_api_json.c b/src/bank-lib/bank_api_json.c new file mode 100644 index 00000000..2a09e527 --- /dev/null +++ b/src/bank-lib/bank_api_json.c @@ -0,0 +1,525 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file bank-lib/bank_api_json.c + * @brief functions to parse incoming requests (JSON snippets) + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include "bank_api_json.h" + +/** + * Navigate and parse data in a JSON tree. + * + * @param root the JSON node to start the navigation at. + * @param spec parse specification array + * @return offset in @a spec where parsing failed, -1 on success (!) + */ +static int +parse_json (json_t *root, + struct BAJ_Specification *spec) +{ + int i; + json_t *pos; /* what's our current position? */ + + pos = root; + for (i=0;BAJ_CMD_END != spec[i].cmd;i++) + { + pos = json_object_get (root, + spec[i].field); + if (NULL == pos) + { + GNUNET_break_op (0); + return i; + } + switch (spec[i].cmd) + { + case BAJ_CMD_END: + GNUNET_assert (0); + return i; + case BAJ_CMD_AMOUNT: + if (GNUNET_OK != + TALER_json_to_amount (pos, + spec[i].details.amount)) + { + GNUNET_break_op (0); + return i; + } + break; + case BAJ_CMD_TIME_ABSOLUTE: + if (GNUNET_OK != + TALER_json_to_abs (pos, + spec[i].details.abs_time)) + { + GNUNET_break_op (0); + return i; + } + break; + + case BAJ_CMD_STRING: + { + const char *str; + + str = json_string_value (pos); + if (NULL == str) + { + GNUNET_break_op (0); + return i; + } + *spec[i].details.strptr = str; + } + break; + + case BAJ_CMD_BINARY_FIXED: + { + const char *str; + int res; + + str = json_string_value (pos); + if (NULL == str) + { + GNUNET_break_op (0); + return i; + } + res = GNUNET_STRINGS_string_to_data (str, strlen (str), + spec[i].details.fixed_data.dest, + spec[i].details.fixed_data.dest_size); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + return i; + } + } + break; + + case BAJ_CMD_BINARY_VARIABLE: + { + const char *str; + size_t size; + void *data; + int res; + + str = json_string_value (pos); + if (NULL == str) + { + GNUNET_break_op (0); + return i; + } + size = (strlen (str) * 5) / 8; + if (size >= 1024) + { + GNUNET_break_op (0); + return i; + } + data = GNUNET_malloc (size); + res = GNUNET_STRINGS_string_to_data (str, + strlen (str), + data, + size); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + GNUNET_free (data); + return i; + } + *spec[i].details.variable_data.dest_p = data; + *spec[i].details.variable_data.dest_size_p = size; + } + break; + + case BAJ_CMD_RSA_PUBLIC_KEY: + { + size_t size; + const char *str; + int res; + void *buf; + + str = json_string_value (pos); + if (NULL == str) + { + GNUNET_break_op (0); + return i; + } + size = (strlen (str) * 5) / 8; + buf = GNUNET_malloc (size); + res = GNUNET_STRINGS_string_to_data (str, + strlen (str), + buf, + size); + if (GNUNET_OK != res) + { + GNUNET_free (buf); + GNUNET_break_op (0); + return i; + } + *spec[i].details.rsa_public_key + = GNUNET_CRYPTO_rsa_public_key_decode (buf, + size); + GNUNET_free (buf); + if (NULL == spec[i].details.rsa_public_key) + { + GNUNET_break_op (0); + return i; + } + } + break; + + case BAJ_CMD_RSA_SIGNATURE: + { + size_t size; + const char *str; + int res; + void *buf; + + str = json_string_value (pos); + if (NULL == str) + { + GNUNET_break_op (0); + return i; + } + size = (strlen (str) * 5) / 8; + buf = GNUNET_malloc (size); + res = GNUNET_STRINGS_string_to_data (str, + strlen (str), + buf, + size); + if (GNUNET_OK != res) + { + GNUNET_free (buf); + GNUNET_break_op (0); + return i; + } + *spec[i].details.rsa_signature + = GNUNET_CRYPTO_rsa_signature_decode (buf, + size); + GNUNET_free (buf); + if (NULL == spec[i].details.rsa_signature) + return i; + } + break; + + case BAJ_CMD_UINT16: + { + json_int_t val; + + if (! json_is_integer (pos)) + { + GNUNET_break_op (0); + return i; + } + val = json_integer_value (pos); + if ( (0 > val) || (val > UINT16_MAX) ) + { + GNUNET_break_op (0); + return i; + } + *spec[i].details.u16 = (uint16_t) val; + } + break; + + case BAJ_CMD_UINT64: + { + json_int_t val; + + if (! json_is_integer (pos)) + { + GNUNET_break_op (0); + return i; + } + val = json_integer_value (pos); + *spec[i].details.u64 = (uint64_t) val; + } + break; + + case BAJ_CMD_JSON_OBJECT: + { + if (! (json_is_object (pos) || json_is_array (pos)) ) + { + GNUNET_break_op (0); + return i; + } + json_incref (pos); + *spec[i].details.obj = pos; + } + break; + + default: + GNUNET_break (0); + return i; + } + } + return -1; /* all OK! */ +} + + +/** + * Free all elements allocated during a + * #BAJ_parse_json() operation. + * + * @param spec specification of the parse operation + * @param end number of elements in @a spec to process + */ +static void +parse_free (struct BAJ_Specification *spec, + int end) +{ + int i; + + for (i=0;i +*/ +/** + * @file mint-lib/mint_api_json.h + * @brief functions to parse incoming requests (JSON snippets) + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include "taler_util.h" +#include + + +/** + * Enumeration with the various commands for the + * #BAJ_parse_json interpreter. + */ +enum BAJ_Command +{ + + /** + * End of command list. + */ + BAJ_CMD_END, + + /** + * Parse amount at current position. + */ + BAJ_CMD_AMOUNT, + + /** + * Parse absolute time at current position. + */ + BAJ_CMD_TIME_ABSOLUTE, + + /** + * Parse fixed binary value at current position. + */ + BAJ_CMD_BINARY_FIXED, + + /** + * Parse variable-size binary value at current position. + */ + BAJ_CMD_BINARY_VARIABLE, + + /** + * Parse RSA public key at current position. + */ + BAJ_CMD_RSA_PUBLIC_KEY, + + /** + * Parse RSA signature at current position. + */ + BAJ_CMD_RSA_SIGNATURE, + + /** + * Parse `const char *` JSON string at current position. + */ + BAJ_CMD_STRING, + + /** + * Parse `uint16_t` integer at the current position. + */ + BAJ_CMD_UINT16, + + /** + * Parse `uint64_t` integer at the current position. + */ + BAJ_CMD_UINT64, + + /** + * Parse JSON object at the current position. + */ + BAJ_CMD_JSON_OBJECT, + + /** + * Parse ??? at current position. + */ + BAJ_CMD_C + +}; + + +/** + * @brief Entry in parser specification for #BAJ_parse_json. + */ +struct BAJ_Specification +{ + + /** + * Command to execute. + */ + enum BAJ_Command cmd; + + /** + * Name of the field to access. + */ + const char *field; + + /** + * Further details for the command. + */ + union { + + /** + * Where to store amount for #BAJ_CMD_AMOUNT. + */ + struct TALER_Amount *amount; + + /** + * Where to store time, for #BAJ_CMD_TIME_ABSOLUTE. + */ + struct GNUNET_TIME_Absolute *abs_time; + + /** + * Where to write binary data, for #BAJ_CMD_BINARY_FIXED. + */ + struct { + /** + * Where to write the data. + */ + void *dest; + + /** + * How many bytes to write to @e dest. + */ + size_t dest_size; + + } fixed_data; + + /** + * Where to write binary data, for #BAJ_CMD_BINARY_VARIABLE. + */ + struct { + /** + * Where to store the pointer with the data (is allocated). + */ + void **dest_p; + + /** + * Where to store the number of bytes allocated at `*dest`. + */ + size_t *dest_size_p; + + } variable_data; + + /** + * Where to store the RSA public key for #BAJ_CMD_RSA_PUBLIC_KEY + */ + struct GNUNET_CRYPTO_rsa_PublicKey **rsa_public_key; + + /** + * Where to store the RSA signature for #BAJ_CMD_RSA_SIGNATURE + */ + struct GNUNET_CRYPTO_rsa_Signature **rsa_signature; + + /** + * Details for #BAJ_CMD_EDDSA_SIGNATURE + */ + struct { + + /** + * Where to store the purpose. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose **purpose_p; + + /** + * Key to verify the signature against. + */ + const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key; + + } eddsa_signature; + + /** + * Where to store a pointer to the string. + */ + const char **strptr; + + /** + * Where to store 16-bit integer. + */ + uint16_t *u16; + + /** + * Where to store 64-bit integer. + */ + uint64_t *u64; + + /** + * Where to store a JSON object. + */ + json_t **obj; + + } details; + +}; + + +/** + * Navigate and parse data in a JSON tree. + * + * @param root the JSON node to start the navigation at. + * @param spec parse specification array + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +int +BAJ_parse_json (const json_t *root, + struct BAJ_Specification *spec); + + +/** + * Free all elements allocated during a + * #BAJ_parse_json() operation. + * + * @param spec specification of the parse operation + */ +void +BAJ_parse_free (struct BAJ_Specification *spec); + + +/** + * End of a parser specification. + */ +#define BAJ_spec_end { .cmd = BAJ_CMD_END } + +/** + * Fixed size object (in network byte order, encoded using Crockford + * Base32hex encoding). + * + * @param name name of the JSON field + * @param obj pointer where to write the data (type of `*obj` will determine size) + */ +#define BAJ_spec_fixed_auto(name,obj) { .cmd = BAJ_CMD_BINARY_FIXED, .field = name, .details.fixed_data.dest = obj, .details.fixed_data.dest_size = sizeof (*obj) } + + +/** + * Variable size object (in network byte order, encoded using Crockford + * Base32hex encoding). + * + * @param name name of the JSON field + * @param obj pointer where to write the data (a `void **`) + * @param size where to store the number of bytes allocated for @a obj (of type `size_t *` + */ +#define BAJ_spec_varsize(name,obj,size) { .cmd = BAJ_CMD_BINARY_VARIABLE, .field = name, .details.variable_data.dest_p = obj, .details.variable_data.dest_size_p = size } + + +/** + * The expected field stores a string. + * + * @param name name of the JSON field + * @param strptr where to store a pointer to the field + */ +struct BAJ_Specification +BAJ_spec_string (const char *name, + const char **strptr); + + +/** + * Absolute time. + * + * @param name name of the JSON field + * @param[out] at where to store the absolute time found under @a name + */ +struct BAJ_Specification +BAJ_spec_absolute_time (const char *name, + struct GNUNET_TIME_Absolute *at); + + +/** + * 16-bit integer. + * + * @param name name of the JSON field + * @param[out] u16 where to store the integer found under @a name + */ +struct BAJ_Specification +BAJ_spec_uint16 (const char *name, + uint16_t *u16); + + +/** + * 64-bit integer. + * + * @param name name of the JSON field + * @param[out] u64 where to store the integer found under @a name + */ +struct BAJ_Specification +BAJ_spec_uint64 (const char *name, + uint64_t *u64); + + +/** + * JSON object. + * + * @param name name of the JSON field + * @param[out] jsonp where to store the JSON found under @a name + */ +struct BAJ_Specification +BAJ_spec_json (const char *name, + json_t **jsonp); + + +/** + * Specification for parsing an amount value. + * + * @param name name of the JSON field + * @param amount where to store the amount under @a name + */ +struct BAJ_Specification +BAJ_spec_amount (const char *name, + struct TALER_Amount *amount); + + +/** + * Specification for parsing an RSA public key. + * + * @param name name of the JSON field + * @param pk where to store the RSA key found under @a name + */ +struct BAJ_Specification +BAJ_spec_rsa_public_key (const char *name, + struct GNUNET_CRYPTO_rsa_PublicKey **pk); + + +/** + * Specification for parsing an RSA signature. + * + * @param name name of the JSON field + * @param sig where to store the RSA signature found under @a name + */ +struct BAJ_Specification +BAJ_spec_rsa_signature (const char *name, + struct GNUNET_CRYPTO_rsa_Signature **sig); + + + + +/* end of mint_api_json.h */ diff --git a/src/bank-lib/test_bank_api.c b/src/bank-lib/test_bank_api.c new file mode 100644 index 00000000..3c1747a1 --- /dev/null +++ b/src/bank-lib/test_bank_api.c @@ -0,0 +1,2610 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015, 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file bank/test_bank_api.c + * @brief testcase to test bank's HTTP API interface + * @author Sree Harsha Totakura + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_signatures.h" +#include "taler_bank_service.h" +#include +#include + +/** + * Is the configuration file is set to include wire format 'test'? + */ +#define WIRE_TEST 1 + +/** + * Is the configuration file is set to include wire format 'sepa'? + */ +#define WIRE_SEPA 1 + +/** + * Main execution context for the main loop. + */ +static struct TALER_BANK_Context *ctx; + +/** + * Handle to access the bank. + */ +static struct TALER_BANK_Handle *bank; + +/** + * Task run on shutdown. + */ +static struct GNUNET_SCHEDULER_Task *shutdown_task; + +/** + * Task that runs the main event loop. + */ +static struct GNUNET_SCHEDULER_Task *ctx_task; + +/** + * Result of the testcases, #GNUNET_OK on success + */ +static int result; + + +/** + * Opcodes for the interpreter. + */ +enum OpCode +{ + /** + * Termination code, stops the interpreter loop (with success). + */ + OC_END = 0, + + /** + * Add funds to a reserve by (faking) incoming wire transfer. + */ + OC_ADMIN_ADD_INCOMING, + + /** + * Check status of a reserve. + */ + OC_WITHDRAW_STATUS, + + /** + * Withdraw a coin from a reserve. + */ + OC_WITHDRAW_SIGN, + + /** + * Deposit a coin (pay with it). + */ + OC_DEPOSIT, + + /** + * Melt a (set of) coins. + */ + OC_REFRESH_MELT, + + /** + * Complete melting session by withdrawing melted coins. + */ + OC_REFRESH_REVEAL, + + /** + * Verify bank's /refresh/link by linking original private key to + * results from #OC_REFRESH_REVEAL step. + */ + OC_REFRESH_LINK, + + /** + * Verify the bank's /wire-method. + */ + OC_WIRE, + + /** + * Verify bank's /wire/deposits method. + */ + OC_WIRE_DEPOSITS, + + /** + * Verify bank's /deposit/wtid method. + */ + OC_DEPOSIT_WTID + +}; + + +/** + * 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_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_BANK_DenomPublicKey *pk; + + /** + * Set (by the interpreter) to the bank'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; + +}; + + +/** + * Details for a bank operation to execute. + */ +struct Command +{ + /** + * Opcode of the command. + */ + enum OpCode oc; + + /** + * Label for the command, can be NULL. + */ + const char *label; + + /** + * Which response code do we expect for this command? + */ + unsigned int expected_response_code; + + /** + * Details about the command. + */ + union + { + + /** + * Information for a #OC_ADMIN_ADD_INCOMING command. + */ + struct + { + + /** + * Label to another admin_add_incoming command if we + * should deposit into an existing reserve, NULL if + * a fresh reserve should be created. + */ + const char *reserve_reference; + + /** + * String describing the amount to add to the reserve. + */ + const char *amount; + + /** + * Wire details (JSON). + */ + const char *wire; + + /** + * Set (by the interpreter) to the reserve's private key + * we used to fill the reserve. + */ + struct TALER_ReservePrivateKeyP reserve_priv; + + /** + * Set to the API's handle during the operation. + */ + struct TALER_BANK_AdminAddIncomingHandle *aih; + + } admin_add_incoming; + + /** + * Information for a #OC_WITHDRAW_STATUS command. + */ + struct + { + + /** + * Label to the #OC_ADMIN_ADD_INCOMING command which + * created the reserve. + */ + const char *reserve_reference; + + /** + * Set to the API's handle during the operation. + */ + struct TALER_BANK_ReserveStatusHandle *wsh; + + /** + * Expected reserve balance. + */ + const char *expected_balance; + + } reserve_status; + + /** + * Information for a #OC_WITHDRAW_SIGN command. + */ + struct + { + + /** + * Which reserve should we withdraw from? + */ + const char *reserve_reference; + + /** + * String describing the denomination value we should withdraw. + * A corresponding denomination key must exist in the bank's + * offerings. Can be NULL if @e pk is set instead. + */ + const char *amount; + + /** + * If @e amount is NULL, this specifies the denomination key to + * use. Otherwise, this will be set (by the interpreter) to the + * denomination PK matching @e amount. + */ + const struct TALER_BANK_DenomPublicKey *pk; + + /** + * Set (by the interpreter) to the bank'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; + + /** + * Blinding key used for the operation. + */ + struct TALER_DenominationBlindingKey blinding_key; + + /** + * Withdraw handle (while operation is running). + */ + struct TALER_BANK_ReserveWithdrawHandle *wsh; + + } reserve_withdraw; + + /** + * Information for a #OC_DEPOSIT command. + */ + struct + { + + /** + * Amount to deposit. + */ + const char *amount; + + /** + * Reference to a reserve_withdraw operation for a coin to + * be used for the /deposit operation. + */ + 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; + + /** + * JSON string describing the contract between the two parties. + */ + const char *contract; + + /** + * Transaction ID to use. + */ + uint64_t transaction_id; + + /** + * Relative time (to add to 'now') to compute the refund deadline. + * Zero for no refunds. + */ + struct GNUNET_TIME_Relative refund_deadline; + + /** + * Set (by the interpreter) to a fresh private key of the merchant, + * if @e refund_deadline is non-zero. + */ + struct TALER_MerchantPrivateKeyP merchant_priv; + + /** + * Deposit handle while operation is running. + */ + struct TALER_BANK_DepositHandle *dh; + + } 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_BANK_DenomPublicKey **fresh_pks; + + /** + * Melt handle while operation is running. + */ + struct TALER_BANK_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 bank. + */ + 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_BANK_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_BANK_RefreshLinkHandle *rlh; + + /** + * Which of the melted coins should be used for the linkage? + */ + unsigned int coin_idx; + + } refresh_link; + + /** + * Information for the /wire command. + */ + struct { + + /** + * Handle to the wire request. + */ + struct TALER_BANK_WireHandle *wh; + + /** + * Format we expect to see, others will be *ignored*. + */ + const char *format; + + } wire; + + /** + * Information for the /wire/deposits's command. + */ + struct { + + /** + * Handle to the wire deposits request. + */ + struct TALER_BANK_WireDepositsHandle *wdh; + + /** + * Reference to a /deposit/wtid command. If set, we use the + * WTID from that command. + */ + const char *wtid_ref; + + /** + * WTID to use (used if @e wtid_ref is NULL). + */ + struct TALER_WireTransferIdentifierRawP wtid; + + /* TODO: may want to add list of deposits we expected + to see aggregated here in the future. */ + + } wire_deposits; + + /** + * Information for the /deposit/wtid command. + */ + struct { + + /** + * Handle to the deposit wtid request. + */ + struct TALER_BANK_DepositWtidHandle *dwh; + + /** + * Which /deposit operation should we obtain WTID data for? + */ + const char *deposit_ref; + + /** + * What is the expected total amount? Only used if + * @e expected_response_code was #MHD_HTTP_OK. + */ + struct TALER_Amount total_amount_expected; + + /** + * Wire transfer identifier, set if #MHD_HTTP_OK was the response code. + */ + struct TALER_WireTransferIdentifierRawP wtid; + + } deposit_wtid; + + } details; + +}; + + +/** + * State of the interpreter loop. + */ +struct InterpreterState +{ + /** + * Keys from the bank. + */ + const struct TALER_BANK_Keys *keys; + + /** + * Commands the interpreter will run. + */ + struct Command *commands; + + /** + * Interpreter task (if one is scheduled). + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Instruction pointer. Tells #interpreter_run() which + * instruction to run next. + */ + unsigned int ip; + +}; + + +/** + * Task that runs the context's event loop with the GNUnet scheduler. + * + * @param cls unused + * @param tc scheduler context (unused) + */ +static void +context_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Run the context task, the working set has changed. + */ +static void +trigger_context_task () +{ + GNUNET_SCHEDULER_cancel (ctx_task); + ctx_task = GNUNET_SCHEDULER_add_now (&context_task, + NULL); +} + + +/** + * The testcase failed, return with an error code. + * + * @param is interpreter state to clean up + */ +static void +fail (struct InterpreterState *is) +{ + result = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Find a command by label. + * + * @param is interpreter state to search + * @param label label to look for + * @return NULL if command was not found + */ +static const struct Command * +find_command (const struct InterpreterState *is, + const char *label) +{ + unsigned int i; + const struct Command *cmd; + + if (NULL == label) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Attempt to lookup command for empty label\n"); + return NULL; + } + for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++) + if ( (NULL != cmd->label) && + (0 == strcmp (cmd->label, + label)) ) + return cmd; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command not found: %s\n", + label); + return NULL; +} + + +/** + * Run the main interpreter loop that performs bank operations. + * + * @param cls contains the `struct InterpreterState` + * @param tc scheduler context + */ +static void +interpreter_run (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * 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 bank's reply is bogus (fails to follow the protocol) + * @param full_response full response from the bank (for logging, in case of errors) + */ +static void +add_incoming_cb (void *cls, + unsigned int http_status, + json_t *full_response) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + + cmd->details.admin_add_incoming.aih = NULL; + if (MHD_HTTP_OK != http_status) + { + GNUNET_break (0); + fail (is); + return; + } + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); +} + + +/** + * Check if the given historic event @a h corresponds to the given + * command @a cmd. + * + * @param h event in history + * @param cmd an #OC_ADMIN_ADD_INCOMING command + * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not + */ +static int +compare_admin_add_incoming_history (const struct TALER_BANK_ReserveHistory *h, + const struct Command *cmd) +{ + struct TALER_Amount amount; + + if (TALER_BANK_RTT_DEPOSIT != h->type) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (cmd->details.admin_add_incoming.amount, + &amount)); + if (0 != TALER_amount_cmp (&amount, + &h->amount)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Check if the given historic event @a h corresponds to the given + * command @a cmd. + * + * @param h event in history + * @param cmd an #OC_WITHDRAW_SIGN command + * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not + */ +static int +compare_reserve_withdraw_history (const struct TALER_BANK_ReserveHistory *h, + const struct Command *cmd) +{ + struct TALER_Amount amount; + struct TALER_Amount amount_with_fee; + + if (TALER_BANK_RTT_WITHDRAWAL != h->type) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (cmd->details.reserve_withdraw.amount, + &amount)); + GNUNET_assert (GNUNET_OK == + TALER_amount_add (&amount_with_fee, + &amount, + &cmd->details.reserve_withdraw.pk->fee_withdraw)); + if (0 != TALER_amount_cmp (&amount_with_fee, + &h->amount)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Function called with the result of a /reserve/status request. + * + * @param cls closure with the interpreter state + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request + * 0 if the bank's reply is bogus (fails to follow the protocol) + * @param[in] json original response in JSON format (useful only for diagnostics) + * @param balance current balance in the reserve, NULL on error + * @param history_length number of entries in the transaction history, 0 on error + * @param history detailed transaction history, NULL on error + */ +static void +reserve_status_cb (void *cls, + unsigned int http_status, + json_t *json, + const struct TALER_Amount *balance, + unsigned int history_length, + const struct TALER_BANK_ReserveHistory *history) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + struct Command *rel; + unsigned int i; + unsigned int j; + struct TALER_Amount amount; + + cmd->details.reserve_status.wsh = 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); + GNUNET_break (0); + json_dumpf (json, stderr, 0); + fail (is); + return; + } + switch (http_status) + { + case MHD_HTTP_OK: + /* FIXME: note that history events may come in a different + order than the commands. However, for now this works... */ + j = 0; + for (i=0;iip;i++) + { + switch ((rel = &is->commands[i])->oc) + { + case OC_ADMIN_ADD_INCOMING: + if ( ( (NULL != rel->label) && + (0 == strcmp (cmd->details.reserve_status.reserve_reference, + rel->label) ) ) || + ( (NULL != rel->details.admin_add_incoming.reserve_reference) && + (0 == strcmp (cmd->details.reserve_status.reserve_reference, + rel->details.admin_add_incoming.reserve_reference) ) ) ) + { + if (GNUNET_OK != + compare_admin_add_incoming_history (&history[j], + rel)) + { + GNUNET_break (0); + fail (is); + return; + } + j++; + } + break; + case OC_WITHDRAW_SIGN: + if (0 == strcmp (cmd->details.reserve_status.reserve_reference, + rel->details.reserve_withdraw.reserve_reference)) + { + if (GNUNET_OK != + compare_reserve_withdraw_history (&history[j], + rel)) + { + GNUNET_break (0); + fail (is); + return; + } + j++; + } + break; + default: + /* unreleated, just skip */ + break; + } + } + if (j != history_length) + { + GNUNET_break (0); + fail (is); + return; + } + if (NULL != cmd->details.reserve_status.expected_balance) + { + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (cmd->details.reserve_status.expected_balance, + &amount)); + if (0 != TALER_amount_cmp (&amount, + balance)) + { + GNUNET_break (0); + fail (is); + return; + } + } + break; + default: + /* Unsupported status code (by test harness) */ + GNUNET_break (0); + break; + } + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); +} + + +/** + * Function called upon completion of our /reserve/withdraw 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 bank's reply is bogus (fails to follow the protocol) + * @param sig signature over the coin, NULL on error + * @param full_response full response from the bank (for logging, in case of errors) + */ +static void +reserve_withdraw_cb (void *cls, + unsigned int http_status, + const struct TALER_DenominationSignature *sig, + json_t *full_response) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + + cmd->details.reserve_withdraw.wsh = 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); + json_dumpf (full_response, stderr, 0); + GNUNET_break (0); + fail (is); + return; + } + switch (http_status) + { + case MHD_HTTP_OK: + if (NULL == sig) + { + GNUNET_break (0); + fail (is); + return; + } + cmd->details.reserve_withdraw.sig.rsa_signature + = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); + break; + case MHD_HTTP_PAYMENT_REQUIRED: + /* nothing to check */ + break; + default: + /* Unsupported status code (by test harness) */ + GNUNET_break (0); + break; + } + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); +} + + +/** + * 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 bank's reply is bogus (fails to follow the protocol) + * @param obj the received JSON reply, should be kept as proof (and, in case of errors, + * be forwarded to the customer) + */ +static void +deposit_cb (void *cls, + unsigned int http_status, + json_t *obj) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + + cmd->details.deposit.dh = NULL; + 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); + json_dumpf (obj, stderr, 0); + fail (is); + return; + } + 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 bank's reply is bogus (fails to follow the protocol) + * @param noreveal_index choice by the bank in the cut-and-choose protocol, + * UINT16_MAX on error + * @param full_response full response from the bank (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); + json_dumpf (full_response, stderr, 0); + 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 bank'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 bank (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); + json_dumpf (full_response, stderr, 0); + 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;idetails.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 bank'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 bank (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; + unsigned int j; + unsigned int found; + + 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); + json_dumpf (full_response, stderr, 0); + 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;idetails.refresh_reveal.fresh_coins[j]; + 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)) ) + { + found++; + break; + } + } + if (found != num_coins) + { + fprintf (stderr, + "Only %u/%u coins match expectations\n", + found, + num_coins); + GNUNET_break (0); + fail (is); + return; + } + break; + default: + break; + } + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); +} + + +/** + * Find denomination key matching the given amount. + * + * @param keys array of keys to search + * @param amount coin value to look for + * @return NULL if no matching key was found + */ +static const struct TALER_BANK_DenomPublicKey * +find_pk (const struct TALER_BANK_Keys *keys, + const struct TALER_Amount *amount) +{ + unsigned int i; + struct GNUNET_TIME_Absolute now; + struct TALER_BANK_DenomPublicKey *pk; + char *str; + + now = GNUNET_TIME_absolute_get (); + for (i=0;inum_denom_keys;i++) + { + pk = &keys->denom_keys[i]; + if ( (0 == TALER_amount_cmp (amount, + &pk->value)) && + (now.abs_value_us >= pk->valid_from.abs_value_us) && + (now.abs_value_us < pk->withdraw_valid_until.abs_value_us) ) + return pk; + } + /* do 2nd pass to check if expiration times are to blame for failure */ + str = TALER_amount_to_string (amount); + for (i=0;inum_denom_keys;i++) + { + pk = &keys->denom_keys[i]; + if ( (0 == TALER_amount_cmp (amount, + &pk->value)) && + ( (now.abs_value_us < pk->valid_from.abs_value_us) || + (now.abs_value_us > pk->withdraw_valid_until.abs_value_us) ) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Have denomination key for `%s', but with wrong expiration range %llu vs [%llu,%llu)\n", + str, + now.abs_value_us, + pk->valid_from.abs_value_us, + pk->withdraw_valid_until.abs_value_us); + GNUNET_free (str); + return NULL; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No denomination key for amount %s found\n", + str); + GNUNET_free (str); + return NULL; +} + + +/** + * Callbacks called with the result(s) of a + * wire format inquiry request to the bank. + * + * The callback is invoked multiple times, once for each supported @a + * method. Finally, it is invoked one more time with cls/0/NULL/NULL + * to indicate the end of the iteration. If any request fails to + * generate a valid response from the bank, @a http_status will also + * be zero and the iteration will also end. Thus, the iteration + * always ends with a final call with an @a http_status of 0. If the + * @a http_status is already 0 on the first call, then the response to + * the /wire request was invalid. Later, clients can tell the + * difference between @a http_status of 0 indicating a failed + * /wire/method request and a regular end of the iteration by @a + * method being non-NULL. If the bank simply correctly asserts that + * it does not support any methods, @a method will be NULL but the @a + * http_status will be #MHD_HTTP_OK for the first call (followed by a + * cls/0/NULL/NULL call to signal the end of the iteration). + * + * @param cls closure with the interpreter state + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful request; + * 0 if the bank's reply is bogus (fails to follow the protocol) + * @param method wire format method supported, i.e. "test" or "sepa", or NULL + * if already the /wire request failed. + * @param obj the received JSON reply, if successful this should be the wire + * format details as provided by /wire/METHOD/, or NULL if the + * reply was not in JSON format (in this case, the client might + * want to do an HTTP request to /wire/METHOD/ with a browser to + * provide more information to the user about the @a method). + */ +static void +wire_cb (void *cls, + unsigned int http_status, + const char *method, + json_t *obj) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + + if (0 == http_status) + { + /* 0 always signals the end of the iteration */ + cmd->details.wire.wh = NULL; + } + else if ( (NULL != method) && + (0 != strcasecmp (method, + cmd->details.wire.format)) ) + { + /* not the method we care about, skip */ + return; + } + if (cmd->expected_response_code != http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u to command %s/%s\n", + http_status, + cmd->label, + method); + json_dumpf (obj, stderr, 0); + fail (is); + return; + } + if (0 == http_status) + { + /* end of iteration, move to next command */ + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); + return; + } + /* For now, we only support to be called only once + with a "positive" result; so we switch to an + expected value of 0 for the 2nd iteration */ + cmd->expected_response_code = 0; +} + + +/** + * 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 bank protocol violation + * @param json original json reply (may include signatures, those have then been + * validated already) + * @param wtid extracted wire transfer identifier, or NULL if the bank could + * not provide any (set only if @a http_status is #MHD_HTTP_OK) + * @param total_amount total amount of the wire transfer, or NULL if the bank could + * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK) + * @param details_length length of the @a details array + * @param details array with details about the combined transactions + */ +static void +wire_deposits_cb (void *cls, + unsigned int http_status, + json_t *json, + const struct GNUNET_HashCode *h_wire, + const struct TALER_Amount *total_amount, + unsigned int details_length, + const struct TALER_WireDepositDetails *details) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + const struct Command *ref; + + cmd->details.wire_deposits.wdh = NULL; + ref = find_command (is, + cmd->details.wire_deposits.wtid_ref); + 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); + json_dumpf (json, stderr, 0); + fail (is); + return; + } + switch (http_status) + { + case MHD_HTTP_OK: + if (0 != TALER_amount_cmp (total_amount, + &ref->details.deposit_wtid.total_amount_expected)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Total amount missmatch to command %s\n", + http_status, + cmd->label); + json_dumpf (json, stderr, 0); + fail (is); + return; + } + if (NULL != ref->details.deposit_wtid.deposit_ref) + { + const struct Command *dep; + struct GNUNET_HashCode hw; + + dep = find_command (is, + ref->details.deposit_wtid.deposit_ref); + GNUNET_CRYPTO_hash (dep->details.deposit.wire_details, + strlen (dep->details.deposit.wire_details), + &hw); + if (0 != memcmp (&hw, + h_wire, + sizeof (struct GNUNET_HashCode))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Wire hash missmatch to command %s\n", + cmd->label); + json_dumpf (json, stderr, 0); + fail (is); + return; + } + } + break; + default: + break; + } + + /* move to next command */ + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); +} + + +/** + * Function called with detailed wire transfer data. + * + * @param cls closure + * @param http_status HTTP status code we got, 0 on bank protocol violation + * @param json original json reply (may include signatures, those have then been + * validated already) + * @param wtid wire transfer identifier used by the bank, NULL if bank 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 bank 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, + json_t *json, + const struct TALER_WireTransferIdentifierRawP *wtid, + struct GNUNET_TIME_Absolute execution_time, + const struct TALER_Amount *coin_contribution, + const struct TALER_Amount *total_amount) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + + cmd->details.deposit_wtid.dwh = 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); + json_dumpf (json, stderr, 0); + fail (is); + return; + } + switch (http_status) + { + case MHD_HTTP_OK: + cmd->details.deposit_wtid.wtid = *wtid; + if (0 != TALER_amount_cmp (total_amount, + &cmd->details.deposit_wtid.total_amount_expected)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Total amount missmatch to command %s\n", + cmd->label); + json_dumpf (json, stderr, 0); + fail (is); + return; + } + break; + default: + break; + } + + /* move to next command */ + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); +} + + +/** + * Run the main interpreter loop that performs bank operations. + * + * @param cls contains the `struct InterpreterState` + * @param tc scheduler context + */ +static void +interpreter_run (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + const struct Command *ref; + struct TALER_ReservePublicKeyP reserve_pub; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_Amount amount; + struct GNUNET_TIME_Absolute execution_date; + json_t *wire; + + is->task = NULL; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + fprintf (stderr, + "Test aborted by shutdown request\n"); + fail (is); + return; + } + switch (cmd->oc) + { + case OC_END: + result = GNUNET_OK; + GNUNET_SCHEDULER_shutdown (); + return; + case OC_ADMIN_ADD_INCOMING: + if (NULL != + cmd->details.admin_add_incoming.reserve_reference) + { + ref = find_command (is, + cmd->details.admin_add_incoming.reserve_reference); + GNUNET_assert (NULL != ref); + GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); + cmd->details.admin_add_incoming.reserve_priv + = ref->details.admin_add_incoming.reserve_priv; + } + else + { + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + + priv = GNUNET_CRYPTO_eddsa_key_create (); + cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *priv; + GNUNET_free (priv); + } + GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv, + &reserve_pub.eddsa_pub); + if (GNUNET_OK != + TALER_string_to_amount (cmd->details.admin_add_incoming.amount, + &amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %u\n", + cmd->details.admin_add_incoming.amount, + is->ip); + fail (is); + return; + } + wire = json_loads (cmd->details.admin_add_incoming.wire, + JSON_REJECT_DUPLICATES, + NULL); + if (NULL == wire) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse wire details `%s' at %u\n", + cmd->details.admin_add_incoming.wire, + is->ip); + fail (is); + return; + } + execution_date = GNUNET_TIME_absolute_get (); + TALER_round_abs_time (&execution_date); + cmd->details.admin_add_incoming.aih + = TALER_BANK_admin_add_incoming (bank, + &reserve_pub, + &amount, + execution_date, + wire, + &add_incoming_cb, + is); + if (NULL == cmd->details.admin_add_incoming.aih) + { + GNUNET_break (0); + fail (is); + return; + } + trigger_context_task (); + return; + case OC_WITHDRAW_STATUS: + GNUNET_assert (NULL != + cmd->details.reserve_status.reserve_reference); + ref = find_command (is, + cmd->details.reserve_status.reserve_reference); + GNUNET_assert (NULL != ref); + GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); + GNUNET_CRYPTO_eddsa_key_get_public (&ref->details.admin_add_incoming.reserve_priv.eddsa_priv, + &reserve_pub.eddsa_pub); + cmd->details.reserve_status.wsh + = TALER_BANK_reserve_status (bank, + &reserve_pub, + &reserve_status_cb, + is); + trigger_context_task (); + return; + case OC_WITHDRAW_SIGN: + GNUNET_assert (NULL != + cmd->details.reserve_withdraw.reserve_reference); + ref = find_command (is, + cmd->details.reserve_withdraw.reserve_reference); + GNUNET_assert (NULL != ref); + GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); + if (NULL != cmd->details.reserve_withdraw.amount) + { + if (GNUNET_OK != + TALER_string_to_amount (cmd->details.reserve_withdraw.amount, + &amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %u\n", + cmd->details.reserve_withdraw.amount, + is->ip); + fail (is); + return; + } + cmd->details.reserve_withdraw.pk = find_pk (is->keys, + &amount); + } + if (NULL == cmd->details.reserve_withdraw.pk) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to determine denomination key at %u\n", + is->ip); + fail (is); + return; + } + + /* create coin's private key */ + { + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + + priv = GNUNET_CRYPTO_eddsa_key_create (); + cmd->details.reserve_withdraw.coin_priv.eddsa_priv = *priv; + GNUNET_free (priv); + } + GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.reserve_withdraw.coin_priv.eddsa_priv, + &coin_pub.eddsa_pub); + cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key + = GNUNET_CRYPTO_rsa_blinding_key_create (GNUNET_CRYPTO_rsa_public_key_len (cmd->details.reserve_withdraw.pk->key.rsa_public_key)); + cmd->details.reserve_withdraw.wsh + = TALER_BANK_reserve_withdraw (bank, + cmd->details.reserve_withdraw.pk, + &ref->details.admin_add_incoming.reserve_priv, + &cmd->details.reserve_withdraw.coin_priv, + &cmd->details.reserve_withdraw.blinding_key, + &reserve_withdraw_cb, + is); + if (NULL == cmd->details.reserve_withdraw.wsh) + { + GNUNET_break (0); + fail (is); + return; + } + trigger_context_task (); + return; + case OC_DEPOSIT: + { + struct GNUNET_HashCode h_contract; + const struct TALER_CoinSpendPrivateKeyP *coin_priv; + const struct TALER_BANK_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; + struct GNUNET_TIME_Absolute wire_deadline; + struct GNUNET_TIME_Absolute timestamp; + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + struct TALER_MerchantPublicKeyP merchant_pub; + json_t *contract; + json_t *wire; + + GNUNET_assert (NULL != + cmd->details.deposit.coin_ref); + ref = find_command (is, + cmd->details.deposit.coin_ref); + GNUNET_assert (NULL != ref); + switch (ref->oc) + { + case OC_WITHDRAW_SIGN: + coin_priv = &ref->details.reserve_withdraw.coin_priv; + coin_pk = ref->details.reserve_withdraw.pk; + coin_pk_sig = &ref->details.reserve_withdraw.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)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %u\n", + cmd->details.deposit.amount, + is->ip); + fail (is); + return; + } + contract = json_loads (cmd->details.deposit.contract, + JSON_REJECT_DUPLICATES, + NULL); + if (NULL == contract) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse contract details `%s' at %u/%s\n", + cmd->details.deposit.contract, + is->ip, + cmd->label); + fail (is); + return; + } + TALER_hash_json (contract, + &h_contract); + wire = json_loads (cmd->details.deposit.wire_details, + JSON_REJECT_DUPLICATES, + NULL); + if (NULL == wire) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse wire details `%s' at %u/%s\n", + cmd->details.deposit.wire_details, + is->ip, + cmd->label); + fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, + &coin_pub.eddsa_pub); + + priv = GNUNET_CRYPTO_eddsa_key_create (); + cmd->details.deposit.merchant_priv.eddsa_priv = *priv; + GNUNET_free (priv); + if (0 != cmd->details.deposit.refund_deadline.rel_value_us) + { + refund_deadline = GNUNET_TIME_relative_to_absolute (cmd->details.deposit.refund_deadline); + } + else + { + refund_deadline = GNUNET_TIME_UNIT_ZERO_ABS; + } + GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.deposit.merchant_priv.eddsa_priv, + &merchant_pub.eddsa_pub); + + wire_deadline = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS); + timestamp = GNUNET_TIME_absolute_get (); + TALER_round_abs_time (×tamp); + { + 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 = h_contract; + TALER_hash_json (wire, + &dr.h_wire); + dr.timestamp = GNUNET_TIME_absolute_hton (timestamp); + dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); + dr.transaction_id = GNUNET_htonll (cmd->details.deposit.transaction_id); + TALER_amount_hton (&dr.amount_with_fee, + &amount); + TALER_amount_hton (&dr.deposit_fee, + &coin_pk->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)); + } + cmd->details.deposit.dh + = TALER_BANK_deposit (bank, + &amount, + wire_deadline, + wire, + &h_contract, + &coin_pub, + coin_pk_sig, + &coin_pk->key, + timestamp, + cmd->details.deposit.transaction_id, + &merchant_pub, + refund_deadline, + &coin_sig, + &deposit_cb, + is); + if (NULL == cmd->details.deposit.dh) + { + GNUNET_break (0); + json_decref (wire); + fail (is); + return; + } + json_decref (wire); + 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_BANK_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_BANK_DenomPublicKey melt_pks[num_melted_coins]; + struct TALER_BANK_DenomPublicKey fresh_pks[num_fresh_coins]; + unsigned int i; + + for (i=0;idetails.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.reserve_withdraw.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.reserve_withdraw.sig; + melt_pks[i] = *ref->details.reserve_withdraw.pk; + } + for (i=0;idetails.refresh_melt.fresh_amounts[i], + &amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %u\n", + cmd->details.reserve_withdraw.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_BANK_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_BANK_refresh_melt (bank, + 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_BANK_refresh_reveal (bank, + 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 reserve_withdraw 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); + } + GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc); + /* finally, use private key from withdraw sign command */ + cmd->details.refresh_link.rlh + = TALER_BANK_refresh_link (bank, + &ref->details.reserve_withdraw.coin_priv, + &link_cb, + is); + if (NULL == cmd->details.refresh_link.rlh) + { + GNUNET_break (0); + fail (is); + return; + } + trigger_context_task (); + return; + case OC_WIRE: + cmd->details.wire.wh = TALER_BANK_wire (bank, + &wire_cb, + is); + trigger_context_task (); + return; + case OC_WIRE_DEPOSITS: + if (NULL != cmd->details.wire_deposits.wtid_ref) + { + ref = find_command (is, + cmd->details.wire_deposits.wtid_ref); + GNUNET_assert (NULL != ref); + cmd->details.wire_deposits.wtid = ref->details.deposit_wtid.wtid; + } + cmd->details.wire_deposits.wdh + = TALER_BANK_wire_deposits (bank, + &cmd->details.wire_deposits.wtid, + &wire_deposits_cb, + is); + trigger_context_task (); + return; + case OC_DEPOSIT_WTID: + { + struct GNUNET_HashCode h_wire; + struct GNUNET_HashCode h_contract; + json_t *wire; + json_t *contract; + const struct Command *coin; + struct TALER_CoinSpendPublicKeyP coin_pub; + + ref = find_command (is, + cmd->details.deposit_wtid.deposit_ref); + GNUNET_assert (NULL != ref); + coin = find_command (is, + ref->details.deposit.coin_ref); + GNUNET_assert (NULL != coin); + switch (coin->oc) + { + case OC_WITHDRAW_SIGN: + GNUNET_CRYPTO_eddsa_key_get_public (&coin->details.reserve_withdraw.coin_priv.eddsa_priv, + &coin_pub.eddsa_pub); + break; + case OC_REFRESH_REVEAL: + { + const struct FreshCoin *fc; + unsigned int idx; + + idx = ref->details.deposit.coin_idx; + GNUNET_assert (idx < coin->details.refresh_reveal.num_fresh_coins); + fc = &coin->details.refresh_reveal.fresh_coins[idx]; + + GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv, + &coin_pub.eddsa_pub); + } + break; + default: + GNUNET_assert (0); + } + + wire = json_loads (ref->details.deposit.wire_details, + JSON_REJECT_DUPLICATES, + NULL); + GNUNET_assert (NULL != wire); + TALER_hash_json (wire, + &h_wire); + json_decref (wire); + contract = json_loads (ref->details.deposit.contract, + JSON_REJECT_DUPLICATES, + NULL); + GNUNET_assert (NULL != contract); + TALER_hash_json (contract, + &h_contract); + json_decref (contract); + cmd->details.deposit_wtid.dwh + = TALER_BANK_deposit_wtid (bank, + &ref->details.deposit.merchant_priv, + &h_wire, + &h_contract, + &coin_pub, + ref->details.deposit.transaction_id, + &deposit_wtid_cb, + is); + trigger_context_task (); + } + return; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unknown instruction %d at %u (%s)\n", + cmd->oc, + is->ip, + cmd->label); + fail (is); + return; + } +} + + +/** + * Function run when the test terminates (good or bad). + * Cleans up our state. + * + * @param cls the interpreter state. + * @param tc unused + */ +static void +do_shutdown (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct InterpreterState *is = cls; + struct Command *cmd; + unsigned int i; + + shutdown_task = NULL; + for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++) + { + switch (cmd->oc) + { + case OC_END: + GNUNET_assert (0); + break; + case OC_ADMIN_ADD_INCOMING: + if (NULL != cmd->details.admin_add_incoming.aih) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_BANK_admin_add_incoming_cancel (cmd->details.admin_add_incoming.aih); + cmd->details.admin_add_incoming.aih = NULL; + } + break; + case OC_WITHDRAW_STATUS: + if (NULL != cmd->details.reserve_status.wsh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_BANK_reserve_status_cancel (cmd->details.reserve_status.wsh); + cmd->details.reserve_status.wsh = NULL; + } + break; + case OC_WITHDRAW_SIGN: + if (NULL != cmd->details.reserve_withdraw.wsh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_BANK_reserve_withdraw_cancel (cmd->details.reserve_withdraw.wsh); + cmd->details.reserve_withdraw.wsh = NULL; + } + if (NULL != cmd->details.reserve_withdraw.sig.rsa_signature) + { + GNUNET_CRYPTO_rsa_signature_free (cmd->details.reserve_withdraw.sig.rsa_signature); + cmd->details.reserve_withdraw.sig.rsa_signature = NULL; + } + if (NULL != cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key) + { + GNUNET_CRYPTO_rsa_blinding_key_free (cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key); + cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key = NULL; + } + break; + case OC_DEPOSIT: + if (NULL != cmd->details.deposit.dh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_BANK_deposit_cancel (cmd->details.deposit.dh); + 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_BANK_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_BANK_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;jdetails.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_BANK_refresh_link_cancel (cmd->details.refresh_link.rlh); + cmd->details.refresh_link.rlh = NULL; + } + break; + case OC_WIRE: + if (NULL != cmd->details.wire.wh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_BANK_wire_cancel (cmd->details.wire.wh); + cmd->details.wire.wh = NULL; + } + break; + case OC_WIRE_DEPOSITS: + if (NULL != cmd->details.wire_deposits.wdh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_BANK_wire_deposits_cancel (cmd->details.wire_deposits.wdh); + cmd->details.wire_deposits.wdh = NULL; + } + break; + case OC_DEPOSIT_WTID: + if (NULL != cmd->details.deposit_wtid.dwh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_BANK_deposit_wtid_cancel (cmd->details.deposit_wtid.dwh); + cmd->details.deposit_wtid.dwh = NULL; + } + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unknown instruction %d at %u (%s)\n", + cmd->oc, + i, + cmd->label); + break; + } + } + if (NULL != is->task) + { + GNUNET_SCHEDULER_cancel (is->task); + is->task = NULL; + } + GNUNET_free (is); + if (NULL != ctx_task) + { + GNUNET_SCHEDULER_cancel (ctx_task); + ctx_task = NULL; + } + if (NULL != bank) + { + TALER_BANK_disconnect (bank); + bank = NULL; + } + if (NULL != ctx) + { + TALER_BANK_fini (ctx); + ctx = NULL; + } +} + + +/** + * Functions of this type are called to provide the retrieved signing and + * denomination keys of the bank. No TALER_BANK_*() functions should be called + * in this callback. + * + * @param cls closure + * @param keys information about keys of the bank + */ +static void +cert_cb (void *cls, + const struct TALER_BANK_Keys *keys) +{ + struct InterpreterState *is = cls; + + /* check that keys is OK */ +#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); GNUNET_SCHEDULER_shutdown(); return; } while (0) + ERR (NULL == keys); + ERR (0 == keys->num_sign_keys); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Read %u signing keys\n", + keys->num_sign_keys); + ERR (0 == keys->num_denom_keys); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Read %u denomination keys\n", + keys->num_denom_keys); +#undef ERR + + /* run actual tests via interpreter-loop */ + is->keys = keys; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); +} + + +/** + * Task that runs the context's event loop with the GNUnet scheduler. + * + * @param cls unused + * @param tc scheduler context (unused) + */ +static void +context_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + long timeout; + int max_fd; + fd_set read_fd_set; + fd_set write_fd_set; + fd_set except_fd_set; + struct GNUNET_NETWORK_FDSet *rs; + struct GNUNET_NETWORK_FDSet *ws; + struct GNUNET_TIME_Relative delay; + + ctx_task = NULL; + TALER_BANK_perform (ctx); + max_fd = -1; + timeout = -1; + FD_ZERO (&read_fd_set); + FD_ZERO (&write_fd_set); + FD_ZERO (&except_fd_set); + TALER_BANK_get_select_info (ctx, + &read_fd_set, + &write_fd_set, + &except_fd_set, + &max_fd, + &timeout); + if (timeout >= 0) + delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, + timeout); + else + delay = GNUNET_TIME_UNIT_FOREVER_REL; + rs = GNUNET_NETWORK_fdset_create (); + GNUNET_NETWORK_fdset_copy_native (rs, + &read_fd_set, + max_fd + 1); + ws = GNUNET_NETWORK_fdset_create (); + GNUNET_NETWORK_fdset_copy_native (ws, + &write_fd_set, + max_fd + 1); + ctx_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, + delay, + rs, + ws, + &context_task, + cls); + GNUNET_NETWORK_fdset_destroy (rs); + GNUNET_NETWORK_fdset_destroy (ws); +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param config configuration + */ +static void +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[] = + { + /* *************** start of /wire testing ************** */ + +#if WIRE_TEST + { .oc = OC_WIRE, + .label = "wire-test", + /* /wire/test replies with a 302 redirect */ + .expected_response_code = MHD_HTTP_FOUND, + .details.wire.format = "test" }, +#endif +#if WIRE_SEPA + { .oc = OC_WIRE, + .label = "wire-sepa", + /* /wire/sepa replies with a 200 redirect */ + .expected_response_code = MHD_HTTP_OK, + .details.wire.format = "sepa" }, +#endif + /* *************** end of /wire testing ************** */ + +#if WIRE_TEST + /* None of this works if 'test' is not allowed as we do + /admin/add/incoming with format 'test' */ + + /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */ + { .oc = OC_ADMIN_ADD_INCOMING, + .label = "create-reserve-1", + .expected_response_code = MHD_HTTP_OK, + .details.admin_add_incoming.wire = "{ \"type\":\"TEST\", \"bank\":\"source bank\", \"account\":42 }", + .details.admin_add_incoming.amount = "EUR:5.01" }, + /* Withdraw a 5 EUR coin, at fee of 1 ct */ + { .oc = OC_WITHDRAW_SIGN, + .label = "withdraw-coin-1", + .expected_response_code = MHD_HTTP_OK, + .details.reserve_withdraw.reserve_reference = "create-reserve-1", + .details.reserve_withdraw.amount = "EUR:5" }, + /* Check that deposit and withdraw operation are in history, and + that the balance is now at zero */ + { .oc = OC_WITHDRAW_STATUS, + .label = "withdraw-status-1", + .expected_response_code = MHD_HTTP_OK, + .details.reserve_status.reserve_reference = "create-reserve-1", + .details.reserve_status.expected_balance = "EUR:0" }, + /* Try to deposit the 5 EUR coin (in full) */ + { .oc = OC_DEPOSIT, + .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 }, + + /* Try to overdraw funds ... */ + { .oc = OC_WITHDRAW_SIGN, + .label = "withdraw-coin-2", + .expected_response_code = MHD_HTTP_PAYMENT_REQUIRED, + .details.reserve_withdraw.reserve_reference = "create-reserve-1", + .details.reserve_withdraw.amount = "EUR:5" }, + + /* Try to double-spend the 5 EUR coin with different wire details */ + { .oc = OC_DEPOSIT, + .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 }, + /* Try to double-spend the 5 EUR coin at the same merchant (but different + transaction ID) */ + { .oc = OC_DEPOSIT, + .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 }, + /* Try to double-spend the 5 EUR coin at the same merchant (but different + contract) */ + { .oc = OC_DEPOSIT, + .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 }, + + /* ***************** /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.reserve_withdraw.reserve_reference = "refresh-create-reserve-1", + .details.reserve_withdraw.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 }, + + + /* 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-1a", + .expected_response_code = MHD_HTTP_OK, + .details.deposit.amount = "EUR:1", + .details.deposit.coin_ref = "refresh-reveal-1", + .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-1", + .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 }, + + // 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 bank knows about the deposit, but has no WTID yet. */ + { .oc = OC_DEPOSIT_WTID, + .label = "deposit-wtid-found", + .expected_response_code = MHD_HTTP_ACCEPTED, + .details.deposit_wtid.deposit_ref = "deposit-simple" }, + /* Try resolving a deposit's WTID for a failed deposit. + As the deposit failed, the answer should be that + the bank does NOT know about the deposit. */ + { .oc = OC_DEPOSIT_WTID, + .label = "deposit-wtid-failing", + .expected_response_code = MHD_HTTP_NOT_FOUND, + .details.deposit_wtid.deposit_ref = "deposit-double-2" }, + /* Try resolving an undefined (all zeros) WTID; this + should fail as obviously the bank didn't use that + WTID value for any transaction. */ + { .oc = OC_WIRE_DEPOSITS, + .label = "wire-deposit-failing", + .expected_response_code = MHD_HTTP_NOT_FOUND }, + + /* TODO: trigger aggregation logic and then check the + cases where tracking succeeds! */ + + /* ************** End of tracking API testing************* */ + + +#endif + + { .oc = OC_END } + }; + + is = GNUNET_new (struct InterpreterState); + is->commands = commands; + + ctx = TALER_BANK_init (); + GNUNET_assert (NULL != ctx); + ctx_task = GNUNET_SCHEDULER_add_now (&context_task, + ctx); + bank = TALER_BANK_connect (ctx, + "http://localhost:8081", + &cert_cb, is, + TALER_BANK_OPTION_END); + GNUNET_assert (NULL != bank); + shutdown_task + = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 150), + &do_shutdown, is); +} + + +/** + * Main function for the testcase for the bank API. + * + * @param argc expected to be 1 + * @param argv expected to only contain the program name + */ +int +main (int argc, + char * const *argv) +{ + struct GNUNET_OS_Process *proc; + struct GNUNET_OS_Process *bankd; + + GNUNET_log_setup ("test-bank-api", + "WARNING", + NULL); + proc = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-bank-keyup", + "taler-bank-keyup", + "-d", "test-bank-home", + "-m", "test-bank-home/master.priv", + NULL); + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + bankd = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-bank-httpd", + "taler-bank-httpd", + "-d", "test-bank-home", + NULL); + /* give child time to start and bind against the socket */ + fprintf (stderr, "Waiting for taler-bank-httpd to be ready"); + do + { + 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")); + fprintf (stderr, "\n"); + result = GNUNET_SYSERR; + GNUNET_SCHEDULER_run (&run, NULL); + GNUNET_OS_process_kill (bankd, + SIGTERM); + GNUNET_OS_process_wait (bankd); + GNUNET_OS_process_destroy (bankd); + return (GNUNET_OK == result) ? 0 : 1; +} + +/* end of test_bank_api.c */ diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h new file mode 100644 index 00000000..b19e213a --- /dev/null +++ b/src/include/taler_bank_service.h @@ -0,0 +1,160 @@ +/* + This file is part of TALER + Copyright (C) 2015, 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file include/taler_bank_service.h + * @brief C interface of libtalerbank, a C library to use the Taler bank's HTTP API + * @author Christian Grothoff + */ +#ifndef _TALER_BANK_SERVICE_H +#define _TALER_BANK_SERVICE_H + +#include "taler_util.h" + +/* ********************* event loop *********************** */ + +/** + * @brief Handle to this library context. This is where the + * main event loop logic lives. + */ +struct TALER_BANK_Context; + + +/** + * Initialise a context. A context should be used for each thread and should + * not be shared among multiple threads. + * + * @param url HTTP base URL for the bank + * @return the context, NULL on error (failure to initialize) + */ +struct TALER_BANK_Context * +TALER_BANK_init (const char *url); + + +/** + * Obtain the information for a select() call to wait until + * #TALER_BANK_perform() is ready again. Note that calling + * any other TALER_BANK-API may also imply that the library + * is again ready for #TALER_BANK_perform(). + * + * Basically, a client should use this API to prepare for select(), + * then block on select(), then call #TALER_BANK_perform() and then + * start again until the work with the context is done. + * + * This function will NOT zero out the sets and assumes that @a max_fd + * and @a timeout are already set to minimal applicable values. It is + * safe to give this API FD-sets and @a max_fd and @a timeout that are + * already initialized to some other descriptors that need to go into + * the select() call. + * + * @param ctx context to get the event loop information for + * @param read_fd_set will be set for any pending read operations + * @param write_fd_set will be set for any pending write operations + * @param except_fd_set is here because curl_multi_fdset() has this argument + * @param max_fd set to the highest FD included in any set; + * if the existing sets have no FDs in it, the initial + * value should be "-1". (Note that `max_fd + 1` will need + * to be passed to select().) + * @param timeout set to the timeout in milliseconds (!); -1 means + * no timeout (NULL, blocking forever is OK), 0 means to + * proceed immediately with #TALER_BANK_perform(). + */ +void +TALER_BANK_get_select_info (struct TALER_BANK_Context *ctx, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *except_fd_set, + int *max_fd, + long *timeout); + + +/** + * Run the main event loop for the Taler interaction. + * + * @param ctx the library context + */ +void +TALER_BANK_perform (struct TALER_BANK_Context *ctx); + + +/** + * Cleanup library initialisation resources. This function should be called + * after using this library to cleanup the resources occupied during library's + * initialisation. + * + * @param ctx the library context + */ +void +TALER_BANK_fini (struct TALER_BANK_Context *ctx); + + +/* ********************* /admin/add/incoming *********************** */ + + +/** + * @brief A /admin/add/incoming Handle + */ +struct TALER_BANK_AdminAddIncomingHandle; + + +/** + * Callbacks of this type are used to serve the result of submitting + * information about an incoming transaction to a bank. + * + * @param cls closure + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request + * 0 if the bank's reply is bogus (fails to follow the protocol) + */ +typedef void +(*TALER_BANK_AdminAddIncomingResultCallback) (void *cls, + unsigned int http_status); + + +/** + * Notify the bank that we have received an incoming transaction + * which fills a reserve. Note that this API is an administrative + * API and thus not accessible to typical bank clients, but only + * to the operators of the bank. + * + * @param bank the bank handle; the bank must be ready to operate + * @param reserve_pub public key of the reserve + * @param amount amount that was deposited + * @param execution_date when did we receive the amount + * @param wire wire details + * @param res_cb the callback to call when the final result for this request is available + * @param res_cb_cls closure for the above callback + * @return NULL + * if the inputs are invalid (i.e. invalid amount). + * In this case, the callback is not called. + */ +struct TALER_BANK_AdminAddIncomingHandle * +TALER_BANK_admin_add_incoming (struct TALER_BANK_Context *bank, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *amount, + const json_t *wire, + TALER_BANK_AdminAddIncomingResultCallback res_cb, + void *res_cb_cls); + + +/** + * Cancel an add incoming. This function cannot be used on a request + * handle if a response is already served for it. + * + * @param aai the admin add incoming request handle + */ +void +TALER_BANK_admin_add_incoming_cancel (struct TALER_BANK_AdminAddIncomingHandle *aai); + +#endif /* _TALER_BANK_SERVICE_H */ -- cgit v1.2.3 From ccb4ac92a3e69d26e6c04b10b407f5491eda7001 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 11:08:19 +0100 Subject: more work on wire plugin --- configure.ac | 1 + src/Makefile.am | 2 +- src/include/taler_bank_service.h | 1 + src/wire/Makefile.am | 1 + src/wire/plugin_wire_test.c | 287 ++++++++++++++++++++++++++++++++++++--- 5 files changed, 273 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/configure.ac b/configure.ac index 8f06b520..95457e5b 100644 --- a/configure.ac +++ b/configure.ac @@ -357,6 +357,7 @@ AC_CONFIG_FILES([Makefile src/include/Makefile src/util/Makefile src/pq/Makefile + src/bank-lib/Makefile src/wire/Makefile src/mintdb/Makefile src/mint/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 986dcc5d..ac839d52 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,7 +7,7 @@ if WALLET_ONLY SUBDIRS = include util else -SUBDIRS = include util $(PQ_DIR) wire mintdb mint mint-tools +SUBDIRS = include util $(PQ_DIR) bank-lib wire mintdb mint mint-tools if HAVE_LIBCURL SUBDIRS += mint-lib else diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h index b19e213a..b292a7bc 100644 --- a/src/include/taler_bank_service.h +++ b/src/include/taler_bank_service.h @@ -16,6 +16,7 @@ /** * @file include/taler_bank_service.h * @brief C interface of libtalerbank, a C library to use the Taler bank's HTTP API + * This is currently ONLY used to provide the "test" wire transfer protocol. * @author Christian Grothoff */ #ifndef _TALER_BANK_SERVICE_H diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am index a27a59c7..64f50d97 100644 --- a/src/wire/Makefile.am +++ b/src/wire/Makefile.am @@ -21,6 +21,7 @@ libtaler_plugin_wire_test_la_LIBADD = \ $(LTLIBINTL) libtaler_plugin_wire_test_la_LDFLAGS = \ $(TALER_PLUGIN_LDFLAGS) \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetutil $(XLIB) diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c index 1ea856fd..917cf09b 100644 --- a/src/wire/plugin_wire_test.c +++ b/src/wire/plugin_wire_test.c @@ -17,13 +17,14 @@ /** * @file plugin_wire_test.c * @brief plugin for the "test" wire method - * @author Florian Dold * @author Christian Grothoff - * @author Sree Harsha Totakura */ #include "platform.h" #include "taler_wire_plugin.h" +#include "taler_bank_service.h" +/* only for HTTP status codes */ +#include /** * Type of the "cls" argument given to each of the functions in @@ -33,18 +34,164 @@ struct TestClosure { /** - * URI of the bank for sending funds to the bank. + * Handle to the bank for sending funds to the bank. */ - char *bank_uri; + struct TALER_BANK_Context *bank; /** * Which currency do we support? */ char *currency; + /** + * Handle to the bank task, or NULL. + */ + struct GNUNET_SCHEDULER_Task *bt; + +}; + + +/** + * Handle returned by #test_prepare_wire_transfer. + */ +struct TALER_WIRE_PrepareHandle +{ + + /** + * Task we use for async execution. + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Test closure we run in. + */ + struct TestClosure *tc; + + /** + * Wire data for the transfer. + */ + json_t *wire; + + /** + * Function to call with the serialized data. + */ + TALER_WIRE_PrepareTransactionCallback ptc; + + /** + * Closure for @e ptc. + */ + void *ptc_cls; + + /** + * Amount to transfer. + */ + struct TALER_Amount amount; + + /** + * Subject of the wire transfer. + */ + struct TALER_WireTransferIdentifierRawP wtid; + + }; +/** + * Handle returned by #test_execute_wire_transfer. + */ +struct TALER_WIRE_ExecuteHandle +{ + + /** + * Handle to the HTTP request to the bank. + */ + struct TALER_BANK_AdminAddIncomingHandle *aaih; + + /** + * Function to call with the result. + */ + TALER_WIRE_ConfirmationCallback cc; + + /** + * Closure for @e cc. + */ + void *cc_cls; +}; + + +/** + * Task that runs the bank's context's event loop with the GNUnet + * scheduler. + * + * @param cls our `struct TestClosure` + * @param tc scheduler context (unused) + */ +static void +context_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *sct) +{ + struct TestClosure *tc = cls; + long timeout; + int max_fd; + fd_set read_fd_set; + fd_set write_fd_set; + fd_set except_fd_set; + struct GNUNET_NETWORK_FDSet *rs; + struct GNUNET_NETWORK_FDSet *ws; + struct GNUNET_TIME_Relative delay; + + tc->bt = NULL; + TALER_BANK_perform (tc->bank); + max_fd = -1; + timeout = -1; + FD_ZERO (&read_fd_set); + FD_ZERO (&write_fd_set); + FD_ZERO (&except_fd_set); + TALER_BANK_get_select_info (tc->bank, + &read_fd_set, + &write_fd_set, + &except_fd_set, + &max_fd, + &timeout); + if (timeout >= 0) + delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, + timeout); + else + delay = GNUNET_TIME_UNIT_FOREVER_REL; + rs = GNUNET_NETWORK_fdset_create (); + GNUNET_NETWORK_fdset_copy_native (rs, + &read_fd_set, + max_fd + 1); + ws = GNUNET_NETWORK_fdset_create (); + GNUNET_NETWORK_fdset_copy_native (ws, + &write_fd_set, + max_fd + 1); + tc->bt = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, + delay, + rs, + ws, + &context_task, + cls); + GNUNET_NETWORK_fdset_destroy (rs); + GNUNET_NETWORK_fdset_destroy (ws); +} + + +/** + * Run the bank task now. + * + * @param tc context for which we should initiate running the task + */ +static void +run_bt (struct TestClosure *tc) +{ + if (NULL != tc->bt) + GNUNET_SCHEDULER_cancel (tc->bt); + tc->bt = GNUNET_SCHEDULER_add_now (&context_task, + tc); +} + + /** * Round amount DOWN to the amount that can be transferred via the wire * method. For example, Taler may support 0.000001 EUR as a unit of @@ -94,7 +241,33 @@ test_wire_validate (const json_t *wire) /** - * Prepare for exeuction of a wire transfer. + * Prepare for exeuction of a wire transfer. Calls the + * callback with the serialized state. + * + * @param cls the `struct TALER_WIRE_PrepareHandle` + * @param sct unused + */ +static void +do_prepare (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *sct) +{ + struct TALER_WIRE_PrepareHandle *pth = cls; + char buf[42]; // FIXME: init: serialize buf! + + pth->task = NULL; + pth->ptc (pth->ptc_cls, + buf, + sizeof (buf)); + GNUNET_free (pth); +} + + +/** + * Prepare for exeuction of a wire transfer. Note that we should call + * @a ptc asynchronously (as that is what the API requires, because + * some transfer methods need it). So while we could immediately call + * @a ptc, we first bundle up all the data and schedule a task to do + * the work. * * @param cls the @e cls of this struct with the plugin-specific state * @param wire valid wire account information @@ -112,8 +285,20 @@ test_prepare_wire_transfer (void *cls, TALER_WIRE_PrepareTransactionCallback ptc, void *ptc_cls) { - GNUNET_break (0); - return NULL; + struct TestClosure *tc = cls; + struct TALER_WIRE_PrepareHandle *pth; + + pth = GNUNET_new (struct TALER_WIRE_PrepareHandle); + pth->tc = tc; + pth->wire = (json_t *) wire; + json_incref (pth->wire); + pth->wtid = *wtid; + pth->ptc = ptc; + pth->ptc_cls = ptc_cls; + pth->amount = *amount; + pth->task = GNUNET_SCHEDULER_add_now (&do_prepare, + pth); + return pth; } @@ -128,7 +313,36 @@ static void test_prepare_wire_transfer_cancel (void *cls, struct TALER_WIRE_PrepareHandle *pth) { - GNUNET_break (0); + GNUNET_SCHEDULER_cancel (pth->task); + json_decref (pth->wire); + GNUNET_free (pth); +} + + +/** + * Called with the result of submitting information about an incoming + * transaction to a bank. + * + * @param cls closure with the `struct TALER_WIRE_ExecuteHandle` + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request + * 0 if the bank's reply is bogus (fails to follow the protocol) + */ +static void +execute_cb (void *cls, + unsigned int http_status) +{ + struct TALER_WIRE_ExecuteHandle *eh = cls; + char s[14]; + + eh->aaih = NULL; + GNUNET_snprintf (s, + sizeof (s), + "%u", + http_status); + eh->cc (eh->cc_cls, + (MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR, + (MHD_HTTP_OK == http_status) ? NULL : s); + GNUNET_free (eh); } @@ -149,8 +363,32 @@ test_execute_wire_transfer (void *cls, TALER_WIRE_ConfirmationCallback cc, void *cc_cls) { - GNUNET_break (0); - return NULL; + struct TestClosure *tc = cls; + struct TALER_WIRE_ExecuteHandle *eh; + json_t *wire; + struct TALER_Amount amount; + struct TALER_WireTransferIdentifierRawP wtid; + + /* FIXME: deserialize buf */ + wire = NULL; + + eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle); + eh->cc = cc; + eh->cc_cls = cc_cls; + eh->aaih = TALER_BANK_admin_add_incoming (tc->bank, + &wtid, + &amount, + wire, + &execute_cb, + eh); + if (NULL == eh->aaih) + { + GNUNET_break (0); + GNUNET_free (eh); + return NULL; + } + run_bt (tc); + return eh; } @@ -170,7 +408,8 @@ static void test_execute_wire_transfer_cancel (void *cls, struct TALER_WIRE_ExecuteHandle *eh) { - GNUNET_break (0); + TALER_BANK_admin_add_incoming_cancel (eh->aaih); + GNUNET_free (eh); } @@ -186,21 +425,20 @@ libtaler_plugin_wire_test_init (void *cls) struct GNUNET_CONFIGURATION_Handle *cfg = cls; struct TestClosure *tc; struct TALER_WIRE_Plugin *plugin; - - tc = GNUNET_new (struct TestClosure); + char *uri; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "wire-test", "bank_uri", - &tc->bank_uri)) - { + &uri)) + { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "wire-test", "bank_uri"); - GNUNET_free (tc); return NULL; } + tc = GNUNET_new (struct TestClosure); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "mint", @@ -210,7 +448,15 @@ libtaler_plugin_wire_test_init (void *cls) GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "mint", "CURRENCY"); - GNUNET_free (tc->bank_uri); + GNUNET_free (uri); + GNUNET_free (tc); + return NULL; + } + tc->bank = TALER_BANK_init (uri); + if (NULL == tc->bank) + { + GNUNET_break (0); + GNUNET_free (tc->currency); GNUNET_free (tc); return NULL; } @@ -239,7 +485,12 @@ libtaler_plugin_wire_test_done (void *cls) struct TALER_WIRE_Plugin *plugin = cls; struct TestClosure *tc = plugin->cls; - GNUNET_free (tc->bank_uri); + if (NULL != tc->bt) + { + GNUNET_SCHEDULER_cancel (tc->bt); + tc->bt = NULL; + } + TALER_BANK_fini (tc->bank); GNUNET_free (tc->currency); GNUNET_free (tc); GNUNET_free (plugin); -- cgit v1.2.3 From 9b4a9cde8717d053706a2df94c38e19e97eef8ee Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 11:09:55 +0100 Subject: test wire formats before using --- src/wire/plugin_wire_test.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c index 917cf09b..776b786e 100644 --- a/src/wire/plugin_wire_test.c +++ b/src/wire/plugin_wire_test.c @@ -288,6 +288,12 @@ test_prepare_wire_transfer (void *cls, struct TestClosure *tc = cls; struct TALER_WIRE_PrepareHandle *pth; + if (GNUNET_YES != + test_wire_validate (wire)) + { + GNUNET_break (0); + return NULL; + } pth = GNUNET_new (struct TALER_WIRE_PrepareHandle); pth->tc = tc; pth->wire = (json_t *) wire; @@ -372,6 +378,8 @@ test_execute_wire_transfer (void *cls, /* FIXME: deserialize buf */ wire = NULL; + GNUNET_assert (GNUNET_YES == + test_wire_validate (wire)); eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle); eh->cc = cc; eh->cc_cls = cc_cls; -- cgit v1.2.3 From 891b533a217a23dfd21c6135d62ca5bc91adc38d Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 11:21:55 +0100 Subject: finish serialization/deserialization logic for test wire transfers --- src/include/taler_wire_plugin.h | 4 +- src/wire/plugin_wire_test.c | 89 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h index 62930450..8fb194c5 100644 --- a/src/include/taler_wire_plugin.h +++ b/src/include/taler_wire_plugin.h @@ -30,8 +30,8 @@ * Callback with prepared transaction. * * @param cls closure - * @param buf transaction data to persist - * @param buf_size number of bytes in @a buf + * @param buf transaction data to persist, NULL on error + * @param buf_size number of bytes in @a buf, 0 on error */ typedef void (*TALER_WIRE_PrepareTransactionCallback) (void *cls, diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c index 776b786e..7940b477 100644 --- a/src/wire/plugin_wire_test.c +++ b/src/wire/plugin_wire_test.c @@ -240,6 +240,29 @@ test_wire_validate (const json_t *wire) } +GNUNET_NETWORK_STRUCT_BEGIN +/** + * Format we used for serialized transaction data. + */ +struct BufFormatP +{ + + /** + * The wire transfer identifier. + */ + struct TALER_WireTransferIdentifierRawP wtid; + + /** + * The amount. + */ + struct TALER_AmountNBO amount; + + /* followed by serialized 'wire' JSON data */ + +}; +GNUNET_NETWORK_STRUCT_END + + /** * Prepare for exeuction of a wire transfer. Calls the * callback with the serialized state. @@ -252,12 +275,44 @@ do_prepare (void *cls, const struct GNUNET_SCHEDULER_TaskContext *sct) { struct TALER_WIRE_PrepareHandle *pth = cls; - char buf[42]; // FIXME: init: serialize buf! + char *wire_enc; + size_t len; + struct BufFormatP bf; pth->task = NULL; - pth->ptc (pth->ptc_cls, - buf, - sizeof (buf)); + /* serialize the state into a 'buf' */ + wire_enc = json_dumps (pth->wire, + JSON_COMPACT | JSON_SORT_KEYS); + if (NULL == wire_enc) + { + GNUNET_break (0); + pth->ptc (pth->ptc_cls, + NULL, + 0); + GNUNET_free (pth); + return; + } + len = strlen (wire_enc) + 1; + bf.wtid = pth->wtid; + TALER_amount_hton (&bf.amount, + &pth->amount); + { + char buf[sizeof (struct BufFormatP) + len]; + + memcpy (buf, + &bf, + sizeof (struct BufFormatP)); + memcpy (&buf[sizeof (struct BufFormatP)], + wire_enc, + len); + + /* finally give the state back */ + pth->ptc (pth->ptc_cls, + buf, + sizeof (buf)); + } + free (wire_enc); /* not using GNUNET_free(), + as this one is allocated by libjansson */ GNUNET_free (pth); } @@ -373,10 +428,27 @@ test_execute_wire_transfer (void *cls, struct TALER_WIRE_ExecuteHandle *eh; json_t *wire; struct TALER_Amount amount; - struct TALER_WireTransferIdentifierRawP wtid; + struct BufFormatP bf; - /* FIXME: deserialize buf */ - wire = NULL; + if ( (buf_size <= sizeof (struct BufFormatP)) || + ('\0' != buf[buf_size -1]) ) + { + GNUNET_break (0); + return NULL; + } + memcpy (&bf, + buf, + sizeof (bf)); + TALER_amount_ntoh (&amount, + &bf.amount); + wire = json_loads (&buf[sizeof (struct BufFormatP)], + JSON_REJECT_DUPLICATES, + NULL); + if (NULL == wire) + { + GNUNET_break (0); + return NULL; + } GNUNET_assert (GNUNET_YES == test_wire_validate (wire)); @@ -384,11 +456,12 @@ test_execute_wire_transfer (void *cls, eh->cc = cc; eh->cc_cls = cc_cls; eh->aaih = TALER_BANK_admin_add_incoming (tc->bank, - &wtid, + &bf.wtid, &amount, wire, &execute_cb, eh); + json_decref (wire); if (NULL == eh->aaih) { GNUNET_break (0); -- cgit v1.2.3 From 4235dbfde0f2d26bf8c78c596bc6b3ab8c093796 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 11:31:06 +0100 Subject: adding (incomplete) plugin for SEPA --- src/wire/Makefile.am | 10 + src/wire/plugin_wire_sepa.c | 537 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 547 insertions(+) create mode 100644 src/wire/plugin_wire_sepa.c (limited to 'src') diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am index 64f50d97..a8bc4af6 100644 --- a/src/wire/Makefile.am +++ b/src/wire/Makefile.am @@ -9,6 +9,7 @@ endif plugindir = $(libdir)/taler plugin_LTLIBRARIES = \ + libtaler_plugin_wire_sepa.la \ libtaler_plugin_wire_test.la noinst_LTLIBRARIES = \ @@ -26,6 +27,15 @@ libtaler_plugin_wire_test_la_LDFLAGS = \ -lgnunetutil $(XLIB) +libtaler_plugin_wire_sepa_la_SOURCES = \ + plugin_wire_sepa.c +libtaler_plugin_wire_sepa_la_LIBADD = \ + $(LTLIBINTL) +libtaler_plugin_wire_sepa_la_LDFLAGS = \ + $(TALER_PLUGIN_LDFLAGS) \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetutil $(XLIB) + libtaler_plugin_wire_template_la_SOURCES = \ plugin_wire_template.c diff --git a/src/wire/plugin_wire_sepa.c b/src/wire/plugin_wire_sepa.c new file mode 100644 index 00000000..1867c048 --- /dev/null +++ b/src/wire/plugin_wire_sepa.c @@ -0,0 +1,537 @@ +/* + This file is part of TALER + Copyright (C) 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file plugin_wire_sepa.c + * @brief wire plugin for transfers using SEPA/EBICS + * @author Florian Dold + * @author Christian Grothoff + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include "taler_wire_plugin.h" + + +/** + * Type of the "cls" argument given to each of the functions in + * our API. + */ +struct SepaClosure +{ + + /** + * Which currency do we support? + */ + char *currency; + +}; + + +/** + * Round amount DOWN to the amount that can be transferred via the wire + * method. For example, Taler may support 0.000001 EUR as a unit of + * payment, but SEPA only supports 0.01 EUR. This function would + * round 0.125 EUR to 0.12 EUR in this case. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param[in,out] amount amount to round down + * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary, + * #GNUNET_SYSERR if the amount or currency was invalid + */ +static int +sepa_amount_round (void *cls, + struct TALER_Amount *amount) +{ + struct SepaClosure *sc = cls; + uint32_t delta; + + if (0 != strcasecmp (amount->currency, + sc->currency)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100); + if (0 == delta) + return GNUNET_NO; + amount->fraction -= delta; + return GNUNET_SYSERR; +} + + +/* Taken from GNU gettext */ + +/** + * Entry in the country table. + */ +struct table_entry +{ + /** + * 2-Character international country code. + */ + const char *code; + + /** + * Long English name of the country. + */ + const char *english; +}; + + +/* Keep the following table in sync with gettext. + WARNING: the entries should stay sorted according to the code */ +/** + * List of country codes. + */ +static const struct table_entry country_table[] = + { + { "AE", "U.A.E." }, + { "AF", "Afghanistan" }, + { "AL", "Albania" }, + { "AM", "Armenia" }, + { "AN", "Netherlands Antilles" }, + { "AR", "Argentina" }, + { "AT", "Austria" }, + { "AU", "Australia" }, + { "AZ", "Azerbaijan" }, + { "BA", "Bosnia and Herzegovina" }, + { "BD", "Bangladesh" }, + { "BE", "Belgium" }, + { "BG", "Bulgaria" }, + { "BH", "Bahrain" }, + { "BN", "Brunei Darussalam" }, + { "BO", "Bolivia" }, + { "BR", "Brazil" }, + { "BT", "Bhutan" }, + { "BY", "Belarus" }, + { "BZ", "Belize" }, + { "CA", "Canada" }, + { "CG", "Congo" }, + { "CH", "Switzerland" }, + { "CI", "Cote d'Ivoire" }, + { "CL", "Chile" }, + { "CM", "Cameroon" }, + { "CN", "People's Republic of China" }, + { "CO", "Colombia" }, + { "CR", "Costa Rica" }, + { "CS", "Serbia and Montenegro" }, + { "CZ", "Czech Republic" }, + { "DE", "Germany" }, + { "DK", "Denmark" }, + { "DO", "Dominican Republic" }, + { "DZ", "Algeria" }, + { "EC", "Ecuador" }, + { "EE", "Estonia" }, + { "EG", "Egypt" }, + { "ER", "Eritrea" }, + { "ES", "Spain" }, + { "ET", "Ethiopia" }, + { "FI", "Finland" }, + { "FO", "Faroe Islands" }, + { "FR", "France" }, + { "GB", "United Kingdom" }, + { "GD", "Caribbean" }, + { "GE", "Georgia" }, + { "GL", "Greenland" }, + { "GR", "Greece" }, + { "GT", "Guatemala" }, + { "HK", "Hong Kong" }, + { "HK", "Hong Kong S.A.R." }, + { "HN", "Honduras" }, + { "HR", "Croatia" }, + { "HT", "Haiti" }, + { "HU", "Hungary" }, + { "ID", "Indonesia" }, + { "IE", "Ireland" }, + { "IL", "Israel" }, + { "IN", "India" }, + { "IQ", "Iraq" }, + { "IR", "Iran" }, + { "IS", "Iceland" }, + { "IT", "Italy" }, + { "JM", "Jamaica" }, + { "JO", "Jordan" }, + { "JP", "Japan" }, + { "KE", "Kenya" }, + { "KG", "Kyrgyzstan" }, + { "KH", "Cambodia" }, + { "KR", "South Korea" }, + { "KW", "Kuwait" }, + { "KZ", "Kazakhstan" }, + { "LA", "Laos" }, + { "LB", "Lebanon" }, + { "LI", "Liechtenstein" }, + { "LK", "Sri Lanka" }, + { "LT", "Lithuania" }, + { "LU", "Luxembourg" }, + { "LV", "Latvia" }, + { "LY", "Libya" }, + { "MA", "Morocco" }, + { "MC", "Principality of Monaco" }, + { "MD", "Moldava" }, + { "MD", "Moldova" }, + { "ME", "Montenegro" }, + { "MK", "Former Yugoslav Republic of Macedonia" }, + { "ML", "Mali" }, + { "MM", "Myanmar" }, + { "MN", "Mongolia" }, + { "MO", "Macau S.A.R." }, + { "MT", "Malta" }, + { "MV", "Maldives" }, + { "MX", "Mexico" }, + { "MY", "Malaysia" }, + { "NG", "Nigeria" }, + { "NI", "Nicaragua" }, + { "NL", "Netherlands" }, + { "NO", "Norway" }, + { "NP", "Nepal" }, + { "NZ", "New Zealand" }, + { "OM", "Oman" }, + { "PA", "Panama" }, + { "PE", "Peru" }, + { "PH", "Philippines" }, + { "PK", "Islamic Republic of Pakistan" }, + { "PL", "Poland" }, + { "PR", "Puerto Rico" }, + { "PT", "Portugal" }, + { "PY", "Paraguay" }, + { "QA", "Qatar" }, + { "RE", "Reunion" }, + { "RO", "Romania" }, + { "RS", "Serbia" }, + { "RU", "Russia" }, + { "RW", "Rwanda" }, + { "SA", "Saudi Arabia" }, + { "SE", "Sweden" }, + { "SG", "Singapore" }, + { "SI", "Slovenia" }, + { "SK", "Slovak" }, + { "SN", "Senegal" }, + { "SO", "Somalia" }, + { "SR", "Suriname" }, + { "SV", "El Salvador" }, + { "SY", "Syria" }, + { "TH", "Thailand" }, + { "TJ", "Tajikistan" }, + { "TM", "Turkmenistan" }, + { "TN", "Tunisia" }, + { "TR", "Turkey" }, + { "TT", "Trinidad and Tobago" }, + { "TW", "Taiwan" }, + { "TZ", "Tanzania" }, + { "UA", "Ukraine" }, + { "US", "United States" }, + { "UY", "Uruguay" }, + { "VA", "Vatican" }, + { "VE", "Venezuela" }, + { "VN", "Viet Nam" }, + { "YE", "Yemen" }, + { "ZA", "South Africa" }, + { "ZW", "Zimbabwe" } + }; + + +/** + * Country code comparator function, for binary search with bsearch(). + * + * @param ptr1 pointer to a `struct table_entry` + * @param ptr2 pointer to a `struct table_entry` + * @return result of strncmp()'ing the 2-digit country codes of the entries + */ +static int +cmp_country_code (const void *ptr1, + const void *ptr2) +{ + const struct table_entry *cc1 = ptr1; + const struct table_entry *cc2 = ptr2; + + return strncmp (cc1->code, + cc2->code, + 2); +} + + +/** + * Validates given IBAN according to the European Banking Standards. See: + * http://www.europeanpaymentscouncil.eu/documents/ECBS%20IBAN%20standard%20EBS204_V3.2.pdf + * + * @param iban the IBAN number to validate + * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not + */ +static int +validate_iban (const char *iban) +{ + char cc[2]; + char ibancpy[35]; + struct table_entry cc_entry; + unsigned int len; + char *nbuf; + unsigned int i; + unsigned int j; + unsigned long long dividend; + unsigned long long remainder; + int nread; + int ret; + + len = strlen (iban); + if (len > 34) + return GNUNET_NO; + strncpy (cc, iban, 2); + strncpy (ibancpy, iban + 4, len - 4); + strncpy (ibancpy + len - 4, iban, 4); + ibancpy[len] = '\0'; + cc_entry.code = cc; + cc_entry.english = NULL; + if (NULL == + bsearch (&cc_entry, + country_table, + sizeof (country_table) / sizeof (struct table_entry), + sizeof (struct table_entry), + &cmp_country_code)) + return GNUNET_NO; + nbuf = GNUNET_malloc ((len * 2) + 1); + for (i=0, j=0; i < len; i++) + { + if (isalpha ((int) ibancpy[i])) + { + if (2 != snprintf(&nbuf[j], + 3, + "%2u", + (ibancpy[i] - 'A' + 10))) + { + GNUNET_free (nbuf); + return GNUNET_NO; + } + j += 2; + continue; + } + nbuf[j] = ibancpy[i]; + j++; + } + for (j=0;'\0' != nbuf[j];j++) + GNUNET_assert (isdigit(nbuf[j])); + GNUNET_assert (sizeof(dividend) >= 8); + remainder = 0; + for (i=0; icurrency)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint", + "CURRENCY"); + GNUNET_free (sc); + return NULL; + } + + plugin = GNUNET_new (struct TALER_WIRE_Plugin); + plugin->cls = sc; + plugin->amount_round = &sepa_amount_round; + plugin->wire_validate = &sepa_wire_validate; + plugin->prepare_wire_transfer = &sepa_prepare_wire_transfer; + plugin->prepare_wire_transfer_cancel = &sepa_prepare_wire_transfer_cancel; + plugin->execute_wire_transfer = &sepa_execute_wire_transfer; + plugin->execute_wire_transfer_cancel = &sepa_execute_wire_transfer_cancel; + return plugin; +} + + +/** + * Shutdown Sepa wire subsystem. + * + * @param cls a `struct TALER_WIRE_Plugin` + * @return NULL (always) + */ +void * +libtaler_plugin_wire_sepa_done (void *cls) +{ + struct TALER_WIRE_Plugin *plugin = cls; + struct SepaClosure *sc = plugin->cls; + + GNUNET_free (sc->currency); + GNUNET_free (sc); + GNUNET_free (plugin); + return NULL; +} + +/* end of plugin_wire_sepa.c */ -- cgit v1.2.3 From e5c5dc9cae56bdea02f7661c1c8a8cacfbe99f1c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 12:50:29 +0100 Subject: move plugin path setup logic to libtalerutil --- src/mintdb/mintdb_plugin.c | 62 -------------------------- src/util/plugin.c | 85 ++++++++++++++++++++++++++++++++++++ src/util/test_wireformats.c | 97 ----------------------------------------- src/wire/test_sepa_wireformat.c | 97 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 159 deletions(-) create mode 100644 src/util/plugin.c delete mode 100644 src/util/test_wireformats.c create mode 100644 src/wire/test_sepa_wireformat.c (limited to 'src') diff --git a/src/mintdb/mintdb_plugin.c b/src/mintdb/mintdb_plugin.c index f1b2ad04..4a0f1dc0 100644 --- a/src/mintdb/mintdb_plugin.c +++ b/src/mintdb/mintdb_plugin.c @@ -83,67 +83,5 @@ TALER_MINTDB_plugin_unload (struct TALER_MINTDB_Plugin *plugin) } -/** - * Libtool search path before we started. - */ -static char *old_dlsearchpath; - - -/** - * Setup libtool paths. - */ -void __attribute__ ((constructor)) -plugin_init () -{ - int err; - const char *opath; - char *path; - char *cpath; - - err = lt_dlinit (); - if (err > 0) - { - FPRINTF (stderr, - _("Initialization of plugin mechanism failed: %s!\n"), - lt_dlerror ()); - return; - } - opath = lt_dlgetsearchpath (); - if (NULL != opath) - old_dlsearchpath = GNUNET_strdup (opath); - path = TALER_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR); - if (NULL != path) - { - if (NULL != opath) - { - GNUNET_asprintf (&cpath, "%s:%s", opath, path); - lt_dlsetsearchpath (cpath); - GNUNET_free (path); - GNUNET_free (cpath); - } - else - { - lt_dlsetsearchpath (path); - GNUNET_free (path); - } - } -} - - -/** - * Shutdown libtool. - */ -void __attribute__ ((destructor)) -plugin_fini () -{ - lt_dlsetsearchpath (old_dlsearchpath); - if (NULL != old_dlsearchpath) - { - GNUNET_free (old_dlsearchpath); - old_dlsearchpath = NULL; - } - lt_dlexit (); -} - /* end of mintdb_plugin.c */ diff --git a/src/util/plugin.c b/src/util/plugin.c new file mode 100644 index 00000000..d76dfa78 --- /dev/null +++ b/src/util/plugin.c @@ -0,0 +1,85 @@ +/* + This file is part of TALER + Copyright (C) 2015 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file util/plugin.c + * @brief Setup paths so that we can load Taler plugins + * @author Christian Grothoff + * @author Sree Harsha Totakura + */ + +/** + * Libtool search path before we started. + */ +static char *old_dlsearchpath; + + +/** + * Setup libtool paths. + */ +void __attribute__ ((constructor)) +plugin_init () +{ + int err; + const char *opath; + char *path; + char *cpath; + + err = lt_dlinit (); + if (err > 0) + { + FPRINTF (stderr, + _("Initialization of plugin mechanism failed: %s!\n"), + lt_dlerror ()); + return; + } + opath = lt_dlgetsearchpath (); + if (NULL != opath) + old_dlsearchpath = GNUNET_strdup (opath); + path = TALER_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR); + if (NULL != path) + { + if (NULL != opath) + { + GNUNET_asprintf (&cpath, "%s:%s", opath, path); + lt_dlsetsearchpath (cpath); + GNUNET_free (path); + GNUNET_free (cpath); + } + else + { + lt_dlsetsearchpath (path); + GNUNET_free (path); + } + } +} + + +/** + * Shutdown libtool. + */ +void __attribute__ ((destructor)) +plugin_fini () +{ + lt_dlsetsearchpath (old_dlsearchpath); + if (NULL != old_dlsearchpath) + { + GNUNET_free (old_dlsearchpath); + old_dlsearchpath = NULL; + } + lt_dlexit (); +} + +/* end of plugin.c */ diff --git a/src/util/test_wireformats.c b/src/util/test_wireformats.c deleted file mode 100644 index b41abb80..00000000 --- a/src/util/test_wireformats.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - This file is part of TALER - (C) 2014 GNUnet e.V. - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ - -/** - * @file util/test_wireformats.c - * @brief Tests for JSON validations - * @author Sree Harsha Totakura - */ - -#include "platform.h" -#include "taler_util.h" -#include "taler_json_lib.h" - -/* Valid SEPA data */ -static const char * const valid_wire_str = - "{ \"type\":\"SEPA\", \ -\"IBAN\":\"DE67830654080004822650\", \ -\"name\":\"GNUnet e.V.\", \ -\"bic\":\"GENODEF1SLR\", \ -\"r\":123456789, \ -\"address\": \"foobar\"}"; - -/* IBAN has wrong country code */ -static const char * const invalid_wire_str = - "{ \"type\":\"SEPA\", \ -\"IBAN\":\"XX67830654080004822650\", \ -\"name\":\"GNUnet e.V.\", \ -\"bic\":\"GENODEF1SLR\", \ -\"r\":123456789, \ -\"address\": \"foobar\"}"; - -/* IBAN has wrong checksum */ -static const char * const invalid_wire_str2 = - "{ \"type\":\"SEPA\", \ -\"IBAN\":\"DE67830654080004822651\", \ -\"name\":\"GNUnet e.V.\", \ -\"bic\":\"GENODEF1SLR\", \ -\"r\":123456789, \ -\"address\": \"foobar\"}"; - -/* Unsupported wireformat type */ -static const char * const unsupported_wire_str = - "{ \"type\":\"unsupported\", \ -\"IBAN\":\"DE67830654080004822650\", \ -\"name\":\"GNUnet e.V.\", \ -\"bic\":\"GENODEF1SLR\", \ -\"r\":123456789, \ -\"address\": \"foobar\"}"; - - -int -main(int argc, - const char *const argv[]) -{ - const char *unsupported[] = { - "unsupported", - NULL - }; - const char *sepa[] = { - "SEPA", - NULL - }; - json_t *wire; - json_error_t error; - int ret; - - GNUNET_log_setup ("test-json-validations", "WARNING", NULL); - (void) memset(&error, 0, sizeof(error)); - GNUNET_assert (NULL != (wire = json_loads (unsupported_wire_str, 0, NULL))); - GNUNET_assert (1 != TALER_json_validate_wireformat (unsupported, wire)); - json_decref (wire); - GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str, 0, NULL))); - GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire)); - json_decref (wire); - GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str2, 0, NULL))); - GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire)); - json_decref (wire); - GNUNET_assert (NULL != (wire = json_loads (valid_wire_str, 0, &error))); - ret = TALER_json_validate_wireformat (sepa, wire); - json_decref (wire); - if (1 == ret) - return 0; - return 1; -} diff --git a/src/wire/test_sepa_wireformat.c b/src/wire/test_sepa_wireformat.c new file mode 100644 index 00000000..b41abb80 --- /dev/null +++ b/src/wire/test_sepa_wireformat.c @@ -0,0 +1,97 @@ +/* + This file is part of TALER + (C) 2014 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file util/test_wireformats.c + * @brief Tests for JSON validations + * @author Sree Harsha Totakura + */ + +#include "platform.h" +#include "taler_util.h" +#include "taler_json_lib.h" + +/* Valid SEPA data */ +static const char * const valid_wire_str = + "{ \"type\":\"SEPA\", \ +\"IBAN\":\"DE67830654080004822650\", \ +\"name\":\"GNUnet e.V.\", \ +\"bic\":\"GENODEF1SLR\", \ +\"r\":123456789, \ +\"address\": \"foobar\"}"; + +/* IBAN has wrong country code */ +static const char * const invalid_wire_str = + "{ \"type\":\"SEPA\", \ +\"IBAN\":\"XX67830654080004822650\", \ +\"name\":\"GNUnet e.V.\", \ +\"bic\":\"GENODEF1SLR\", \ +\"r\":123456789, \ +\"address\": \"foobar\"}"; + +/* IBAN has wrong checksum */ +static const char * const invalid_wire_str2 = + "{ \"type\":\"SEPA\", \ +\"IBAN\":\"DE67830654080004822651\", \ +\"name\":\"GNUnet e.V.\", \ +\"bic\":\"GENODEF1SLR\", \ +\"r\":123456789, \ +\"address\": \"foobar\"}"; + +/* Unsupported wireformat type */ +static const char * const unsupported_wire_str = + "{ \"type\":\"unsupported\", \ +\"IBAN\":\"DE67830654080004822650\", \ +\"name\":\"GNUnet e.V.\", \ +\"bic\":\"GENODEF1SLR\", \ +\"r\":123456789, \ +\"address\": \"foobar\"}"; + + +int +main(int argc, + const char *const argv[]) +{ + const char *unsupported[] = { + "unsupported", + NULL + }; + const char *sepa[] = { + "SEPA", + NULL + }; + json_t *wire; + json_error_t error; + int ret; + + GNUNET_log_setup ("test-json-validations", "WARNING", NULL); + (void) memset(&error, 0, sizeof(error)); + GNUNET_assert (NULL != (wire = json_loads (unsupported_wire_str, 0, NULL))); + GNUNET_assert (1 != TALER_json_validate_wireformat (unsupported, wire)); + json_decref (wire); + GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str, 0, NULL))); + GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire)); + json_decref (wire); + GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str2, 0, NULL))); + GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire)); + json_decref (wire); + GNUNET_assert (NULL != (wire = json_loads (valid_wire_str, 0, &error))); + ret = TALER_json_validate_wireformat (sepa, wire); + json_decref (wire); + if (1 == ret) + return 0; + return 1; +} -- cgit v1.2.3 From fc5791353087812db6df374d1e453a387c57550c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 12:51:04 +0100 Subject: move wireformat test to plugin --- src/util/Makefile.am | 14 ++----- src/util/plugin.c | 3 ++ src/wire/Makefile.am | 17 ++++++++ src/wire/test_sepa_wireformat.c | 92 ++++++++++++++++++++++++++++++++--------- 4 files changed, 95 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/util/Makefile.am b/src/util/Makefile.am index eaf3a482..8efc3987 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -33,6 +33,7 @@ libtalerutil_la_SOURCES = \ util.c \ json.c \ os_installation.c \ + plugin.c \ wireformats.c libtalerutil_la_LIBADD = \ @@ -48,14 +49,12 @@ libtalerutil_la_LDFLAGS = \ TESTS = \ test_amount \ test_crypto \ - test_json \ - test_wireformats + test_json check_PROGRAMS= \ test_amount \ test_crypto \ - test_json \ - test_wireformats + test_json test_amount_SOURCES = \ @@ -76,10 +75,3 @@ test_json_LDADD = \ -lgnunetutil \ -ljansson \ libtalerutil.la - -test_wireformats_SOURCES = \ - test_wireformats.c -test_wireformats_LDADD = \ - -lgnunetutil \ - -ljansson \ - libtalerutil.la diff --git a/src/util/plugin.c b/src/util/plugin.c index d76dfa78..6f8e03df 100644 --- a/src/util/plugin.c +++ b/src/util/plugin.c @@ -19,6 +19,9 @@ * @author Christian Grothoff * @author Sree Harsha Totakura */ +#include "platform.h" +#include "taler_util.h" +#include /** * Libtool search path before we started. diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am index a8bc4af6..528d9101 100644 --- a/src/wire/Makefile.am +++ b/src/wire/Makefile.am @@ -45,3 +45,20 @@ libtaler_plugin_wire_template_la_LDFLAGS = \ $(TALER_PLUGIN_LDFLAGS) \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetutil $(XLIB) + + + +TESTS = \ + test_sepa_wireformat + +check_PROGRAMS= \ + test_sepa_wireformat + + + +test_sepa_wireformat_SOURCES = \ + test_sepa_wireformat.c +test_sepa_wireformat_LDADD = \ + -lgnunetutil \ + -ljansson \ + $(top_builddir)/src/util/libtalerutil.la diff --git a/src/wire/test_sepa_wireformat.c b/src/wire/test_sepa_wireformat.c index b41abb80..d00228dd 100644 --- a/src/wire/test_sepa_wireformat.c +++ b/src/wire/test_sepa_wireformat.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2014 GNUnet e.V. + (C) 2015, 2016 GNUnet e.V. 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 @@ -15,14 +15,15 @@ */ /** - * @file util/test_wireformats.c - * @brief Tests for JSON validations + * @file wire/test_sepa_wireformat.c + * @brief Tests for JSON SEPA format validation * @author Sree Harsha Totakura */ #include "platform.h" #include "taler_util.h" -#include "taler_json_lib.h" +#include "taler_wire_plugin.h" + /* Valid SEPA data */ static const char * const valid_wire_str = @@ -61,37 +62,88 @@ static const char * const unsupported_wire_str = \"address\": \"foobar\"}"; +/** + * Initialize the plugin. + * + * @param cfg configuration to use + * @return #GNUNET_OK on success + */ +static struct TALER_WIRE_Plugin * +wire_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *plugin_name) +{ + char *lib_name; + struct TALER_WIRE_Plugin *plugin; + + (void) GNUNET_asprintf (&lib_name, + "libtaler_plugin_wire_%s", + plugin_name); + plugin = GNUNET_PLUGIN_load (lib_name, + (void *) cfg); + if (NULL != plugin) + plugin->library_name = lib_name; + else + GNUNET_free (lib_name); + return plugin; +} + + +/** + * Shutdown the plugin. + * + * @param plugin the plugin to unload + */ +static void +wire_plugin_unload (struct TALER_WIRE_Plugin *plugin) +{ + char *lib_name; + + if (NULL == plugin) + return; + lib_name = plugin->library_name; + GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name, + plugin)); + GNUNET_free (lib_name); +} + + int main(int argc, const char *const argv[]) { - const char *unsupported[] = { - "unsupported", - NULL - }; - const char *sepa[] = { - "SEPA", - NULL - }; json_t *wire; json_error_t error; int ret; + struct GNUNET_CONFIGURATION_Handle *cfg; + struct TALER_WIRE_Plugin *plugin; - GNUNET_log_setup ("test-json-validations", "WARNING", NULL); + GNUNET_log_setup ("test-sepa-wireformats", + "WARNING", + NULL); + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_set_value_string (cfg, + "mint", + "currency", + "EUR"); + plugin = wire_plugin_load (cfg, + "sepa"); + GNUNET_assert (NULL != plugin); (void) memset(&error, 0, sizeof(error)); GNUNET_assert (NULL != (wire = json_loads (unsupported_wire_str, 0, NULL))); - GNUNET_assert (1 != TALER_json_validate_wireformat (unsupported, wire)); + GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire)); json_decref (wire); GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str, 0, NULL))); - GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire)); + GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire)); json_decref (wire); GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str2, 0, NULL))); - GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire)); + GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire)); json_decref (wire); GNUNET_assert (NULL != (wire = json_loads (valid_wire_str, 0, &error))); - ret = TALER_json_validate_wireformat (sepa, wire); + ret = plugin->wire_validate (wire); json_decref (wire); - if (1 == ret) - return 0; - return 1; + wire_plugin_unload (plugin); + GNUNET_CONFIGURATION_destroy (cfg); + if (GNUNET_NO == ret) + return 1; + return 0; } -- cgit v1.2.3 From 936acfa13116a7cb81e656db3acc31c5f9ab2fd5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 12:53:07 +0100 Subject: check type in plugin --- src/wire/plugin_wire_sepa.c | 8 ++++++++ src/wire/test_sepa_wireformat.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/wire/plugin_wire_sepa.c b/src/wire/plugin_wire_sepa.c index 1867c048..00d19d4b 100644 --- a/src/wire/plugin_wire_sepa.c +++ b/src/wire/plugin_wire_sepa.c @@ -385,6 +385,14 @@ sepa_wire_validate (const json_t *wire) TALER_json_warn (error); return GNUNET_SYSERR; } + if (0 != strcasecmp (type, + "sepa")) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Transfer type `%s' invalid\n", + type); + return GNUNET_SYSERR; + } if (1 != validate_iban (iban)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, diff --git a/src/wire/test_sepa_wireformat.c b/src/wire/test_sepa_wireformat.c index d00228dd..958aac26 100644 --- a/src/wire/test_sepa_wireformat.c +++ b/src/wire/test_sepa_wireformat.c @@ -130,7 +130,7 @@ main(int argc, GNUNET_assert (NULL != plugin); (void) memset(&error, 0, sizeof(error)); GNUNET_assert (NULL != (wire = json_loads (unsupported_wire_str, 0, NULL))); - GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire)); + GNUNET_assert (GNUNET_YES != plugin->wire_validate (wire)); json_decref (wire); GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str, 0, NULL))); GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire)); -- cgit v1.2.3 From 79731479333a31955cb8faeceb4e24f3a745208a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 13:21:37 +0100 Subject: fix #4149 --- src/mint/Makefile.am | 13 +- src/mint/taler-mint-httpd.c | 45 ++----- src/mint/taler-mint-httpd.h | 8 +- src/mint/taler-mint-httpd_admin.c | 4 +- src/mint/taler-mint-httpd_deposit.c | 4 +- src/mint/taler-mint-httpd_validation.c | 231 +++++++++++++++++++++++++++++++++ src/mint/taler-mint-httpd_validation.h | 76 +++++++++++ src/mint/taler-mint-httpd_wire.c | 31 +---- 8 files changed, 331 insertions(+), 81 deletions(-) create mode 100644 src/mint/taler-mint-httpd_validation.c create mode 100644 src/mint/taler-mint-httpd_validation.h (limited to 'src') diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index a115d63a..aa9b1974 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -11,17 +11,18 @@ bin_PROGRAMS = \ taler_mint_httpd_SOURCES = \ taler-mint-httpd.c taler-mint-httpd.h \ - taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \ - taler-mint-httpd_db.c taler-mint-httpd_db.h \ - taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \ - taler-mint-httpd_responses.c taler-mint-httpd_responses.h \ - taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \ taler-mint-httpd_admin.c taler-mint-httpd_admin.h \ + taler-mint-httpd_db.c taler-mint-httpd_db.h \ taler-mint-httpd_deposit.c taler-mint-httpd_deposit.h \ + taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \ + taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \ + taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \ + taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h \ taler-mint-httpd_reserve.c taler-mint-httpd_reserve.h \ + taler-mint-httpd_responses.c taler-mint-httpd_responses.h \ taler-mint-httpd_tracking.c taler-mint-httpd_tracking.h \ taler-mint-httpd_wire.c taler-mint-httpd_wire.h \ - taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h + taler-mint-httpd_validation.c taler-mint-httpd_validation.h taler_mint_httpd_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/util/libtalerutil.la \ diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c index 26a440c9..5d6aa058 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -39,6 +39,7 @@ #include "taler-mint-httpd_test.h" #endif #include "taler_mintdb_plugin.h" +#include "taler-mint-httpd_validation.h" /** * Which currency is used by this mint? @@ -66,13 +67,6 @@ struct GNUNET_CONFIGURATION_Handle *cfg; */ struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key; -/** - * In which format does this MINT expect wiring instructions? - * NULL-terminated array of 0-terminated wire format types, - * suitable for passing to #TALER_json_validate_wireformat(). - */ -const char **TMH_expected_wire_formats; - /** * Our DB plugin. */ @@ -384,9 +378,6 @@ mint_serve_process_config (const char *mint_directory) { unsigned long long port; char *TMH_master_public_key_str; - char *wireformats; - const char *token; - unsigned int len; cfg = TALER_config_load (mint_directory); if (NULL == cfg) @@ -414,35 +405,9 @@ mint_serve_process_config (const char *mint_directory) (unsigned int) TALER_CURRENCY_LEN); return GNUNET_SYSERR; } - /* Find out list of supported wire formats */ if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "mint", - "wireformat", - &wireformats)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "mint", - "wireformat"); + TMH_VALIDATION_init (cfg)) return GNUNET_SYSERR; - } - /* build NULL-terminated array of TMH_expected_wire_formats */ - TMH_expected_wire_formats = GNUNET_new_array (1, - const char *); - len = 1; - for (token = strtok (wireformats, - " "); - NULL != token; - token = strtok (NULL, - " ")) - { - /* Grow by 1, appending NULL-terminator */ - GNUNET_array_append (TMH_expected_wire_formats, - len, - NULL); - TMH_expected_wire_formats[len - 2] = GNUNET_strdup (token); - } - GNUNET_free (wireformats); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, @@ -453,6 +418,7 @@ mint_serve_process_config (const char *mint_directory) GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "mint", "master_public_key"); + TMH_VALIDATION_done (); return GNUNET_SYSERR; } if (GNUNET_OK != @@ -463,6 +429,7 @@ mint_serve_process_config (const char *mint_directory) fprintf (stderr, "Invalid master public key given in mint configuration."); GNUNET_free (TMH_master_public_key_str); + TMH_VALIDATION_done (); return GNUNET_SYSERR; } GNUNET_free (TMH_master_public_key_str); @@ -472,6 +439,7 @@ mint_serve_process_config (const char *mint_directory) { fprintf (stderr, "Failed to initialize DB subsystem\n"); + TMH_VALIDATION_done (); return GNUNET_SYSERR; } if (GNUNET_YES == @@ -496,6 +464,7 @@ mint_serve_process_config (const char *mint_directory) "mint", "port", "port number required"); + TMH_VALIDATION_done (); return GNUNET_SYSERR; } @@ -505,6 +474,7 @@ mint_serve_process_config (const char *mint_directory) fprintf (stderr, "Invalid configuration (value out of range): %llu is not a valid port\n", port); + TMH_VALIDATION_done (); return GNUNET_SYSERR; } serve_port = (uint16_t) port; @@ -772,6 +742,7 @@ main (int argc, session); } TALER_MINTDB_plugin_unload (TMH_plugin); + TMH_VALIDATION_done (); return (GNUNET_SYSERR == ret) ? 1 : 0; } diff --git a/src/mint/taler-mint-httpd.h b/src/mint/taler-mint-httpd.h index e83dd66f..d45325aa 100644 --- a/src/mint/taler-mint-httpd.h +++ b/src/mint/taler-mint-httpd.h @@ -27,6 +27,7 @@ #include + /** * Which currency is used by this mint? */ @@ -52,13 +53,6 @@ extern int TMH_test_mode; */ extern char *TMH_mint_directory; -/** - * In which formats does this MINT expect wiring instructions? - * NULL-terminated array of 0-terminated wire format types, - * suitable for passing to #TALER_json_validate_wireformat(). - */ -extern const char **TMH_expected_wire_formats; - /** * Master public key (according to the * configuration in the mint directory). diff --git a/src/mint/taler-mint-httpd_admin.c b/src/mint/taler-mint-httpd_admin.c index 99f25641..e6f186f0 100644 --- a/src/mint/taler-mint-httpd_admin.c +++ b/src/mint/taler-mint-httpd_admin.c @@ -24,6 +24,7 @@ #include "taler-mint-httpd_admin.h" #include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_responses.h" +#include "taler-mint-httpd_validation.h" /** @@ -144,8 +145,7 @@ TMH_ADMIN_handler_admin_add_incoming (struct TMH_RequestHandler *rh, return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } if (GNUNET_YES != - TALER_json_validate_wireformat (TMH_expected_wire_formats, - wire)) + TMH_json_validate_wireformat (wire)) { GNUNET_break_op (0); TMH_PARSE_release_data (spec); diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c index 0aef4775..40c5a4db 100644 --- a/src/mint/taler-mint-httpd_deposit.c +++ b/src/mint/taler-mint-httpd_deposit.c @@ -34,6 +34,7 @@ #include "taler-mint-httpd_deposit.h" #include "taler-mint-httpd_responses.h" #include "taler-mint-httpd_keystate.h" +#include "taler-mint-httpd_validation.h" /** @@ -162,8 +163,7 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, return MHD_YES; /* failure */ if (GNUNET_YES != - TALER_json_validate_wireformat (TMH_expected_wire_formats, - wire)) + TMH_json_validate_wireformat (wire)) { TMH_PARSE_release_data (spec); return TMH_RESPONSE_reply_arg_unknown (connection, diff --git a/src/mint/taler-mint-httpd_validation.c b/src/mint/taler-mint-httpd_validation.c new file mode 100644 index 00000000..461e8875 --- /dev/null +++ b/src/mint/taler-mint-httpd_validation.c @@ -0,0 +1,231 @@ +/* + This file is part of TALER + Copyright (C) 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file taler-mint-httpd_validation.c + * @brief helpers for calling the wire plugins to validate addresses + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include "taler-mint-httpd_validation.h" +#include "taler_wire_plugin.h" + + +/** + * Information we keep for each plugin. + */ +struct Plugin +{ + + /** + * We keep plugins in a DLL. + */ + struct Plugin *next; + + /** + * We keep plugins in a DLL. + */ + struct Plugin *prev; + + /** + * Type of the wireformat. + */ + char *type; + + /** + * Pointer to the plugin. + */ + struct TALER_WIRE_Plugin *plugin; + +}; + +/** + * Head of DLL of wire plugins. + */ +static struct Plugin *wire_head; + +/** + * Tail of DLL of wire plugins. + */ +static struct Plugin *wire_tail; + + +/** + * Initialize validation subsystem. + * + * @param cfg configuration to use + * @return #GNUNET_OK on success + */ +int +TMH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct Plugin *p; + char *wireformats; + char *lib_name; + const char *token; + + /* Find out list of supported wire formats */ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "mint", + "wireformat", + &wireformats)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint", + "wireformat"); + return GNUNET_SYSERR; + } + for (token = strtok (wireformats, + " "); + NULL != token; + token = strtok (NULL, + " ")) + { + (void) GNUNET_asprintf (&lib_name, + "libtaler_plugin_wire_%s", + lib_name); + p = GNUNET_new (struct Plugin); + p->type = GNUNET_strdup (token); + p->plugin = GNUNET_PLUGIN_load (lib_name, + (void *) cfg); + if (NULL == p->plugin) + { + GNUNET_free (p); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to load plugin %s\n", + lib_name); + GNUNET_free (lib_name); + TMH_VALIDATION_done (); + return GNUNET_SYSERR; + } + p->plugin->library_name = lib_name; + GNUNET_CONTAINER_DLL_insert (wire_head, + wire_tail, + p); + } + GNUNET_free (wireformats); + return GNUNET_OK; +} + + +/** + * Shutdown validation subsystem. + */ +void +TMH_VALIDATION_done () +{ + struct Plugin *p; + char *lib_name; + + while (NULL != (p = wire_head)) + { + GNUNET_CONTAINER_DLL_remove (wire_head, + wire_tail, + p); + lib_name = p->plugin->library_name; + GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name, + p->plugin)); + GNUNET_free (lib_name); + GNUNET_free (p->type); + GNUNET_free (p); + } +} + + +/** + * Check if the given wire format JSON object is correctly formatted as + * a wire address. + * + * @param wire the JSON wire format object + * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not + */ +int +TMH_json_validate_wireformat (const json_t *wire) +{ + const char *stype; + json_error_t error; + struct Plugin *p; + + if (0 != json_unpack_ex ((json_t *) wire, + &error, 0, + "{s:s}", + "type", &stype)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + for (p=wire_head; NULL != p; p = p->next) + if (0 == strcasecmp (p->type, + stype)) + return p->plugin->wire_validate (wire); + return GNUNET_NO; +} + + +/** + * Check if we support the given wire method. + * + * @param type type of wire method to check + * @return #GNUNET_YES if the method is supported + */ +int +TMH_VALIDATION_test_method (const char *type) +{ + struct Plugin *p; + + for (p=wire_head;NULL != p;p = p->next) + if (0 == strcasecmp (type, + p->type)) + return GNUNET_YES; + return GNUNET_NO; +} + + +/** + * Obtain supported validation methods as a JSON array, + * and as a hash. + * + * @param[out] h set to the hash of the JSON methods + * @return JSON array with the supported validation methods + */ +json_t * +TMH_VALIDATION_get_methods (struct GNUNET_HashCode *h) +{ + json_t *methods; + struct GNUNET_HashContext *hc; + const char *wf; + struct Plugin *p; + + methods = json_array (); + hc = GNUNET_CRYPTO_hash_context_start (); + for (p=wire_head;NULL != p;p = p->next) + { + wf = p->type; + json_array_append_new (methods, + json_string (wf)); + GNUNET_CRYPTO_hash_context_read (hc, + wf, + strlen (wf) + 1); + } + GNUNET_CRYPTO_hash_context_finish (hc, + h); + return methods; +} + + +/* end of taler-mint-httpd_validation.c */ diff --git a/src/mint/taler-mint-httpd_validation.h b/src/mint/taler-mint-httpd_validation.h new file mode 100644 index 00000000..f5fb1900 --- /dev/null +++ b/src/mint/taler-mint-httpd_validation.h @@ -0,0 +1,76 @@ +/* + This file is part of TALER + Copyright (C) 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file taler-mint-httpd_validation.h + * @brief helpers for calling the wire plugins to validate addresses + * @author Christian Grothoff + */ +#ifndef TALER_MINT_HTTPD_VALIDATION_H +#define TALER_MINT_HTTPD_VALIDATION_H +#include +#include + + +/** + * Initialize validation subsystem. + * + * @param cfg configuration to use + * @return #GNUNET_OK on success + */ +int +TMH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg); + + +/** + * Shutdown validation subsystem. + */ +void +TMH_VALIDATION_done (void); + + +/** + * Check if the given wire format JSON object is correctly formatted as + * a wire address. + * + * @param wire the JSON wire format object + * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not + */ +int +TMH_json_validate_wireformat (const json_t *wire); + +/** + * Check if we support the given wire method. + * + * @param type type of wire method to check + * @return #GNUNET_YES if the method is supported + */ +int +TMH_VALIDATION_test_method (const char *type); + + +/** + * Obtain supported validation methods as a JSON array, + * and as a hash. + * + * @param[out] h set to the hash of the JSON methods + * @return JSON array with the supported validation methods + */ +json_t * +TMH_VALIDATION_get_methods (struct GNUNET_HashCode *h); + + +#endif diff --git a/src/mint/taler-mint-httpd_wire.c b/src/mint/taler-mint-httpd_wire.c index 1eb3f6be..020a7e10 100644 --- a/src/mint/taler-mint-httpd_wire.c +++ b/src/mint/taler-mint-httpd_wire.c @@ -21,6 +21,7 @@ #include "platform.h" #include "taler-mint-httpd_keystate.h" #include "taler-mint-httpd_responses.h" +#include "taler-mint-httpd_validation.h" #include "taler-mint-httpd_wire.h" #include @@ -45,24 +46,10 @@ TMH_WIRE_handler_wire (struct TMH_RequestHandler *rh, struct TALER_MintPublicKeyP pub; struct TALER_MintSignatureP sig; json_t *methods; - struct GNUNET_HashContext *hc; - unsigned int i; - const char *wf; - methods = json_array (); - hc = GNUNET_CRYPTO_hash_context_start (); - for (i=0;NULL != (wf = TMH_expected_wire_formats[i]); i++) - { - json_array_append_new (methods, - json_string (wf)); - GNUNET_CRYPTO_hash_context_read (hc, - wf, - strlen (wf) + 1); - } wsm.purpose.size = htonl (sizeof (wsm)); wsm.purpose.purpose = htonl (TALER_SIGNATURE_MINT_WIRE_TYPES); - GNUNET_CRYPTO_hash_context_finish (hc, - &wsm.h_wire_types); + methods = TMH_VALIDATION_get_methods (&wsm.h_wire_types); TMH_KS_sign (&wsm.purpose, &pub, &sig); @@ -97,7 +84,6 @@ TMH_WIRE_handler_wire_test (struct TMH_RequestHandler *rh, struct MHD_Response *response; int ret; char *wire_test_redirect; - unsigned int i; response = MHD_create_response_from_buffer (0, NULL, MHD_RESPMEM_PERSISTENT); @@ -107,11 +93,7 @@ TMH_WIRE_handler_wire_test (struct TMH_RequestHandler *rh, return MHD_NO; } TMH_RESPONSE_add_global_headers (response); - for (i=0;NULL != TMH_expected_wire_formats[i];i++) - if (0 == strcasecmp ("test", - TMH_expected_wire_formats[i])) - break; - if (NULL == TMH_expected_wire_formats[i]) + if (GNUNET_NO == TMH_VALIDATION_test_method ("test")) { /* Return 501: not implemented */ ret = MHD_queue_response (connection, @@ -165,13 +147,8 @@ TMH_WIRE_handler_wire_sepa (struct TMH_RequestHandler *rh, char *sepa_wire_file; int fd; struct stat sbuf; - unsigned int i; - for (i=0;NULL != TMH_expected_wire_formats[i];i++) - if (0 == strcasecmp ("sepa", - TMH_expected_wire_formats[i])) - break; - if (NULL == TMH_expected_wire_formats[i]) + if (GNUNET_NO == TMH_VALIDATION_test_method ("sepa")) { /* Return 501: not implemented */ response = MHD_create_response_from_buffer (0, NULL, -- cgit v1.2.3 From fae7db7e930af35e3dac9a6980b69464b4317a85 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 14:23:18 +0100 Subject: skeleton for binary to implement #4141 --- src/mint/Makefile.am | 10 +++ src/mint/taler-mint-aggregator.c | 147 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 src/mint/taler-mint-aggregator.c (limited to 'src') diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index aa9b1974..fda014d5 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -7,8 +7,18 @@ if USE_COVERAGE endif bin_PROGRAMS = \ + taler-mint-aggregator \ taler-mint-httpd +taler_mint_aggregator_SOURCES = \ + taler-mint-aggregator.c +taler_mint_aggregator_LDADD = \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/mintdb/libtalermintdb.la \ + -ljansson \ + -lgnunetutil + taler_mint_httpd_SOURCES = \ taler-mint-httpd.c taler-mint-httpd.h \ taler-mint-httpd_admin.c taler-mint-httpd_admin.h \ diff --git a/src/mint/taler-mint-aggregator.c b/src/mint/taler-mint-aggregator.c new file mode 100644 index 00000000..a739d87e --- /dev/null +++ b/src/mint/taler-mint-aggregator.c @@ -0,0 +1,147 @@ +/* + This file is part of TALER + Copyright (C) 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file taler-mint-aggregator.c + * @brief Process that aggregates outgoing transactions and executes them + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include +#include "taler_wire_plugin.h" + +/** + * Which currency is used by this mint? + */ +static char *mint_currency_string; + +/** + * Base directory of the mint (global) + */ +static char *mint_directory; + +/** + * The mint's configuration (global) + */ +static struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Our DB plugin. + */ +static struct TALER_MINTDB_Plugin *db_plugin; + + +/** + * Load configuration parameters for the mint + * server into the corresponding global variables. + * + * @param mint_directory the mint's directory + * @return #GNUNET_OK on success + */ +static int +mint_serve_process_config (const char *mint_directory) +{ + unsigned long long port; + + cfg = TALER_config_load (mint_directory); + if (NULL == cfg) + { + fprintf (stderr, + "Failed to load mint configuration\n"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "mint", + "currency", + &mint_currency_string)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint", + "currency"); + return GNUNET_SYSERR; + } + if (strlen (mint_currency_string) >= TALER_CURRENCY_LEN) + { + fprintf (stderr, + "Currency `%s' longer than the allowed limit of %u characters.", + mint_currency_string, + (unsigned int) TALER_CURRENCY_LEN); + return GNUNET_SYSERR; + } + + if (NULL == + (db_plugin = TALER_MINTDB_plugin_load (cfg))) + { + fprintf (stderr, + "Failed to initialize DB subsystem\n"); + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + +/** + * The main function of the taler-mint-httpd server ("the mint"). + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, + char *const *argv) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + {'d', "mint-dir", "DIR", + "mint directory with configuration and keys for operating the mint", 1, + &GNUNET_GETOPT_set_filename, &mint_directory}, + TALER_GETOPT_OPTION_HELP ("background process that aggregates and executes wire transfers to merchants"), + GNUNET_GETOPT_OPTION_VERSION (VERSION "-" VCS_VERSION), + GNUNET_GETOPT_OPTION_END + }; + int ret; + + GNUNET_assert (GNUNET_OK == + GNUNET_log_setup ("taler-mint-aggregator", + "INFO", + NULL)); + if (0 >= + GNUNET_GETOPT_run ("taler-mint-aggregator", + options, + argc, argv)) + return 1; + if (NULL == mint_directory) + { + fprintf (stderr, + "Mint directory not specified\n"); + return 1; + } + + if (GNUNET_OK != + mint_serve_process_config (mint_directory)) + return 1; + + + + TALER_MINTDB_plugin_unload (db_plugin); + return (GNUNET_SYSERR == ret) ? 1 : 0; +} + +/* end of taler-mint-aggregator.c */ -- cgit v1.2.3 From 57c1d2318f14c4b5c21609cb96f32517d02752e7 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 14:57:32 +0100 Subject: getting aggregator structure laid out for #4141 --- src/include/Makefile.am | 1 + src/include/taler_mintdb_plugin.h | 8 +- src/mint/Makefile.am | 1 + src/mint/taler-mint-aggregator.c | 164 ++++++++++++++++++++++++++++++++++++-- src/wire/Makefile.am | 13 +++ src/wire/test_sepa_wireformat.c | 53 +----------- 6 files changed, 182 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/include/Makefile.am b/src/include/Makefile.am index 2f3973c4..4d7ae3cb 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -21,6 +21,7 @@ talerinclude_HEADERS = \ taler_mintdb_plugin.h \ taler_pq_lib.h \ taler_signatures.h \ + taler_wire_lib.h \ taler_wire_plugin.h endif diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index e5cf6d6f..4230a761 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -1145,10 +1145,10 @@ struct TALER_MINTDB_Plugin */ int (*insert_refresh_out) (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t newcoin_index, - const struct TALER_DenominationSignature *ev_sig); + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + uint16_t newcoin_index, + const struct TALER_DenominationSignature *ev_sig); /** diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index fda014d5..8e2eae77 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -15,6 +15,7 @@ taler_mint_aggregator_SOURCES = \ taler_mint_aggregator_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/mintdb/libtalermintdb.la \ -ljansson \ -lgnunetutil diff --git a/src/mint/taler-mint-aggregator.c b/src/mint/taler-mint-aggregator.c index a739d87e..d3c66f02 100644 --- a/src/mint/taler-mint-aggregator.c +++ b/src/mint/taler-mint-aggregator.c @@ -23,13 +23,20 @@ #include #include #include -#include "taler_wire_plugin.h" +#include "taler_mintdb_lib.h" +#include "taler_mintdb_plugin.h" +#include "taler_wire_lib.h" /** * Which currency is used by this mint? */ static char *mint_currency_string; +/** + * Which wireformat should be supported by this aggregator? + */ +static char *mint_wireformat; + /** * Base directory of the mint (global) */ @@ -45,6 +52,16 @@ static struct GNUNET_CONFIGURATION_Handle *cfg; */ static struct TALER_MINTDB_Plugin *db_plugin; +/** + * Our wire plugin. + */ +static struct TALER_WIRE_Plugin *wire_plugin; + +/** + * Task for the main #run() function. + */ +static struct GNUNET_SCHEDULER_Task *task; + /** * Load configuration parameters for the mint @@ -56,7 +73,7 @@ static struct TALER_MINTDB_Plugin *db_plugin; static int mint_serve_process_config (const char *mint_directory) { - unsigned long long port; + char *type; cfg = TALER_config_load (mint_directory); if (NULL == cfg) @@ -84,19 +101,151 @@ mint_serve_process_config (const char *mint_directory) (unsigned int) TALER_CURRENCY_LEN); return GNUNET_SYSERR; } + if (NULL != mint_wireformat) + GNUNET_CONFIGURATION_set_value_string (cfg, + "mint", + "wireformat", + mint_wireformat); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "mint", + "wireformat", + &type)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint", + "wireformat"); + return GNUNET_SYSERR; + } if (NULL == (db_plugin = TALER_MINTDB_plugin_load (cfg))) { fprintf (stderr, "Failed to initialize DB subsystem\n"); + GNUNET_free (type); + return GNUNET_SYSERR; + } + + if (NULL == + (wire_plugin = TALER_WIRE_plugin_load (cfg, + type))) + { + fprintf (stderr, + "Failed to load wire plugin for `%s'\n", + type); + GNUNET_free (type); return GNUNET_SYSERR; } + GNUNET_free (type); return GNUNET_OK; } +/** + * Function called with details about deposits that have been made, + * with the goal of executing the corresponding wire transaction. + * + * @param cls closure + * @param id transaction ID (used as future `min_id` to avoid + * iterating over transactions more than once) + * @param amount_with_fee amount that was deposited including fee + * @param deposit_fee amount the mint gets to keep as transaction fees + * @param transaction_id unique transaction ID chosen by the merchant + * @param h_contract hash of the contract between merchant and customer + * @param wire_deadline by which the merchant adviced that he would like the + * wire transfer to be executed + * @param wire wire details for the merchant + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static int +deposit_cb (void *cls, + uint64_t id, + const struct TALER_Amount *amount_with_fee, + const struct TALER_Amount *deposit_fee, + uint64_t transaction_id, + const struct GNUNET_HashCode *h_contract, + struct GNUNET_TIME_Absolute wire_deadline, + const json_t *wire) +{ + /* FIXME: compute aggregates, etc. */ + return GNUNET_OK; +} + + +/** + * Main work function that queries the DB and executes transactions. + */ +static void +run (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *global_ret = cls; + struct TALER_MINTDB_Session *session; + int ret; + + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + if (NULL == (session = db_plugin->get_session (db_plugin->cls, + GNUNET_NO))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to obtain database session!\n"); + *global_ret = GNUNET_SYSERR; + return; + } + if (GNUNET_OK != + db_plugin->start (db_plugin->cls, + session)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to start database transaction!\n"); + *global_ret = GNUNET_SYSERR; + return; + } + ret = db_plugin->iterate_deposits (db_plugin->cls, + session, + 0 /* FIXME: remove? */, + 128 /* FIXME: make configurable? */, + &deposit_cb, + NULL); + if (GNUNET_OK != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to execute deposit iteration!\n"); + *global_ret = GNUNET_SYSERR; + db_plugin->rollback (db_plugin->cls, + session); + return; + } + /* FIXME: finish aggregate computation */ + /* FIXME: insert pre-commit data for transaction into DB */ + /* FIXME: mark transactions selected for aggregate as finished */ + + if (GNUNET_OK != + db_plugin->commit (db_plugin->cls, + session)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to commit database transaction!\n"); + } + + /* FIXME: run 2nd transaction: + - begin + - select pre-commit data from DB + - execute wire transfer + - insert aggregation tracking information into DB + - commit! + */ + + + task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS /* FIXME: adjust! */, + &run, + global_ret); +} + + /** * The main function of the taler-mint-httpd server ("the mint"). * @@ -112,11 +261,14 @@ main (int argc, {'d', "mint-dir", "DIR", "mint directory with configuration and keys for operating the mint", 1, &GNUNET_GETOPT_set_filename, &mint_directory}, + {'f', "format", "WIREFORMAT", + "wireformat to use, overrides WIREFORMAT option in [mint] section", 1, + &GNUNET_GETOPT_set_filename, &mint_wireformat}, TALER_GETOPT_OPTION_HELP ("background process that aggregates and executes wire transfers to merchants"), GNUNET_GETOPT_OPTION_VERSION (VERSION "-" VCS_VERSION), GNUNET_GETOPT_OPTION_END }; - int ret; + int ret = GNUNET_OK; GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-aggregator", @@ -133,14 +285,16 @@ main (int argc, "Mint directory not specified\n"); return 1; } - if (GNUNET_OK != mint_serve_process_config (mint_directory)) + { return 1; + } - + GNUNET_SCHEDULER_run (&run, &ret); TALER_MINTDB_plugin_unload (db_plugin); + TALER_WIRE_plugin_unload (wire_plugin); return (GNUNET_SYSERR == ret) ? 1 : 0; } diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am index 528d9101..fb6f2561 100644 --- a/src/wire/Makefile.am +++ b/src/wire/Makefile.am @@ -15,6 +15,9 @@ plugin_LTLIBRARIES = \ noinst_LTLIBRARIES = \ libtaler_plugin_wire_template.la +lib_LTLIBRARIES = \ + libtalerwire.la + libtaler_plugin_wire_test_la_SOURCES = \ plugin_wire_test.c @@ -47,6 +50,15 @@ libtaler_plugin_wire_template_la_LDFLAGS = \ -lgnunetutil $(XLIB) +libtalerwire_la_SOURCES = \ + wire.c +libtalerwire_la_LIBADD = \ + -lgnunetutil + $(XLIB) +libtalerwire_la_LDFLAGS = \ + -version-info 0:0:0 \ + -export-dynamic -no-undefined + TESTS = \ test_sepa_wireformat @@ -61,4 +73,5 @@ test_sepa_wireformat_SOURCES = \ test_sepa_wireformat_LDADD = \ -lgnunetutil \ -ljansson \ + libtalerwire.la \ $(top_builddir)/src/util/libtalerutil.la diff --git a/src/wire/test_sepa_wireformat.c b/src/wire/test_sepa_wireformat.c index 958aac26..edbe5bc4 100644 --- a/src/wire/test_sepa_wireformat.c +++ b/src/wire/test_sepa_wireformat.c @@ -22,7 +22,7 @@ #include "platform.h" #include "taler_util.h" -#include "taler_wire_plugin.h" +#include "taler_wire_lib.h" /* Valid SEPA data */ @@ -62,51 +62,6 @@ static const char * const unsupported_wire_str = \"address\": \"foobar\"}"; -/** - * Initialize the plugin. - * - * @param cfg configuration to use - * @return #GNUNET_OK on success - */ -static struct TALER_WIRE_Plugin * -wire_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *plugin_name) -{ - char *lib_name; - struct TALER_WIRE_Plugin *plugin; - - (void) GNUNET_asprintf (&lib_name, - "libtaler_plugin_wire_%s", - plugin_name); - plugin = GNUNET_PLUGIN_load (lib_name, - (void *) cfg); - if (NULL != plugin) - plugin->library_name = lib_name; - else - GNUNET_free (lib_name); - return plugin; -} - - -/** - * Shutdown the plugin. - * - * @param plugin the plugin to unload - */ -static void -wire_plugin_unload (struct TALER_WIRE_Plugin *plugin) -{ - char *lib_name; - - if (NULL == plugin) - return; - lib_name = plugin->library_name; - GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name, - plugin)); - GNUNET_free (lib_name); -} - - int main(int argc, const char *const argv[]) @@ -125,8 +80,8 @@ main(int argc, "mint", "currency", "EUR"); - plugin = wire_plugin_load (cfg, - "sepa"); + plugin = TALER_WIRE_plugin_load (cfg, + "sepa"); GNUNET_assert (NULL != plugin); (void) memset(&error, 0, sizeof(error)); GNUNET_assert (NULL != (wire = json_loads (unsupported_wire_str, 0, NULL))); @@ -141,7 +96,7 @@ main(int argc, GNUNET_assert (NULL != (wire = json_loads (valid_wire_str, 0, &error))); ret = plugin->wire_validate (wire); json_decref (wire); - wire_plugin_unload (plugin); + TALER_WIRE_plugin_unload (plugin); GNUNET_CONFIGURATION_destroy (cfg); if (GNUNET_NO == ret) return 1; -- cgit v1.2.3 From bd3700e608daf2ae52400275b1957656ccf2d6aa Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 15:08:29 +0100 Subject: getting aggregator structure laid out for #4141 --- src/include/taler_mintdb_plugin.h | 74 ++++++++++++++++++++++++++++++++++++++- src/mint/taler-mint-aggregator.c | 21 ++++++----- 2 files changed, 86 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 4230a761..e91eb7d4 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -618,6 +618,19 @@ typedef void const struct TALER_Amount *transfer_value); +/** + * Callback with data about a prepared transaction. + * + * @param cls closure + * @param buf transaction data that was persisted, NULL on error + * @param buf_size number of bytes in @a buf, 0 on error + */ +typedef void +(*TALER_MINTDB_WirePreparationCallback) (void *cls, + const char *buf, + size_t buf_size); + + /** * @brief The plugin API, returned from the plugin's "init" function. * The argument given to "init" is simply a configuration handle. @@ -1308,7 +1321,66 @@ struct TALER_MINTDB_Plugin const struct TALER_Amount *transfer_value); + + /** + * Function called to insert wire transfer commit data into the DB. + * + * @param cls closure + * @param session database connection + * @param type type fo the wire transfer (i.e. "sepa") + * @param buf buffer with wire transfer preparation data + * @param buf_size number of bytes in @a buf + * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors + */ + int + (*wire_prepare_data_insert)(void *cls, + struct TALER_MINTDB_Session *session, + const char *type, + const char *buf, + size_t buf_size); + + + /** + * Function called to mark wire transfer commit data as finished. + * + * @param cls closure + * @param session database connection + * @param type type fo the wire transfer (i.e. "sepa") + * @param buf buffer with wire transfer preparation data + * @param buf_size number of bytes in @a buf + * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors + */ + int + (*wire_prepare_data_mark_finished)(void *cls, + struct TALER_MINTDB_Session *session, + const char *type, + const char *buf, + size_t buf_size); + + + /** + * Function called to iterate over unfinished wire transfer + * preparation data. Fetches at most one item. + * + * @param cls closure + * @param session database connection + * @param type type fo the wire transfer (i.e. "sepa") + * @param cb function to call for ONE unfinished item + * @param cb_cls closure for @a cb + * @return #GNUNET_OK on success, + * #GNUNET_NO if there are no entries, + * #GNUNET_SYSERR on DB errors + */ + int + (*wire_prepare_data_iterate)(void *cls, + struct TALER_MINTDB_Session *session, + const char *type, + TALER_MINTDB_WirePreparationCallback cb, + void *cb_cls); + + + }; -#endif /* _NEURO_MINT_DB_H */ +#endif /* _TALER_MINT_DB_H */ diff --git a/src/mint/taler-mint-aggregator.c b/src/mint/taler-mint-aggregator.c index d3c66f02..70a68d00 100644 --- a/src/mint/taler-mint-aggregator.c +++ b/src/mint/taler-mint-aggregator.c @@ -220,9 +220,11 @@ run (void *cls, return; } /* FIXME: finish aggregate computation */ - /* FIXME: insert pre-commit data for transaction into DB */ - /* FIXME: mark transactions selected for aggregate as finished */ + /* wire_plugin->prepare_wire_transfer () -- ASYNC! */ + /* db_plugin->wire_prepare_data_insert () -- transactional! */ + /* db_plugin->XXX () -- mark transactions selected for aggregate as finished */ + /* then finally: commit! */ if (GNUNET_OK != db_plugin->commit (db_plugin->cls, session)) @@ -231,12 +233,15 @@ run (void *cls, "Failed to commit database transaction!\n"); } - /* FIXME: run 2nd transaction: - - begin - - select pre-commit data from DB - - execute wire transfer - - insert aggregation tracking information into DB - - commit! + /* While possible, run 2nd type of transaction: + db_plugin->start() + - select pre-commit data from DB: + db_plugin->wire_prepare_data_iterate () + - execute wire transfer (successfully!) + wire_plugin->execute_wire_transfer() # ASYNC! + db_plugin->wire_prepare_data_mark_finished () + db_plugin->insert_aggregation_tracking () + db_plugin->commit() */ -- cgit v1.2.3 From 0ba855ae92c2bf38c51f830fdea3062e88fd44f3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 25 Jan 2016 15:18:31 +0100 Subject: more mintdb API design for #4141 --- src/include/taler_mintdb_plugin.h | 76 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index e91eb7d4..65c694a7 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -543,6 +543,8 @@ struct TALER_MINTDB_Session; */ typedef int (*TALER_MINTDB_DepositIterator)(void *cls, + // unsigned long long rowid, /* ? */ + // May also need/want Merchant pub!? uint64_t id, const struct TALER_Amount *amount_with_fee, const struct TALER_Amount *deposit_fee, @@ -893,6 +895,78 @@ struct TALER_MINTDB_Plugin const struct TALER_MINTDB_Deposit *deposit); + /** + * Mark a deposit as tiny, thereby declaring that it cannot be + * executed by itself and should no longer be returned by + * @e iterate_ready_deposits() + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param session connection to the database + * @param deposit_rowid identifies the deposit row to modify + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ + int + (*mark_deposit_tiny) (void *cls, + struct TALER_MINTDB_Session *session, + unsigned long long rowid); + + + /** + * Mark a deposit as done, thereby declaring that it cannot be + * executed at all anymore, and should no longer be returned by + * @e iterate_ready_deposits() or @e iterate_matching_deposits(). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param session connection to the database + * @param deposit_rowid identifies the deposit row to modify + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ + int + (*mark_deposit_done) (void *cls, + struct TALER_MINTDB_Session *session, + unsigned long long rowid); + + + /** + * Obtain information about deposits that are ready to be executed. + * Such deposits must not be marked as "tiny" or "done", and the + * execution time must be in the past. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param session connection to the database + * @param deposit_cb function to call for ONE such deposit + * @param deposit_cb_cls closure for @a deposit_cb + * @return number of rows processed, 0 if none exist, + * #GNUNET_SYSERR on error + */ + int + (*iterate_ready_deposits) (void *cls, + struct TALER_MINTDB_Session *session, + TALER_MINTDB_DepositIterator deposit_cb, + void *deposit_cb_cls); + + + /** + * Obtain information about other pending deposits for the same + * destination. Those deposits must not already be "done". + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param session connection to the database + * @param h_wire destination of the wire transfer + * @param FIXME: do we also need merchant_pub here? + * @param deposit_cb function to call for each deposit + * @param deposit_cb_cls closure for @a deposit_cb + * @return number of rows processed, 0 if none exist, + * #GNUNET_SYSERR on error + */ + int + (*iterate_matching_deposits) (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *h_wire, + TALER_MINTDB_DepositIterator deposit_cb, + void *deposit_cb_cls); + + /** * Obtain information about deposits. Iterates over all deposits * above a certain ID. Use a @a min_id of 0 to start at the beginning. @@ -907,6 +981,7 @@ struct TALER_MINTDB_Plugin * @param deposit_cb_cls closure for @a deposit_cb * @return number of rows processed, 0 if none exist, * #GNUNET_SYSERR on error + * @deprecated this is likely dead */ int (*iterate_deposits) (void *cls, @@ -1379,7 +1454,6 @@ struct TALER_MINTDB_Plugin void *cb_cls); - }; -- cgit v1.2.3 From f5fea55e1cbef79e133478b9d52515696f22574f Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 27 Jan 2016 15:03:10 +0100 Subject: -missing file --- src/include/taler_wire_lib.h | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/include/taler_wire_lib.h (limited to 'src') diff --git a/src/include/taler_wire_lib.h b/src/include/taler_wire_lib.h new file mode 100644 index 00000000..4b0fcbbc --- /dev/null +++ b/src/include/taler_wire_lib.h @@ -0,0 +1,48 @@ +/* + This file is part of TALER + Copyright (C) 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file include/taler_wire_lib.h + * @brief Interface for loading and unloading wire plugins + * @author Christian Grothoff + */ +#ifndef TALER_WIRE_H +#define TALER_WIRE_H + +#include +#include "taler_wire_plugin.h" + + +/** + * Load a WIRE plugin. + * + * @param cfg configuration to use + * @param plugin_name name of the plugin to load + * @return #GNUNET_OK on success + */ +struct TALER_WIRE_Plugin * +TALER_WIRE_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *plugin_name); + +/** + * Unload a WIRE plugin. + * + * @param plugin the plugin to unload + */ +void +TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin); + + +#endif -- cgit v1.2.3 From 7ad6421df8925f485c9c9c080c14823169d452d9 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 27 Jan 2016 15:03:23 +0100 Subject: -missing file --- src/wire/wire.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/wire/wire.c (limited to 'src') diff --git a/src/wire/wire.c b/src/wire/wire.c new file mode 100644 index 00000000..f0c0cd7a --- /dev/null +++ b/src/wire/wire.c @@ -0,0 +1,72 @@ +/* + This file is part of TALER + (C) 2015, 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file wire/wire.c + * @brief Functions for loading wire plugins + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_wire_lib.h" + +/** + * Load a WIRE plugin. + * + * @param cfg configuration to use + * @param plugin_name name of the plugin to load + * @return #GNUNET_OK on success + */ +struct TALER_WIRE_Plugin * +TALER_WIRE_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *plugin_name) +{ + char *lib_name; + struct TALER_WIRE_Plugin *plugin; + + (void) GNUNET_asprintf (&lib_name, + "libtaler_plugin_wire_%s", + plugin_name); + plugin = GNUNET_PLUGIN_load (lib_name, + (void *) cfg); + if (NULL != plugin) + plugin->library_name = lib_name; + else + GNUNET_free (lib_name); + return plugin; +} + + +/** + * Unload a WIRE plugin. + * + * @param plugin the plugin to unload + */ +void +TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin) +{ + char *lib_name; + + if (NULL == plugin) + return; + lib_name = plugin->library_name; + GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name, + plugin)); + GNUNET_free (lib_name); +} + + +/* end of wire.c */ -- cgit v1.2.3 From 0554fa7691ee495f545a72972d6417db8b2623c2 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 27 Jan 2016 15:05:39 +0100 Subject: fix syntax issues in Makefile.am --- src/bank-lib/Makefile.am | 14 -------------- src/include/taler_signatures.h | 27 +++++++++++++++++++++++++++ src/wire/Makefile.am | 3 ++- 3 files changed, 29 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am index 5b3b4d25..326fdd5a 100644 --- a/src/bank-lib/Makefile.am +++ b/src/bank-lib/Makefile.am @@ -31,17 +31,3 @@ libtalerbank_la_LIBADD += -lgnurl endif endif -#check_PROGRAMS = \ -# test_bank_api - -#TESTS = \ -# $(check_PROGRAMS) - -#test_bank_api_SOURCES = \ -# test_bank_api.c -#test_bank_api_LDADD = \ -# libtalerbank.la \ -# $(LIBGCRYPT_LIBS) \ -# $(top_builddir)/src/util/libtalerutil.la \ -# -lgnunetutil \ -# -ljansson diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 29008fc9..85c681da 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -859,6 +859,33 @@ struct TALER_ContractPS */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + /** + * Merchant-generated transaction ID to detect duplicate + * transactions, in big endian. The merchant must communicate a + * merchant-unique ID to the customer for each transaction. Note + * that different coins that are part of the same transaction can + * use the same transaction ID. The transaction ID is useful for + * later disputes, and the merchant's contract offer (@e h_contract) + * with the customer should include the offer's term and transaction + * ID signed with a key from the merchant. This field must match + * the corresponding field in the JSON contract. + */ + uint64_t transaction_id GNUNET_PACKED; + + /** + * The total amount to be paid to the merchant. Note that if deposit + * fees are higher than @e max_fee, the actual total must be higher + * to cover the additional fees. This field must match the + * corresponding field in the JSON contract. + */ + struct TALER_AmountNBO total_amount; + + /** + * The maximum fee the merchant is willing to cover. This field + * must match the corresponding field in the JSON contract. + */ + struct TALER_AmountNBO max_fee; + /** * Hash of the JSON contract in UTF-8 including 0-termination, * using JSON_COMPACT | JSON_SORT_KEYS diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am index fb6f2561..eb2e893f 100644 --- a/src/wire/Makefile.am +++ b/src/wire/Makefile.am @@ -53,7 +53,7 @@ libtaler_plugin_wire_template_la_LDFLAGS = \ libtalerwire_la_SOURCES = \ wire.c libtalerwire_la_LIBADD = \ - -lgnunetutil + -lgnunetutil \ $(XLIB) libtalerwire_la_LDFLAGS = \ -version-info 0:0:0 \ @@ -75,3 +75,4 @@ test_sepa_wireformat_LDADD = \ -ljansson \ libtalerwire.la \ $(top_builddir)/src/util/libtalerutil.la + -- cgit v1.2.3 From 48c2edc28dc54c21ecb30b9aec31a3699c3f9bd0 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 27 Jan 2016 16:42:24 +0100 Subject: working on mintdb for #4141 --- src/include/taler_mintdb_plugin.h | 54 +++---- src/mint/taler-mint-aggregator.c | 21 +-- src/mintdb/plugin_mintdb_postgres.c | 295 ++++++++++++++++++++++++++++++------ 3 files changed, 278 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 65c694a7..11b30583 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -530,22 +530,24 @@ struct TALER_MINTDB_Session; * corresponding wire transaction. * * @param cls closure - * @param id transaction ID (used as future `min_id` to avoid - * iterating over transactions more than once) + * @param rowid unique ID for the deposit in our DB, used for marking + * it as 'tiny' or 'done' + * @param merchant_pub public key of the merchant + * @param coin_pub public key of the coin * @param amount_with_fee amount that was deposited including fee * @param deposit_fee amount the mint gets to keep as transaction fees * @param transaction_id unique transaction ID chosen by the merchant * @param h_contract hash of the contract between merchant and customer * @param wire_deadline by which the merchant adviced that he would like the * wire transfer to be executed - * @param wire wire details for the merchant + * @param wire wire details for the merchant, NULL from iterate_matching_deposits() * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ typedef int (*TALER_MINTDB_DepositIterator)(void *cls, - // unsigned long long rowid, /* ? */ - // May also need/want Merchant pub!? - uint64_t id, + unsigned long long rowid, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_Amount *amount_with_fee, const struct TALER_Amount *deposit_fee, uint64_t transaction_id, @@ -940,10 +942,10 @@ struct TALER_MINTDB_Plugin * #GNUNET_SYSERR on error */ int - (*iterate_ready_deposits) (void *cls, - struct TALER_MINTDB_Session *session, - TALER_MINTDB_DepositIterator deposit_cb, - void *deposit_cb_cls); + (*get_ready_deposit) (void *cls, + struct TALER_MINTDB_Session *session, + TALER_MINTDB_DepositIterator deposit_cb, + void *deposit_cb_cls); /** @@ -953,9 +955,10 @@ struct TALER_MINTDB_Plugin * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to the database * @param h_wire destination of the wire transfer - * @param FIXME: do we also need merchant_pub here? + * @param merchant_pub public key of the merchant * @param deposit_cb function to call for each deposit * @param deposit_cb_cls closure for @a deposit_cb + * @param limit maximum number of matching deposits to return * @return number of rows processed, 0 if none exist, * #GNUNET_SYSERR on error */ @@ -963,33 +966,10 @@ struct TALER_MINTDB_Plugin (*iterate_matching_deposits) (void *cls, struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *h_wire, + const struct TALER_MerchantPublicKeyP *merchant_pub, TALER_MINTDB_DepositIterator deposit_cb, - void *deposit_cb_cls); - - - /** - * Obtain information about deposits. Iterates over all deposits - * above a certain ID. Use a @a min_id of 0 to start at the beginning. - * This operation is executed in its own transaction in transaction - * mode "REPEATABLE READ", i.e. we should only see valid deposits. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param session connection to the database - * @param min_id deposit to start at - * @param limit maximum number of transactions to fetch - * @param deposit_cb function to call for each deposit - * @param deposit_cb_cls closure for @a deposit_cb - * @return number of rows processed, 0 if none exist, - * #GNUNET_SYSERR on error - * @deprecated this is likely dead - */ - int - (*iterate_deposits) (void *cls, - struct TALER_MINTDB_Session *session, - uint64_t min_id, - uint32_t limit, - TALER_MINTDB_DepositIterator deposit_cb, - void *deposit_cb_cls); + void *deposit_cb_cls, + uint32_t limit); /** diff --git a/src/mint/taler-mint-aggregator.c b/src/mint/taler-mint-aggregator.c index 70a68d00..ee0f6ab2 100644 --- a/src/mint/taler-mint-aggregator.c +++ b/src/mint/taler-mint-aggregator.c @@ -148,8 +148,9 @@ mint_serve_process_config (const char *mint_directory) * with the goal of executing the corresponding wire transaction. * * @param cls closure - * @param id transaction ID (used as future `min_id` to avoid - * iterating over transactions more than once) + * @param row_id identifies database entry + * @param merchant_pub public key of the merchant + * @param coin_pub public key of the coin * @param amount_with_fee amount that was deposited including fee * @param deposit_fee amount the mint gets to keep as transaction fees * @param transaction_id unique transaction ID chosen by the merchant @@ -161,7 +162,9 @@ mint_serve_process_config (const char *mint_directory) */ static int deposit_cb (void *cls, - uint64_t id, + unsigned long long row_id, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_Amount *amount_with_fee, const struct TALER_Amount *deposit_fee, uint64_t transaction_id, @@ -204,12 +207,12 @@ run (void *cls, *global_ret = GNUNET_SYSERR; return; } - ret = db_plugin->iterate_deposits (db_plugin->cls, - session, - 0 /* FIXME: remove? */, - 128 /* FIXME: make configurable? */, - &deposit_cb, - NULL); + ret = db_plugin->get_ready_deposit (db_plugin->cls, + session, + &deposit_cb, + NULL); + // FIXME: handle 0 == ret... + if (GNUNET_OK != ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index e9a9466b..5ebf8dc7 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -425,7 +425,7 @@ postgres_create_tables (void *cls, /* This table contains the wire transfers the mint is supposed to execute to transmit funds to the merchants (and manage refunds). */ SQLEXEC("CREATE TABLE IF NOT EXISTS deposits " - "(serial_id BIGSERIAL" + "(serial_id BIGSERIAL PRIMARY KEY" ",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)" ",denom_pub BYTEA NOT NULL REFERENCES denominations (pub)" ",denom_sig BYTEA NOT NULL" @@ -444,6 +444,8 @@ postgres_create_tables (void *cls, ",h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)" ",coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)" ",wire TEXT NOT NULL" + ",tiny BOOLEAN NOT NULL DEFAULT false" + ",done BOOLEAN NOT NULL DEFAULT false" ")"); /* Index for get_deposit statement on coin_pub, transaction_id and merchant_pub */ SQLEXEC_INDEX("CREATE INDEX deposits_coin_pub_index " @@ -899,8 +901,8 @@ postgres_prepare (PGconn *db_conn) " )", 5, NULL); - /* Used in #postgres_iterate_deposits() */ - PREPARE ("deposits_iterate", + /* Used in #postgres_get_ready_deposit() */ + PREPARE ("deposits_get_ready", "SELECT" " serial_id" ",amount_with_fee_val" @@ -913,11 +915,50 @@ postgres_prepare (PGconn *db_conn) ",transaction_id" ",h_contract" ",wire" + ",merchant_pub" + ",coin_pub" " FROM deposits" - " WHERE serial_id>=$1" - " ORDER BY serial_id ASC" - " LIMIT $2;", - 2, NULL); + " WHERE" + " tiny=false AND" + " done=false" + " ORDER BY execution_time ASC" + " LIMIT 1;", + 0, NULL); + /* Used in #postgres_iterate_matching_deposits() */ + PREPARE ("deposits_iterate_matching", + "SELECT" + " serial_id" + ",amount_with_fee_val" + ",amount_with_fee_frac" + ",amount_with_fee_curr" + ",deposit_fee_val" + ",deposit_fee_frac" + ",deposit_fee_curr" + ",wire_deadline" + ",transaction_id" + ",h_contract" + ",coin_pub" + " FROM deposits" + " WHERE" + " merchant_pub=$1 AND" + " h_wire=$2 AND" + " done=false" + " ORDER BY execution_time ASC" + " LIMIT $3", + 3, NULL); + /* Used in #postgres_mark_deposit_tiny() */ + PREPARE ("mark_deposit_tiny", + "UPDATE deposits" + " SET tiny=true" + " WHERE serial_id=$1", + 1, NULL); + /* Used in #postgres_mark_deposit_done() */ + PREPARE ("mark_deposit_done", + "UPDATE deposits" + " SET done=true" + " WHERE serial_id=$1", + 1, NULL); + /* Used in #postgres_get_coin_transactions() to obtain information about how a coin has been spend with /deposit requests. */ PREPARE ("get_deposit_with_coin_pub", @@ -2039,82 +2080,239 @@ postgres_have_deposit (void *cls, /** - * Obtain information about deposits. Iterates over all deposits - * above a certain ID. Use a @a min_id of 0 to start at the beginning. - * This operation is executed in its own transaction in transaction - * mode "REPEATABLE READ", i.e. we should only see valid deposits. + * Mark a deposit as tiny, thereby declaring that it cannot be + * executed by itself and should no longer be returned by + * @e iterate_ready_deposits() * * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to the database - * @param min_id deposit to start at - * @param limit maximum number of transactions to fetch - * @param deposit_cb function to call for each deposit + * @param deposit_rowid identifies the deposit row to modify + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static int +postgres_mark_deposit_tiny (void *cls, + struct TALER_MINTDB_Session *session, + unsigned long long rowid) +{ + uint64_t serial_id = rowid; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_uint64 (&serial_id), + TALER_PQ_query_param_end + }; + PGresult *result; + + result = TALER_PQ_exec_prepared (session->conn, + "mark_deposit_tiny", + params); + if (PGRES_COMMAND_OK != + PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + return GNUNET_OK; +} + + +/** + * Mark a deposit as done, thereby declaring that it cannot be + * executed at all anymore, and should no longer be returned by + * @e iterate_ready_deposits() or @e iterate_matching_deposits(). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param session connection to the database + * @param deposit_rowid identifies the deposit row to modify + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static int +postgres_mark_deposit_done (void *cls, + struct TALER_MINTDB_Session *session, + unsigned long long rowid) +{ + uint64_t serial_id = rowid; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_uint64 (&serial_id), + TALER_PQ_query_param_end + }; + PGresult *result; + + result = TALER_PQ_exec_prepared (session->conn, + "mark_deposit_done", + params); + if (PGRES_COMMAND_OK != + PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + return GNUNET_OK; +} + + +/** + * Obtain information about deposits that are ready to be executed. + * Such deposits must not be marked as "tiny" or "done", and the + * execution time must be in the past. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param session connection to the database + * @param deposit_cb function to call for ONE such deposit * @param deposit_cb_cls closure for @a deposit_cb * @return number of rows processed, 0 if none exist, * #GNUNET_SYSERR on error */ static int -postgres_iterate_deposits (void *cls, - struct TALER_MINTDB_Session *session, - uint64_t min_id, - uint32_t limit, - TALER_MINTDB_DepositIterator deposit_cb, - void *deposit_cb_cls) +postgres_get_ready_deposit (void *cls, + struct TALER_MINTDB_Session *session, + TALER_MINTDB_DepositIterator deposit_cb, + void *deposit_cb_cls) { struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_uint64 (&min_id), - TALER_PQ_query_param_uint32 (&limit), TALER_PQ_query_param_end }; PGresult *result; - unsigned int i; unsigned int n; + int ret; - if (GNUNET_OK != - postgres_start (cls, session)) - return GNUNET_SYSERR; - result = PQexec (session->conn, - "SET TRANSACTION REPEATABLE READ"); - if (PGRES_COMMAND_OK != + result = TALER_PQ_exec_prepared (session->conn, + "deposits_get_ready", + params); + if (PGRES_TUPLES_OK != PQresultStatus (result)) { - TALER_LOG_ERROR ("Failed to set transaction to REPEATABL EREAD: %s\n", - PQresultErrorMessage (result)); - GNUNET_break (0); + BREAK_DB_ERR (result); PQclear (result); return GNUNET_SYSERR; } + if (0 == (n = PQntuples (result))) + { + PQclear (result); + return 0; + } + GNUNET_break (1 == n); + { + struct TALER_Amount amount_with_fee; + struct TALER_Amount deposit_fee; + struct GNUNET_TIME_Absolute wire_deadline; + struct GNUNET_HashCode h_contract; + struct TALER_MerchantPublicKeyP merchant_pub; + struct TALER_CoinSpendPublicKeyP coin_pub; + uint64_t transaction_id; + uint64_t serial_id; + json_t *wire; + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_uint64 ("serial_id", + &serial_id), + TALER_PQ_result_spec_uint64 ("transaction_id", + &transaction_id), + TALER_PQ_result_spec_amount ("amount_with_fee", + &amount_with_fee), + TALER_PQ_result_spec_amount ("deposit_fee", + &deposit_fee), + TALER_PQ_result_spec_absolute_time ("wire_deadline", + &wire_deadline), + TALER_PQ_result_spec_auto_from_type ("h_contract", + &h_contract), + TALER_PQ_result_spec_auto_from_type ("merchant_pub", + &merchant_pub), + TALER_PQ_result_spec_auto_from_type ("coin_pub", + &coin_pub), + TALER_PQ_result_spec_json ("wire", + &wire), + TALER_PQ_result_spec_end + }; + if (GNUNET_OK != + TALER_PQ_extract_result (result, rs, 0)) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + ret = deposit_cb (deposit_cb_cls, + serial_id, + &merchant_pub, + &coin_pub, + &amount_with_fee, + &deposit_fee, + transaction_id, + &h_contract, + wire_deadline, + wire); + TALER_PQ_cleanup_result (rs); + PQclear (result); + } + return (GNUNET_OK == ret) ? 1 : 0; +} + + +/** + * Obtain information about other pending deposits for the same + * destination. Those deposits must not already be "done". + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param session connection to the database + * @param h_wire destination of the wire transfer + * @param merchant_pub public key of the merchant + * @param deposit_cb function to call for each deposit + * @param deposit_cb_cls closure for @a deposit_cb + * @param limit maximum number of matching deposits to return + * @return number of rows processed, 0 if none exist, + * #GNUNET_SYSERR on error + */ +static int +postgres_iterate_matching_deposits (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *h_wire, + const struct TALER_MerchantPublicKeyP *merchant_pub, + TALER_MINTDB_DepositIterator deposit_cb, + void *deposit_cb_cls, + uint32_t limit) +{ + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_auto_from_type (merchant_pub), + TALER_PQ_query_param_auto_from_type (h_wire), + TALER_PQ_query_param_uint32 (&limit), + TALER_PQ_query_param_end + }; + PGresult *result; + unsigned int i; + unsigned int n; result = TALER_PQ_exec_prepared (session->conn, - "deposits_iterate", + "deposits_iterate_matching", params); if (PGRES_TUPLES_OK != PQresultStatus (result)) { BREAK_DB_ERR (result); PQclear (result); - postgres_rollback (cls, session); return GNUNET_SYSERR; } if (0 == (n = PQntuples (result))) { PQclear (result); - postgres_rollback (cls, session); return 0; } + if (n > limit) + n = limit; for (i=0;iget_reserve_history = &postgres_get_reserve_history; plugin->free_reserve_history = &common_free_reserve_history; plugin->have_deposit = &postgres_have_deposit; - plugin->iterate_deposits = &postgres_iterate_deposits; + plugin->mark_deposit_tiny = &postgres_mark_deposit_tiny; + plugin->mark_deposit_done = &postgres_mark_deposit_done; + plugin->get_ready_deposit = &postgres_get_ready_deposit; + plugin->iterate_matching_deposits = &postgres_iterate_matching_deposits; plugin->insert_deposit = &postgres_insert_deposit; plugin->get_refresh_session = &postgres_get_refresh_session; plugin->create_refresh_session = &postgres_create_refresh_session; -- cgit v1.2.3 From 9a45742fe4b819c22b0b0d8832ff4a0a07096a90 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 27 Jan 2016 16:46:51 +0100 Subject: adding stubs for wire prepare data functions --- src/include/taler_mintdb_plugin.h | 1 - src/mintdb/plugin_mintdb_postgres.c | 72 +++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 11b30583..ba09ea42 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -1376,7 +1376,6 @@ struct TALER_MINTDB_Plugin const struct TALER_Amount *transfer_value); - /** * Function called to insert wire transfer commit data into the DB. * diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 5ebf8dc7..1e48a2ec 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -3987,6 +3987,75 @@ postgres_insert_aggregation_tracking (void *cls, } +/** + * Function called to insert wire transfer commit data into the DB. + * + * @param cls closure + * @param session database connection + * @param type type fo the wire transfer (i.e. "sepa") + * @param buf buffer with wire transfer preparation data + * @param buf_size number of bytes in @a buf + * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors + */ +static int +postgres_wire_prepare_data_insert (void *cls, + struct TALER_MINTDB_Session *session, + const char *type, + const char *buf, + size_t buf_size) +{ + GNUNET_break (0); // not implemented + return GNUNET_SYSERR; +} + + +/** + * Function called to mark wire transfer commit data as finished. + * + * @param cls closure + * @param session database connection + * @param type type fo the wire transfer (i.e. "sepa") + * @param buf buffer with wire transfer preparation data + * @param buf_size number of bytes in @a buf + * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors + */ +static int +postgres_wire_prepare_data_mark_finished (void *cls, + struct TALER_MINTDB_Session *session, + const char *type, + const char *buf, + size_t buf_size) +{ + GNUNET_break (0); // not implemented + return GNUNET_SYSERR; +} + + +/** + * Function called to iterate over unfinished wire transfer + * preparation data. Fetches at most one item. + * + * @param cls closure + * @param session database connection + * @param type type fo the wire transfer (i.e. "sepa") + * @param cb function to call for ONE unfinished item + * @param cb_cls closure for @a cb + * @return #GNUNET_OK on success, + * #GNUNET_NO if there are no entries, + * #GNUNET_SYSERR on DB errors + */ +static int +postgres_wire_prepare_data_iterate (void *cls, + struct TALER_MINTDB_Session *session, + const char *type, + TALER_MINTDB_WirePreparationCallback cb, + void *cb_cls) +{ + GNUNET_break (0); // not implemented + return GNUNET_SYSERR; +} + + /** * Initialize Postgres database subsystem. * @@ -4064,6 +4133,9 @@ libtaler_plugin_mintdb_postgres_init (void *cls) plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer; plugin->wire_lookup_deposit_wtid = &postgres_wire_lookup_deposit_wtid; plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking; + plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert; + plugin->wire_prepare_data_mark_finished = &postgres_wire_prepare_data_mark_finished; + plugin->wire_prepare_data_iterate = &postgres_wire_prepare_data_iterate; return plugin; } -- cgit v1.2.3 From b7215299d8fee1b57c1aa9f5bbb8f8202cfd0369 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 27 Jan 2016 17:09:08 +0100 Subject: adding SQL for prewire data storage --- src/include/taler_mintdb_plugin.h | 2 ++ src/mintdb/plugin_mintdb_postgres.c | 72 +++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) (limited to 'src') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index ba09ea42..1d40f293 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -626,11 +626,13 @@ typedef void * Callback with data about a prepared transaction. * * @param cls closure + * @param rowid row identifier used to mark prepared transaction as done * @param buf transaction data that was persisted, NULL on error * @param buf_size number of bytes in @a buf, 0 on error */ typedef void (*TALER_MINTDB_WirePreparationCallback) (void *cls, + unsigned long long rowid, const char *buf, size_t buf_size); diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 1e48a2ec..0f377969 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -477,6 +477,18 @@ postgres_create_tables (void *cls, SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_deposit_index " "ON aggregation_tracking(coin_pub,h_contract,h_wire,transaction_id,merchant_pub)"); + /* This table contains the pre-commit data for + wire transfers the mint is about to execute. */ + SQLEXEC("CREATE TABLE IF NOT EXISTS prewire " + "(serial_id BIGSERIAL PRIMARY KEY" + ",type TEXT NOT NULL" + ",finished BOOLEAN NOT NULL DEFAULT false" + ",buf BYTEA NOT NULL" + ")"); + /* Index for prepare_data_iterate statement */ + SQLEXEC_INDEX("CREATE INDEX prepare_iteration_index " + "ON prewire(type,finished)"); + #undef SQLEXEC #undef SQLEXEC_INDEX @@ -588,6 +600,7 @@ postgres_prepare (PGconn *db_conn) ") VALUES " "($1, $2, $3, $4, $5);", 5, NULL); + /* Used in #postgres_reserves_update() when the reserve is updated */ PREPARE ("reserve_update", "UPDATE reserves" @@ -597,6 +610,7 @@ postgres_prepare (PGconn *db_conn) ",current_balance_frac=$3 " "WHERE current_balance_curr=$4 AND reserve_pub=$5", 5, NULL); + /* Used in #postgres_reserves_in_insert() to store transaction details */ PREPARE ("reserves_in_add_transaction", "INSERT INTO reserves_in " @@ -609,6 +623,7 @@ postgres_prepare (PGconn *db_conn) ") VALUES " "($1, $2, $3, $4, $5, $6);", 6, NULL); + /* Used in #postgres_get_reserve_history() to obtain inbound transactions for a reserve */ PREPARE ("reserves_in_get_transactions", @@ -621,6 +636,7 @@ postgres_prepare (PGconn *db_conn) " FROM reserves_in" " WHERE reserve_pub=$1", 1, NULL); + /* Used in #postgres_insert_withdraw_info() to store the signature of a blinded coin with the blinded coin's details before returning it during /reserve/withdraw. We store @@ -645,6 +661,7 @@ postgres_prepare (PGconn *db_conn) ") VALUES " "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);", 12, NULL); + /* Used in #postgres_get_withdraw_info() to locate the response for a /reserve/withdraw request using the hash of the blinded message. Used to @@ -665,6 +682,7 @@ postgres_prepare (PGconn *db_conn) " FROM reserves_out" " WHERE h_blind_ev=$1", 1, NULL); + /* Used during #postgres_get_reserve_history() to obtain all of the /reserve/withdraw operations that have been performed on a given reserve. (i.e. to @@ -685,6 +703,7 @@ postgres_prepare (PGconn *db_conn) " FROM reserves_out" " WHERE reserve_pub=$1;", 1, NULL); + /* Used in #postgres_get_refresh_session() to fetch high-level information about a refresh session */ PREPARE ("get_refresh_session", @@ -695,6 +714,7 @@ postgres_prepare (PGconn *db_conn) " FROM refresh_sessions " " WHERE session_hash=$1 ", 1, NULL); + /* Used in #postgres_create_refresh_session() to store high-level information about a refresh session */ PREPARE ("insert_refresh_session", @@ -706,6 +726,7 @@ postgres_prepare (PGconn *db_conn) ") VALUES " "($1, $2, $3, $4);", 4, NULL); + /* Used in #postgres_get_known_coin() to fetch the denomination public key and signature for a coin known to the mint. */ @@ -716,6 +737,7 @@ postgres_prepare (PGconn *db_conn) " FROM known_coins" " WHERE coin_pub=$1", 1, NULL); + /* Used in #postgres_insert_known_coin() to store the denomination public key and signature for a coin known to the mint. */ @@ -727,6 +749,7 @@ postgres_prepare (PGconn *db_conn) ") VALUES " "($1,$2,$3);", 3, NULL); + /* Store information about the desired denominations for a refresh operation, used in #postgres_insert_refresh_order() */ PREPARE ("insert_refresh_order", @@ -737,6 +760,7 @@ postgres_prepare (PGconn *db_conn) ") VALUES " "($1, $2, $3);", 3, NULL); + /* Obtain information about the desired denominations for a refresh operation, used in #postgres_get_refresh_order() */ PREPARE ("get_refresh_order", @@ -762,6 +786,7 @@ postgres_prepare (PGconn *db_conn) ") VALUES " "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);", 10, NULL); + /* Used in #postgres_get_refresh_melt to obtain information about melted coins */ PREPARE ("get_refresh_melt", @@ -777,6 +802,7 @@ postgres_prepare (PGconn *db_conn) " FROM refresh_melts" " WHERE session_hash=$1 AND oldcoin_index=$2", 2, NULL); + /* Query the 'refresh_melts' by coin public key */ PREPARE ("get_refresh_melt_by_coin", "SELECT" @@ -792,6 +818,7 @@ postgres_prepare (PGconn *db_conn) " FROM refresh_melts" " WHERE coin_pub=$1", 1, NULL); + /* Used in #postgres_insert_refresh_commit_links() to store commitments */ PREPARE ("insert_refresh_commit_link", @@ -804,6 +831,7 @@ postgres_prepare (PGconn *db_conn) ") VALUES " "($1, $2, $3, $4, $5);", 5, NULL); + /* Used in #postgres_get_refresh_commit_links() to retrieve original commitments during /refresh/reveal */ PREPARE ("get_refresh_commit_link", @@ -813,6 +841,7 @@ postgres_prepare (PGconn *db_conn) " FROM refresh_commit_link" " WHERE session_hash=$1 AND cnc_index=$2 AND oldcoin_index=$3", 3, NULL); + /* Used in #postgres_insert_refresh_commit_coins() to store coin commitments. */ PREPARE ("insert_refresh_commit_coin", @@ -825,6 +854,7 @@ postgres_prepare (PGconn *db_conn) ") VALUES " "($1, $2, $3, $4, $5);", 5, NULL); + /* Used in #postgres_get_refresh_commit_coins() to retrieve the original coin envelopes, to either be verified or signed. */ @@ -835,6 +865,7 @@ postgres_prepare (PGconn *db_conn) " FROM refresh_commit_coin" " WHERE session_hash=$1 AND cnc_index=$2 AND newcoin_index=$3", 3, NULL); + /* Store information about a /deposit the mint is to execute. Used in #postgres_insert_deposit(). */ PREPARE ("insert_deposit", @@ -861,6 +892,7 @@ postgres_prepare (PGconn *db_conn) "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10," " $11, $12, $13, $14, $15, $16, $17, $18);", 18, NULL); + /* Fetch an existing deposit request, used to ensure idempotency during /deposit processing. Used in #postgres_have_deposit(). */ PREPARE ("get_deposit", @@ -880,6 +912,7 @@ postgres_prepare (PGconn *db_conn) " (merchant_pub=$3)" " )", 3, NULL); + /* Fetch an existing deposit request. Used in #postgres_wire_lookup_deposit_wtid(). */ PREPARE ("get_deposit_for_wtid", @@ -924,6 +957,7 @@ postgres_prepare (PGconn *db_conn) " ORDER BY execution_time ASC" " LIMIT 1;", 0, NULL); + /* Used in #postgres_iterate_matching_deposits() */ PREPARE ("deposits_iterate_matching", "SELECT" @@ -946,12 +980,14 @@ postgres_prepare (PGconn *db_conn) " ORDER BY execution_time ASC" " LIMIT $3", 3, NULL); + /* Used in #postgres_mark_deposit_tiny() */ PREPARE ("mark_deposit_tiny", "UPDATE deposits" " SET tiny=true" " WHERE serial_id=$1", 1, NULL); + /* Used in #postgres_mark_deposit_done() */ PREPARE ("mark_deposit_done", "UPDATE deposits" @@ -993,6 +1029,7 @@ postgres_prepare (PGconn *db_conn) ") VALUES " "($1, $2, $3)", 3, NULL); + /* Used in #postgres_get_link_data_list(). We use the session_hash to obtain the "noreveal_index" for that session, and then select the encrypted link vectors (link_vector_enc) and the @@ -1018,6 +1055,7 @@ postgres_prepare (PGconn *db_conn) " AND ro.newcoin_index=rc.newcoin_index" " AND rcc.cnc_index=rs.noreveal_index", 1, NULL); + /* Used in #postgres_get_transfer(). Given the public key of a melted coin, we obtain the corresponding encrypted link secret and the transfer public key. This is done by first finding @@ -1036,6 +1074,7 @@ postgres_prepare (PGconn *db_conn) " AND rm.oldcoin_index = rcl.oldcoin_index" " AND rcl.cnc_index=rs.noreveal_index", 1, NULL); + /* Used in #postgres_lookup_wire_transfer */ PREPARE ("lookup_transactions", "SELECT" @@ -1057,6 +1096,7 @@ postgres_prepare (PGconn *db_conn) " FROM aggregation_tracking" " WHERE wtid_raw=$1", 1, NULL); + /* Used in #postgres_wire_lookup_deposit_wtid */ PREPARE ("lookup_deposit_wtid", "SELECT" @@ -1079,6 +1119,7 @@ postgres_prepare (PGconn *db_conn) " transaction_id=$4 AND" " merchant_pub=$5", 5, NULL); + /* Used in #postgres_insert_aggregation_tracking */ PREPARE ("insert_aggregation_tracking", "INSERT INTO aggregation_tracking " @@ -1102,6 +1143,37 @@ postgres_prepare (PGconn *db_conn) "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)", 16, NULL); + + /* Used in #postgres_wire_prepare_data_insert() to store + wire transfer information before actually committing it with the bank */ + PREPARE ("wire_prepare_data_insert", + "INSERT INTO prewire " + "(type" + ",buf" + ") VALUES " + "($1, $2)", + 2, NULL); + + /* Used in #postgres_wire_prepare_data_mark_finished() */ + PREPARE ("wire_prepare_data_mark_done", + "UPDATE prewire" + " SET finished=true" + " WHERE serial_id=$1", + 1, NULL); + + /* Used in #postgres_wire_prepare_data_iterate() */ + PREPARE ("wire_prepare_data_iterate", + "SELECT" + " serial_id" + ",buf" + " FROM prewire" + " WHERE" + " type=$1 AND" + " finished=false" + " ORDER BY serial_id ASC" + " LIMIT 1", + 1, NULL); + return GNUNET_OK; #undef PREPARE } -- cgit v1.2.3 From 649879b0b6413166e4e24c7bd52993576b72d266 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 27 Jan 2016 18:28:52 +0100 Subject: implementing prepare data postgres functions for #4141 --- src/include/taler_mintdb_plugin.h | 22 +++---- src/include/taler_pq_lib.h | 12 ++-- src/mintdb/plugin_mintdb_postgres.c | 128 +++++++++++++++++++++++++++++------- 3 files changed, 121 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 1d40f293..7c48114b 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -1383,7 +1383,7 @@ struct TALER_MINTDB_Plugin * * @param cls closure * @param session database connection - * @param type type fo the wire transfer (i.e. "sepa") + * @param type type of the wire transfer (i.e. "sepa") * @param buf buffer with wire transfer preparation data * @param buf_size number of bytes in @a buf * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors @@ -1401,21 +1401,17 @@ struct TALER_MINTDB_Plugin * * @param cls closure * @param session database connection - * @param type type fo the wire transfer (i.e. "sepa") - * @param buf buffer with wire transfer preparation data - * @param buf_size number of bytes in @a buf + * @param rowid which entry to mark as finished * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors */ int (*wire_prepare_data_mark_finished)(void *cls, struct TALER_MINTDB_Session *session, - const char *type, - const char *buf, - size_t buf_size); + unsigned long long rowid); /** - * Function called to iterate over unfinished wire transfer + * Function called to get an unfinished wire transfer * preparation data. Fetches at most one item. * * @param cls closure @@ -1428,11 +1424,11 @@ struct TALER_MINTDB_Plugin * #GNUNET_SYSERR on DB errors */ int - (*wire_prepare_data_iterate)(void *cls, - struct TALER_MINTDB_Session *session, - const char *type, - TALER_MINTDB_WirePreparationCallback cb, - void *cb_cls); + (*wire_prepare_data_get)(void *cls, + struct TALER_MINTDB_Session *session, + const char *type, + TALER_MINTDB_WirePreparationCallback cb, + void *cb_cls); }; diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h index e0ca429e..c9a9ebdc 100644 --- a/src/include/taler_pq_lib.h +++ b/src/include/taler_pq_lib.h @@ -156,7 +156,7 @@ struct TALER_PQ_QueryParam * @param x pointer to the query parameter to pass */ struct TALER_PQ_QueryParam -TALER_PQ_query_param_amount_nbo(const struct TALER_AmountNBO *x); +TALER_PQ_query_param_amount_nbo (const struct TALER_AmountNBO *x); /** @@ -168,7 +168,7 @@ TALER_PQ_query_param_amount_nbo(const struct TALER_AmountNBO *x); * @param x pointer to the query parameter to pass */ struct TALER_PQ_QueryParam -TALER_PQ_query_param_amount(const struct TALER_Amount *x); +TALER_PQ_query_param_amount (const struct TALER_Amount *x); /** @@ -178,7 +178,7 @@ TALER_PQ_query_param_amount(const struct TALER_Amount *x); * @param x the query parameter to pass. */ struct TALER_PQ_QueryParam -TALER_PQ_query_param_rsa_public_key(const struct GNUNET_CRYPTO_rsa_PublicKey *x); +TALER_PQ_query_param_rsa_public_key (const struct GNUNET_CRYPTO_rsa_PublicKey *x); /** @@ -188,7 +188,7 @@ TALER_PQ_query_param_rsa_public_key(const struct GNUNET_CRYPTO_rsa_PublicKey *x) * @param x the query parameter to pass */ struct TALER_PQ_QueryParam -TALER_PQ_query_param_rsa_signature(const struct GNUNET_CRYPTO_rsa_Signature *x); +TALER_PQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_rsa_Signature *x); /** @@ -198,7 +198,7 @@ TALER_PQ_query_param_rsa_signature(const struct GNUNET_CRYPTO_rsa_Signature *x); * @param x pointer to the query parameter to pass */ struct TALER_PQ_QueryParam -TALER_PQ_query_param_absolute_time(const struct GNUNET_TIME_Absolute *x); +TALER_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x); /** @@ -208,7 +208,7 @@ TALER_PQ_query_param_absolute_time(const struct GNUNET_TIME_Absolute *x); * @param x pointer to the query parameter to pass */ struct TALER_PQ_QueryParam -TALER_PQ_query_param_absolute_time_nbo(const struct GNUNET_TIME_AbsoluteNBO *x); +TALER_PQ_query_param_absolute_time_nbo (const struct GNUNET_TIME_AbsoluteNBO *x); /** diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 0f377969..ee63b006 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -1161,8 +1161,8 @@ postgres_prepare (PGconn *db_conn) " WHERE serial_id=$1", 1, NULL); - /* Used in #postgres_wire_prepare_data_iterate() */ - PREPARE ("wire_prepare_data_iterate", + /* Used in #postgres_wire_prepare_data_get() */ + PREPARE ("wire_prepare_data_get", "SELECT" " serial_id" ",buf" @@ -4064,7 +4064,7 @@ postgres_insert_aggregation_tracking (void *cls, * * @param cls closure * @param session database connection - * @param type type fo the wire transfer (i.e. "sepa") + * @param type type of the wire transfer (i.e. "sepa") * @param buf buffer with wire transfer preparation data * @param buf_size number of bytes in @a buf * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors @@ -4076,8 +4076,24 @@ postgres_wire_prepare_data_insert (void *cls, const char *buf, size_t buf_size) { - GNUNET_break (0); // not implemented - return GNUNET_SYSERR; + PGresult *result; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_fixed_size (type, strlen (type) + 1), + TALER_PQ_query_param_fixed_size (buf, buf_size), + TALER_PQ_query_param_end + }; + + result = TALER_PQ_exec_prepared (session->conn, + "wire_prepare_data_insert", + params); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + return GNUNET_OK; } @@ -4086,25 +4102,38 @@ postgres_wire_prepare_data_insert (void *cls, * * @param cls closure * @param session database connection - * @param type type fo the wire transfer (i.e. "sepa") - * @param buf buffer with wire transfer preparation data - * @param buf_size number of bytes in @a buf + * @param rowid which entry to mark as finished * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors */ static int postgres_wire_prepare_data_mark_finished (void *cls, struct TALER_MINTDB_Session *session, - const char *type, - const char *buf, - size_t buf_size) + unsigned long long rowid) { - GNUNET_break (0); // not implemented - return GNUNET_SYSERR; + uint64_t serial_id = rowid; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_uint64 (&serial_id), + TALER_PQ_query_param_end + }; + PGresult *result; + + result = TALER_PQ_exec_prepared (session->conn, + "wire_prepare_data_mark_done", + params); + if (PGRES_COMMAND_OK != + PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + return GNUNET_OK; } /** - * Function called to iterate over unfinished wire transfer + * Function called to get an unfinished wire transfer * preparation data. Fetches at most one item. * * @param cls closure @@ -4117,14 +4146,69 @@ postgres_wire_prepare_data_mark_finished (void *cls, * #GNUNET_SYSERR on DB errors */ static int -postgres_wire_prepare_data_iterate (void *cls, - struct TALER_MINTDB_Session *session, - const char *type, - TALER_MINTDB_WirePreparationCallback cb, - void *cb_cls) +postgres_wire_prepare_data_get (void *cls, + struct TALER_MINTDB_Session *session, + const char *type, + TALER_MINTDB_WirePreparationCallback cb, + void *cb_cls) { - GNUNET_break (0); // not implemented - return GNUNET_SYSERR; + PGresult *result; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_fixed_size (type, strlen (type) + 1), + TALER_PQ_query_param_end + }; + + result = TALER_PQ_exec_prepared (session->conn, + "wire_prepare_data_get", + params); + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + QUERY_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + if (0 == PQntuples (result)) + { + PQclear (result); + return GNUNET_NO; + } + if (1 != PQntuples (result)) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + + { + uint64_t serial_id; + void *buf = NULL; + size_t buf_size; + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_uint64 ("serial_id", + &serial_id), + TALER_PQ_result_spec_variable_size ("buf", + &buf, + &buf_size), + TALER_PQ_result_spec_end + }; + + if (GNUNET_OK != + TALER_PQ_extract_result (result, + rs, + 0)) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + cb (cb_cls, + serial_id, + buf, + buf_size); + TALER_PQ_cleanup_result (rs); + } + PQclear (result); + return GNUNET_OK; } @@ -4207,7 +4291,7 @@ libtaler_plugin_mintdb_postgres_init (void *cls) plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking; plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert; plugin->wire_prepare_data_mark_finished = &postgres_wire_prepare_data_mark_finished; - plugin->wire_prepare_data_iterate = &postgres_wire_prepare_data_iterate; + plugin->wire_prepare_data_get = &postgres_wire_prepare_data_get; return plugin; } -- cgit v1.2.3 From 4506b4878ffb4f827cd0be0ab7587ed3654edd0d Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 27 Jan 2016 19:56:57 +0100 Subject: fix name of member to order by --- src/mintdb/plugin_mintdb_postgres.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index ee63b006..fc204f5e 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -954,7 +954,7 @@ postgres_prepare (PGconn *db_conn) " WHERE" " tiny=false AND" " done=false" - " ORDER BY execution_time ASC" + " ORDER BY wire_deadline ASC" " LIMIT 1;", 0, NULL); @@ -977,7 +977,7 @@ postgres_prepare (PGconn *db_conn) " merchant_pub=$1 AND" " h_wire=$2 AND" " done=false" - " ORDER BY execution_time ASC" + " ORDER BY wire_deadline ASC" " LIMIT $3", 3, NULL); -- cgit v1.2.3 From 46d9cc367bdc9bf8cda7ae12e78ea0a2e0853d36 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 28 Jan 2016 03:58:21 +0100 Subject: finishing core logic for #4141, but untested --- src/include/taler_mint_service.h | 5 +- src/include/taler_mintdb_plugin.h | 6 +- src/include/taler_signatures.h | 14 - src/mint-lib/mint_api_deposit_wtid.c | 9 +- src/mint/taler-mint-aggregator.c | 661 ++++++++++++++++++++++++++++++++-- src/mint/taler-mint-httpd_db.c | 3 - src/mint/taler-mint-httpd_responses.c | 5 - src/mint/taler-mint-httpd_responses.h | 2 +- src/mintdb/plugin_mintdb_postgres.c | 25 +- 9 files changed, 640 insertions(+), 90 deletions(-) (limited to 'src') diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h index b151cb00..1502edfb 100644 --- a/src/include/taler_mint_service.h +++ b/src/include/taler_mint_service.h @@ -1173,8 +1173,6 @@ struct TALER_MINT_DepositWtidHandle; * 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 mint could - * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK) */ typedef void (*TALER_MINT_DepositWtidCallback)(void *cls, @@ -1182,8 +1180,7 @@ typedef void json_t *json, const struct TALER_WireTransferIdentifierRawP *wtid, struct GNUNET_TIME_Absolute execution_time, - const struct TALER_Amount *coin_contribution, - const struct TALER_Amount *total_amount); + const struct TALER_Amount *coin_contribution); /** diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 7c48114b..d2cc3d76 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -583,7 +583,6 @@ typedef void * @param coin_contribution how much did the coin we asked about * contribute to the total transfer value? (deposit value including fee) * @param coin_fee how much did the mint charge for the deposit fee - * @param total_amount how much was the total wire transfer? * @param execution_time when was the transaction done, or * when we expect it to be done (if @a wtid was NULL) */ @@ -592,7 +591,6 @@ typedef void const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_Amount *coin_contribution, const struct TALER_Amount *coin_fee, - const struct TALER_Amount *total_amount, struct GNUNET_TIME_Absolute execution_time); @@ -1360,7 +1358,6 @@ struct TALER_MINTDB_Plugin * @param coin_pub which public key was this payment about * @param coin_value amount contributed by this coin in total * @param coin_fee deposit fee charged by mint for this coin - * @param transfer_value total amount of the wire transfer * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors */ int @@ -1374,8 +1371,7 @@ struct TALER_MINTDB_Plugin struct GNUNET_TIME_Absolute execution_time, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_Amount *coin_value, - const struct TALER_Amount *coin_fee, - const struct TALER_Amount *transfer_value); + const struct TALER_Amount *coin_fee); /** diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 85c681da..2526597e 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -953,20 +953,6 @@ struct TALER_ConfirmWirePS */ struct TALER_AmountNBO coin_contribution; - /** - * The total amount the mint transferred in the transaction. - * Note that we may be aggregating multiple coin's @e coin_contribution - * values into a single wire transfer, so this value may be larger - * than that of @e coin_contribution. It may also be smaller, as - * @e coin_contribution may be say "1.123456" but the wire unit may - * be rounded down, i.e. to "1.12" (depending on the transfer method). - * - * Note that the mint books the deltas from rounding down as profit, - * so aggregating transfers is a good thing for the merchant (as it - * reduces rounding down expenses). - */ - struct TALER_AmountNBO total_amount; - }; GNUNET_NETWORK_STRUCT_END diff --git a/src/mint-lib/mint_api_deposit_wtid.c b/src/mint-lib/mint_api_deposit_wtid.c index 50f9c55d..d29f406e 100644 --- a/src/mint-lib/mint_api_deposit_wtid.c +++ b/src/mint-lib/mint_api_deposit_wtid.c @@ -148,9 +148,7 @@ handle_deposit_wtid_finished (void *cls, const struct TALER_WireTransferIdentifierRawP *wtid = NULL; struct GNUNET_TIME_Absolute execution_time = GNUNET_TIME_UNIT_FOREVER_ABS; const struct TALER_Amount *coin_contribution = NULL; - const struct TALER_Amount *total_amount = NULL; struct TALER_Amount coin_contribution_s; - struct TALER_Amount total_amount_s; dwh->job = NULL; json = MAC_download_get_result (&dwh->db, @@ -166,7 +164,6 @@ handle_deposit_wtid_finished (void *cls, MAJ_spec_fixed_auto ("wtid", &dwh->depconf.wtid), MAJ_spec_absolute_time ("execution_time", &execution_time), MAJ_spec_amount ("coin_contribution", &coin_contribution_s), - MAJ_spec_amount ("total_amount", &total_amount_s), MAJ_spec_end }; @@ -183,9 +180,6 @@ handle_deposit_wtid_finished (void *cls, TALER_amount_hton (&dwh->depconf.coin_contribution, &coin_contribution_s); coin_contribution = &coin_contribution_s; - TALER_amount_hton (&dwh->depconf.total_amount, - &total_amount_s); - total_amount = &total_amount_s; if (GNUNET_OK != verify_deposit_wtid_signature_ok (dwh, json)) @@ -244,8 +238,7 @@ handle_deposit_wtid_finished (void *cls, json, wtid, execution_time, - coin_contribution, - total_amount); + coin_contribution); json_decref (json); TALER_MINT_deposit_wtid_cancel (dwh); } diff --git a/src/mint/taler-mint-aggregator.c b/src/mint/taler-mint-aggregator.c index ee0f6ab2..5e05c867 100644 --- a/src/mint/taler-mint-aggregator.c +++ b/src/mint/taler-mint-aggregator.c @@ -18,6 +18,10 @@ * @file taler-mint-aggregator.c * @brief Process that aggregates outgoing transactions and executes them * @author Christian Grothoff + * + * TODO: + * - simplify global_ret: make it a global! + * - handle shutdown more nicely (call 'cancel' method on wire transfers) */ #include "platform.h" #include @@ -62,6 +66,16 @@ static struct TALER_WIRE_Plugin *wire_plugin; */ static struct GNUNET_SCHEDULER_Task *task; +/** + * Limit on the number of transactions we aggregate at once. Note + * that the limit must be big enough to ensure that when transactions + * of the smallest possible unit are aggregated, they do surpass the + * "tiny" threshold beyond which we never trigger a wire transaction! + * + * TODO: make configurable (via config file or command line option) + */ +static unsigned int aggregation_limit = 10000; + /** * Load configuration parameters for the mint @@ -143,11 +157,86 @@ mint_serve_process_config (const char *mint_directory) } +/** + * Information about one aggregation process to + * be executed. + */ +struct AggregationUnit +{ + /** + * Public key of the merchant. + */ + struct TALER_MerchantPublicKeyP merchant_pub; + + /** + * Total amount to be transferred. + */ + struct TALER_Amount total_amount; + + /** + * Hash of @e wire. + */ + struct GNUNET_HashCode h_wire; + + /** + * Wire transfer identifier we use. + */ + struct TALER_WireTransferIdentifierRawP wtid; + + /** + * Row ID of the transaction that started it all. + */ + unsigned long long row_id; + + /** + * The current time. + */ + struct GNUNET_TIME_Absolute execution_time; + + /** + * Wire details of the merchant. + */ + json_t *wire; + + /** + * Database session for all of our transactions. + */ + struct TALER_MINTDB_Session *session; + + /** + * Wire preparation handle. + */ + struct TALER_WIRE_PrepareHandle *ph; + + /** + * Array of #aggregation_limit row_ids from the + * aggregation. + */ + unsigned long long *additional_rows; + + /** + * Pointer to global return value. Closure for #run(). + */ + int *global_ret; + + /** + * Offset specifying how many #additional_rows are in use. + */ + unsigned int rows_offset; + + /** + * Set to #GNUNET_YES if we have to abort due to failure. + */ + int failed; + +}; + + /** * Function called with details about deposits that have been made, * with the goal of executing the corresponding wire transaction. * - * @param cls closure + * @param cls closure with the `struct AggregationUnit` * @param row_id identifies database entry * @param merchant_pub public key of the merchant * @param coin_pub public key of the coin @@ -172,20 +261,187 @@ deposit_cb (void *cls, struct GNUNET_TIME_Absolute wire_deadline, const json_t *wire) { - /* FIXME: compute aggregates, etc. */ + struct AggregationUnit *au = cls; + + au->merchant_pub = *merchant_pub; + if (GNUNET_OK != + TALER_amount_subtract (&au->total_amount, + amount_with_fee, + deposit_fee)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fatally malformed record at %llu\n", + row_id); + return GNUNET_SYSERR; + } + au->row_id = row_id; + au->wire = (json_t *) wire; + au->execution_time = GNUNET_TIME_absolute_get (); + TALER_hash_json (au->wire, + &au->h_wire); + json_incref (au->wire); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &au->wtid, + sizeof (au->wtid)); + if (GNUNET_OK != + db_plugin->insert_aggregation_tracking (db_plugin->cls, + au->session, + &au->wtid, + merchant_pub, + &au->h_wire, + h_contract, + transaction_id, + au->execution_time, + coin_pub, + amount_with_fee, + deposit_fee)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + db_plugin->mark_deposit_done (db_plugin->cls, + au->session, + row_id)) + { + GNUNET_break (0); + au->failed = GNUNET_YES; + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + + +/** + * Function called with details about another deposit we + * can aggregate into an existing aggregation unit. + * + * @param cls closure with the `struct AggregationUnit` + * @param row_id identifies database entry + * @param merchant_pub public key of the merchant + * @param coin_pub public key of the coin + * @param amount_with_fee amount that was deposited including fee + * @param deposit_fee amount the mint gets to keep as transaction fees + * @param transaction_id unique transaction ID chosen by the merchant + * @param h_contract hash of the contract between merchant and customer + * @param wire_deadline by which the merchant adviced that he would like the + * wire transfer to be executed + * @param wire wire details for the merchant + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static int +aggregate_cb (void *cls, + unsigned long long row_id, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *amount_with_fee, + const struct TALER_Amount *deposit_fee, + uint64_t transaction_id, + const struct GNUNET_HashCode *h_contract, + struct GNUNET_TIME_Absolute wire_deadline, + const json_t *wire) +{ + struct AggregationUnit *au = cls; + struct TALER_Amount delta; + + GNUNET_break (0 == + memcmp (&au->merchant_pub, + merchant_pub, + sizeof (struct TALER_MerchantPublicKeyP))); + /* compute contribution of this coin after fees */ + if (GNUNET_OK != + TALER_amount_subtract (&delta, + amount_with_fee, + deposit_fee)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fatally malformed record at %llu\n", + row_id); + return GNUNET_SYSERR; + } + /* add to total */ + if (GNUNET_OK != + TALER_amount_add (&au->total_amount, + &au->total_amount, + &delta)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Overflow or currency incompatibility during aggregation at %llu\n", + row_id); + /* Skip this one, but keep going! */ + return GNUNET_OK; + } + if (au->rows_offset >= aggregation_limit) + { + /* Bug: we asked for at most #aggregation_limit results! */ + GNUNET_break (0); + /* Skip this one, but keep going. */ + return GNUNET_OK; + } + if (NULL == au->additional_rows) + au->additional_rows = GNUNET_new_array (aggregation_limit, + unsigned long long); + /* "append" to our list of rows */ + au->additional_rows[au->rows_offset++] = row_id; + /* insert into aggregation tracking table */ + if (GNUNET_OK != + db_plugin->insert_aggregation_tracking (db_plugin->cls, + au->session, + &au->wtid, + merchant_pub, + &au->h_wire, + h_contract, + transaction_id, + au->execution_time, + coin_pub, + amount_with_fee, + deposit_fee)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + db_plugin->mark_deposit_done (db_plugin->cls, + au->session, + row_id)) + { + GNUNET_break (0); + au->failed = GNUNET_YES; + return GNUNET_SYSERR; + } return GNUNET_OK; } /** - * Main work function that queries the DB and executes transactions. + * Function to be called with the prepared transfer data. + * + * @param cls closure with the `struct AggregationUnit` + * @param buf transaction data to persist, NULL on error + * @param buf_size number of bytes in @a buf, 0 on error + */ +static void +prepare_cb (void *cls, + const char *buf, + size_t buf_size); + + +/** + * Main work function that queries the DB and aggregates transactions + * into larger wire transfers. + * + * @param cls pointer to an `int` which we will return from main() + * @param tc scheduler context */ static void -run (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +run_aggregation (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { int *global_ret = cls; struct TALER_MINTDB_Session *session; + struct AggregationUnit *au; + unsigned int i; int ret; if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) @@ -207,50 +463,399 @@ run (void *cls, *global_ret = GNUNET_SYSERR; return; } + au = GNUNET_new (struct AggregationUnit); + au->session = session; ret = db_plugin->get_ready_deposit (db_plugin->cls, session, &deposit_cb, - NULL); - // FIXME: handle 0 == ret... - + au); if (GNUNET_OK != ret) + { + GNUNET_free (au); + db_plugin->rollback (db_plugin->cls, + session); + if (0 != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to execute deposit iteration!\n"); + *global_ret = GNUNET_SYSERR; + return; + } + /* nothing to do, sleep for a minute and try again */ + task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, + &run_aggregation, + global_ret); + return; + } + /* Now try to find other deposits to aggregate */ + ret = db_plugin->iterate_matching_deposits (db_plugin->cls, + session, + &au->h_wire, + &au->merchant_pub, + &aggregate_cb, + au, + aggregation_limit); + if ( (GNUNET_SYSERR == ret) || + (GNUNET_YES == au->failed) ) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to execute deposit iteration!\n"); + GNUNET_free_non_null (au->additional_rows); + GNUNET_free (au); + db_plugin->rollback (db_plugin->cls, + session); *global_ret = GNUNET_SYSERR; + return; + } + /* Round to the unit supported by the wire transfer method */ + GNUNET_assert (GNUNET_SYSERR != + wire_plugin->amount_round (wire_plugin->cls, + &au->total_amount)); + /* Check if after rounding down, we still have an amount to transfer */ + if ( (0 == au->total_amount.value) && + (0 == au->total_amount.fraction) ) + { + /* Rollback ongoing transaction, as we will not use the respective + WTID and thus need to remove the tracking data */ + db_plugin->rollback (db_plugin->cls, + session); + /* Start another transaction to mark all* of the selected deposits + *as minor! */ + if (GNUNET_OK != + db_plugin->start (db_plugin->cls, + session)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to start database transaction!\n"); + *global_ret = GNUNET_SYSERR; + GNUNET_free_non_null (au->additional_rows); + GNUNET_free (au); + return; + } + /* Mark transactions by row_id as minor */ + ret = GNUNET_OK; + if (GNUNET_OK != + db_plugin->mark_deposit_tiny (db_plugin->cls, + session, + au->row_id)) + ret = GNUNET_SYSERR; + else + for (i=0;irows_offset;i++) + if (GNUNET_OK != + db_plugin->mark_deposit_tiny (db_plugin->cls, + session, + au->additional_rows[i])) + ret = GNUNET_SYSERR; + /* commit */ + if (GNUNET_OK != + db_plugin->commit (db_plugin->cls, + session)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to commit database transaction!\n"); + } + GNUNET_free_non_null (au->additional_rows); + GNUNET_free (au); + /* start again */ + task = GNUNET_SCHEDULER_add_now (&run_aggregation, + global_ret); + return; + } + au->global_ret = global_ret; + au->ph = wire_plugin->prepare_wire_transfer (wire_plugin->cls, + au->wire, + &au->total_amount, + &au->wtid, + &prepare_cb, + au); + /* FIXME: currently we have no clean-up plan on + shutdown to call prepare_wire_transfer_cancel! + Maybe make 'au' global? */ + if (NULL == au->ph) + { + GNUNET_break (0); /* why? how to best recover? */ db_plugin->rollback (db_plugin->cls, session); + GNUNET_free_non_null (au->additional_rows); + GNUNET_free (au); + /* start again */ + task = GNUNET_SCHEDULER_add_now (&run_aggregation, + global_ret); return; } - /* FIXME: finish aggregate computation */ - /* wire_plugin->prepare_wire_transfer () -- ASYNC! */ - /* db_plugin->wire_prepare_data_insert () -- transactional! */ - /* db_plugin->XXX () -- mark transactions selected for aggregate as finished */ + /* otherwise we continue with #prepare_cb(), see below */ +} + - /* then finally: commit! */ +/** + * Execute the wire transfers that we have committed to + * do. + * + * @param cls pointer to an `int` which we will return from main() + * @param tc scheduler context + */ +static void +run_transfers (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Function to be called with the prepared transfer data. + * + * @param cls closure with the `struct AggregationUnit` + * @param buf transaction data to persist, NULL on error + * @param buf_size number of bytes in @a buf, 0 on error + */ +static void +prepare_cb (void *cls, + const char *buf, + size_t buf_size) +{ + struct AggregationUnit *au = cls; + int *global_ret = au->global_ret; + struct TALER_MINTDB_Session *session = au->session; + + GNUNET_free_non_null (au->additional_rows); + GNUNET_free (au); + if (NULL == buf) + { + GNUNET_break (0); /* why? how to best recover? */ + db_plugin->rollback (db_plugin->cls, + session); + /* start again */ + task = GNUNET_SCHEDULER_add_now (&run_aggregation, + global_ret); + return; + } + + /* Commit our intention to execute the wire transfer! */ + if (GNUNET_OK != + db_plugin->wire_prepare_data_insert (db_plugin->cls, + session, + mint_wireformat, + buf, + buf_size)) + { + GNUNET_break (0); /* why? how to best recover? */ + db_plugin->rollback (db_plugin->cls, + session); + /* start again */ + task = GNUNET_SCHEDULER_add_now (&run_aggregation, + global_ret); + return; + } + + /* Now we can finally commit the overall transaction, as we are + again consistent if all of this passes. */ if (GNUNET_OK != db_plugin->commit (db_plugin->cls, session)) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Failed to commit database transaction!\n"); + /* try again */ + task = GNUNET_SCHEDULER_add_now (&run_aggregation, + global_ret); + return; } - /* While possible, run 2nd type of transaction: - db_plugin->start() - - select pre-commit data from DB: - db_plugin->wire_prepare_data_iterate () - - execute wire transfer (successfully!) - wire_plugin->execute_wire_transfer() # ASYNC! - db_plugin->wire_prepare_data_mark_finished () - db_plugin->insert_aggregation_tracking () - db_plugin->commit() - */ + /* run alternative task: actually do wire transfer! */ + task = GNUNET_SCHEDULER_add_now (&run_transfers, + &global_ret); +} + + +/** + * Data we keep to #run_transfers(). + */ +struct WirePrepareData +{ + + /** + * Database session for all of our transactions. + */ + struct TALER_MINTDB_Session *session; + + /** + * Wire execution handle. + */ + struct TALER_WIRE_ExecuteHandle *eh; + + /** + * Pointer to global return value. Closure for #run(). + */ + int *global_ret; + + + /** + * Row ID of the transfer. + */ + unsigned long long row_id; +}; - task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS /* FIXME: adjust! */, - &run, - global_ret); + +/** + * Function called with the result from the execute step. + * + * @param cls closure with the `struct WirePrepareData` + * @param success #GNUNET_OK on success, #GNUNET_SYSERR on failure + * @param emsg NULL on success, otherwise an error message + */ +static void +wire_confirm_cb (void *cls, + int success, + const char *emsg) +{ + struct WirePrepareData *wpd = cls; + int *global_ret = wpd->global_ret; + struct TALER_MINTDB_Session *session = wpd->session; + + wpd->eh = NULL; + if (GNUNET_SYSERR == success) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Wire transaction failed: %s\n", + emsg); + db_plugin->rollback (db_plugin->cls, + session); + *global_ret = GNUNET_SYSERR; + GNUNET_free (wpd); + return; + } + if (GNUNET_OK != + db_plugin->wire_prepare_data_mark_finished (db_plugin->cls, + session, + wpd->row_id)) + { + GNUNET_break (0); /* why!? */ + db_plugin->rollback (db_plugin->cls, + session); + *global_ret = GNUNET_SYSERR; + GNUNET_free (wpd); + return; + } + GNUNET_free (wpd); + if (GNUNET_OK != + db_plugin->commit (db_plugin->cls, + session)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Failed to commit database transaction!\n"); + /* try again */ + task = GNUNET_SCHEDULER_add_now (&run_aggregation, + global_ret); + return; + } + /* continue with #run_transfers(), just to guard + against the unlikely case that there are more. */ + task = GNUNET_SCHEDULER_add_now (&run_transfers, + &global_ret); + +} + + +/** + * Callback with data about a prepared transaction. + * + * @param cls closure with the `struct WirePrepareData` + * @param rowid row identifier used to mark prepared transaction as done + * @param buf transaction data that was persisted, NULL on error + * @param buf_size number of bytes in @a buf, 0 on error + */ +static void +wire_prepare_cb (void *cls, + unsigned long long rowid, + const char *buf, + size_t buf_size) +{ + struct WirePrepareData *wpd = cls; + int *global_ret = wpd->global_ret; + + wpd->row_id = rowid; + wpd->eh = wire_plugin->execute_wire_transfer (wire_plugin->cls, + buf, + buf_size, + &wire_confirm_cb, + wpd); + /* FIXME: currently we have no clean-up plan on + shutdown to call execute_wire_transfer_cancel! + Maybe make 'wpd' global? */ + if (NULL == wpd->eh) + { + GNUNET_break (0); /* why? how to best recover? */ + db_plugin->rollback (db_plugin->cls, + wpd->session); + *global_ret = GNUNET_SYSERR; + GNUNET_free (wpd); + return; + } +} + + +/** + * Execute the wire transfers that we have committed to + * do. + * + * @param cls pointer to an `int` which we will return from main() + * @param tc scheduler context + */ +static void +run_transfers (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *global_ret = cls; + int ret; + struct WirePrepareData *wpd; + struct TALER_MINTDB_Session *session; + + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + if (NULL == (session = db_plugin->get_session (db_plugin->cls, + GNUNET_NO))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to obtain database session!\n"); + *global_ret = GNUNET_SYSERR; + return; + } + if (GNUNET_OK != + db_plugin->start (db_plugin->cls, + session)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to start database transaction!\n"); + *global_ret = GNUNET_SYSERR; + return; + } + wpd = GNUNET_new (struct WirePrepareData); + wpd->session = session; + wpd->global_ret = global_ret; + ret = db_plugin->wire_prepare_data_get (db_plugin->cls, + session, + mint_wireformat, + &wire_prepare_cb, + wpd); + if (GNUNET_SYSERR == ret) + { + GNUNET_break (0); /* why? how to best recover? */ + db_plugin->rollback (db_plugin->cls, + session); + *global_ret = GNUNET_SYSERR; + GNUNET_free (wpd); + return; + } + if (GNUNET_NO == ret) + { + /* no more prepared wire transfers, go back to aggregation! */ + db_plugin->rollback (db_plugin->cls, + session); + task = GNUNET_SCHEDULER_add_now (&run_aggregation, + global_ret); + GNUNET_free (wpd); + return; + } + /* otherwise, continues in #wire_prepare_cb() */ } @@ -299,7 +904,7 @@ main (int argc, return 1; } - GNUNET_SCHEDULER_run (&run, &ret); + GNUNET_SCHEDULER_run (&run_transfers, &ret); TALER_MINTDB_plugin_unload (db_plugin); TALER_WIRE_plugin_unload (wire_plugin); diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index c39cbbcf..b93ead3a 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -1804,7 +1804,6 @@ struct DepositWtidContext * @param coin_contribution how much did the coin we asked about * contribute to the total transfer value? (deposit value including fee) * @param coin_fee how much did the mint charge for the deposit fee - * @param total_amount how much was the total wire transfer? * @param execution_time when was the transaction done, or * when we expect it to be done (if @a wtid was NULL); * #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown @@ -1815,7 +1814,6 @@ handle_wtid_data (void *cls, const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_Amount *coin_contribution, const struct TALER_Amount *coin_fee, - const struct TALER_Amount *total_amount, struct GNUNET_TIME_Absolute execution_time) { struct DepositWtidContext *ctx = cls; @@ -1843,7 +1841,6 @@ handle_wtid_data (void *cls, &ctx->h_wire, &ctx->coin_pub, &coin_delta, - total_amount, ctx->transaction_id, wtid, execution_time); diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index 041f694b..2ebd0d33 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -1097,7 +1097,6 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection, * @param coin_pub public key of the coin * @param coin_contribution how much did the coin we asked about * contribute to the total transfer value? (deposit value minus fee) - * @param total_amount how much was the total wire transfer? * @param transaction_id merchant transaction identifier * @param wtid raw wire transfer identifier * @param exec_time execution time of the wire transfer @@ -1109,7 +1108,6 @@ TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection, const struct GNUNET_HashCode *h_wire, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_Amount *coin_contribution, - const struct TALER_Amount *total_amount, uint64_t transaction_id, const struct TALER_WireTransferIdentifierRawP *wtid, struct GNUNET_TIME_Absolute exec_time) @@ -1128,8 +1126,6 @@ TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection, cw.execution_time = GNUNET_TIME_absolute_hton (exec_time); TALER_amount_hton (&cw.coin_contribution, coin_contribution); - TALER_amount_hton (&cw.total_amount, - total_amount); TMH_KS_sign (&cw.purpose, &pub, &sig); @@ -1140,7 +1136,6 @@ TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection, sizeof (*wtid)), "execution_time", TALER_json_from_abs (exec_time), "coin_contribution", TALER_json_from_amount (coin_contribution), - "total_amount", TALER_json_from_amount (total_amount), "mint_sig", TALER_json_from_data (&sig, sizeof (sig)), "mint_pub", TALER_json_from_data (&pub, diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h index caad2904..a0396c8a 100644 --- a/src/mint/taler-mint-httpd_responses.h +++ b/src/mint/taler-mint-httpd_responses.h @@ -280,6 +280,7 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection, * @param h_contract hash of the contract * @param h_wire hash of wire account details * @param coin_pub public key of the coin + * @param coin_contribution contribution of this coin to the total amount transferred * @param transaction_id merchant transaction identifier * @param wtid raw wire transfer identifier * @param exec_time execution time of the wire transfer @@ -291,7 +292,6 @@ TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection, const struct GNUNET_HashCode *h_wire, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_Amount *coin_contribution, - const struct TALER_Amount *total_amount, uint64_t transaction_id, const struct TALER_WireTransferIdentifierRawP *wtid, struct GNUNET_TIME_Absolute exec_time); diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index fc204f5e..2ab3e81a 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -466,9 +466,6 @@ postgres_create_tables (void *cls, ",coin_fee_val INT8 NOT NULL" ",coin_fee_frac INT4 NOT NULL" ",coin_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" - ",transfer_total_val INT8 NOT NULL" - ",transfer_total_frac INT4 NOT NULL" - ",transfer_total_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" ")"); /* Index for lookup_transactions statement on wtid */ SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index " @@ -1090,9 +1087,6 @@ postgres_prepare (PGconn *db_conn) ",coin_fee_val" ",coin_fee_frac" ",coin_fee_curr" - ",transfer_total_val" - ",transfer_total_frac" - ",transfer_total_curr" " FROM aggregation_tracking" " WHERE wtid_raw=$1", 1, NULL); @@ -1108,9 +1102,6 @@ postgres_prepare (PGconn *db_conn) ",coin_fee_val" ",coin_fee_frac" ",coin_fee_curr" - ",transfer_total_val" - ",transfer_total_frac" - ",transfer_total_curr" " FROM aggregation_tracking" " WHERE" " coin_pub=$1 AND" @@ -1136,12 +1127,9 @@ postgres_prepare (PGconn *db_conn) ",coin_fee_val" ",coin_fee_frac" ",coin_fee_curr" - ",transfer_total_val" - ",transfer_total_frac" - ",transfer_total_curr" ") VALUES " - "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)", - 16, NULL); + "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)", + 13, NULL); /* Used in #postgres_wire_prepare_data_insert() to store @@ -3950,7 +3938,6 @@ postgres_wire_lookup_deposit_wtid (void *cls, NULL, &coin_amount, &coin_fee, - NULL, exec_time); PQclear (result); return GNUNET_YES; @@ -3967,13 +3954,11 @@ postgres_wire_lookup_deposit_wtid (void *cls, struct GNUNET_TIME_Absolute exec_time; struct TALER_Amount coin_amount; struct TALER_Amount coin_fee; - struct TALER_Amount transaction_amount; struct TALER_PQ_ResultSpec rs[] = { TALER_PQ_result_spec_auto_from_type ("wtid_raw", &wtid), TALER_PQ_result_spec_absolute_time ("execution_time", &exec_time), TALER_PQ_result_spec_amount ("coin_amount", &coin_amount), TALER_PQ_result_spec_amount ("coin_fee", &coin_fee), - TALER_PQ_result_spec_amount ("transfer_total", &transaction_amount), TALER_PQ_result_spec_end }; if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)) @@ -3986,7 +3971,6 @@ postgres_wire_lookup_deposit_wtid (void *cls, &wtid, &coin_amount, &coin_fee, - &transaction_amount, exec_time); } PQclear (result); @@ -4007,7 +3991,6 @@ postgres_wire_lookup_deposit_wtid (void *cls, * @param coin_pub which public key was this payment about * @param coin_value amount contributed by this coin in total * @param coin_fee deposit fee charged by mint for this coin - * @param transfer_value total amount of the wire transfer * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors */ static int @@ -4021,8 +4004,7 @@ postgres_insert_aggregation_tracking (void *cls, struct GNUNET_TIME_Absolute execution_time, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_Amount *coin_value, - const struct TALER_Amount *coin_fee, - const struct TALER_Amount *transfer_value) + const struct TALER_Amount *coin_fee) { struct TALER_PQ_QueryParam params[] = { TALER_PQ_query_param_auto_from_type (h_contract), @@ -4034,7 +4016,6 @@ postgres_insert_aggregation_tracking (void *cls, TALER_PQ_query_param_absolute_time (&execution_time), TALER_PQ_query_param_amount (coin_value), TALER_PQ_query_param_amount (coin_fee), - TALER_PQ_query_param_amount (transfer_value), TALER_PQ_query_param_end }; PGresult *result; -- cgit v1.2.3 From 67bc16d9ebb8d68851c34e18155b6648bce2c06a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 29 Jan 2016 11:39:45 +0100 Subject: update bank-lib to reflect API discussions --- src/bank-lib/bank_api_admin.c | 8 ++++---- src/include/taler_bank_service.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/bank-lib/bank_api_admin.c b/src/bank-lib/bank_api_admin.c index ed205eeb..bfcf16a2 100644 --- a/src/bank-lib/bank_api_admin.c +++ b/src/bank-lib/bank_api_admin.c @@ -150,7 +150,7 @@ handle_admin_add_incoming_finished (void *cls, * @param reserve_pub public key of the reserve * @param amount amount that was deposited * @param execution_date when did we receive the amount - * @param wire wire details + * @param account_no account number (53 bits at most) * @param res_cb the callback to call when the final result for this request is available * @param res_cb_cls closure for the above callback * @return NULL @@ -161,7 +161,7 @@ struct TALER_BANK_AdminAddIncomingHandle * TALER_BANK_admin_add_incoming (struct TALER_BANK_Context *bank, const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_Amount *amount, - const json_t *wire, + uint64_t account_no, TALER_BANK_AdminAddIncomingResultCallback res_cb, void *res_cb_cls) { @@ -170,11 +170,11 @@ TALER_BANK_admin_add_incoming (struct TALER_BANK_Context *bank, CURL *eh; admin_obj = json_pack ("{s:o, s:o," /* reserve_pub/amount */ - " s:O}", /* execution_Date/wire */ + " s:I}", /* execution_Date/wire */ "wtid", TALER_json_from_data (wtid, sizeof (*wtid)), "amount", TALER_json_from_amount (amount), - "wire", wire); + "account", (json_int_t) account_no); aai = GNUNET_new (struct TALER_BANK_AdminAddIncomingHandle); aai->bank = bank; aai->cb = res_cb; diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h index b292a7bc..a4f33fc9 100644 --- a/src/include/taler_bank_service.h +++ b/src/include/taler_bank_service.h @@ -133,7 +133,7 @@ typedef void * @param reserve_pub public key of the reserve * @param amount amount that was deposited * @param execution_date when did we receive the amount - * @param wire wire details + * @param account_no account number (53 bits at most) * @param res_cb the callback to call when the final result for this request is available * @param res_cb_cls closure for the above callback * @return NULL @@ -144,7 +144,7 @@ struct TALER_BANK_AdminAddIncomingHandle * TALER_BANK_admin_add_incoming (struct TALER_BANK_Context *bank, const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_Amount *amount, - const json_t *wire, + uint64_t account_no, TALER_BANK_AdminAddIncomingResultCallback res_cb, void *res_cb_cls); -- cgit v1.2.3 From ae45b7ac9579cc9fcb9c3008162e07b694eb52f8 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 29 Jan 2016 14:24:18 +0100 Subject: fix testcase --- src/bank-lib/Makefile.am | 12 + src/bank-lib/bank_api_context.c | 2 +- src/bank-lib/test_bank_api.c | 2184 ++------------------------------------- 3 files changed, 71 insertions(+), 2127 deletions(-) (limited to 'src') diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am index 326fdd5a..2f44adad 100644 --- a/src/bank-lib/Makefile.am +++ b/src/bank-lib/Makefile.am @@ -31,3 +31,15 @@ libtalerbank_la_LIBADD += -lgnurl endif endif +check_PROGRAMS = \ + test_bank_api + +TESTS = \ + $(check_PROGRAMS) + +test_bank_api_SOURCES = \ + test_bank_api.c +test_bank_api_LDADD = \ + libtalerbank.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetutil diff --git a/src/bank-lib/bank_api_context.c b/src/bank-lib/bank_api_context.c index f54e9e70..a47b4072 100644 --- a/src/bank-lib/bank_api_context.c +++ b/src/bank-lib/bank_api_context.c @@ -395,7 +395,7 @@ TALER_BANK_fini (struct TALER_BANK_Context *ctx) * @return the full URI to use with cURL */ char * -MAH_path_to_url (struct TALER_BANK_Context *h, +BAC_path_to_url (struct TALER_BANK_Context *h, const char *path) { char *url; diff --git a/src/bank-lib/test_bank_api.c b/src/bank-lib/test_bank_api.c index 3c1747a1..b14f523b 100644 --- a/src/bank-lib/test_bank_api.c +++ b/src/bank-lib/test_bank_api.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016 GNUnet e.V. + Copyright (C) 2016 GNUnet e.V. 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 @@ -16,7 +16,6 @@ /** * @file bank/test_bank_api.c * @brief testcase to test bank's HTTP API interface - * @author Sree Harsha Totakura * @author Christian Grothoff */ #include "platform.h" @@ -26,26 +25,12 @@ #include #include -/** - * Is the configuration file is set to include wire format 'test'? - */ -#define WIRE_TEST 1 - -/** - * Is the configuration file is set to include wire format 'sepa'? - */ -#define WIRE_SEPA 1 /** * Main execution context for the main loop. */ static struct TALER_BANK_Context *ctx; -/** - * Handle to access the bank. - */ -static struct TALER_BANK_Handle *bank; - /** * Task run on shutdown. */ @@ -75,102 +60,7 @@ enum OpCode /** * Add funds to a reserve by (faking) incoming wire transfer. */ - OC_ADMIN_ADD_INCOMING, - - /** - * Check status of a reserve. - */ - OC_WITHDRAW_STATUS, - - /** - * Withdraw a coin from a reserve. - */ - OC_WITHDRAW_SIGN, - - /** - * Deposit a coin (pay with it). - */ - OC_DEPOSIT, - - /** - * Melt a (set of) coins. - */ - OC_REFRESH_MELT, - - /** - * Complete melting session by withdrawing melted coins. - */ - OC_REFRESH_REVEAL, - - /** - * Verify bank's /refresh/link by linking original private key to - * results from #OC_REFRESH_REVEAL step. - */ - OC_REFRESH_LINK, - - /** - * Verify the bank's /wire-method. - */ - OC_WIRE, - - /** - * Verify bank's /wire/deposits method. - */ - OC_WIRE_DEPOSITS, - - /** - * Verify bank's /deposit/wtid method. - */ - OC_DEPOSIT_WTID - -}; - - -/** - * 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_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_BANK_DenomPublicKey *pk; - - /** - * Set (by the interpreter) to the bank'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; + OC_ADMIN_ADD_INCOMING }; @@ -207,28 +97,21 @@ struct Command struct { - /** - * Label to another admin_add_incoming command if we - * should deposit into an existing reserve, NULL if - * a fresh reserve should be created. - */ - const char *reserve_reference; - /** * String describing the amount to add to the reserve. */ const char *amount; /** - * Wire details (JSON). + * Account number. */ - const char *wire; + uint64_t account_no; /** - * Set (by the interpreter) to the reserve's private key - * we used to fill the reserve. + * Wire transfer identifier to use. Initialized to + * a random value. */ - struct TALER_ReservePrivateKeyP reserve_priv; + struct TALER_WireTransferIdentifierRawP wtid; /** * Set to the API's handle during the operation. @@ -237,303 +120,6 @@ struct Command } admin_add_incoming; - /** - * Information for a #OC_WITHDRAW_STATUS command. - */ - struct - { - - /** - * Label to the #OC_ADMIN_ADD_INCOMING command which - * created the reserve. - */ - const char *reserve_reference; - - /** - * Set to the API's handle during the operation. - */ - struct TALER_BANK_ReserveStatusHandle *wsh; - - /** - * Expected reserve balance. - */ - const char *expected_balance; - - } reserve_status; - - /** - * Information for a #OC_WITHDRAW_SIGN command. - */ - struct - { - - /** - * Which reserve should we withdraw from? - */ - const char *reserve_reference; - - /** - * String describing the denomination value we should withdraw. - * A corresponding denomination key must exist in the bank's - * offerings. Can be NULL if @e pk is set instead. - */ - const char *amount; - - /** - * If @e amount is NULL, this specifies the denomination key to - * use. Otherwise, this will be set (by the interpreter) to the - * denomination PK matching @e amount. - */ - const struct TALER_BANK_DenomPublicKey *pk; - - /** - * Set (by the interpreter) to the bank'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; - - /** - * Blinding key used for the operation. - */ - struct TALER_DenominationBlindingKey blinding_key; - - /** - * Withdraw handle (while operation is running). - */ - struct TALER_BANK_ReserveWithdrawHandle *wsh; - - } reserve_withdraw; - - /** - * Information for a #OC_DEPOSIT command. - */ - struct - { - - /** - * Amount to deposit. - */ - const char *amount; - - /** - * Reference to a reserve_withdraw operation for a coin to - * be used for the /deposit operation. - */ - 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; - - /** - * JSON string describing the contract between the two parties. - */ - const char *contract; - - /** - * Transaction ID to use. - */ - uint64_t transaction_id; - - /** - * Relative time (to add to 'now') to compute the refund deadline. - * Zero for no refunds. - */ - struct GNUNET_TIME_Relative refund_deadline; - - /** - * Set (by the interpreter) to a fresh private key of the merchant, - * if @e refund_deadline is non-zero. - */ - struct TALER_MerchantPrivateKeyP merchant_priv; - - /** - * Deposit handle while operation is running. - */ - struct TALER_BANK_DepositHandle *dh; - - } 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_BANK_DenomPublicKey **fresh_pks; - - /** - * Melt handle while operation is running. - */ - struct TALER_BANK_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 bank. - */ - 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_BANK_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_BANK_RefreshLinkHandle *rlh; - - /** - * Which of the melted coins should be used for the linkage? - */ - unsigned int coin_idx; - - } refresh_link; - - /** - * Information for the /wire command. - */ - struct { - - /** - * Handle to the wire request. - */ - struct TALER_BANK_WireHandle *wh; - - /** - * Format we expect to see, others will be *ignored*. - */ - const char *format; - - } wire; - - /** - * Information for the /wire/deposits's command. - */ - struct { - - /** - * Handle to the wire deposits request. - */ - struct TALER_BANK_WireDepositsHandle *wdh; - - /** - * Reference to a /deposit/wtid command. If set, we use the - * WTID from that command. - */ - const char *wtid_ref; - - /** - * WTID to use (used if @e wtid_ref is NULL). - */ - struct TALER_WireTransferIdentifierRawP wtid; - - /* TODO: may want to add list of deposits we expected - to see aggregated here in the future. */ - - } wire_deposits; - - /** - * Information for the /deposit/wtid command. - */ - struct { - - /** - * Handle to the deposit wtid request. - */ - struct TALER_BANK_DepositWtidHandle *dwh; - - /** - * Which /deposit operation should we obtain WTID data for? - */ - const char *deposit_ref; - - /** - * What is the expected total amount? Only used if - * @e expected_response_code was #MHD_HTTP_OK. - */ - struct TALER_Amount total_amount_expected; - - /** - * Wire transfer identifier, set if #MHD_HTTP_OK was the response code. - */ - struct TALER_WireTransferIdentifierRawP wtid; - - } deposit_wtid; - } details; }; @@ -604,6 +190,7 @@ fail (struct InterpreterState *is) } +#if 0 /** * Find a command by label. * @@ -622,813 +209,53 @@ find_command (const struct InterpreterState *is, { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Attempt to lookup command for empty label\n"); - return NULL; - } - for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++) - if ( (NULL != cmd->label) && - (0 == strcmp (cmd->label, - label)) ) - return cmd; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command not found: %s\n", - label); - return NULL; -} - - -/** - * Run the main interpreter loop that performs bank operations. - * - * @param cls contains the `struct InterpreterState` - * @param tc scheduler context - */ -static void -interpreter_run (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc); - - -/** - * 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 bank's reply is bogus (fails to follow the protocol) - * @param full_response full response from the bank (for logging, in case of errors) - */ -static void -add_incoming_cb (void *cls, - unsigned int http_status, - json_t *full_response) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.admin_add_incoming.aih = NULL; - if (MHD_HTTP_OK != http_status) - { - GNUNET_break (0); - fail (is); - return; - } - is->ip++; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); -} - - -/** - * Check if the given historic event @a h corresponds to the given - * command @a cmd. - * - * @param h event in history - * @param cmd an #OC_ADMIN_ADD_INCOMING command - * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not - */ -static int -compare_admin_add_incoming_history (const struct TALER_BANK_ReserveHistory *h, - const struct Command *cmd) -{ - struct TALER_Amount amount; - - if (TALER_BANK_RTT_DEPOSIT != h->type) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.admin_add_incoming.amount, - &amount)); - if (0 != TALER_amount_cmp (&amount, - &h->amount)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Check if the given historic event @a h corresponds to the given - * command @a cmd. - * - * @param h event in history - * @param cmd an #OC_WITHDRAW_SIGN command - * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not - */ -static int -compare_reserve_withdraw_history (const struct TALER_BANK_ReserveHistory *h, - const struct Command *cmd) -{ - struct TALER_Amount amount; - struct TALER_Amount amount_with_fee; - - if (TALER_BANK_RTT_WITHDRAWAL != h->type) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.reserve_withdraw.amount, - &amount)); - GNUNET_assert (GNUNET_OK == - TALER_amount_add (&amount_with_fee, - &amount, - &cmd->details.reserve_withdraw.pk->fee_withdraw)); - if (0 != TALER_amount_cmp (&amount_with_fee, - &h->amount)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Function called with the result of a /reserve/status request. - * - * @param cls closure with the interpreter state - * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request - * 0 if the bank's reply is bogus (fails to follow the protocol) - * @param[in] json original response in JSON format (useful only for diagnostics) - * @param balance current balance in the reserve, NULL on error - * @param history_length number of entries in the transaction history, 0 on error - * @param history detailed transaction history, NULL on error - */ -static void -reserve_status_cb (void *cls, - unsigned int http_status, - json_t *json, - const struct TALER_Amount *balance, - unsigned int history_length, - const struct TALER_BANK_ReserveHistory *history) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - struct Command *rel; - unsigned int i; - unsigned int j; - struct TALER_Amount amount; - - cmd->details.reserve_status.wsh = 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); - GNUNET_break (0); - json_dumpf (json, stderr, 0); - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - /* FIXME: note that history events may come in a different - order than the commands. However, for now this works... */ - j = 0; - for (i=0;iip;i++) - { - switch ((rel = &is->commands[i])->oc) - { - case OC_ADMIN_ADD_INCOMING: - if ( ( (NULL != rel->label) && - (0 == strcmp (cmd->details.reserve_status.reserve_reference, - rel->label) ) ) || - ( (NULL != rel->details.admin_add_incoming.reserve_reference) && - (0 == strcmp (cmd->details.reserve_status.reserve_reference, - rel->details.admin_add_incoming.reserve_reference) ) ) ) - { - if (GNUNET_OK != - compare_admin_add_incoming_history (&history[j], - rel)) - { - GNUNET_break (0); - fail (is); - return; - } - j++; - } - break; - case OC_WITHDRAW_SIGN: - if (0 == strcmp (cmd->details.reserve_status.reserve_reference, - rel->details.reserve_withdraw.reserve_reference)) - { - if (GNUNET_OK != - compare_reserve_withdraw_history (&history[j], - rel)) - { - GNUNET_break (0); - fail (is); - return; - } - j++; - } - break; - default: - /* unreleated, just skip */ - break; - } - } - if (j != history_length) - { - GNUNET_break (0); - fail (is); - return; - } - if (NULL != cmd->details.reserve_status.expected_balance) - { - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.reserve_status.expected_balance, - &amount)); - if (0 != TALER_amount_cmp (&amount, - balance)) - { - GNUNET_break (0); - fail (is); - return; - } - } - break; - default: - /* Unsupported status code (by test harness) */ - GNUNET_break (0); - break; - } - is->ip++; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); -} - - -/** - * Function called upon completion of our /reserve/withdraw 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 bank's reply is bogus (fails to follow the protocol) - * @param sig signature over the coin, NULL on error - * @param full_response full response from the bank (for logging, in case of errors) - */ -static void -reserve_withdraw_cb (void *cls, - unsigned int http_status, - const struct TALER_DenominationSignature *sig, - json_t *full_response) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.reserve_withdraw.wsh = 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); - json_dumpf (full_response, stderr, 0); - GNUNET_break (0); - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - if (NULL == sig) - { - GNUNET_break (0); - fail (is); - return; - } - cmd->details.reserve_withdraw.sig.rsa_signature - = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); - break; - case MHD_HTTP_PAYMENT_REQUIRED: - /* nothing to check */ - break; - default: - /* Unsupported status code (by test harness) */ - GNUNET_break (0); - break; - } - is->ip++; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); -} - - -/** - * 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 bank's reply is bogus (fails to follow the protocol) - * @param obj the received JSON reply, should be kept as proof (and, in case of errors, - * be forwarded to the customer) - */ -static void -deposit_cb (void *cls, - unsigned int http_status, - json_t *obj) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.deposit.dh = NULL; - 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); - json_dumpf (obj, stderr, 0); - fail (is); - return; - } - 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 bank's reply is bogus (fails to follow the protocol) - * @param noreveal_index choice by the bank in the cut-and-choose protocol, - * UINT16_MAX on error - * @param full_response full response from the bank (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); - json_dumpf (full_response, stderr, 0); - 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 bank'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 bank (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); - json_dumpf (full_response, stderr, 0); - 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;idetails.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 bank'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 bank (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; - unsigned int j; - unsigned int found; - - 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); - json_dumpf (full_response, stderr, 0); - 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;idetails.refresh_reveal.fresh_coins[j]; - 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)) ) - { - found++; - break; - } - } - if (found != num_coins) - { - fprintf (stderr, - "Only %u/%u coins match expectations\n", - found, - num_coins); - GNUNET_break (0); - fail (is); - return; - } - break; - default: - break; - } - is->ip++; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); -} - - -/** - * Find denomination key matching the given amount. - * - * @param keys array of keys to search - * @param amount coin value to look for - * @return NULL if no matching key was found - */ -static const struct TALER_BANK_DenomPublicKey * -find_pk (const struct TALER_BANK_Keys *keys, - const struct TALER_Amount *amount) -{ - unsigned int i; - struct GNUNET_TIME_Absolute now; - struct TALER_BANK_DenomPublicKey *pk; - char *str; - - now = GNUNET_TIME_absolute_get (); - for (i=0;inum_denom_keys;i++) - { - pk = &keys->denom_keys[i]; - if ( (0 == TALER_amount_cmp (amount, - &pk->value)) && - (now.abs_value_us >= pk->valid_from.abs_value_us) && - (now.abs_value_us < pk->withdraw_valid_until.abs_value_us) ) - return pk; - } - /* do 2nd pass to check if expiration times are to blame for failure */ - str = TALER_amount_to_string (amount); - for (i=0;inum_denom_keys;i++) - { - pk = &keys->denom_keys[i]; - if ( (0 == TALER_amount_cmp (amount, - &pk->value)) && - ( (now.abs_value_us < pk->valid_from.abs_value_us) || - (now.abs_value_us > pk->withdraw_valid_until.abs_value_us) ) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Have denomination key for `%s', but with wrong expiration range %llu vs [%llu,%llu)\n", - str, - now.abs_value_us, - pk->valid_from.abs_value_us, - pk->withdraw_valid_until.abs_value_us); - GNUNET_free (str); - return NULL; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No denomination key for amount %s found\n", - str); - GNUNET_free (str); - return NULL; -} - - -/** - * Callbacks called with the result(s) of a - * wire format inquiry request to the bank. - * - * The callback is invoked multiple times, once for each supported @a - * method. Finally, it is invoked one more time with cls/0/NULL/NULL - * to indicate the end of the iteration. If any request fails to - * generate a valid response from the bank, @a http_status will also - * be zero and the iteration will also end. Thus, the iteration - * always ends with a final call with an @a http_status of 0. If the - * @a http_status is already 0 on the first call, then the response to - * the /wire request was invalid. Later, clients can tell the - * difference between @a http_status of 0 indicating a failed - * /wire/method request and a regular end of the iteration by @a - * method being non-NULL. If the bank simply correctly asserts that - * it does not support any methods, @a method will be NULL but the @a - * http_status will be #MHD_HTTP_OK for the first call (followed by a - * cls/0/NULL/NULL call to signal the end of the iteration). - * - * @param cls closure with the interpreter state - * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful request; - * 0 if the bank's reply is bogus (fails to follow the protocol) - * @param method wire format method supported, i.e. "test" or "sepa", or NULL - * if already the /wire request failed. - * @param obj the received JSON reply, if successful this should be the wire - * format details as provided by /wire/METHOD/, or NULL if the - * reply was not in JSON format (in this case, the client might - * want to do an HTTP request to /wire/METHOD/ with a browser to - * provide more information to the user about the @a method). - */ -static void -wire_cb (void *cls, - unsigned int http_status, - const char *method, - json_t *obj) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - if (0 == http_status) - { - /* 0 always signals the end of the iteration */ - cmd->details.wire.wh = NULL; - } - else if ( (NULL != method) && - (0 != strcasecmp (method, - cmd->details.wire.format)) ) - { - /* not the method we care about, skip */ - return; - } - if (cmd->expected_response_code != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s/%s\n", - http_status, - cmd->label, - method); - json_dumpf (obj, stderr, 0); - fail (is); - return; - } - if (0 == http_status) - { - /* end of iteration, move to next command */ - is->ip++; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); - return; - } - /* For now, we only support to be called only once - with a "positive" result; so we switch to an - expected value of 0 for the 2nd iteration */ - cmd->expected_response_code = 0; -} - - -/** - * 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 bank protocol violation - * @param json original json reply (may include signatures, those have then been - * validated already) - * @param wtid extracted wire transfer identifier, or NULL if the bank could - * not provide any (set only if @a http_status is #MHD_HTTP_OK) - * @param total_amount total amount of the wire transfer, or NULL if the bank could - * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK) - * @param details_length length of the @a details array - * @param details array with details about the combined transactions - */ -static void -wire_deposits_cb (void *cls, - unsigned int http_status, - json_t *json, - const struct GNUNET_HashCode *h_wire, - const struct TALER_Amount *total_amount, - unsigned int details_length, - const struct TALER_WireDepositDetails *details) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - const struct Command *ref; - - cmd->details.wire_deposits.wdh = NULL; - ref = find_command (is, - cmd->details.wire_deposits.wtid_ref); - 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); - json_dumpf (json, stderr, 0); - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - if (0 != TALER_amount_cmp (total_amount, - &ref->details.deposit_wtid.total_amount_expected)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Total amount missmatch to command %s\n", - http_status, - cmd->label); - json_dumpf (json, stderr, 0); - fail (is); - return; - } - if (NULL != ref->details.deposit_wtid.deposit_ref) - { - const struct Command *dep; - struct GNUNET_HashCode hw; - - dep = find_command (is, - ref->details.deposit_wtid.deposit_ref); - GNUNET_CRYPTO_hash (dep->details.deposit.wire_details, - strlen (dep->details.deposit.wire_details), - &hw); - if (0 != memcmp (&hw, - h_wire, - sizeof (struct GNUNET_HashCode))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire hash missmatch to command %s\n", - cmd->label); - json_dumpf (json, stderr, 0); - fail (is); - return; - } - } - break; - default: - break; + return NULL; } - - /* move to next command */ - is->ip++; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); + for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++) + if ( (NULL != cmd->label) && + (0 == strcmp (cmd->label, + label)) ) + return cmd; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command not found: %s\n", + label); + return NULL; } +#endif + + +/** + * Run the main interpreter loop that performs bank operations. + * + * @param cls contains the `struct InterpreterState` + * @param tc scheduler context + */ +static void +interpreter_run (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); /** - * Function called with detailed wire transfer data. + * Function called upon completion of our /admin/add/incoming request. * - * @param cls closure - * @param http_status HTTP status code we got, 0 on bank protocol violation - * @param json original json reply (may include signatures, those have then been - * validated already) - * @param wtid wire transfer identifier used by the bank, NULL if bank 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 bank could - * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK) + * @param cls closure with the interpreter state + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request + * 0 if the bank's reply is bogus (fails to follow the protocol) */ static void -deposit_wtid_cb (void *cls, - unsigned int http_status, - json_t *json, - const struct TALER_WireTransferIdentifierRawP *wtid, - struct GNUNET_TIME_Absolute execution_time, - const struct TALER_Amount *coin_contribution, - const struct TALER_Amount *total_amount) +add_incoming_cb (void *cls, + unsigned int http_status) { struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; - cmd->details.deposit_wtid.dwh = NULL; + cmd->details.admin_add_incoming.aih = 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); - json_dumpf (json, stderr, 0); + GNUNET_break (0); fail (is); return; } - switch (http_status) - { - case MHD_HTTP_OK: - cmd->details.deposit_wtid.wtid = *wtid; - if (0 != TALER_amount_cmp (total_amount, - &cmd->details.deposit_wtid.total_amount_expected)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Total amount missmatch to command %s\n", - cmd->label); - json_dumpf (json, stderr, 0); - fail (is); - return; - } - break; - default: - break; - } - - /* move to next command */ is->ip++; is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, is); @@ -1447,12 +274,7 @@ interpreter_run (void *cls, { struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; - const struct Command *ref; - struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_Amount amount; - struct GNUNET_TIME_Absolute execution_date; - json_t *wire; is->task = NULL; if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) @@ -1469,26 +291,7 @@ interpreter_run (void *cls, GNUNET_SCHEDULER_shutdown (); return; case OC_ADMIN_ADD_INCOMING: - if (NULL != - cmd->details.admin_add_incoming.reserve_reference) - { - ref = find_command (is, - cmd->details.admin_add_incoming.reserve_reference); - GNUNET_assert (NULL != ref); - GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); - cmd->details.admin_add_incoming.reserve_priv - = ref->details.admin_add_incoming.reserve_priv; - } - else - { - struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - priv = GNUNET_CRYPTO_eddsa_key_create (); - cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *priv; - GNUNET_free (priv); - } - GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv, - &reserve_pub.eddsa_pub); if (GNUNET_OK != TALER_string_to_amount (cmd->details.admin_add_incoming.amount, &amount)) @@ -1500,26 +303,14 @@ interpreter_run (void *cls, fail (is); return; } - wire = json_loads (cmd->details.admin_add_incoming.wire, - JSON_REJECT_DUPLICATES, - NULL); - if (NULL == wire) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse wire details `%s' at %u\n", - cmd->details.admin_add_incoming.wire, - is->ip); - fail (is); - return; - } - execution_date = GNUNET_TIME_absolute_get (); - TALER_round_abs_time (&execution_date); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &cmd->details.admin_add_incoming.wtid, + sizeof (cmd->details.admin_add_incoming.wtid)); cmd->details.admin_add_incoming.aih - = TALER_BANK_admin_add_incoming (bank, - &reserve_pub, + = TALER_BANK_admin_add_incoming (ctx, + &cmd->details.admin_add_incoming.wtid, &amount, - execution_date, - wire, + cmd->details.admin_add_incoming.account_no, &add_incoming_cb, is); if (NULL == cmd->details.admin_add_incoming.aih) @@ -1530,473 +321,6 @@ interpreter_run (void *cls, } trigger_context_task (); return; - case OC_WITHDRAW_STATUS: - GNUNET_assert (NULL != - cmd->details.reserve_status.reserve_reference); - ref = find_command (is, - cmd->details.reserve_status.reserve_reference); - GNUNET_assert (NULL != ref); - GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); - GNUNET_CRYPTO_eddsa_key_get_public (&ref->details.admin_add_incoming.reserve_priv.eddsa_priv, - &reserve_pub.eddsa_pub); - cmd->details.reserve_status.wsh - = TALER_BANK_reserve_status (bank, - &reserve_pub, - &reserve_status_cb, - is); - trigger_context_task (); - return; - case OC_WITHDRAW_SIGN: - GNUNET_assert (NULL != - cmd->details.reserve_withdraw.reserve_reference); - ref = find_command (is, - cmd->details.reserve_withdraw.reserve_reference); - GNUNET_assert (NULL != ref); - GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); - if (NULL != cmd->details.reserve_withdraw.amount) - { - if (GNUNET_OK != - TALER_string_to_amount (cmd->details.reserve_withdraw.amount, - &amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - cmd->details.reserve_withdraw.amount, - is->ip); - fail (is); - return; - } - cmd->details.reserve_withdraw.pk = find_pk (is->keys, - &amount); - } - if (NULL == cmd->details.reserve_withdraw.pk) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to determine denomination key at %u\n", - is->ip); - fail (is); - return; - } - - /* create coin's private key */ - { - struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - - priv = GNUNET_CRYPTO_eddsa_key_create (); - cmd->details.reserve_withdraw.coin_priv.eddsa_priv = *priv; - GNUNET_free (priv); - } - GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.reserve_withdraw.coin_priv.eddsa_priv, - &coin_pub.eddsa_pub); - cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key - = GNUNET_CRYPTO_rsa_blinding_key_create (GNUNET_CRYPTO_rsa_public_key_len (cmd->details.reserve_withdraw.pk->key.rsa_public_key)); - cmd->details.reserve_withdraw.wsh - = TALER_BANK_reserve_withdraw (bank, - cmd->details.reserve_withdraw.pk, - &ref->details.admin_add_incoming.reserve_priv, - &cmd->details.reserve_withdraw.coin_priv, - &cmd->details.reserve_withdraw.blinding_key, - &reserve_withdraw_cb, - is); - if (NULL == cmd->details.reserve_withdraw.wsh) - { - GNUNET_break (0); - fail (is); - return; - } - trigger_context_task (); - return; - case OC_DEPOSIT: - { - struct GNUNET_HashCode h_contract; - const struct TALER_CoinSpendPrivateKeyP *coin_priv; - const struct TALER_BANK_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; - struct GNUNET_TIME_Absolute wire_deadline; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - struct TALER_MerchantPublicKeyP merchant_pub; - json_t *contract; - json_t *wire; - - GNUNET_assert (NULL != - cmd->details.deposit.coin_ref); - ref = find_command (is, - cmd->details.deposit.coin_ref); - GNUNET_assert (NULL != ref); - switch (ref->oc) - { - case OC_WITHDRAW_SIGN: - coin_priv = &ref->details.reserve_withdraw.coin_priv; - coin_pk = ref->details.reserve_withdraw.pk; - coin_pk_sig = &ref->details.reserve_withdraw.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)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - cmd->details.deposit.amount, - is->ip); - fail (is); - return; - } - contract = json_loads (cmd->details.deposit.contract, - JSON_REJECT_DUPLICATES, - NULL); - if (NULL == contract) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse contract details `%s' at %u/%s\n", - cmd->details.deposit.contract, - is->ip, - cmd->label); - fail (is); - return; - } - TALER_hash_json (contract, - &h_contract); - wire = json_loads (cmd->details.deposit.wire_details, - JSON_REJECT_DUPLICATES, - NULL); - if (NULL == wire) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse wire details `%s' at %u/%s\n", - cmd->details.deposit.wire_details, - is->ip, - cmd->label); - fail (is); - return; - } - GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, - &coin_pub.eddsa_pub); - - priv = GNUNET_CRYPTO_eddsa_key_create (); - cmd->details.deposit.merchant_priv.eddsa_priv = *priv; - GNUNET_free (priv); - if (0 != cmd->details.deposit.refund_deadline.rel_value_us) - { - refund_deadline = GNUNET_TIME_relative_to_absolute (cmd->details.deposit.refund_deadline); - } - else - { - refund_deadline = GNUNET_TIME_UNIT_ZERO_ABS; - } - GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.deposit.merchant_priv.eddsa_priv, - &merchant_pub.eddsa_pub); - - wire_deadline = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS); - timestamp = GNUNET_TIME_absolute_get (); - TALER_round_abs_time (×tamp); - { - 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 = h_contract; - TALER_hash_json (wire, - &dr.h_wire); - dr.timestamp = GNUNET_TIME_absolute_hton (timestamp); - dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); - dr.transaction_id = GNUNET_htonll (cmd->details.deposit.transaction_id); - TALER_amount_hton (&dr.amount_with_fee, - &amount); - TALER_amount_hton (&dr.deposit_fee, - &coin_pk->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)); - } - cmd->details.deposit.dh - = TALER_BANK_deposit (bank, - &amount, - wire_deadline, - wire, - &h_contract, - &coin_pub, - coin_pk_sig, - &coin_pk->key, - timestamp, - cmd->details.deposit.transaction_id, - &merchant_pub, - refund_deadline, - &coin_sig, - &deposit_cb, - is); - if (NULL == cmd->details.deposit.dh) - { - GNUNET_break (0); - json_decref (wire); - fail (is); - return; - } - json_decref (wire); - 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_BANK_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_BANK_DenomPublicKey melt_pks[num_melted_coins]; - struct TALER_BANK_DenomPublicKey fresh_pks[num_fresh_coins]; - unsigned int i; - - for (i=0;idetails.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.reserve_withdraw.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.reserve_withdraw.sig; - melt_pks[i] = *ref->details.reserve_withdraw.pk; - } - for (i=0;idetails.refresh_melt.fresh_amounts[i], - &amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - cmd->details.reserve_withdraw.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_BANK_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_BANK_refresh_melt (bank, - 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_BANK_refresh_reveal (bank, - 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 reserve_withdraw 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); - } - GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc); - /* finally, use private key from withdraw sign command */ - cmd->details.refresh_link.rlh - = TALER_BANK_refresh_link (bank, - &ref->details.reserve_withdraw.coin_priv, - &link_cb, - is); - if (NULL == cmd->details.refresh_link.rlh) - { - GNUNET_break (0); - fail (is); - return; - } - trigger_context_task (); - return; - case OC_WIRE: - cmd->details.wire.wh = TALER_BANK_wire (bank, - &wire_cb, - is); - trigger_context_task (); - return; - case OC_WIRE_DEPOSITS: - if (NULL != cmd->details.wire_deposits.wtid_ref) - { - ref = find_command (is, - cmd->details.wire_deposits.wtid_ref); - GNUNET_assert (NULL != ref); - cmd->details.wire_deposits.wtid = ref->details.deposit_wtid.wtid; - } - cmd->details.wire_deposits.wdh - = TALER_BANK_wire_deposits (bank, - &cmd->details.wire_deposits.wtid, - &wire_deposits_cb, - is); - trigger_context_task (); - return; - case OC_DEPOSIT_WTID: - { - struct GNUNET_HashCode h_wire; - struct GNUNET_HashCode h_contract; - json_t *wire; - json_t *contract; - const struct Command *coin; - struct TALER_CoinSpendPublicKeyP coin_pub; - - ref = find_command (is, - cmd->details.deposit_wtid.deposit_ref); - GNUNET_assert (NULL != ref); - coin = find_command (is, - ref->details.deposit.coin_ref); - GNUNET_assert (NULL != coin); - switch (coin->oc) - { - case OC_WITHDRAW_SIGN: - GNUNET_CRYPTO_eddsa_key_get_public (&coin->details.reserve_withdraw.coin_priv.eddsa_priv, - &coin_pub.eddsa_pub); - break; - case OC_REFRESH_REVEAL: - { - const struct FreshCoin *fc; - unsigned int idx; - - idx = ref->details.deposit.coin_idx; - GNUNET_assert (idx < coin->details.refresh_reveal.num_fresh_coins); - fc = &coin->details.refresh_reveal.fresh_coins[idx]; - - GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv, - &coin_pub.eddsa_pub); - } - break; - default: - GNUNET_assert (0); - } - - wire = json_loads (ref->details.deposit.wire_details, - JSON_REJECT_DUPLICATES, - NULL); - GNUNET_assert (NULL != wire); - TALER_hash_json (wire, - &h_wire); - json_decref (wire); - contract = json_loads (ref->details.deposit.contract, - JSON_REJECT_DUPLICATES, - NULL); - GNUNET_assert (NULL != contract); - TALER_hash_json (contract, - &h_contract); - json_decref (contract); - cmd->details.deposit_wtid.dwh - = TALER_BANK_deposit_wtid (bank, - &ref->details.deposit.merchant_priv, - &h_wire, - &h_contract, - &coin_pub, - ref->details.deposit.transaction_id, - &deposit_wtid_cb, - is); - trigger_context_task (); - } - return; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown instruction %d at %u (%s)\n", @@ -2043,131 +367,6 @@ do_shutdown (void *cls, cmd->details.admin_add_incoming.aih = NULL; } break; - case OC_WITHDRAW_STATUS: - if (NULL != cmd->details.reserve_status.wsh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_BANK_reserve_status_cancel (cmd->details.reserve_status.wsh); - cmd->details.reserve_status.wsh = NULL; - } - break; - case OC_WITHDRAW_SIGN: - if (NULL != cmd->details.reserve_withdraw.wsh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_BANK_reserve_withdraw_cancel (cmd->details.reserve_withdraw.wsh); - cmd->details.reserve_withdraw.wsh = NULL; - } - if (NULL != cmd->details.reserve_withdraw.sig.rsa_signature) - { - GNUNET_CRYPTO_rsa_signature_free (cmd->details.reserve_withdraw.sig.rsa_signature); - cmd->details.reserve_withdraw.sig.rsa_signature = NULL; - } - if (NULL != cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key) - { - GNUNET_CRYPTO_rsa_blinding_key_free (cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key); - cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key = NULL; - } - break; - case OC_DEPOSIT: - if (NULL != cmd->details.deposit.dh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_BANK_deposit_cancel (cmd->details.deposit.dh); - 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_BANK_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_BANK_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;jdetails.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_BANK_refresh_link_cancel (cmd->details.refresh_link.rlh); - cmd->details.refresh_link.rlh = NULL; - } - break; - case OC_WIRE: - if (NULL != cmd->details.wire.wh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_BANK_wire_cancel (cmd->details.wire.wh); - cmd->details.wire.wh = NULL; - } - break; - case OC_WIRE_DEPOSITS: - if (NULL != cmd->details.wire_deposits.wdh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_BANK_wire_deposits_cancel (cmd->details.wire_deposits.wdh); - cmd->details.wire_deposits.wdh = NULL; - } - break; - case OC_DEPOSIT_WTID: - if (NULL != cmd->details.deposit_wtid.dwh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_BANK_deposit_wtid_cancel (cmd->details.deposit_wtid.dwh); - cmd->details.deposit_wtid.dwh = NULL; - } - break; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown instruction %d at %u (%s)\n", @@ -2188,11 +387,6 @@ do_shutdown (void *cls, GNUNET_SCHEDULER_cancel (ctx_task); ctx_task = NULL; } - if (NULL != bank) - { - TALER_BANK_disconnect (bank); - bank = NULL; - } if (NULL != ctx) { TALER_BANK_fini (ctx); @@ -2201,40 +395,6 @@ do_shutdown (void *cls, } -/** - * Functions of this type are called to provide the retrieved signing and - * denomination keys of the bank. No TALER_BANK_*() functions should be called - * in this callback. - * - * @param cls closure - * @param keys information about keys of the bank - */ -static void -cert_cb (void *cls, - const struct TALER_BANK_Keys *keys) -{ - struct InterpreterState *is = cls; - - /* check that keys is OK */ -#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); GNUNET_SCHEDULER_shutdown(); return; } while (0) - ERR (NULL == keys); - ERR (0 == keys->num_sign_keys); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %u signing keys\n", - keys->num_sign_keys); - ERR (0 == keys->num_denom_keys); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %u denomination keys\n", - keys->num_denom_keys); -#undef ERR - - /* run actual tests via interpreter-loop */ - is->keys = keys; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); -} - - /** * Task that runs the context's event loop with the GNUnet scheduler. * @@ -2304,235 +464,14 @@ 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[] = { - /* *************** start of /wire testing ************** */ - -#if WIRE_TEST - { .oc = OC_WIRE, - .label = "wire-test", - /* /wire/test replies with a 302 redirect */ - .expected_response_code = MHD_HTTP_FOUND, - .details.wire.format = "test" }, -#endif -#if WIRE_SEPA - { .oc = OC_WIRE, - .label = "wire-sepa", - /* /wire/sepa replies with a 200 redirect */ - .expected_response_code = MHD_HTTP_OK, - .details.wire.format = "sepa" }, -#endif - /* *************** end of /wire testing ************** */ - -#if WIRE_TEST - /* None of this works if 'test' is not allowed as we do - /admin/add/incoming with format 'test' */ - - /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "create-reserve-1", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.wire = "{ \"type\":\"TEST\", \"bank\":\"source bank\", \"account\":42 }", - .details.admin_add_incoming.amount = "EUR:5.01" }, - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-1", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference = "create-reserve-1", - .details.reserve_withdraw.amount = "EUR:5" }, - /* Check that deposit and withdraw operation are in history, and - that the balance is now at zero */ - { .oc = OC_WITHDRAW_STATUS, - .label = "withdraw-status-1", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_status.reserve_reference = "create-reserve-1", - .details.reserve_status.expected_balance = "EUR:0" }, - /* Try to deposit the 5 EUR coin (in full) */ - { .oc = OC_DEPOSIT, - .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 }, - - /* Try to overdraw funds ... */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-2", - .expected_response_code = MHD_HTTP_PAYMENT_REQUIRED, - .details.reserve_withdraw.reserve_reference = "create-reserve-1", - .details.reserve_withdraw.amount = "EUR:5" }, - - /* Try to double-spend the 5 EUR coin with different wire details */ - { .oc = OC_DEPOSIT, - .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 }, - /* Try to double-spend the 5 EUR coin at the same merchant (but different - transaction ID) */ - { .oc = OC_DEPOSIT, - .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 }, - /* Try to double-spend the 5 EUR coin at the same merchant (but different - contract) */ - { .oc = OC_DEPOSIT, - .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 }, - - /* ***************** /refresh testing ******************** */ - - /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct */ + /* Add EUR:5.01 to account 42 */ { .oc = OC_ADMIN_ADD_INCOMING, - .label = "refresh-create-reserve-1", + .label = "deposit-1", .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.wire = "{ \"type\":\"TEST\", \"bank\":\"source bank\", \"account\":424 }", + .details.admin_add_incoming.account_no = 42, .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.reserve_withdraw.reserve_reference = "refresh-create-reserve-1", - .details.reserve_withdraw.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 }, - - - /* 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-1a", - .expected_response_code = MHD_HTTP_OK, - .details.deposit.amount = "EUR:1", - .details.deposit.coin_ref = "refresh-reveal-1", - .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-1", - .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 }, - - // 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 bank knows about the deposit, but has no WTID yet. */ - { .oc = OC_DEPOSIT_WTID, - .label = "deposit-wtid-found", - .expected_response_code = MHD_HTTP_ACCEPTED, - .details.deposit_wtid.deposit_ref = "deposit-simple" }, - /* Try resolving a deposit's WTID for a failed deposit. - As the deposit failed, the answer should be that - the bank does NOT know about the deposit. */ - { .oc = OC_DEPOSIT_WTID, - .label = "deposit-wtid-failing", - .expected_response_code = MHD_HTTP_NOT_FOUND, - .details.deposit_wtid.deposit_ref = "deposit-double-2" }, - /* Try resolving an undefined (all zeros) WTID; this - should fail as obviously the bank didn't use that - WTID value for any transaction. */ - { .oc = OC_WIRE_DEPOSITS, - .label = "wire-deposit-failing", - .expected_response_code = MHD_HTTP_NOT_FOUND }, - - /* TODO: trigger aggregation logic and then check the - cases where tracking succeeds! */ - - /* ************** End of tracking API testing************* */ - - -#endif { .oc = OC_END } }; @@ -2540,15 +479,12 @@ run (void *cls, is = GNUNET_new (struct InterpreterState); is->commands = commands; - ctx = TALER_BANK_init (); + ctx = TALER_BANK_init ("http://localhost:8081"); GNUNET_assert (NULL != ctx); ctx_task = GNUNET_SCHEDULER_add_now (&context_task, ctx); - bank = TALER_BANK_connect (ctx, - "http://localhost:8081", - &cert_cb, is, - TALER_BANK_OPTION_END); - GNUNET_assert (NULL != bank); + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 150), @@ -2566,22 +502,11 @@ int main (int argc, char * const *argv) { - struct GNUNET_OS_Process *proc; struct GNUNET_OS_Process *bankd; GNUNET_log_setup ("test-bank-api", "WARNING", NULL); - proc = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-bank-keyup", - "taler-bank-keyup", - "-d", "test-bank-home", - "-m", "test-bank-home/master.priv", - NULL); - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); bankd = GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, NULL, @@ -2589,8 +514,15 @@ main (int argc, "taler-bank-httpd", "-d", "test-bank-home", NULL); + if (NULL == bankd) + { + fprintf (stderr, + "taler-bank-httpd not found, skipping test\n"); + return 77; /* report 'skip' */ + } /* give child time to start and bind against the socket */ - fprintf (stderr, "Waiting for taler-bank-httpd to be ready"); + fprintf (stderr, + "Waiting for taler-bank-httpd to be ready"); do { fprintf (stderr, "."); -- cgit v1.2.3