fix #3716: make sure amount-API offers proper checks against overflow and other issues

This commit is contained in:
Christian Grothoff 2015-03-18 18:55:41 +01:00
parent 08958c73e8
commit 23bf1eee74
15 changed files with 668 additions and 347 deletions

View File

@ -25,10 +25,27 @@
/** /**
* Number of characters (plus 1 for 0-termination) we use to * Number of characters (plus 1 for 0-termination) we use to
* represent currency names (i.e. EUR, USD, etc.). We use * 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 #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 GNUNET_NETWORK_STRUCT_BEGIN
@ -41,12 +58,12 @@ struct TALER_AmountNBO
/** /**
* Value in the main currency, in NBO. * Value in the main currency, in NBO.
*/ */
uint32_t value; uint64_t value GNUNET_PACKED;
/** /**
* Additinal fractional value, in NBO. * Additinal fractional value, in NBO.
*/ */
uint32_t fraction; uint32_t fraction GNUNET_PACKED;
/** /**
* Type of the currency being represented. * Type of the currency being represented.
@ -65,7 +82,7 @@ struct TALER_Amount
/** /**
* Value (numerator of fraction) * Value (numerator of fraction)
*/ */
uint32_t value; uint64_t value;
/** /**
* Fraction (denominator of fraction) * Fraction (denominator of fraction)
@ -73,7 +90,8 @@ struct TALER_Amount
uint32_t fraction; 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]; char currency[TALER_CURRENCY_LEN];
}; };
@ -92,82 +110,123 @@ TALER_string_to_amount (const char *str,
struct TALER_Amount *denom); 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. * Convert amount from host to network representation.
* *
* @param res where to store amount in network representation
* @param d amount in host representation * @param d amount in host representation
* @return amount in network representation
*/ */
struct TALER_AmountNBO void
TALER_amount_hton (const struct TALER_Amount d); TALER_amount_hton (struct TALER_AmountNBO *res,
const struct TALER_Amount *d);
/** /**
* Convert amount from network to host representation. * Convert amount from network to host representation.
* *
* @param res where to store amount in host representation
* @param d amount in network representation * @param d amount in network representation
* @return amount in host representation
*/ */
struct TALER_Amount void
TALER_amount_ntoh (const struct TALER_AmountNBO dn); TALER_amount_ntoh (struct TALER_Amount *res,
const struct TALER_AmountNBO *dn);
/** /**
* Compare the value/fraction of two amounts. Does not compare the currency, * Compare the value/fraction of two amounts. Does not compare the currency.
* i.e. comparing amounts with the same value and fraction but different * Comparing amounts of different currencies will cause the program to abort().
* currency would return 0. * 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 a1 first amount
* @param a2 second amount * @param a2 second amount
* @return result of the comparison * @return result of the comparison
*/ */
int int
TALER_amount_cmp (struct TALER_Amount a1, TALER_amount_cmp (const struct TALER_Amount *a1,
struct TALER_Amount a2); 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. * 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 a1 amount to subtract from
* @param a2 amount to subtract * @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 int
TALER_amount_subtract (struct TALER_Amount a1, TALER_amount_subtract (struct TALER_Amount *diff,
struct TALER_Amount a2); 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 a1 first amount to add
* @param a2 second 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 int
TALER_amount_add (struct TALER_Amount a1, TALER_amount_add (struct TALER_Amount *sum,
struct TALER_Amount a2); const struct TALER_Amount *a1,
const struct TALER_Amount *a2);
/** /**
* Normalize the given amount. * Normalize the given amount.
* *
* @param amout amount to normalize * @param amount amount to normalize
* @return normalized amount * @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 int
TALER_amount_normalize (struct TALER_Amount amount); TALER_amount_normalize (struct TALER_Amount *amount);
/** /**
* Convert amount to string. * Convert amount to string.
* *
* @param amount amount to convert 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 * char *
TALER_amount_to_string (struct TALER_Amount amount); TALER_amount_to_string (const struct TALER_Amount *amount);
#endif #endif

View File

@ -39,7 +39,7 @@
* @return a json object describing the amount * @return a json object describing the amount
*/ */
json_t * json_t *
TALER_JSON_from_amount (struct TALER_Amount amount); TALER_JSON_from_amount (const struct TALER_Amount *amount);
/** /**

View File

@ -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.start = GNUNET_TIME_absolute_hton (valid_from);
denom_key_issue.expire_withdraw = GNUNET_TIME_absolute_hton (withdraw_valid_until); 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.expire_spend = GNUNET_TIME_absolute_hton (deposit_valid_until);
denom_key_issue.value = TALER_amount_hton (value); TALER_amount_hton (&denom_key_issue.value,
denom_key_issue.fee_withdraw = TALER_amount_hton (fee_withdraw); &value);
denom_key_issue.fee_deposit = TALER_amount_hton (fee_deposit); TALER_amount_hton (&denom_key_issue.fee_withdraw,
denom_key_issue.fee_refresh = TALER_amount_hton (fee_refresh); &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 == EXITIF (GNUNET_SYSERR ==
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOM, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOM,
&denom_key_issue.purpose, &denom_key_issue.purpose,

View File

@ -807,7 +807,8 @@ reserves_update (PGconn *db,
TALER_DB_QUERY_PARAM_PTR (&expiry_nbo), TALER_DB_QUERY_PARAM_PTR (&expiry_nbo),
TALER_DB_QUERY_PARAM_END 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); expiry_nbo = GNUNET_TIME_absolute_hton (reserve->expiry);
result = TALER_DB_exec_prepared (db, result = TALER_DB_exec_prepared (db,
"update_reserve", "update_reserve",
@ -837,7 +838,7 @@ reserves_update (PGconn *db,
int int
TALER_MINT_DB_reserves_in_insert (PGconn *db, TALER_MINT_DB_reserves_in_insert (PGconn *db,
struct Reserve *reserve, struct Reserve *reserve,
const struct TALER_Amount balance, const struct TALER_Amount *balance,
const struct GNUNET_TIME_Absolute expiry) const struct GNUNET_TIME_Absolute expiry)
{ {
struct TALER_AmountNBO balance_nbo; struct TALER_AmountNBO balance_nbo;
@ -862,7 +863,8 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db,
TALER_MINT_DB_rollback (db); TALER_MINT_DB_rollback (db);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
balance_nbo = TALER_amount_hton (balance); TALER_amount_hton (&balance_nbo,
balance);
expiry_nbo = GNUNET_TIME_absolute_hton (expiry); expiry_nbo = GNUNET_TIME_absolute_hton (expiry);
if (GNUNET_NO == reserve_exists) if (GNUNET_NO == reserve_exists)
{ {
@ -907,19 +909,27 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db,
QUERY_ERR (result); QUERY_ERR (result);
goto rollback; goto rollback;
} }
PQclear (result); result = NULL; PQclear (result);
result = NULL;
if (GNUNET_NO == reserve_exists) if (GNUNET_NO == reserve_exists)
{ {
if (GNUNET_OK != TALER_MINT_DB_commit (db)) if (GNUNET_OK != TALER_MINT_DB_commit (db))
return GNUNET_SYSERR; return GNUNET_SYSERR;
reserve->balance = balance; reserve->balance = *balance;
reserve->expiry = expiry; reserve->expiry = expiry;
return GNUNET_OK; return GNUNET_OK;
} }
/* Update reserve */ /* Update reserve */
struct Reserve updated_reserve; struct Reserve updated_reserve;
updated_reserve.pub = reserve->pub; 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); updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry, reserve->expiry);
if (GNUNET_OK != reserves_update (db, &updated_reserve)) if (GNUNET_OK != reserves_update (db, &updated_reserve))
goto rollback; goto rollback;
@ -1350,7 +1360,8 @@ TALER_MINT_DB_insert_deposit (PGconn *db_conn,
GNUNET_CRYPTO_rsa_signature_encode (deposit->coin.denom_sig, GNUNET_CRYPTO_rsa_signature_encode (deposit->coin.denom_sig,
&denom_sig_enc); &denom_sig_enc);
json_wire_enc = json_dumps (deposit->wire, JSON_COMPACT); 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[]= { struct TALER_DB_QueryParam params[]= {
TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub), TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub),
TALER_DB_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size), TALER_DB_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size),

