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