exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

json.c (22872B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014, 2015, 2016, 2020, 2021 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 json/json.c
     18  * @brief helper functions for JSON processing using libjansson
     19  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
     20  * @author Christian Grothoff
     21  */
     22 #include <gnunet/gnunet_util_lib.h>
     23 #include "taler/taler_util.h"
     24 #include "taler/taler_json_lib.h"
     25 #include <unistr.h>
     26 
     27 
     28 /**
     29  * Check if @a json contains a 'real' value anywhere.
     30  *
     31  * @param json json to check
     32  * @return true if a real is in it somewhere
     33  */
     34 static bool
     35 contains_real (const json_t *json)
     36 {
     37   if (json_is_real (json))
     38     return true;
     39   if (json_is_object (json))
     40   {
     41     json_t *member;
     42     const char *name;
     43 
     44     json_object_foreach ((json_t *) json, name, member)
     45     if (contains_real (member))
     46       return true;
     47     return false;
     48   }
     49   if (json_is_array (json))
     50   {
     51     json_t *member;
     52     size_t index;
     53 
     54     json_array_foreach ((json_t *) json, index, member)
     55     if (contains_real (member))
     56       return true;
     57     return false;
     58   }
     59   return false;
     60 }
     61 
     62 
     63 /**
     64  * Dump the @a json to a string and hash it.
     65  *
     66  * @param json value to hash
     67  * @param salt salt value to include when using HKDF,
     68  *        NULL to not use any salt and to use SHA512
     69  * @param[out] hc where to store the hash
     70  * @return #GNUNET_OK on success,
     71  *         #GNUNET_NO if @a json was not hash-able
     72  *         #GNUNET_SYSERR on failure
     73  */
     74 static enum GNUNET_GenericReturnValue
     75 dump_and_hash (const json_t *json,
     76                const char *salt,
     77                struct GNUNET_HashCode *hc)
     78 {
     79   char *wire_enc;
     80   size_t len;
     81 
     82   if (NULL == json)
     83   {
     84     GNUNET_break_op (0);
     85     return GNUNET_NO;
     86   }
     87   if (contains_real (json))
     88   {
     89     GNUNET_break_op (0);
     90     return GNUNET_NO;
     91   }
     92   if (NULL == (wire_enc = json_dumps (json,
     93                                       JSON_ENCODE_ANY
     94                                       | JSON_COMPACT
     95                                       | JSON_SORT_KEYS)))
     96   {
     97     GNUNET_break (0);
     98     return GNUNET_SYSERR;
     99   }
    100   len = TALER_rfc8785encode (&wire_enc);
    101   if (NULL == salt)
    102   {
    103     GNUNET_CRYPTO_hash (wire_enc,
    104                         len,
    105                         hc);
    106   }
    107   else
    108   {
    109     if (GNUNET_YES !=
    110         GNUNET_CRYPTO_hkdf_gnunet (
    111           hc,
    112           sizeof (*hc),
    113           salt,
    114           strlen (salt) + 1,
    115           wire_enc,
    116           len))
    117     {
    118       GNUNET_break (0);
    119       free (wire_enc);
    120       return GNUNET_SYSERR;
    121     }
    122   }
    123   free (wire_enc);
    124   return GNUNET_OK;
    125 }
    126 
    127 
    128 /**
    129  * Replace "forgettable" parts of a JSON object with their salted hash.
    130  *
    131  * @param[in] in some JSON value
    132  * @param[out] out resulting JSON value
    133  * @return #GNUNET_OK on success,
    134  *         #GNUNET_NO if @a json was not hash-able
    135  *         #GNUNET_SYSERR on failure
    136  */
    137 static enum GNUNET_GenericReturnValue
    138 forget (const json_t *in,
    139         json_t **out)
    140 {
    141   if (json_is_real (in))
    142   {
    143     /* floating point is not allowed! */
    144     GNUNET_break_op (0);
    145     return GNUNET_NO;
    146   }
    147   if (json_is_array (in))
    148   {
    149     /* array is a JSON array */
    150     size_t index;
    151     json_t *value;
    152     json_t *ret;
    153 
    154     ret = json_array ();
    155     if (NULL == ret)
    156     {
    157       GNUNET_break (0);
    158       return GNUNET_SYSERR;
    159     }
    160     json_array_foreach (in, index, value) {
    161       enum GNUNET_GenericReturnValue iret;
    162       json_t *t;
    163 
    164       iret = forget (value,
    165                      &t);
    166       if (GNUNET_OK != iret)
    167       {
    168         json_decref (ret);
    169         return iret;
    170       }
    171       if (0 != json_array_append_new (ret,
    172                                       t))
    173       {
    174         GNUNET_break (0);
    175         json_decref (ret);
    176         return GNUNET_SYSERR;
    177       }
    178     }
    179     *out = ret;
    180     return GNUNET_OK;
    181   }
    182   if (json_is_object (in))
    183   {
    184     json_t *ret;
    185     const char *key;
    186     json_t *value;
    187     json_t *fg;
    188     json_t *rx;
    189 
    190     fg = json_object_get (in,
    191                           "$forgettable");
    192     rx = json_object_get (in,
    193                           "$forgotten");
    194     if (NULL != rx)
    195     {
    196       rx = json_deep_copy (rx); /* should be shallow
    197                                    by structure, but
    198                                    deep copy is safer */
    199       if (NULL == rx)
    200       {
    201         GNUNET_break (0);
    202         return GNUNET_SYSERR;
    203       }
    204     }
    205     ret = json_object ();
    206     if (NULL == ret)
    207     {
    208       GNUNET_break (0);
    209       return GNUNET_SYSERR;
    210     }
    211     json_object_foreach ((json_t*) in, key, value) {
    212       json_t *t;
    213       json_t *salt;
    214       enum GNUNET_GenericReturnValue iret;
    215 
    216       if (fg == value)
    217         continue; /* skip! */
    218       if (rx == value)
    219         continue; /* skip! */
    220       if ( (NULL != rx) &&
    221            (NULL !=
    222             json_object_get (rx,
    223                              key)) )
    224       {
    225         (void) json_object_del (ret,
    226                                 key);
    227         continue; /* already forgotten earlier */
    228       }
    229       iret = forget (value,
    230                      &t);
    231       if (GNUNET_OK != iret)
    232       {
    233         json_decref (ret);
    234         json_decref (rx);
    235         return iret;
    236       }
    237       if ( (NULL != fg) &&
    238            (NULL != (salt = json_object_get (fg,
    239                                              key))) )
    240       {
    241         /* 't' is to be forgotten! */
    242         struct GNUNET_HashCode hc;
    243 
    244         if (! json_is_string (salt))
    245         {
    246           GNUNET_break_op (0);
    247           json_decref (ret);
    248           json_decref (rx);
    249           json_decref (t);
    250           return GNUNET_NO;
    251         }
    252         iret = dump_and_hash (t,
    253                               json_string_value (salt),
    254                               &hc);
    255         if (GNUNET_OK != iret)
    256         {
    257           json_decref (ret);
    258           json_decref (rx);
    259           json_decref (t);
    260           return iret;
    261         }
    262         json_decref (t);
    263         /* scrub salt */
    264         if (0 !=
    265             json_object_del (fg,
    266                              key))
    267         {
    268           GNUNET_break_op (0);
    269           json_decref (ret);
    270           json_decref (rx);
    271           return GNUNET_NO;
    272         }
    273         if (NULL == rx)
    274           rx = json_object ();
    275         if (NULL == rx)
    276         {
    277           GNUNET_break (0);
    278           json_decref (ret);
    279           return GNUNET_SYSERR;
    280         }
    281         if (0 !=
    282             json_object_set_new (rx,
    283                                  key,
    284                                  GNUNET_JSON_from_data_auto (&hc)))
    285         {
    286           GNUNET_break (0);
    287           json_decref (ret);
    288           json_decref (rx);
    289           return GNUNET_SYSERR;
    290         }
    291       }
    292       else
    293       {
    294         /* 't' to be used without 'forgetting' */
    295         if (0 !=
    296             json_object_set_new (ret,
    297                                  key,
    298                                  t))
    299         {
    300           GNUNET_break (0);
    301           json_decref (ret);
    302           json_decref (rx);
    303           return GNUNET_SYSERR;
    304         }
    305       }
    306     } /* json_object_foreach */
    307     if ( (NULL != rx) &&
    308          (0 !=
    309           json_object_set_new (ret,
    310                                "$forgotten",
    311                                rx)) )
    312     {
    313       GNUNET_break (0);
    314       json_decref (ret);
    315       return GNUNET_SYSERR;
    316     }
    317     *out = ret;
    318     return GNUNET_OK;
    319   }
    320   *out = json_incref ((json_t *) in);
    321   return GNUNET_OK;
    322 }
    323 
    324 
    325 enum GNUNET_GenericReturnValue
    326 TALER_JSON_contract_hash (const json_t *json,
    327                           struct TALER_PrivateContractHashP *hc)
    328 {
    329   enum GNUNET_GenericReturnValue ret;
    330   json_t *cjson;
    331   json_t *dc;
    332 
    333   dc = json_deep_copy (json);
    334   ret = forget (dc,
    335                 &cjson);
    336   json_decref (dc);
    337   if (GNUNET_OK != ret)
    338     return ret;
    339   ret = dump_and_hash (cjson,
    340                        NULL,
    341                        &hc->hash);
    342   json_decref (cjson);
    343   return ret;
    344 }
    345 
    346 
    347 enum GNUNET_GenericReturnValue
    348 TALER_JSON_contract_mark_forgettable (json_t *json,
    349                                       const char *field)
    350 {
    351   json_t *fg;
    352   struct GNUNET_ShortHashCode salt;
    353 
    354   if (! json_is_object (json))
    355   {
    356     GNUNET_break (0);
    357     return GNUNET_SYSERR;
    358   }
    359   /* check field name is legal for forgettable field */
    360   for (const char *f = field; '\0' != *f; f++)
    361   {
    362     char c = *f;
    363 
    364     if ( (c >= 'a') && (c <= 'z') )
    365       continue;
    366     if ( (c >= 'A') && (c <= 'Z') )
    367       continue;
    368     if ( (c >= '0') && (c <= '9') )
    369       continue;
    370     if ('_' == c)
    371       continue;
    372     GNUNET_break (0);
    373     return GNUNET_SYSERR;
    374   }
    375   if (NULL == json_object_get (json,
    376                                field))
    377   {
    378     /* field must exist */
    379     GNUNET_break (0);
    380     return GNUNET_SYSERR;
    381   }
    382   fg = json_object_get (json,
    383                         "$forgettable");
    384   if (NULL == fg)
    385   {
    386     fg = json_object ();
    387     if (0 !=
    388         json_object_set_new (json,
    389                              "$forgettable",
    390                              fg))
    391     {
    392       GNUNET_break (0);
    393       return GNUNET_SYSERR;
    394     }
    395   }
    396 
    397   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
    398                               &salt,
    399                               sizeof (salt));
    400   if (0 !=
    401       json_object_set_new (fg,
    402                            field,
    403                            GNUNET_JSON_from_data_auto (&salt)))
    404   {
    405     GNUNET_break (0);
    406     return GNUNET_SYSERR;
    407   }
    408   return GNUNET_OK;
    409 }
    410 
    411 
    412 enum GNUNET_GenericReturnValue
    413 TALER_JSON_contract_part_forget (json_t *json,
    414                                  const char *field)
    415 {
    416   json_t *fg;
    417   const json_t *part;
    418   json_t *fp;
    419   json_t *rx;
    420   struct GNUNET_HashCode hc;
    421   const char *salt;
    422   enum GNUNET_GenericReturnValue ret;
    423 
    424   if (! json_is_object (json))
    425   {
    426     GNUNET_break (0);
    427     return GNUNET_SYSERR;
    428   }
    429   if (NULL == (part = json_object_get (json,
    430                                        field)))
    431   {
    432     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    433                 "Did not find field `%s' we were asked to forget\n",
    434                 field);
    435     return GNUNET_SYSERR;
    436   }
    437   fg = json_object_get (json,
    438                         "$forgettable");
    439   if (NULL == fg)
    440   {
    441     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    442                 "Did not find '$forgettable' attribute trying to forget field `%s'\n",
    443                 field);
    444     return GNUNET_SYSERR;
    445   }
    446   rx = json_object_get (json,
    447                         "$forgotten");
    448   if (NULL == rx)
    449   {
    450     rx = json_object ();
    451     if (0 !=
    452         json_object_set_new (json,
    453                              "$forgotten",
    454                              rx))
    455     {
    456       GNUNET_break (0);
    457       return GNUNET_SYSERR;
    458     }
    459   }
    460   if (NULL !=
    461       json_object_get (rx,
    462                        field))
    463   {
    464     if (! json_is_null (json_object_get (json,
    465                                          field)))
    466     {
    467       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    468                   "Field `%s' market as forgotten, but still exists!\n",
    469                   field);
    470       return GNUNET_SYSERR;
    471     }
    472     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    473                 "Already forgot field `%s'\n",
    474                 field);
    475     return GNUNET_NO;
    476   }
    477   salt = json_string_value (json_object_get (fg,
    478                                              field));
    479   if (NULL == salt)
    480   {
    481     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    482                 "Did not find required salt to forget field `%s'\n",
    483                 field);
    484     return GNUNET_SYSERR;
    485   }
    486 
    487   /* need to recursively forget to compute 'hc' */
    488   ret = forget (part,
    489                 &fp);
    490   if (GNUNET_OK != ret)
    491     return ret;
    492   if (GNUNET_OK !=
    493       dump_and_hash (fp,
    494                      salt,
    495                      &hc))
    496   {
    497     json_decref (fp);
    498     GNUNET_break (0);
    499     return GNUNET_SYSERR;
    500   }
    501   json_decref (fp);
    502   /* drop salt */
    503   if (0 !=
    504       json_object_del (fg,
    505                        field))
    506   {
    507     json_decref (fp);
    508     GNUNET_break (0);
    509     return GNUNET_SYSERR;
    510   }
    511 
    512   /* remember field as 'forgotten' */
    513   if (0 !=
    514       json_object_set_new (rx,
    515                            field,
    516                            GNUNET_JSON_from_data_auto (&hc)))
    517   {
    518     GNUNET_break (0);
    519     return GNUNET_SYSERR;
    520   }
    521   /* finally, set 'forgotten' field to null */
    522   if (0 !=
    523       json_object_del (json,
    524                        field))
    525   {
    526     GNUNET_break (0);
    527     return GNUNET_SYSERR;
    528   }
    529   return GNUNET_OK;
    530 }
    531 
    532 
    533 /**
    534  * Loop over all of the values of a '$forgettable' object.  Replace 'True'
    535  * values with proper random salts.  Fails if any forgettable values are
    536  * neither 'True' nor valid salts (strings).
    537  *
    538  * @param[in,out] f JSON to transform
    539  * @return #GNUNET_OK on success
    540  */
    541 static enum GNUNET_GenericReturnValue
    542 seed_forgettable (json_t *f)
    543 {
    544   const char *key;
    545   json_t *val;
    546 
    547   json_object_foreach (f,
    548                        key,
    549                        val)
    550   {
    551     if (json_is_string (val))
    552       continue;
    553     if (json_is_true (val))
    554     {
    555       struct GNUNET_ShortHashCode sh;
    556 
    557       GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
    558                                   &sh,
    559                                   sizeof (sh));
    560       if (0 !=
    561           json_object_set_new (f,
    562                                key,
    563                                GNUNET_JSON_from_data_auto (&sh)))
    564       {
    565         GNUNET_break (0);
    566         return GNUNET_SYSERR;
    567       }
    568       continue;
    569     }
    570     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    571                 "Forgettable field `%s' has invalid value\n",
    572                 key);
    573     return GNUNET_SYSERR;
    574   }
    575   return GNUNET_OK;
    576 }
    577 
    578 
    579 enum GNUNET_GenericReturnValue
    580 TALER_JSON_contract_seed_forgettable (const json_t *spec,
    581                                       json_t *contract)
    582 {
    583   if (json_is_object (spec))
    584   {
    585     const char *key;
    586     json_t *val;
    587 
    588     json_object_foreach ((json_t *) spec,
    589                          key,
    590                          val)
    591     {
    592       json_t *cval = json_object_get (contract,
    593                                       key);
    594 
    595       if (0 == strcmp ("$forgettable",
    596                        key))
    597       {
    598         json_t *xval = json_deep_copy (val);
    599 
    600         if (GNUNET_OK !=
    601             seed_forgettable (xval))
    602         {
    603           json_decref (xval);
    604           return GNUNET_SYSERR;
    605         }
    606         GNUNET_assert (0 ==
    607                        json_object_set_new (contract,
    608                                             "$forgettable",
    609                                             xval));
    610         continue;
    611       }
    612       if (NULL == cval)
    613         continue;
    614       if (GNUNET_OK !=
    615           TALER_JSON_contract_seed_forgettable (val,
    616                                                 cval))
    617         return GNUNET_SYSERR;
    618     }
    619   }
    620   if (json_is_array (spec))
    621   {
    622     size_t index;
    623     json_t *val;
    624 
    625     json_array_foreach ((json_t *) spec,
    626                         index,
    627                         val)
    628     {
    629       json_t *ival = json_array_get (contract,
    630                                      index);
    631 
    632       if (NULL == ival)
    633         continue;
    634       if (GNUNET_OK !=
    635           TALER_JSON_contract_seed_forgettable (val,
    636                                                 ival))
    637         return GNUNET_SYSERR;
    638     }
    639   }
    640   return GNUNET_OK;
    641 }
    642 
    643 
    644 /**
    645  * Parse a json path.
    646  *
    647  * @param obj the object that the path is relative to.
    648  * @param prev the parent of @e obj.
    649  * @param path the path to parse.
    650  * @param cb the callback to call, if we get to the end of @e path.
    651  * @param cb_cls the closure for the callback.
    652  * @return #GNUNET_OK on success, #GNUNET_SYSERR if @e path is malformed.
    653  */
    654 static enum GNUNET_GenericReturnValue
    655 parse_path (json_t *obj,
    656             json_t *prev,
    657             const char *path,
    658             TALER_JSON_ExpandPathCallback cb,
    659             void *cb_cls)
    660 {
    661   char *id = GNUNET_strdup (path);
    662   char *next_id = strchr (id,
    663                           '.');
    664   char *next_path;
    665   char *bracket;
    666   json_t *next_obj = NULL;
    667   char *next_dot;
    668 
    669   GNUNET_assert (NULL != id); /* make stupid compiler happy */
    670   if (NULL == next_id)
    671   {
    672     cb (cb_cls,
    673         id,
    674         prev);
    675     GNUNET_free (id);
    676     return GNUNET_OK;
    677   }
    678   bracket = strchr (next_id,
    679                     '[');
    680   *next_id = '\0';
    681   next_id++;
    682   next_path = GNUNET_strdup (next_id);
    683   next_dot = strchr (next_id,
    684                      '.');
    685   if (NULL != next_dot)
    686     *next_dot = '\0';
    687   /* If this is the first time this is called, make sure id is "$" */
    688   if ( (NULL == prev) &&
    689        (0 != strcmp (id,
    690                      "$")))
    691   {
    692     GNUNET_free (id);
    693     GNUNET_free (next_path);
    694     return GNUNET_SYSERR;
    695   }
    696 
    697   /* Check for bracketed indices */
    698   if (NULL != bracket)
    699   {
    700     char *end_bracket = strchr (bracket,
    701                                 ']');
    702     json_t *array;
    703 
    704     if (NULL == end_bracket)
    705     {
    706       GNUNET_free (id);
    707       GNUNET_free (next_path);
    708       return GNUNET_SYSERR;
    709     }
    710     *end_bracket = '\0';
    711     *bracket = '\0';
    712     bracket++;
    713     array = json_object_get (obj,
    714                              next_id);
    715     if (0 == strcmp (bracket,
    716                      "*"))
    717     {
    718       size_t index;
    719       json_t *value;
    720       int ret = GNUNET_OK;
    721 
    722       json_array_foreach (array, index, value) {
    723         ret = parse_path (value,
    724                           obj,
    725                           next_path,
    726                           cb,
    727                           cb_cls);
    728         if (GNUNET_OK != ret)
    729         {
    730           GNUNET_free (id);
    731           GNUNET_free (next_path);
    732           return ret;
    733         }
    734       }
    735     }
    736     else
    737     {
    738       unsigned int index;
    739       char dummy;
    740 
    741       if (1 != sscanf (bracket,
    742                        "%u%c",
    743                        &index,
    744                        &dummy))
    745       {
    746         GNUNET_free (id);
    747         GNUNET_free (next_path);
    748         return GNUNET_SYSERR;
    749       }
    750       next_obj = json_array_get (array,
    751                                  index);
    752     }
    753   }
    754   else
    755   {
    756     /* No brackets, so just fetch the object by name */
    757     next_obj = json_object_get (obj,
    758                                 next_id);
    759   }
    760 
    761   if (NULL != next_obj)
    762   {
    763     int ret = parse_path (next_obj,
    764                           obj,
    765                           next_path,
    766                           cb,
    767                           cb_cls);
    768     GNUNET_free (id);
    769     GNUNET_free (next_path);
    770     return ret;
    771   }
    772   GNUNET_free (id);
    773   GNUNET_free (next_path);
    774   return GNUNET_OK;
    775 }
    776 
    777 
    778 enum GNUNET_GenericReturnValue
    779 TALER_JSON_expand_path (json_t *json,
    780                         const char *path,
    781                         TALER_JSON_ExpandPathCallback cb,
    782                         void *cb_cls)
    783 {
    784   return parse_path (json,
    785                      NULL,
    786                      path,
    787                      cb,
    788                      cb_cls);
    789 }
    790 
    791 
    792 enum TALER_ErrorCode
    793 TALER_JSON_get_error_code (const json_t *json)
    794 {
    795   const json_t *jc;
    796 
    797   if (NULL == json)
    798     return TALER_EC_GENERIC_INVALID_RESPONSE;
    799   jc = json_object_get (json, "code");
    800   /* The caller already knows that the JSON represents an error,
    801      so we are dealing with a missing error code here.  */
    802   if (NULL == jc)
    803   {
    804     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    805                 "Expected Taler error code `code' in JSON, but field does not exist!\n");
    806     return TALER_EC_INVALID;
    807   }
    808   if (json_is_integer (jc))
    809     return (enum TALER_ErrorCode) (int) json_integer_value (jc);
    810   GNUNET_break_op (0);
    811   return TALER_EC_INVALID;
    812 }
    813 
    814 
    815 const char *
    816 TALER_JSON_get_error_hint (const json_t *json)
    817 {
    818   const json_t *jc;
    819 
    820   if (NULL == json)
    821     return NULL;
    822   jc = json_object_get (json,
    823                         "hint");
    824   if (NULL == jc)
    825     return NULL; /* no hint, is allowed */
    826   if (! json_is_string (jc))
    827   {
    828     /* Hints must be strings */
    829     GNUNET_break_op (0);
    830     return NULL;
    831   }
    832   return json_string_value (jc);
    833 }
    834 
    835 
    836 enum TALER_ErrorCode
    837 TALER_JSON_get_error_code2 (const void *data,
    838                             size_t data_size)
    839 {
    840   json_t *json;
    841   enum TALER_ErrorCode ec;
    842   json_error_t err;
    843 
    844   json = json_loadb (data,
    845                      data_size,
    846                      JSON_REJECT_DUPLICATES,
    847                      &err);
    848   if (NULL == json)
    849     return TALER_EC_INVALID;
    850   ec = TALER_JSON_get_error_code (json);
    851   json_decref (json);
    852   if (ec == TALER_EC_NONE)
    853     return TALER_EC_INVALID;
    854   return ec;
    855 }
    856 
    857 
    858 void
    859 TALER_deposit_policy_hash (const json_t *policy,
    860                            struct TALER_ExtensionPolicyHashP *ech)
    861 {
    862   GNUNET_assert (GNUNET_OK ==
    863                  dump_and_hash (policy,
    864                                 "taler-extensions-policy",
    865                                 &ech->hash));
    866 }
    867 
    868 
    869 char *
    870 TALER_JSON_canonicalize (const json_t *input)
    871 {
    872   char *wire_enc;
    873 
    874   if (NULL == (wire_enc = json_dumps (input,
    875                                       JSON_ENCODE_ANY
    876                                       | JSON_COMPACT
    877                                       | JSON_SORT_KEYS)))
    878   {
    879     GNUNET_break (0);
    880     return NULL;
    881   }
    882   TALER_rfc8785encode (&wire_enc);
    883   return wire_enc;
    884 }
    885 
    886 
    887 enum GNUNET_GenericReturnValue
    888 TALER_JSON_extensions_manifests_hash (const json_t *manifests,
    889                                       struct TALER_ExtensionManifestsHashP *ech)
    890 {
    891   return dump_and_hash (manifests,
    892                         "taler-extensions-manifests",
    893                         &ech->hash);
    894 }
    895 
    896 
    897 json_t *
    898 TALER_JSON_currency_specs_to_json (
    899   const struct TALER_CurrencySpecification *cspec)
    900 {
    901   json_t *ca;
    902 
    903   ca = json_array ();
    904   GNUNET_assert (NULL != ca);
    905   for (unsigned int i = 0; i<cspec->num_common_amounts; i++)
    906     GNUNET_assert (
    907       0 ==
    908       json_array_append_new (
    909         ca,
    910         TALER_JSON_from_amount (&cspec->common_amounts[i])));
    911   return GNUNET_JSON_PACK (
    912     GNUNET_JSON_pack_string ("name",
    913                              cspec->name),
    914     /* 'currency' is deprecated as of exchange v18 and merchant v6;
    915        remove this line once current-age > 6*/
    916     GNUNET_JSON_pack_string ("currency",
    917                              cspec->currency),
    918     GNUNET_JSON_pack_array_steal ("common_amounts",
    919                                   ca),
    920     GNUNET_JSON_pack_uint64 ("num_fractional_input_digits",
    921                              cspec->num_fractional_input_digits),
    922     GNUNET_JSON_pack_uint64 ("num_fractional_normal_digits",
    923                              cspec->num_fractional_normal_digits),
    924     GNUNET_JSON_pack_uint64 ("num_fractional_trailing_zero_digits",
    925                              cspec->num_fractional_trailing_zero_digits),
    926     GNUNET_JSON_pack_object_incref ("alt_unit_names",
    927                                     cspec->map_alt_unit_names));
    928 }
    929 
    930 
    931 /* End of json/json.c */