testing_api_cmd_insert_deposit.c (12637B)
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_hkdf_gnunet ( 195 &merchant_priv, 196 sizeof (struct TALER_MerchantPrivateKeyP), 197 "merchant-priv", 198 strlen ("merchant-priv"), 199 ids->merchant_name, 200 strlen (ids->merchant_name))); 201 GNUNET_assert ( 202 GNUNET_YES == 203 GNUNET_CRYPTO_hkdf_gnunet ( 204 &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 GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv.eddsa_priv, 211 &bd.merchant_pub.eddsa_pub); 212 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, 213 &bd.h_contract_terms.hash); 214 if (GNUNET_OK != 215 TALER_string_to_amount (ids->amount_with_fee, 216 &deposit.amount_with_fee)) 217 { 218 TALER_TESTING_interpreter_fail (is); 219 TALER_denom_pub_free (&dpk); 220 TALER_denom_priv_free (&denom_priv); 221 return; 222 } 223 224 TALER_denom_pub_hash (&dpk, 225 &deposit.coin.denom_pub_hash); 226 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, 227 &deposit.coin.coin_pub, 228 sizeof (deposit.coin.coin_pub)); 229 { 230 struct TALER_CoinPubHashP c_hash; 231 struct TALER_PlanchetDetail pd; 232 struct TALER_BlindedDenominationSignature bds; 233 struct TALER_PlanchetMasterSecretP ps; 234 union GNUNET_CRYPTO_BlindingSecretP bks; 235 const struct TALER_ExchangeBlindingValues *alg_values; 236 237 alg_values = TALER_denom_ewv_rsa_singleton (); 238 TALER_planchet_blinding_secret_create (&ps, 239 alg_values, 240 &bks); 241 GNUNET_assert (GNUNET_OK == 242 TALER_denom_blind (&dpk, 243 &bks, 244 NULL, /* no age restriction active */ 245 NULL, /* no nonce needed */ 246 &deposit.coin.coin_pub, 247 alg_values, 248 &c_hash, 249 &pd.blinded_planchet)); 250 GNUNET_assert (GNUNET_OK == 251 TALER_denom_sign_blinded (&bds, 252 &denom_priv, 253 false, 254 &pd.blinded_planchet)); 255 TALER_blinded_planchet_free (&pd.blinded_planchet); 256 GNUNET_assert (GNUNET_OK == 257 TALER_denom_sig_unblind (&deposit.coin.denom_sig, 258 &bds, 259 &bks, 260 &c_hash, 261 alg_values, 262 &dpk)); 263 TALER_blinded_denom_sig_free (&bds); 264 } 265 GNUNET_asprintf (&receiver_wire_account.full_payto, 266 "payto://x-taler-bank/localhost/%s?receiver-name=%s", 267 ids->merchant_account, 268 ids->merchant_account); 269 bd.receiver_wire_account = receiver_wire_account; 270 TALER_full_payto_hash (bd.receiver_wire_account, 271 &bd.wire_target_h_payto); 272 memset (&bd.wire_salt, 273 46, 274 sizeof (bd.wire_salt)); 275 bd.wallet_timestamp = GNUNET_TIME_timestamp_get (); 276 bd.wire_deadline = GNUNET_TIME_relative_to_timestamp ( 277 ids->wire_deadline); 278 /* finally, actually perform the DB operation */ 279 { 280 uint64_t known_coin_id; 281 struct TALER_Amount total; 282 struct TALER_DenominationHashP dph; 283 struct TALER_AgeCommitmentHashP agh; 284 bool balance_ok; 285 uint32_t bad_index; 286 bool ctr_conflict; 287 288 if ( (GNUNET_OK != 289 ids->plugin->start (ids->plugin->cls, 290 "libtalertesting: insert deposit")) || 291 (0 > 292 ids->plugin->ensure_coin_known (ids->plugin->cls, 293 &deposit.coin, 294 &known_coin_id, 295 &dph, 296 &agh)) || 297 (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 298 ids->plugin->do_deposit (ids->plugin->cls, 299 &bd, 300 &issue.fees.deposit, 301 &ids->exchange_timestamp, 302 &total, 303 &balance_ok, 304 &bad_index, 305 &ctr_conflict)) || 306 (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 307 ids->plugin->commit (ids->plugin->cls)) ) 308 { 309 GNUNET_break (0); 310 ids->plugin->rollback (ids->plugin->cls); 311 GNUNET_free (receiver_wire_account.full_payto); 312 TALER_denom_pub_free (&dpk); 313 TALER_denom_priv_free (&denom_priv); 314 TALER_TESTING_interpreter_fail (is); 315 return; 316 } 317 } 318 319 TALER_denom_sig_free (&deposit.coin.denom_sig); 320 TALER_denom_pub_free (&dpk); 321 TALER_denom_priv_free (&denom_priv); 322 GNUNET_free (receiver_wire_account.full_payto); 323 TALER_TESTING_interpreter_next (is); 324 } 325 326 327 /** 328 * Free the state of a "auditor-dbinit" CMD, and possibly kills its 329 * process if it did not terminate correctly. 330 * 331 * @param cls closure. 332 * @param cmd the command being freed. 333 */ 334 static void 335 insert_deposit_cleanup (void *cls, 336 const struct TALER_TESTING_Command *cmd) 337 { 338 struct InsertDepositState *ids = cls; 339 340 (void) cmd; 341 if ( (NULL != ids->plugin) && 342 (! ids->cached) ) 343 { 344 // FIXME: historically, we also did: 345 // ids->plugin->drop_tables (ids->plugin->cls); 346 TALER_EXCHANGEDB_plugin_unload (ids->plugin); 347 ids->plugin = NULL; 348 } 349 GNUNET_free (ids); 350 } 351 352 353 struct TALER_TESTING_Command 354 TALER_TESTING_cmd_insert_deposit ( 355 const char *label, 356 const struct GNUNET_CONFIGURATION_Handle *db_cfg, 357 const char *merchant_name, 358 const char *merchant_account, 359 struct GNUNET_TIME_Timestamp exchange_timestamp, 360 struct GNUNET_TIME_Relative wire_deadline, 361 const char *amount_with_fee, 362 const char *deposit_fee) 363 { 364 static struct TALER_EXCHANGEDB_Plugin *pluginc; 365 static const struct GNUNET_CONFIGURATION_Handle *db_cfgc; 366 struct InsertDepositState *ids; 367 368 ids = GNUNET_new (struct InsertDepositState); 369 if (db_cfgc == db_cfg) 370 { 371 ids->plugin = pluginc; 372 ids->cached = true; 373 } 374 else 375 { 376 ids->plugin = TALER_EXCHANGEDB_plugin_load (db_cfg, 377 false); 378 pluginc = ids->plugin; 379 db_cfgc = db_cfg; 380 } 381 ids->merchant_name = merchant_name; 382 ids->merchant_account = merchant_account; 383 ids->exchange_timestamp = exchange_timestamp; 384 ids->wire_deadline = wire_deadline; 385 ids->amount_with_fee = amount_with_fee; 386 ids->deposit_fee = deposit_fee; 387 388 { 389 struct TALER_TESTING_Command cmd = { 390 .cls = ids, 391 .label = label, 392 .run = &insert_deposit_run, 393 .cleanup = &insert_deposit_cleanup 394 }; 395 396 return cmd; 397 } 398 } 399 400 401 /* end of testing_api_cmd_insert_deposit.c */