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