Compare commits

...

5 Commits

Author SHA1 Message Date
4a03a20a04
first steps towards new /keys response: grouped denominations added 2022-06-22 21:01:30 +02:00
Christian Grothoff
007e4bc954
-fix #7262 2022-06-19 14:24:44 +02:00
Christian Grothoff
bad572a010
-add missing checkpointing logic 2022-06-19 14:04:41 +02:00
Christian Grothoff
8da74a6ca7
-more work on taler-helper-auditor-reserve p2p audits 2022-06-18 20:36:44 +02:00
Christian Grothoff
f6a7d4a1ed
-more DB logic for taler-helper-auditor-reserves 2022-06-15 22:31:44 +02:00
16 changed files with 3479 additions and 2444 deletions

View File

@ -1 +1 @@
1655124520
1655640402

View File

@ -113,7 +113,7 @@ currency = TESTKUDOS
[merchant-exchange-default]
CURRENCY = TESTKUDOS
EXCHANGE_BASE_URL = http://localhost:8081/
MASTER_KEY = GPB1CSPV6E49MET2PX6BFWVBGFF8BAE14M05RJB54C28EY1HX42G
MASTER_KEY = JM0NJXHM6Y6HYAPK2WDFH3HDJ2E9KZWGKM3E0FYRV2V3HCTB3DQ0
[merchant-account-merchant]
ACTIVE_default = YES
@ -157,7 +157,7 @@ CONFIG = postgres:///auditor-basedb
[exchange]
LOOKAHEAD_SIGN = 32 weeks 1 day
SIGNKEY_DURATION = 4 weeks
MASTER_PUBLIC_KEY = GPB1CSPV6E49MET2PX6BFWVBGFF8BAE14M05RJB54C28EY1HX42G
MASTER_PUBLIC_KEY = JM0NJXHM6Y6HYAPK2WDFH3HDJ2E9KZWGKM3E0FYRV2V3HCTB3DQ0
SIGNKEY_LEGAL_DURATION = 4 weeks
UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http
@ -175,7 +175,7 @@ DATABASE = postgres:///auditor-basedb
CONFIG = postgres:///auditor-basedb
[auditor]
PUBLIC_KEY = FBVV6V14GCCR95X2W2PMCBTFYZ4S3SX3JN504M8SJTMK1JBBV99G
PUBLIC_KEY = 73NJKBP4MHJF8274K88F4WFWKNYMK8T6MTSE6HHYS6WC01H9YH7G
TINY_AMOUNT = TESTKUDOS:0.01
BASE_URL = http://localhost:8083/

View File

@ -1 +1 @@
GPB1CSPV6E49MET2PX6BFWVBGFF8BAE14M05RJB54C28EY1HX42G
JM0NJXHM6Y6HYAPK2WDFH3HDJ2E9KZWGKM3E0FYRV2V3HCTB3DQ0

File diff suppressed because it is too large Load Diff

View File

@ -1 +1 @@
1655124599
1655640625

View File

@ -1 +1 @@
6CF031C2JWXQB98SNHARWSPJMYR8JM26BVDV08CPH63T2M9JXV30
MREDG0XYVSX4RPYSA6JNQZ93P2DDBG45F3M6RBZXRS49M0JTVN40

File diff suppressed because it is too large Load Diff

View File

