testing_api_cmd_contract_get.c (8231B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2022 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 by 7 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 GNU 13 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_contract_get.c 21 * @brief command for testing GET /contracts/$CPUB 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 #include "taler/taler_signatures.h" 29 #include "taler/backoff.h" 30 31 32 /** 33 * State for a "contract get" CMD. 34 */ 35 struct ContractGetState 36 { 37 38 /** 39 * JSON string describing the resulting contract. 40 */ 41 json_t *contract_terms; 42 43 /** 44 * Private key to decrypt the contract. 45 */ 46 struct TALER_ContractDiffiePrivateP contract_priv; 47 48 /** 49 * Set to the returned merge key. 50 */ 51 struct TALER_PurseMergePrivateKeyP merge_priv; 52 53 /** 54 * Public key of the purse. 55 */ 56 struct TALER_PurseContractPublicKeyP purse_pub; 57 58 /** 59 * Reference to the command that uploaded the contract. 60 */ 61 const char *contract_ref; 62 63 /** 64 * ContractGet handle while operation is running. 65 */ 66 struct TALER_EXCHANGE_GetContractsHandle *dh; 67 68 /** 69 * Interpreter state. 70 */ 71 struct TALER_TESTING_Interpreter *is; 72 73 /** 74 * Expected HTTP response code. 75 */ 76 unsigned int expected_response_code; 77 78 /** 79 * True if this is for a 'merge' operation, 80 * 'false' if this is for a 'deposit' operation. 81 */ 82 bool merge; 83 84 }; 85 86 87 /** 88 * Callback to analyze the /contracts/$CPUB response, just used to check if 89 * the response code is acceptable. 90 * 91 * @param cls closure. 92 * @param dr get response details 93 */ 94 static void 95 get_cb (void *cls, 96 const struct TALER_EXCHANGE_GetContractsResponse *dr) 97 { 98 struct ContractGetState *ds = cls; 99 const struct TALER_TESTING_Command *ref; 100 101 ds->dh = NULL; 102 if (ds->expected_response_code != dr->hr.http_status) 103 { 104 TALER_TESTING_unexpected_status (ds->is, 105 dr->hr.http_status, 106 ds->expected_response_code); 107 return; 108 } 109 ref = TALER_TESTING_interpreter_lookup_command (ds->is, 110 ds->contract_ref); 111 GNUNET_assert (NULL != ref); 112 if (MHD_HTTP_OK == dr->hr.http_status) 113 { 114 const struct TALER_PurseMergePrivateKeyP *mp; 115 const json_t *ct; 116 117 ds->purse_pub = dr->details.ok.purse_pub; 118 if (ds->merge) 119 { 120 if (GNUNET_OK != 121 TALER_TESTING_get_trait_merge_priv (ref, 122 &mp)) 123 { 124 GNUNET_break (0); 125 TALER_TESTING_interpreter_fail (ds->is); 126 return; 127 } 128 ds->contract_terms = 129 TALER_CRYPTO_contract_decrypt_for_merge ( 130 &ds->contract_priv, 131 &ds->purse_pub, 132 dr->details.ok.econtract, 133 dr->details.ok.econtract_size, 134 &ds->merge_priv); 135 if (0 != 136 GNUNET_memcmp (mp, 137 &ds->merge_priv)) 138 { 139 GNUNET_break (0); 140 TALER_TESTING_interpreter_fail (ds->is); 141 return; 142 } 143 } 144 else 145 { 146 ds->contract_terms = 147 TALER_CRYPTO_contract_decrypt_for_deposit ( 148 &ds->contract_priv, 149 dr->details.ok.econtract, 150 dr->details.ok.econtract_size); 151 } 152 if (NULL == ds->contract_terms) 153 { 154 GNUNET_break (0); 155 TALER_TESTING_interpreter_fail (ds->is); 156 return; 157 } 158 if (GNUNET_OK != 159 TALER_TESTING_get_trait_contract_terms (ref, 160 &ct)) 161 { 162 GNUNET_break (0); 163 TALER_TESTING_interpreter_fail (ds->is); 164 return; 165 } 166 if (1 != /* 1: equal, 0: not equal */ 167 json_equal (ct, 168 ds->contract_terms)) 169 { 170 GNUNET_break (0); 171 TALER_TESTING_interpreter_fail (ds->is); 172 return; 173 } 174 } 175 TALER_TESTING_interpreter_next (ds->is); 176 } 177 178 179 /** 180 * Run the command. 181 * 182 * @param cls closure. 183 * @param cmd the command to execute. 184 * @param is the interpreter state. 185 */ 186 static void 187 get_run (void *cls, 188 const struct TALER_TESTING_Command *cmd, 189 struct TALER_TESTING_Interpreter *is) 190 { 191 struct ContractGetState *ds = cls; 192 const struct TALER_ContractDiffiePrivateP *contract_priv; 193 const struct TALER_TESTING_Command *ref; 194 const char *exchange_url; 195 196 (void) cmd; 197 ds->is = is; 198 exchange_url = TALER_TESTING_get_exchange_url (is); 199 if (NULL == exchange_url) 200 { 201 GNUNET_break (0); 202 return; 203 } 204 ref = TALER_TESTING_interpreter_lookup_command (ds->is, 205 ds->contract_ref); 206 GNUNET_assert (NULL != ref); 207 if (GNUNET_OK != 208 TALER_TESTING_get_trait_contract_priv (ref, 209 &contract_priv)) 210 { 211 GNUNET_break (0); 212 TALER_TESTING_interpreter_fail (ds->is); 213 return; 214 } 215 ds->contract_priv = *contract_priv; 216 ds->dh = TALER_EXCHANGE_get_contracts_create ( 217 TALER_TESTING_interpreter_get_context (is), 218 exchange_url, 219 contract_priv); 220 if (NULL == ds->dh) 221 { 222 GNUNET_break (0); 223 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 224 "Could not GET contract\n"); 225 TALER_TESTING_interpreter_fail (is); 226 return; 227 } 228 if (TALER_EC_NONE != 229 TALER_EXCHANGE_get_contracts_start (ds->dh, 230 &get_cb, 231 ds)) 232 { 233 GNUNET_break (0); 234 TALER_EXCHANGE_get_contracts_cancel (ds->dh); 235 ds->dh = NULL; 236 TALER_TESTING_interpreter_fail (is); 237 return; 238 } 239 } 240 241 242 /** 243 * Free the state of a "get" CMD, and possibly cancel a 244 * pending operation thereof. 245 * 246 * @param cls closure, must be a `struct ContractGetState`. 247 * @param cmd the command which is being cleaned up. 248 */ 249 static void 250 get_cleanup (void *cls, 251 const struct TALER_TESTING_Command *cmd) 252 { 253 struct ContractGetState *ds = cls; 254 255 if (NULL != ds->dh) 256 { 257 TALER_TESTING_command_incomplete (ds->is, 258 cmd->label); 259 TALER_EXCHANGE_get_contracts_cancel (ds->dh); 260 ds->dh = NULL; 261 } 262 json_decref (ds->contract_terms); 263 GNUNET_free (ds); 264 } 265 266 267 /** 268 * Offer internal data from a "get" CMD, to other commands. 269 * 270 * @param cls closure. 271 * @param[out] ret result. 272 * @param trait name of the trait. 273 * @param index index number of the object to offer. 274 * @return #GNUNET_OK on success. 275 */ 276 static enum GNUNET_GenericReturnValue 277 get_traits (void *cls, 278 const void **ret, 279 const char *trait, 280 unsigned int index) 281 { 282 struct ContractGetState *ds = cls; 283 struct TALER_TESTING_Trait traits[] = { 284 TALER_TESTING_make_trait_merge_priv (&ds->merge_priv), 285 TALER_TESTING_make_trait_purse_pub (&ds->purse_pub), 286 TALER_TESTING_make_trait_contract_terms (ds->contract_terms), 287 TALER_TESTING_trait_end () 288 }; 289 290 /* skip 'merge_priv' if we are in 'merge' mode */ 291 return TALER_TESTING_get_trait (&traits[ds->merge ? 0 : 1], 292 ret, 293 trait, 294 index); 295 } 296 297 298 struct TALER_TESTING_Command 299 TALER_TESTING_cmd_contract_get ( 300 const char *label, 301 unsigned int expected_http_status, 302 bool for_merge, 303 const char *contract_ref) 304 { 305 struct ContractGetState *ds; 306 307 ds = GNUNET_new (struct ContractGetState); 308 ds->expected_response_code = expected_http_status; 309 ds->contract_ref = contract_ref; 310 ds->merge = for_merge; 311 { 312 struct TALER_TESTING_Command cmd = { 313 .cls = ds, 314 .label = label, 315 .run = &get_run, 316 .cleanup = &get_cleanup, 317 .traits = &get_traits 318 }; 319 320 return cmd; 321 } 322 } 323 324 325 /* end of testing_api_cmd_contract_get.c */