diff options
| author | Christian Grothoff <christian@grothoff.org> | 2019-07-24 00:13:53 +0200 | 
|---|---|---|
| committer | Christian Grothoff <christian@grothoff.org> | 2019-07-24 00:13:53 +0200 | 
| commit | 5844a20f15cf6d35503386a717e9d582189a7261 (patch) | |
| tree | 0175ab0041b8e5f01a4f5cdcb561314ac8c3e804 /src | |
| parent | e75d552227a4c06faad0a0eae7539458ff58f9af (diff) | |
implement zombie check
Diffstat (limited to 'src')
| -rw-r--r-- | src/exchange/taler-exchange-httpd_deposit.c | 4 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_payback.c | 2 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_refresh_melt.c | 107 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_refund.c | 2 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_responses.c | 2 | ||||
| -rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 35 | ||||
| -rw-r--r-- | src/include/taler_error_codes.h | 8 | ||||
| -rw-r--r-- | src/lib/test_exchange_api_revocation.c | 31 | ||||
| -rw-r--r-- | src/lib/testing_api_cmd_payback.c | 101 | 
9 files changed, 188 insertions, 104 deletions
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 5320c9c7..51adacb7 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -179,7 +179,9 @@ deposit_transaction (void *cls,    /* Start with fee for THIS transaction */    spent = deposit->amount_with_fee; -  /* add cost of all previous transactions */ +  /* add cost of all previous transactions; skip PAYBACK as revoked +     denominations are not eligible for deposit, and if we are the old coin +     pub of a revoked coin (aka a zombie), then ONLY refresh is allowed. */    qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,                                            session,                                            &deposit->coin.coin_pub, diff --git a/src/exchange/taler-exchange-httpd_payback.c b/src/exchange/taler-exchange-httpd_payback.c index 195e5613..8cfc1aec 100644 --- a/src/exchange/taler-exchange-httpd_payback.c +++ b/src/exchange/taler-exchange-httpd_payback.c @@ -289,7 +289,7 @@ payback_transaction (void *cls,      return GNUNET_DB_STATUS_HARD_ERROR;    } -  /* Calculate remaining balance. */ +  /* Calculate remaining balance, including paybacks already applied. */    qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,                                            session,                                            &pc->coin->coin_pub, diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.c b/src/exchange/taler-exchange-httpd_refresh_melt.c index 5674a12f..8677d627 100644 --- a/src/exchange/taler-exchange-httpd_refresh_melt.c +++ b/src/exchange/taler-exchange-httpd_refresh_melt.c @@ -48,11 +48,11 @@   */  static int  reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection, -				       const struct TALER_CoinSpendPublicKeyP *coin_pub, -				       struct TALER_Amount coin_value, -				       struct TALER_EXCHANGEDB_TransactionList *tl, -				       const struct TALER_Amount *requested, -				       const struct TALER_Amount *residual) +                                       const struct TALER_CoinSpendPublicKeyP *coin_pub, +                                       struct TALER_Amount coin_value, +                                       struct TALER_EXCHANGEDB_TransactionList *tl, +                                       const struct TALER_Amount *requested, +                                       const struct TALER_Amount *residual)  {    json_t *history; @@ -64,9 +64,9 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,                                         MHD_HTTP_FORBIDDEN,                                         "{s:s, s:I, s:o, s:o, s:o, s:o, s:o}",                                         "error", -				       "insufficient funds", -				       "code", -				       (json_int_t) TALER_EC_REFRESH_MELT_INSUFFICIENT_FUNDS, +                                       "insufficient funds", +                                       "code", +                                       (json_int_t) TALER_EC_REFRESH_MELT_INSUFFICIENT_FUNDS,                                         "coin_pub",                                         GNUNET_JSON_from_data_auto (coin_pub),                                         "original_value", @@ -90,8 +90,8 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,   */  static int  reply_refresh_melt_success (struct MHD_Connection *connection, -			    const struct TALER_RefreshCommitmentP *rc, -			    uint32_t noreveal_index) +                            const struct TALER_RefreshCommitmentP *rc, +                            uint32_t noreveal_index)  {    struct TALER_RefreshMeltConfirmationPS body;    struct TALER_ExchangePublicKeyP pub; @@ -139,6 +139,13 @@ struct RefreshMeltContext     */    struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; +  /** +   * Set to #GNUNET_YES if this @a dki was revoked and the operation +   * is thus only allowed for zombie coins where the transaction +   * history includes a #TALER_EXCHANGEDB_TT_OLD_COIN_PAYBACK. +   */ +  int zombie_required; +  }; @@ -168,11 +175,12 @@ refresh_check_melt (struct MHD_Connection *connection,    /* Start with cost of this melt transaction */    spent = rmc->refresh_session.amount_with_fee; -  /* add historic transaction costs of this coin */ +  /* add historic transaction costs of this coin, including paybacks as +     we might be a zombie coin */    qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,                                            session,                                            &rmc->refresh_session.coin.coin_pub, -                                          GNUNET_NO, +                                          GNUNET_YES,                                            &tl);    if (0 > qs)    { @@ -181,16 +189,40 @@ refresh_check_melt (struct MHD_Connection *connection,                                                         TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);      return qs;    } +  if (rmc->zombie_required) +  { +    for (struct TALER_EXCHANGEDB_TransactionList *tp = tl; +         NULL != tp; +         tp = tp->next) +    { +      if (TALER_EXCHANGEDB_TT_OLD_COIN_PAYBACK == tp->type) +      { +        rmc->zombie_required = GNUNET_NO; /* was satisfied! */ +        break; +      } +    } +    if (rmc->zombie_required) +    { +      /* zombie status not satisfied */ +      GNUNET_break (0); +      TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, +                                              tl); +      *mhd_ret = TEH_RESPONSE_reply_external_error (connection, +                                                    TALER_EC_REFRESH_MELT_COIN_EXPIRED_NO_ZOMBIE, +                                                    "denomination expired"); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +  }    if (GNUNET_OK !=        TEH_DB_calculate_transaction_list_totals (tl, -						&spent, -						&spent)) +                                                &spent, +                                                &spent))    {      GNUNET_break (0);      TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,                                              tl);      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, -						     TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED); +                                                     TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED);      return GNUNET_DB_STATUS_HARD_ERROR;    } @@ -206,11 +238,11 @@ refresh_check_melt (struct MHD_Connection *connection,                                            &spent,                                            &rmc->refresh_session.amount_with_fee));      *mhd_ret = reply_refresh_melt_insufficient_funds (connection, -						      &rmc->refresh_session.coin.coin_pub, -						      coin_value, -						      tl, -						      &rmc->refresh_session.amount_with_fee, -						      &coin_residual); +                                                      &rmc->refresh_session.coin.coin_pub, +                                                      coin_value, +                                                      tl, +                                                      &rmc->refresh_session.amount_with_fee, +                                                      &coin_residual);      TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,                                              tl);      return GNUNET_DB_STATUS_HARD_ERROR; @@ -245,9 +277,9 @@ refresh_check_melt (struct MHD_Connection *connection,   */  static enum GNUNET_DB_QueryStatus  refresh_melt_transaction (void *cls, -			  struct MHD_Connection *connection, -			  struct TALER_EXCHANGEDB_Session *session, -			  int *mhd_ret) +                          struct MHD_Connection *connection, +                          struct TALER_EXCHANGEDB_Session *session, +                          int *mhd_ret)  {    struct RefreshMeltContext *rmc = cls;    enum GNUNET_DB_QueryStatus qs; @@ -262,8 +294,8 @@ refresh_melt_transaction (void *cls,    {      TALER_LOG_DEBUG ("Found already-melted coin\n");      *mhd_ret = reply_refresh_melt_success (connection, -					   &rmc->refresh_session.rc, -					   noreveal_index); +                                           &rmc->refresh_session.rc, +                                           noreveal_index);      /* Note: we return "hard error" to ensure the wrapper         does not retry the transaction, and to also not generate         a "fresh" response (as we would on "success") */ @@ -273,22 +305,22 @@ refresh_melt_transaction (void *cls,    {      if (GNUNET_DB_STATUS_HARD_ERROR == qs)        *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, -						       TALER_EC_REFRESH_MELT_DB_FETCH_ERROR); +                                                       TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);      return qs;    }    /* check coin has enough funds remaining on it to cover melt cost */    qs = refresh_check_melt (connection, -			   session, -			   rmc, -			   mhd_ret); +                           session, +                           rmc, +                           mhd_ret);    if (0 > qs)      return qs;    /* pick challenge and persist it */    rmc->refresh_session.noreveal_index      = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, -				TALER_CNC_KAPPA); +                                TALER_CNC_KAPPA);    if (0 >=        (qs = TEH_plugin->insert_melt (TEH_plugin->cls, @@ -298,7 +330,7 @@ refresh_melt_transaction (void *cls,      if (GNUNET_DB_STATUS_SOFT_ERROR != qs)      {        *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, -						       TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR); +                                                       TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR);        return GNUNET_DB_STATUS_HARD_ERROR;      }      return qs; @@ -327,9 +359,9 @@ handle_refresh_melt (struct MHD_Connection *connection,      struct TALER_Amount fee_refresh;      TALER_amount_ntoh (&fee_refresh, -		       &rmc->dki->issue.properties.fee_refresh); +                       &rmc->dki->issue.properties.fee_refresh);      if (TALER_amount_cmp (&fee_refresh, -			  &rmc->refresh_session.amount_with_fee) > 0) +                          &rmc->refresh_session.amount_with_fee) > 0)      {        GNUNET_break_op (0);        return TEH_RESPONSE_reply_external_error (connection, @@ -358,8 +390,8 @@ handle_refresh_melt (struct MHD_Connection *connection,      {        GNUNET_break_op (0);        return TEH_RESPONSE_reply_signature_invalid (connection, -						   TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID, -						   "confirm_sig"); +                                                   TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID, +                                                   "confirm_sig");      }    } @@ -505,9 +537,8 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,                                                    TEH_KS_DKU_ZOMBIE);      if (NULL != dki)      { -      /* Test if zombie-condition is actually satisfied for the coin */ -      if (0 /* FIXME: test if zombie-satisfied */) -        rmc.dki = dki; +      rmc.dki = dki; +      rmc.zombie_required = GNUNET_YES;      }    } diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c index 69b5cae9..5fea37da 100644 --- a/src/exchange/taler-exchange-httpd_refund.c +++ b/src/exchange/taler-exchange-httpd_refund.c @@ -165,7 +165,7 @@ refund_transaction (void *cls,                                            session,                                            &refund->coin.coin_pub,                                            GNUNET_NO, -					  &tl); +                                          &tl);    if (0 > qs)    {      if (GNUNET_DB_STATUS_HARD_ERROR == qs) diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 3cf3e781..f05e4260 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -393,7 +393,7 @@ TEH_RESPONSE_reply_external_error (struct MHD_Connection *connection,   */  int  TEH_RESPONSE_reply_commit_error (struct MHD_Connection *connection, -				 enum TALER_ErrorCode ec) +                                 enum TALER_ErrorCode ec)  {    return TEH_RESPONSE_reply_json_pack (connection,                                         MHD_HTTP_INTERNAL_SERVER_ERROR, diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 56e9202b..c95ed261 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -322,11 +322,10 @@ postgres_create_tables (void *cls)                             ",h_coin_ev BYTEA NOT NULL CHECK(LENGTH(h_coin_ev)=64)"                             ",ev_sig BYTEA NOT NULL"                             ",PRIMARY KEY (rc, newcoin_index)" +                           ",UNIQUE (h_coin_ev)"                             ");"),      GNUNET_PQ_make_try_execute ("CREATE INDEX refresh_revealed_coins_coin_pub_index ON "                                  "refresh_revealed_coins (denom_pub_hash);"), -    GNUNET_PQ_make_try_execute ("CREATE INDEX refresh_revealed_coins_h_coin_ev_index ON " -                                "refresh_revealed_coins (h_coin_ev);"),      /* Table with the transfer keys of a refresh operation; includes         the rc for which this is the link information, the @@ -1661,23 +1660,24 @@ postgres_prepare (PGconn *db_conn)         affecting old coins of refreshed coins */      GNUNET_PQ_make_prepare ("payback_by_old_coin",                              "SELECT" -                            " pr.coin_pub" -                            ",pr.coin_sig" -                            ",pr.coin_blind" -                            ",pr.amount_val" -                            ",pr.amount_frac" -                            ",pr.amount_curr" -                            ",pr.timestamp" +                            " coin_pub" +                            ",coin_sig" +                            ",coin_blind" +                            ",amount_val" +                            ",amount_frac" +                            ",amount_curr" +                            ",timestamp"                              ",coins.denom_pub_hash"                              ",coins.denom_sig" -                            " FROM refresh_commitments" -                            "    JOIN refresh_revealed_coins rrc" -                            "      USING (rc)" -                            "    JOIN payback_refresh pr" -                            "      ON (rrc.coin_ev = pr.h_blind_ev)" +                            " FROM payback_refresh"                              "    JOIN known_coins coins" -                            "      ON (coins.coin_pub = pr.coin_pub)" -                            " WHERE old_coin_pub=$1" +                            "      USING (coin_pub)" +                            " WHERE h_blind_ev IN" +                            "   (SELECT rrc.h_coin_ev" +                            "    FROM refresh_commitments" +                            "       JOIN refresh_revealed_coins rrc" +                            "           USING (rc)" +                            "    WHERE old_coin_pub=$1)"                              " FOR UPDATE;",                              1),      /* Used in #postgres_get_reserve_history() */ @@ -4821,9 +4821,6 @@ postgres_get_coin_transactions (void *cls,      /** #TALER_EXCHANGEDB_TT_REFUND */      { "get_refunds_by_coin",        &add_coin_refund }, -    /** #TALER_EXCHANGEDB_TT_OLD_COIN_PAYBACK */ -    { "payback_by_old_coin", -      &add_old_coin_payback },      { NULL, NULL }    };    static const struct Work work_wp[] = { diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h index 5767a73b..cd99a40f 100644 --- a/src/include/taler_error_codes.h +++ b/src/include/taler_error_codes.h @@ -523,6 +523,12 @@ enum TALER_ErrorCode     */    TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1308, +  /** +   * The denomination of the given coin has past its expiration date and it is +   * also not a valid zombie (that is, was not refreshed with the fresh coin +   * being subjected to payback). +   */ +  TALER_EC_REFRESH_MELT_COIN_EXPIRED_NO_ZOMBIE = 1309,    /**     * The provided transfer keys do not match up with the @@ -624,7 +630,7 @@ enum TALER_ErrorCode     */    TALER_EC_REFRESH_REVEAL_LINK_SIGNATURE_INVALID = 1362, -   +    /**     * The coin specified in the link request is unknown to the exchange.     * This response is provided with HTTP status code diff --git a/src/lib/test_exchange_api_revocation.c b/src/lib/test_exchange_api_revocation.c index 090b38b2..bd1d91c6 100644 --- a/src/lib/test_exchange_api_revocation.c +++ b/src/lib/test_exchange_api_revocation.c @@ -159,16 +159,27 @@ run (void *cls,                                "refresh-melt-1",                                CONFIG_FILE),      /* Refund coin to original coin */ -    TALER_TESTING_cmd_payback ("payback-1", +    TALER_TESTING_cmd_payback ("payback-1a",                                 MHD_HTTP_OK, -                               "refresh-reveal-1", -                               "EUR:5", +                               "refresh-reveal-1#0", +                               "EUR:1",                                 "refresh-melt-1"), -    /** -     * Melt original coin AGAIN -     * (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */ +    TALER_TESTING_cmd_payback ("payback-1b", +                               MHD_HTTP_OK, +                               "refresh-reveal-1#1", +                               "EUR:1", +                               "refresh-melt-1"), +    TALER_TESTING_cmd_payback ("payback-1c", +                               MHD_HTTP_OK, +                               "refresh-reveal-1#2", +                               "EUR:1", +                               "refresh-melt-1"), +    /* Melt original coin AGAIN (FIXME: this command +       is simply WRONG as it neither matches +       the EUR:3 that were paid back NOR is melt_double +       precisely right here!) -- it always tries to MELT EUR:4, which is too much! */      TALER_TESTING_cmd_refresh_melt_double -      ("refresh-melt-2", "EUR:4", +      ("refresh-melt-2", "EUR:3",         "withdraw-coin-1", MHD_HTTP_OK),      /**       * Complete (successful) melt operation, and withdraw the coins @@ -190,18 +201,18 @@ run (void *cls,      TALER_TESTING_cmd_payback ("payback-2",                                 MHD_HTTP_OK,                                 "refresh-melt-2", -                               "EUR:5", +                               "EUR:1",                                 "refresh-melt-2"),      /* Refund original coin to reserve */      TALER_TESTING_cmd_payback ("payback-3",                                 MHD_HTTP_OK,                                 "withdraw-coin-1", -                               "EUR:5", +                               "EUR:1",                                 NULL),      /* Check the money is back with the reserve */      TALER_TESTING_cmd_status ("payback-reserve-status-1",                                "create-reserve-1", -                              "EUR:4.0", +                              "EUR:1.0",                                MHD_HTTP_OK),      TALER_TESTING_cmd_end ()    }; diff --git a/src/lib/testing_api_cmd_payback.c b/src/lib/testing_api_cmd_payback.c index c7ea5091..de394fe2 100644 --- a/src/lib/testing_api_cmd_payback.c +++ b/src/lib/testing_api_cmd_payback.c @@ -107,6 +107,51 @@ struct PaybackState  }; + +/** + * Parser reference to a coin. + * + * @param coin_reference of format $LABEL['#' $INDEX]? + * @param cref[out] where we return a copy of $LABEL + * @param idx[out] where we set $INDEX + * @return #GNUNET_SYSERR if $INDEX is present but not numeric + */ +static int +parse_coin_reference (const char *coin_reference, +                      char **cref, +                      unsigned int *idx) +{ +  const char *index; + +  /* We allow command references of the form "$LABEL#$INDEX" or +     just "$LABEL", which implies the index is 0. Figure out +     which one it is. */ +  index = strchr (coin_reference, '#'); +  if (NULL == index) +  { +    *idx = 0; +    *cref = GNUNET_strdup (coin_reference); +    return GNUNET_OK; +  } +  *cref = GNUNET_strndup (coin_reference, +                          index - coin_reference); +  if (1 != sscanf (index + 1, +                   "%u", +                   idx)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Numeric index (not `%s') required after `#' in command reference of command in %s:%u\n", +                index, +                __FILE__, +                __LINE__); +    GNUNET_free (*cref); +    *cref = NULL; +    return GNUNET_SYSERR; +  } +  return GNUNET_OK; +} + +  /**   * Check the result of the payback request: checks whether   * the HTTP response code is good, and that the coin that @@ -137,7 +182,6 @@ payback_cb (void *cls,    struct TALER_TESTING_Command *cmd = &is->commands[is->ip];    const struct TALER_TESTING_Command *reserve_cmd;    char *cref; -  const char *index;    unsigned int idx;    ps->ph = NULL; @@ -155,34 +199,15 @@ payback_cb (void *cls,      return;    } -  /* We allow command references of the form "$LABEL#$INDEX" or -     just "$LABEL", which implies the index is 0. Figure out -     which one it is. */ -  index = strchr (ps->coin_reference, '#'); -  if (NULL == index) +  if (GNUNET_OK != +      parse_coin_reference (ps->coin_reference, +                            &cref, +                            &idx))    { -    idx = 0; -    cref = GNUNET_strdup (ps->coin_reference); -  } -  else -  { -    cref = GNUNET_strndup (ps->coin_reference, -                           index - ps->coin_reference); -    if (1 != sscanf (index, -                     "%u", -                     &idx)) -    { -      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -                  "Numeric index (not `%s') required after `#' in command reference of command %s in %s:%u\n", -                  index, -                  cmd->label, -                  __FILE__, -                  __LINE__); -      TALER_TESTING_interpreter_fail (is); -      GNUNET_free (cref); -      return; -    } +    TALER_TESTING_interpreter_fail (is); +    return;    } +    reserve_cmd = TALER_TESTING_interpreter_lookup_command      (is, cref);    GNUNET_free (cref); @@ -309,10 +334,22 @@ payback_run (void *cls,    const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;    const struct TALER_DenominationSignature *coin_sig;    struct TALER_PlanchetSecretsP planchet; +  char *cref; +  unsigned int idx;    ps->is = is; +  if (GNUNET_OK != +      parse_coin_reference (ps->coin_reference, +                            &cref, +                            &idx)) +  { +    TALER_TESTING_interpreter_fail (is); +    return; +  } +    coin_cmd = TALER_TESTING_interpreter_lookup_command -    (is, ps->coin_reference); +    (is, cref); +  GNUNET_free (cref);    if (NULL == coin_cmd)    { @@ -322,7 +359,7 @@ payback_run (void *cls,    }    if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv -    (coin_cmd, 0, &coin_priv)) +    (coin_cmd, idx, &coin_priv))    {      GNUNET_break (0);      TALER_TESTING_interpreter_fail (is); @@ -330,7 +367,7 @@ payback_run (void *cls,    }    if (GNUNET_OK != TALER_TESTING_get_trait_blinding_key -    (coin_cmd, 0, &blinding_key)) +    (coin_cmd, idx, &blinding_key))    {      GNUNET_break (0);      TALER_TESTING_interpreter_fail (is); @@ -340,7 +377,7 @@ payback_run (void *cls,    planchet.blinding_key = *blinding_key;    if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub -    (coin_cmd, 0, &denom_pub)) +    (coin_cmd, idx, &denom_pub))    {      GNUNET_break (0);      TALER_TESTING_interpreter_fail (is); @@ -348,7 +385,7 @@ payback_run (void *cls,    }    if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig -     (coin_cmd, 0, &coin_sig)) +     (coin_cmd, idx, &coin_sig))    {      GNUNET_break (0);      TALER_TESTING_interpreter_fail (is);  | 