@ -2350,6 +2350,11 @@ check_denomination (
* @param cls closure
* @param rowid unique serial ID for the deposit in our DB
* @param deposit deposit details
* @param reserve_pub which reserve is the purse merged into, NULL if unknown
* @param flags purse flags
* @param auditor_balance purse balance (according to the
* auditor during auditing)
* @param purse_total target amount the purse should reach
* @param denom_pub denomination public key of @a coin_pub
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
@ -2358,6 +2363,10 @@ purse_deposit_cb (
void *cls,
uint64_t rowid,
const struct TALER_EXCHANGEDB_PurseDeposit *deposit,
const struct TALER_ReservePublicKeyP *reserve_pub,
enum TALER_WalletAccountMergeFlags flags,
const struct TALER_Amount *auditor_balance,
const struct TALER_Amount *purse_total,
const struct TALER_DenominationPublicKey *denom_pub)
{
struct CoinContext *cc = cls;
@ -2366,6 +2375,10 @@ purse_deposit_cb (
const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
struct DenominationSummary *ds;
(void) flags;
(void) auditor_balance;
(void) purse_total;
(void) reserve_pub;
GNUNET_assert (rowid >= ppc.last_purse_deposits_serial_id);
ppc.last_purse_deposits_serial_id = rowid + 1;
qs = TALER_ARL_get_denomination_info (denom_pub,

View File

@ -69,6 +69,11 @@ static json_t *denomination_key_validity_withdraw_inconsistencies;
*/
static json_t *report_reserve_balance_insufficient_inconsistencies;
/**
* Array of reports about purse balance insufficient inconsitencies.
*/
static json_t *report_purse_balance_insufficient_inconsistencies;
/**
* Total amount reserves were charged beyond their balance.
*/
@ -399,6 +404,54 @@ struct ReserveContext
};
/**
* Create a new reserve for @a reserve_pub in @a rc.
*
* @param[in,out] rc context to update
* @param reserve_pub key for which to create a reserve
* @return NULL on error
*/
static struct ReserveSummary *
setup_reserve (struct ReserveContext *rc,
const struct TALER_ReservePublicKeyP *reserve_pub)
{
struct ReserveSummary *rs;
struct GNUNET_HashCode key;
enum GNUNET_DB_QueryStatus qs;
GNUNET_CRYPTO_hash (reserve_pub,
sizeof (*reserve_pub),
&key);
rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
&key);
if (NULL != rs)
return rs;
rs = GNUNET_new (struct ReserveSummary);
rs->reserve_pub = *reserve_pub;
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (TALER_ARL_currency,
&rs->total_in));
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (TALER_ARL_currency,
&rs->total_out));
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (TALER_ARL_currency,
&rs->total_fee));
if (0 > (qs = load_auditor_reserve_summary (rs)))
{
GNUNET_free (rs);
rc->qs = qs;
return NULL;
}
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (rc->reserves,
&key,
rs,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
return rs;
}
/**
* Function called with details about incoming wire transfers.
*
@ -421,54 +474,26 @@ handle_reserve_in (void *cls,
struct GNUNET_TIME_Timestamp execution_date)
{
struct ReserveContext *rc = cls;
struct GNUNET_HashCode key;
struct ReserveSummary *rs;
struct GNUNET_TIME_Timestamp expiry;
enum GNUNET_DB_QueryStatus qs;
(void) wire_reference;
/* should be monotonically increasing */
GNUNET_assert (rowid >= ppr.last_reserve_in_serial_id);
ppr.last_reserve_in_serial_id = rowid + 1;
GNUNET_CRYPTO_hash (reserve_pub,
sizeof (*reserve_pub),
&key);
rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
&key);
rs = setup_reserve (rc,
reserve_pub);
if (NULL == rs)
{
rs = GNUNET_new (struct ReserveSummary);
GNUNET_break (0);
return GNUNET_SYSERR;
}
TALER_ARL_amount_add (&rs->total_in,
&rs->total_in,
credit);
if (NULL == rs->sender_account)
rs->sender_account = GNUNET_strdup (sender_account_details);
rs->reserve_pub = *reserve_pub;
rs->total_in = *credit;
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (credit->currency,
&rs->total_out));
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (credit->currency,
&rs->total_fee));
if (0 > (qs = load_auditor_reserve_summary (rs)))
{
GNUNET_break (0);
GNUNET_free (rs);
rc->qs = qs;
return GNUNET_SYSERR;
}
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (rc->reserves,
&key,
rs,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
else
{
TALER_ARL_amount_add (&rs->total_in,
&rs->total_in,
credit);
if (NULL == rs->sender_account)
rs->sender_account = GNUNET_strdup (sender_account_details);
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Additional incoming wire transfer for reserve `%s' of %s\n",
TALER_B2S (reserve_pub),
@ -509,7 +534,6 @@ handle_reserve_out (void *cls,
const struct TALER_Amount *amount_with_fee)
{
struct ReserveContext *rc = cls;
struct GNUNET_HashCode key;
struct ReserveSummary *rs;
const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
struct TALER_Amount auditor_amount_with_fee;
@ -608,44 +632,16 @@ handle_reserve_out (void *cls,
rowid,
"amount with fee from exchange does not match denomination value plus fee");
}
GNUNET_CRYPTO_hash (reserve_pub,
sizeof (*reserve_pub),
&key);
rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
&key);
rs = setup_reserve (rc,
reserve_pub);
if (NULL == rs)
{
rs = GNUNET_new (struct ReserveSummary);
rs->reserve_pub = *reserve_pub;
rs->total_out = auditor_amount_with_fee;
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (amount_with_fee->currency,
&rs->total_in));
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (amount_with_fee->currency,
&rs->total_fee));
qs = load_auditor_reserve_summary (rs);
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
GNUNET_free (rs);
rc->qs = qs;
return GNUNET_SYSERR;
}
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (rc->reserves,
&key,
rs,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
else
{
TALER_ARL_amount_add (&rs->total_out,
&rs->total_out,
&auditor_amount_with_fee);
GNUNET_break (0);
return GNUNET_SYSERR;
}
TALER_ARL_amount_add (&rs->total_out,
&rs->total_out,
&auditor_amount_with_fee);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Reserve `%s' reduced by %s from withdraw\n",
TALER_B2S (reserve_pub),
@ -691,7 +687,6 @@ handle_recoup_by_reserve (
const union TALER_DenominationBlindingKeyP *coin_blind)
{
struct ReserveContext *rc = cls;
struct GNUNET_HashCode key;
struct ReserveSummary *rs;
struct GNUNET_TIME_Timestamp expiry;
struct TALER_MasterSignatureP msig;
@ -764,12 +759,13 @@ handle_recoup_by_reserve (
{
rev = "revoked";
}
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (rc->revoked,
&coin->denom_pub_hash.
hash,
(void *) rev,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
GNUNET_assert (
GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (
rc->revoked,
&coin->denom_pub_hash.hash,
(void *) rev,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
}
else
@ -794,42 +790,16 @@ handle_recoup_by_reserve (
amount);
}
GNUNET_CRYPTO_hash (reserve_pub,
sizeof (*reserve_pub),
&key);
rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
&key);
rs = setup_reserve (rc,
reserve_pub);
if (NULL == rs)
{
rs = GNUNET_new (struct ReserveSummary);
rs->reserve_pub = *reserve_pub;
rs->total_in = *amount;
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (amount->currency,
&rs->total_out));
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (amount->currency,
&rs->total_fee));
qs = load_auditor_reserve_summary (rs);
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
GNUNET_free (rs);
rc->qs = qs;
return GNUNET_SYSERR;
}
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (rc->reserves,
&key,
rs,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
else
{
TALER_ARL_amount_add (&rs->total_in,
&rs->total_in,
amount);
GNUNET_break (0);
return GNUNET_SYSERR;
}
TALER_ARL_amount_add (&rs->total_in,
&rs->total_in,
amount);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Additional /recoup value to for reserve `%s' of %s\n",
TALER_B2S (reserve_pub),
@ -923,44 +893,20 @@ handle_reserve_closed (
const struct TALER_WireTransferIdentifierRawP *transfer_details)
{
struct ReserveContext *rc = cls;
struct GNUNET_HashCode key;
struct ReserveSummary *rs;
enum GNUNET_DB_QueryStatus qs;
(void) transfer_details;
/* should be monotonically increasing */
GNUNET_assert (rowid >= ppr.last_reserve_close_serial_id);
ppr.last_reserve_close_serial_id = rowid + 1;
GNUNET_CRYPTO_hash (reserve_pub,
sizeof (*reserve_pub),
&key);
rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
&key);
rs = setup_reserve (rc,
reserve_pub);
if (NULL == rs)
{
rs = GNUNET_new (struct ReserveSummary);
rs->reserve_pub = *reserve_pub;
rs->total_out = *amount_with_fee;
rs->total_fee = *closing_fee;
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (amount_with_fee->currency,
&rs->total_in));
qs = load_auditor_reserve_summary (rs);
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
GNUNET_free (rs);
rc->qs = qs;
return GNUNET_SYSERR;
}
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (rc->reserves,
&key,
rs,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
GNUNET_break (0);
return GNUNET_SYSERR;
}
else
{
struct TALER_Amount expected_fee;
@ -1088,7 +1034,7 @@ verify_reserve_balance (void *cls,
making an illegitimate gain over the amount it dropped.
We don't add the amount to some total simply because it is
not an actualized gain and could be trivially corrected by
restoring the summary. *///
restoring the summary. */
TALER_ARL_report (report_reserve_balance_insufficient_inconsistencies,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("reserve_pub",
@ -1315,6 +1261,12 @@ verify_reserve_balance (void *cls,
* @param cls closure
* @param rowid unique serial ID for the deposit in our DB
* @param deposit deposit details
* @param reserve_pub which reserve is the purse merged into, NULL if unknown
* @param auditor_balance balance of the purse calculated by auditor
* @param flags purse flags
* @param auditor_balance purse balance (according to the
* auditor during auditing)
* @param purse_total target amount the purse should reach
* @param denom_pub denomination public key of @a coin_pub
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
@ -1323,11 +1275,130 @@ handle_purse_deposits (
void *cls,
uint64_t rowid,
const struct TALER_EXCHANGEDB_PurseDeposit *deposit,
const struct TALER_ReservePublicKeyP *reserve_pub,
enum TALER_WalletAccountMergeFlags flags,
const struct TALER_Amount *auditor_balance,
const struct TALER_Amount *purse_total,
const struct TALER_DenominationPublicKey *denom_pub)
{
GNUNET_break (0); // FIXME
/* Credit purse value (if last op)! */
return GNUNET_SYSERR;
struct ReserveContext *rc = cls;
const char *base_url
= (NULL == deposit->exchange_base_url)
? TALER_ARL_exchange_url
: deposit->exchange_base_url;
enum GNUNET_DB_QueryStatus qs;
struct TALER_Amount amount_minus_fee;
struct TALER_Amount new_balance;
struct ReserveSummary *rs;
/* should be monotonically increasing */
GNUNET_assert (rowid >= ppr.last_purse_deposits_serial_id);
ppr.last_purse_deposits_serial_id = rowid + 1;
if (GNUNET_OK !=
TALER_wallet_purse_deposit_verify (base_url,
&deposit->purse_pub,
&deposit->amount,
&deposit->coin_pub,
&deposit->coin_sig))
{
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("operation",
"purse-deposit"),
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
&deposit->amount),
GNUNET_JSON_pack_data_auto ("key_pub",
&deposit->coin_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
&deposit->amount);
return GNUNET_OK;
}
{
const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
enum GNUNET_DB_QueryStatus qs;
struct TALER_DenominationHashP h_denom_pub;
qs = TALER_ARL_get_denomination_info (denom_pub,
&issue,
&h_denom_pub);
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Hard database error trying to get denomination %s from database!\n",
TALER_B2S (denom_pub));
rc->qs = qs;
return GNUNET_SYSERR;
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
report_row_inconsistency ("purse-deposit",
rowid,
"denomination key not found");
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
}
TALER_ARL_amount_subtract (&amount_minus_fee,
&deposit->amount,
&issue->fees.deposit);
}
TALER_ARL_amount_add (&new_balance,
auditor_balance,
&amount_minus_fee);
qs = TALER_ARL_edb->set_purse_balance (TALER_ARL_edb->cls,
&deposit->purse_pub,
&new_balance);
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
if (qs < 0)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
rc->qs = qs;
return GNUNET_SYSERR;
}
if (TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE !=
(flags & TALER_WAMF_MERGE_MODE_MASK))
{
/* This just created the purse, actual credit to
the reserve will be done in handle_account_merged() */
return GNUNET_OK;
}
if ( (NULL != deposit->exchange_base_url) &&
(0 != strcmp (deposit->exchange_base_url,
TALER_ARL_exchange_url)) )
{
/* credited reserve is at another exchange, do NOT credit here! */
return GNUNET_OK;
}
rs = setup_reserve (rc,
reserve_pub);
if (NULL == rs)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if ( (-1 != TALER_amount_cmp (&new_balance,
purse_total)) &&
(-1 == TALER_amount_cmp (auditor_balance,
purse_total)) )
{
/* new balance at or above purse_total
(and previous balance was below); thus
credit reserve with purse value! */
TALER_ARL_amount_add (&rs->total_in,
&rs->total_in,
purse_total);
}
return GNUNET_OK;
}
@ -1338,6 +1409,15 @@ handle_purse_deposits (
*
* @param cls closure
* @param rowid unique serial ID for the deposit in our DB
* @param partner_base_url where is the reserve, NULL for this exchange
* @param amount total amount expected in the purse
* @param balance current balance in the purse (according to the auditor)
* @param flags purse flags
* @param merge_pub merge capability key
* @param reserve_pub reserve the merge affects
* @param merge_sig signature affirming the merge
* @param purse_pub purse key
* @param merge_timestamp when did the merge happen
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
static enum GNUNET_GenericReturnValue
@ -1346,6 +1426,7 @@ handle_purse_merged (
uint64_t rowid,
const char *partner_base_url,
const struct TALER_Amount *amount,
const struct TALER_Amount *balance,
enum TALER_WalletAccountMergeFlags flags,
const struct TALER_PurseMergePublicKeyP *merge_pub,
const struct TALER_ReservePublicKeyP *reserve_pub,
@ -1353,9 +1434,92 @@ handle_purse_merged (
const struct TALER_PurseContractPublicKeyP *purse_pub,
struct GNUNET_TIME_Timestamp merge_timestamp)
{
GNUNET_break (0); // FIXME
/* Credit purse value (if last op)! */
return GNUNET_SYSERR;
struct ReserveContext *rc = cls;
struct ReserveSummary *rs;
char *reserve_url;
/* should be monotonically increasing */
GNUNET_assert (rowid >= ppr.last_purse_merges_serial_id);
ppr.last_purse_merges_serial_id = rowid + 1;
reserve_url
= TALER_reserve_make_payto (NULL == partner_base_url
? TALER_ARL_exchange_url
: partner_base_url,
reserve_pub);
if (GNUNET_OK !=
TALER_wallet_purse_merge_verify (reserve_url,
merge_timestamp,
purse_pub,
merge_pub,
merge_sig))
{
GNUNET_free (reserve_url);
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("operation",
"merge-purse"),
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
amount),
GNUNET_JSON_pack_data_auto ("key_pub",
merge_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount);
return GNUNET_OK;
}
GNUNET_free (reserve_url);
if (TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE ==
(flags & TALER_WAMF_MERGE_MODE_MASK))
{
/* This just created the purse, actual credit to
the reserve will be done in handle_purse_deposits() */
return GNUNET_OK;
}
if ( (NULL != partner_base_url) &&
(0 != strcmp (partner_base_url,
TALER_ARL_exchange_url)) )
{
/* credited reserve is at another exchange, do NOT credit here! */
return GNUNET_OK;
}
rs = setup_reserve (rc,
reserve_pub);
if (NULL == rs)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (-1 == TALER_amount_cmp (balance,
amount))
{
struct TALER_Amount loss;
TALER_ARL_amount_subtract (&loss,
amount,
balance);
/* illegal merge, balance is still below total purse value */
TALER_ARL_report (report_purse_balance_insufficient_inconsistencies,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("operation",
"merge-purse"),
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
&loss),
GNUNET_JSON_pack_data_auto ("purse_pub",
purse_pub)));
TALER_ARL_amount_add (&total_balance_insufficient_loss,
&total_balance_insufficient_loss,
&loss);
return GNUNET_OK;
}
TALER_ARL_amount_add (&rs->total_in,
&rs->total_in,
amount);
// rs->a_expiration_date = FIXME: do we care? If so, set to what (so that the auditor no longer complains about the reserve not being closed)
return GNUNET_OK;
}
@ -1381,11 +1545,55 @@ handle_account_merged (
enum TALER_WalletAccountMergeFlags flags,
const struct TALER_Amount *purse_fee,
struct GNUNET_TIME_Timestamp merge_timestamp,
struct TALER_ReserveSignatureP *reserve_sig)
const struct TALER_ReserveSignatureP *reserve_sig)
{
GNUNET_break (0); // FIXME
/* Debit purse fee */
return GNUNET_SYSERR;
struct ReserveContext *rc = cls;
struct ReserveSummary *rs;
/* should be monotonically increasing */
GNUNET_assert (rowid >= ppr.last_account_merges_serial_id);
ppr.last_account_merges_serial_id = rowid + 1;
if (GNUNET_OK !=
TALER_wallet_account_merge_verify (merge_timestamp,
purse_pub,
purse_expiration,
h_contract_terms,
amount,
purse_fee,
min_age,
flags,
reserve_pub,
reserve_sig))
{
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("operation",
"account-merge"),
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
purse_fee),
GNUNET_JSON_pack_data_auto ("key_pub",
reserve_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
purse_fee);
return GNUNET_OK;
}
rs = setup_reserve (rc,
reserve_pub);
if (NULL == rs)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
TALER_ARL_amount_add (&total_purse_fee_income,
&total_purse_fee_income,
purse_fee);
TALER_ARL_amount_add (&rs->total_out,
&rs->total_out,
purse_fee);
return GNUNET_OK;
}
@ -1407,9 +1615,47 @@ handle_history_request (
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_ReserveSignatureP *reserve_sig)
{
GNUNET_break (0); // FIXME
/* Debit purse fee */
return GNUNET_SYSERR;
struct ReserveContext *rc = cls;
struct ReserveSummary *rs;
/* should be monotonically increasing */
GNUNET_assert (rowid >= ppr.last_history_requests_serial_id);
ppr.last_history_requests_serial_id = rowid + 1;
if (GNUNET_OK !=
TALER_wallet_reserve_history_verify (ts,
history_fee,
reserve_pub,
reserve_sig))
{
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("operation",
"account-history"),
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
history_fee),
GNUNET_JSON_pack_data_auto ("key_pub",
reserve_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
history_fee);
return GNUNET_OK;
}
rs = setup_reserve (rc,
reserve_pub);
if (NULL == rs)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
TALER_ARL_amount_add (&total_history_fee_income,
&total_history_fee_income,
history_fee);
TALER_ARL_amount_add (&rs->total_out,
&rs->total_out,
history_fee);
return GNUNET_OK;
}
@ -1515,17 +1761,6 @@ analyze_reserves (void *cls)
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return qs;
}
/* Credit purse value (if last op)! */
qs = TALER_ARL_edb->select_purse_merges_above_serial_id (
TALER_ARL_edb->cls,
ppr.last_purse_merges_serial_id,
&handle_purse_merged,
&rc);
if (qs < 0)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return qs;
}
qs = TALER_ARL_edb->select_purse_deposits_above_serial_id (
TALER_ARL_edb->cls,
ppr.last_purse_deposits_serial_id,
@ -1547,6 +1782,17 @@ analyze_reserves (void *cls)
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return qs;
}
/* Credit purse value (if last op)! */
qs = TALER_ARL_edb->select_purse_merges_above_serial_id (
TALER_ARL_edb->cls,
ppr.last_purse_merges_serial_id,
&handle_purse_merged,
&rc);
if (qs < 0)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return qs;
}
/* Charge history fee! */
qs = TALER_ARL_edb->select_history_requests_above_serial_id (
TALER_ARL_edb->cls,
@ -1721,6 +1967,9 @@ run (void *cls,
GNUNET_assert (NULL !=
(report_reserve_balance_insufficient_inconsistencies
= json_array ()));
GNUNET_assert (NULL !=
(report_purse_balance_insufficient_inconsistencies
= json_array ()));
GNUNET_assert (NULL !=
(report_reserve_not_closed_inconsistencies
= json_array ()));
@ -1741,6 +1990,9 @@ run (void *cls,
GNUNET_JSON_pack_array_steal (
"reserve_balance_insufficient_inconsistencies",
report_reserve_balance_insufficient_inconsistencies),
GNUNET_JSON_pack_array_steal (
"purse_balance_insufficient_inconsistencies",
report_purse_balance_insufficient_inconsistencies),
/* Tested in test-auditor.sh #3 */
TALER_JSON_pack_amount ("total_loss_balance_insufficient",
&total_balance_insufficient_loss),

View File

@ -220,8 +220,14 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: insert_reserve_info\n");
struct TALER_Amount reserve_balance, withdraw_fee_balance;
struct TALER_Amount reserve_balance2 = {}, withdraw_fee_balance2 = {};
struct TALER_Amount reserve_balance;
struct TALER_Amount withdraw_fee_balance;
struct TALER_Amount purse_fee_balance;
struct TALER_Amount history_fee_balance;
struct TALER_Amount reserve_balance2 = {};
struct TALER_Amount withdraw_fee_balance2 = {};
struct TALER_Amount purse_fee_balance2 = {};
struct TALER_Amount history_fee_balance2 = {};
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":12.345678",
@ -229,6 +235,12 @@ run (void *cls)
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":23.456789",
&withdraw_fee_balance));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":23.456789",
&purse_fee_balance));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":23.456789",
&history_fee_balance));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
@ -268,10 +280,12 @@ run (void *cls)
FAILIF (0 != strcmp (payto,
"payto://bla/blub"));
GNUNET_free (payto);
FAILIF (0 != GNUNET_memcmp (&date, &future)
|| 0 != GNUNET_memcmp (&reserve_balance2, &reserve_balance)
|| 0 != GNUNET_memcmp (&withdraw_fee_balance2,
&withdraw_fee_balance));
FAILIF (0 != GNUNET_memcmp (&date,
&future)
|| 0 != TALER_amount_cmp (&reserve_balance2,
&reserve_balance)
|| 0 != TALER_amount_cmp (&withdraw_fee_balance2,
&withdraw_fee_balance));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: insert_reserve_summary\n");
@ -279,8 +293,10 @@ run (void *cls)
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_reserve_summary (plugin->cls,
&master_pub,
&reserve_balance,
&withdraw_fee_balance,
&reserve_balance));
&purse_fee_balance,
&history_fee_balance));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: update_reserve_summary\n");
@ -289,25 +305,34 @@ run (void *cls)
plugin->update_reserve_summary (plugin->cls,
&master_pub,
&reserve_balance,
&withdraw_fee_balance));
&withdraw_fee_balance,
&purse_fee_balance,
&history_fee_balance));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: get_reserve_summary\n");
ZR_BLK (&reserve_balance2);
ZR_BLK (&withdraw_fee_balance2);
ZR_BLK (&purse_fee_balance2);
ZR_BLK (&history_fee_balance2);
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_reserve_summary (plugin->cls,
&master_pub,
&reserve_balance2,
&withdraw_fee_balance2));
FAILIF ( (0 != GNUNET_memcmp (&reserve_balance2,
&reserve_balance) ||
(0 != GNUNET_memcmp (&withdraw_fee_balance2,
&withdraw_fee_balance)) ) );
&withdraw_fee_balance2,
&purse_fee_balance2,
&history_fee_balance2));
FAILIF ( (0 != TALER_amount_cmp (&reserve_balance2,
&reserve_balance) ||
(0 != TALER_amount_cmp (&withdraw_fee_balance2,
&withdraw_fee_balance)) ||
(0 != TALER_amount_cmp (&purse_fee_balance2,
&purse_fee_balance)) ||
(0 != TALER_amount_cmp (&history_fee_balance2,
&history_fee_balance))));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: insert_denomination_balance\n");

View File

@ -283,7 +283,6 @@ struct SigningKey
};
struct TEH_KeyStateHandle
{
@ -404,7 +403,6 @@ struct SuspendedKeysRequests
struct GNUNET_TIME_Absolute timeout;
};
/**
* Stores the latest generation of our key state.
*/
@ -1353,7 +1351,7 @@ denomination_info_cb (
dk->meta = *meta;
dk->master_sig = *master_sig;
dk->recoup_possible = recoup_possible;
dk->denom_pub.age_mask = meta->age_mask;
dk->denom_pub.age_mask = meta->age_mask; /* FIXME-oec: age_mask -> reserved_field */
GNUNET_assert (
GNUNET_OK ==
@ -1361,6 +1359,7 @@ denomination_info_cb (
&dk->h_denom_pub.hash,
dk,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
@ -1727,12 +1726,12 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh,
* @a recoup and @a denoms.
*
* @param[in,out] ksh key state handle we build @a krd for
* @param[in] denom_keys_hash hash over all the denominatoin keys in @a denoms and age_restricted_denoms
* @param[in] denom_keys_hash hash over all the denominatoin keys in @a denoms
* @param last_cpd timestamp to use
* @param signkeys list of sign keys to return
* @param recoup list of revoked keys to return
* @param denoms list of denominations to return
* @param age_restricted_denoms list of age restricted denominations to return, can be NULL
* @param grouped_denominations list of grouped denominations to return
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
@ -1742,7 +1741,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
json_t *signkeys,
json_t *recoup,
json_t *denoms,
json_t *age_restricted_denoms)
json_t *grouped_denominations)
{
struct KeysResponseData krd;
struct TALER_ExchangePublicKeyP exchange_pub;
@ -1753,6 +1752,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
GNUNET_assert (NULL != signkeys);
GNUNET_assert (NULL != recoup);
GNUNET_assert (NULL != denoms);
GNUNET_assert (NULL != grouped_denominations);
GNUNET_assert (NULL != ksh->auditors);
GNUNET_assert (NULL != TEH_currency);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -1778,6 +1778,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
return GNUNET_SYSERR;
}
}
{
const struct SigningKey *sk;
@ -1803,6 +1804,8 @@ create_krd (struct TEH_KeyStateHandle *ksh,
recoup),
GNUNET_JSON_pack_array_incref ("denoms",
denoms),
GNUNET_JSON_pack_array_incref ("denominations",
grouped_denominations),
GNUNET_JSON_pack_array_incref ("auditors",
ksh->auditors),
GNUNET_JSON_pack_array_incref ("global_fees",
@ -1833,7 +1836,6 @@ create_krd (struct TEH_KeyStateHandle *ksh,
{
json_t *extensions = json_object ();
bool has_extensions = false;
bool age_restriction_enabled = false;
/* Fill in the configurations of the enabled extensions */
for (const struct TALER_Extension *extension = TALER_extensions_get_head ();
@ -1851,8 +1853,6 @@ create_krd (struct TEH_KeyStateHandle *ksh,
/* flag our findings so far */
has_extensions = true;
age_restriction_enabled = (extension->type ==
TALER_Extension_AgeRestriction);
GNUNET_assert (NULL != extension->config_json);
@ -1901,20 +1901,6 @@ create_krd (struct TEH_KeyStateHandle *ksh,
{
json_decref (extensions);
}
// Special case for age restrictions: if enabled, provide the list of
// age-restricted denominations.
if (age_restriction_enabled &&
NULL != age_restricted_denoms)
{
GNUNET_assert (
0 ==
json_object_set (
keys,
"age_restricted_denoms",
age_restricted_denoms));
}
}
@ -2010,12 +1996,10 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
json_t *recoup;
struct SignKeyCtx sctx;
json_t *denoms = NULL;
json_t *age_restricted_denoms = NULL;
json_t *grouped_denominations = NULL;
struct GNUNET_TIME_Timestamp last_cpd;
struct GNUNET_CONTAINER_Heap *heap;
struct GNUNET_HashContext *hash_context = NULL;
struct GNUNET_HashContext *hash_context_restricted = NULL;
bool have_age_restricted_denoms = false;
sctx.signkeys = json_array ();
GNUNET_assert (NULL != sctx.signkeys);
@ -2045,20 +2029,25 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
GNUNET_assert (NULL != denoms);
hash_context = GNUNET_CRYPTO_hash_context_start ();
/* If age restriction is enabled, initialize the array of age restricted
denoms and prepare a hash for them, separate from the others. We will join
those hashes afterwards.*/
if (0)
{
age_restricted_denoms = json_array ();
GNUNET_assert (NULL != age_restricted_denoms);
hash_context_restricted = GNUNET_CRYPTO_hash_context_start ();
}
grouped_denominations = json_array ();
GNUNET_assert (NULL != grouped_denominations);
last_cpd = GNUNET_TIME_UNIT_ZERO_TS;
{
struct TEH_DenominationKey *dk;
struct GNUNET_CONTAINER_MultiHashMap *denominations_by_group;
denominations_by_group =
GNUNET_CONTAINER_multihashmap_create (1024,
GNUNET_NO /* NO, because keys are only on the stack */);
/* groupData is the value we store for each group meta-data */
struct groupData
{
json_t *json; /* The json blob with the group meta-data and list of denominations */
struct GNUNET_HashContext *hash_context; /* hash over all denominations in that group */
};
/* heap = min heap, sorted by start time */
while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap)))
@ -2068,12 +2057,12 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
dk->meta.start) &&
(! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time)) )
{
struct GNUNET_HashCode hc;
/* FIXME-oec: Do we need to take hash_context_restricted into account
* in this if-branch!? Current tests suggests: no, (they don't fail).
* But something seems to be odd about only finishing hash_context.
/*
* This is not the first entry in the heap (because last_cpd !=
* GNUNET_TIME_UNIT_ZERO_TS) and the previous entry had a different
* start time. Therefore, we create an entry in the ksh.
*/
struct GNUNET_HashCode hc;
GNUNET_CRYPTO_hash_context_finish (
GNUNET_CRYPTO_hash_context_copy (hash_context),
@ -2085,7 +2074,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
sctx.signkeys,
recoup,
denoms,
age_restricted_denoms))
grouped_denominations))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to generate key response data for %s\n",
@ -2096,8 +2085,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
/* intentionally empty */;
GNUNET_CONTAINER_heap_destroy (heap);
json_decref (denoms);
if (NULL != age_restricted_denoms)
json_decref (age_restricted_denoms);
json_decref (grouped_denominations);
json_decref (sctx.signkeys);
json_decref (recoup);
return GNUNET_SYSERR;
@ -2108,9 +2096,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
{
json_t *denom;
json_t *array;
struct GNUNET_HashContext *hc;
denom =
GNUNET_JSON_PACK (
@ -2131,32 +2116,221 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
TALER_JSON_PACK_DENOM_FEES ("fee",
&dk->meta.fees));
/* Put the denom into the correct array depending on the settings and
* the properties of the denomination. Also, we build up the right
* hash for the corresponding array. */
if (0 &&
(0 != dk->denom_pub.age_mask.bits))
{
have_age_restricted_denoms = true;
array = age_restricted_denoms;
hc = hash_context_restricted;
}
else
{
array = denoms;
hc = hash_context;
}
GNUNET_CRYPTO_hash_context_read (hc,
GNUNET_CRYPTO_hash_context_read (hash_context,
&dk->h_denom_pub,
sizeof (struct GNUNET_HashCode));
GNUNET_assert (
0 ==
json_array_append_new (
array,
denoms,
denom));
}
/**
* Group the denominations by {cipher, value, fees, age_mask}.
*
* For each group we save the group meta-data and the list of
* denominations in this group as a json-blob in the multihashmap
* denominations_by_group.
**/
{
static const char *denoms_key = "denoms";
struct groupData *group;
json_t *list;
json_t *entry;
struct GNUNET_HashCode key;
/* Find the group/JSON-blob for the key */
struct
{
enum TALER_DenominationCipher cipher;
struct TALER_AgeMask age_mask;
struct TALER_Amount value;
struct TALER_DenomFeeSet fees;
} meta = {
.cipher = dk->denom_pub.cipher,
.value = dk->meta.value,
.fees = dk->meta.fees,
.age_mask = dk->meta.age_mask,
};
GNUNET_CRYPTO_hash (&meta, sizeof(meta), &key);
group = (struct groupData *) GNUNET_CONTAINER_multihashmap_get (
denominations_by_group,
&key);
if (NULL == group)
{
/*
* There is no group for this meta-data yet, so let's create a new
* group entry.
*/
bool age_restricted = meta.age_mask.bits != 0;
char *cipher;
group = GNUNET_new (struct groupData);
group->hash_context = GNUNET_CRYPTO_hash_context_start ();
switch (meta.cipher)
{
case TALER_DENOMINATION_RSA:
cipher = age_restricted ? "RSA+age_restriction": "RSA";
break;
case TALER_DENOMINATION_CS:
cipher = age_restricted ? "CS+age_restriction": "CS";
break;
default:
GNUNET_assert (false);
}
group->json = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("cipher", cipher),
TALER_JSON_PACK_DENOM_FEES ("fee", &meta.fees),
TALER_JSON_pack_amount ("value", &meta.value));
GNUNET_assert (NULL != group->json);
if (age_restricted)
{
GNUNET_assert (0 ==
json_object_set (group->json,
"age_mask",
json_integer (meta.age_mask.bits)));
}
/* Create a new array for the denominations in this group */
list = json_array ();
GNUNET_assert (NULL != list);
GNUNET_assert (0 ==
json_object_set (group->json, denoms_key, list));
GNUNET_assert (
GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (denominations_by_group,
&key,
group,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
/*
* Now that we have found/created the right group, add the denomination
* to the list
*/
{
struct GNUNET_JSON_PackSpec key_spec;
switch (meta.cipher)
{
case TALER_DENOMINATION_RSA:
key_spec =
GNUNET_JSON_pack_rsa_public_key ("rsa_pub",
dk->denom_pub.details.
rsa_public_key);
break;
case TALER_DENOMINATION_CS:
key_spec =
GNUNET_JSON_pack_data_varsize ("cs_pub",
&dk->denom_pub.details.
cs_public_key,
sizeof (dk->denom_pub.details.
cs_public_key));
break;
default:
GNUNET_assert (false);
}
entry = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("master_sig",
&dk->master_sig),
GNUNET_JSON_pack_timestamp ("stamp_start",
dk->meta.start),
GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw",
dk->meta.expire_withdraw),
GNUNET_JSON_pack_timestamp ("stamp_expire_deposit",
dk->meta.expire_deposit),
GNUNET_JSON_pack_timestamp ("stamp_expire_legal",
dk->meta.expire_legal),
key_spec
);
GNUNET_assert (NULL != entry);
}
/*
* Build up the running hash of all denominations in this group
* TODO: FIXME-oec: this is cipher and age_restriction dependend?!
*/
GNUNET_CRYPTO_hash_context_read (group->hash_context,
&dk->h_denom_pub,
sizeof (struct GNUNET_HashCode));
/* Finally, add the denomination to the list of denominations in this
* group */
list = json_object_get (group->json, denoms_key);
GNUNET_assert (NULL != list);
GNUNET_assert (true == json_is_array (list));
GNUNET_assert (0 ==
json_array_append_new (list, entry));
}
}
/* Create the JSON-array of grouped denominations */
if (0 <
GNUNET_CONTAINER_multihashmap_size (denominations_by_group))
{
struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
struct GNUNET_HashCode all_hashcode;
struct GNUNET_HashContext *all_hash_ctx;
struct groupData *group = NULL;
all_hash_ctx =
GNUNET_CRYPTO_hash_context_start ();
iter =
GNUNET_CONTAINER_multihashmap_iterator_create (denominations_by_group);
while (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_iterator_next (iter, NULL, (const
void **)
&group))
{
struct GNUNET_HashCode hc;
GNUNET_CRYPTO_hash_context_finish (
group->hash_context,
&hc);
GNUNET_CRYPTO_hash_context_read (all_hash_ctx,
&hc,
sizeof (struct GNUNET_HashCode));
GNUNET_assert (0 ==
json_object_set (
group->json,
"hash",
GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto (NULL, &hc))));
GNUNET_assert (0 ==
json_array_append_new (
grouped_denominations,
group->json));
GNUNET_free (group);
}
GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group);
GNUNET_CRYPTO_hash_context_finish (
all_hash_ctx,
&all_hashcode);
/* FIXME-oec: TODO:
* sign all_hashcode and add the signature to the /keys response */
}
}
@ -2165,18 +2339,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
{
struct GNUNET_HashCode hc;
/* If age restriction is active and we had at least one denomination of
* that sort, we simply add the hash of all age restricted denominations at
* the end of the others. */
if (0 && have_age_restricted_denoms)
{
struct GNUNET_HashCode hcr;
GNUNET_CRYPTO_hash_context_finish (hash_context_restricted, &hcr);
GNUNET_CRYPTO_hash_context_read (hash_context,
&hcr,
sizeof (struct GNUNET_HashCode));
}
GNUNET_CRYPTO_hash_context_finish (hash_context,
&hc);
@ -2187,14 +2349,12 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
sctx.signkeys,
recoup,
denoms,
age_restricted_denoms))
grouped_denominations))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to generate key response data for %s\n",
GNUNET_TIME_timestamp2s (last_cpd));
json_decref (denoms);
if (0 && NULL != age_restricted_denoms)
json_decref (age_restricted_denoms);
json_decref (sctx.signkeys);
json_decref (recoup);
return GNUNET_SYSERR;
@ -2210,8 +2370,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
json_decref (sctx.signkeys);
json_decref (recoup);
json_decref (denoms);
if (NULL != age_restricted_denoms)
json_decref (age_restricted_denoms);
return GNUNET_OK;
}
@ -2387,11 +2545,13 @@ build_key_state (struct HelperState *hs,
true);
return NULL;
}
if (management_only)
{
ksh->management_only = true;
return ksh;
}
if (GNUNET_OK !=
finish_keys_response (ksh))
{
@ -2401,6 +2561,7 @@ build_key_state (struct HelperState *hs,
true);
return NULL;
}
return ksh;
}

View File

@ -904,6 +904,14 @@ prepare_statements (struct PostgresClosure *pg)
" FROM exchange_do_purse_deposit"
" ($1,$2,$3,$4,$5,$6,$7,$8);",
8),
/* Used in #postgres_update_aggregation_transient() */
GNUNET_PQ_make_prepare (
"set_purse_balance",
"UPDATE purse_requests"
" SET balance_val=$2"
" ,balance_frac=$3"
" WHERE purse_pub=$1;",
3),
/* used in #postgres_expire_purse() */
GNUNET_PQ_make_prepare (
"call_expire_purse",
@ -1503,19 +1511,27 @@ prepare_statements (struct PostgresClosure *pg)
GNUNET_PQ_make_prepare (
"audit_get_purse_deposits_incr",
"SELECT"
" amount_with_fee_val"
",amount_with_fee_frac"
",purse_pub"
",coin_sig"
" pd.amount_with_fee_val"
",pd.amount_with_fee_frac"
",pr.amount_with_fee_val AS total_val"
",pr.amount_with_fee_frac AS total_frac"
",pr.balance_val"
",pr.balance_frac"
",pr.flags"
",pd.purse_pub"
",pd.coin_sig"
",partner_base_url"
",denom.denom_pub"
",pm.reserve_pub"
",kc.coin_pub"
",kc.age_commitment_hash"
",purse_deposit_serial_id"
" FROM purse_deposits"
",pd.purse_deposit_serial_id"
" FROM purse_deposits pd"
" LEFT JOIN partners USING (partner_serial_id)"
" JOIN known_coins kc USING (coin_pub)"
" JOIN denominations denom USING (denominations_serial)"
" LEFT JOIN purse_merges pm USING (purse_pub)"
" JOIN purse_requests pr USING (purse_pub)"
" JOIN known_coins kc USING (coin_pub)"
" JOIN denominations denom USING (denominations_serial)"
" WHERE ("
" (purse_deposit_serial_id>=$1)"
" )"
@ -1523,7 +1539,7 @@ prepare_statements (struct PostgresClosure *pg)
1),
GNUNET_PQ_make_prepare (
"audit_get_account_merges_incr",
"audit_get_account_merge_incr",
"SELECT"
" am.account_merge_request_serial_id"
",am.reserve_pub"
@ -1548,12 +1564,14 @@ prepare_statements (struct PostgresClosure *pg)
1),
GNUNET_PQ_make_prepare (
"audit_get_purse_merges_incr",
"audit_get_purse_merge_incr",
"SELECT"
" pm.purse_merge_request_serial_id"
",partner_base_url"
",pr.amount_with_fee_val"
",pr.amount_with_fee_frac"
",pr.balance_val"
",pr.balance_frac"
",pr.flags"
",pr.merge_pub"
",pm.reserve_pub"
@ -10532,21 +10550,36 @@ purse_deposit_serial_helper_cb (void *cls,
};
struct TALER_DenominationPublicKey denom_pub;
uint64_t rowid;
uint32_t flags32;
struct TALER_ReservePublicKeyP reserve_pub;
bool not_merged = false;
struct TALER_Amount purse_balance;
struct TALER_Amount purse_total;
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
&deposit.amount),
TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
&purse_balance),
TALER_PQ_RESULT_SPEC_AMOUNT ("total",
&purse_total),
TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
&deposit.deposit_fee),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("partner_base_url",
&deposit.exchange_base_url),
NULL),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
&reserve_pub),
&not_merged),
TALER_PQ_result_spec_denom_pub ("denom_pub",
&denom_pub),
GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
&deposit.purse_pub),
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
&deposit.coin_sig),
GNUNET_PQ_result_spec_uint32 ("flags",
&flags32),
GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
&deposit.coin_pub),
GNUNET_PQ_result_spec_allow_null (
@ -10574,6 +10607,10 @@ purse_deposit_serial_helper_cb (void *cls,
ret = dsc->cb (dsc->cb_cls,
rowid,
&deposit,
not_merged ? NULL : &reserve_pub,
(enum TALER_WalletAccountMergeFlags) flags32,
&purse_balance,
&purse_total,
&denom_pub);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
@ -10623,6 +10660,120 @@ postgres_select_purse_deposits_above_serial_id (
}
/**
* Closure for #account_merge_serial_helper_cb().
*/
struct AccountMergeSerialContext
{
/**
* Callback to call.
*/
TALER_EXCHANGEDB_AccountMergeCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
/**
* Plugin context.
*/
struct PostgresClosure *pg;
/**
* Status code, set to #GNUNET_SYSERR on hard errors.
*/
enum GNUNET_GenericReturnValue status;
};
/**
* Helper function to be called with the results of a SELECT statement
* that has returned @a num_results results.
*
* @param cls closure of type `struct AccountMergeSerialContext`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
account_merge_serial_helper_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct AccountMergeSerialContext *dsc = cls;
struct PostgresClosure *pg = dsc->pg;
for (unsigned int i = 0; i<num_results; i++)
{
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_PurseContractPublicKeyP purse_pub;
struct TALER_PrivateContractHashP h_contract_terms;
struct GNUNET_TIME_Timestamp purse_expiration;
struct TALER_Amount amount;
uint32_t min_age;
uint32_t flags32;
enum TALER_WalletAccountMergeFlags flags;
struct TALER_Amount purse_fee;
struct GNUNET_TIME_Timestamp merge_timestamp;
struct TALER_ReserveSignatureP reserve_sig;
uint64_t rowid;
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
&amount),
TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee",
&purse_fee),
GNUNET_PQ_result_spec_uint32 ("flags",
&flags32),
GNUNET_PQ_result_spec_uint32 ("age_limit",
&min_age),
GNUNET_PQ_result_spec_timestamp ("purse_expiration",
&purse_expiration),
GNUNET_PQ_result_spec_timestamp ("merge_timestamp",
&merge_timestamp),
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
&h_contract_terms),
GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
&purse_pub),
GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
&reserve_sig),
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
&reserve_pub),
GNUNET_PQ_result_spec_uint64 ("account_merge_request_serial_id",
&rowid),
GNUNET_PQ_result_spec_end
};
enum GNUNET_GenericReturnValue ret;
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
dsc->status = GNUNET_SYSERR;
return;
}
flags = (enum TALER_WalletAccountMergeFlags) flags32;
ret = dsc->cb (dsc->cb_cls,
rowid,
&reserve_pub,
&purse_pub,
&h_contract_terms,
purse_expiration,
&amount,
min_age,
flags,
&purse_fee,
merge_timestamp,
&reserve_sig);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
}
/**
* Select account merges above @a serial_id in monotonically increasing
* order.
@ -10640,8 +10791,139 @@ postgres_select_account_merges_above_serial_id (
TALER_EXCHANGEDB_AccountMergeCallback cb,
void *cb_cls)
{
GNUNET_break (0); // FIXME: not implemented
return GNUNET_DB_STATUS_HARD_ERROR;
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
struct AccountMergeSerialContext dsc = {
.cb = cb,
.cb_cls = cb_cls,
.pg = pg,
.status = GNUNET_OK
};
enum GNUNET_DB_QueryStatus qs;
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"audit_get_account_merge_incr",
params,
&account_merge_serial_helper_cb,
&dsc);
if (GNUNET_OK != dsc.status)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
/**
* Closure for #purse_deposit_serial_helper_cb().
*/
struct PurseMergeSerialContext
{
/**
* Callback to call.
*/
TALER_EXCHANGEDB_PurseMergeCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
/**
* Plugin context.
*/
struct PostgresClosure *pg;
/**
* Status code, set to #GNUNET_SYSERR on hard errors.
*/
enum GNUNET_GenericReturnValue status;
};
/**
* Helper function to be called with the results of a SELECT statement
* that has returned @a num_results results.
*
* @param cls closure of type `struct PurseMergeSerialContext`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
purse_merges_serial_helper_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct PurseMergeSerialContext *dsc = cls;
struct PostgresClosure *pg = dsc->pg;
for (unsigned int i = 0; i<num_results; i++)
{
uint64_t rowid;
char *partner_base_url = NULL;
struct TALER_Amount amount;
struct TALER_Amount balance;
uint32_t flags32;
enum TALER_WalletAccountMergeFlags flags;
struct TALER_PurseMergePublicKeyP merge_pub;
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_PurseMergeSignatureP merge_sig;
struct TALER_PurseContractPublicKeyP purse_pub;
struct GNUNET_TIME_Timestamp merge_timestamp;
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
&amount),
TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
&balance),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("partner_base_url",
&partner_base_url),
NULL),
GNUNET_PQ_result_spec_uint32 ("flags",
&flags32),
GNUNET_PQ_result_spec_timestamp ("merge_timestamp",
&merge_timestamp),
GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
&purse_pub),
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
&reserve_pub),
GNUNET_PQ_result_spec_auto_from_type ("merge_sig",
&merge_sig),
GNUNET_PQ_result_spec_auto_from_type ("merge_pub",
&merge_pub),
GNUNET_PQ_result_spec_uint64 ("purse_merge_request_serial_id",
&rowid),
GNUNET_PQ_result_spec_end
};
enum GNUNET_GenericReturnValue ret;
flags = (enum TALER_WalletAccountMergeFlags) flags;
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
dsc->status = GNUNET_SYSERR;
return;
}
ret = dsc->cb (dsc->cb_cls,
rowid,
partner_base_url,
&amount,
&balance,
flags,
&merge_pub,
&reserve_pub,
&merge_sig,
&purse_pub,
merge_timestamp);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
}
@ -10662,8 +10944,115 @@ postgres_select_purse_merges_above_serial_id (
TALER_EXCHANGEDB_PurseMergeCallback cb,
void *cb_cls)
{
GNUNET_break (0); // FIXME: not implemented
return GNUNET_DB_STATUS_HARD_ERROR;
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
struct PurseMergeSerialContext dsc = {
.cb = cb,
.cb_cls = cb_cls,
.pg = pg,
.status = GNUNET_OK
};
enum GNUNET_DB_QueryStatus qs;
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"audit_get_purse_merge_incr",
params,
&purse_merges_serial_helper_cb,
&dsc);
if (GNUNET_OK != dsc.status)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
/**
* Closure for #purse_deposit_serial_helper_cb().
*/
struct HistoryRequestSerialContext
{
/**
* Callback to call.
*/
TALER_EXCHANGEDB_HistoryRequestCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
/**
* Plugin context.
*/
struct PostgresClosure *pg;
/**
* Status code, set to #GNUNET_SYSERR on hard errors.
*/
enum GNUNET_GenericReturnValue status;
};
/**
* Helper function to be called with the results of a SELECT statement
* that has returned @a num_results results.
*
* @param cls closure of type `struct HistoryRequestSerialContext`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
history_request_serial_helper_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct HistoryRequestSerialContext *dsc = cls;
struct PostgresClosure *pg = dsc->pg;
for (unsigned int i = 0; i<num_results; i++)
{
uint64_t rowid;
struct TALER_Amount history_fee;
struct GNUNET_TIME_Timestamp ts;
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_ReserveSignatureP reserve_sig;
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
&history_fee),
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
&reserve_pub),
GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
&reserve_sig),
GNUNET_PQ_result_spec_uint64 ("history_request_serial_id",
&rowid),
GNUNET_PQ_result_spec_timestamp ("request_timestamp",
&ts),
GNUNET_PQ_result_spec_end
};
enum GNUNET_GenericReturnValue ret;
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
dsc->status = GNUNET_SYSERR;
return;
}
ret = dsc->cb (dsc->cb_cls,
rowid,
&history_fee,
ts,
&reserve_pub,
&reserve_sig);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
}
@ -10684,8 +11073,27 @@ postgres_select_history_requests_above_serial_id (
TALER_EXCHANGEDB_HistoryRequestCallback cb,
void *cb_cls)
{
GNUNET_break (0); // FIXME: not implemented
return GNUNET_DB_STATUS_HARD_ERROR;
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
struct HistoryRequestSerialContext dsc = {
.cb = cb,
.cb_cls = cb_cls,
.pg = pg,
.status = GNUNET_OK
};
enum GNUNET_DB_QueryStatus qs;
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"audit_get_history_requests_incr",
params,
&history_request_serial_helper_cb,
&dsc);
if (GNUNET_OK != dsc.status)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
@ -15173,6 +15581,35 @@ postgres_do_purse_deposit (
}
/**
* Set the current @a balance in the purse
* identified by @a purse_pub. Used by the auditor
* to update the balance as calculated by the auditor.
*
* @param cls closure
* @param purse_pub public key of a purse
* @param balance new balance to store under the purse
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_set_purse_balance (
void *cls,
const struct TALER_PurseContractPublicKeyP *purse_pub,
const struct TALER_Amount *balance)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (purse_pub),
TALER_PQ_query_param_amount (balance),
GNUNET_PQ_query_param_end
};
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"set_purse_balance",
params);
}
/**
* Function called to obtain a coin deposit data from
* depositing the coin into a purse.
@ -15804,6 +16241,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &postgres_select_purse_by_merge_pub;
plugin->do_purse_deposit
= &postgres_do_purse_deposit;
plugin->set_purse_balance
= &postgres_set_purse_balance;
plugin->get_purse_deposit
= &postgres_get_purse_deposit;
plugin->do_purse_merge

View File

@ -1990,6 +1990,11 @@ typedef enum GNUNET_GenericReturnValue
* @param cls closure
* @param rowid unique serial ID for the deposit in our DB
* @param deposit deposit details
* @param reserve_pub which reserve is the purse merged into, NULL if unknown
* @param flags purse flags
* @param auditor_balance purse balance (according to the
* auditor during auditing)
* @param purse_total target amount the purse should reach
* @param denom_pub denomination public key of @a coin_pub
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
@ -1998,6 +2003,10 @@ typedef enum GNUNET_GenericReturnValue
void *cls,
uint64_t rowid,
const struct TALER_EXCHANGEDB_PurseDeposit *deposit,
const struct TALER_ReservePublicKeyP *reserve_pub,
enum TALER_WalletAccountMergeFlags flags,
const struct TALER_Amount *auditor_balance,
const struct TALER_Amount *purse_total,
const struct TALER_DenominationPublicKey *denom_pub);
@ -2023,7 +2032,7 @@ typedef enum GNUNET_GenericReturnValue
enum TALER_WalletAccountMergeFlags flags,
const struct TALER_Amount *purse_fee,
struct GNUNET_TIME_Timestamp merge_timestamp,
struct TALER_ReserveSignatureP *reserve_sig);
const struct TALER_ReserveSignatureP *reserve_sig);
/**
@ -2033,6 +2042,15 @@ typedef enum GNUNET_GenericReturnValue
*
* @param cls closure
* @param rowid unique serial ID for the deposit in our DB
* @param partner_base_url where is the reserve, NULL for this exchange
* @param amount total amount expected in the purse
* @param balance current balance in the purse (according to the auditor)
* @param flags purse flags
* @param merge_pub merge capability key
* @param reserve_pub reserve the merge affects
* @param merge_sig signature affirming the merge
* @param purse_pub purse key
* @param merge_timestamp when did the merge happen
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
typedef enum GNUNET_GenericReturnValue
@ -2041,6 +2059,7 @@ typedef enum GNUNET_GenericReturnValue
uint64_t rowid,
const char *partner_base_url,
const struct TALER_Amount *amount,
const struct TALER_Amount *balance,
enum TALER_WalletAccountMergeFlags flags,
const struct TALER_PurseMergePublicKeyP *merge_pub,
const struct TALER_ReservePublicKeyP *reserve_pub,
@ -5290,6 +5309,23 @@ struct TALER_EXCHANGEDB_Plugin
bool *conflict);
/**
* Set the current @a balance in the purse
* identified by @a purse_pub. Used by the auditor
* to update the balance as calculated by the auditor.
*
* @param cls closure
* @param purse_pub public key of a purse
* @param balance new balance to store under the purse
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
(*set_purse_balance)(
void *cls,
const struct TALER_PurseContractPublicKeyP *purse_pub,
const struct TALER_Amount *balance);
/**
* Function called to obtain a coin deposit data from
* depositing the coin into a purse.

View File

@ -417,6 +417,19 @@ char *
TALER_payto_validate (const char *payto_uri);
/**
* Create payto://-URI for a given exchange base URL
* and a @a reserve_pub.
*
* @param exchange_url the base URL of the exchange
* @param reserve_pub the public key of the reserve
* @return payto://-URI for the reserve (without receiver-name!)
*/
char *
TALER_reserve_make_payto (const char *exchange_url,
const struct TALER_ReservePublicKeyP *reserve_pub);
/**
* Check that an IBAN number is well-formed.
*

View File

@ -119,50 +119,6 @@ struct TALER_EXCHANGE_AccountMergeHandle
};
static char *
make_payto (const char *exchange_url,
const struct TALER_ReservePublicKeyP *reserve_pub)
{
char pub_str[sizeof (*reserve_pub) * 2];
char *end;
bool is_http;
char *reserve_url;
end = GNUNET_STRINGS_data_to_string (
reserve_pub,
sizeof (*reserve_pub),
pub_str,
sizeof (pub_str));
*end = '\0';
if (0 == strncmp (exchange_url,
"http://",
strlen ("http://")))
{
is_http = true;
exchange_url = &exchange_url[strlen ("http://")];
}
else if (0 == strncmp (exchange_url,
"https://",
strlen ("https://")))
{
is_http = false;
exchange_url = &exchange_url[strlen ("https://")];
}
else
{
GNUNET_break (0);
return NULL;
}
/* exchange_url includes trailing '/' */
GNUNET_asprintf (&reserve_url,
"payto://%s/%s%s",
is_http ? "taler+http" : "taler",
exchange_url,
pub_str);
return reserve_url;
}
/**
* Function called when we're done processing the
* HTTP /purse/$PID/merge request.
@ -379,8 +335,8 @@ TALER_EXCHANGE_account_merge (
"/purses/%s/merge",
pub_str);
}
reserve_url = make_payto (pch->provider_url,
&pch->reserve_pub);
reserve_url = TALER_reserve_make_payto (pch->provider_url,
&pch->reserve_pub);
if (NULL == reserve_url)
{
GNUNET_break (0);

View File

@ -312,4 +312,48 @@ TALER_payto_from_reserve (const char *exchange_base_url,
}
char *
TALER_reserve_make_payto (const char *exchange_url,
const struct TALER_ReservePublicKeyP *reserve_pub)
{
char pub_str[sizeof (*reserve_pub) * 2];
char *end;
bool is_http;
char *reserve_url;
end = GNUNET_STRINGS_data_to_string (
reserve_pub,
sizeof (*reserve_pub),
pub_str,
sizeof (pub_str));
*end = '\0';
if (0 == strncmp (exchange_url,
"http://",
strlen ("http://")))
{
is_http = true;
exchange_url = &exchange_url[strlen ("http://")];
}
else if (0 == strncmp (exchange_url,
"https://",
strlen ("https://")))
{
is_http = false;
exchange_url = &exchange_url[strlen ("https://")];
}
else
{
GNUNET_break (0);
return NULL;
}
/* exchange_url includes trailing '/' */
GNUNET_asprintf (&reserve_url,
"payto://%s/%s%s",
is_http ? "taler+http" : "taler",
exchange_url,
pub_str);
return reserve_url;
}
/* end of payto.c */