testing_api_cmd_reserve_open.c (10107B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2022 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 3, or 8 (at your 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, see 17 <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file testing/testing_api_cmd_reserve_open.c 21 * @brief Implement the /reserve/$RID/open test command. 22 * @author Christian Grothoff 23 */ 24 #include "taler/taler_json_lib.h" 25 #include <gnunet/gnunet_curl_lib.h> 26 struct OpenState; 27 #define TALER_EXCHANGE_POST_RESERVES_OPEN_RESULT_CLOSURE struct OpenState 28 #include "taler/exchange/post-reserves-RESERVE_PUB-open.h" 29 #include "taler/taler_testing_lib.h" 30 31 32 /** 33 * Information we track per coin used to pay for opening the 34 * reserve. 35 */ 36 struct CoinDetail 37 { 38 /** 39 * Name of the command and index of the coin to use. 40 */ 41 const char *name; 42 43 /** 44 * Amount to charge to this coin. 45 */ 46 struct TALER_Amount amount; 47 }; 48 49 50 /** 51 * State for a "open" CMD. 52 */ 53 struct OpenState 54 { 55 /** 56 * Label to the command which created the reserve to check, 57 * needed to resort the reserve key. 58 */ 59 const char *reserve_reference; 60 61 /** 62 * Requested expiration time. 63 */ 64 struct GNUNET_TIME_Relative req_expiration_time; 65 66 /** 67 * Requested minimum number of purses. 68 */ 69 uint32_t min_purses; 70 71 /** 72 * Amount to pay for the opening from the reserve balance. 73 */ 74 struct TALER_Amount reserve_pay; 75 76 /** 77 * Handle to the "reserve open" operation. 78 */ 79 struct TALER_EXCHANGE_PostReservesOpenHandle *rsh; 80 81 /** 82 * Expected reserve balance. 83 */ 84 const char *expected_balance; 85 86 /** 87 * Length of the @e cd array. 88 */ 89 unsigned int cpl; 90 91 /** 92 * Coin details, array of length @e cpl. 93 */ 94 struct CoinDetail *cd; 95 96 /** 97 * Private key of the reserve being analyzed. 98 */ 99 const struct TALER_ReservePrivateKeyP *reserve_priv; 100 101 /** 102 * Public key of the reserve being analyzed. 103 */ 104 struct TALER_ReservePublicKeyP reserve_pub; 105 106 /** 107 * Expected HTTP response code. 108 */ 109 unsigned int expected_response_code; 110 111 /** 112 * Interpreter state. 113 */ 114 struct TALER_TESTING_Interpreter *is; 115 }; 116 117 118 /** 119 * Check that the reserve balance and HTTP response code are 120 * both acceptable. 121 * 122 * @param ss closure. 123 * @param rs HTTP response details 124 */ 125 static void 126 reserve_open_cb (struct OpenState *ss, 127 const struct TALER_EXCHANGE_PostReservesOpenResponse *rs) 128 { 129 struct TALER_TESTING_Interpreter *is = ss->is; 130 131 ss->rsh = NULL; 132 if (ss->expected_response_code != rs->hr.http_status) 133 { 134 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 135 "Unexpected HTTP response code: %d in %s:%u\n", 136 rs->hr.http_status, 137 __FILE__, 138 __LINE__); 139 json_dumpf (rs->hr.reply, 140 stderr, 141 JSON_INDENT (2)); 142 TALER_TESTING_interpreter_fail (ss->is); 143 return; 144 } 145 if (MHD_HTTP_OK != rs->hr.http_status) 146 { 147 TALER_TESTING_interpreter_next (is); 148 return; 149 } 150 TALER_TESTING_interpreter_next (is); 151 } 152 153 154 /** 155 * Run the command. 156 * 157 * @param cls closure. 158 * @param cmd the command being executed. 159 * @param is the interpreter state. 160 */ 161 static void 162 open_run (void *cls, 163 const struct TALER_TESTING_Command *cmd, 164 struct TALER_TESTING_Interpreter *is) 165 { 166 struct OpenState *ss = cls; 167 const struct TALER_TESTING_Command *create_reserve; 168 struct TALER_EXCHANGE_PurseDeposit cp[GNUNET_NZL (ss->cpl)]; 169 170 ss->is = is; 171 create_reserve 172 = TALER_TESTING_interpreter_lookup_command (is, 173 ss->reserve_reference); 174 175 if (NULL == create_reserve) 176 { 177 GNUNET_break (0); 178 TALER_TESTING_interpreter_fail (is); 179 return; 180 } 181 if (GNUNET_OK != 182 TALER_TESTING_get_trait_reserve_priv (create_reserve, 183 &ss->reserve_priv)) 184 { 185 GNUNET_break (0); 186 TALER_LOG_ERROR ("Failed to find reserve_priv for open query\n"); 187 TALER_TESTING_interpreter_fail (is); 188 return; 189 } 190 GNUNET_CRYPTO_eddsa_key_get_public (&ss->reserve_priv->eddsa_priv, 191 &ss->reserve_pub.eddsa_pub); 192 for (unsigned int i = 0; i<ss->cpl; i++) 193 { 194 struct TALER_EXCHANGE_PurseDeposit *cpi = &cp[i]; 195 const struct TALER_TESTING_Command *cmdi; 196 const struct TALER_AgeCommitmentProof *age_commitment_proof; 197 const struct TALER_CoinSpendPrivateKeyP *coin_priv; 198 const struct TALER_DenominationSignature *denom_sig; 199 const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; 200 char *cref; 201 unsigned int cidx; 202 203 if (GNUNET_OK != 204 TALER_TESTING_parse_coin_reference (ss->cd[i].name, 205 &cref, 206 &cidx)) 207 { 208 GNUNET_break (0); 209 TALER_LOG_ERROR ("Failed to parse coin reference `%s'\n", 210 ss->cd[i].name); 211 TALER_TESTING_interpreter_fail (is); 212 return; 213 } 214 cmdi = TALER_TESTING_interpreter_lookup_command (is, 215 cref); 216 GNUNET_free (cref); 217 if (NULL == cmdi) 218 { 219 GNUNET_break (0); 220 TALER_LOG_ERROR ("Command `%s' not found\n", 221 ss->cd[i].name); 222 TALER_TESTING_interpreter_fail (is); 223 return; 224 } 225 if ( (GNUNET_OK != 226 TALER_TESTING_get_trait_age_commitment_proof (cmdi, 227 cidx, 228 &age_commitment_proof)) 229 || 230 (GNUNET_OK != 231 TALER_TESTING_get_trait_coin_priv (cmdi, 232 cidx, 233 &coin_priv)) || 234 (GNUNET_OK != 235 TALER_TESTING_get_trait_denom_sig (cmdi, 236 cidx, 237 &denom_sig)) || 238 (GNUNET_OK != 239 TALER_TESTING_get_trait_denom_pub (cmdi, 240 cidx, 241 &denom_pub)) ) 242 { 243 GNUNET_break (0); 244 TALER_LOG_ERROR ("Coin trait not found in `%s'\n", 245 ss->cd[i].name); 246 TALER_TESTING_interpreter_fail (is); 247 return; 248 } 249 cpi->age_commitment_proof = age_commitment_proof; 250 cpi->coin_priv = *coin_priv; 251 cpi->denom_sig = *denom_sig; 252 cpi->amount = ss->cd[i].amount; 253 cpi->h_denom_pub = denom_pub->h_key; 254 } 255 ss->rsh = TALER_EXCHANGE_post_reserves_open_create ( 256 TALER_TESTING_interpreter_get_context (is), 257 TALER_TESTING_get_exchange_url (is), 258 TALER_TESTING_get_keys (is), 259 ss->reserve_priv, 260 &ss->reserve_pay, 261 ss->cpl, 262 cp, 263 GNUNET_TIME_relative_to_timestamp (ss->req_expiration_time), 264 ss->min_purses); 265 if (NULL == ss->rsh) 266 { 267 GNUNET_break (0); 268 TALER_TESTING_interpreter_fail (is); 269 return; 270 } 271 { 272 enum TALER_ErrorCode ec; 273 274 ec = TALER_EXCHANGE_post_reserves_open_start (ss->rsh, 275 &reserve_open_cb, 276 ss); 277 if (TALER_EC_NONE != ec) 278 { 279 GNUNET_break (0); 280 ss->rsh = NULL; 281 TALER_TESTING_interpreter_fail (is); 282 return; 283 } 284 } 285 } 286 287 288 /** 289 * Cleanup the state from a "reserve open" CMD, and possibly 290 * cancel a pending operation thereof. 291 * 292 * @param cls closure. 293 * @param cmd the command which is being cleaned up. 294 */ 295 static void 296 open_cleanup (void *cls, 297 const struct TALER_TESTING_Command *cmd) 298 { 299 struct OpenState *ss = cls; 300 301 if (NULL != ss->rsh) 302 { 303 TALER_TESTING_command_incomplete (ss->is, 304 cmd->label); 305 TALER_EXCHANGE_post_reserves_open_cancel (ss->rsh); 306 ss->rsh = NULL; 307 } 308 GNUNET_free (ss->cd); 309 GNUNET_free (ss); 310 } 311 312 313 struct TALER_TESTING_Command 314 TALER_TESTING_cmd_reserve_open (const char *label, 315 const char *reserve_reference, 316 const char *reserve_pay, 317 struct GNUNET_TIME_Relative expiration_time, 318 uint32_t min_purses, 319 unsigned int expected_response_code, 320 ...) 321 { 322 struct OpenState *ss; 323 va_list ap; 324 const char *name; 325 unsigned int i; 326 327 GNUNET_assert (NULL != reserve_reference); 328 ss = GNUNET_new (struct OpenState); 329 ss->reserve_reference = reserve_reference; 330 ss->req_expiration_time = expiration_time; 331 ss->min_purses = min_purses; 332 GNUNET_assert (GNUNET_OK == 333 TALER_string_to_amount (reserve_pay, 334 &ss->reserve_pay)); 335 ss->expected_response_code = expected_response_code; 336 va_start (ap, 337 expected_response_code); 338 while (NULL != (name = va_arg (ap, const char *))) 339 ss->cpl++; 340 va_end (ap); 341 GNUNET_assert (0 == (ss->cpl % 2)); 342 ss->cpl /= 2; /* name and amount per coin */ 343 ss->cd = GNUNET_new_array (ss->cpl, 344 struct CoinDetail); 345 i = 0; 346 va_start (ap, 347 expected_response_code); 348 while (NULL != (name = va_arg (ap, const char *))) 349 { 350 struct CoinDetail *cd = &ss->cd[i]; 351 cd->name = name; 352 GNUNET_assert (GNUNET_OK == 353 TALER_string_to_amount (va_arg (ap, 354 const char *), 355 &cd->amount)); 356 i++; 357 } 358 va_end (ap); 359 { 360 struct TALER_TESTING_Command cmd = { 361 .cls = ss, 362 .label = label, 363 .run = &open_run, 364 .cleanup = &open_cleanup 365 }; 366 367 return cmd; 368 } 369 }