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