-refactor coin history checker

This commit is contained in:
Christian Grothoff 2022-05-23 00:19:57 +02:00
parent 3599ac0ac2
commit 3ee8879ada
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC

View File

@ -27,7 +27,7 @@
/**
* UUID array for duplicate detection.
* Context for history entry helpers.
*/
struct HistoryParseContext
{
@ -543,7 +543,7 @@ TALER_EXCHANGE_parse_reserve_history (
.reserve_pub = reserve_pub,
.uuids = uuid,
.total_in = total_in,
.total_in = total_out
.total_out = total_out
};
GNUNET_assert (GNUNET_OK ==
@ -642,71 +642,74 @@ TALER_EXCHANGE_free_reserve_history (
}
// FIMXE: also transform with helpers...
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_verify_coin_history (
const struct TALER_EXCHANGE_DenomPublicKey *dk,
const char *currency,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
json_t *history,
struct TALER_DenominationHashP *h_denom_pub,
struct TALER_Amount *total)
/**
* Context for coin helpers.
*/
struct CoinHistoryParseContext
{
size_t len;
/**
* Denomination of the coin.
*/
const struct TALER_EXCHANGE_DenomPublicKey *dk;
/**
* Our coin public key.
*/
const struct TALER_CoinSpendPublicKeyP *coin_pub;
/**
* Hash of @e dk, set from parsing.
*/
struct TALER_DenominationHashP *h_denom_pub;
/**
* Where to sum up total refunds.
*/
struct TALER_Amount rtotal;
/**
* Where to sum up fees.
*/
struct TALER_Amount fee;
if (NULL == history)
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
len = json_array_size (history);
if (0 == len)
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (currency,
total));
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (currency,
&rtotal));
for (size_t off = 0; off<len; off++)
{
int add;
json_t *transaction;
struct TALER_Amount amount;
const char *type;
struct GNUNET_JSON_Specification spec_glob[] = {
TALER_JSON_spec_amount_any ("amount",
&amount),
GNUNET_JSON_spec_string ("type",
&type),
GNUNET_JSON_spec_end ()
/**
* Total amount encountered.
*/
struct TALER_Amount *total;
};
transaction = json_array_get (history,
off);
if (GNUNET_OK !=
GNUNET_JSON_parse (transaction,
spec_glob,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (GNUNET_YES !=
TALER_amount_cmp_currency (&amount,
&rtotal))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
add = GNUNET_SYSERR;
if (0 == strcasecmp (type,
"DEPOSIT"))
/**
* Signature of functions that operate on one of
* the coin's history entries.
*
* @param[in,out] pc overall context
* @param amount main amount of this operation
* @param transaction JSON details for the operation
* @return #GNUNET_SYSERR on error,
* #GNUNET_OK to add, #GNUNET_NO to subtract
*/
typedef enum GNUNET_GenericReturnValue
(*CoinCheckHelper)(struct CoinHistoryParseContext *pc,
const struct TALER_Amount *amount,
json_t *transaction);
/**
* Handle deposit entry in the coin's history.
*
* @param[in,out] pc overall context
* @param amount main amount of this operation
* @param transaction JSON details for the operation
* @return #GNUNET_SYSERR on error,
* #GNUNET_OK to add, #GNUNET_NO to subtract
*/
static enum GNUNET_GenericReturnValue
help_deposit (struct CoinHistoryParseContext *pc,
const struct TALER_Amount *amount,
json_t *transaction)
{
struct TALER_MerchantWireHashP h_wire;
struct TALER_PrivateContractHashP h_contract_terms;
@ -725,7 +728,7 @@ TALER_EXCHANGE_verify_coin_history (
GNUNET_JSON_spec_fixed_auto ("h_wire",
&h_wire),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
h_denom_pub),
pc->h_denom_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
&hac),
@ -737,7 +740,7 @@ TALER_EXCHANGE_verify_coin_history (
&refund_deadline),
NULL),
TALER_JSON_spec_amount_any ("deposit_fee",
&fee),
&pc->fee),
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
&merchant_pub),
GNUNET_JSON_spec_end ()
@ -753,40 +756,53 @@ TALER_EXCHANGE_verify_coin_history (
}
if (GNUNET_OK !=
TALER_wallet_deposit_verify (
&amount,
&fee,
amount,
&pc->fee,
&h_wire,
&h_contract_terms,
no_hac ? NULL : &hac,
NULL /* h_extensions! */,
h_denom_pub,
pc->h_denom_pub,
wallet_timestamp,
&merchant_pub,
refund_deadline,
coin_pub,
pc->coin_pub,
&sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (NULL != dk)
if (NULL != pc->dk)
{
/* check that deposit fee matches our expectations from /keys! */
if ( (GNUNET_YES !=
TALER_amount_cmp_currency (&fee,
&dk->fees.deposit)) ||
TALER_amount_cmp_currency (&pc->fee,
&pc->dk->fees.deposit)) ||
(0 !=
TALER_amount_cmp (&fee,
&dk->fees.deposit)) )
TALER_amount_cmp (&pc->fee,
&pc->dk->fees.deposit)) )
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
add = GNUNET_YES;
return GNUNET_YES;
}
else if (0 == strcasecmp (type,
"MELT"))
/**
* Handle melt entry in the coin's history.
*
* @param[in,out] pc overall context
* @param amount main amount of this operation
* @param transaction JSON details for the operation
* @return #GNUNET_SYSERR on error,
* #GNUNET_OK to add, #GNUNET_NO to subtract
*/
static enum GNUNET_GenericReturnValue
help_melt (struct CoinHistoryParseContext *pc,
const struct TALER_Amount *amount,
json_t *transaction)
{
struct TALER_CoinSpendSignatureP sig;
struct TALER_RefreshCommitmentP rc;
@ -798,13 +814,13 @@ TALER_EXCHANGE_verify_coin_history (
GNUNET_JSON_spec_fixed_auto ("rc",
&rc),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
h_denom_pub),
pc->h_denom_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
&h_age_commitment),
&no_hac),
TALER_JSON_spec_amount_any ("melt_fee",
&fee),
&pc->fee),
GNUNET_JSON_spec_end ()
};
@ -817,15 +833,15 @@ TALER_EXCHANGE_verify_coin_history (
return GNUNET_SYSERR;
}
if (NULL != dk)
if (NULL != pc->dk)
{
/* check that melt fee matches our expectations from /keys! */
if ( (GNUNET_YES !=
TALER_amount_cmp_currency (&fee,
&dk->fees.refresh)) ||
TALER_amount_cmp_currency (&pc->fee,
&pc->dk->fees.refresh)) ||
(0 !=
TALER_amount_cmp (&fee,
&dk->fees.refresh)) )
TALER_amount_cmp (&pc->fee,
&pc->dk->fees.refresh)) )
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
@ -834,23 +850,36 @@ TALER_EXCHANGE_verify_coin_history (
if (GNUNET_OK !=
TALER_wallet_melt_verify (
&amount,
&fee,
amount,
&pc->fee,
&rc,
h_denom_pub,
pc->h_denom_pub,
no_hac
? NULL
: &h_age_commitment,
coin_pub,
pc->coin_pub,
&sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
add = GNUNET_YES;
return GNUNET_YES;
}
else if (0 == strcasecmp (type,
"REFUND"))
/**
* Handle refund entry in the coin's history.
*
* @param[in,out] pc overall context
* @param amount main amount of this operation
* @param transaction JSON details for the operation
* @return #GNUNET_SYSERR on error,
* #GNUNET_OK to add, #GNUNET_NO to subtract
*/
static enum GNUNET_GenericReturnValue
help_refund (struct CoinHistoryParseContext *pc,
const struct TALER_Amount *amount,
json_t *transaction)
{
struct TALER_PrivateContractHashP h_contract_terms;
struct TALER_MerchantPublicKeyP merchant_pub;
@ -883,13 +912,13 @@ TALER_EXCHANGE_verify_coin_history (
if (0 >
TALER_amount_add (&sig_amount,
&refund_fee,
&amount))
amount))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_merchant_refund_verify (coin_pub,
TALER_merchant_refund_verify (pc->coin_pub,
&h_contract_terms,
rtransaction_id,
&sig_amount,
@ -908,23 +937,36 @@ TALER_EXCHANGE_verify_coin_history (
well-formendess check). */
/* check that refund fee matches our expectations from /keys! */
if (NULL != dk)
if (NULL != pc->dk)
{
if ( (GNUNET_YES !=
TALER_amount_cmp_currency (&refund_fee,
&dk->fees.refund)) ||
&pc->dk->fees.refund)) ||
(0 !=
TALER_amount_cmp (&refund_fee,
&dk->fees.refund)) )
&pc->dk->fees.refund)) )
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
add = GNUNET_NO;
return GNUNET_NO;
}
else if (0 == strcasecmp (type,
"RECOUP"))
/**
* Handle recoup entry in the coin's history.
*
* @param[in,out] pc overall context
* @param amount main amount of this operation
* @param transaction JSON details for the operation
* @return #GNUNET_SYSERR on error,
* #GNUNET_OK to add, #GNUNET_NO to subtract
*/
static enum GNUNET_GenericReturnValue
help_recoup (struct CoinHistoryParseContext *pc,
const struct TALER_Amount *amount,
json_t *transaction)
{
struct TALER_ReservePublicKeyP reserve_pub;
struct GNUNET_TIME_Timestamp timestamp;
@ -933,8 +975,6 @@ TALER_EXCHANGE_verify_coin_history (
struct TALER_ExchangeSignatureP exchange_sig;
struct TALER_CoinSpendSignatureP coin_sig;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount_any ("amount",
&amount),
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
&exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
@ -946,7 +986,7 @@ TALER_EXCHANGE_verify_coin_history (
GNUNET_JSON_spec_fixed_auto ("coin_blind",
&coin_bks),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
h_denom_pub),
pc->h_denom_pub),
GNUNET_JSON_spec_timestamp ("timestamp",
&timestamp),
GNUNET_JSON_spec_end ()
@ -963,8 +1003,8 @@ TALER_EXCHANGE_verify_coin_history (
if (GNUNET_OK !=
TALER_exchange_online_confirm_recoup_verify (
timestamp,
&amount,
coin_pub,
amount,
pc->coin_pub,
&reserve_pub,
&exchange_pub,
&exchange_sig))
@ -973,31 +1013,41 @@ TALER_EXCHANGE_verify_coin_history (
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_wallet_recoup_verify (h_denom_pub,
TALER_wallet_recoup_verify (pc->h_denom_pub,
&coin_bks,
coin_pub,
pc->coin_pub,
&coin_sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
add = GNUNET_YES;
return GNUNET_YES;
}
else if (0 == strcasecmp (type,
"RECOUP-REFRESH"))
/**
* Handle recoup-refresh entry in the coin's history.
*
* @param[in,out] pc overall context
* @param amount main amount of this operation
* @param transaction JSON details for the operation
* @return #GNUNET_SYSERR on error,
* #GNUNET_OK to add, #GNUNET_NO to subtract
*/
static enum GNUNET_GenericReturnValue
help_recoup_refresh (struct CoinHistoryParseContext *pc,
const struct TALER_Amount *amount,
json_t *transaction)
{
/* This is the coin that was subjected to a recoup,
the value being credited to the old coin. */
struct TALER_CoinSpendPublicKeyP old_coin_pub;
union TALER_DenominationBlindingKeyP coin_bks;
struct TALER_Amount amount;
struct GNUNET_TIME_Timestamp timestamp;
struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP exchange_sig;
struct TALER_CoinSpendSignatureP coin_sig;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount_any ("amount",
&amount),
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
&exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
@ -1009,7 +1059,7 @@ TALER_EXCHANGE_verify_coin_history (
GNUNET_JSON_spec_fixed_auto ("coin_blind",
&coin_bks),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
h_denom_pub),
pc->h_denom_pub),
GNUNET_JSON_spec_timestamp ("timestamp",
&timestamp),
GNUNET_JSON_spec_end ()
@ -1026,8 +1076,8 @@ TALER_EXCHANGE_verify_coin_history (
if (GNUNET_OK !=
TALER_exchange_online_confirm_recoup_refresh_verify (
timestamp,
&amount,
coin_pub,
amount,
pc->coin_pub,
&old_coin_pub,
&exchange_pub,
&exchange_sig))
@ -1036,18 +1086,31 @@ TALER_EXCHANGE_verify_coin_history (
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_wallet_recoup_verify (h_denom_pub,
TALER_wallet_recoup_verify (pc->h_denom_pub,
&coin_bks,
coin_pub,
pc->coin_pub,
&coin_sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
add = GNUNET_YES;
return GNUNET_YES;
}
else if (0 == strcasecmp (type,
"OLD-COIN-RECOUP"))
/**
* Handle old coin recoup entry in the coin's history.
*
* @param[in,out] pc overall context
* @param amount main amount of this operation
* @param transaction JSON details for the operation
* @return #GNUNET_SYSERR on error,
* #GNUNET_OK to add, #GNUNET_NO to subtract
*/
static enum GNUNET_GenericReturnValue
help_old_coin_recoup (struct CoinHistoryParseContext *pc,
const struct TALER_Amount *amount,
json_t *transaction)
{
/* This is the coin that was credited in a recoup,
the value being credited to the this coin. */
@ -1056,8 +1119,6 @@ TALER_EXCHANGE_verify_coin_history (
struct TALER_CoinSpendPublicKeyP new_coin_pub;
struct GNUNET_TIME_Timestamp timestamp;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount_any ("amount",
&amount),
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
&exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
@ -1080,35 +1141,139 @@ TALER_EXCHANGE_verify_coin_history (
if (GNUNET_OK !=
TALER_exchange_online_confirm_recoup_refresh_verify (
timestamp,
&amount,
amount,
&new_coin_pub,
coin_pub,
pc->coin_pub,
&exchange_pub,
&exchange_sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
add = GNUNET_NO;
return GNUNET_NO;
}
else if (0 == strcasecmp (type,
"LOCK_NONCE"))
/**
* Handle purse deposit entry in the coin's history.
*
* @param[in,out] pc overall context
* @param amount main amount of this operation
* @param transaction JSON details for the operation
* @return #GNUNET_SYSERR on error,
* #GNUNET_OK to add, #GNUNET_NO to subtract
*/
static enum GNUNET_GenericReturnValue
help_purse_deposit (struct CoinHistoryParseContext *pc,
const struct TALER_Amount *amount,
json_t *transaction)
{
GNUNET_break (0); // FIXME: implement!
return GNUNET_SYSERR;
}
else
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_verify_coin_history (
const struct TALER_EXCHANGE_DenomPublicKey *dk,
const char *currency,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
json_t *history,
struct TALER_DenominationHashP *h_denom_pub,
struct TALER_Amount *total)
{
/* signature not supported, new version on server? */
const struct
{
const char *type;
CoinCheckHelper helper;
} map[] = {
{ "DEPOSIT", &help_deposit },
{ "MELT", &help_melt },
{ "REFUND", &help_refund },
{ "RECOUP", &help_recoup },
{ "RECOUP-REFRESH", &help_recoup_refresh },
{ "OLD-COIN-RECOUP", &help_old_coin_recoup },
{ "PURSE_DEPOSIT", &help_purse_deposit },
{ NULL, NULL }
};
struct CoinHistoryParseContext pc = {
.dk = dk,
.coin_pub = coin_pub,
.total = total,
.h_denom_pub = h_denom_pub
};
size_t len;
if (NULL == history)
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
len = json_array_size (history);
if (0 == len)
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (currency,
total));
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (currency,
&pc.rtotal));
for (size_t off = 0; off<len; off++)
{
enum GNUNET_GenericReturnValue add;
json_t *transaction;
struct TALER_Amount amount;
const char *type;
struct GNUNET_JSON_Specification spec_glob[] = {
TALER_JSON_spec_amount_any ("amount",
&amount),
GNUNET_JSON_spec_string ("type",
&type),
GNUNET_JSON_spec_end ()
};
transaction = json_array_get (history,
off);
if (GNUNET_OK !=
GNUNET_JSON_parse (transaction,
spec_glob,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (GNUNET_YES !=
TALER_amount_cmp_currency (&amount,
&pc.rtotal))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
add = GNUNET_SYSERR;
for (unsigned int i = 0; NULL != map[i].type; i++)
{
if (0 == strcasecmp (type,
map[i].type))
{
add = map[i].helper (&pc,
&amount,
transaction);
break;
}
}
switch (add)
{
case GNUNET_SYSERR:
/* entry type not supported, new version on server? */
GNUNET_break_op (0);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected type `%s' in response\n",
type);
GNUNET_assert (GNUNET_SYSERR == add);
return GNUNET_SYSERR;
}
if (GNUNET_YES == add)
{
case GNUNET_YES:
/* This amount should be added to the total */
if (0 >
TALER_amount_add (total,
@ -1119,35 +1284,30 @@ TALER_EXCHANGE_verify_coin_history (
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
else
{
case GNUNET_NO:
/* This amount should be subtracted from the total.
However, for the implementation, we first *add* up all of
these negative amounts, as we might get refunds before
deposits from a semi-evil exchange. Then, at the end, we do
the subtraction by calculating "total = total - rtotal" */
GNUNET_assert (GNUNET_NO == add);
if (0 >
TALER_amount_add (&rtotal,
&rtotal,
TALER_amount_add (&pc.rtotal,
&pc.rtotal,
&amount))
{
/* overflow in refund history? inconceivable! Bad exchange! */
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
} /* end of switch(add) */
}
}
/* Finally, subtract 'rtotal' from total to handle the subtractions */
if (0 >
TALER_amount_subtract (total,
total,
&rtotal))
&pc.rtotal))
{
/* underflow in history? inconceivable! Bad exchange! */
GNUNET_break_op (0);