gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant] branch master updated: expand DB API to persist /keys


From: gnunet
Subject: [taler-merchant] branch master updated: expand DB API to persist /keys
Date: Tue, 11 Jul 2023 07:20:24 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new 6299ac5c expand DB API to persist /keys
6299ac5c is described below

commit 6299ac5c753a1f58e8729dd6cb19811844cea337
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Tue Jul 11 07:20:15 2023 +0200

    expand DB API to persist /keys
---
 src/backenddb/Makefile.am                  |  2 +
 src/backenddb/merchant-0001.sql            |  2 +-
 src/backenddb/pg_insert_exchange_keys.c    | 67 +++++++++++++++++++++
 src/backenddb/pg_insert_exchange_keys.h    | 40 +++++++++++++
 src/backenddb/pg_select_exchange_keys.c    | 63 ++++++++++++++++++++
 src/backenddb/pg_select_exchange_keys.h    | 43 ++++++++++++++
 src/backenddb/plugin_merchantdb_postgres.c | 83 ++++++++++++++------------
 src/include/taler_merchantdb_plugin.h      | 94 +++++++++++++++++++-----------
 8 files changed, 321 insertions(+), 73 deletions(-)

diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am
index 745e33eb..240b1cc1 100644
--- a/src/backenddb/Makefile.am
+++ b/src/backenddb/Makefile.am
@@ -81,6 +81,8 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \
   pg_insert_product.h pg_insert_product.c \
   pg_update_product.h pg_update_product.c \
   pg_lock_product.h pg_lock_product.c \
+  pg_insert_exchange_keys.h pg_insert_exchange_keys.c \
+  pg_select_exchange_keys.h pg_select_exchange_keys.c \
   pg_expire_locks.h pg_expire_locks.c \
   pg_delete_order.h pg_delete_order.c \
   pg_lookup_order.h pg_lookup_order.c \
diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql
index 0aa97f73..313d8ee9 100644
--- a/src/backenddb/merchant-0001.sql
+++ b/src/backenddb/merchant-0001.sql
@@ -763,7 +763,7 @@ COMMENT ON COLUMN 
merchant_exchange_accounts.credit_restrictions
 
 CREATE TABLE IF NOT EXISTS merchant_exchange_keys
   (mek_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
-  ,master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)
+  ,exchange_url VARCHAR PRIMARY KEY CHECK
   ,keys_json VARCHAR NOT NULL
   ,expiration_time INT8 NOT NULL
   );
