testing_api_cmd_insert_deposit.c (12887B)
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_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 uint64_t known_coin_id; 286 struct TALER_Amount total; 287 struct TALER_DenominationHashP dph; 288 struct TALER_AgeCommitmentHashP agh; 289 bool balance_ok; 290 uint32_t bad_index; 291 bool ctr_conflict; 292 293 if ( (GNUNET_OK != 294 TALER_EXCHANGEDB_start (ids->plugin, 295 "libtalertesting: insert deposit")) || 296 (0 > 297 TALER_EXCHANGEDB_ensure_coin_known (ids->plugin, 298 &deposit.coin, 299 &known_coin_id, 300 &dph, 301 &agh)) || 302 (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 303 TALER_EXCHANGEDB_do_deposit (ids->plugin, 304 &bd, 305 &issue.fees.deposit, 306 &ids->exchange_timestamp, 307 &total, 308 &balance_ok, 309 &bad_index, 310 &ctr_conflict)) || 311 (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 312 TALER_EXCHANGEDB_commit (ids->plugin)) ) 313 { 314 GNUNET_break (0); 315 TALER_EXCHANGEDB_rollback (ids->plugin); 316 GNUNET_free (receiver_wire_account.full_payto); 317 TALER_denom_pub_free (&dpk); 318 TALER_denom_priv_free (&denom_priv); 319 TALER_TESTING_interpreter_fail (is); 320 return; 321 } 322 } 323 324 TALER_denom_sig_free (&deposit.coin.denom_sig); 325 TALER_denom_pub_free (&dpk); 326 TALER_denom_priv_free (&denom_priv); 327 GNUNET_free (receiver_wire_account.full_payto); 328 TALER_TESTING_interpreter_next (is); 329 } 330 331 332 /** 333 * Free the state of a "auditor-dbinit" CMD, and possibly kills its 334 * process if it did not terminate correctly. 335 * 336 * @param cls closure. 337 * @param cmd the command being freed. 338 */ 339 static void 340 insert_deposit_cleanup (void *cls, 341 const struct TALER_TESTING_Command *cmd) 342 { 343 struct InsertDepositState *ids = cls; 344 345 (void) cmd; 346 if ( (NULL != ids->plugin) && 347 (! ids->cached) ) 348 { 349 // FIXME: historically, we also did: 350 // TALER_EXCHANGEDB_drop_tables (ids->plugin); 351 TALER_EXCHANGEDB_disconnect (ids->plugin); 352 ids->plugin = NULL; 353 } 354 GNUNET_free (ids); 355 } 356 357 358 struct TALER_TESTING_Command 359 TALER_TESTING_cmd_insert_deposit ( 360 const char *label, 361 const struct GNUNET_CONFIGURATION_Handle *db_cfg, 362 const char *merchant_name, 363 const char *merchant_account, 364 struct GNUNET_TIME_Timestamp exchange_timestamp, 365 struct GNUNET_TIME_Relative wire_deadline, 366 const char *amount_with_fee, 367 const char *deposit_fee) 368 { 369 static struct TALER_EXCHANGEDB_PostgresContext *pluginc; 370 static const struct GNUNET_CONFIGURATION_Handle *db_cfgc; 371 struct InsertDepositState *ids; 372 373 ids = GNUNET_new (struct InsertDepositState); 374 if (db_cfgc == db_cfg) 375 { 376 ids->plugin = pluginc; 377 ids->cached = true; 378 } 379 else 380 { 381 ids->plugin = TALER_EXCHANGEDB_connect (db_cfg); 382 pluginc = ids->plugin; 383 db_cfgc = db_cfg; 384 } 385 ids->merchant_name = merchant_name; 386 ids->merchant_account = merchant_account; 387 ids->exchange_timestamp = exchange_timestamp; 388 ids->wire_deadline = wire_deadline; 389 ids->amount_with_fee = amount_with_fee; 390 ids->deposit_fee = deposit_fee; 391 392 { 393 struct TALER_TESTING_Command cmd = { 394 .cls = ids, 395 .label = label, 396 .run = &insert_deposit_run, 397 .cleanup = &insert_deposit_cleanup 398 }; 399 400 return cmd; 401 } 402 } 403 404 405 /* end of testing_api_cmd_insert_deposit.c */