LCOV - code coverage report
Current view: top level - util - contract_serialize.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 76 92 82.6 %
Date: 2025-06-23 16:22:09 Functions: 10 10 100.0 %

          Line data    Source code
       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           2 : json_from_merchant_details (
      38             :   const struct TALER_MERCHANT_Contract *contract)
      39             : {
      40           2 :   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           6 : json_from_contract_input (
      69             :   const struct TALER_MERCHANT_ContractInput *input)
      70             : {
      71           6 :   switch (input->type)
      72             :   {
      73           0 :   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
      74           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      75             :                 "invalid contract input type");
      76           0 :     GNUNET_assert (0);
      77             :     return NULL;
      78           6 :   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
      79           6 :     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           0 :   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      89             :               "unsupported contract input type %d",
      90             :               input->type);
      91           0 :   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          10 : json_from_contract_output (
     104             :   const struct TALER_MERCHANT_ContractOutput *output)
     105             : {
     106          10 :   switch (output->type)
     107             :   {
     108           0 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
     109           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     110             :                 "invalid contract output type");
     111           0 :     GNUNET_assert (0);
     112             :     return NULL;
     113           9 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
     114           9 :     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           1 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
     124             :     {
     125             :       json_t *donau_urls;
     126             : 
     127           1 :       donau_urls = json_array ();
     128           1 :       GNUNET_assert (NULL != donau_urls);
     129             : 
     130           1 :       for (unsigned i = 0;
     131           4 :            i < output->details.donation_receipt.donau_urls_len;
     132           3 :            i++)
     133           3 :         GNUNET_assert (0 ==
     134             :                        json_array_append (donau_urls,
     135             :                                           json_string (output->details.
     136             :                                                        donation_receipt.
     137             :                                                        donau_urls[i])));
     138             : 
     139           1 :       return GNUNET_JSON_PACK (
     140             :         GNUNET_JSON_pack_string ("type",
     141             :                                  "tax-receipt"),
     142             :         GNUNET_JSON_pack_array_steal ("donau_urls",
     143             :                                       donau_urls),
     144             :         GNUNET_JSON_pack_allow_null (
     145             :           TALER_JSON_pack_amount ("amount",
     146             :                                   &output->details.donation_receipt.amount)));
     147             :     }
     148             :   }
     149             : 
     150           0 :   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     151             :               "unsupported contract output type %d",
     152             :               output->type);
     153           0 :   GNUNET_assert (0);
     154             :   return NULL;
     155             : }
     156             : 
     157             : 
     158             : json_t *
     159          11 : TALER_MERCHANT_json_from_contract_choice (
     160             :   const struct TALER_MERCHANT_ContractChoice *choice,
     161             :   bool order)
     162             : {
     163             :   json_t *inputs;
     164             :   json_t *outputs;
     165             : 
     166          11 :   inputs = json_array ();
     167          11 :   GNUNET_assert (NULL != inputs);
     168          17 :   for (unsigned int i = 0; i < choice->inputs_len; i++)
     169           6 :     GNUNET_assert (0 ==
     170             :                    json_array_append_new (inputs,
     171             :                                           json_from_contract_input (
     172             :                                             &choice->inputs[i])));
     173             : 
     174          11 :   outputs = json_array ();
     175          11 :   GNUNET_assert (NULL != outputs);
     176          21 :   for (unsigned int i = 0; i < choice->outputs_len; i++)
     177          10 :     GNUNET_assert (0 ==
     178             :                    json_array_append_new (outputs,
     179             :                                           json_from_contract_output (
     180             :                                             &choice->outputs[i])));
     181             : 
     182          11 :   return GNUNET_JSON_PACK (
     183             :     TALER_JSON_pack_amount ("amount",
     184             :                             &choice->amount),
     185             :     (order)
     186             :     ? GNUNET_JSON_pack_allow_null (
     187             :       TALER_JSON_pack_amount ("max_fee",
     188             :                               /* workaround for nullable amount */
     189             :                               (GNUNET_OK ==
     190             :                                TALER_amount_is_valid (&choice->max_fee))
     191             :                               ? &choice->max_fee
     192             :                               : NULL))
     193             :     : TALER_JSON_pack_amount ("max_fee",
     194             :                               &choice->max_fee),
     195             :     (order)
     196             :     ? GNUNET_JSON_pack_allow_null (
     197             :       GNUNET_JSON_pack_array_steal ("inputs",
     198             :                                     inputs))
     199             :     : GNUNET_JSON_pack_array_steal ("inputs",
     200             :                                     inputs),
     201             :     (order)
     202             :     ? GNUNET_JSON_pack_allow_null (
     203             :       GNUNET_JSON_pack_array_steal ("outputs",
     204             :                                     outputs))
     205             :     :    GNUNET_JSON_pack_array_steal ("outputs",
     206             :                                        outputs));
     207             : }
     208             : 
     209             : 
     210             : /**
     211             :  * Get JSON representation of contract token family key.
     212             :  *
     213             :  * @param[in] key contract token family key
     214             :  * @return JSON representation of @a key; NULL on error
     215             :  */
     216             : static json_t *
     217          10 : json_from_token_family_key (
     218             :   const struct TALER_MERCHANT_ContractTokenFamilyKey *key)
     219             : {
     220          10 :   return GNUNET_JSON_PACK (
     221             :     GNUNET_JSON_pack_timestamp ("signature_validity_start",
     222             :                                 key->valid_after),
     223             :     GNUNET_JSON_pack_timestamp ("signature_validity_end",
     224             :                                 key->valid_before),
     225             :     TALER_JSON_pack_token_pub (NULL,
     226             :                                &key->pub));
     227             : }
     228             : 
     229             : 
     230             : /**
     231             :  * Get JSON representation of contract token family details.
     232             :  *
     233             :  * @param[in] family contract token family
     234             :  * @return JSON representation of @a family->details; NULL on error
     235             :  */
     236             : static json_t *
     237          11 : json_from_token_family_details (
     238             :   const struct TALER_MERCHANT_ContractTokenFamily *family)
     239             : {
     240          11 :   switch (family->kind)
     241             :   {
     242           0 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
     243           0 :     break;
     244           9 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
     245             :     {
     246             :       json_t *trusted_domains;
     247             : 
     248           9 :       trusted_domains = json_array ();
     249           9 :       GNUNET_assert (NULL != trusted_domains);
     250           9 :       for (unsigned int i = 0;
     251          12 :            i < family->details.subscription.trusted_domains_len;
     252           3 :            i++)
     253           3 :         GNUNET_assert (0 ==
     254             :                        json_array_append_new (
     255             :                          trusted_domains,
     256             :                          json_string (
     257             :                            family->details.subscription.trusted_domains[i])));
     258             : 
     259           9 :       return GNUNET_JSON_PACK (
     260             :         GNUNET_JSON_pack_string ("class",
     261             :                                  "subscription"),
     262             :         GNUNET_JSON_pack_array_steal ("trusted_domains",
     263             :                                       trusted_domains));
     264             :     }
     265           2 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
     266             :     {
     267             :       json_t *expected_domains;
     268             : 
     269           2 :       expected_domains = json_array ();
     270           2 :       GNUNET_assert (NULL != expected_domains);
     271           2 :       for (unsigned int i = 0;
     272           5 :            i < family->details.discount.expected_domains_len;
     273           3 :            i++)
     274           3 :         GNUNET_assert (0 ==
     275             :                        json_array_append_new (
     276             :                          expected_domains,
     277             :                          json_string (
     278             :                            family->details.discount.expected_domains[i])));
     279             : 
     280           2 :       return GNUNET_JSON_PACK (
     281             :         GNUNET_JSON_pack_string ("class",
     282             :                                  "discount"),
     283             :         GNUNET_JSON_pack_array_steal ("expected_domains",
     284             :                                       expected_domains));
     285             :     }
     286             :   }
     287             : 
     288           0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     289             :               "unsupported token family kind %d",
     290             :               family->kind);
     291           0 :   GNUNET_assert (0);
     292             :   return NULL;
     293             : }
     294             : 
     295             : 
     296             : json_t *
     297          11 : TALER_MERCHANT_json_from_token_family (
     298             :   const struct TALER_MERCHANT_ContractTokenFamily *family)
     299             : {
     300             :   json_t *keys;
     301             : 
     302          11 :   keys = json_array ();
     303          11 :   GNUNET_assert (NULL != keys);
     304          21 :   for (unsigned int i = 0; i < family->keys_len; i++)
     305          10 :     GNUNET_assert (0 == json_array_append_new (
     306             :                      keys,
     307             :                      json_from_token_family_key (
     308             :                        &family->keys[i])));
     309             : 
     310          11 :   return GNUNET_JSON_PACK (
     311             :     GNUNET_JSON_pack_string ("name",
     312             :                              family->name),
     313             :     GNUNET_JSON_pack_string ("description",
     314             :                              family->description),
     315             :     GNUNET_JSON_pack_object_steal ("description_i18n",
     316             :                                    family->description_i18n),
     317             :     GNUNET_JSON_pack_array_steal ("keys",
     318             :                                   keys),
     319             :     GNUNET_JSON_pack_object_steal ("details",
     320             :                                    json_from_token_family_details (family)),
     321             :     GNUNET_JSON_pack_bool ("critical",
     322             :                            family->critical));
     323             : }
     324             : 
     325             : 
     326             : /**
     327             :  * Get JSON object with contract terms v0-specific fields.
     328             :  *
     329             :  * @param[in] input contract terms v0
     330             :  * @return JSON object with @a input v0 fields; NULL on error
     331             :  */
     332             : static json_t *
     333           1 : json_from_contract_v0 (
     334             :   const struct TALER_MERCHANT_Contract *input)
     335             : {
     336           1 :   return GNUNET_JSON_PACK (
     337             :     TALER_JSON_pack_amount ("amount",
     338             :                             &input->details.v0.brutto),
     339             :     TALER_JSON_pack_amount ("max_fee",
     340             :                             &input->details.v0.max_fee));
     341             : }
     342             : 
     343             : 
     344             : /**
     345             :  * Get JSON object with contract terms v1-specific fields.
     346             :  *
     347             :  * @param[in] input contract terms v1
     348             :  * @return JSON object with @a input v1 fields; NULL on error
     349             :  */
     350             : static json_t *
     351           1 : json_from_contract_v1 (
     352             :   const struct TALER_MERCHANT_Contract *input)
     353             : {
     354             :   json_t *choices;
     355             :   json_t *families;
     356             : 
     357           1 :   choices = json_array ();
     358           1 :   GNUNET_assert (0 != choices);
     359           2 :   for (unsigned i = 0; i < input->details.v1.choices_len; i++)
     360           1 :     GNUNET_assert (0 == json_array_append_new (
     361             :                      choices,
     362             :                      TALER_MERCHANT_json_from_contract_choice (
     363             :                        &input->details.v1.choices[i],
     364             :                        false)));
     365             : 
     366           1 :   families = json_object ();
     367           1 :   GNUNET_assert (0 != families);
     368           3 :   for (unsigned i = 0; i < input->details.v1.token_authorities_len; i++)
     369           2 :     GNUNET_assert (0 == json_object_set_new (
     370             :                      families,
     371             :                      input->details.v1.token_authorities[i].slug,
     372             :                      TALER_MERCHANT_json_from_token_family (
     373             :                        &input->details.v1.token_authorities[i])));
     374             : 
     375           1 :   return GNUNET_JSON_PACK (
     376             :     GNUNET_JSON_pack_array_steal ("choices",
     377             :                                   choices),
     378             :     GNUNET_JSON_pack_object_steal ("token_families",
     379             :                                    families));
     380             : }
     381             : 
     382             : 
     383             : json_t *
     384           2 : TALER_MERCHANT_contract_serialize (
     385             :   const struct TALER_MERCHANT_Contract *input,
     386             :   bool nonce_optional)
     387             : {
     388             :   json_t *details;
     389             : 
     390           2 :   switch (input->version)
     391             :   {
     392           1 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
     393           1 :     details = json_from_contract_v0 (input);
     394           1 :     goto success;
     395           1 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
     396           1 :     details = json_from_contract_v1 (input);
     397           1 :     goto success;
     398             :   }
     399             : 
     400           0 :   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     401             :               "unknown contract type version %d",
     402             :               input->version);
     403           0 :   GNUNET_assert (0);
     404             :   return NULL;
     405             : 
     406           2 : success:
     407           2 :   return GNUNET_JSON_PACK (
     408             :     GNUNET_JSON_pack_uint64 ("version",
     409             :                              input->version),
     410             :     GNUNET_JSON_pack_string ("summary",
     411             :                              input->summary),
     412             :     GNUNET_JSON_pack_allow_null (
     413             :       GNUNET_JSON_pack_object_steal ("summary_i18n",
     414             :                                      input->summary_i18n)),
     415             :     GNUNET_JSON_pack_string ("order_id",
     416             :                              input->order_id),
     417             :     GNUNET_JSON_pack_allow_null (
     418             :       GNUNET_JSON_pack_string ("public_reorder_url",
     419             :                                input->public_reorder_url)),
     420             :     GNUNET_JSON_pack_allow_null (
     421             :       GNUNET_JSON_pack_string ("fulfillment_url",
     422             :                                input->fulfillment_url)),
     423             :     GNUNET_JSON_pack_allow_null (
     424             :       GNUNET_JSON_pack_string ("fulfillment_message",
     425             :                                input->fulfillment_message)),
     426             :     GNUNET_JSON_pack_allow_null (
     427             :       GNUNET_JSON_pack_object_steal ("fulfillment_message_i18n",
     428             :                                      input->fulfillment_message_i18n)),
     429             :     GNUNET_JSON_pack_array_steal ("products",
     430             :                                   input->products),
     431             :     GNUNET_JSON_pack_timestamp ("timestamp",
     432             :                                 input->timestamp),
     433             :     GNUNET_JSON_pack_timestamp ("refund_deadline",
     434             :                                 input->refund_deadline),
     435             :     GNUNET_JSON_pack_timestamp ("pay_deadline",
     436             :                                 input->pay_deadline),
     437             :     GNUNET_JSON_pack_timestamp ("wire_transfer_deadline",
     438             :                                 input->wire_deadline),
     439             :     GNUNET_JSON_pack_data_auto ("merchant_pub",
     440             :                                 &input->merchant_pub.eddsa_pub),
     441             :     GNUNET_JSON_pack_string ("merchant_base_url",
     442             :                              input->merchant_base_url),
     443             :     GNUNET_JSON_pack_object_steal ("merchant",
     444             :                                    json_from_merchant_details (input)),
     445             :     GNUNET_JSON_pack_data_auto ("h_wire",
     446             :                                 &input->h_wire),
     447             :     GNUNET_JSON_pack_string ("wire_method",
     448             :                              input->wire_method),
     449             :     GNUNET_JSON_pack_array_steal ("exchanges",
     450             :                                   input->exchanges),
     451             :     GNUNET_JSON_pack_allow_null (
     452             :       GNUNET_JSON_pack_object_steal ("delivery_location",
     453             :                                      input->delivery_location)),
     454             :     GNUNET_JSON_pack_allow_null (
     455             :       GNUNET_JSON_pack_timestamp ("delivery_date",
     456             :                                   input->delivery_date)),
     457             :     (nonce_optional)
     458             :     ? GNUNET_JSON_pack_allow_null (
     459             :       GNUNET_JSON_pack_string ("nonce",
     460             :                                input->nonce))
     461             :     : GNUNET_JSON_pack_string ("nonce",
     462             :                                input->nonce),
     463             :     GNUNET_JSON_pack_allow_null (
     464             :       GNUNET_JSON_pack_time_rel ("auto_refund",
     465             :                                  input->auto_refund)),
     466             :     GNUNET_JSON_pack_allow_null (
     467             :       GNUNET_JSON_pack_object_steal ("extra",
     468             :                                      input->extra)),
     469             :     GNUNET_JSON_pack_allow_null (
     470             :       GNUNET_JSON_pack_uint64 ("minimum_age",
     471             :                                input->minimum_age)),
     472             :     GNUNET_JSON_pack_object_steal (NULL,
     473             :                                    details));
     474             : }

Generated by: LCOV version 1.16