diff --git a/src/backenddb/pg_insert_exchange_keys.c 
b/src/backenddb/pg_insert_exchange_keys.c
new file mode 100644
index 00000000..a38c8359
--- /dev/null
+++ b/src/backenddb/pg_insert_exchange_keys.c
@@ -0,0 +1,67 @@
+/*
+   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 backenddb/pg_insert_exchange_keys.c
+ * @brief Implementation of the insert_exchange_keys function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_insert_exchange_keys.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_exchange_keys (void *cls,
+                             const struct TALER_EXCHANGE_Keys *keys)
+{
+  struct PostgresClosure *pg = cls;
+  json_t *jkeys = TALER_EXCHANGE_keys_to_json (keys);
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (keys->exchange_url),
+    GNUNET_PQ_query_param_timestamp (&keys->last_denom_issue_date),
+    TALER_PQ_query_param_json (jkeys),
+    GNUNET_PQ_query_param_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  PREPARE (pg,
+           "insert_exchange_keys",
+           "INSERT INTO merchant_exchange_keys"
+           "(keys_json"
+           ",expiration_time"
+           ",exchange_url"
+           ") VALUES ($1, $2, $3);");
+  PREPARE (pg,
+           "update_exchange_keys",
+           "UPDATE merchant_exchange_keys SET"
+           " keys_json=$1"
+           ",expiration_time=$2"
+           " WHERE"
+           " exchange_url=$3;");
+  qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                           "update_exchange_keys",
+                                           params);
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "insert_exchange_keys",
+                                             params);
+  json_decref (jkeys);
+  return qs;
+}
diff --git a/src/backenddb/pg_insert_exchange_keys.h 
b/src/backenddb/pg_insert_exchange_keys.h
new file mode 100644
index 00000000..6a658c88
--- /dev/null
+++ b/src/backenddb/pg_insert_exchange_keys.h
@@ -0,0 +1,40 @@
+/*
+   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 backenddb/pg_insert_exchange_keys.h
+ * @brief implementation of the insert_exchange_keys function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_EXCHANGE_KEYS_H
+#define PG_INSERT_EXCHANGE_KEYS_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Insert or update @a keys into the database.
+ *
+ * @param cls plugin closure
+ * @param keys data to store
+ * @return transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_exchange_keys (void *cls,
+                             const struct TALER_EXCHANGE_Keys *keys);
+
+#endif
diff --git a/src/backenddb/pg_select_exchange_keys.c 
b/src/backenddb/pg_select_exchange_keys.c
new file mode 100644
index 00000000..9b9aaf60
--- /dev/null
+++ b/src/backenddb/pg_select_exchange_keys.c
@@ -0,0 +1,63 @@
+/*
+   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 backenddb/pg_select_exchange_keys.c
+ * @brief Implementation of the select_exchange_keys function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_select_exchange_keys.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_select_exchange_keys (void *cls,
+                             const char *exchange_url,
+                             struct TALER_EXCHANGE_Keys **keys)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (exchange_url),
+    GNUNET_PQ_query_param_end
+  };
+  json_t *jkeys;
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    TALER_PQ_result_spec_json ("keys",
+                               &jkeys),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  PREPARE (pg,
+           "select_exchange_keys",
+           "SELECT"
+           " keys_json"
+           " FROM merchant_exchange_keys"
+           " WHERE exchange_url=$1;");
+  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                 "select_exchange_keys",
+                                                 params,
+                                                 rs);
+  if (qs <= 0)
+    return qs;
+  *keys = TALER_EXCHANGE_keys_from_json (jkeys);
+  json_decref (jkeys);
+  return qs;
+}
diff --git a/src/backenddb/pg_select_exchange_keys.h 
b/src/backenddb/pg_select_exchange_keys.h
new file mode 100644
index 00000000..e2cd0be7
--- /dev/null
+++ b/src/backenddb/pg_select_exchange_keys.h
@@ -0,0 +1,43 @@
+/*
+   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 backenddb/pg_select_exchange_keys.h
+ * @brief implementation of the select_exchange_keys function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_SELECT_EXCHANGE_KEYS_H
+#define PG_SELECT_EXCHANGE_KEYS_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Retrieve exchange's keys from the database.
+ *
+ * @param cls plugin closure
+ * @param exchange_url base URL of the exchange
+ * @param[out] keys set to the keys of the exchange
+ * @return transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_select_exchange_keys (void *cls,
+                             const char *exchange_url,
+                             struct TALER_EXCHANGE_Keys **keys);
+
+
+#endif
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index cac8c982..975fae93 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -82,6 +82,8 @@
 #include "pg_lookup_order_status_by_serial.h"
 #include "pg_lookup_payment_status.h"
 #include "pg_set_transfer_status_to_confirmed.h"
+#include "pg_insert_exchange_keys.h"
+#include "pg_select_exchange_keys.h"
 
 
 /**
@@ -2581,8 +2583,8 @@ struct LookupRewardsContext
  */
 static void
 lookup_reserve_rewards_cb (void *cls,
-                        PGresult *result,
-                        unsigned int num_results)
+                           PGresult *result,
+                           unsigned int num_results)
 {
   struct LookupRewardsContext *ltc = cls;
   struct PostgresClosure *pg = ltc->pg;
@@ -2847,8 +2849,8 @@ struct LookupReserveForRewardContext
  */
 static void
 lookup_reserve_for_reward_cb (void *cls,
-                           PGresult *result,
-                           unsigned int num_results)
+                              PGresult *result,
+                              unsigned int num_results)
 {
   struct LookupReserveForRewardContext *lac = cls;
   struct PostgresClosure *pg = lac->pg;
@@ -2896,7 +2898,8 @@ lookup_reserve_for_reward_cb (void *cls,
       /* insufficient balance */
       if (lac->ok)
         continue;  /* got another reserve */
-      lac->ec = 
TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_INSUFFICIENT_FUNDS;
+      lac->ec =
+        TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_INSUFFICIENT_FUNDS;
       continue;
     }
     if ( (! GNUNET_TIME_absolute_is_never (lac->expiration.abs_time)) &&
@@ -2948,13 +2951,13 @@ lookup_reserve_for_reward_cb (void *cls,
  */
 static enum TALER_ErrorCode
 postgres_authorize_reward (void *cls,
-                        const char *instance_id,
-                        const struct TALER_ReservePublicKeyP *reserve_pub,
-                        const struct TALER_Amount *amount,
-                        const char *justification,
-                        const char *next_url,
-                        struct TALER_RewardIdentifierP *reward_id,
-                        struct GNUNET_TIME_Timestamp *expiration)
+                           const char *instance_id,
+                           const struct TALER_ReservePublicKeyP *reserve_pub,
+                           const struct TALER_Amount *amount,
+                           const char *justification,
+                           const char *next_url,
+                           struct TALER_RewardIdentifierP *reward_id,
+                           struct GNUNET_TIME_Timestamp *expiration)
 {
   struct PostgresClosure *pg = cls;
   unsigned int retries = 0;
@@ -3304,14 +3307,14 @@ postgres_lookup_pickup (void *cls,
  */
 static enum GNUNET_DB_QueryStatus
 postgres_lookup_reward (void *cls,
-                     const char *instance_id,
-                     const struct TALER_RewardIdentifierP *reward_id,
-                     struct TALER_Amount *total_authorized,
-                     struct TALER_Amount *total_picked_up,
-                     struct GNUNET_TIME_Timestamp *expiration,
-                     char **exchange_url,
-                     char **next_url,
-                     struct TALER_ReservePrivateKeyP *reserve_priv)
+                        const char *instance_id,
+                        const struct TALER_RewardIdentifierP *reward_id,
+                        struct TALER_Amount *total_authorized,
+                        struct TALER_Amount *total_picked_up,
+                        struct GNUNET_TIME_Timestamp *expiration,
+                        char **exchange_url,
+                        char **next_url,
+                        struct TALER_ReservePrivateKeyP *reserve_priv)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
@@ -3379,8 +3382,8 @@ struct LookupMerchantRewardsContext
  */
 static void
 lookup_rewards_cb (void *cls,
-                PGresult *result,
-                unsigned int num_results)
+                   PGresult *result,
+                   unsigned int num_results)
 {
   struct LookupMerchantRewardsContext *plc = cls;
   struct PostgresClosure *pg = plc->pg;
@@ -3433,12 +3436,12 @@ lookup_rewards_cb (void *cls,
  */
 static enum GNUNET_DB_QueryStatus
 postgres_lookup_rewards (void *cls,
-                      const char *instance_id,
-                      enum TALER_EXCHANGE_YesNoAll expired,
-                      int64_t limit,
-                      uint64_t offset,
-                      TALER_MERCHANTDB_RewardsCallback cb,
-                      void *cb_cls)
+                         const char *instance_id,
+                         enum TALER_EXCHANGE_YesNoAll expired,
+                         int64_t limit,
+                         uint64_t offset,
+                         TALER_MERCHANTDB_RewardsCallback cb,
+                         void *cb_cls)
 {
   struct PostgresClosure *pg = cls;
   struct LookupMerchantRewardsContext plc = {
@@ -3574,16 +3577,16 @@ lookup_pickup_details_cb (void *cls,
  */
 static enum GNUNET_DB_QueryStatus
 postgres_lookup_reward_details (void *cls,
-                             const char *instance_id,
-                             const struct TALER_RewardIdentifierP *reward_id,
-                             bool fpu,
-                             struct TALER_Amount *total_authorized,
-                             struct TALER_Amount *total_picked_up,
-                             char **justification,
-                             struct GNUNET_TIME_Timestamp *expiration,
-                             struct TALER_ReservePublicKeyP *reserve_pub,
-                             unsigned int *pickups_length,
-                             struct TALER_MERCHANTDB_PickupDetails **pickups)
+                                const char *instance_id,
+                                const struct TALER_RewardIdentifierP 
*reward_id,
+                                bool fpu,
+                                struct TALER_Amount *total_authorized,
+                                struct TALER_Amount *total_picked_up,
+                                char **justification,
+                                struct GNUNET_TIME_Timestamp *expiration,
+                                struct TALER_ReservePublicKeyP *reserve_pub,
+                                unsigned int *pickups_length,
+                                struct TALER_MERCHANTDB_PickupDetails 
**pickups)
 {
   struct PostgresClosure *pg = cls;
   uint64_t reward_serial;
@@ -5946,6 +5949,10 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
     &postgres_insert_pickup_blind_signature;
   plugin->select_open_transfers
     = &TMH_PG_select_open_transfers;
+  plugin->insert_exchange_keys
+    = &TMH_PG_insert_exchange_keys;
+  plugin->select_exchange_keys
+    = &TMH_PG_select_exchange_keys;
   plugin->lookup_templates = &postgres_lookup_templates;
   plugin->lookup_template = &postgres_lookup_template;
   plugin->delete_template = &postgres_delete_template;
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index 54247d4c..81f9804b 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -956,9 +956,9 @@ typedef void
  */
 typedef void
 (*TALER_MERCHANTDB_RewardsCallback)(void *cls,
-                                 uint64_t row_id,
-                                 struct TALER_RewardIdentifierP reward_id,
-                                 struct TALER_Amount amount);
+                                    uint64_t row_id,
+                                    struct TALER_RewardIdentifierP reward_id,
+                                    struct TALER_Amount amount);
 
 
 /**
@@ -2666,13 +2666,13 @@ struct TALER_MERCHANTDB_Plugin
    */
   enum TALER_ErrorCode
   (*authorize_reward)(void *cls,
-                   const char *instance_id,
-                   const struct TALER_ReservePublicKeyP *reserve_pub,
-                   const struct TALER_Amount *amount,
-                   const char *justification,
-                   const char *next_url,
-                   struct TALER_RewardIdentifierP *reward_id,
-                   struct GNUNET_TIME_Timestamp *expiration);
+                      const char *instance_id,
+                      const struct TALER_ReservePublicKeyP *reserve_pub,
+                      const struct TALER_Amount *amount,
+                      const char *justification,
+                      const char *next_url,
+                      struct TALER_RewardIdentifierP *reward_id,
+                      struct GNUNET_TIME_Timestamp *expiration);
 
 
   /**
@@ -2716,14 +2716,14 @@ struct TALER_MERCHANTDB_Plugin
    */
   enum GNUNET_DB_QueryStatus
   (*lookup_reward)(void *cls,
-                const char *instance_id,
-                const struct TALER_RewardIdentifierP *reward_id,
-                struct TALER_Amount *total_authorized,
-                struct TALER_Amount *total_picked_up,
-                struct GNUNET_TIME_Timestamp *expiration,
-                char **exchange_url,
-                char **next_url,
-                struct TALER_ReservePrivateKeyP *reserve_priv);
+                   const char *instance_id,
+                   const struct TALER_RewardIdentifierP *reward_id,
+                   struct TALER_Amount *total_authorized,
+                   struct TALER_Amount *total_picked_up,
+                   struct GNUNET_TIME_Timestamp *expiration,
+                   char **exchange_url,
+                   char **next_url,
+                   struct TALER_ReservePrivateKeyP *reserve_priv);
 
 
   /**
@@ -2741,12 +2741,12 @@ struct TALER_MERCHANTDB_Plugin
    */
   enum GNUNET_DB_QueryStatus
   (*lookup_rewards)(void *cls,
-                 const char *instance_id,
-                 enum TALER_EXCHANGE_YesNoAll expired,
-                 int64_t limit,
-                 uint64_t offset,
-                 TALER_MERCHANTDB_RewardsCallback cb,
-                 void *cb_cls);
+                    const char *instance_id,
+                    enum TALER_EXCHANGE_YesNoAll expired,
+                    int64_t limit,
+                    uint64_t offset,
+                    TALER_MERCHANTDB_RewardsCallback cb,
+                    void *cb_cls);
 
 
   /**
@@ -2767,16 +2767,16 @@ struct TALER_MERCHANTDB_Plugin
    */
   enum GNUNET_DB_QueryStatus
   (*lookup_reward_details)(void *cls,
-                        const char *instance_id,
-                        const struct TALER_RewardIdentifierP *reward_id,
-                        bool fpu,
-                        struct TALER_Amount *total_authorized,
-                        struct TALER_Amount *total_picked_up,
-                        char **justification,
-                        struct GNUNET_TIME_Timestamp *expiration,
-                        struct TALER_ReservePublicKeyP *reserve_pub,
-                        unsigned int *pickups_length,
-                        struct TALER_MERCHANTDB_PickupDetails **pickups);
+                           const char *instance_id,
+                           const struct TALER_RewardIdentifierP *reward_id,
+                           bool fpu,
+                           struct TALER_Amount *total_authorized,
+                           struct TALER_Amount *total_picked_up,
+                           char **justification,
+                           struct GNUNET_TIME_Timestamp *expiration,
+                           struct TALER_ReservePublicKeyP *reserve_pub,
+                           unsigned int *pickups_length,
+                           struct TALER_MERCHANTDB_PickupDetails **pickups);
 
 
   /**
@@ -3100,6 +3100,32 @@ struct TALER_MERCHANTDB_Plugin
   (*delete_pending_webhook)(void *cls,
                             uint64_t webhook_pending_serial);
 
+
+  /**
+   * Retrieve exchange's keys from the database.
+   *
+   * @param cls plugin closure
+   * @param exchange_url base URL of the exchange
+   * @param[out] keys set to the keys of the exchange
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*select_exchange_keys)(void *cls,
+                          const char *exchange_url,
+                          struct TALER_EXCHANGE_Keys **keys);
+
+
+  /**
+   * Insert or update @a keys into the database.
+   *
+   * @param cls plugin closure
+   * @param keys data to store
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*insert_exchange_keys)(void *cls,
+                          const struct TALER_EXCHANGE_Keys *keys);
+
 };
 
 #endif

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]