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