From f6cb7a8193a01f9d3c050631cea181b8864162db Mon Sep 17 00:00:00 2001 From: Christian Blättler Date: Sun, 10 Mar 2024 19:52:44 +0100 Subject: first shot at v1 contract serialization --- .../taler-merchant-httpd_private-post-orders.c | 353 ++++++++++++++++++--- 1 file changed, 303 insertions(+), 50 deletions(-) (limited to 'src/backend/taler-merchant-httpd_private-post-orders.c') diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c index 1c25da4e..d8120e91 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.c +++ b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -243,7 +243,7 @@ struct OrderContext const char *public_reorder_url; /** - * Optional array of contract choices. Is null for v0 contracts. + * Array of contract choices. Is null for v0 contracts. */ const json_t *choices; @@ -288,6 +288,18 @@ struct OrderContext */ struct TALER_Amount brutto; + /** + * Array of limits per currency. Is null for v0 contracts. + * See @e max_fees for v0 contracts. + */ + // const json_t *limits; + + /** + * Maximum fee as given by the client request. Only used in v0 contracts. + * See @e limits for v1 contracts. + */ + // struct TALER_Amount max_fee; + /** * Array of fee limits and wire account details by currency. */ @@ -1246,20 +1258,12 @@ get_exchange_keys (void *cls, rx); } - -/** - * Serialize order into @a oc->serialize_order.contract, - * ready to be stored in the database. Upon success, continue - * processing with check_contract(). - * - * @param[in,out] oc order context - */ static void -serialize_order (struct OrderContext *oc) +serialize_order_v0 (struct OrderContext *oc) { const struct TALER_MERCHANTDB_InstanceSettings *settings = &oc->hc->instance->settings; - struct TALER_MerchantContractChoice *choice = oc->parse_order.choices; + struct TALER_MerchantContractChoice *choice = oc->parse_choices.choices; struct TALER_MerchantContractLimits *limits = oc->set_max_fee.limits; json_t *merchant = NULL; @@ -1393,10 +1397,195 @@ serialize_order (struct OrderContext *oc) GNUNET_JSON_from_time_rel ( oc->parse_order.auto_refund))); } +} - oc->phase++; +static void +serialize_order_v1 (struct OrderContext *oc) +{ + const struct TALER_MERCHANTDB_InstanceSettings *settings = + &oc->hc->instance->settings; + json_t *merchant = NULL; + json_t *limits = json_object (); + json_t *choices = json_array (); + + { + merchant = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("name", + settings->name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("website", + settings->website)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("email", + settings->email)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("logo", + settings->logo))); + GNUNET_assert (NULL != merchant); + { + json_t *loca; + + /* Handle merchant address */ + loca = settings->address; + if (NULL != loca) + { + loca = json_deep_copy (loca); + GNUNET_assert (NULL != loca); + GNUNET_assert (0 == + json_object_set_new (merchant, + "address", + loca)); + } + } + { + json_t *juri; + + /* Handle merchant jurisdiction */ + juri = settings->jurisdiction; + if (NULL != juri) + { + juri = json_deep_copy (juri); + GNUNET_assert (NULL != juri); + GNUNET_assert (0 == + json_object_set_new (merchant, + "jurisdiction", + juri)); + } + } + } + + for (unsigned int i = 0; iset_max_fee.limits_len; i++) + { + struct TALER_MerchantContractLimits l = oc->set_max_fee.limits[i]; + GNUNET_assert (0 == + json_object_set_new( + limits, + l.currency, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("h_wire", + &l.h_wire), + GNUNET_JSON_pack_string ("wire_method", + l.wire_method), + TALER_JSON_pack_amount("max_fee", + &l.max_fee)))); + } + + for (unsigned int i = 0; iparse_choices.choices_len; i++) + { + struct TALER_MerchantContractChoice choice = oc->parse_choices.choices[i]; + GNUNET_assert (0 == + json_array_append_new( + choices, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("summary", + choice.summary), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("summary_i18n", + choice.summary_i18n)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("fulfillment_message", + choice.fulfillment_message)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("fulfillment_message_i18n", + choice.fulfillment_message_i18n)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("fulfillment_url", + choice.fulfillment_url))))); + } + + oc->serialize_order.contract = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("choices", + choices)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("limits", + limits)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("public_reorder_url", + oc->parse_order.public_reorder_url)), + GNUNET_JSON_pack_array_incref ("products", + oc->merge_inventory.products), + GNUNET_JSON_pack_data_auto ("h_wire", + &oc->add_payment_details.wm->h_wire), + GNUNET_JSON_pack_string ("wire_method", + oc->add_payment_details.wm->wire_method), + GNUNET_JSON_pack_string ("order_id", + oc->parse_order.order_id), + GNUNET_JSON_pack_timestamp ("timestamp", + oc->parse_order.timestamp), + GNUNET_JSON_pack_timestamp ("pay_deadline", + oc->parse_order.pay_deadline), + GNUNET_JSON_pack_timestamp ("wire_transfer_deadline", + oc->parse_order.wire_deadline), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_timestamp ("delivery_date", + oc->parse_order.delivery_date)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("delivery_location", + oc->parse_order.delivery_location)), + GNUNET_JSON_pack_string ("merchant_base_url", + oc->parse_order.merchant_base_url), + GNUNET_JSON_pack_object_incref ("merchant", + merchant), + GNUNET_JSON_pack_data_auto ("merchant_pub", + &oc->hc->instance->merchant_pub), + GNUNET_JSON_pack_array_incref ("exchanges", + oc->set_exchanges.exchanges), + TALER_JSON_pack_amount ("amount", + &oc->parse_order.brutto), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("extra", + (json_t *) oc->parse_order.extra)) + ); + + /* Pack does not work here, because it doesn't set zero-values for timestamps */ + GNUNET_assert (0 == + json_object_set_new (oc->serialize_order.contract, + "refund_deadline", + GNUNET_JSON_from_timestamp ( + oc->parse_order.refund_deadline))); + GNUNET_log ( + GNUNET_ERROR_TYPE_INFO, + "Refund deadline for contact is %llu\n", + (unsigned long long) oc->parse_order.refund_deadline.abs_time.abs_value_us); + GNUNET_log ( + GNUNET_ERROR_TYPE_INFO, + "Wallet timestamp for contact is %llu\n", + (unsigned long long) oc->parse_order.timestamp.abs_time.abs_value_us); + + /* Pack does not work here, because it sets zero-values for relative times */ + /* auto_refund should only be set if it is not 0 */ + if (! GNUNET_TIME_relative_is_zero (oc->parse_order.auto_refund)) + { + GNUNET_assert (0 == + json_object_set_new (oc->serialize_order.contract, + "auto_refund", + GNUNET_JSON_from_time_rel ( + oc->parse_order.auto_refund))); + } } +/** + * Serialize order into @a oc->serialize_order.contract, + * ready to be stored in the database. Upon success, continue + * processing with check_contract(). + * + * @param[in,out] oc order context + */ +static void +serialize_order (struct OrderContext *oc) +{ + if (TALER_MCV_V0 == oc->parse_order.version) + { + serialize_order_v0 (oc); + } + else + { + serialize_order_v1 (oc); + } + + oc->phase++; +} /** * Set max_fee in @a oc based on STEFAN value if @@ -1410,27 +1599,30 @@ set_max_fee (struct OrderContext *oc) { const struct TALER_MERCHANTDB_InstanceSettings *settings = &oc->hc->instance->settings; - struct TALER_MerchantContractLimits *parsed_limits = oc->parse_order.limits; - struct TALER_MerchantContractLimits *limits = oc->set_max_fee.limits; - GNUNET_assert (1 == oc->parse_order.limits_len && NULL != parsed_limits); - GNUNET_assert (1 == oc->set_max_fee.limits_len && NULL != limits); + GNUNET_array_grow(oc->set_max_fee.limits, + oc->set_max_fee.limits_len, + oc->parse_order.limits_len); - if (GNUNET_OK != - TALER_amount_is_valid (&parsed_limits->max_fee)) + for (unsigned int i = 0; iparse_order.limits_len; i++) { - struct TALER_Amount stefan; + if (GNUNET_OK != + TALER_amount_is_valid (&oc->parse_order.limits[i].max_fee)) + { + struct TALER_Amount stefan; - if ( (settings->use_stefan) && - (GNUNET_OK == - TALER_amount_is_valid (&oc->set_exchanges.max_stefan_fee)) ) - stefan = oc->set_exchanges.max_stefan_fee; - else - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (oc->parse_order.brutto.currency, - &stefan)); - limits->max_fee = stefan; + if ( (settings->use_stefan) && + (GNUNET_OK == + TALER_amount_is_valid (&oc->set_exchanges.max_stefan_fee)) ) + stefan = oc->set_exchanges.max_stefan_fee; + else + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (oc->parse_order.brutto.currency, + &stefan)); + oc->set_max_fee.limits[i].max_fee = stefan; + } } + oc->phase++; } @@ -1508,18 +1700,21 @@ set_exchanges (struct OrderContext *oc) static void parse_order (struct OrderContext *oc) { - struct TALER_MerchantContractLimits *limits; + // struct TALER_MerchantContractLimits *limits; const struct TALER_MERCHANTDB_InstanceSettings *settings = &oc->hc->instance->settings; const char *merchant_base_url = NULL; const char *version = NULL; const json_t *jmerchant = NULL; - limits = GNUNET_new (struct TALER_MerchantContractLimits); + const json_t *jlimits; + struct TALER_Amount max_fee; + // limits = GNUNET_new (struct TALER_MerchantContractLimits); /* auto_refund only needs to be type-checked, * mostly because in GNUnet relative times can't * be negative. */ bool no_fee; + bool no_limits; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("version", @@ -1565,8 +1760,12 @@ parse_order (struct OrderContext *oc) NULL), GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_amount_any ("max_fee", - &limits->max_fee), + &max_fee), &no_fee), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("limits", + &jlimits), + &no_limits), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("delivery_location", &oc->parse_order.delivery_location), @@ -1602,10 +1801,83 @@ parse_order (struct OrderContext *oc) if (NULL == version || 0 == strcmp("v0", version)) { oc->parse_order.version = TALER_MCV_V0; + + if ( (! no_fee) && + (GNUNET_OK != + TALER_amount_cmp_currency (&oc->parse_order.brutto, + &max_fee)) ) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_CURRENCY_MISMATCH, + "different currencies used for 'max_fee' and 'amount' currency"); + return; + } + + /** + * v0 only supports one currency. For the sake of simplicity, + * we still use the limits array with just one item. + */ + GNUNET_array_grow (oc->parse_order.limits, + oc->parse_order.limits_len, + 1); + + oc->parse_order.limits->max_fee = max_fee; + + /* Copy currency of max_fee into limits label */ + GNUNET_snprintf (oc->parse_order.limits->currency, + sizeof (oc->parse_order.limits->currency), + "%s", + oc->parse_order.limits->max_fee.currency); } else if (0 != strcmp("v1", version)) { oc->parse_order.version = TALER_MCV_V1; + + GNUNET_array_grow (oc->parse_order.limits, + oc->parse_order.limits_len, + json_array_size (jlimits)); + + for (unsigned int i = 0; iparse_order.limits_len; i++) + { + struct TALER_MerchantContractLimits *limits = &oc->parse_order.limits[i]; + const char *error_name; + unsigned int error_line; + struct GNUNET_JSON_Specification ispec[] = { + TALER_JSON_spec_amount_any ("max_fee", + &limits->max_fee), + // TODO: Add the rest of the fields + GNUNET_JSON_spec_end () + }; + + ret = GNUNET_JSON_parse (json_array_get (jlimits, + i), + ispec, + &error_name, + &error_line); + if (GNUNET_OK != ret) + { + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Limits parsing failed at #%u: %s:%u\n", + i, + error_name, + error_line); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "order.limits"); + return; + } + + /* Copy currency of max_fee into limits label */ + GNUNET_snprintf (limits->currency, + sizeof (limits->currency), + "%s", + limits->max_fee.currency); + } } else { @@ -1638,25 +1910,6 @@ parse_order (struct OrderContext *oc) "no trusted exchange for this currency"); return; } - if ( (! no_fee) && - (GNUNET_OK != - TALER_amount_cmp_currency (&oc->parse_order.brutto, - &limits->max_fee)) ) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_CURRENCY_MISMATCH, - "different currencies used for 'max_fee' and 'amount' currency"); - return; - } - - /* Copy currency of max_fee into limits label */ - GNUNET_snprintf (limits->currency, - sizeof (limits->currency), - "%s", - limits->max_fee.currency); /* Add order_id if it doesn't exist. */ if (NULL == oc->parse_order.order_id) -- cgit v1.2.3