/* This file is part of TALER Copyright (C) 2018 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 */ /** * @file testing/testing_api_cmd_insert_deposit.c * @brief deposit a coin directly into the database. * @author Marcello Stanisci * @author Christian Grothoff */ #include "platform.h" #include "taler_util.h" #include "taler_json_lib.h" #include #include "taler_signatures.h" #include "taler_testing_lib.h" #include "taler_exchangedb_plugin.h" /** * State for a "insert-deposit" CMD. */ struct InsertDepositState { /** * Configuration file used by the command. */ const struct TALER_TESTING_DatabaseConnection *dbc; /** * Human-readable name of the shop. */ const char *merchant_name; /** * Merchant account name (NOT a payto-URI). */ const char *merchant_account; /** * Deadline before which the aggregator should * send the payment to the merchant. */ struct GNUNET_TIME_Relative wire_deadline; /** * When did the exchange receive the deposit? */ struct GNUNET_TIME_Absolute exchange_timestamp; /** * Amount to deposit, inclusive of deposit fee. */ const char *amount_with_fee; /** * Deposit fee. */ const char *deposit_fee; }; /** * Setup (fake) information about a coin used in deposit. * * @param[out] issue information to initialize with "valid" data */ static void fake_issue (struct TALER_EXCHANGEDB_DenominationKeyInformationP *issue) { struct GNUNET_TIME_Absolute now; memset (issue, 0, sizeof (struct TALER_EXCHANGEDB_DenominationKeyInformationP)); now = GNUNET_TIME_absolute_get (); (void) GNUNET_TIME_round_abs (&now); issue->properties.start = GNUNET_TIME_absolute_hton (now); issue->properties.expire_withdraw = GNUNET_TIME_absolute_hton ( GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_MINUTES)); issue->properties.expire_deposit = GNUNET_TIME_absolute_hton ( GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_HOURS)); issue->properties.expire_legal = GNUNET_TIME_absolute_hton ( GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_DAYS)); GNUNET_assert (GNUNET_OK == TALER_string_to_amount_nbo ("EUR:1", &issue->properties.value)); GNUNET_assert (GNUNET_OK == TALER_string_to_amount_nbo ("EUR:0.1", &issue->properties.fee_withdraw)); GNUNET_assert (GNUNET_OK == TALER_string_to_amount_nbo ("EUR:0.1", &issue->properties.fee_deposit)); GNUNET_assert (GNUNET_OK == TALER_string_to_amount_nbo ("EUR:0.1", &issue->properties.fee_refresh)); GNUNET_assert (GNUNET_OK == TALER_string_to_amount_nbo ("EUR:0.1", &issue->properties.fee_refund)); } /** * Run the command. * * @param cls closure. * @param cmd the commaind being run. * @param is interpreter state. */ static void insert_deposit_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) { struct InsertDepositState *ids = cls; struct TALER_EXCHANGEDB_Deposit deposit; struct TALER_MerchantPrivateKeyP merchant_priv; struct TALER_EXCHANGEDB_DenominationKeyInformationP issue; struct TALER_DenominationPublicKey dpk = { .cipher = TALER_DENOMINATION_RSA }; struct GNUNET_CRYPTO_RsaPrivateKey *denom_priv; struct GNUNET_HashCode hc; // prepare and store issue first. fake_issue (&issue); denom_priv = GNUNET_CRYPTO_rsa_private_key_create (1024); dpk.details.rsa_public_key = GNUNET_CRYPTO_rsa_private_key_get_public ( denom_priv); TALER_denom_pub_hash (&dpk, &issue.properties.denom_hash); if ( (GNUNET_OK != ids->dbc->plugin->start (ids->dbc->plugin->cls, "talertestinglib: denomination insertion")) || (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ids->dbc->plugin->insert_denomination_info (ids->dbc->plugin->cls, &dpk, &issue)) || (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != ids->dbc->plugin->commit (ids->dbc->plugin->cls)) ) { TALER_TESTING_interpreter_fail (is); return; } /* prepare and store deposit now. */ memset (&deposit, 0, sizeof (deposit)); GNUNET_CRYPTO_kdf (&merchant_priv, sizeof (struct TALER_MerchantPrivateKeyP), "merchant-priv", strlen ("merchant-priv"), ids->merchant_name, strlen (ids->merchant_name), NULL, 0); GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv.eddsa_priv, &deposit.merchant_pub.eddsa_pub); GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &deposit.h_contract_terms.hash); if ( (GNUNET_OK != TALER_string_to_amount (ids->amount_with_fee, &deposit.amount_with_fee)) || (GNUNET_OK != TALER_string_to_amount (ids->deposit_fee, &deposit.deposit_fee)) ) { TALER_TESTING_interpreter_fail (is); return; } TALER_denom_pub_hash (&dpk, &deposit.coin.denom_pub_hash); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &deposit.coin.coin_pub, sizeof (deposit.coin.coin_pub)); GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &hc); deposit.coin.denom_sig.cipher = TALER_DENOMINATION_RSA; deposit.coin.denom_sig.details.rsa_signature = GNUNET_CRYPTO_rsa_sign_fdh (denom_priv, &hc); { char *str; struct TALER_WireSalt salt; GNUNET_asprintf (&str, "payto://x-taler-bank/localhost/%s", ids->merchant_account); memset (&salt, 46, sizeof (salt)); deposit.receiver_wire_account = GNUNET_JSON_PACK ( GNUNET_JSON_pack_data_auto ("salt", &salt), GNUNET_JSON_pack_string ("payto_uri", str)); GNUNET_free (str); } GNUNET_assert (GNUNET_OK == TALER_JSON_merchant_wire_signature_hash ( deposit.receiver_wire_account, &deposit.h_wire)); deposit.timestamp = GNUNET_TIME_absolute_get (); (void) GNUNET_TIME_round_abs (&deposit.timestamp); deposit.wire_deadline = GNUNET_TIME_relative_to_absolute (ids->wire_deadline); (void) GNUNET_TIME_round_abs (&deposit.wire_deadline); /* finally, actually perform the DB operation */ if ( (GNUNET_OK != ids->dbc->plugin->start (ids->dbc->plugin->cls, "libtalertesting: insert deposit")) || (0 > ids->dbc->plugin->ensure_coin_known (ids->dbc->plugin->cls, &deposit.coin)) || (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ids->dbc->plugin->insert_deposit (ids->dbc->plugin->cls, ids->exchange_timestamp, &deposit)) || (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != ids->dbc->plugin->commit (ids->dbc->plugin->cls)) ) { GNUNET_break (0); ids->dbc->plugin->rollback (ids->dbc->plugin->cls); TALER_TESTING_interpreter_fail (is); } TALER_denom_sig_free (&deposit.coin.denom_sig); TALER_denom_pub_free (&dpk); GNUNET_CRYPTO_rsa_private_key_free (denom_priv); json_decref (deposit.receiver_wire_account); TALER_TESTING_interpreter_next (is); } /** * Free the state of a "auditor-dbinit" CMD, and possibly kills its * process if it did not terminate correctly. * * @param cls closure. * @param cmd the command being freed. */ static void insert_deposit_cleanup (void *cls, const struct TALER_TESTING_Command *cmd) { struct InsertDepositState *ids = cls; GNUNET_free (ids); } struct TALER_TESTING_Command TALER_TESTING_cmd_insert_deposit ( const char *label, const struct TALER_TESTING_DatabaseConnection *dbc, const char *merchant_name, const char *merchant_account, struct GNUNET_TIME_Absolute exchange_timestamp, struct GNUNET_TIME_Relative wire_deadline, const char *amount_with_fee, const char *deposit_fee) { struct InsertDepositState *ids; GNUNET_TIME_round_abs (&exchange_timestamp); ids = GNUNET_new (struct InsertDepositState); ids->dbc = dbc; ids->merchant_name = merchant_name; ids->merchant_account = merchant_account; ids->exchange_timestamp = exchange_timestamp; ids->wire_deadline = wire_deadline; ids->amount_with_fee = amount_with_fee; ids->deposit_fee = deposit_fee; { struct TALER_TESTING_Command cmd = { .cls = ids, .label = label, .run = &insert_deposit_run, .cleanup = &insert_deposit_cleanup }; return cmd; } } /* end of testing_api_cmd_insert_deposit.c */