contract_serialize.c (16452B)
1 /* 2 This file is part of GNU Taler 3 Copyright (C) 2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file util/contract_serialize.c 18 * @brief shared logic for contract terms serialization 19 * @author Iván Ávalos 20 */ 21 22 #include "platform.h" 23 #include <gnunet/gnunet_json_lib.h> 24 #include <gnunet/gnunet_common.h> 25 #include <taler/taler_json_lib.h> 26 #include <jansson.h> 27 #include "taler/taler_util.h" 28 #include "taler_merchant_util.h" 29 30 /** 31 * Get JSON representation of merchant details. 32 * 33 * @param[in] contract contract terms 34 * @return JSON object with merchant details; NULL on error 35 */ 36 static json_t * 37 json_from_merchant_details ( 38 const struct TALER_MERCHANT_Contract *contract) 39 { 40 return GNUNET_JSON_PACK ( 41 GNUNET_JSON_pack_string ("name", 42 contract->merchant.name), 43 GNUNET_JSON_pack_allow_null ( 44 GNUNET_JSON_pack_string ("email", 45 contract->merchant.email)), 46 GNUNET_JSON_pack_allow_null ( 47 GNUNET_JSON_pack_string ("website", 48 contract->merchant.website)), 49 GNUNET_JSON_pack_allow_null ( 50 GNUNET_JSON_pack_string ("logo", 51 contract->merchant.logo)), 52 GNUNET_JSON_pack_allow_null ( 53 GNUNET_JSON_pack_object_steal ("address", 54 contract->merchant.address)), 55 GNUNET_JSON_pack_allow_null ( 56 GNUNET_JSON_pack_object_steal ("jurisdiction", 57 contract->merchant.jurisdiction))); 58 } 59 60 61 /** 62 * Get JSON representation of contract choice input. 63 * 64 * @param[in] input contract terms choice input 65 * @return JSON representation of @a input; NULL on error 66 */ 67 static json_t * 68 json_from_contract_input ( 69 const struct TALER_MERCHANT_ContractInput *input) 70 { 71 switch (input->type) 72 { 73 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID: 74 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 75 "invalid contract input type"); 76 GNUNET_assert (0); 77 return NULL; 78 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: 79 return GNUNET_JSON_PACK ( 80 GNUNET_JSON_pack_string ("type", 81 "token"), 82 GNUNET_JSON_pack_string ("token_family_slug", 83 input->details.token.token_family_slug), 84 GNUNET_JSON_pack_int64 ("count", 85 input->details.token.count)); 86 } 87 88 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 89 "unsupported contract input type %d", 90 input->type); 91 GNUNET_assert (0); 92 return NULL; 93 } 94 95 96 /** 97 * Get JSON representation of contract choice output. 98 * 99 * @param[in] output contract terms choice output 100 * @return JSON representation of @a output; NULL on error 101 */ 102 static json_t * 103 json_from_contract_output ( 104 const struct TALER_MERCHANT_ContractOutput *output) 105 { 106 switch (output->type) 107 { 108 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: 109 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 110 "invalid contract output type"); 111 GNUNET_assert (0); 112 return NULL; 113 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: 114 return GNUNET_JSON_PACK ( 115 GNUNET_JSON_pack_string ("type", 116 "token"), 117 GNUNET_JSON_pack_string ("token_family_slug", 118 output->details.token.token_family_slug), 119 GNUNET_JSON_pack_uint64 ("count", 120 output->details.token.count), 121 GNUNET_JSON_pack_uint64 ("key_index", 122 output->details.token.key_index)); 123 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: 124 { 125 json_t *donau_urls; 126 127 donau_urls = json_array (); 128 GNUNET_assert (NULL != donau_urls); 129 for (unsigned i = 0; 130 i < output->details.donation_receipt.donau_urls_len; 131 i++) 132 GNUNET_assert (0 == 133 json_array_append_new ( 134 donau_urls, 135 json_string ( 136 output->details.donation_receipt.donau_urls[i]))); 137 138 return GNUNET_JSON_PACK ( 139 GNUNET_JSON_pack_string ("type", 140 "tax-receipt"), 141 GNUNET_JSON_pack_array_steal ("donau_urls", 142 donau_urls), 143 GNUNET_JSON_pack_allow_null ( 144 TALER_JSON_pack_amount ("amount", 145 &output->details.donation_receipt.amount))); 146 } 147 } 148 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 149 "Unsupported contract output type %d", 150 output->type); 151 GNUNET_assert (0); 152 return NULL; 153 } 154 155 156 json_t * 157 TALER_MERCHANT_json_from_contract_choice ( 158 const struct TALER_MERCHANT_ContractChoice *choice, 159 bool order) 160 { 161 json_t *inputs; 162 json_t *outputs; 163 164 inputs = json_array (); 165 GNUNET_assert (NULL != inputs); 166 for (unsigned int i = 0; i < choice->inputs_len; i++) 167 GNUNET_assert (0 == 168 json_array_append_new (inputs, 169 json_from_contract_input ( 170 &choice->inputs[i]))); 171 outputs = json_array (); 172 GNUNET_assert (NULL != outputs); 173 for (unsigned int i = 0; i < choice->outputs_len; i++) 174 GNUNET_assert (0 == 175 json_array_append_new (outputs, 176 json_from_contract_output ( 177 &choice->outputs[i]))); 178 179 return GNUNET_JSON_PACK ( 180 TALER_JSON_pack_amount ("amount", 181 &choice->amount), 182 GNUNET_JSON_pack_allow_null ( 183 GNUNET_JSON_pack_string ("description", 184 choice->description)), 185 GNUNET_JSON_pack_allow_null ( 186 GNUNET_JSON_pack_object_incref ("description_i18n", 187 choice->description_i18n)), 188 (order) 189 ? GNUNET_JSON_pack_allow_null ( 190 TALER_JSON_pack_amount ( 191 "max_fee", 192 /* workaround for nullable amount */ 193 (GNUNET_OK == 194 TALER_amount_is_valid (&choice->max_fee)) 195 ? &choice->max_fee 196 : NULL)) 197 : TALER_JSON_pack_amount ("max_fee", 198 &choice->max_fee), 199 (order) 200 ? GNUNET_JSON_pack_allow_null ( 201 GNUNET_JSON_pack_array_steal ("inputs", 202 inputs)) 203 : GNUNET_JSON_pack_array_steal ("inputs", 204 inputs), 205 (order) 206 ? GNUNET_JSON_pack_allow_null ( 207 GNUNET_JSON_pack_array_steal ("outputs", 208 outputs)) 209 : GNUNET_JSON_pack_array_steal ("outputs", 210 outputs)); 211 } 212 213 214 /** 215 * Get JSON representation of contract token family key. 216 * 217 * @param[in] key contract token family key 218 * @return JSON representation of @a key; NULL on error 219 */ 220 static json_t * 221 json_from_token_family_key ( 222 const struct TALER_MERCHANT_ContractTokenFamilyKey *key) 223 { 224 return GNUNET_JSON_PACK ( 225 GNUNET_JSON_pack_timestamp ("signature_validity_start", 226 key->valid_after), 227 GNUNET_JSON_pack_timestamp ("signature_validity_end", 228 key->valid_before), 229 TALER_JSON_pack_token_pub (NULL, 230 &key->pub)); 231 } 232 233 234 /** 235 * Get JSON representation of contract token family details. 236 * 237 * @param[in] family contract token family 238 * @return JSON representation of @a family->details; NULL on error 239 */ 240 static json_t * 241 json_from_token_family_details ( 242 const struct TALER_MERCHANT_ContractTokenFamily *family) 243 { 244 switch (family->kind) 245 { 246 case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID: 247 break; 248 case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION: 249 { 250 json_t *trusted_domains; 251 252 trusted_domains = json_array (); 253 GNUNET_assert (NULL != trusted_domains); 254 for (unsigned int i = 0; 255 i < family->details.subscription.trusted_domains_len; 256 i++) 257 GNUNET_assert (0 == 258 json_array_append_new ( 259 trusted_domains, 260 json_string ( 261 family->details.subscription.trusted_domains[i]))); 262 263 return GNUNET_JSON_PACK ( 264 GNUNET_JSON_pack_string ("class", 265 "subscription"), 266 GNUNET_JSON_pack_array_steal ("trusted_domains", 267 trusted_domains)); 268 } 269 case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT: 270 { 271 json_t *expected_domains; 272 273 expected_domains = json_array (); 274 GNUNET_assert (NULL != expected_domains); 275 for (unsigned int i = 0; 276 i < family->details.discount.expected_domains_len; 277 i++) 278 GNUNET_assert (0 == 279 json_array_append_new ( 280 expected_domains, 281 json_string ( 282 family->details.discount.expected_domains[i]))); 283 284 return GNUNET_JSON_PACK ( 285 GNUNET_JSON_pack_string ("class", 286 "discount"), 287 GNUNET_JSON_pack_array_steal ("expected_domains", 288 expected_domains)); 289 } 290 } 291 292 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 293 "unsupported token family kind %d", 294 family->kind); 295 GNUNET_assert (0); 296 return NULL; 297 } 298 299 300 json_t * 301 TALER_MERCHANT_json_from_token_family ( 302 const struct TALER_MERCHANT_ContractTokenFamily *family) 303 { 304 json_t *keys; 305 306 keys = json_array (); 307 GNUNET_assert (NULL != keys); 308 for (unsigned int i = 0; i < family->keys_len; i++) 309 GNUNET_assert (0 == json_array_append_new ( 310 keys, 311 json_from_token_family_key ( 312 &family->keys[i]))); 313 314 return GNUNET_JSON_PACK ( 315 GNUNET_JSON_pack_string ("name", 316 family->name), 317 GNUNET_JSON_pack_string ("description", 318 family->description), 319 GNUNET_JSON_pack_object_incref ("description_i18n", 320 family->description_i18n), 321 GNUNET_JSON_pack_array_steal ("keys", 322 keys), 323 GNUNET_JSON_pack_object_steal ("details", 324 json_from_token_family_details (family)), 325 GNUNET_JSON_pack_bool ("critical", 326 family->critical)); 327 } 328 329 330 /** 331 * Get JSON object with contract terms v0-specific fields. 332 * 333 * @param[in] input contract terms v0 334 * @return JSON object with @a input v0 fields; NULL on error 335 */ 336 static json_t * 337 json_from_contract_v0 ( 338 const struct TALER_MERCHANT_Contract *input) 339 { 340 return GNUNET_JSON_PACK ( 341 TALER_JSON_pack_amount ("amount", 342 &input->details.v0.brutto), 343 TALER_JSON_pack_amount ("max_fee", 344 &input->details.v0.max_fee)); 345 } 346 347 348 /** 349 * Get JSON object with contract terms v1-specific fields. 350 * 351 * @param[in] input contract terms v1 352 * @return JSON object with @a input v1 fields; NULL on error 353 */ 354 static json_t * 355 json_from_contract_v1 ( 356 const struct TALER_MERCHANT_Contract *input) 357 { 358 json_t *choices; 359 json_t *families; 360 361 choices = json_array (); 362 GNUNET_assert (0 != choices); 363 for (unsigned i = 0; i < input->details.v1.choices_len; i++) 364 GNUNET_assert (0 == json_array_append_new ( 365 choices, 366 TALER_MERCHANT_json_from_contract_choice ( 367 &input->details.v1.choices[i], 368 false))); 369 370 families = json_object (); 371 GNUNET_assert (0 != families); 372 for (unsigned i = 0; i < input->details.v1.token_authorities_len; i++) 373 GNUNET_assert (0 == json_object_set_new ( 374 families, 375 input->details.v1.token_authorities[i].slug, 376 TALER_MERCHANT_json_from_token_family ( 377 &input->details.v1.token_authorities[i]))); 378 379 return GNUNET_JSON_PACK ( 380 GNUNET_JSON_pack_array_steal ("choices", 381 choices), 382 GNUNET_JSON_pack_object_steal ("token_families", 383 families)); 384 } 385 386 387 json_t * 388 TALER_MERCHANT_contract_serialize ( 389 const struct TALER_MERCHANT_Contract *input, 390 bool nonce_optional) 391 { 392 json_t *details; 393 394 switch (input->version) 395 { 396 case TALER_MERCHANT_CONTRACT_VERSION_0: 397 details = json_from_contract_v0 (input); 398 goto success; 399 case TALER_MERCHANT_CONTRACT_VERSION_1: 400 details = json_from_contract_v1 (input); 401 goto success; 402 } 403 404 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 405 "unknown contract type version %d", 406 input->version); 407 GNUNET_assert (0); 408 return NULL; 409 410 success: 411 return GNUNET_JSON_PACK ( 412 GNUNET_JSON_pack_uint64 ("version", 413 input->version), 414 GNUNET_JSON_pack_string ("summary", 415 input->summary), 416 GNUNET_JSON_pack_allow_null ( 417 GNUNET_JSON_pack_object_steal ("summary_i18n", 418 input->summary_i18n)), 419 GNUNET_JSON_pack_string ("order_id", 420 input->order_id), 421 GNUNET_JSON_pack_allow_null ( 422 GNUNET_JSON_pack_string ("public_reorder_url", 423 input->public_reorder_url)), 424 GNUNET_JSON_pack_allow_null ( 425 GNUNET_JSON_pack_string ("fulfillment_url", 426 input->fulfillment_url)), 427 GNUNET_JSON_pack_allow_null ( 428 GNUNET_JSON_pack_string ("fulfillment_message", 429 input->fulfillment_message)), 430 GNUNET_JSON_pack_allow_null ( 431 GNUNET_JSON_pack_object_steal ("fulfillment_message_i18n", 432 input->fulfillment_message_i18n)), 433 GNUNET_JSON_pack_array_steal ("products", 434 input->products), 435 GNUNET_JSON_pack_timestamp ("timestamp", 436 input->timestamp), 437 GNUNET_JSON_pack_timestamp ("refund_deadline", 438 input->refund_deadline), 439 GNUNET_JSON_pack_timestamp ("pay_deadline", 440 input->pay_deadline), 441 GNUNET_JSON_pack_timestamp ("wire_transfer_deadline", 442 input->wire_deadline), 443 GNUNET_JSON_pack_data_auto ("merchant_pub", 444 &input->merchant_pub.eddsa_pub), 445 GNUNET_JSON_pack_string ("merchant_base_url", 446 input->merchant_base_url), 447 GNUNET_JSON_pack_object_steal ("merchant", 448 json_from_merchant_details (input)), 449 GNUNET_JSON_pack_data_auto ("h_wire", 450 &input->h_wire), 451 GNUNET_JSON_pack_string ("wire_method", 452 input->wire_method), 453 GNUNET_JSON_pack_array_steal ("exchanges", 454 input->exchanges), 455 GNUNET_JSON_pack_allow_null ( 456 GNUNET_JSON_pack_object_steal ("delivery_location", 457 input->delivery_location)), 458 GNUNET_JSON_pack_allow_null ( 459 GNUNET_JSON_pack_timestamp ("delivery_date", 460 input->delivery_date)), 461 (nonce_optional) 462 ? GNUNET_JSON_pack_allow_null ( 463 GNUNET_JSON_pack_string ("nonce", 464 input->nonce)) 465 : GNUNET_JSON_pack_string ("nonce", 466 input->nonce), 467 GNUNET_JSON_pack_allow_null ( 468 GNUNET_JSON_pack_time_rel ("auto_refund", 469 input->auto_refund)), 470 GNUNET_JSON_pack_allow_null ( 471 GNUNET_JSON_pack_object_steal ("extra", 472 input->extra)), 473 GNUNET_JSON_pack_allow_null ( 474 GNUNET_JSON_pack_uint64 ("minimum_age", 475 input->minimum_age)), 476 GNUNET_JSON_pack_object_steal (NULL, 477 details)); 478 }