2022-10-13 22:43:22 +02:00
|
|
|
/*
|
|
|
|
This file is part of TALER
|
|
|
|
Copyright (C) 2022 Taler Systems SA
|
|
|
|
|
|
|
|
TALER is free software; you can redistribute it and/or modify it under the
|
|
|
|
terms of the GNU General Public License as published by the Free Software
|
|
|
|
Foundation; either version 3, or (at your option) any later version.
|
|
|
|
|
|
|
|
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* @file pg_get_coin_transactions.c
|
|
|
|
* @brief Low-level (statement-level) Postgres database access for the exchange
|
|
|
|
* @author Christian Grothoff
|
|
|
|
*/
|
|
|
|
#include "platform.h"
|
|
|
|
#include "taler_error_codes.h"
|
|
|
|
#include "taler_dbevents.h"
|
|
|
|
#include "taler_pq_lib.h"
|
|
|
|
#include "pg_get_coin_transactions.h"
|
|
|
|
#include "pg_helper.h"
|
|
|
|
#include "plugin_exchangedb_common.h"
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Closure for callbacks called from #postgres_get_coin_transactions()
|
|
|
|
*/
|
|
|
|
struct CoinHistoryContext
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Head of the coin's history list.
|
|
|
|
*/
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList *head;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public key of the coin we are building the history for.
|
|
|
|
*/
|
|
|
|
const struct TALER_CoinSpendPublicKeyP *coin_pub;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Closure for all callbacks of this database plugin.
|
|
|
|
*/
|
|
|
|
void *db_cls;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Plugin context.
|
|
|
|
*/
|
|
|
|
struct PostgresClosure *pg;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set to 'true' if the transaction failed.
|
|
|
|
*/
|
|
|
|
bool failed;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set to 'true' if we found a deposit or melt (for invariant check).
|
|
|
|
*/
|
|
|
|
bool have_deposit_or_melt;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to be called with the results of a SELECT statement
|
|
|
|
* that has returned @a num_results results.
|
|
|
|
*
|
|
|
|
* @param cls closure of type `struct CoinHistoryContext`
|
|
|
|
* @param result the postgres result
|
|
|
|
* @param num_results the number of results in @a result
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_coin_deposit (void *cls,
|
|
|
|
PGresult *result,
|
|
|
|
unsigned int num_results)
|
|
|
|
{
|
|
|
|
struct CoinHistoryContext *chc = cls;
|
|
|
|
struct PostgresClosure *pg = chc->pg;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < num_results; i++)
|
|
|
|
{
|
|
|
|
struct TALER_EXCHANGEDB_DepositListEntry *deposit;
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList *tl;
|
|
|
|
uint64_t serial_id;
|
|
|
|
|
|
|
|
chc->have_deposit_or_melt = true;
|
|
|
|
deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
|
|
|
|
{
|
|
|
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
|
|
|
&deposit->amount_with_fee),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
|
|
|
|
&deposit->deposit_fee),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
|
|
|
|
&deposit->h_denom_pub),
|
|
|
|
GNUNET_PQ_result_spec_allow_null (
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
|
|
|
|
&deposit->h_age_commitment),
|
|
|
|
&deposit->no_age_commitment),
|
|
|
|
GNUNET_PQ_result_spec_timestamp ("wallet_timestamp",
|
|
|
|
&deposit->timestamp),
|
|
|
|
GNUNET_PQ_result_spec_timestamp ("refund_deadline",
|
|
|
|
&deposit->refund_deadline),
|
|
|
|
GNUNET_PQ_result_spec_timestamp ("wire_deadline",
|
|
|
|
&deposit->wire_deadline),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
|
|
|
|
&deposit->merchant_pub),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
|
|
|
|
&deposit->h_contract_terms),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
|
|
|
|
&deposit->wire_salt),
|
|
|
|
GNUNET_PQ_result_spec_string ("payto_uri",
|
|
|
|
&deposit->receiver_wire_account),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
|
|
|
|
&deposit->csig),
|
|
|
|
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
|
|
|
&serial_id),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("done",
|
|
|
|
&deposit->done),
|
|
|
|
GNUNET_PQ_result_spec_end
|
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_PQ_extract_result (result,
|
|
|
|
rs,
|
|
|
|
i))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (deposit);
|
|
|
|
chc->failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
|
|
|
tl->next = chc->head;
|
|
|
|
tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
|
|
|
|
tl->details.deposit = deposit;
|
|
|
|
tl->serial_id = serial_id;
|
|
|
|
chc->head = tl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to be called with the results of a SELECT statement
|
|
|
|
* that has returned @a num_results results.
|
|
|
|
*
|
|
|
|
* @param cls closure of type `struct CoinHistoryContext`
|
|
|
|
* @param result the postgres result
|
|
|
|
* @param num_results the number of results in @a result
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_coin_purse_deposit (void *cls,
|
|
|
|
PGresult *result,
|
|
|
|
unsigned int num_results)
|
|
|
|
{
|
|
|
|
struct CoinHistoryContext *chc = cls;
|
|
|
|
struct PostgresClosure *pg = chc->pg;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < num_results; i++)
|
|
|
|
{
|
|
|
|
struct TALER_EXCHANGEDB_PurseDepositListEntry *deposit;
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList *tl;
|
|
|
|
uint64_t serial_id;
|
|
|
|
|
|
|
|
chc->have_deposit_or_melt = true;
|
|
|
|
deposit = GNUNET_new (struct TALER_EXCHANGEDB_PurseDepositListEntry);
|
|
|
|
{
|
2022-10-30 17:36:57 +01:00
|
|
|
bool not_finished;
|
2022-10-13 22:43:22 +02:00
|
|
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
|
|
|
&deposit->amount),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
|
|
|
|
&deposit->deposit_fee),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
|
|
|
|
&deposit->purse_pub),
|
|
|
|
GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
|
|
|
|
&serial_id),
|
|
|
|
GNUNET_PQ_result_spec_allow_null (
|
|
|
|
GNUNET_PQ_result_spec_string ("partner_base_url",
|
|
|
|
&deposit->exchange_base_url),
|
|
|
|
NULL),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
|
|
|
|
&deposit->coin_sig),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
|
|
|
|
&deposit->h_age_commitment),
|
2022-10-30 17:36:57 +01:00
|
|
|
GNUNET_PQ_result_spec_allow_null (
|
|
|
|
GNUNET_PQ_result_spec_bool ("refunded",
|
|
|
|
&deposit->refunded),
|
|
|
|
¬_finished),
|
2022-10-13 22:43:22 +02:00
|
|
|
GNUNET_PQ_result_spec_end
|
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_PQ_extract_result (result,
|
|
|
|
rs,
|
|
|
|
i))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (deposit);
|
|
|
|
chc->failed = true;
|
|
|
|
return;
|
|
|
|
}
|
2022-10-30 17:36:57 +01:00
|
|
|
if (not_finished)
|
|
|
|
deposit->refunded = false;
|
2022-10-13 22:43:22 +02:00
|
|
|
deposit->no_age_commitment = GNUNET_is_zero (&deposit->h_age_commitment);
|
|
|
|
}
|
|
|
|
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
|
|
|
tl->next = chc->head;
|
|
|
|
tl->type = TALER_EXCHANGEDB_TT_PURSE_DEPOSIT;
|
|
|
|
tl->details.purse_deposit = deposit;
|
|
|
|
tl->serial_id = serial_id;
|
|
|
|
chc->head = tl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to be called with the results of a SELECT statement
|
|
|
|
* that has returned @a num_results results.
|
|
|
|
*
|
|
|
|
* @param cls closure of type `struct CoinHistoryContext`
|
|
|
|
* @param result the postgres result
|
|
|
|
* @param num_results the number of results in @a result
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_coin_melt (void *cls,
|
|
|
|
PGresult *result,
|
|
|
|
unsigned int num_results)
|
|
|
|
{
|
|
|
|
struct CoinHistoryContext *chc = cls;
|
|
|
|
struct PostgresClosure *pg = chc->pg;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i<num_results; i++)
|
|
|
|
{
|
|
|
|
struct TALER_EXCHANGEDB_MeltListEntry *melt;
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList *tl;
|
|
|
|
uint64_t serial_id;
|
|
|
|
|
|
|
|
chc->have_deposit_or_melt = true;
|
|
|
|
melt = GNUNET_new (struct TALER_EXCHANGEDB_MeltListEntry);
|
|
|
|
{
|
|
|
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("rc",
|
|
|
|
&melt->rc),
|
|
|
|
/* oldcoin_index not needed */
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
|
|
|
|
&melt->h_denom_pub),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
|
|
|
|
&melt->coin_sig),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
|
|
|
&melt->amount_with_fee),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
|
|
|
|
&melt->melt_fee),
|
|
|
|
GNUNET_PQ_result_spec_allow_null (
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
|
|
|
|
&melt->h_age_commitment),
|
|
|
|
&melt->no_age_commitment),
|
|
|
|
GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
|
|
|
|
&serial_id),
|
|
|
|
GNUNET_PQ_result_spec_end
|
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_PQ_extract_result (result,
|
|
|
|
rs,
|
|
|
|
i))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (melt);
|
|
|
|
chc->failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
|
|
|
tl->next = chc->head;
|
|
|
|
tl->type = TALER_EXCHANGEDB_TT_MELT;
|
|
|
|
tl->details.melt = melt;
|
|
|
|
tl->serial_id = serial_id;
|
|
|
|
chc->head = tl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to be called with the results of a SELECT statement
|
|
|
|
* that has returned @a num_results results.
|
|
|
|
*
|
|
|
|
* @param cls closure of type `struct CoinHistoryContext`
|
|
|
|
* @param result the postgres result
|
|
|
|
* @param num_results the number of results in @a result
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_coin_refund (void *cls,
|
|
|
|
PGresult *result,
|
|
|
|
unsigned int num_results)
|
|
|
|
{
|
|
|
|
struct CoinHistoryContext *chc = cls;
|
|
|
|
struct PostgresClosure *pg = chc->pg;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i<num_results; i++)
|
|
|
|
{
|
|
|
|
struct TALER_EXCHANGEDB_RefundListEntry *refund;
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList *tl;
|
|
|
|
uint64_t serial_id;
|
|
|
|
|
|
|
|
refund = GNUNET_new (struct TALER_EXCHANGEDB_RefundListEntry);
|
|
|
|
{
|
|
|
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
|
|
|
|
&refund->merchant_pub),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
|
|
|
|
&refund->merchant_sig),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
|
|
|
|
&refund->h_contract_terms),
|
|
|
|
GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
|
|
|
|
&refund->rtransaction_id),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
|
|
|
&refund->refund_amount),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
|
|
|
|
&refund->refund_fee),
|
|
|
|
GNUNET_PQ_result_spec_uint64 ("refund_serial_id",
|
|
|
|
&serial_id),
|
|
|
|
GNUNET_PQ_result_spec_end
|
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_PQ_extract_result (result,
|
|
|
|
rs,
|
|
|
|
i))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (refund);
|
|
|
|
chc->failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
|
|
|
tl->next = chc->head;
|
|
|
|
tl->type = TALER_EXCHANGEDB_TT_REFUND;
|
|
|
|
tl->details.refund = refund;
|
|
|
|
tl->serial_id = serial_id;
|
|
|
|
chc->head = tl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-10-15 10:43:26 +02:00
|
|
|
/**
|
|
|
|
* Function to be called with the results of a SELECT statement
|
|
|
|
* that has returned @a num_results results.
|
|
|
|
*
|
|
|
|
* @param cls closure of type `struct CoinHistoryContext`
|
|
|
|
* @param result the postgres result
|
|
|
|
* @param num_results the number of results in @a result
|
|
|
|
*/
|
|
|
|
static void
|
2022-10-30 17:36:57 +01:00
|
|
|
add_coin_purse_decision (void *cls,
|
|
|
|
PGresult *result,
|
|
|
|
unsigned int num_results)
|
2022-10-15 10:43:26 +02:00
|
|
|
{
|
|
|
|
struct CoinHistoryContext *chc = cls;
|
|
|
|
struct PostgresClosure *pg = chc->pg;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i<num_results; i++)
|
|
|
|
{
|
|
|
|
struct TALER_EXCHANGEDB_PurseRefundListEntry *prefund;
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList *tl;
|
|
|
|
uint64_t serial_id;
|
|
|
|
|
|
|
|
prefund = GNUNET_new (struct TALER_EXCHANGEDB_PurseRefundListEntry);
|
|
|
|
{
|
|
|
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
|
|
|
|
&prefund->purse_pub),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
|
|
|
&prefund->refund_amount),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
|
|
|
|
&prefund->refund_fee),
|
2022-10-30 17:36:57 +01:00
|
|
|
GNUNET_PQ_result_spec_uint64 ("purse_decision_serial_id",
|
2022-10-15 10:43:26 +02:00
|
|
|
&serial_id),
|
|
|
|
GNUNET_PQ_result_spec_end
|
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_PQ_extract_result (result,
|
|
|
|
rs,
|
|
|
|
i))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (prefund);
|
|
|
|
chc->failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
|
|
|
tl->next = chc->head;
|
|
|
|
tl->type = TALER_EXCHANGEDB_TT_PURSE_REFUND;
|
|
|
|
tl->details.purse_refund = prefund;
|
|
|
|
tl->serial_id = serial_id;
|
|
|
|
chc->head = tl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-10-13 22:43:22 +02:00
|
|
|
/**
|
|
|
|
* Function to be called with the results of a SELECT statement
|
|
|
|
* that has returned @a num_results results.
|
|
|
|
*
|
|
|
|
* @param cls closure of type `struct CoinHistoryContext`
|
|
|
|
* @param result the postgres result
|
|
|
|
* @param num_results the number of results in @a result
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_old_coin_recoup (void *cls,
|
|
|
|
PGresult *result,
|
|
|
|
unsigned int num_results)
|
|
|
|
{
|
|
|
|
struct CoinHistoryContext *chc = cls;
|
|
|
|
struct PostgresClosure *pg = chc->pg;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i<num_results; i++)
|
|
|
|
{
|
|
|
|
struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList *tl;
|
|
|
|
uint64_t serial_id;
|
|
|
|
|
|
|
|
recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
|
|
|
|
{
|
|
|
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
|
|
|
|
&recoup->coin.coin_pub),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
|
|
|
|
&recoup->coin_sig),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
|
|
|
|
&recoup->coin_blind),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
|
|
|
|
&recoup->value),
|
|
|
|
GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
|
|
|
|
&recoup->timestamp),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
|
|
|
|
&recoup->coin.denom_pub_hash),
|
|
|
|
TALER_PQ_result_spec_denom_sig ("denom_sig",
|
|
|
|
&recoup->coin.denom_sig),
|
|
|
|
GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
|
|
|
|
&serial_id),
|
|
|
|
GNUNET_PQ_result_spec_end
|
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_PQ_extract_result (result,
|
|
|
|
rs,
|
|
|
|
i))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (recoup);
|
|
|
|
chc->failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
recoup->old_coin_pub = *chc->coin_pub;
|
|
|
|
}
|
|
|
|
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
|
|
|
tl->next = chc->head;
|
|
|
|
tl->type = TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP;
|
|
|
|
tl->details.old_coin_recoup = recoup;
|
|
|
|
tl->serial_id = serial_id;
|
|
|
|
chc->head = tl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to be called with the results of a SELECT statement
|
|
|
|
* that has returned @a num_results results.
|
|
|
|
*
|
|
|
|
* @param cls closure of type `struct CoinHistoryContext`
|
|
|
|
* @param result the postgres result
|
|
|
|
* @param num_results the number of results in @a result
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_coin_recoup (void *cls,
|
|
|
|
PGresult *result,
|
|
|
|
unsigned int num_results)
|
|
|
|
{
|
|
|
|
struct CoinHistoryContext *chc = cls;
|
|
|
|
struct PostgresClosure *pg = chc->pg;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i<num_results; i++)
|
|
|
|
{
|
|
|
|
struct TALER_EXCHANGEDB_RecoupListEntry *recoup;
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList *tl;
|
|
|
|
uint64_t serial_id;
|
|
|
|
|
|
|
|
recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupListEntry);
|
|
|
|
{
|
|
|
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
|
|
|
|
&recoup->reserve_pub),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
|
|
|
|
&recoup->coin_sig),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
|
|
|
|
&recoup->h_denom_pub),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
|
|
|
|
&recoup->coin_blind),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
|
|
|
|
&recoup->value),
|
|
|
|
GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
|
|
|
|
&recoup->timestamp),
|
|
|
|
GNUNET_PQ_result_spec_uint64 ("recoup_uuid",
|
|
|
|
&serial_id),
|
|
|
|
GNUNET_PQ_result_spec_end
|
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_PQ_extract_result (result,
|
|
|
|
rs,
|
|
|
|
i))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (recoup);
|
|
|
|
chc->failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
|
|
|
tl->next = chc->head;
|
|
|
|
tl->type = TALER_EXCHANGEDB_TT_RECOUP;
|
|
|
|
tl->details.recoup = recoup;
|
|
|
|
tl->serial_id = serial_id;
|
|
|
|
chc->head = tl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to be called with the results of a SELECT statement
|
|
|
|
* that has returned @a num_results results.
|
|
|
|
*
|
|
|
|
* @param cls closure of type `struct CoinHistoryContext`
|
|
|
|
* @param result the postgres result
|
|
|
|
* @param num_results the number of results in @a result
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_coin_recoup_refresh (void *cls,
|
|
|
|
PGresult *result,
|
|
|
|
unsigned int num_results)
|
|
|
|
{
|
|
|
|
struct CoinHistoryContext *chc = cls;
|
|
|
|
struct PostgresClosure *pg = chc->pg;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i<num_results; i++)
|
|
|
|
{
|
|
|
|
struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList *tl;
|
|
|
|
uint64_t serial_id;
|
|
|
|
|
|
|
|
recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
|
|
|
|
{
|
|
|
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
|
|
|
|
&recoup->old_coin_pub),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
|
|
|
|
&recoup->coin_sig),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
|
|
|
|
&recoup->coin_blind),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
|
|
|
|
&recoup->value),
|
|
|
|
GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
|
|
|
|
&recoup->timestamp),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
|
|
|
|
&recoup->coin.denom_pub_hash),
|
|
|
|
TALER_PQ_result_spec_denom_sig ("denom_sig",
|
|
|
|
&recoup->coin.denom_sig),
|
|
|
|
GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
|
|
|
|
&serial_id),
|
|
|
|
GNUNET_PQ_result_spec_end
|
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_PQ_extract_result (result,
|
|
|
|
rs,
|
|
|
|
i))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (recoup);
|
|
|
|
chc->failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
recoup->coin.coin_pub = *chc->coin_pub;
|
|
|
|
}
|
|
|
|
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
|
|
|
tl->next = chc->head;
|
|
|
|
tl->type = TALER_EXCHANGEDB_TT_RECOUP_REFRESH;
|
|
|
|
tl->details.recoup_refresh = recoup;
|
|
|
|
tl->serial_id = serial_id;
|
|
|
|
chc->head = tl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to be called with the results of a SELECT statement
|
|
|
|
* that has returned @a num_results results.
|
|
|
|
*
|
|
|
|
* @param cls closure of type `struct CoinHistoryContext`
|
|
|
|
* @param result the postgres result
|
|
|
|
* @param num_results the number of results in @a result
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_coin_reserve_open (void *cls,
|
|
|
|
PGresult *result,
|
|
|
|
unsigned int num_results)
|
|
|
|
{
|
|
|
|
struct CoinHistoryContext *chc = cls;
|
|
|
|
struct PostgresClosure *pg = chc->pg;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i<num_results; i++)
|
|
|
|
{
|
|
|
|
struct TALER_EXCHANGEDB_ReserveOpenListEntry *role;
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList *tl;
|
|
|
|
uint64_t serial_id;
|
|
|
|
|
|
|
|
role = GNUNET_new (struct TALER_EXCHANGEDB_ReserveOpenListEntry);
|
|
|
|
{
|
|
|
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
|
|
|
|
&role->reserve_sig),
|
|
|
|
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
|
|
|
|
&role->coin_sig),
|
|
|
|
TALER_PQ_RESULT_SPEC_AMOUNT ("contribution",
|
|
|
|
&role->coin_contribution),
|
|
|
|
GNUNET_PQ_result_spec_uint64 ("reserve_open_deposit_uuid",
|
|
|
|
&serial_id),
|
|
|
|
GNUNET_PQ_result_spec_end
|
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_PQ_extract_result (result,
|
|
|
|
rs,
|
|
|
|
i))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (role);
|
|
|
|
chc->failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
|
|
|
tl->next = chc->head;
|
|
|
|
tl->type = TALER_EXCHANGEDB_TT_RESERVE_OPEN;
|
|
|
|
tl->details.reserve_open = role;
|
|
|
|
tl->serial_id = serial_id;
|
|
|
|
chc->head = tl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Work we need to do.
|
|
|
|
*/
|
|
|
|
struct Work
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* SQL prepared statement name.
|
|
|
|
*/
|
|
|
|
const char *statement;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to call to handle the result(s).
|
|
|
|
*/
|
|
|
|
GNUNET_PQ_PostgresResultHandler cb;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
enum GNUNET_DB_QueryStatus
|
|
|
|
TEH_PG_get_coin_transactions (
|
|
|
|
void *cls,
|
|
|
|
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList **tlp)
|
|
|
|
{
|
|
|
|
struct PostgresClosure *pg = cls;
|
|
|
|
static const struct Work work[] = {
|
|
|
|
/** #TALER_EXCHANGEDB_TT_DEPOSIT */
|
|
|
|
{ "get_deposit_with_coin_pub",
|
|
|
|
&add_coin_deposit },
|
|
|
|
/** #TALER_EXCHANGEDB_TT_MELT */
|
|
|
|
{ "get_refresh_session_by_coin",
|
|
|
|
&add_coin_melt },
|
|
|
|
/** #TALER_EXCHANGEDB_TT_PURSE_DEPOSIT */
|
|
|
|
{ "get_purse_deposit_by_coin_pub",
|
|
|
|
&add_coin_purse_deposit },
|
2022-10-15 10:43:26 +02:00
|
|
|
/** #TALER_EXCHANGEDB_TT_PURSE_REFUND */
|
2022-10-30 17:36:57 +01:00
|
|
|
{ "get_purse_decision_by_coin_pub",
|
|
|
|
&add_coin_purse_decision },
|
2022-10-13 22:43:22 +02:00
|
|
|
/** #TALER_EXCHANGEDB_TT_REFUND */
|
|
|
|
{ "get_refunds_by_coin",
|
|
|
|
&add_coin_refund },
|
|
|
|
/** #TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP */
|
|
|
|
{ "recoup_by_old_coin",
|
|
|
|
&add_old_coin_recoup },
|
|
|
|
/** #TALER_EXCHANGEDB_TT_RECOUP */
|
|
|
|
{ "recoup_by_coin",
|
|
|
|
&add_coin_recoup },
|
|
|
|
/** #TALER_EXCHANGEDB_TT_RECOUP_REFRESH */
|
|
|
|
{ "recoup_by_refreshed_coin",
|
|
|
|
&add_coin_recoup_refresh },
|
|
|
|
/** #TALER_EXCHANGEDB_TT_RESERVE_OPEN */
|
|
|
|
{ "reserve_open_by_coin",
|
|
|
|
&add_coin_reserve_open },
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
struct GNUNET_PQ_QueryParam params[] = {
|
|
|
|
GNUNET_PQ_query_param_auto_from_type (coin_pub),
|
|
|
|
GNUNET_PQ_query_param_end
|
|
|
|
};
|
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
|
|
|
struct CoinHistoryContext chc = {
|
|
|
|
.head = NULL,
|
|
|
|
.coin_pub = coin_pub,
|
|
|
|
.pg = pg,
|
|
|
|
.db_cls = cls
|
|
|
|
};
|
|
|
|
|
|
|
|
PREPARE (pg,
|
|
|
|
"get_deposit_with_coin_pub",
|
|
|
|
"SELECT"
|
|
|
|
" dep.amount_with_fee_val"
|
|
|
|
",dep.amount_with_fee_frac"
|
|
|
|
",denoms.fee_deposit_val"
|
|
|
|
",denoms.fee_deposit_frac"
|
|
|
|
",denoms.denom_pub_hash"
|
|
|
|
",kc.age_commitment_hash"
|
|
|
|
",dep.wallet_timestamp"
|
|
|
|
",dep.refund_deadline"
|
|
|
|
",dep.wire_deadline"
|
|
|
|
",dep.merchant_pub"
|
|
|
|
",dep.h_contract_terms"
|
|
|
|
",dep.wire_salt"
|
|
|
|
",wt.payto_uri"
|
|
|
|
",dep.coin_sig"
|
|
|
|
",dep.deposit_serial_id"
|
|
|
|
",dep.done"
|
|
|
|
" FROM deposits dep"
|
|
|
|
" JOIN wire_targets wt"
|
|
|
|
" USING (wire_target_h_payto)"
|
|
|
|
" JOIN known_coins kc"
|
|
|
|
" ON (kc.coin_pub = dep.coin_pub)"
|
|
|
|
" JOIN denominations denoms"
|
|
|
|
" USING (denominations_serial)"
|
|
|
|
" WHERE dep.coin_pub=$1;");
|
|
|
|
PREPARE (pg,
|
|
|
|
"get_refresh_session_by_coin",
|
|
|
|
"SELECT"
|
|
|
|
" rc"
|
|
|
|
",old_coin_sig"
|
|
|
|
",amount_with_fee_val"
|
|
|
|
",amount_with_fee_frac"
|
|
|
|
",denoms.denom_pub_hash"
|
|
|
|
",denoms.fee_refresh_val"
|
|
|
|
",denoms.fee_refresh_frac"
|
|
|
|
",kc.age_commitment_hash"
|
|
|
|
",melt_serial_id"
|
|
|
|
" FROM refresh_commitments"
|
|
|
|
" JOIN known_coins kc"
|
|
|
|
" ON (refresh_commitments.old_coin_pub = kc.coin_pub)"
|
|
|
|
" JOIN denominations denoms"
|
|
|
|
" USING (denominations_serial)"
|
|
|
|
" WHERE old_coin_pub=$1;");
|
|
|
|
PREPARE (pg,
|
|
|
|
"get_purse_deposit_by_coin_pub",
|
|
|
|
"SELECT"
|
|
|
|
" partner_base_url"
|
|
|
|
",pd.amount_with_fee_val"
|
|
|
|
",pd.amount_with_fee_frac"
|
|
|
|
",denoms.fee_deposit_val"
|
|
|
|
",denoms.fee_deposit_frac"
|
|
|
|
",pd.purse_pub"
|
|
|
|
",kc.age_commitment_hash"
|
|
|
|
",pd.coin_sig"
|
|
|
|
",pd.purse_deposit_serial_id"
|
2022-10-30 17:36:57 +01:00
|
|
|
",pdes.refunded"
|
2022-10-13 22:43:22 +02:00
|
|
|
" FROM purse_deposits pd"
|
|
|
|
" LEFT JOIN partners"
|
|
|
|
" USING (partner_serial_id)"
|
|
|
|
" JOIN purse_requests pr"
|
|
|
|
" USING (purse_pub)"
|
2022-10-30 17:36:57 +01:00
|
|
|
" LEFT JOIN purse_decision pdes"
|
|
|
|
" USING (purse_pub)"
|
2022-10-13 22:43:22 +02:00
|
|
|
" JOIN known_coins kc"
|
|
|
|
" ON (pd.coin_pub = kc.coin_pub)"
|
|
|
|
" JOIN denominations denoms"
|
|
|
|
" USING (denominations_serial)"
|
|
|
|
// FIXME: use to-be-created materialized index
|
|
|
|
// on coin_pub (query crosses partitions!)
|
|
|
|
" WHERE pd.coin_pub=$1;");
|
|
|
|
PREPARE (pg,
|
|
|
|
"get_refunds_by_coin",
|
|
|
|
"SELECT"
|
|
|
|
" dep.merchant_pub"
|
|
|
|
",ref.merchant_sig"
|
|
|
|
",dep.h_contract_terms"
|
|
|
|
",ref.rtransaction_id"
|
|
|
|
",ref.amount_with_fee_val"
|
|
|
|
",ref.amount_with_fee_frac"
|
|
|
|
",denom.fee_refund_val "
|
|
|
|
",denom.fee_refund_frac "
|
|
|
|
",ref.refund_serial_id"
|
|
|
|
" FROM refunds ref"
|
|
|
|
" JOIN deposits dep"
|
|
|
|
" ON (ref.coin_pub = dep.coin_pub AND ref.deposit_serial_id = dep.deposit_serial_id)"
|
|
|
|
" JOIN known_coins kc"
|
|
|
|
" ON (ref.coin_pub = kc.coin_pub)"
|
|
|
|
" JOIN denominations denom"
|
|
|
|
" USING (denominations_serial)"
|
|
|
|
" WHERE ref.coin_pub=$1;");
|
2022-10-15 10:43:26 +02:00
|
|
|
PREPARE (pg,
|
2022-10-30 17:36:57 +01:00
|
|
|
"get_purse_decision_by_coin_pub",
|
2022-10-15 10:43:26 +02:00
|
|
|
"SELECT"
|
2022-10-30 17:36:57 +01:00
|
|
|
" pdes.purse_pub"
|
2022-10-15 10:43:26 +02:00
|
|
|
",pd.amount_with_fee_val"
|
|
|
|
",pd.amount_with_fee_frac"
|
|
|
|
",denom.fee_refund_val "
|
|
|
|
",denom.fee_refund_frac "
|
2022-10-30 17:36:57 +01:00
|
|
|
",pdes.purse_decision_serial_id"
|
2022-10-15 10:43:26 +02:00
|
|
|
" FROM purse_deposits pd"
|
2022-10-30 17:36:57 +01:00
|
|
|
" JOIN purse_decision pdes"
|
2022-10-15 10:43:26 +02:00
|
|
|
" USING (purse_pub)"
|
|
|
|
" JOIN known_coins kc"
|
|
|
|
" ON (pd.coin_pub = kc.coin_pub)"
|
|
|
|
" JOIN denominations denom"
|
|
|
|
" USING (denominations_serial)"
|
2022-10-30 17:36:57 +01:00
|
|
|
" WHERE pd.coin_pub=$1"
|
|
|
|
" AND pdes.refunded;");
|
2022-10-13 22:43:22 +02:00
|
|
|
PREPARE (pg,
|
|
|
|
"recoup_by_old_coin",
|
|
|
|
"SELECT"
|
|
|
|
" coins.coin_pub"
|
|
|
|
",coin_sig"
|
|
|
|
",coin_blind"
|
|
|
|
",amount_val"
|
|
|
|
",amount_frac"
|
|
|
|
",recoup_timestamp"
|
|
|
|
",denoms.denom_pub_hash"
|
|
|
|
",coins.denom_sig"
|
|
|
|
",recoup_refresh_uuid"
|
|
|
|
" FROM recoup_refresh"
|
|
|
|
" JOIN known_coins coins"
|
|
|
|
" USING (coin_pub)"
|
|
|
|
" JOIN denominations denoms"
|
|
|
|
" USING (denominations_serial)"
|
|
|
|
" WHERE rrc_serial IN"
|
|
|
|
" (SELECT rrc.rrc_serial"
|
|
|
|
" FROM refresh_commitments"
|
|
|
|
" JOIN refresh_revealed_coins rrc"
|
|
|
|
" USING (melt_serial_id)"
|
|
|
|
" WHERE old_coin_pub=$1);");
|
|
|
|
PREPARE (pg,
|
|
|
|
"recoup_by_coin",
|
|
|
|
"SELECT"
|
|
|
|
" reserves.reserve_pub"
|
|
|
|
",denoms.denom_pub_hash"
|
|
|
|
",coin_sig"
|
|
|
|
",coin_blind"
|
|
|
|
",amount_val"
|
|
|
|
",amount_frac"
|
|
|
|
",recoup_timestamp"
|
|
|
|
",recoup_uuid"
|
|
|
|
" FROM recoup rcp"
|
|
|
|
/* NOTE: suboptimal JOIN follows: crosses shards!
|
|
|
|
Could theoretically be improved via a materialized
|
|
|
|
index. But likely not worth it (query is rare and
|
|
|
|
number of reserve shards might be limited) */
|
|
|
|
" JOIN reserves_out ro"
|
|
|
|
" USING (reserve_out_serial_id)"
|
|
|
|
" JOIN reserves"
|
|
|
|
" USING (reserve_uuid)"
|
|
|
|
" JOIN known_coins coins"
|
|
|
|
" USING (coin_pub)"
|
|
|
|
" JOIN denominations denoms"
|
|
|
|
" ON (denoms.denominations_serial = coins.denominations_serial)"
|
|
|
|
" WHERE coins.coin_pub=$1;");
|
|
|
|
/* Used in #postgres_get_coin_transactions() to obtain recoup transactions
|
|
|
|
for a refreshed coin */
|
|
|
|
PREPARE (pg,
|
|
|
|
"recoup_by_refreshed_coin",
|
|
|
|
"SELECT"
|
|
|
|
" old_coins.coin_pub AS old_coin_pub"
|
|
|
|
",coin_sig"
|
|
|
|
",coin_blind"
|
|
|
|
",amount_val"
|
|
|
|
",amount_frac"
|
|
|
|
",recoup_timestamp"
|
|
|
|
",denoms.denom_pub_hash"
|
|
|
|
",coins.denom_sig"
|
|
|
|
",recoup_refresh_uuid"
|
|
|
|
" FROM recoup_refresh"
|
|
|
|
" JOIN refresh_revealed_coins rrc"
|
|
|
|
" USING (rrc_serial)"
|
|
|
|
" JOIN refresh_commitments rfc"
|
|
|
|
" ON (rrc.melt_serial_id = rfc.melt_serial_id)"
|
|
|
|
" JOIN known_coins old_coins"
|
|
|
|
" ON (rfc.old_coin_pub = old_coins.coin_pub)"
|
|
|
|
" JOIN known_coins coins"
|
|
|
|
" ON (recoup_refresh.coin_pub = coins.coin_pub)"
|
|
|
|
" JOIN denominations denoms"
|
|
|
|
" ON (denoms.denominations_serial = coins.denominations_serial)"
|
|
|
|
" WHERE coins.coin_pub=$1;");
|
|
|
|
PREPARE (pg,
|
|
|
|
"reserve_open_by_coin",
|
|
|
|
"SELECT"
|
|
|
|
" reserve_open_deposit_uuid"
|
|
|
|
",coin_sig"
|
|
|
|
",reserve_sig"
|
|
|
|
",contribution_val"
|
|
|
|
",contribution_frac"
|
|
|
|
" FROM reserves_open_deposits"
|
|
|
|
" WHERE coin_pub=$1;");
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Getting transactions for coin %s\n",
|
|
|
|
TALER_B2S (coin_pub));
|
|
|
|
for (unsigned int i = 0; NULL != work[i].statement; i++)
|
|
|
|
{
|
|
|
|
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
|
|
|
|
work[i].statement,
|
|
|
|
params,
|
|
|
|
work[i].cb,
|
|
|
|
&chc);
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Coin %s yielded %d transactions of type %s\n",
|
|
|
|
TALER_B2S (coin_pub),
|
|
|
|
qs,
|
|
|
|
work[i].statement);
|
|
|
|
if ( (0 > qs) ||
|
|
|
|
(chc.failed) )
|
|
|
|
{
|
|
|
|
if (NULL != chc.head)
|
|
|
|
TEH_COMMON_free_coin_transaction_list (cls,
|
|
|
|
chc.head);
|
|
|
|
*tlp = NULL;
|
|
|
|
if (chc.failed)
|
|
|
|
qs = GNUNET_DB_STATUS_HARD_ERROR;
|
|
|
|
return qs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*tlp = chc.head;
|
|
|
|
if (NULL == chc.head)
|
|
|
|
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
|
|
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
|
|
|
}
|