testing_api_cmd_insert_deposit.c (12907B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2018, 2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it 6 under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with TALER; see the file COPYING. If not, 17 see <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file testing/testing_api_cmd_insert_deposit.c 21 * @brief deposit a coin directly into the database. 22 * @author Marcello Stanisci 23 * @author Christian Grothoff 24 */ 25 #include "platform.h" /* UNNECESSARY? */ 26 #include "taler/taler_util.h" 27 #include "taler/taler_json_lib.h" 28 #include <gnunet/gnunet_curl_lib.h> 29 #include "taler/taler_signatures.h" 30 #include "taler/taler_testing_lib.h" 31 #include "exchangedb_lib.h" 32 #include "exchangedb_lib.h" 33 #include "exchange-database/rollback.h" 34 #include "exchange-database/start.h" /* UNNECESSARY? */ 35 #include "exchange-database/commit.h" 36 #include "exchange-database/preflight.h" 37 #include "exchange-database/do_deposit.h" 38 #include "exchange-database/insert_denomination_info.h" 39 #include "exchange-database/do_insert_known_coins.h" 40 41 /** 42 * State for a "insert-deposit" CMD. 43 */ 44 struct InsertDepositState 45 { 46 /** 47 * Database connection we use. 48 */ 49 struct TALER_EXCHANGEDB_PostgresContext *plugin; 50 51 /** 52 * Human-readable name of the shop. 53 */ 54 const char *merchant_name; 55 56 /** 57 * Merchant account name (NOT a payto-URI). 58 */ 59 const char *merchant_account; 60 61 /** 62 * Deadline before which the aggregator should 63 * send the payment to the merchant. 64 */ 65 struct GNUNET_TIME_Relative wire_deadline; 66 67 /** 68 * When did the exchange receive the deposit? 69 */ 70 struct GNUNET_TIME_Timestamp exchange_timestamp; 71 72 /** 73 * Amount to deposit, inclusive of deposit fee. 74 */ 75 const char *amount_with_fee; 76 77 /** 78 * Deposit fee. 79 */ 80 const char *deposit_fee; 81 82 /** 83 * Do we used a cached @e plugin? 84 */ 85 bool cached; 86 }; 87 88 /** 89 * Setup (fake) information about a coin used in deposit. 90 * 91 * @param[out] issue information to initialize with "valid" data 92 */ 93 static void 94 fake_issue (struct TALER_EXCHANGEDB_DenominationKeyInformation *issue) 95 { 96 struct GNUNET_TIME_Timestamp now; 97 98 memset (issue, 99 0, 100 sizeof (*issue)); 101 now = GNUNET_TIME_timestamp_get (); 102 issue->start 103 = now; 104 issue->expire_withdraw 105 = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MINUTES); 106 issue->expire_deposit 107 = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_HOURS); 108 issue->expire_legal 109 = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_DAYS); 110 GNUNET_assert (GNUNET_OK == 111 TALER_string_to_amount ("EUR:1", 112 &issue->value)); 113 GNUNET_assert (GNUNET_OK == 114 TALER_string_to_amount ("EUR:0.1", 115 &issue->fees.withdraw)); 116 GNUNET_assert (GNUNET_OK == 117 TALER_string_to_amount ("EUR:0.1", 118 &issue->fees.deposit)); 119 GNUNET_assert (GNUNET_OK == 120 TALER_string_to_amount ("EUR:0.1", 121 &issue->fees.refresh)); 122 GNUNET_assert (GNUNET_OK == 123 TALER_string_to_amount ("EUR:0.1", 124 &issue->fees.refund)); 125 } 126 127 128 /** 129 * Run the command. 130 * 131 * @param cls closure. 132 * @param cmd the commaind being run. 133 * @param is interpreter state. 134 */ 135 static void 136 insert_deposit_run (void *cls, 137 const struct TALER_TESTING_Command *cmd, 138 struct TALER_TESTING_Interpreter *is) 139 { 140 struct InsertDepositState *ids = cls; 141 struct TALER_EXCHANGEDB_CoinDepositInformation deposit; 142 struct TALER_EXCHANGEDB_BatchDeposit bd; 143 struct TALER_MerchantPrivateKeyP merchant_priv; 144 struct TALER_EXCHANGEDB_DenominationKeyInformation issue; 145 struct TALER_DenominationPublicKey dpk; 146 struct TALER_DenominationPrivateKey denom_priv; 147 struct TALER_FullPayto receiver_wire_account; 148 149 (void) cmd; 150 if (NULL == ids->plugin) 151 { 152 GNUNET_break (0); 153 TALER_TESTING_interpreter_fail (is); 154 return; 155 } 156 if (GNUNET_OK != 157 TALER_EXCHANGEDB_preflight (ids->plugin)) 158 { 159 GNUNET_break (0); 160 TALER_TESTING_interpreter_fail (is); 161 return; 162 } 163 fake_issue (&issue); 164 GNUNET_assert (GNUNET_OK == 165 TALER_denom_priv_create (&denom_priv, 166 &dpk, 167 GNUNET_CRYPTO_BSA_RSA, 168 1024)); 169 TALER_denom_pub_hash (&dpk, 170 &issue.denom_hash); 171 172 if ( (GNUNET_OK != 173 TALER_EXCHANGEDB_start (ids->plugin, 174 "talertestinglib: denomination insertion")) || 175 (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 176 TALER_EXCHANGEDB_insert_denomination_info (ids->plugin, 177 &dpk, 178 &issue)) || 179 (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 180 TALER_EXCHANGEDB_commit (ids->plugin)) ) 181 { 182 TALER_TESTING_interpreter_fail (is); 183 TALER_denom_pub_free (&dpk); 184 TALER_denom_priv_free (&denom_priv); 185 return; 186 } 187 188 /* prepare and store deposit now. */ 189 memset (&deposit, 190 0, 191 sizeof (deposit)); 192 memset (&bd, 193 0, 194 sizeof (bd)); 195 bd.cdis = &deposit; 196 bd.num_cdis = 1; 197 198 GNUNET_assert ( 199 GNUNET_YES == 200 GNUNET_CRYPTO_hkdf_gnunet ( 201 &merchant_priv, 202 sizeof (struct TALER_MerchantPrivateKeyP), 203 "merchant-priv", 204 strlen ("merchant-priv"), 205 ids->merchant_name, 206 strlen (ids->merchant_name))); 207 GNUNET_assert ( 208 GNUNET_YES == 209 GNUNET_CRYPTO_hkdf_gnunet ( 210 &bd.merchant_sig, 211 sizeof (struct TALER_MerchantSignatureP), 212 "merchant-sig", 213 strlen ("merchant-sig"), 214 ids->merchant_name, 215 strlen (ids->merchant_name))); 216 GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv.eddsa_priv, 217 &bd.merchant_pub.eddsa_pub); 218 GNUNET_CRYPTO_random_block (&bd.h_contract_terms.hash, 219 sizeof bd.h_contract_terms.hash); 220 if (GNUNET_OK != 221 TALER_string_to_amount (ids->amount_with_fee, 222 &deposit.amount_with_fee)) 223 { 224 TALER_TESTING_interpreter_fail (is); 225 TALER_denom_pub_free (&dpk); 226 TALER_denom_priv_free (&denom_priv); 227 return; 228 } 229 230 TALER_denom_pub_hash (&dpk, 231 &deposit.coin.denom_pub_hash); 232 GNUNET_CRYPTO_random_block (&deposit.coin.coin_pub, 233 sizeof (deposit.coin.coin_pub)); 234 { 235 struct TALER_CoinPubHashP c_hash; 236 struct TALER_PlanchetDetail pd; 237 struct TALER_BlindedDenominationSignature bds; 238 struct TALER_PlanchetMasterSecretP ps; 239 union GNUNET_CRYPTO_BlindingSecretP bks; 240 const struct TALER_ExchangeBlindingValues *alg_values; 241 242 alg_values = TALER_denom_ewv_rsa_singleton (); 243 TALER_planchet_blinding_secret_create (&ps, 244 alg_values, 245 &bks); 246 GNUNET_assert (GNUNET_OK == 247 TALER_denom_blind (&dpk, 248 &bks, 249 NULL, /* no age restriction active */ 250 NULL, /* no nonce needed */ 251 &deposit.coin.coin_pub, 252 alg_values, 253 &c_hash, 254 &pd.blinded_planchet)); 255 GNUNET_assert (GNUNET_OK == 256 TALER_denom_sign_blinded (&bds, 257 &denom_priv, 258 false, 259 &pd.blinded_planchet)); 260 TALER_blinded_planchet_free (&pd.blinded_planchet); 261 GNUNET_assert (GNUNET_OK == 262 TALER_denom_sig_unblind (&deposit.coin.denom_sig, 263 &bds, 264 &bks, 265 &c_hash, 266 alg_values, 267 &dpk)); 268 TALER_blinded_denom_sig_free (&bds); 269 } 270 GNUNET_asprintf (&receiver_wire_account.full_payto, 271 "payto://x-taler-bank/localhost/%s?receiver-name=%s", 272 ids->merchant_account, 273 ids->merchant_account); 274 bd.receiver_wire_account = receiver_wire_account; 275 TALER_full_payto_hash (bd.receiver_wire_account, 276 &bd.wire_target_h_payto); 277 memset (&bd.wire_salt, 278 46, 279 sizeof (bd.wire_salt)); 280 bd.wallet_timestamp = GNUNET_TIME_timestamp_get (); 281 bd.wire_deadline = GNUNET_TIME_relative_to_timestamp ( 282 ids->wire_deadline); 283 /* finally, actually perform the DB operation */ 284 { 285 const struct TALER_CoinPublicInfo *coins[1] = { &deposit.coin }; 286 struct TALER_EXCHANGEDB_CoinKnownResult ckr; 287 struct TALER_Amount total; 288 bool balance_ok; 289 uint32_t bad_index; 290 bool ctr_conflict; 291 292 if ( (GNUNET_OK != 293 TALER_EXCHANGEDB_start (ids->plugin, 294 "libtalertesting: insert deposit")) || 295 (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 296 TALER_EXCHANGEDB_do_insert_known_coins (ids->plugin, 297 1, 298 coins, 299 &ckr)) || 300 (0 > ckr.status) || 301 (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 302 TALER_EXCHANGEDB_do_deposit (ids->plugin, 303 &bd, 304 &issue.fees.deposit, 305 &ids->exchange_timestamp, 306 &total, 307 &balance_ok, 308 &bad_index, 309 &ctr_conflict)) || 310 (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 311 TALER_EXCHANGEDB_commit (ids->plugin)) ) 312 { 313 GNUNET_break (0); 314 TALER_EXCHANGEDB_rollback (ids->plugin); 315 GNUNET_free (receiver_wire_account.full_payto); 316 TALER_denom_pub_free (&dpk); 317 TALER_denom_priv_free (&denom_priv); 318 TALER_TESTING_interpreter_fail (is); 319 return; 320 } 321 } 322 323 TALER_denom_sig_free (&deposit.coin.denom_sig); 324 TALER_denom_pub_free (&dpk); 325 TALER_denom_priv_free (&denom_priv); 326 GNUNET_free (receiver_wire_account.full_payto); 327 TALER_TESTING_interpreter_next (is); 328 } 329 330 331 /** 332 * Free the state of a "auditor-dbinit" CMD, and possibly kills its 333 * process if it did not terminate correctly. 334 * 335 * @param cls closure. 336 * @param cmd the command being freed. 337 */ 338 static void 339 insert_deposit_cleanup (void *cls, 340 const struct TALER_TESTING_Command *cmd) 341 { 342 struct InsertDepositState *ids = cls; 343 344 (void) cmd; 345 if ( (NULL != ids->plugin) && 346 (! ids->cached) ) 347 { 348 // FIXME: historically, we also did: 349 // TALER_EXCHANGEDB_drop_tables (ids->plugin); 350 TALER_EXCHANGEDB_disconnect (ids->plugin); 351 ids->plugin = NULL; 352 } 353 GNUNET_free (ids); 354 } 355 356 357 struct TALER_TESTING_Command 358 TALER_TESTING_cmd_insert_deposit ( 359 const char *label, 360 const struct GNUNET_CONFIGURATION_Handle *db_cfg, 361 const char *merchant_name, 362 const char *merchant_account, 363 struct GNUNET_TIME_Timestamp exchange_timestamp, 364 struct GNUNET_TIME_Relative wire_deadline, 365 const char *amount_with_fee, 366 const char *deposit_fee) 367 { 368 static struct TALER_EXCHANGEDB_PostgresContext *pluginc; 369 static const struct GNUNET_CONFIGURATION_Handle *db_cfgc; 370 struct InsertDepositState *ids; 371 372 ids = GNUNET_new (struct InsertDepositState); 373 if (db_cfgc == db_cfg) 374 { 375 ids->plugin = pluginc; 376 ids->cached = true; 377 } 378 else 379 { 380 ids->plugin = TALER_EXCHANGEDB_connect (db_cfg); 381 pluginc = ids->plugin; 382 db_cfgc = db_cfg; 383 } 384 ids->merchant_name = merchant_name; 385 ids->merchant_account = merchant_account; 386 ids->exchange_timestamp = exchange_timestamp; 387 ids->wire_deadline = wire_deadline; 388 ids->amount_with_fee = amount_with_fee; 389 ids->deposit_fee = deposit_fee; 390 391 { 392 struct TALER_TESTING_Command cmd = { 393 .cls = ids, 394 .label = label, 395 .run = &insert_deposit_run, 396 .cleanup = &insert_deposit_cleanup 397 }; 398 399 return cmd; 400 } 401 } 402 403 404 /* end of testing_api_cmd_insert_deposit.c */