From f8a77d3ca9f39e7ee07b5e536919d7344d8905a0 Mon Sep 17 00:00:00 2001 From: Sree Harsha Totakura Date: Sat, 14 Mar 2015 15:01:53 +0100 Subject: util: Use TALER_CURRENCY_LEN instead of sizeof() --- src/util/amount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/util/amount.c') diff --git a/src/util/amount.c b/src/util/amount.c index 9bdc0fd93..0b77de82b 100644 --- a/src/util/amount.c +++ b/src/util/amount.c @@ -159,7 +159,7 @@ TALER_amount_ntoh (const struct TALER_AmountNBO dn) struct TALER_Amount d; d.value = ntohl (dn.value); d.fraction = ntohl (dn.fraction); - memcpy (d.currency, dn.currency, sizeof(dn.currency)); + memcpy (d.currency, dn.currency, TALER_CURRENCY_LEN); return d; } -- cgit v1.2.3 From 4eeaff13557c507061872e2346bfb53af6d51f1e Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 17 Mar 2015 11:37:21 +0100 Subject: documentation, comments --- src/mint/taler-mint-keyup.c | 251 +++++++++++++++++++++++--------------------- src/util/amount.c | 46 +++++--- 2 files changed, 166 insertions(+), 131 deletions(-) (limited to 'src/util/amount.c') diff --git a/src/mint/taler-mint-keyup.c b/src/mint/taler-mint-keyup.c index 8a645b589..ac5f7b1d6 100644 --- a/src/mint/taler-mint-keyup.c +++ b/src/mint/taler-mint-keyup.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) 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 @@ -19,6 +19,7 @@ * using the mint's offline master key. * @author Florian Dold * @author Benedikt Mueller + * @author Christian Grothoff */ #include #include @@ -26,17 +27,20 @@ #include "key_io.h" /** - * FIXME: allow user to specify (within reason). - */ -#define RSA_KEYSIZE 2048 - -/** - * + * When generating filenames from a cryptographic hash, we do not use + * all 512 bits but cut off after this number of characters (in + * base32-encoding). Base32 is 5 bit per character, and given that we + * have very few coin types we hash, at 100 bits the chance of + * collision (by accident over tiny set -- birthday paradox does not + * apply here!) is negligible. */ #define HASH_CUTOFF 20 /** * Macro to round microseconds to seconds in GNUNET_TIME_* structs. + * + * @param name value to round + * @param field rel_value_us or abs_value_us */ #define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000); @@ -78,55 +82,71 @@ struct CoinTypeNBO * What is the fee charged for melting? */ struct TALER_AmountNBO fee_refresh; + + /** + * Key size in NBO. + */ + uint32_t rsa_keysize; }; GNUNET_NETWORK_STRUCT_END /** - * + * Set of all of the parameters that chracterize a coin. */ struct CoinTypeParams { /** - * + * How long can the coin be spend? Should be significantly + * larger than @e duration_withdraw (i.e. years). */ struct GNUNET_TIME_Relative duration_spend; /** - * + * How long can the coin be withdrawn (generated)? Should be small + * enough to limit how many coins will be signed into existence with + * the same key, but large enough to still provide a reasonable + * anonymity set. */ struct GNUNET_TIME_Relative duration_withdraw; /** - * + * How much should coin creation (@e duration_withdraw) duration + * overlap with the next coin? Basically, the starting time of two + * coins is always @e duration_withdraw - @e duration_overlap apart. */ struct GNUNET_TIME_Relative duration_overlap; /** - * + * What is the value of the coin? */ struct TALER_Amount value; /** - * + * What is the fee charged for withdrawl? */ struct TALER_Amount fee_withdraw; /** - * + * What is the fee charged for deposits? */ struct TALER_Amount fee_deposit; /** - * + * What is the fee charged for melting? */ struct TALER_Amount fee_refresh; /** - * + * Time at which this coin is supposed to become valid. */ struct GNUNET_TIME_Absolute anchor; + + /** + * Length of the RSA key in bits. + */ + uint32_t rsa_keysize; }; @@ -172,7 +192,6 @@ static struct GNUNET_CRYPTO_EddsaPublicKey *master_pub; static struct GNUNET_TIME_Absolute lookahead_sign_stamp; - /** * Obtain the name of the directory we use to store signing * keys created at time @a start. @@ -212,14 +231,13 @@ hash_coin_type (const struct CoinTypeParams *p, memset (&p_nbo, 0, sizeof (struct CoinTypeNBO)); - p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend); p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw); p_nbo.value = TALER_amount_hton (p->value); p_nbo.fee_withdraw = TALER_amount_hton (p->fee_withdraw); p_nbo.fee_deposit = TALER_amount_hton (p->fee_deposit); p_nbo.fee_refresh = TALER_amount_hton (p->fee_refresh); - + p_nbo.rsa_keysize = htonl (p->rsa_keysize); GNUNET_CRYPTO_hash (&p_nbo, sizeof (struct CoinTypeNBO), hash); @@ -246,18 +264,19 @@ get_cointype_dir (const struct CoinTypeParams *p) struct GNUNET_HashCode hash; char *hash_str; char *val_str; - unsigned int i; + size_t i; hash_coin_type (p, &hash); hash_str = GNUNET_STRINGS_data_to_string_alloc (&hash, sizeof (struct GNUNET_HashCode)); - GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1); GNUNET_assert (NULL != hash_str); + GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1); hash_str[HASH_CUTOFF] = 0; val_str = TALER_amount_to_string (p->value); for (i = 0; i < strlen (val_str); i++) - if (':' == val_str[i] || '.' == val_str[i]) + if ( (':' == val_str[i]) || + ('.' == val_str[i]) ) val_str[i] = '_'; GNUNET_snprintf (dir, @@ -267,23 +286,27 @@ get_cointype_dir (const struct CoinTypeParams *p) val_str, hash_str); GNUNET_free (hash_str); + GNUNET_free (val_str); return dir; } /** + * Obtain the name of the file we would use to store the key + * information for a coin of the given type @a p and validity + * start time @a start * - * - * @param p - * @param start - * @return + * @param p parameters for the coin + * @param start when would the coin begin to be issued + * @return name of the file to use for this coin + * (valid until next call to this function) */ static const char * -get_cointype_file (struct CoinTypeParams *p, +get_cointype_file (const struct CoinTypeParams *p, struct GNUNET_TIME_Absolute start) { - const char *dir; static char filename[4096]; + const char *dir; dir = get_cointype_dir (p); GNUNET_snprintf (filename, @@ -296,13 +319,15 @@ get_cointype_file (struct CoinTypeParams *p, /** - * Get the latest key file from the past. + * Get the latest key file from a past run of the key generation + * tool. Used to calculate the starting time for the keys we + * generate during this invocation. * - * @param cls closure + * @param cls closure, a `struct GNUNET_TIME_Absolute *`, updated + * to contain the highest timestamp (below #now) + * that was found * @param filename complete filename (absolute path) - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! + * @return #GNUNET_OK (to continue to iterate) */ static int get_anchor_iter (void *cls, @@ -317,31 +342,28 @@ get_anchor_iter (void *cls, stamp.abs_value_us = strtol (base, &end, 10); - if ((NULL == end) || (0 != *end)) { fprintf(stderr, - "Ignoring unexpected file '%s'.\n", + "Ignoring unexpected file `%s'.\n", filename); return GNUNET_OK; } - - // TODO: check if it's actually a valid key file - - if ( (stamp.abs_value_us <= now.abs_value_us) && - ( stamp.abs_value_us > anchor->abs_value_us) ) - *anchor = stamp; - + // TODO: check if it's actually a valid key file? + if (stamp.abs_value_us <= now.abs_value_us) + *anchor = GNUNET_TIME_absolute_max (stamp, + *anchor); return GNUNET_OK; } /** * Get the timestamp where the first new key should be generated. - * Relies on correctly named key files. + * Relies on correctly named key files (as we do not parse them, + * but just look at the filenames to "guess" at their contents). * - * @param dir directory with the signed stuff - * @param duration how long is one key valid? + * @param dir directory that should contain the existing keys + * @param duration how long is one key valid (for signing)? * @param overlap what's the overlap between the keys validity period? * @param[out] anchor the timestamp where the first new key should be generated */ @@ -354,15 +376,14 @@ get_anchor (const char *dir, GNUNET_assert (0 == duration.rel_value_us % 1000000); GNUNET_assert (0 == overlap.rel_value_us % 1000000); if (GNUNET_YES != - GNUNET_DISK_directory_test (dir, GNUNET_YES)) + GNUNET_DISK_directory_test (dir, + GNUNET_YES)) { *anchor = now; - fprintf (stderr, - "Cannot look for anchor (%s)\n", - dir); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No existing keys found, starting with fresh key set.\n"); return; } - *anchor = GNUNET_TIME_UNIT_ZERO_ABS; if (-1 == GNUNET_DISK_directory_scan (dir, @@ -370,23 +391,30 @@ get_anchor (const char *dir, anchor)) { *anchor = now; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No existing keys found, starting with fresh key set.\n"); return; } + /* FIXME: this check is a bit dubious, as 'now' + may be way into the future if we want to generate + many keys... */ if ((GNUNET_TIME_absolute_add (*anchor, duration)).abs_value_us < now.abs_value_us) { - // there's no good anchor, start from now - // (existing keys are too old) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Existing keys are way too old, starting with fresh key set.\n"); *anchor = now; } - else if (anchor->abs_value_us != now.abs_value_us) + else if (anchor->abs_value_us != now.abs_value_us) // Also odd... { - // we have a good anchor - *anchor = GNUNET_TIME_absolute_add (*anchor, duration); - *anchor = GNUNET_TIME_absolute_subtract (*anchor, overlap); + /* Real starting time is the last start time + duration - overlap */ + *anchor = GNUNET_TIME_absolute_add (*anchor, + duration); + *anchor = GNUNET_TIME_absolute_subtract (*anchor, + overlap); } - // anchor is now the stamp where we need to create a new key + /* anchor is now the stamp where we need to create a new key */ } @@ -395,7 +423,7 @@ get_anchor (const char *dir, * * @param start * @param duration - * @param pi + * @param pi[OUT] */ static void create_signkey_issue_priv (struct GNUNET_TIME_Absolute start, @@ -431,21 +459,7 @@ create_signkey_issue_priv (struct GNUNET_TIME_Absolute start, /** * * - * @param signkey_filename - * @return - */ -static int -check_signkey_valid (const char *signkey_filename) -{ - // FIXME: do real checks - return GNUNET_OK; -} - - -/** - * - * - * @return + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ static int mint_keys_update_signkeys () @@ -513,28 +527,27 @@ mint_keys_update_signkeys () return GNUNET_SYSERR; } } - else if (GNUNET_OK != check_signkey_valid (skf)) - { - return GNUNET_SYSERR; - } - anchor = GNUNET_TIME_absolute_add (anchor, signkey_duration); + anchor = GNUNET_TIME_absolute_add (anchor, + signkey_duration); } return GNUNET_OK; } /** + * Parse configuration for coin type parameters. Also determines + * our anchor by looking at the existing coins of the same type. * - * - * @param ct - * @param params - * @return + * @param ct section in the configuration file giving the coin type parameters + * @param params[OUT] set to the coin parameters from the configuration + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid */ static int get_cointype_params (const char *ct, struct CoinTypeParams *params) { const char *dir; + unsigned long long rsa_keysize; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, @@ -560,7 +573,8 @@ get_cointype_params (const char *ct, ct); return GNUNET_SYSERR; } - ROUND_TO_SECS (params->duration_spend, rel_value_us); + ROUND_TO_SECS (params->duration_spend, + rel_value_us); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_overlap", @@ -574,7 +588,26 @@ get_cointype_params (const char *ct, } ROUND_TO_SECS (params->duration_overlap, rel_value_us); - + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (kcfg, + "mint_denom_rsa_keysize", + ct, + &rsa_keysize)) + { + fprintf (stderr, + "RSA keysize not given for coin type '%s'\n", + ct); + return GNUNET_SYSERR; + } + if ( (rsa_keysize > 4 * 2048) || + (rsa_keysize < 1024) ) + { + fprintf (stderr, + "Given RSA keysize %llu outside of permitted range\n", + rsa_keysize); + return GNUNET_SYSERR; + } + params->rsa_keysize = (unsigned int) rsa_keysize; if (GNUNET_OK != TALER_config_get_denom (kcfg, "mint_denom_value", @@ -586,7 +619,6 @@ get_cointype_params (const char *ct, ct); return GNUNET_SYSERR; } - if (GNUNET_OK != TALER_config_get_denom (kcfg, "mint_denom_fee_withdraw", @@ -598,7 +630,6 @@ get_cointype_params (const char *ct, ct); return GNUNET_SYSERR; } - if (GNUNET_OK != TALER_config_get_denom (kcfg, "mint_denom_fee_deposit", @@ -610,7 +641,6 @@ get_cointype_params (const char *ct, ct); return GNUNET_SYSERR; } - if (GNUNET_OK != TALER_config_get_denom (kcfg, "mint_denom_fee_refresh", @@ -624,22 +654,29 @@ get_cointype_params (const char *ct, } dir = get_cointype_dir (params); - get_anchor (dir, params->duration_spend, params->duration_overlap, ¶ms->anchor); + get_anchor (dir, + params->duration_spend, + params->duration_overlap, + ¶ms->anchor); return GNUNET_OK; } /** + * Initialize the private and public key information structure for + * signing coins into existence. Generates the private signing key + * and signes it together with the coin's meta data using the master + * signing key. * - * - * @param params - * @param dki + * @param params parameters used to initialize the @a dki + * @param dki[OUT] initialized according to @a params */ static void -create_denomkey_issue (struct CoinTypeParams *params, +create_denomkey_issue (const struct CoinTypeParams *params, struct TALER_MINT_DenomKeyIssuePriv *dki) { - GNUNET_assert (NULL != (dki->denom_priv = GNUNET_CRYPTO_rsa_private_key_create (RSA_KEYSIZE))); + GNUNET_assert (NULL != + (dki->denom_priv = GNUNET_CRYPTO_rsa_private_key_create (params->rsa_keysize))); dki->denom_pub = GNUNET_CRYPTO_rsa_private_key_get_public (dki->denom_priv); GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub, &dki->issue.denom_hash); @@ -660,7 +697,6 @@ create_denomkey_issue (struct CoinTypeParams *params, dki->issue.purpose.size = htonl (sizeof (struct TALER_MINT_DenomKeyIssuePriv) - offsetof (struct TALER_MINT_DenomKeyIssuePriv, issue.purpose)); - GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (master_priv, &dki->issue.purpose, @@ -669,26 +705,11 @@ create_denomkey_issue (struct CoinTypeParams *params, /** + * Generate new coin signing keys for the coin type of the given @a + * coin_alias. * - * - * @param filename - * @param params - * @return - */ -static int -check_cointype_valid (const char *filename, - struct CoinTypeParams *params) -{ - // FIXME: add real checks - return GNUNET_OK; -} - - -/** - * - * - * @param coin_alias - * @return + * @param coin_alias name of the coin's section in the configuration + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ static int mint_keys_update_cointype (const char *coin_alias) @@ -732,10 +753,6 @@ mint_keys_update_cointype (const char *coin_alias) return GNUNET_SYSERR; } } - else if (GNUNET_OK != check_cointype_valid (dkf, &p)) - { - return GNUNET_SYSERR; - } p.anchor = GNUNET_TIME_absolute_add (p.anchor, p.duration_spend); p.anchor = GNUNET_TIME_absolute_subtract (p.anchor, p.duration_overlap); } diff --git a/src/util/amount.c b/src/util/amount.c index 0b77de82b..b3e3b4217 100644 --- a/src/util/amount.c +++ b/src/util/amount.c @@ -30,8 +30,14 @@ #include "taler_util.h" #include +/** + * + */ #define AMOUNT_FRAC_BASE 1000000 +/** + * + */ #define AMOUNT_FRAC_LEN 6 @@ -47,34 +53,39 @@ int TALER_string_to_amount (const char *str, struct TALER_Amount *denom) { - unsigned int i; // pos in str + size_t i; // pos in str int n; // number tmp - unsigned int c; // currency pos + size_t c; // currency pos uint32_t b; // base for suffix - memset (denom, 0, sizeof (struct TALER_Amount)); - - i = n = c = 0; + memset (denom, + 0, + sizeof (struct TALER_Amount)); + i = 0; while (isspace(str[i])) i++; if (0 == str[i]) { - printf("null before currency\n"); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "null before currency\n"); return GNUNET_SYSERR; } + c = 0; while (str[i] != ':') { if (0 == str[i]) { - printf("null before colon"); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "null before colon"); return GNUNET_SYSERR; } if (c > 3) { - printf("currency too long\n"); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "currency too long\n"); return GNUNET_SYSERR; } denom->currency[c] = str[i]; @@ -87,7 +98,8 @@ TALER_string_to_amount (const char *str, if (0 == str[i]) { - printf("null before value\n"); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "null before value\n"); return GNUNET_SYSERR; } @@ -100,7 +112,10 @@ TALER_string_to_amount (const char *str, n = str[i] - '0'; if (n < 0 || n > 9) { - printf("invalid character '%c' before comma at %u\n", (char) n, i); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "invalid character '%c' before comma at %u\n", + (char) n, + i); return GNUNET_SYSERR; } denom->value = (denom->value * 10) + n; @@ -112,7 +127,8 @@ TALER_string_to_amount (const char *str, if (0 == str[i]) { - printf("null after dot"); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "null after dot"); return GNUNET_SYSERR; } @@ -121,9 +137,10 @@ TALER_string_to_amount (const char *str, while (0 != str[i]) { n = str[i] - '0'; - if (b == 0 || n < 0 || n > 9) + if ( (0 == b) || (n < 0) || (n > 9) ) { - printf("error after comma"); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "error after comma"); return GNUNET_SYSERR; } denom->fraction += n * b; @@ -293,7 +310,8 @@ TALER_amount_add (struct TALER_Amount a1, struct TALER_Amount TALER_amount_normalize (struct TALER_Amount amount) { - while (amount.value != UINT32_MAX && amount.fraction >= AMOUNT_FRAC_BASE) + while ( (amount.value != UINT32_MAX) && + (amount.fraction >= AMOUNT_FRAC_BASE) ) { amount.fraction -= AMOUNT_FRAC_BASE; amount.value += 1; -- cgit v1.2.3 From 23bf1eee74bed73cf98264c247ab44df8dadfcd9 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 18 Mar 2015 18:55:41 +0100 Subject: fix #3716: make sure amount-API offers proper checks against overflow and other issues --- src/include/taler_amount_lib.h | 121 ++++++--- src/include/taler_json_lib.h | 2 +- src/lib/mint_api.c | 12 +- src/mint/mint_db.c | 25 +- src/mint/mint_db.h | 2 +- src/mint/taler-mint-httpd_db.c | 143 ++++++---- src/mint/taler-mint-httpd_deposit.c | 3 +- src/mint/taler-mint-httpd_keystate.c | 21 +- src/mint/taler-mint-httpd_parsing.c | 14 +- src/mint/taler-mint-httpd_refresh.c | 40 ++- src/mint/taler-mint-httpd_responses.c | 68 +++-- src/mint/taler-mint-keyup.c | 27 +- src/mint/taler-mint-reservemod.c | 8 +- src/util/amount.c | 492 +++++++++++++++++++++------------- src/util/json.c | 21 +- 15 files changed, 660 insertions(+), 339 deletions(-) (limited to 'src/util/amount.c') diff --git a/src/include/taler_amount_lib.h b/src/include/taler_amount_lib.h index 8bea0256c..e64ff4d92 100644 --- a/src/include/taler_amount_lib.h +++ b/src/include/taler_amount_lib.h @@ -25,10 +25,27 @@ /** * Number of characters (plus 1 for 0-termination) we use to * represent currency names (i.e. EUR, USD, etc.). We use - * 8 for alignment (!). + * 4 for alignment as 3 characters are typical and we need a + * 0-terminator. So do not change this. */ #define TALER_CURRENCY_LEN 4 +/** + * The "fraction" value in a `struct TALER_Amount` represents which + * fraction of the "main" value? + * + * Note that we need sub-cent precision here as transaction fees might + * be that low, and as we want to support microdonations. + */ +#define TALER_AMOUNT_FRAC_BASE 1000000 + +/** + * How many digits behind the comma are required to represent the + * fractional value in human readable decimal format? Must match + * lg(#TALER_AMOUNT_FRAC_BASE). + */ +#define TALER_AMOUNT_FRAC_LEN 6 + GNUNET_NETWORK_STRUCT_BEGIN @@ -41,12 +58,12 @@ struct TALER_AmountNBO /** * Value in the main currency, in NBO. */ - uint32_t value; + uint64_t value GNUNET_PACKED; /** * Additinal fractional value, in NBO. */ - uint32_t fraction; + uint32_t fraction GNUNET_PACKED; /** * Type of the currency being represented. @@ -65,7 +82,7 @@ struct TALER_Amount /** * Value (numerator of fraction) */ - uint32_t value; + uint64_t value; /** * Fraction (denominator of fraction) @@ -73,7 +90,8 @@ struct TALER_Amount uint32_t fraction; /** - * Currency string, left adjusted and padded with zeros. + * Currency string, left adjusted and padded with zeros. All zeros + * for "invalid" values. */ char currency[TALER_CURRENCY_LEN]; }; @@ -92,82 +110,123 @@ TALER_string_to_amount (const char *str, struct TALER_Amount *denom); +/** + * Get the value of "zero" in a particular currency. + * + * @param cur currency description + * @param denom denomination to write the result to + * @return #GNUNET_OK if @a cur is a valid currency specification, + * #GNUNET_SYSERR if it is invalid. + */ +int +TALER_amount_get_zero (const char *cur, + struct TALER_Amount *denom); + + /** * Convert amount from host to network representation. * + * @param res where to store amount in network representation * @param d amount in host representation - * @return amount in network representation */ -struct TALER_AmountNBO -TALER_amount_hton (const struct TALER_Amount d); +void +TALER_amount_hton (struct TALER_AmountNBO *res, + const struct TALER_Amount *d); /** * Convert amount from network to host representation. * + * @param res where to store amount in host representation * @param d amount in network representation - * @return amount in host representation */ -struct TALER_Amount -TALER_amount_ntoh (const struct TALER_AmountNBO dn); +void +TALER_amount_ntoh (struct TALER_Amount *res, + const struct TALER_AmountNBO *dn); /** - * Compare the value/fraction of two amounts. Does not compare the currency, - * i.e. comparing amounts with the same value and fraction but different - * currency would return 0. + * Compare the value/fraction of two amounts. Does not compare the currency. + * Comparing amounts of different currencies will cause the program to abort(). + * If unsure, check with #TALER_amount_cmp_currency() first to be sure that + * the currencies of the two amounts are identical. * * @param a1 first amount * @param a2 second amount * @return result of the comparison */ int -TALER_amount_cmp (struct TALER_Amount a1, - struct TALER_Amount a2); +TALER_amount_cmp (const struct TALER_Amount *a1, + const struct TALER_Amount *a2); + + +/** + * Test if @a a1 and @a a2 are the same currency. + * + * @param a1 amount to test + * @param a2 amount to test + * @return #GNUNET_YES if @a a1 and @a a2 are the same currency + * #GNUNET_NO if the currencies are different + * #GNUNET_SYSERR if either amount is invalid + */ +int +TALER_amount_cmp_currency (const struct TALER_Amount *a1, + const struct TALER_Amount *a2); /** * Perform saturating subtraction of amounts. * + * @param diff where to store (@a a1 - @a a2), or invalid if @a a2 > @a a1 * @param a1 amount to subtract from * @param a2 amount to subtract - * @return (a1-a2) or 0 if a2>=a1 + * @return #GNUNET_OK if the subtraction worked, + * #GNUNET_NO if @a a1 = @a a2 + * #GNUNET_SYSERR if @a a2 > @a a1 or currencies are incompatible; + * @a diff is set to invalid */ -struct TALER_Amount -TALER_amount_subtract (struct TALER_Amount a1, - struct TALER_Amount a2); +int +TALER_amount_subtract (struct TALER_Amount *diff, + const struct TALER_Amount *a1, + const struct TALER_Amount *a2); /** - * Perform saturating addition of amounts + * Perform addition of amounts. * + * @param sum where to store @a a1 + @a a2, set to "invalid" on overflow * @param a1 first amount to add * @param a2 second amount to add - * @return sum of a1 and a2 + * @return #GNUNET_OK if the addition worked, + * #GNUNET_SYSERR on overflow */ -struct TALER_Amount -TALER_amount_add (struct TALER_Amount a1, - struct TALER_Amount a2); +int +TALER_amount_add (struct TALER_Amount *sum, + const struct TALER_Amount *a1, + const struct TALER_Amount *a2); /** * Normalize the given amount. * - * @param amout amount to normalize - * @return normalized amount + * @param amount amount to normalize + * @return #GNUNET_OK if normalization worked + * #GNUNET_NO if value was already normalized + * #GNUNET_SYSERR if value was invalid or could not be normalized */ -struct TALER_Amount -TALER_amount_normalize (struct TALER_Amount amount); +int +TALER_amount_normalize (struct TALER_Amount *amount); /** * Convert amount to string. * * @param amount amount to convert to string - * @return freshly allocated string representation + * @return freshly allocated string representation, + * NULL if the @a amount was invalid */ char * -TALER_amount_to_string (struct TALER_Amount amount); +TALER_amount_to_string (const struct TALER_Amount *amount); #endif diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index 1048b89f6..c5515966f 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -39,7 +39,7 @@ * @return a json object describing the amount */ json_t * -TALER_JSON_from_amount (struct TALER_Amount amount); +TALER_JSON_from_amount (const struct TALER_Amount *amount); /** diff --git a/src/lib/mint_api.c b/src/lib/mint_api.c index af4f04956..eb2697a2e 100644 --- a/src/lib/mint_api.c +++ b/src/lib/mint_api.c @@ -412,10 +412,14 @@ parse_json_denomkey (struct TALER_MINT_DenomPublicKey **_denom_key, denom_key_issue.start = GNUNET_TIME_absolute_hton (valid_from); denom_key_issue.expire_withdraw = GNUNET_TIME_absolute_hton (withdraw_valid_until); denom_key_issue.expire_spend = GNUNET_TIME_absolute_hton (deposit_valid_until); - denom_key_issue.value = TALER_amount_hton (value); - denom_key_issue.fee_withdraw = TALER_amount_hton (fee_withdraw); - denom_key_issue.fee_deposit = TALER_amount_hton (fee_deposit); - denom_key_issue.fee_refresh = TALER_amount_hton (fee_refresh); + TALER_amount_hton (&denom_key_issue.value, + &value); + TALER_amount_hton (&denom_key_issue.fee_withdraw, + &fee_withdraw); + TALER_amount_hton (&denom_key_issue.fee_deposit, + &fee_deposit); + TALER_amount_hton (&denom_key_issue.fee_refresh, + &fee_refresh); EXITIF (GNUNET_SYSERR == GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOM, &denom_key_issue.purpose, diff --git a/src/mint/mint_db.c b/src/mint/mint_db.c index 6ed7193e3..545a5252b 100644 --- a/src/mint/mint_db.c +++ b/src/mint/mint_db.c @@ -807,7 +807,8 @@ reserves_update (PGconn *db, TALER_DB_QUERY_PARAM_PTR (&expiry_nbo), TALER_DB_QUERY_PARAM_END }; - balance_nbo = TALER_amount_hton (reserve->balance); + TALER_amount_hton (&balance_nbo, + &reserve->balance); expiry_nbo = GNUNET_TIME_absolute_hton (reserve->expiry); result = TALER_DB_exec_prepared (db, "update_reserve", @@ -837,7 +838,7 @@ reserves_update (PGconn *db, int TALER_MINT_DB_reserves_in_insert (PGconn *db, struct Reserve *reserve, - const struct TALER_Amount balance, + const struct TALER_Amount *balance, const struct GNUNET_TIME_Absolute expiry) { struct TALER_AmountNBO balance_nbo; @@ -862,7 +863,8 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db, TALER_MINT_DB_rollback (db); return GNUNET_SYSERR; } - balance_nbo = TALER_amount_hton (balance); + TALER_amount_hton (&balance_nbo, + balance); expiry_nbo = GNUNET_TIME_absolute_hton (expiry); if (GNUNET_NO == reserve_exists) { @@ -907,19 +909,27 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db, QUERY_ERR (result); goto rollback; } - PQclear (result); result = NULL; + PQclear (result); + result = NULL; if (GNUNET_NO == reserve_exists) { if (GNUNET_OK != TALER_MINT_DB_commit (db)) return GNUNET_SYSERR; - reserve->balance = balance; + reserve->balance = *balance; reserve->expiry = expiry; return GNUNET_OK; } /* Update reserve */ struct Reserve updated_reserve; updated_reserve.pub = reserve->pub; - updated_reserve.balance = TALER_amount_add (reserve->balance, balance); + + if (GNUNET_OK != + TALER_amount_add (&updated_reserve.balance, + &reserve->balance, + balance)) + { + return GNUNET_SYSERR; + } updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry, reserve->expiry); if (GNUNET_OK != reserves_update (db, &updated_reserve)) goto rollback; @@ -1350,7 +1360,8 @@ TALER_MINT_DB_insert_deposit (PGconn *db_conn, GNUNET_CRYPTO_rsa_signature_encode (deposit->coin.denom_sig, &denom_sig_enc); json_wire_enc = json_dumps (deposit->wire, JSON_COMPACT); - amount_nbo = TALER_amount_hton (deposit->amount); + TALER_amount_hton (&amount_nbo, + &deposit->amount); struct TALER_DB_QueryParam params[]= { TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub), TALER_DB_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size), diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h index 4a9ec1524..9312f548a 100644 --- a/src/mint/mint_db.h +++ b/src/mint/mint_db.h @@ -224,7 +224,7 @@ TALER_MINT_DB_reserve_get (PGconn *db, int TALER_MINT_DB_reserves_in_insert (PGconn *db, struct Reserve *reserve, - const struct TALER_Amount balance, + const struct TALER_Amount *balance, const struct GNUNET_TIME_Absolute expiry); diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index df68b6570..05c2a48a7 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -34,26 +34,6 @@ #include "taler-mint-httpd_keystate.h" -/** - * Get an amount in the mint's currency that is zero. - * - * @return zero amount in the mint's currency - */ -static struct TALER_Amount -mint_amount_native_zero () -{ - struct TALER_Amount amount; - - memset (&amount, - 0, - sizeof (amount)); - memcpy (amount.currency, - MINT_CURRENCY, - strlen (MINT_CURRENCY) + 1); - return amount; -} - - /** * Execute a deposit. The validity of the coin and signature * have already been checked. The database must now check that @@ -99,9 +79,12 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, mks = TALER_MINT_key_state_acquire (); dki = TALER_MINT_get_denom_key (mks, deposit->coin.denom_pub); - value = TALER_amount_ntoh (dki->issue.value); - fee_deposit = TALER_amount_ntoh (dki->issue.fee_deposit); - fee_refresh = TALER_amount_ntoh (dki->issue.fee_refresh); + TALER_amount_ntoh (&value, + &dki->issue.value); + TALER_amount_ntoh (&fee_deposit, + &dki->issue.fee_deposit); + TALER_amount_ntoh (&fee_refresh, + &dki->issue.fee_refresh); TALER_MINT_key_state_release (mks); if (GNUNET_OK != @@ -113,26 +96,49 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, tl = TALER_MINT_DB_get_coin_transactions (db_conn, &deposit->coin.coin_pub); spent = fee_deposit; /* fee for THIS transaction */ - /* FIXME: need to deal better with integer overflows - in the logic that follows! (change amount.c API! -- #3637) */ - spent = TALER_amount_add (spent, - deposit->amount); + if (GNUNET_OK != + TALER_amount_add (&spent, + &spent, + &deposit->amount)) + { + GNUNET_break (0); + TALER_MINT_DB_free_coin_transaction_list (tl); + return TALER_MINT_reply_internal_db_error (connection); + } for (pos = tl; NULL != pos; pos = pos->next) { switch (pos->type) { case TALER_MINT_DB_TT_DEPOSIT: - spent = TALER_amount_add (spent, - pos->details.deposit->amount); - spent = TALER_amount_add (spent, - fee_deposit); + if ( (GNUNET_OK != + TALER_amount_add (&spent, + &spent, + &pos->details.deposit->amount)) || + (GNUNET_OK != + TALER_amount_add (&spent, + &spent, + &fee_deposit)) ) + { + GNUNET_break (0); + TALER_MINT_DB_free_coin_transaction_list (tl); + return TALER_MINT_reply_internal_db_error (connection); + } break; case TALER_MINT_DB_TT_REFRESH_MELT: - spent = TALER_amount_add (spent, - pos->details.melt->amount); - spent = TALER_amount_add (spent, - fee_refresh); + if ( (GNUNET_OK != + TALER_amount_add (&spent, + &spent, + &pos->details.melt->amount)) || + (GNUNET_OK != + TALER_amount_add (&spent, + &spent, + &fee_refresh)) ) + { + GNUNET_break (0); + TALER_MINT_DB_free_coin_transaction_list (tl); + return TALER_MINT_reply_internal_db_error (connection); + } break; case TALER_MINT_DB_TT_LOCK: /* should check if lock is still active, @@ -146,11 +152,12 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, } } - if (0 < TALER_amount_cmp (spent, value)) + if (0 < TALER_amount_cmp (&spent, + &value)) { TALER_MINT_DB_rollback (db_conn); ret = TALER_MINT_reply_deposit_insufficient_funds (connection, - tl); + tl); TALER_MINT_DB_free_coin_transaction_list (tl); return ret; } @@ -251,6 +258,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, struct TALER_Amount withdraw_total; struct TALER_Amount balance; struct TALER_Amount value; + struct TALER_Amount fee_withdraw; struct GNUNET_HashCode h_blind; int res; @@ -318,8 +326,20 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, } /* calculate amount required including fees */ - amount_required = TALER_amount_add (TALER_amount_ntoh (dki->issue.value), - TALER_amount_ntoh (dki->issue.fee_withdraw)); + TALER_amount_ntoh (&value, + &dki->issue.value); + TALER_amount_ntoh (&fee_withdraw, + &dki->issue.fee_withdraw); + + if (GNUNET_OK != + TALER_amount_add (&amount_required, + &value, + &fee_withdraw)) + { + TALER_MINT_DB_rollback (db_conn); + TALER_MINT_key_state_release (key_state); + return TALER_MINT_reply_internal_db_error (connection); + } /* calculate balance of the reserve */ res = 0; @@ -331,29 +351,45 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, if (0 == (res & 1)) deposit_total = pos->details.bank->amount; else - deposit_total = TALER_amount_add (deposit_total, - pos->details.bank->amount); + if (GNUNET_OK != + TALER_amount_add (&deposit_total, + &deposit_total, + &pos->details.bank->amount)) + { + TALER_MINT_DB_rollback (db_conn); + TALER_MINT_key_state_release (key_state); + return TALER_MINT_reply_internal_db_error (connection); + } res |= 1; break; case TALER_MINT_DB_RO_WITHDRAW_COIN: tdki = TALER_MINT_get_denom_key (key_state, pos->details.withdraw->denom_pub); - value = TALER_amount_ntoh (tdki->issue.value); + TALER_amount_ntoh (&value, + &tdki->issue.value); if (0 == (res & 2)) withdraw_total = value; else - withdraw_total = TALER_amount_add (withdraw_total, - value); + if (GNUNET_OK != + TALER_amount_add (&withdraw_total, + &withdraw_total, + &value)) + { + TALER_MINT_DB_rollback (db_conn); + TALER_MINT_key_state_release (key_state); + return TALER_MINT_reply_internal_db_error (connection); + } res |= 2; break; } } - GNUNET_break (0 > TALER_amount_cmp (withdraw_total, - deposit_total)); - balance = TALER_amount_subtract (deposit_total, - withdraw_total); - if (0 < TALER_amount_cmp (amount_required, - balance)) + /* All reserve balances should be non-negative */ + GNUNET_break (GNUNET_SYSERR != + TALER_amount_subtract (&balance, + &deposit_total, + &withdraw_total)); + if (0 < TALER_amount_cmp (&amount_required, + &balance)) { TALER_MINT_key_state_release (key_state); TALER_MINT_DB_rollback (db_conn); @@ -450,7 +486,8 @@ refresh_accept_melts (struct MHD_Connection *connection, "denom not found")) ? GNUNET_NO : GNUNET_SYSERR; - coin_value = TALER_amount_ntoh (dki->value); + TALER_amount_ntoh (&coin_value, + &dki->value); tl = TALER_MINT_DB_get_coin_transactions (db_conn, &coin_public_info->coin_pub); /* FIXME: #3636: compute how much value is left with this coin and @@ -459,8 +496,8 @@ refresh_accept_melts (struct MHD_Connection *connection, /* Refuse to refresh when the coin does not have enough money left to * pay the refreshing fees of the coin. */ - if (TALER_amount_cmp (coin_residual, - coin_details->melt_amount) < 0) + if (TALER_amount_cmp (&coin_residual, + &coin_details->melt_amount) < 0) { res = (MHD_YES == TALER_MINT_reply_refresh_melt_insufficient_funds (connection, diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c index d37e69e40..37d6d23d5 100644 --- a/src/mint/taler-mint-httpd_deposit.c +++ b/src/mint/taler-mint-httpd_deposit.c @@ -65,7 +65,8 @@ verify_and_execute_deposit (struct MHD_Connection *connection, dr.h_contract = deposit->h_contract; dr.h_wire = deposit->h_wire; dr.transaction_id = GNUNET_htonll (deposit->transaction_id); - dr.amount = TALER_amount_hton (deposit->amount); + TALER_amount_hton (&dr.amount, + &deposit->amount); dr.coin_pub = deposit->coin.coin_pub; if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_WALLET_DEPOSIT, diff --git a/src/mint/taler-mint-httpd_keystate.c b/src/mint/taler-mint-httpd_keystate.c index bf802f5b5..ca802e2d3 100644 --- a/src/mint/taler-mint-httpd_keystate.c +++ b/src/mint/taler-mint-httpd_keystate.c @@ -114,6 +114,19 @@ static json_t * denom_key_issue_to_json (struct GNUNET_CRYPTO_rsa_PublicKey *pk, const struct TALER_MINT_DenomKeyIssue *dki) { + struct TALER_Amount value; + struct TALER_Amount fee_withdraw; + struct TALER_Amount fee_deposit; + struct TALER_Amount fee_refresh; + + TALER_amount_ntoh (&value, + &dki->value); + TALER_amount_ntoh (&fee_withdraw, + &dki->fee_withdraw); + TALER_amount_ntoh (&fee_deposit, + &dki->fee_deposit); + TALER_amount_ntoh (&fee_refresh, + &dki->fee_refresh); return json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}", "master_sig", @@ -128,13 +141,13 @@ denom_key_issue_to_json (struct GNUNET_CRYPTO_rsa_PublicKey *pk, "denom_pub", TALER_JSON_from_rsa_public_key (pk), "value", - TALER_JSON_from_amount (TALER_amount_ntoh (dki->value)), + TALER_JSON_from_amount (&value), "fee_withdraw", - TALER_JSON_from_amount (TALER_amount_ntoh (dki->fee_withdraw)), + TALER_JSON_from_amount (&fee_withdraw), "fee_deposit", - TALER_JSON_from_amount (TALER_amount_ntoh (dki->fee_deposit)), + TALER_JSON_from_amount (&fee_deposit), "fee_refresh", - TALER_JSON_from_amount (TALER_amount_ntoh (dki->fee_refresh))); + TALER_JSON_from_amount (&fee_refresh)); } diff --git a/src/mint/taler-mint-httpd_parsing.c b/src/mint/taler-mint-httpd_parsing.c index 6c5f72b32..b8bc043ec 100644 --- a/src/mint/taler-mint-httpd_parsing.c +++ b/src/mint/taler-mint-httpd_parsing.c @@ -878,8 +878,10 @@ TALER_MINT_parse_amount_json (struct MHD_Connection *connection, json_int_t value; json_int_t fraction; const char *currency; - struct TALER_Amount a; + memset (amount, + 0, + sizeof (struct TALER_Amount)); if (-1 == json_unpack (f, "{s:I, s:I, s:s}", "value", &value, @@ -897,7 +899,7 @@ TALER_MINT_parse_amount_json (struct MHD_Connection *connection, } if ( (value < 0) || (fraction < 0) || - (value > UINT32_MAX) || + (value > UINT64_MAX) || (fraction > UINT32_MAX) ) { LOG_WARNING ("Amount specified not in allowed range\n"); @@ -922,11 +924,11 @@ TALER_MINT_parse_amount_json (struct MHD_Connection *connection, return GNUNET_SYSERR; return GNUNET_NO; } - a.value = (uint32_t) value; - a.fraction = (uint32_t) fraction; + amount->value = (uint64_t) value; + amount->fraction = (uint32_t) fraction; GNUNET_assert (strlen (MINT_CURRENCY) < TALER_CURRENCY_LEN); - strcpy (a.currency, MINT_CURRENCY); - *amount = TALER_amount_normalize (a); + strcpy (amount->currency, MINT_CURRENCY); + TALER_amount_normalize (amount); return GNUNET_OK; } diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c index c7bda5a79..e62521ef4 100644 --- a/src/mint/taler-mint-httpd_refresh.c +++ b/src/mint/taler-mint-httpd_refresh.c @@ -82,6 +82,8 @@ handle_refresh_melt_binary (struct MHD_Connection *connection, struct TALER_Amount cost; struct TALER_Amount total_cost; struct TALER_Amount melt; + struct TALER_Amount value; + struct TALER_Amount fee_withdraw; struct TALER_Amount total_melt; /* check that signature from the session public key is ok */ @@ -107,7 +109,8 @@ handle_refresh_melt_binary (struct MHD_Connection *connection, body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_SESSION); body.purpose.size = htonl (sizeof (struct RefreshMeltSessionSignature)); body.melt_hash = melt_hash; - body.amount = TALER_amount_hton (coin_melt_details->melt_amount); + TALER_amount_hton (&body.amount, + &coin_melt_details->melt_amount); if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_REFRESH_MELT_SESSION, &body.purpose, @@ -130,11 +133,22 @@ handle_refresh_melt_binary (struct MHD_Connection *connection, { dki = &TALER_MINT_get_denom_key (key_state, denom_pubs[i])->issue; - cost = TALER_amount_add (TALER_amount_ntoh (dki->value), - TALER_amount_ntoh (dki->fee_withdraw)); + TALER_amount_ntoh (&value, + &dki->value); + TALER_amount_ntoh (&fee_withdraw, + &dki->fee_withdraw); // FIXME: #3637 - total_cost = TALER_amount_add (cost, - total_cost); + if ( (GNUNET_OK != + TALER_amount_add (&cost, + &value, + &fee_withdraw)) || + (GNUNET_OK != + TALER_amount_add (&total_cost, + &cost, + &total_cost)) ) + { + // FIXME... + } } // FIXME: badness, use proper way to set to zero... @@ -146,13 +160,18 @@ handle_refresh_melt_binary (struct MHD_Connection *connection, // melt = coin_values[i]; // FIXME: #3636! // FIXME: #3637 - total_melt = TALER_amount_add (melt, - total_melt); + if (GNUNET_OK != + TALER_amount_add (&total_melt, + &melt, + &total_melt)) + { + // FIXME ... + } } TALER_MINT_key_state_release (key_state); if (0 != - TALER_amount_cmp (total_cost, - total_melt) ) + TALER_amount_cmp (&total_cost, + &total_melt) ) { /* We require total value of coins being melted and total value of coins being generated to match! */ @@ -269,7 +288,8 @@ verify_coin_public_info (struct MHD_Connection *connection, body.purpose.size = htonl (sizeof (struct RefreshMeltCoinSignature)); body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_COIN); body.melt_hash = *melt_hash; - body.amount = TALER_amount_hton (r_melt_detail->melt_amount); + TALER_amount_hton (&body.amount, + &r_melt_detail->melt_amount); body.coin_pub = r_public_info->coin_pub; if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_COIN, diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index 3d827b118..a4b442152 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -299,7 +299,8 @@ TALER_MINT_reply_deposit_success (struct MHD_Connection *connection, dc.h_contract = *h_contract; dc.h_wire = *h_wire; dc.transaction_id = GNUNET_htonll (transaction_id); - dc.amount = TALER_amount_hton (*amount); + TALER_amount_hton (&dc.amount, + amount); dc.coin_pub = *coin_pub; dc.merchant = *merchant; TALER_MINT_keys_sign (&dc.purpose, @@ -346,7 +347,8 @@ compile_transaction_history (const struct TALER_MINT_DB_TransactionList *tl) dr.h_contract = deposit->h_contract; dr.h_wire = deposit->h_wire; dr.transaction_id = GNUNET_htonll (deposit->transaction_id); - dr.amount = TALER_amount_hton (deposit->amount); + TALER_amount_hton (&dr.amount, + &deposit->amount); dr.coin_pub = deposit->coin.coin_pub; transaction = TALER_JSON_from_ecdsa_sig (&dr.purpose, &deposit->csig); @@ -362,7 +364,8 @@ compile_transaction_history (const struct TALER_MINT_DB_TransactionList *tl) ms.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_COIN); ms.purpose.size = htonl (sizeof (struct RefreshMeltCoinSignature)); ms.melt_hash = melt->melt_hash; - ms.amount = TALER_amount_hton (melt->amount); + TALER_amount_hton (&ms.amount, + &melt->amount); ms.coin_pub = melt->coin.coin_pub; transaction = TALER_JSON_from_ecdsa_sig (&ms.purpose, &melt->coin_sig); @@ -382,7 +385,7 @@ compile_transaction_history (const struct TALER_MINT_DB_TransactionList *tl) json_array_append_new (history, json_pack ("{s:s, s:o}", "type", type, - "amount", TALER_JSON_from_amount (value), + "amount", TALER_JSON_from_amount (&value), "signature", transaction)); } return history; @@ -419,7 +422,7 @@ TALER_MINT_reply_deposit_insufficient_funds (struct MHD_Connection *connection, * * @param rh reserve history to JSON-ify * @param balance[OUT] set to current reserve balance - * @return json representation of the @a rh + * @return json representation of the @a rh, NULL on error */ static json_t * compile_reserve_history (const struct ReserveHistory *rh, @@ -446,14 +449,20 @@ compile_reserve_history (const struct ReserveHistory *rh, if (0 == ret) deposit_total = pos->details.bank->amount; else - deposit_total = TALER_amount_add (deposit_total, - pos->details.bank->amount); + if (GNUNET_OK != + TALER_amount_add (&deposit_total, + &deposit_total, + &pos->details.bank->amount)) + { + json_decref (json_history); + return NULL; + } ret = 1; json_array_append_new (json_history, json_pack ("{s:s, s:o, s:o}", "type", "DEPOSIT", "wire", pos->details.bank->wire, - "amount", TALER_JSON_from_amount (pos->details.bank->amount))); + "amount", TALER_JSON_from_amount (&pos->details.bank->amount))); break; case TALER_MINT_DB_RO_WITHDRAW_COIN: break; @@ -472,12 +481,20 @@ compile_reserve_history (const struct ReserveHistory *rh, dki = TALER_MINT_get_denom_key (key_state, pos->details.withdraw->denom_pub); - value = TALER_amount_ntoh (dki->issue.value); + TALER_amount_ntoh (&value, + &dki->issue.value); if (0 == ret) withdraw_total = value; else - withdraw_total = TALER_amount_add (withdraw_total, - value); + if (GNUNET_OK != + TALER_amount_add (&withdraw_total, + &withdraw_total, + &value)) + { + TALER_MINT_key_state_release (key_state); + json_decref (json_history); + return NULL; + } ret = 1; wr.purpose.purpose = htonl (TALER_SIGNATURE_WITHDRAW); wr.purpose.size = htonl (sizeof (struct TALER_WithdrawRequest)); @@ -493,15 +510,22 @@ compile_reserve_history (const struct ReserveHistory *rh, json_pack ("{s:s, s:o, s:o}", "type", "WITHDRAW", "signature", transaction, - "amount", TALER_JSON_from_amount (value))); + "amount", TALER_JSON_from_amount (&value))); break; } } TALER_MINT_key_state_release (key_state); - *balance = TALER_amount_subtract (deposit_total, - withdraw_total); + if (GNUNET_SYSERR == + TALER_amount_subtract (balance, + &deposit_total, + &withdraw_total)) + { + GNUNET_break (0); + json_decref (json_history); + return NULL; + } return json_history; } @@ -524,7 +548,10 @@ TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection, json_history = compile_reserve_history (rh, &balance); - json_balance = TALER_JSON_from_amount (balance); + if (NULL == json_history) + return TALER_MINT_reply_internal_error (connection, + "balance calculation failure"); + json_balance = TALER_JSON_from_amount (&balance); ret = TALER_MINT_reply_json_pack (connection, MHD_HTTP_OK, "{s:o, s:o}", @@ -556,7 +583,10 @@ TALER_MINT_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connec json_history = compile_reserve_history (rh, &balance); - json_balance = TALER_JSON_from_amount (balance); + if (NULL == json_history) + return TALER_MINT_reply_internal_error (connection, + "balance calculation failure"); + json_balance = TALER_JSON_from_amount (&balance); ret = TALER_MINT_reply_json_pack (connection, MHD_HTTP_PAYMENT_REQUIRED, "{s:s, s:o, s:o}", @@ -625,9 +655,9 @@ TALER_MINT_reply_refresh_melt_insufficient_funds (struct MHD_Connection *connect "error", "insufficient funds", "coin-pub", TALER_JSON_from_data (coin_pub, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)), - "original-value", TALER_JSON_from_amount (coin_value), - "residual-value", TALER_JSON_from_amount (residual), - "requested-value", TALER_JSON_from_amount (requested), + "original-value", TALER_JSON_from_amount (&coin_value), + "residual-value", TALER_JSON_from_amount (&residual), + "requested-value", TALER_JSON_from_amount (&requested), "history", history); } diff --git a/src/mint/taler-mint-keyup.c b/src/mint/taler-mint-keyup.c index 33bb87249..759e7c1b3 100644 --- a/src/mint/taler-mint-keyup.c +++ b/src/mint/taler-mint-keyup.c @@ -233,10 +233,14 @@ hash_coin_type (const struct CoinTypeParams *p, sizeof (struct CoinTypeNBO)); p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend); p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw); - p_nbo.value = TALER_amount_hton (p->value); - p_nbo.fee_withdraw = TALER_amount_hton (p->fee_withdraw); - p_nbo.fee_deposit = TALER_amount_hton (p->fee_deposit); - p_nbo.fee_refresh = TALER_amount_hton (p->fee_refresh); + TALER_amount_hton (&p_nbo.value, + &p->value); + TALER_amount_hton (&p_nbo.fee_withdraw, + &p->fee_withdraw); + TALER_amount_hton (&p_nbo.fee_deposit, + &p->fee_deposit); + TALER_amount_hton (&p_nbo.fee_refresh, + &p->fee_refresh); p_nbo.rsa_keysize = htonl (p->rsa_keysize); GNUNET_CRYPTO_hash (&p_nbo, sizeof (struct CoinTypeNBO), @@ -273,7 +277,7 @@ get_cointype_dir (const struct CoinTypeParams *p) GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1); hash_str[HASH_CUTOFF] = 0; - val_str = TALER_amount_to_string (p->value); + val_str = TALER_amount_to_string (&p->value); for (i = 0; i < strlen (val_str); i++) if ( (':' == val_str[i]) || ('.' == val_str[i]) ) @@ -687,11 +691,14 @@ create_denomkey_issue (const struct CoinTypeParams *params, dki->issue.expire_spend = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor, params->duration_spend)); - dki->issue.value = TALER_amount_hton (params->value); - dki->issue.fee_withdraw = TALER_amount_hton (params->fee_withdraw); - dki->issue.fee_deposit = TALER_amount_hton (params->fee_deposit); - dki->issue.fee_refresh = TALER_amount_hton (params->fee_refresh); - + TALER_amount_hton (&dki->issue.value, + ¶ms->value); + TALER_amount_hton (&dki->issue.fee_withdraw, + ¶ms->fee_withdraw); + TALER_amount_hton (&dki->issue.fee_deposit, + ¶ms->fee_deposit); + TALER_amount_hton (&dki->issue.fee_refresh, + ¶ms->fee_refresh); dki->issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOM); dki->issue.purpose.size = htonl (sizeof (struct TALER_MINT_DenomKeyIssuePriv) - offsetof (struct TALER_MINT_DenomKeyIssuePriv, diff --git a/src/mint/taler-mint-reservemod.c b/src/mint/taler-mint-reservemod.c index dea59d6ee..df3f0cd5d 100644 --- a/src/mint/taler-mint-reservemod.c +++ b/src/mint/taler-mint-reservemod.c @@ -161,9 +161,11 @@ reservemod_add (struct TALER_Amount denom) "balance_fraction", "balance_currency", &old_denom)); - new_denom = TALER_amount_add (old_denom, - denom); - new_denom_nbo = TALER_amount_hton (new_denom); + TALER_amount_add (&new_denom, + &old_denom, + &denom); + TALER_amount_hton (&new_denom_nbo, + &new_denom); result = PQexecParams (db_conn, "UPDATE reserves" " SET balance_value = $1, balance_fraction = $2, status_sig = NULL, status_sign_pub = NULL" diff --git a/src/util/amount.c b/src/util/amount.c index b3e3b4217..5e7f69fd9 100644 --- a/src/util/amount.c +++ b/src/util/amount.c @@ -30,16 +30,6 @@ #include "taler_util.h" #include -/** - * - */ -#define AMOUNT_FRAC_BASE 1000000 - -/** - * - */ -#define AMOUNT_FRAC_LEN 6 - /** * Parse money amount description, in the format "A:B.C". @@ -53,159 +43,260 @@ int TALER_string_to_amount (const char *str, struct TALER_Amount *denom) { - size_t i; // pos in str - int n; // number tmp - size_t c; // currency pos - uint32_t b; // base for suffix + size_t i; + int n; + uint32_t b; + const char *colon; + const char *value; memset (denom, 0, sizeof (struct TALER_Amount)); - - i = 0; - while (isspace(str[i])) - i++; - - if (0 == str[i]) + /* skip leading whitespace */ + while (isspace(str[0])) + str++; + if ('\0' == str[0]) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "null before currency\n"); + "Null before currency\n"); return GNUNET_SYSERR; } - - c = 0; - while (str[i] != ':') + /* parse currency */ + colon = strchr (str, (int) ':'); + if ( (NULL == colon) || + ((colon - str) >= TALER_CURRENCY_LEN) ) { - if (0 == str[i]) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "null before colon"); - return GNUNET_SYSERR; - } - if (c > 3) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "currency too long\n"); - return GNUNET_SYSERR; - } - denom->currency[c] = str[i]; - c++; - i++; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid currency specified before colon: `%s'", + str); + goto fail; } - - // skip colon - i++; - - if (0 == str[i]) + memcpy (denom->currency, + str, + colon - str); + /* skip colon */ + value = colon + 1; + if ('\0' == value[0]) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "null before value\n"); - return GNUNET_SYSERR; + "Null before value\n"); + goto fail; } - while (str[i] != '.') + /* parse value */ + i = 0; + while ('.' != value[i]) { - if (0 == str[i]) + if ('\0' == value[i]) { return GNUNET_OK; } + if ( (str[i] < '0') || (str[i] > '9') ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid character `%c'\n", + str[i]); + goto fail; + } n = str[i] - '0'; - if (n < 0 || n > 9) + if (denom->value * 10 + n < denom->value) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "invalid character '%c' before comma at %u\n", - (char) n, - i); - return GNUNET_SYSERR; + "Value too large\n"); + goto fail; } denom->value = (denom->value * 10) + n; i++; } - // skip the dot + /* skip the dot */ i++; - if (0 == str[i]) + /* parse fraction */ + if ('\0' == str[i]) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "null after dot"); - return GNUNET_SYSERR; + "Null after dot"); + goto fail; } - - b = 100000; - - while (0 != str[i]) + b = TALER_AMOUNT_FRAC_BASE / 10; + while ('\0' != str[i]) { - n = str[i] - '0'; - if ( (0 == b) || (n < 0) || (n > 9) ) + if (0 == b) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "error after comma"); - return GNUNET_SYSERR; + "Fractional value too small (only %u digits supported)", + (unsigned int) TALER_AMOUNT_FRAC_LEN); + goto fail; + } + if ( (str[i] < '0') || (str[i] > '9') ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Error after comma"); + goto fail; } + n = str[i] - '0'; denom->fraction += n * b; b /= 10; i++; } - return GNUNET_OK; + + fail: + /* set currency to 'invalid' to prevent accidental use */ + memset (denom->currency, + 0, + TALER_CURRENCY_LEN); + return GNUNET_SYSERR; +} + + +/** + * Convert amount from host to network representation. + * + * @param res where to store amount in network representation + * @param d amount in host representation + */ +void +TALER_amount_hton (struct TALER_AmountNBO *res, + const struct TALER_Amount *d) +{ + res->value = GNUNET_htonll (d->value); + res->fraction = htonl (d->fraction); + memcpy (res->currency, + d->currency, + TALER_CURRENCY_LEN); +} + + +/** + * Convert amount from network to host representation. + * + * @param res where to store amount in host representation + * @param d amount in network representation + */ +void +TALER_amount_ntoh (struct TALER_Amount *res, + const struct TALER_AmountNBO *dn) +{ + res->value = GNUNET_ntohll (dn->value); + res->fraction = ntohl (dn->fraction); + memcpy (res->currency, + dn->currency, + TALER_CURRENCY_LEN); } /** - * FIXME + * Get the value of "zero" in a particular currency. + * + * @param cur currency description + * @param denom denomination to write the result to + * @return #GNUNET_OK if @a cur is a valid currency specification, + * #GNUNET_SYSERR if it is invalid. */ -struct TALER_AmountNBO -TALER_amount_hton (const struct TALER_Amount d) +int +TALER_amount_get_zero (const char *cur, + struct TALER_Amount *denom) { - struct TALER_AmountNBO dn; - dn.value = htonl (d.value); - dn.fraction = htonl (d.fraction); - memcpy (dn.currency, d.currency, TALER_CURRENCY_LEN); + size_t slen; - return dn; + slen = strlen (cur); + if (slen >= TALER_CURRENCY_LEN) + return GNUNET_SYSERR; + memset (denom, + 0, + sizeof (struct TALER_Amount)); + memcpy (denom->currency, + cur, + slen); + return GNUNET_OK; } /** - * FIXME + * Set @a a to "invalid". + * + * @param a amount to set to invalid */ -struct TALER_Amount -TALER_amount_ntoh (const struct TALER_AmountNBO dn) +static void +invalidate (struct TALER_Amount *a) { - struct TALER_Amount d; - d.value = ntohl (dn.value); - d.fraction = ntohl (dn.fraction); - memcpy (d.currency, dn.currency, TALER_CURRENCY_LEN); + memset (a, + 0, + sizeof (struct TALER_Amount)); +} + - return d; +/** + * Test if @a a is valid + * + * @param a amount to test + * @return #GNUNET_YES if valid, + * #GNUNET_NO if invalid + */ +static int +test_valid (const struct TALER_Amount *a) +{ + return ('\0' != a->currency[0]); +} + + +/** + * Test if @a a1 and @a a2 are the same currency. + * + * @param a1 amount to test + * @param a2 amount to test + * @return #GNUNET_YES if @a a1 and @a a2 are the same currency + * #GNUNET_NO if the currencies are different, + * #GNUNET_SYSERR if either amount is invalid + */ +int +TALER_amount_cmp_currency (const struct TALER_Amount *a1, + const struct TALER_Amount *a2) +{ + if ( (GNUNET_NO == test_valid (a1)) || + (GNUNET_NO == test_valid (a2)) ) + return GNUNET_SYSERR; + if (0 == strcmp (a1->currency, + a2->currency)) + return GNUNET_YES; + return GNUNET_NO; } /** - * Compare the value/fraction of two amounts. Does not compare the currency, - * i.e. comparing amounts with the same value and fraction but different - * currency would return 0. + * Compare the value/fraction of two amounts. Does not compare the currency. + * Comparing amounts of different currencies will cause the program to abort(). + * If unsure, check with #TALER_amount_cmp_currency() first to be sure that + * the currencies of the two amounts are identical. * * @param a1 first amount * @param a2 second amount * @return result of the comparison */ int -TALER_amount_cmp (struct TALER_Amount a1, - struct TALER_Amount a2) +TALER_amount_cmp (const struct TALER_Amount *a1, + const struct TALER_Amount *a2) { - a1 = TALER_amount_normalize (a1); - a2 = TALER_amount_normalize (a2); - if (a1.value == a2.value) + struct TALER_Amount n1; + struct TALER_Amount n2; + + GNUNET_assert (GNUNET_YES == + TALER_amount_cmp_currency (a1, a2)); + n1 = *a1; + n2 = *a2; + TALER_amount_normalize (&n1); + TALER_amount_normalize (&n2); + if (n1.value == n2.value) { - if (a1.fraction < a2.fraction) + if (n1.fraction < n2.fraction) return -1; - if (a1.fraction > a2.fraction) + if (n1.fraction > n2.fraction) return 1; return 0; } - if (a1.value < a2.value) + if (n1.value < n2.value) return -1; return 1; } @@ -214,109 +305,142 @@ TALER_amount_cmp (struct TALER_Amount a1, /** * Perform saturating subtraction of amounts. * + * @param diff where to store (@a a1 - @a a2), or invalid if @a a2 > @a a1 * @param a1 amount to subtract from * @param a2 amount to subtract - * @return (a1-a2) or 0 if a2>=a1 + * @return #GNUNET_OK if the subtraction worked, + * #GNUNET_NO if @a a1 = @a a2 + * #GNUNET_SYSERR if @a a2 > @a a1 or currencies are incompatible; + * @a diff is set to invalid */ -struct TALER_Amount -TALER_amount_subtract (struct TALER_Amount a1, - struct TALER_Amount a2) +int +TALER_amount_subtract (struct TALER_Amount *diff, + const struct TALER_Amount *a1, + const struct TALER_Amount *a2) { - a1 = TALER_amount_normalize (a1); - a2 = TALER_amount_normalize (a2); + struct TALER_Amount n1; + struct TALER_Amount n2; - if (a1.value < a2.value) + if (GNUNET_YES != + TALER_amount_cmp_currency (a1, a2)) { - a1.value = 0; - a1.fraction = 0; - return a1; + invalidate (diff); + return GNUNET_SYSERR; } + n1 = *a1; + n2 = *a2; + TALER_amount_normalize (&n1); + TALER_amount_normalize (&n2); - if (a1.fraction < a2.fraction) + if (n1.fraction < n2.fraction) { - if (0 == a1.value) + if (0 == n1.value) { - a1.fraction = 0; - return a1; + invalidate (diff); + return GNUNET_SYSERR; } - a1.fraction += AMOUNT_FRAC_BASE; - a1.value -= 1; + n1.fraction += TALER_AMOUNT_FRAC_BASE; + n1.value--; } - - a1.fraction -= a2.fraction; - a1.value -= a2.value; - - return a1; + if (n1.value < n2.value) + { + invalidate (diff); + return GNUNET_SYSERR; + } + GNUNET_assert (GNUNET_OK == + TALER_amount_get_zero (a1->currency, + diff)); + GNUNET_assert (n1.fraction >= n2.fraction); + diff->fraction = n1.fraction - n2.fraction; + GNUNET_assert (n1.value >= n2.value); + diff->value = n1.value - n2.value; + if ( (0 == diff->fraction) && + (0 == diff->value) ) + return GNUNET_NO; + return GNUNET_OK; } /** - * Perform saturating addition of amounts. + * Perform addition of amounts. * + * @param sum where to store @a a1 + @a a2, set to "invalid" on overflow * @param a1 first amount to add * @param a2 second amount to add - * @return sum of a1 and a2 + * @return #GNUNET_OK if the addition worked, + * #GNUNET_SYSERR on overflow */ -struct TALER_Amount -TALER_amount_add (struct TALER_Amount a1, - struct TALER_Amount a2) +int +TALER_amount_add (struct TALER_Amount *sum, + const struct TALER_Amount *a1, + const struct TALER_Amount *a2) { - a1 = TALER_amount_normalize (a1); - a2 = TALER_amount_normalize (a2); - - a1.value += a2.value; - a1.fraction += a2.fraction; + struct TALER_Amount n1; + struct TALER_Amount n2; - if (0 == a1.currency[0]) + if (GNUNET_YES != + TALER_amount_cmp_currency (a1, a2)) { - memcpy (a2.currency, - a1.currency, - TALER_CURRENCY_LEN); - } - - if (0 == a2.currency[0]) - { - memcpy (a1.currency, - a2.currency, - TALER_CURRENCY_LEN); + invalidate (sum); + return GNUNET_SYSERR; } - - if ( (0 != a1.currency[0]) && - (0 != memcmp (a1.currency, - a2.currency, - TALER_CURRENCY_LEN)) ) + n1 = *a1; + n2 = *a2; + TALER_amount_normalize (&n1); + TALER_amount_normalize (&n2); + + GNUNET_assert (GNUNET_OK == + TALER_amount_get_zero (a1->currency, + sum)); + sum->value = n1.value + n2.value; + if (sum->value < n1.value) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "adding mismatching currencies\n"); + /* integer overflow */ + invalidate (sum); + return GNUNET_SYSERR; } - - if (a1.value < a2.value) + sum->fraction = n1.fraction + n2.fraction; + if (GNUNET_SYSERR == + TALER_amount_normalize (sum)) { - a1.value = UINT32_MAX; - a2.value = UINT32_MAX; - return a1; + /* integer overflow via carry from fraction */ + invalidate (sum); + return GNUNET_SYSERR; } - - return TALER_amount_normalize (a1); + return GNUNET_OK; } /** * Normalize the given amount. * - * @param amout amount to normalize - * @return normalized amount + * @param amount amount to normalize + * @return #GNUNET_OK if normalization worked + * #GNUNET_NO if value was already normalized + * #GNUNET_SYSERR if value was invalid or could not be normalized */ -struct TALER_Amount -TALER_amount_normalize (struct TALER_Amount amount) +int +TALER_amount_normalize (struct TALER_Amount *amount) { - while ( (amount.value != UINT32_MAX) && - (amount.fraction >= AMOUNT_FRAC_BASE) ) + int ret; + + if (GNUNET_YES != test_valid (amount)) + return GNUNET_SYSERR; + ret = GNUNET_NO; + while ( (amount->value != UINT64_MAX) && + (amount->fraction >= TALER_AMOUNT_FRAC_BASE) ) { - amount.fraction -= AMOUNT_FRAC_BASE; - amount.value += 1; + amount->fraction -= TALER_AMOUNT_FRAC_BASE; + amount->value++; + ret = GNUNET_OK; } - return amount; + if (amount->fraction >= TALER_AMOUNT_FRAC_BASE) + { + /* failed to normalize, adding up fractions caused + main value to overflow! */ + return GNUNET_SYSERR; + } + return ret; } @@ -327,40 +451,40 @@ TALER_amount_normalize (struct TALER_Amount amount) * @return freshly allocated string representation */ char * -TALER_amount_to_string (struct TALER_Amount amount) +TALER_amount_to_string (const struct TALER_Amount *amount) { - char tail[AMOUNT_FRAC_LEN + 1] = { 0 }; - char curr[TALER_CURRENCY_LEN + 1] = { 0 }; - char *result = NULL; - int len; - - memcpy (curr, amount.currency, TALER_CURRENCY_LEN); - - amount = TALER_amount_normalize (amount); - if (0 != amount.fraction) + char *result; + uint32_t n; + char tail[TALER_AMOUNT_FRAC_LEN + 1]; + unsigned int i; + struct TALER_Amount norm; + + if (GNUNET_YES != test_valid (amount)) + return NULL; + norm = *amount; + GNUNET_break (GNUNET_SYSERR != + TALER_amount_normalize (&norm)); + if (0 != (n = norm.fraction)) { - unsigned int i; - uint32_t n = amount.fraction; - for (i = 0; (i < AMOUNT_FRAC_LEN) && (n != 0); i++) + for (i = 0; (i < TALER_AMOUNT_FRAC_LEN) && (0 != n); i++) { - tail[i] = '0' + (n / (AMOUNT_FRAC_BASE / 10)); - n = (n * 10) % (AMOUNT_FRAC_BASE); + tail[i] = '0' + (n / (TALER_AMOUNT_FRAC_BASE / 10)); + n = (n * 10) % (TALER_AMOUNT_FRAC_BASE); } - tail[i] = 0; - len = GNUNET_asprintf (&result, - "%s:%lu.%s", - curr, - (unsigned long) amount.value, - tail); + tail[i] = '\0'; + GNUNET_asprintf (&result, + "%s:%llu.%s", + norm.currency, + (unsigned long long) norm.value, + tail); } else { - len = GNUNET_asprintf (&result, - "%s:%lu", - curr, - (unsigned long) amount.value); + GNUNET_asprintf (&result, + "%s:%llu", + norm.currency, + (unsigned long long) norm.value); } - GNUNET_assert (len > 0); return result; } diff --git a/src/util/json.c b/src/util/json.c index 84fac4c98..7390eb474 100644 --- a/src/util/json.c +++ b/src/util/json.c @@ -48,14 +48,25 @@ * @return a json object describing the amount */ json_t * -TALER_JSON_from_amount (struct TALER_Amount amount) +TALER_JSON_from_amount (const struct TALER_Amount *amount) { json_t *j; - j = json_pack ("{s: s, s:I, s:I}", - "currency", amount.currency, - "value", (json_int_t) amount.value, - "fraction", (json_int_t) amount.fraction); + if ( (amount->value != (uint64_t) ((json_int_t) amount->value)) || + (0 > ((json_int_t) amount->value)) ) + { + /* Theoretically, json_int_t can be a 32-bit "long", or we might + have a 64-bit value which converted to a 63-bit signed long + long causes problems here. So we check. Note that depending + on the platform, the compiler may be able to statically tell + that at least the first check is always false. */ + GNUNET_break (0); + return NULL; + } + j = json_pack ("{s:s, s:I, s:I}", + "currency", amount->currency, + "value", (json_int_t) amount->value, + "fraction", (json_int_t) amount->fraction); GNUNET_assert (NULL != j); return j; } -- cgit v1.2.3