towards supporting #3887 in the auditor
This commit is contained in:
parent
4b82a591c5
commit
4b5efa4e81
@ -808,9 +808,8 @@ handle_reserve_out (void *cls,
|
||||
* @param timestamp when did we receive the payback request
|
||||
* @param amount how much should be added back to the reserve
|
||||
* @param reserve_pub public key of the reserve
|
||||
* @param coin_pub public key of the coin
|
||||
* @param coin public information about the coin
|
||||
* @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_PAYBACK
|
||||
* @param h_denom_pub hash of the denomination key of the coin
|
||||
* @param coin_blind blinding factor used to blind the coin
|
||||
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
|
||||
*/
|
||||
@ -820,9 +819,8 @@ handle_payback_by_reserve (void *cls,
|
||||
struct GNUNET_TIME_Absolute timestamp,
|
||||
const struct TALER_Amount *amount,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||
const struct TALER_CoinPublicInfo *coin,
|
||||
const struct TALER_CoinSpendSignatureP *coin_sig,
|
||||
const struct GNUNET_HashCode *h_denom_pub,
|
||||
const struct TALER_DenominationBlindingKeyP *coin_blind)
|
||||
{
|
||||
struct ReserveContext *rc = cls;
|
||||
@ -834,6 +832,9 @@ handle_payback_by_reserve (void *cls,
|
||||
GNUNET_assert (rowid >= pp.last_reserve_payback_serial_id);
|
||||
pp.last_reserve_payback_serial_id = rowid + 1;
|
||||
|
||||
/* TODO: check that coin signature on payback request is valid
|
||||
and/or that the coin was eligible for payback! #3887!*/
|
||||
|
||||
GNUNET_CRYPTO_hash (reserve_pub,
|
||||
sizeof (*reserve_pub),
|
||||
&key);
|
||||
@ -1529,6 +1530,17 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TALER_EXCHANGEDB_TT_PAYBACK:
|
||||
amount_with_fee = &tl->details.payback->value;
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_add (&expenditures,
|
||||
&expenditures,
|
||||
amount_with_fee))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check that the fees given in the transaction list and in dki match */
|
||||
@ -1656,6 +1668,9 @@ wire_transfer_information_cb (void *cls,
|
||||
case TALER_EXCHANGEDB_TT_REFUND:
|
||||
coin = &tl->details.refund->coin;
|
||||
break;
|
||||
case TALER_EXCHANGEDB_TT_PAYBACK:
|
||||
coin = &tl->details.payback->coin;
|
||||
break;
|
||||
}
|
||||
GNUNET_assert (NULL != coin); /* hard check that switch worked */
|
||||
if (GNUNET_OK !=
|
||||
|
@ -1424,7 +1424,7 @@ postgres_get_auditor_progress (void *cls,
|
||||
GNUNET_PQ_result_spec_uint64 ("last_reserve_payback_serial_id",
|
||||
&pp->last_reserve_payback_serial_id),
|
||||
GNUNET_PQ_result_spec_uint64 ("last_reserve_close_serial_id",
|
||||
&pp->last_reserve_out_serial_id),
|
||||
&pp->last_reserve_close_serial_id),
|
||||
GNUNET_PQ_result_spec_uint64 ("last_withdraw_serial_id",
|
||||
&pp->last_withdraw_serial_id),
|
||||
GNUNET_PQ_result_spec_uint64 ("last_deposit_serial_id",
|
||||
|
@ -2413,7 +2413,7 @@ TEH_DB_execute_payback (struct MHD_Connection *connection,
|
||||
ret = TEH_plugin->insert_payback_request (TEH_plugin->cls,
|
||||
session,
|
||||
&reserve_pub,
|
||||
&coin->coin_pub,
|
||||
coin,
|
||||
coin_sig,
|
||||
coin_blind,
|
||||
&amount,
|
||||
|
@ -565,7 +565,7 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
|
||||
pc.timestamp = GNUNET_TIME_absolute_hton (payback->timestamp);
|
||||
TALER_amount_hton (&pc.payback_amount,
|
||||
&payback->value);
|
||||
pc.coin_pub = payback->coin_pub;
|
||||
pc.coin_pub = payback->coin.coin_pub;
|
||||
pc.reserve_pub = payback->reserve_pub;
|
||||
TEH_KS_sign (&pc.purpose,
|
||||
&epub,
|
||||
@ -737,7 +737,7 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh,
|
||||
pc.timestamp = GNUNET_TIME_absolute_hton (payback->timestamp);
|
||||
TALER_amount_hton (&pc.payback_amount,
|
||||
&payback->value);
|
||||
pc.coin_pub = payback->coin_pub;
|
||||
pc.coin_pub = payback->coin.coin_pub;
|
||||
pc.reserve_pub = payback->reserve_pub;
|
||||
TEH_KS_sign (&pc.purpose,
|
||||
&pub,
|
||||
|
@ -56,7 +56,8 @@ common_free_reserve_history (void *cls,
|
||||
break;
|
||||
case TALER_EXCHANGEDB_RO_PAYBACK_COIN:
|
||||
payback = rh->details.payback;
|
||||
GNUNET_CRYPTO_rsa_public_key_free (payback->denom_pub.rsa_public_key);
|
||||
GNUNET_CRYPTO_rsa_signature_free (payback->coin.denom_sig.rsa_signature);
|
||||
GNUNET_CRYPTO_rsa_public_key_free (payback->coin.denom_pub.rsa_public_key);
|
||||
GNUNET_free (payback);
|
||||
break;
|
||||
}
|
||||
@ -133,6 +134,10 @@ common_free_coin_transaction_list (void *cls,
|
||||
GNUNET_free (list->details.refund);
|
||||
break;
|
||||
case TALER_EXCHANGEDB_TT_PAYBACK:
|
||||
if (NULL != list->details.payback->coin.denom_pub.rsa_public_key)
|
||||
GNUNET_CRYPTO_rsa_public_key_free (list->details.payback->coin.denom_pub.rsa_public_key);
|
||||
if (NULL != list->details.payback->coin.denom_sig.rsa_signature)
|
||||
GNUNET_CRYPTO_rsa_signature_free (list->details.payback->coin.denom_sig.rsa_signature);
|
||||
GNUNET_free (list->details.payback);
|
||||
break;
|
||||
}
|
||||
|
@ -517,7 +517,7 @@ postgres_create_tables (void *cls)
|
||||
SQLEXEC("CREATE TABLE IF NOT EXISTS payback "
|
||||
"(payback_uuid BIGSERIAL"
|
||||
",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
|
||||
",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)"
|
||||
",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE"
|
||||
",coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64)"
|
||||
",coin_blind BYTEA NOT NULL CHECK(LENGTH(coin_blind)=32)"
|
||||
",amount_val INT8 NOT NULL"
|
||||
@ -1429,12 +1429,13 @@ postgres_prepare (PGconn *db_conn)
|
||||
",coin_sig"
|
||||
",coin_blind"
|
||||
",h_blind_ev"
|
||||
",denom.denom_pub"
|
||||
",coins.denom_pub"
|
||||
",coins.denom_sig"
|
||||
",amount_val"
|
||||
",amount_frac"
|
||||
",amount_curr"
|
||||
" FROM payback"
|
||||
" JOIN reserves_out denom USING (reserve_pub,h_blind_ev)"
|
||||
" JOIN known_coins coins USING (coin_pub)"
|
||||
" WHERE payback_uuid>=$1"
|
||||
" ORDER BY payback_uuid ASC",
|
||||
1, NULL);
|
||||
@ -1450,10 +1451,10 @@ postgres_prepare (PGconn *db_conn)
|
||||
",amount_frac"
|
||||
",amount_curr"
|
||||
",timestamp"
|
||||
",denom.denom_pub"
|
||||
",denom.denom_sig"
|
||||
",coins.denom_pub"
|
||||
",coins.denom_sig"
|
||||
" FROM payback"
|
||||
" JOIN reserves_out denom USING (reserve_pub,h_blind_ev)"
|
||||
" JOIN known_coins coins USING (coin_pub)"
|
||||
" WHERE payback.reserve_pub=$1",
|
||||
1, NULL);
|
||||
|
||||
@ -1468,10 +1469,10 @@ postgres_prepare (PGconn *db_conn)
|
||||
",amount_frac"
|
||||
",amount_curr"
|
||||
",timestamp"
|
||||
",denom.denom_pub"
|
||||
",denom.denom_sig"
|
||||
",coins.denom_pub"
|
||||
",coins.denom_sig"
|
||||
" FROM payback"
|
||||
" JOIN reserves_out denom USING (reserve_pub,h_blind_ev)"
|
||||
" JOIN known_coins coins USING (coin_pub)"
|
||||
" WHERE payback.coin_pub=$1",
|
||||
1, NULL);
|
||||
|
||||
@ -2413,7 +2414,7 @@ postgres_get_reserve_history (void *cls,
|
||||
TALER_PQ_result_spec_amount ("amount",
|
||||
&payback->value),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
|
||||
&payback->coin_pub),
|
||||
&payback->coin.coin_pub),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
|
||||
&payback->coin_blind),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
|
||||
@ -2421,7 +2422,9 @@ postgres_get_reserve_history (void *cls,
|
||||
GNUNET_PQ_result_spec_absolute_time ("timestamp",
|
||||
&payback->timestamp),
|
||||
GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
|
||||
&payback->denom_pub.rsa_public_key),
|
||||
&payback->coin.denom_pub.rsa_public_key),
|
||||
GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
|
||||
&payback->coin.denom_sig.rsa_signature),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
if (GNUNET_OK !=
|
||||
@ -4226,7 +4229,9 @@ postgres_get_coin_transactions (void *cls,
|
||||
GNUNET_PQ_result_spec_absolute_time ("timestamp",
|
||||
&payback->timestamp),
|
||||
GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
|
||||
&payback->denom_pub.rsa_public_key),
|
||||
&payback->coin.denom_pub.rsa_public_key),
|
||||
GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
|
||||
&payback->coin.denom_sig.rsa_signature),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
if (GNUNET_OK !=
|
||||
@ -4239,7 +4244,7 @@ postgres_get_coin_transactions (void *cls,
|
||||
PQclear (result);
|
||||
goto cleanup;
|
||||
}
|
||||
payback->coin_pub = *coin_pub;
|
||||
payback->coin.coin_pub = *coin_pub;
|
||||
}
|
||||
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
||||
tl->next = head;
|
||||
@ -4912,6 +4917,7 @@ postgres_start_deferred_wire_out (void *cls,
|
||||
PQclear (result);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
PQclear (result);
|
||||
result = PQexec (session->conn,
|
||||
"SET CONSTRAINTS wire_out_ref DEFERRED");
|
||||
if (PGRES_COMMAND_OK !=
|
||||
@ -5702,14 +5708,12 @@ postgres_select_payback_above_serial_id (void *cls,
|
||||
{
|
||||
uint64_t rowid;
|
||||
struct TALER_ReservePublicKeyP reserve_pub;
|
||||
struct TALER_CoinSpendPublicKeyP coin_pub;
|
||||
struct TALER_CoinPublicInfo coin;
|
||||
struct TALER_CoinSpendSignatureP coin_sig;
|
||||
struct TALER_DenominationBlindingKeyP coin_blind;
|
||||
struct TALER_Amount amount;
|
||||
struct GNUNET_HashCode h_blind_ev;
|
||||
struct GNUNET_TIME_Absolute timestamp;
|
||||
struct TALER_DenominationPublicKey denom_pub;
|
||||
struct GNUNET_HashCode h_denom_pub;
|
||||
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("payback_uuid",
|
||||
@ -5719,7 +5723,7 @@ postgres_select_payback_above_serial_id (void *cls,
|
||||
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
|
||||
&reserve_pub),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
|
||||
&coin_pub),
|
||||
&coin.coin_pub),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
|
||||
&coin_sig),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
|
||||
@ -5727,7 +5731,9 @@ postgres_select_payback_above_serial_id (void *cls,
|
||||
GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
|
||||
&h_blind_ev),
|
||||
GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
|
||||
&denom_pub.rsa_public_key),
|
||||
&coin.denom_pub.rsa_public_key),
|
||||
GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
|
||||
&coin.denom_sig.rsa_signature),
|
||||
TALER_PQ_result_spec_amount ("amount",
|
||||
&amount),
|
||||
GNUNET_PQ_result_spec_end
|
||||
@ -5742,16 +5748,13 @@ postgres_select_payback_above_serial_id (void *cls,
|
||||
PQclear (result);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.rsa_public_key,
|
||||
&h_denom_pub);
|
||||
ret = cb (cb_cls,
|
||||
rowid,
|
||||
timestamp,
|
||||
&amount,
|
||||
&reserve_pub,
|
||||
&coin_pub,
|
||||
&coin,
|
||||
&coin_sig,
|
||||
&h_denom_pub,
|
||||
&coin_blind);
|
||||
GNUNET_PQ_cleanup_result (rs);
|
||||
if (GNUNET_OK != ret)
|
||||
@ -5772,7 +5775,7 @@ postgres_select_payback_above_serial_id (void *cls,
|
||||
* @param cls closure
|
||||
* @param session database connection
|
||||
* @param reserve_pub public key of the reserve that is being refunded
|
||||
* @param coin_pub public key of the coin
|
||||
* @param coin information about the coin
|
||||
* @param coin_sig signature of the coin of type #TALER_SIGNATURE_WALLET_COIN_PAYBACK
|
||||
* @param coin_blind blinding key of the coin
|
||||
* @param amount total amount to be paid back
|
||||
@ -5786,7 +5789,7 @@ static int
|
||||
postgres_insert_payback_request (void *cls,
|
||||
struct TALER_EXCHANGEDB_Session *session,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||
const struct TALER_CoinPublicInfo *coin,
|
||||
const struct TALER_CoinSpendSignatureP *coin_sig,
|
||||
const struct TALER_DenominationBlindingKeyP *coin_blind,
|
||||
const struct TALER_Amount *amount,
|
||||
@ -5798,7 +5801,7 @@ postgres_insert_payback_request (void *cls,
|
||||
struct TALER_EXCHANGEDB_Reserve reserve;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
|
||||
GNUNET_PQ_query_param_auto_from_type (coin_pub),
|
||||
GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub),
|
||||
GNUNET_PQ_query_param_auto_from_type (coin_sig),
|
||||
GNUNET_PQ_query_param_auto_from_type (coin_blind),
|
||||
TALER_PQ_query_param_amount (amount),
|
||||
@ -5806,7 +5809,31 @@ postgres_insert_payback_request (void *cls,
|
||||
GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
int ret;
|
||||
|
||||
/* check if the coin is already known */
|
||||
ret = get_known_coin (cls,
|
||||
session,
|
||||
&coin->coin_pub,
|
||||
NULL);
|
||||
if (GNUNET_SYSERR == ret)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (GNUNET_NO == ret) /* if not, insert it */
|
||||
{
|
||||
if (GNUNET_SYSERR ==
|
||||
insert_known_coin (cls,
|
||||
session,
|
||||
coin))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* now store actual payback information */
|
||||
result = GNUNET_PQ_exec_prepared (session->conn,
|
||||
"payback_insert",
|
||||
params);
|
||||
|
@ -1385,6 +1385,7 @@ run (void *cls)
|
||||
rh = NULL;
|
||||
wire = NULL;
|
||||
session = NULL;
|
||||
deposit.coin.denom_sig.rsa_signature = NULL;
|
||||
ZR_BLK (&cbc);
|
||||
ZR_BLK (&cbc2);
|
||||
if (NULL ==
|
||||
@ -1513,12 +1514,15 @@ run (void *cls)
|
||||
|
||||
RND_BLK (&coin_sig);
|
||||
RND_BLK (&coin_blind);
|
||||
RND_BLK (&deposit.coin.coin_pub);
|
||||
deposit.coin.denom_pub = dkp->pub;
|
||||
deposit.coin.denom_sig = cbc.sig;
|
||||
deadline = GNUNET_TIME_absolute_get ();
|
||||
FAILIF (GNUNET_OK !=
|
||||
plugin->insert_payback_request (plugin->cls,
|
||||
session,
|
||||
&reserve_pub,
|
||||
&deposit.coin.coin_pub,
|
||||
&deposit.coin,
|
||||
&coin_sig,
|
||||
&coin_blind,
|
||||
&value,
|
||||
@ -1569,7 +1573,7 @@ run (void *cls)
|
||||
FAILIF (0 != memcmp (&payback->reserve_pub,
|
||||
&reserve_pub,
|
||||
sizeof (reserve_pub)));
|
||||
FAILIF (0 != memcmp (&payback->coin_pub,
|
||||
FAILIF (0 != memcmp (&payback->coin.coin_pub,
|
||||
&deposit.coin.coin_pub,
|
||||
sizeof (deposit.coin.coin_pub)));
|
||||
FAILIF (0 != TALER_amount_cmp (&payback->value,
|
||||
@ -1717,7 +1721,7 @@ run (void *cls)
|
||||
plugin->insert_payback_request (plugin->cls,
|
||||
session,
|
||||
&reserve_pub,
|
||||
&deposit.coin.coin_pub,
|
||||
&deposit.coin,
|
||||
&coin_sig,
|
||||
&coin_blind,
|
||||
&value,
|
||||
@ -1824,7 +1828,7 @@ run (void *cls)
|
||||
FAILIF (0 != memcmp (&payback->reserve_pub,
|
||||
&reserve_pub,
|
||||
sizeof (reserve_pub)));
|
||||
FAILIF (0 != memcmp (&payback->coin_pub,
|
||||
FAILIF (0 != memcmp (&payback->coin.coin_pub,
|
||||
&deposit.coin.coin_pub,
|
||||
sizeof (deposit.coin.coin_pub)));
|
||||
FAILIF (0 != TALER_amount_cmp (&payback->value,
|
||||
|
@ -153,9 +153,9 @@ struct TALER_EXCHANGEDB_Payback
|
||||
{
|
||||
|
||||
/**
|
||||
* Public key of the coin that was paid back.
|
||||
* Information about the coin that was paid back.
|
||||
*/
|
||||
struct TALER_CoinSpendPublicKeyP coin_pub;
|
||||
struct TALER_CoinPublicInfo coin;
|
||||
|
||||
/**
|
||||
* Blinding factor supplied to prove to the exchange that
|
||||
@ -184,12 +184,6 @@ struct TALER_EXCHANGEDB_Payback
|
||||
*/
|
||||
struct GNUNET_TIME_Absolute timestamp;
|
||||
|
||||
/**
|
||||
* Public key representing the denomination of
|
||||
* @e coin_pub.
|
||||
*/
|
||||
struct TALER_DenominationPublicKey denom_pub;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -914,9 +908,8 @@ typedef int
|
||||
* @param timestamp when did we receive the payback request
|
||||
* @param amount how much should be added back to the reserve
|
||||
* @param reserve_pub public key of the reserve
|
||||
* @param coin_pub public key of the coin
|
||||
* @param coin public information about the coin
|
||||
* @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_PAYBACK
|
||||
* @param h_denom_pub hash of the denomination key of the coin
|
||||
* @param coin_blind blinding factor used to blind the coin
|
||||
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
|
||||
*/
|
||||
@ -926,9 +919,8 @@ typedef int
|
||||
struct GNUNET_TIME_Absolute timestamp,
|
||||
const struct TALER_Amount *amount,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||
const struct TALER_CoinPublicInfo *coin,
|
||||
const struct TALER_CoinSpendSignatureP *coin_sig,
|
||||
const struct GNUNET_HashCode *h_denom_pub,
|
||||
const struct TALER_DenominationBlindingKeyP *coin_blind);
|
||||
|
||||
|
||||
@ -1979,7 +1971,7 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
* @param cls closure
|
||||
* @param session database connection
|
||||
* @param reserve_pub public key of the reserve that is being refunded
|
||||
* @param coin_pub public key of the coin
|
||||
* @param coin public information about a coin
|
||||
* @param coin_sig signature of the coin of type #TALER_SIGNATURE_WALLET_COIN_PAYBACK
|
||||
* @param coin_blind blinding key of the coin
|
||||
* @param h_blind_ev blinded envelope, as calculated by the exchange
|
||||
@ -1994,7 +1986,7 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
(*insert_payback_request)(void *cls,
|
||||
struct TALER_EXCHANGEDB_Session *session,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||
const struct TALER_CoinPublicInfo *coin,
|
||||
const struct TALER_CoinSpendSignatureP *coin_sig,
|
||||
const struct TALER_DenominationBlindingKeyP *coin_blind,
|
||||
const struct TALER_Amount *amount,
|
||||
|
Loading…
Reference in New Issue
Block a user