testing_api_cmd_insert_deposit.c (12757B)
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 "taler/platform.h" 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 "taler/taler_exchangedb_plugin.h" 32 #include "taler/taler_exchangedb_lib.h" 33 34 35 /** 36 * State for a "insert-deposit" CMD. 37 */ 38 struct InsertDepositState 39 { 40 /** 41 * Database connection we use. 42 */ 43 struct TALER_EXCHANGEDB_Plugin *plugin; 44 45 /** 46 * Human-readable name of the shop. 47 */ 48 const char *merchant_name; 49 50 /** 51 * Merchant account name (NOT a payto-URI). 52 */ 53 const char *merchant_account; 54 55 /** 56 * Deadline before which the aggregator should 57 * send the payment to the merchant. 58 */ 59 struct GNUNET_TIME_Relative wire_deadline; 60 61 /** 62 * When did the exchange receive the deposit? 63 */ 64 struct GNUNET_TIME_Timestamp exchange_timestamp; 65 66 /** 67 * Amount to deposit, inclusive of deposit fee. 68 */ 69 const char *amount_with_fee; 70 71 /** 72 * Deposit fee. 73 */ 74 const char *deposit_fee; 75 76 /** 77 * Do we used a cached @e plugin? 78 */ 79 bool cached; 80 }; 81 82 /** 83 * Setup (fake) information about a coin used in deposit. 84 * 85 * @param[out] issue information to initialize with "valid" data 86 */ 87 static void 88 fake_issue (struct TALER_EXCHANGEDB_DenominationKeyInformation *issue) 89 { 90 struct GNUNET_TIME_Timestamp now; 91 92 memset (issue, 93 0, 94 sizeof (*issue)); 95 now = GNUNET_TIME_timestamp_get (); 96 issue->start 97 = now; 98 issue->expire_withdraw 99 = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MINUTES); 100 issue->expire_deposit 101 = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_HOURS); 102 issue->expire_legal 103 = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_DAYS); 104 GNUNET_assert (GNUNET_OK == 105 TALER_string_to_amount ("EUR:1", 106 &issue->value)); 107 GNUNET_assert (GNUNET_OK == 108 TALER_string_to_amount ("EUR:0.1", 109 &issue->fees.withdraw)); 110 GNUNET_assert (GNUNET_OK == 111 TALER_string_to_amount ("EUR:0.1", 112 &issue->fees.deposit)); 113 GNUNET_assert (GNUNET_OK == 114 TALER_string_to_amount ("EUR:0.1", 115 &issue->fees.refresh)); 116 GNUNET_assert (GNUNET_OK == 117 TALER_string_to_amount ("EUR:0.1", 118 &issue->fees.refund)); 119 } 120 121 122 /** 123 * Run the command. 124 * 125 * @param cls closure. 126 * @param cmd the commaind being run. 127 * @param is interpreter state. 128 */ 129 static void 130 insert_deposit_run (void *cls, 131 const struct TALER_TESTING_Command *cmd, 132 struct TALER_TESTING_Interpreter *is) 133 { 134 struct InsertDepositState *ids = cls; 135 struct TALER_EXCHANGEDB_CoinDepositInformation deposit; 136 struct TALER_EXCHANGEDB_BatchDeposit bd; 137 struct TALER_MerchantPrivateKeyP merchant_priv; 138 struct TALER_EXCHANGEDB_DenominationKeyInformation issue; 139 struct TALER_DenominationPublicKey dpk; 140 struct TALER_DenominationPrivateKey denom_priv; 141 struct TALER_FullPayto receiver_wire_account; 142 143 (void) cmd; 144 if (NULL == ids->plugin) 145 { 146 GNUNET_break (0); 147 TALER_TESTING_interpreter_fail (is); 148 return; 149 } 150 if (GNUNET_OK != 151 ids->plugin->preflight (ids->plugin->cls)) 152 { 153 GNUNET_break (0); 154 TALER_TESTING_interpreter_fail (is); 155 return; 156 } 157 fake_issue (&issue); 158 GNUNET_assert (GNUNET_OK == 159 TALER_denom_priv_create (&denom_priv, 160 &dpk, 161 GNUNET_CRYPTO_BSA_RSA, 162 1024)); 163 TALER_denom_pub_hash (&dpk, 164 &issue.denom_hash); 165 166 if ( (GNUNET_OK != 167 ids->plugin->start (ids->plugin->cls, 168 "talertestinglib: denomination insertion")) || 169 (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 170 ids->plugin->insert_denomination_info (ids->plugin->cls, 171 &dpk, 172 &issue)) || 173 (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 174 ids->plugin->commit (ids->plugin->cls)) ) 175 { 176 TALER_TESTING_interpreter_fail (is); 177 TALER_denom_pub_free (&dpk); 178 TALER_denom_priv_free (&denom_priv); 179 return; 180 } 181 182 /* prepare and store deposit now. */ 183 memset (&deposit, 184 0, 185 sizeof (deposit)); 186 memset (&bd, 187 0, 188 sizeof (bd)); 189 bd.cdis = &deposit; 190 bd.num_cdis = 1; 191 192 GNUNET_assert ( 193 GNUNET_YES == 194 GNUNET_CRYPTO_kdf (&merchant_priv, 195 sizeof (struct TALER_MerchantPrivateKeyP), 196 "merchant-priv", 197 strlen ("merchant-priv"), 198 ids->merchant_name, 199 strlen (ids->merchant_name), 200 NULL, 201 0)); 202 GNUNET_assert ( 203 GNUNET_YES == 204 GNUNET_CRYPTO_kdf (&bd.merchant_sig, 205 sizeof (struct TALER_MerchantSignatureP), 206 "merchant-sig", 207 strlen ("merchant-sig"), 208 ids->merchant_name, 209 strlen (ids->merchant_name), 210 NULL, 211 0)); 212 GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv.eddsa_priv, 213 &bd.merchant_pub.eddsa_pub); 214 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, 215 &bd.h_contract_terms.hash); 216 if (GNUNET_OK != 217 TALER_string_to_amount (ids->amount_with_fee, 218 &deposit.amount_with_fee)) 219 { 220 TALER_TESTING_interpreter_fail (is); 221 TALER_denom_pub_free (&dpk); 222 TALER_denom_priv_free (&denom_priv); 223 return; 224 } 225 226 TALER_denom_pub_hash (&dpk, 227 &deposit.coin.denom_pub_hash); 228 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, 229 &deposit.coin.coin_pub, 230 sizeof (deposit.coin.coin_pub)); 231 { 232 struct TALER_CoinPubHashP c_hash; 233 struct TALER_PlanchetDetail pd; 234 struct TALER_BlindedDenominationSignature bds; 235 struct TALER_PlanchetMasterSecretP ps; 236 union GNUNET_CRYPTO_BlindingSecretP bks; 237 const struct TALER_ExchangeBlindingValues *alg_values; 238 239 alg_values = TALER_denom_ewv_rsa_singleton (); 240 TALER_planchet_blinding_secret_create (&ps, 241 alg_values, 242 &bks); 243 GNUNET_assert (GNUNET_OK == 244 TALER_denom_blind (&dpk, 245 &bks, 246 NULL, /* no age restriction active */ 247 NULL, /* no nonce needed */ 248 &deposit.coin.coin_pub, 249 alg_values, 250 &c_hash, 251 &pd.blinded_planchet)); 252 GNUNET_assert (GNUNET_OK == 253 TALER_denom_sign_blinded (&bds, 254 &denom_priv, 255 false, 256 &pd.blinded_planchet)); 257 TALER_blinded_planchet_free (&pd.blinded_planchet); 258 GNUNET_assert (GNUNET_OK == 259 TALER_denom_sig_unblind (&deposit.coin.denom_sig, 260 &bds, 261 &bks, 262 &c_hash, 263 alg_values, 264 &dpk)); 265 TALER_blinded_denom_sig_free (&bds); 266 } 267 GNUNET_asprintf (&receiver_wire_account.full_payto, 268 "payto://x-taler-bank/localhost/%s?receiver-name=%s", 269 ids->merchant_account, 270 ids->merchant_account); 271 bd.receiver_wire_account = receiver_wire_account; 272 TALER_full_payto_hash (bd.receiver_wire_account, 273 &bd.wire_target_h_payto); 274 memset (&bd.wire_salt, 275 46, 276 sizeof (bd.wire_salt)); 277 bd.wallet_timestamp = GNUNET_TIME_timestamp_get (); 278 bd.wire_deadline = GNUNET_TIME_relative_to_timestamp ( 279 ids->wire_deadline); 280 /* finally, actually perform the DB operation */ 281 { 282 uint64_t known_coin_id; 283 struct TALER_DenominationHashP dph; 284 struct TALER_AgeCommitmentHashP agh; 285 bool balance_ok; 286 uint32_t bad_index; 287 bool ctr_conflict; 288 289 if ( (GNUNET_OK != 290 ids->plugin->start (ids->plugin->cls, 291 "libtalertesting: insert deposit")) || 292 (0 > 293 ids->plugin->ensure_coin_known (ids->plugin->cls, 294 &deposit.coin, 295 &known_coin_id, 296 &dph, 297 &agh)) || 298 (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 299 ids->plugin->do_deposit (ids->plugin->cls, 300 &bd, 301 &ids->exchange_timestamp, 302 &balance_ok, 303 &bad_index, 304 &ctr_conflict)) || 305 (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 306 ids->plugin->commit (ids->plugin->cls)) ) 307 { 308 GNUNET_break (0); 309 ids->plugin->rollback (ids->plugin->cls); 310 GNUNET_free (receiver_wire_account.full_payto); 311 TALER_denom_pub_free (&dpk); 312 TALER_denom_priv_free (&denom_priv); 313 TALER_TESTING_interpreter_fail (is); 314 return; 315 } 316 } 317 318 TALER_denom_sig_free (&deposit.coin.denom_sig); 319 TALER_denom_pub_free (&dpk); 320 TALER_denom_priv_free (&denom_priv); 321 GNUNET_free (receiver_wire_account.full_payto); 322 TALER_TESTING_interpreter_next (is); 323 } 324 325 326 /** 327 * Free the state of a "auditor-dbinit" CMD, and possibly kills its 328 * process if it did not terminate correctly. 329 * 330 * @param cls closure. 331 * @param cmd the command being freed. 332 */ 333 static void 334 insert_deposit_cleanup (void *cls, 335 const struct TALER_TESTING_Command *cmd) 336 { 337 struct InsertDepositState *ids = cls; 338 339 (void) cmd; 340 if ( (NULL != ids->plugin) && 341 (! ids->cached) ) 342 { 343 // FIXME: historically, we also did: 344 // ids->plugin->drop_tables (ids->plugin->cls); 345 TALER_EXCHANGEDB_plugin_unload (ids->plugin); 346 ids->plugin = NULL; 347 } 348 GNUNET_free (ids); 349 } 350 351 352 struct TALER_TESTING_Command 353 TALER_TESTING_cmd_insert_deposit ( 354 const char *label, 355 const struct GNUNET_CONFIGURATION_Handle *db_cfg, 356 const char *merchant_name, 357 const char *merchant_account, 358 struct GNUNET_TIME_Timestamp exchange_timestamp, 359 struct GNUNET_TIME_Relative wire_deadline, 360 const char *amount_with_fee, 361 const char *deposit_fee) 362 { 363 static struct TALER_EXCHANGEDB_Plugin *pluginc; 364 static const struct GNUNET_CONFIGURATION_Handle *db_cfgc; 365 struct InsertDepositState *ids; 366 367 ids = GNUNET_new (struct InsertDepositState); 368 if (db_cfgc == db_cfg) 369 { 370 ids->plugin = pluginc; 371 ids->cached = true; 372 } 373 else 374 { 375 ids->plugin = TALER_EXCHANGEDB_plugin_load (db_cfg, 376 false); 377 pluginc = ids->plugin; 378 db_cfgc = db_cfg; 379 } 380 ids->merchant_name = merchant_name; 381 ids->merchant_account = merchant_account; 382 ids->exchange_timestamp = exchange_timestamp; 383 ids->wire_deadline = wire_deadline; 384 ids->amount_with_fee = amount_with_fee; 385 ids->deposit_fee = deposit_fee; 386 387 { 388 struct TALER_TESTING_Command cmd = { 389 .cls = ids, 390 .label = label, 391 .run = &insert_deposit_run, 392 .cleanup = &insert_deposit_cleanup 393 }; 394 395 return cmd; 396 } 397 } 398 399 400 /* end of testing_api_cmd_insert_deposit.c */