View File

@ -224,7 +224,7 @@ TALER_MINT_DB_reserve_get (PGconn *db,
int int
TALER_MINT_DB_reserves_in_insert (PGconn *db, TALER_MINT_DB_reserves_in_insert (PGconn *db,
struct Reserve *reserve, struct Reserve *reserve,
const struct TALER_Amount balance, const struct TALER_Amount *balance,
const struct GNUNET_TIME_Absolute expiry); const struct GNUNET_TIME_Absolute expiry);

View File

@ -34,26 +34,6 @@
#include "taler-mint-httpd_keystate.h" #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 * Execute a deposit. The validity of the coin and signature
* have already been checked. The database must now check that * 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 (); mks = TALER_MINT_key_state_acquire ();
dki = TALER_MINT_get_denom_key (mks, dki = TALER_MINT_get_denom_key (mks,
deposit->coin.denom_pub); deposit->coin.denom_pub);
value = TALER_amount_ntoh (dki->issue.value); TALER_amount_ntoh (&value,
fee_deposit = TALER_amount_ntoh (dki->issue.fee_deposit); &dki->issue.value);
fee_refresh = TALER_amount_ntoh (dki->issue.fee_refresh); TALER_amount_ntoh (&fee_deposit,
&dki->issue.fee_deposit);
TALER_amount_ntoh (&fee_refresh,
&dki->issue.fee_refresh);
TALER_MINT_key_state_release (mks); TALER_MINT_key_state_release (mks);
if (GNUNET_OK != 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, tl = TALER_MINT_DB_get_coin_transactions (db_conn,
&deposit->coin.coin_pub); &deposit->coin.coin_pub);
spent = fee_deposit; /* fee for THIS transaction */ spent = fee_deposit; /* fee for THIS transaction */
/* FIXME: need to deal better with integer overflows if (GNUNET_OK !=
in the logic that follows! (change amount.c API! -- #3637) */ TALER_amount_add (&spent,
spent = TALER_amount_add (spent, &spent,
deposit->amount); &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) for (pos = tl; NULL != pos; pos = pos->next)
{ {
switch (pos->type) switch (pos->type)
{ {
case TALER_MINT_DB_TT_DEPOSIT: case TALER_MINT_DB_TT_DEPOSIT:
spent = TALER_amount_add (spent, if ( (GNUNET_OK !=
pos->details.deposit->amount); TALER_amount_add (&spent,
spent = TALER_amount_add (spent, &spent,
fee_deposit); &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; break;
case TALER_MINT_DB_TT_REFRESH_MELT: case TALER_MINT_DB_TT_REFRESH_MELT:
spent = TALER_amount_add (spent, if ( (GNUNET_OK !=
pos->details.melt->amount); TALER_amount_add (&spent,
spent = TALER_amount_add (spent, &spent,
fee_refresh); &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; break;
case TALER_MINT_DB_TT_LOCK: case TALER_MINT_DB_TT_LOCK:
/* should check if lock is still active, /* should check if lock is still active,
@ -146,7 +152,8 @@ 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); TALER_MINT_DB_rollback (db_conn);
ret = TALER_MINT_reply_deposit_insufficient_funds (connection, ret = TALER_MINT_reply_deposit_insufficient_funds (connection,
@ -251,6 +258,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
struct TALER_Amount withdraw_total; struct TALER_Amount withdraw_total;
struct TALER_Amount balance; struct TALER_Amount balance;
struct TALER_Amount value; struct TALER_Amount value;
struct TALER_Amount fee_withdraw;
struct GNUNET_HashCode h_blind; struct GNUNET_HashCode h_blind;
int res; int res;
@ -318,8 +326,20 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
} }
/* calculate amount required including fees */ /* calculate amount required including fees */
amount_required = TALER_amount_add (TALER_amount_ntoh (dki->issue.value), TALER_amount_ntoh (&value,
TALER_amount_ntoh (dki->issue.fee_withdraw)); &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 */ /* calculate balance of the reserve */
res = 0; res = 0;
@ -331,29 +351,45 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
if (0 == (res & 1)) if (0 == (res & 1))
deposit_total = pos->details.bank->amount; deposit_total = pos->details.bank->amount;
else else
deposit_total = TALER_amount_add (deposit_total, if (GNUNET_OK !=
pos->details.bank->amount); 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; res |= 1;
break; break;
case TALER_MINT_DB_RO_WITHDRAW_COIN: case TALER_MINT_DB_RO_WITHDRAW_COIN:
tdki = TALER_MINT_get_denom_key (key_state, tdki = TALER_MINT_get_denom_key (key_state,
pos->details.withdraw->denom_pub); pos->details.withdraw->denom_pub);
value = TALER_amount_ntoh (tdki->issue.value); TALER_amount_ntoh (&value,
&tdki->issue.value);
if (0 == (res & 2)) if (0 == (res & 2))
withdraw_total = value; withdraw_total = value;
else else
withdraw_total = TALER_amount_add (withdraw_total, if (GNUNET_OK !=
value); 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; res |= 2;
break; break;
} }
} }
GNUNET_break (0 > TALER_amount_cmp (withdraw_total, /* All reserve balances should be non-negative */
deposit_total)); GNUNET_break (GNUNET_SYSERR !=
balance = TALER_amount_subtract (deposit_total, TALER_amount_subtract (&balance,
withdraw_total); &deposit_total,
if (0 < TALER_amount_cmp (amount_required, &withdraw_total));
balance)) if (0 < TALER_amount_cmp (&amount_required,
&balance))
{ {
TALER_MINT_key_state_release (key_state); TALER_MINT_key_state_release (key_state);
TALER_MINT_DB_rollback (db_conn); TALER_MINT_DB_rollback (db_conn);
@ -450,7 +486,8 @@ refresh_accept_melts (struct MHD_Connection *connection,
"denom not found")) "denom not found"))
? GNUNET_NO : GNUNET_SYSERR; ? 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, tl = TALER_MINT_DB_get_coin_transactions (db_conn,
&coin_public_info->coin_pub); &coin_public_info->coin_pub);
/* FIXME: #3636: compute how much value is left with this coin and /* 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 /* Refuse to refresh when the coin does not have enough money left to
* pay the refreshing fees of the coin. */ * pay the refreshing fees of the coin. */
if (TALER_amount_cmp (coin_residual, if (TALER_amount_cmp (&coin_residual,
coin_details->melt_amount) < 0) &coin_details->melt_amount) < 0)
{ {
res = (MHD_YES == res = (MHD_YES ==
TALER_MINT_reply_refresh_melt_insufficient_funds (connection, TALER_MINT_reply_refresh_melt_insufficient_funds (connection,

View File

@ -65,7 +65,8 @@ verify_and_execute_deposit (struct MHD_Connection *connection,
dr.h_contract = deposit->h_contract; dr.h_contract = deposit->h_contract;
dr.h_wire = deposit->h_wire; dr.h_wire = deposit->h_wire;
dr.transaction_id = GNUNET_htonll (deposit->transaction_id); 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; dr.coin_pub = deposit->coin.coin_pub;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_WALLET_DEPOSIT, GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_WALLET_DEPOSIT,

View File

@ -114,6 +114,19 @@ static json_t *
denom_key_issue_to_json (struct GNUNET_CRYPTO_rsa_PublicKey *pk, denom_key_issue_to_json (struct GNUNET_CRYPTO_rsa_PublicKey *pk,
const struct TALER_MINT_DenomKeyIssue *dki) 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 return
json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}", json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}",
"master_sig", "master_sig",
@ -128,13 +141,13 @@ denom_key_issue_to_json (struct GNUNET_CRYPTO_rsa_PublicKey *pk,
"denom_pub", "denom_pub",
TALER_JSON_from_rsa_public_key (pk), TALER_JSON_from_rsa_public_key (pk),
"value", "value",
TALER_JSON_from_amount (TALER_amount_ntoh (dki->value)), TALER_JSON_from_amount (&value),
"fee_withdraw", "fee_withdraw",
TALER_JSON_from_amount (TALER_amount_ntoh (dki->fee_withdraw)), TALER_JSON_from_amount (&fee_withdraw),
"fee_deposit", "fee_deposit",
TALER_JSON_from_amount (TALER_amount_ntoh (dki->fee_deposit)), TALER_JSON_from_amount (&fee_deposit),
"fee_refresh", "fee_refresh",
TALER_JSON_from_amount (TALER_amount_ntoh (dki->fee_refresh))); TALER_JSON_from_amount (&fee_refresh));
} }

View File

@ -878,8 +878,10 @@ TALER_MINT_parse_amount_json (struct MHD_Connection *connection,
json_int_t value; json_int_t value;
json_int_t fraction; json_int_t fraction;
const char *currency; const char *currency;
struct TALER_Amount a;
memset (amount,
0,
sizeof (struct TALER_Amount));
if (-1 == json_unpack (f, if (-1 == json_unpack (f,
"{s:I, s:I, s:s}", "{s:I, s:I, s:s}",
"value", &value, "value", &value,
@ -897,7 +899,7 @@ TALER_MINT_parse_amount_json (struct MHD_Connection *connection,
} }
if ( (value < 0) || if ( (value < 0) ||
(fraction < 0) || (fraction < 0) ||
(value > UINT32_MAX) || (value > UINT64_MAX) ||
(fraction > UINT32_MAX) ) (fraction > UINT32_MAX) )
{ {
LOG_WARNING ("Amount specified not in allowed range\n"); 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_SYSERR;
return GNUNET_NO; return GNUNET_NO;
} }
a.value = (uint32_t) value; amount->value = (uint64_t) value;
a.fraction = (uint32_t) fraction; amount->fraction = (uint32_t) fraction;
GNUNET_assert (strlen (MINT_CURRENCY) < TALER_CURRENCY_LEN); GNUNET_assert (strlen (MINT_CURRENCY) < TALER_CURRENCY_LEN);
strcpy (a.currency, MINT_CURRENCY); strcpy (amount->currency, MINT_CURRENCY);
*amount = TALER_amount_normalize (a); TALER_amount_normalize (amount);
return GNUNET_OK; return GNUNET_OK;
} }

View File

@ -82,6 +82,8 @@ handle_refresh_melt_binary (struct MHD_Connection *connection,
struct TALER_Amount cost; struct TALER_Amount cost;
struct TALER_Amount total_cost; struct TALER_Amount total_cost;
struct TALER_Amount melt; struct TALER_Amount melt;
struct TALER_Amount value;
struct TALER_Amount fee_withdraw;
struct TALER_Amount total_melt; struct TALER_Amount total_melt;
/* check that signature from the session public key is ok */ /* 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.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_SESSION);
body.purpose.size = htonl (sizeof (struct RefreshMeltSessionSignature)); body.purpose.size = htonl (sizeof (struct RefreshMeltSessionSignature));
body.melt_hash = melt_hash; 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, if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_REFRESH_MELT_SESSION,
&body.purpose, &body.purpose,
@ -130,11 +133,22 @@ handle_refresh_melt_binary (struct MHD_Connection *connection,
{ {
dki = &TALER_MINT_get_denom_key (key_state, dki = &TALER_MINT_get_denom_key (key_state,
denom_pubs[i])->issue; denom_pubs[i])->issue;
cost = TALER_amount_add (TALER_amount_ntoh (dki->value), TALER_amount_ntoh (&value,
TALER_amount_ntoh (dki->fee_withdraw)); &dki->value);
TALER_amount_ntoh (&fee_withdraw,
&dki->fee_withdraw);
// FIXME: #3637 // FIXME: #3637
total_cost = TALER_amount_add (cost, if ( (GNUNET_OK !=
total_cost); 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... // 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! // melt = coin_values[i]; // FIXME: #3636!
// FIXME: #3637 // FIXME: #3637
total_melt = TALER_amount_add (melt, if (GNUNET_OK !=
total_melt); TALER_amount_add (&total_melt,
&melt,
&total_melt))
{
// FIXME ...
}
} }
TALER_MINT_key_state_release (key_state); TALER_MINT_key_state_release (key_state);
if (0 != if (0 !=
TALER_amount_cmp (total_cost, TALER_amount_cmp (&total_cost,
total_melt) ) &total_melt) )
{ {
/* We require total value of coins being melted and /* We require total value of coins being melted and
total value of coins being generated to match! */ 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.size = htonl (sizeof (struct RefreshMeltCoinSignature));
body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_COIN); body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_COIN);
body.melt_hash = *melt_hash; 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; body.coin_pub = r_public_info->coin_pub;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_COIN, GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_COIN,

View File

@ -299,7 +299,8 @@ TALER_MINT_reply_deposit_success (struct MHD_Connection *connection,
dc.h_contract = *h_contract; dc.h_contract = *h_contract;
dc.h_wire = *h_wire; dc.h_wire = *h_wire;
dc.transaction_id = GNUNET_htonll (transaction_id); 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.coin_pub = *coin_pub;
dc.merchant = *merchant; dc.merchant = *merchant;
TALER_MINT_keys_sign (&dc.purpose, 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_contract = deposit->h_contract;
dr.h_wire = deposit->h_wire; dr.h_wire = deposit->h_wire;
dr.transaction_id = GNUNET_htonll (deposit->transaction_id); 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; dr.coin_pub = deposit->coin.coin_pub;
transaction = TALER_JSON_from_ecdsa_sig (&dr.purpose, transaction = TALER_JSON_from_ecdsa_sig (&dr.purpose,
&deposit->csig); &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.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_COIN);
ms.purpose.size = htonl (sizeof (struct RefreshMeltCoinSignature)); ms.purpose.size = htonl (sizeof (struct RefreshMeltCoinSignature));
ms.melt_hash = melt->melt_hash; 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; ms.coin_pub = melt->coin.coin_pub;
transaction = TALER_JSON_from_ecdsa_sig (&ms.purpose, transaction = TALER_JSON_from_ecdsa_sig (&ms.purpose,
&melt->coin_sig); &melt->coin_sig);
@ -382,7 +385,7 @@ compile_transaction_history (const struct TALER_MINT_DB_TransactionList *tl)
json_array_append_new (history, json_array_append_new (history,
json_pack ("{s:s, s:o}", json_pack ("{s:s, s:o}",
"type", type, "type", type,
"amount", TALER_JSON_from_amount (value), "amount", TALER_JSON_from_amount (&value),
"signature", transaction)); "signature", transaction));
} }
return history; return history;
@ -419,7 +422,7 @@ TALER_MINT_reply_deposit_insufficient_funds (struct MHD_Connection *connection,
* *
* @param rh reserve history to JSON-ify * @param rh reserve history to JSON-ify
* @param balance[OUT] set to current reserve balance * @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 * static json_t *
compile_reserve_history (const struct ReserveHistory *rh, compile_reserve_history (const struct ReserveHistory *rh,
@ -446,14 +449,20 @@ compile_reserve_history (const struct ReserveHistory *rh,
if (0 == ret) if (0 == ret)
deposit_total = pos->details.bank->amount; deposit_total = pos->details.bank->amount;
else else
deposit_total = TALER_amount_add (deposit_total, if (GNUNET_OK !=
pos->details.bank->amount); TALER_amount_add (&deposit_total,
&deposit_total,
&pos->details.bank->amount))
{
json_decref (json_history);
return NULL;
}
ret = 1; ret = 1;
json_array_append_new (json_history, json_array_append_new (json_history,
json_pack ("{s:s, s:o, s:o}", json_pack ("{s:s, s:o, s:o}",
"type", "DEPOSIT", "type", "DEPOSIT",
"wire", pos->details.bank->wire, "wire", pos->details.bank->wire,
"amount", TALER_JSON_from_amount (pos->details.bank->amount))); "amount", TALER_JSON_from_amount (&pos->details.bank->amount)));
break; break;
case TALER_MINT_DB_RO_WITHDRAW_COIN: case TALER_MINT_DB_RO_WITHDRAW_COIN:
break; break;
@ -472,12 +481,20 @@ compile_reserve_history (const struct ReserveHistory *rh,
dki = TALER_MINT_get_denom_key (key_state, dki = TALER_MINT_get_denom_key (key_state,
pos->details.withdraw->denom_pub); pos->details.withdraw->denom_pub);
value = TALER_amount_ntoh (dki->issue.value); TALER_amount_ntoh (&value,
&dki->issue.value);
if (0 == ret) if (0 == ret)
withdraw_total = value; withdraw_total = value;
else else
withdraw_total = TALER_amount_add (withdraw_total, if (GNUNET_OK !=
value); TALER_amount_add (&withdraw_total,
&withdraw_total,
&value))
{
TALER_MINT_key_state_release (key_state);
json_decref (json_history);
return NULL;
}
ret = 1; ret = 1;
wr.purpose.purpose = htonl (TALER_SIGNATURE_WITHDRAW); wr.purpose.purpose = htonl (TALER_SIGNATURE_WITHDRAW);
wr.purpose.size = htonl (sizeof (struct TALER_WithdrawRequest)); 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}", json_pack ("{s:s, s:o, s:o}",
"type", "WITHDRAW", "type", "WITHDRAW",
"signature", transaction, "signature", transaction,
"amount", TALER_JSON_from_amount (value))); "amount", TALER_JSON_from_amount (&value)));
break; break;
} }
} }
TALER_MINT_key_state_release (key_state); TALER_MINT_key_state_release (key_state);
*balance = TALER_amount_subtract (deposit_total, if (GNUNET_SYSERR ==
withdraw_total); TALER_amount_subtract (balance,
&deposit_total,
&withdraw_total))
{
GNUNET_break (0);
json_decref (json_history);
return NULL;
}
return json_history; return json_history;
} }
@ -524,7 +548,10 @@ TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection,
json_history = compile_reserve_history (rh, json_history = compile_reserve_history (rh,
&balance); &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, ret = TALER_MINT_reply_json_pack (connection,
MHD_HTTP_OK, MHD_HTTP_OK,
"{s:o, s:o}", "{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, json_history = compile_reserve_history (rh,
&balance); &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, ret = TALER_MINT_reply_json_pack (connection,
MHD_HTTP_PAYMENT_REQUIRED, MHD_HTTP_PAYMENT_REQUIRED,
"{s:s, s:o, s:o}", "{s:s, s:o, s:o}",
@ -625,9 +655,9 @@ TALER_MINT_reply_refresh_melt_insufficient_funds (struct MHD_Connection *connect
"error", "insufficient funds", "error", "insufficient funds",
"coin-pub", TALER_JSON_from_data (coin_pub, "coin-pub", TALER_JSON_from_data (coin_pub,
sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)), sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)),
"original-value", TALER_JSON_from_amount (coin_value), "original-value", TALER_JSON_from_amount (&coin_value),
"residual-value", TALER_JSON_from_amount (residual), "residual-value", TALER_JSON_from_amount (&residual),
"requested-value", TALER_JSON_from_amount (requested), "requested-value", TALER_JSON_from_amount (&requested),
"history", history); "history", history);
} }

View File

@ -233,10 +233,14 @@ hash_coin_type (const struct CoinTypeParams *p,
sizeof (struct CoinTypeNBO)); sizeof (struct CoinTypeNBO));
p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend); p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend);
p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw); p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw);
p_nbo.value = TALER_amount_hton (p->value); TALER_amount_hton (&p_nbo.value,
p_nbo.fee_withdraw = TALER_amount_hton (p->fee_withdraw); &p->value);
p_nbo.fee_deposit = TALER_amount_hton (p->fee_deposit); TALER_amount_hton (&p_nbo.fee_withdraw,
p_nbo.fee_refresh = TALER_amount_hton (p->fee_refresh); &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); p_nbo.rsa_keysize = htonl (p->rsa_keysize);
GNUNET_CRYPTO_hash (&p_nbo, GNUNET_CRYPTO_hash (&p_nbo,
sizeof (struct CoinTypeNBO), sizeof (struct CoinTypeNBO),
@ -273,7 +277,7 @@ get_cointype_dir (const struct CoinTypeParams *p)
GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1); GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1);
hash_str[HASH_CUTOFF] = 0; 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++) for (i = 0; i < strlen (val_str); i++)
if ( (':' == val_str[i]) || if ( (':' == val_str[i]) ||
('.' == val_str[i]) ) ('.' == val_str[i]) )
@ -687,11 +691,14 @@ create_denomkey_issue (const struct CoinTypeParams *params,
dki->issue.expire_spend = dki->issue.expire_spend =
GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor, GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
params->duration_spend)); params->duration_spend));
dki->issue.value = TALER_amount_hton (params->value); TALER_amount_hton (&dki->issue.value,
dki->issue.fee_withdraw = TALER_amount_hton (params->fee_withdraw); &params->value);
dki->issue.fee_deposit = TALER_amount_hton (params->fee_deposit); TALER_amount_hton (&dki->issue.fee_withdraw,
dki->issue.fee_refresh = TALER_amount_hton (params->fee_refresh); &params->fee_withdraw);
TALER_amount_hton (&dki->issue.fee_deposit,
&params->fee_deposit);
TALER_amount_hton (&dki->issue.fee_refresh,
&params->fee_refresh);
dki->issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOM); dki->issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOM);
dki->issue.purpose.size = htonl (sizeof (struct TALER_MINT_DenomKeyIssuePriv) - dki->issue.purpose.size = htonl (sizeof (struct TALER_MINT_DenomKeyIssuePriv) -
offsetof (struct TALER_MINT_DenomKeyIssuePriv, offsetof (struct TALER_MINT_DenomKeyIssuePriv,

View File

@ -161,9 +161,11 @@ reservemod_add (struct TALER_Amount denom)
"balance_fraction", "balance_fraction",
"balance_currency", "balance_currency",
&old_denom)); &old_denom));
new_denom = TALER_amount_add (old_denom, TALER_amount_add (&new_denom,
denom); &old_denom,
new_denom_nbo = TALER_amount_hton (new_denom); &denom);
TALER_amount_hton (&new_denom_nbo,
&new_denom);
result = PQexecParams (db_conn, result = PQexecParams (db_conn,
"UPDATE reserves" "UPDATE reserves"
" SET balance_value = $1, balance_fraction = $2, status_sig = NULL, status_sign_pub = NULL" " SET balance_value = $1, balance_fraction = $2, status_sig = NULL, status_sign_pub = NULL"

View File

@ -30,16 +30,6 @@
#include "taler_util.h" #include "taler_util.h"
#include <gcrypt.h> #include <gcrypt.h>
/**
*
*/
#define AMOUNT_FRAC_BASE 1000000
/**
*
*/
#define AMOUNT_FRAC_LEN 6
/** /**
* Parse money amount description, in the format "A:B.C". * Parse money amount description, in the format "A:B.C".
@ -53,159 +43,260 @@ int
TALER_string_to_amount (const char *str, TALER_string_to_amount (const char *str,
struct TALER_Amount *denom) struct TALER_Amount *denom)
{ {
size_t i; // pos in str size_t i;
int n; // number tmp int n;
size_t c; // currency pos uint32_t b;
uint32_t b; // base for suffix const char *colon;
const char *value;
memset (denom, memset (denom,
0, 0,
sizeof (struct TALER_Amount)); sizeof (struct TALER_Amount));
/* skip leading whitespace */
while (isspace(str[0]))
str++;
if ('\0' == str[0])
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Null before currency\n");
return GNUNET_SYSERR;
}
/* parse currency */
colon = strchr (str, (int) ':');
if ( (NULL == colon) ||
((colon - str) >= TALER_CURRENCY_LEN) )
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Invalid currency specified before colon: `%s'",
str);
goto fail;
}
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");
goto fail;
}
/* parse value */
i = 0; i = 0;
while (isspace(str[i])) while ('.' != value[i])
i++;
if (0 == str[i])
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, if ('\0' == value[i])
"null before currency\n");
return GNUNET_SYSERR;
}
c = 0;
while (str[i] != ':')
{
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++;
}
// skip colon
i++;
if (0 == str[i])
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"null before value\n");
return GNUNET_SYSERR;
}
while (str[i] != '.')
{
if (0 == str[i])
{ {
return GNUNET_OK; return GNUNET_OK;
} }
n = str[i] - '0'; if ( (str[i] < '0') || (str[i] > '9') )
if (n < 0 || n > 9)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"invalid character '%c' before comma at %u\n", "Invalid character `%c'\n",
(char) n, str[i]);
i); goto fail;
return GNUNET_SYSERR; }
n = str[i] - '0';
if (denom->value * 10 + n < denom->value)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Value too large\n");
goto fail;
} }
denom->value = (denom->value * 10) + n; denom->value = (denom->value * 10) + n;
i++; i++;
} }
// skip the dot /* skip the dot */
i++; i++;
if (0 == str[i]) /* parse fraction */
if ('\0' == str[i])
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"null after dot"); "Null after dot");
return GNUNET_SYSERR; goto fail;
} }
b = TALER_AMOUNT_FRAC_BASE / 10;
b = 100000; while ('\0' != str[i])
while (0 != str[i])
{ {
if (0 == b)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"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'; n = str[i] - '0';
if ( (0 == b) || (n < 0) || (n > 9) )
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"error after comma");
return GNUNET_SYSERR;
}
denom->fraction += n * b; denom->fraction += n * b;
b /= 10; b /= 10;
i++; 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);
}
/**
* 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)
{
size_t slen;
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; return GNUNET_OK;
} }
/** /**
* FIXME * Set @a a to "invalid".
*
* @param a amount to set to invalid
*/ */
struct TALER_AmountNBO static void
TALER_amount_hton (const struct TALER_Amount d) invalidate (struct TALER_Amount *a)
{ {
struct TALER_AmountNBO dn; memset (a,
dn.value = htonl (d.value); 0,
dn.fraction = htonl (d.fraction); sizeof (struct TALER_Amount));
memcpy (dn.currency, d.currency, TALER_CURRENCY_LEN);
return dn;
} }
/** /**
* FIXME * Test if @a a is valid
*
* @param a amount to test
* @return #GNUNET_YES if valid,
* #GNUNET_NO if invalid
*/ */
struct TALER_Amount static int
TALER_amount_ntoh (const struct TALER_AmountNBO dn) test_valid (const struct TALER_Amount *a)
{ {
struct TALER_Amount d; return ('\0' != a->currency[0]);
d.value = ntohl (dn.value);
d.fraction = ntohl (dn.fraction);
memcpy (d.currency, dn.currency, TALER_CURRENCY_LEN);
return d;
} }
/** /**
* Compare the value/fraction of two amounts. Does not compare the currency, * Test if @a a1 and @a a2 are the same currency.
* i.e. comparing amounts with the same value and fraction but different *
* currency would return 0. * @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.
* 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 a1 first amount
* @param a2 second amount * @param a2 second amount
* @return result of the comparison * @return result of the comparison
*/ */
int int
TALER_amount_cmp (struct TALER_Amount a1, TALER_amount_cmp (const struct TALER_Amount *a1,
struct TALER_Amount a2) const struct TALER_Amount *a2)
{ {
a1 = TALER_amount_normalize (a1); struct TALER_Amount n1;
a2 = TALER_amount_normalize (a2); struct TALER_Amount n2;
if (a1.value == a2.value)
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; return -1;
if (a1.fraction > a2.fraction) if (n1.fraction > n2.fraction)
return 1; return 1;
return 0; return 0;
} }
if (a1.value < a2.value) if (n1.value < n2.value)
return -1; return -1;
return 1; return 1;
} }
@ -214,109 +305,142 @@ TALER_amount_cmp (struct TALER_Amount a1,
/** /**
* Perform saturating subtraction of amounts. * 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 a1 amount to subtract from
* @param a2 amount to subtract * @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 int
TALER_amount_subtract (struct TALER_Amount a1, TALER_amount_subtract (struct TALER_Amount *diff,
struct TALER_Amount a2) const struct TALER_Amount *a1,
const struct TALER_Amount *a2)
{ {
a1 = TALER_amount_normalize (a1); struct TALER_Amount n1;
a2 = TALER_amount_normalize (a2); struct TALER_Amount n2;
if (a1.value < a2.value) if (GNUNET_YES !=
TALER_amount_cmp_currency (a1, a2))
{ {
a1.value = 0; invalidate (diff);
a1.fraction = 0; return GNUNET_SYSERR;
return a1;
} }
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; invalidate (diff);
return a1; return GNUNET_SYSERR;
} }
a1.fraction += AMOUNT_FRAC_BASE; n1.fraction += TALER_AMOUNT_FRAC_BASE;
a1.value -= 1; n1.value--;
} }
if (n1.value < n2.value)
a1.fraction -= a2.fraction; {
a1.value -= a2.value; invalidate (diff);
return GNUNET_SYSERR;
return a1; }
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 a1 first amount to add
* @param a2 second 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 int
TALER_amount_add (struct TALER_Amount a1, TALER_amount_add (struct TALER_Amount *sum,
struct TALER_Amount a2) const struct TALER_Amount *a1,
const struct TALER_Amount *a2)
{ {
a1 = TALER_amount_normalize (a1); struct TALER_Amount n1;
a2 = TALER_amount_normalize (a2); struct TALER_Amount n2;
a1.value += a2.value; if (GNUNET_YES !=
a1.fraction += a2.fraction; TALER_amount_cmp_currency (a1, a2))
if (0 == a1.currency[0])
{ {
memcpy (a2.currency, invalidate (sum);
a1.currency, return GNUNET_SYSERR;
TALER_CURRENCY_LEN);
} }
n1 = *a1;
n2 = *a2;
TALER_amount_normalize (&n1);
TALER_amount_normalize (&n2);
if (0 == a2.currency[0]) GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (a1->currency,
sum));
sum->value = n1.value + n2.value;
if (sum->value < n1.value)
{ {
memcpy (a1.currency, /* integer overflow */
a2.currency, invalidate (sum);
TALER_CURRENCY_LEN); return GNUNET_SYSERR;
} }
sum->fraction = n1.fraction + n2.fraction;
if ( (0 != a1.currency[0]) && if (GNUNET_SYSERR ==
(0 != memcmp (a1.currency, TALER_amount_normalize (sum))
a2.currency,
TALER_CURRENCY_LEN)) )
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, /* integer overflow via carry from fraction */
"adding mismatching currencies\n"); invalidate (sum);
return GNUNET_SYSERR;
} }
return GNUNET_OK;
if (a1.value < a2.value)
{
a1.value = UINT32_MAX;
a2.value = UINT32_MAX;
return a1;
}
return TALER_amount_normalize (a1);
} }
/** /**
* Normalize the given amount. * Normalize the given amount.
* *
* @param amout amount to normalize * @param amount amount to normalize
* @return normalized amount * @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 int
TALER_amount_normalize (struct TALER_Amount amount) TALER_amount_normalize (struct TALER_Amount *amount)
{ {
while ( (amount.value != UINT32_MAX) && int ret;
(amount.fraction >= AMOUNT_FRAC_BASE) )
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->fraction -= TALER_AMOUNT_FRAC_BASE;
amount.value += 1; 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 * @return freshly allocated string representation
*/ */
char * 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; unsigned int i;
uint32_t n = amount.fraction; struct TALER_Amount norm;
for (i = 0; (i < AMOUNT_FRAC_LEN) && (n != 0); i++)
if (GNUNET_YES != test_valid (amount))
return NULL;
norm = *amount;
GNUNET_break (GNUNET_SYSERR !=
TALER_amount_normalize (&norm));
if (0 != (n = norm.fraction))
{ {
tail[i] = '0' + (n / (AMOUNT_FRAC_BASE / 10)); for (i = 0; (i < TALER_AMOUNT_FRAC_LEN) && (0 != n); i++)
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; tail[i] = '\0';
len = GNUNET_asprintf (&result, GNUNET_asprintf (&result,
"%s:%lu.%s", "%s:%llu.%s",
curr, norm.currency,
(unsigned long) amount.value, (unsigned long long) norm.value,
tail); tail);
} }
else else
{ {
len = GNUNET_asprintf (&result, GNUNET_asprintf (&result,
"%s:%lu", "%s:%llu",
curr, norm.currency,
(unsigned long) amount.value); (unsigned long long) norm.value);
} }
GNUNET_assert (len > 0);
return result; return result;
} }

View File

@ -48,14 +48,25 @@
* @return a json object describing the amount * @return a json object describing the amount
*/ */
json_t * json_t *
TALER_JSON_from_amount (struct TALER_Amount amount) TALER_JSON_from_amount (const struct TALER_Amount *amount)
{ {
json_t *j; json_t *j;
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}", j = json_pack ("{s:s, s:I, s:I}",
"currency", amount.currency, "currency", amount->currency,
"value", (json_int_t) amount.value, "value", (json_int_t) amount->value,
"fraction", (json_int_t) amount.fraction); "fraction", (json_int_t) amount->fraction);
GNUNET_assert (NULL != j); GNUNET_assert (NULL != j);
return j; return j;
} }