diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/exchange/taler-exchange-httpd_purses_create.c | 2 | ||||
| -rw-r--r-- | src/include/taler_exchange_service.h | 15 | ||||
| -rw-r--r-- | src/include/taler_testing_lib.h | 125 | ||||
| -rw-r--r-- | src/testing/Makefile.am | 1 | ||||
| -rw-r--r-- | src/testing/testing_api_cmd_purse_create_deposit.c | 397 | 
5 files changed, 535 insertions, 5 deletions
diff --git a/src/exchange/taler-exchange-httpd_purses_create.c b/src/exchange/taler-exchange-httpd_purses_create.c index 610320da..00bb1d1e 100644 --- a/src/exchange/taler-exchange-httpd_purses_create.c +++ b/src/exchange/taler-exchange-httpd_purses_create.c @@ -540,7 +540,7 @@ parse_coin (struct MHD_Connection *connection,                  &coin->cpi.denom_pub_hash,                  TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,                  "PURSE CREATE")) -        ? GNUNET_NO : GNUNET_SYSERR; +             ? GNUNET_NO : GNUNET_SYSERR;      }      if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))      { diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 20e7943f..876e529f 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -4216,11 +4216,18 @@ struct TALER_EXCHANGE_PurseCreateDepositResponse       */      struct      { +      /** +       * Signing key used by the exchange to sign the +       * purse create with deposit confirmation. +       */ +      struct TALER_ExchangePublicKeyP exchange_pub;        /** -       * Private key that can be used to obtain the contract. +       * Signature from the exchange on the +       * purse create with deposit confirmation.         */ -      struct TALER_ContractDiffiePrivateP contract_priv; +      struct TALER_ExchangeSignatureP exchange_sig; +      } success; @@ -4464,7 +4471,7 @@ struct TALER_EXCHANGE_PurseCreateMergeHandle;   * @param reserve_priv private key of the reserve   * @param purse_priv private key of the purse   * @param contract_terms contract the purse is about - * @param min_age minimum age we need to prove for the purse + * @param upload_contract true to upload the contract   * @param purse_expiration when will the unmerged purse expire   * @paran merge_timestamp when should the merge happen (use current time)   * @param purse_value_after_fees target amount in the purse @@ -4479,7 +4486,7 @@ TALER_EXCHANGE_purse_create_with_merge (    const struct TALER_ReservePrivateKeyP *reserve_priv,    const struct TALER_PurseContractPrivateKeyP *purse_priv,    const json_t *contract_terms, -  uint8_t min_age, +  bool upload_contract,    struct GNUNET_TIME_Timestamp purse_expiration,    struct GNUNET_TIME_Timestamp merge_timestamp,    const struct TALER_Amount *purse_value_after_fees, diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index 4badc0b9..6980d9d8 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -2343,6 +2343,126 @@ TALER_TESTING_cmd_oauth (const char *label,                           uint16_t port); +/* ****************** P2P payment commands ****************** */ + + +/** + * Creates a purse with deposits. + * + * @param label command label + * @param expected_http_status what HTTP status do we expect to get returned from the exchange + * @param target_amount amount for the purse to be full, without fees + * @param contract_terms contract, JSON string + * @param upload_contract should we upload the contract + * @param purse_expiration how long until the purse expires + * @param ... NULL-terminated list of references to coins to be deposited + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_create_with_deposit ( +  const char *label, +  unsigned int expected_http_status, +  const char *target_amount, +  const char *contract_terms, +  bool upload_contract, +  struct GNUNET_TIME_Relative purse_expiration, +  ...); + + +/** + * Retrieve contract (also checks that the contract matches + * the upload command). + * + * @param label command label + * @param expected_http_status what HTTP status do we expect to get returned from the exchange + * @param contract_ref reference to a command providing us with the contract private key + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_contract_get ( +  const char *label, +  unsigned int expected_http_status, +  const char *contract_ref); + + +/** + * Retrieve purse state by merge private key. + * + * @param label command label + * @param expected_http_status what HTTP status do we expect to get returned from the exchange + * @param merge_ref reference to a command providing us with the merge private key + * @param reserve_ref reference to a command providing us with a reserve private key; if NULL, we create a fresh reserve + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_merge ( +  const char *label, +  unsigned int expected_http_status, +  const char *merge_ref, +  const char *reserve_ref); + + +/** + * Retrieve purse state by purse private key. + * + * @param label command label + * @param expected_http_status what HTTP status do we expect to get returned from the exchange + * @param purse_ref reference to a command providing us with the purse private key + * @param merge_to how long to wait for a merge + * @param deposit_to how long to wait for a deposit + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_get ( +  const char *label, +  unsigned int expected_http_status, +  const char *purse_ref, +  struct GNUNET_TIME_Relative merge_to, +  struct GNUNET_TIME_Relative deposit_to); + + +/** + * Creates a purse with reserve. + * + * @param label command label + * @param expected_http_status what HTTP status do we expect to get returned from the exchange + * @param target_amount amount for the purse to be full, without fees + * @param contract_terms contract, JSON string + * @param upload_contract should we upload the contract + * @param purse_expiration how long until the purse expires + * @param reserve_ref reference to reserve key, or NULL to create a new reserve + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_create_with_reserve ( +  const char *label, +  unsigned int expected_http_status, +  const char *target_amount, +  const char *contract_terms, +  bool upload_contract, +  struct GNUNET_TIME_Relative purse_expiration, +  const char *reserve_ref); + + +/** + * Deposit coins into a purse. + * + * @param label command label + * @param expected_http_status what HTTP status do we expect to get returned from the exchange + * @param min_age age restriction of the purse + * @param purse_ref reference to the purse + * @param ... NULL-terminated list of references to coins to be deposited + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_deposit_coins ( +  const char *label, +  unsigned int expected_http_status, +  uint8_t min_age, +  const char *purse_ref, +  ...); + +  /* *** Generic trait logic for implementing traits ********* */ @@ -2499,6 +2619,11 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,   */  #define TALER_TESTING_SIMPLE_TRAITS(op) \    op (bank_row, const uint64_t)                                    \ +  op (purse_priv, const struct TALER_PurseContractPrivateKeyP)     \ +  op (purse_pub, const struct TALER_PurseContractPublicKeyP)       \ +  op (merge_priv, const struct TALER_PurseMergePrivateKeyP)        \ +  op (merge_pub, const struct TALER_PurseMergePublicKeyP)          \ +  op (contract_priv, const struct TALER_ContractDiffiePrivateP)    \    op (reserve_priv, const struct TALER_ReservePrivateKeyP)         \    op (h_payto, const struct TALER_PaytoHashP)                      \    op (planchet_secret, const struct TALER_PlanchetMasterSecretP)   \ diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index 5b4dba30..c0e67588 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -69,6 +69,7 @@ libtalertesting_la_SOURCES = \    testing_api_cmd_offline_sign_fees.c \    testing_api_cmd_offline_sign_keys.c \    testing_api_cmd_offline_sign_extensions.c \ +  testing_api_cmd_purse_create_deposit.c \    testing_api_cmd_set_wire_fee.c \    testing_api_cmd_recoup.c \    testing_api_cmd_recoup_refresh.c \ diff --git a/src/testing/testing_api_cmd_purse_create_deposit.c b/src/testing/testing_api_cmd_purse_create_deposit.c new file mode 100644 index 00000000..8aeab744 --- /dev/null +++ b/src/testing/testing_api_cmd_purse_create_deposit.c @@ -0,0 +1,397 @@ +/* +  This file is part of TALER +  Copyright (C) 2022 Taler Systems SA + +  TALER is free software; you can redistribute it and/or modify it +  under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 3, or (at your +  option) any later version. + +  TALER is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU General Public +  License along with TALER; see the file COPYING.  If not, see +  <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_purse_create_deposit.c + * @brief command for testing /purses/$PID/create + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + +/** + * Information we keep per deposited coin. + */ +struct Coin +{ +  /** +   * Reference to the respective command. +   */ +  const char *command_ref; + +  /** +   * index of the specific coin in the traits of @e command_ref. +   */ +  unsigned int coin_index; + +  /** +   * Amount to deposit (with fee). +   */ +  struct TALER_Amount deposit_with_fee; + +}; + + +/** + * State for a "purse create deposit" CMD. + */ +struct PurseCreateDepositState +{ + +  /** +   * Total purse target amount without fees. +   */ +  struct TALER_Amount target_amount; + +  /** +   * Reference to any command that is able to provide a coin. +   */ +  struct Coin *coin_references; + +  /** +   * JSON string describing what a proposal is about. +   */ +  json_t *contract_terms; + +  /** +   * Purse expiration time. +   */ +  struct GNUNET_TIME_Timestamp purse_expiration; + +  /** +   * Relative purse expiration time. +   */ +  struct GNUNET_TIME_Relative rel_expiration; + +  /** +   * Set (by the interpreter) to a fresh private key.  This +   * key will be used to create the purse. +   */ +  struct TALER_PurseContractPrivateKeyP purse_priv; + +  /** +   * Set (by the interpreter) to a fresh private key.  This +   * key will be used to merge the purse. +   */ +  struct TALER_PurseMergePrivateKeyP merge_priv; + +  /** +   * Set (by the interpreter) to a fresh private key.  This +   * key will be used to decrypt the contract. +   */ +  struct TALER_ContractDiffiePrivateP contract_priv; + +  /** +   * Signing key used by the exchange to sign the +   * deposit confirmation. +   */ +  struct TALER_ExchangePublicKeyP exchange_pub; + +  /** +   * Signature from the exchange on the +   * deposit confirmation. +   */ +  struct TALER_ExchangeSignatureP exchange_sig; + +  /** +   * Set (by the interpreter) to a public key corresponding +   * to @e purse_priv. +   */ +  struct TALER_PurseContractPublicKeyP purse_pub; + +  /** +   * PurseCreateDeposit handle while operation is running. +   */ +  struct TALER_EXCHANGE_PurseCreateDepositHandle *dh; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; + +  /** +   * Expected HTTP response code. +   */ +  unsigned int expected_response_code; + +  /** +   * Length of the @e coin_references array. +   */ +  unsigned int num_coin_references; + +  /** +   * Should we upload the contract? +   */ +  bool upload_contract; + +}; + + +/** + * Callback to analyze the /purses/$PID/create response, just used to check if + * the response code is acceptable. + * + * @param cls closure. + * @param dr deposit response details + */ +static void +deposit_cb (void *cls, +            const struct TALER_EXCHANGE_PurseCreateDepositResponse *dr) +{ +  struct PurseCreateDepositState *ds = cls; + +  ds->dh = NULL; +  if (ds->expected_response_code != dr->hr.http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Unexpected response code %u to command %s in %s:%u\n", +                dr->hr.http_status, +                ds->is->commands[ds->is->ip].label, +                __FILE__, +                __LINE__); +    json_dumpf (dr->hr.reply, +                stderr, +                0); +    TALER_TESTING_interpreter_fail (ds->is); +    return; +  } +  if (MHD_HTTP_OK == dr->hr.http_status) +  { +    ds->exchange_pub = dr->details.success.exchange_pub; +    ds->exchange_sig = dr->details.success.exchange_sig; +  } +  TALER_TESTING_interpreter_next (ds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +deposit_run (void *cls, +             const struct TALER_TESTING_Command *cmd, +             struct TALER_TESTING_Interpreter *is) +{ +  struct PurseCreateDepositState *ds = cls; +  struct TALER_EXCHANGE_PurseDeposit deposits[ds->num_coin_references]; + +  (void) cmd; +  ds->is = is; +  for (unsigned int i = 0; i<ds->num_coin_references; i++) +  { +    const struct Coin *cr = &ds->coin_references[i]; +    struct TALER_EXCHANGE_PurseDeposit *pd = &deposits[i]; +    const struct TALER_TESTING_Command *coin_cmd; +    const struct TALER_CoinSpendPrivateKeyP *coin_priv; +    const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL; +    struct TALER_AgeCommitmentHash h_age_commitment = {0}; +    const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; +    const struct TALER_DenominationSignature *denom_pub_sig; + +    coin_cmd = TALER_TESTING_interpreter_lookup_command (is, +                                                         cr->command_ref); +    if (NULL == coin_cmd) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (is); +      return; +    } + +    if ( (GNUNET_OK != +          TALER_TESTING_get_trait_coin_priv (coin_cmd, +                                             cr->coin_index, +                                             &coin_priv)) || +         (GNUNET_OK != +          TALER_TESTING_get_trait_age_commitment_proof (coin_cmd, +                                                        cr->coin_index, +                                                        &age_commitment_proof)) +         || +         (GNUNET_OK != +          TALER_TESTING_get_trait_denom_pub (coin_cmd, +                                             cr->coin_index, +                                             &denom_pub)) || +         (GNUNET_OK != +          TALER_TESTING_get_trait_denom_sig (coin_cmd, +                                             cr->coin_index, +                                             &denom_pub_sig)) ) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (is); +      return; +    } +    if (NULL != age_commitment_proof) +    { +      TALER_age_commitment_hash (&age_commitment_proof->commitment, +                                 &h_age_commitment); +    } +#if FIXME_OEC +    pd->age_commitment = *h_age_commitment; +#endif +    pd->denom_sig = *denom_pub_sig; +    pd->coin_priv = *coin_priv; +    pd->amount = cr->deposit_with_fee; +    pd->h_denom_pub = denom_pub->h_key; +  } + +  GNUNET_CRYPTO_eddsa_key_create (&ds->purse_priv.eddsa_priv); +  GNUNET_CRYPTO_eddsa_key_create (&ds->merge_priv.eddsa_priv); +  GNUNET_CRYPTO_ecdhe_key_create (&ds->contract_priv.ecdhe_priv); +  ds->purse_expiration +    = GNUNET_TIME_relative_to_timestamp (ds->rel_expiration); +  GNUNET_CRYPTO_eddsa_key_get_public (&ds->purse_priv.eddsa_priv, +                                      &ds->purse_pub.eddsa_pub); +  ds->dh = TALER_EXCHANGE_purse_create_with_deposit ( +    is->exchange, +    &ds->purse_priv, +    &ds->merge_priv, +    &ds->contract_priv, +    ds->contract_terms, +    ds->purse_expiration, +    ds->num_coin_references, +    deposits, +    ds->upload_contract, +    &deposit_cb, +    ds); +  if (NULL == ds->dh) +  { +    GNUNET_break (0); +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Could not create purse with deposit\n"); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +} + + +/** + * Free the state of a "deposit" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct PurseCreateDepositState`. + * @param cmd the command which is being cleaned up. + */ +static void +deposit_cleanup (void *cls, +                 const struct TALER_TESTING_Command *cmd) +{ +  struct PurseCreateDepositState *ds = cls; + +  if (NULL != ds->dh) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Command %u (%s) did not complete\n", +                ds->is->ip, +                cmd->label); +    TALER_EXCHANGE_purse_create_with_deposit_cancel (ds->dh); +    ds->dh = NULL; +  } +  json_decref (ds->contract_terms); +  GNUNET_free (ds->coin_references); +  GNUNET_free (ds); +} + + +/** + * Offer internal data from a "deposit" CMD, to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +deposit_traits (void *cls, +                const void **ret, +                const char *trait, +                unsigned int index) +{ +  struct PurseCreateDepositState *ds = cls; +  struct TALER_TESTING_Trait traits[] = { +    TALER_TESTING_make_trait_merge_priv (&ds->merge_priv), +    TALER_TESTING_make_trait_contract_priv (&ds->contract_priv), +    TALER_TESTING_make_trait_purse_priv (&ds->purse_priv), +    TALER_TESTING_make_trait_purse_pub (&ds->purse_pub), +    TALER_TESTING_make_trait_contract_terms (ds->contract_terms), +    TALER_TESTING_make_trait_deposit_amount (&ds->target_amount), +    TALER_TESTING_make_trait_timestamp (index, +                                        &ds->purse_expiration), +    TALER_TESTING_trait_end () +  }; + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_create_with_deposit ( +  const char *label, +  unsigned int expected_http_status, +  const char *target_amount, +  const char *contract_terms, +  bool upload_contract, +  struct GNUNET_TIME_Relative purse_expiration, +  ...) +{ +  struct PurseCreateDepositState *ds; + +  ds = GNUNET_new (struct PurseCreateDepositState); +  ds->rel_expiration = purse_expiration; +  ds->upload_contract = upload_contract; +  ds->expected_response_code = expected_http_status; +  ds->contract_terms = json_loads (contract_terms, +                                   JSON_REJECT_DUPLICATES, +                                   NULL); +  if (NULL == ds->contract_terms) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Failed to parse contract terms `%s' for CMD `%s'\n", +                contract_terms, +                label); +    GNUNET_assert (0); +  } +  // FIXME: parse varargs! +  GNUNET_assert (GNUNET_OK == +                 TALER_string_to_amount (target_amount, +                                         &ds->target_amount)); +  { +    struct TALER_TESTING_Command cmd = { +      .cls = ds, +      .label = label, +      .run = &deposit_run, +      .cleanup = &deposit_cleanup, +      .traits = &deposit_traits +    }; + +    return cmd; +  } +} + + +/* end of testing_api_cmd_purse_create_deposit.c */  | 
