summaryrefslogtreecommitdiff
path: root/src/backenddb
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-09-05 13:13:19 +0200
committerChristian Grothoff <christian@grothoff.org>2020-09-05 13:13:19 +0200
commit6f8c2241fcd836bad836861c4b3eb7d43504a780 (patch)
treece75d371761d655a8eb040b4e620267a8c116c45 /src/backenddb
parentc0f992290a61e67f08e720649673951ef86638b5 (diff)
downloadmerchant-6f8c2241fcd836bad836861c4b3eb7d43504a780.tar.gz
merchant-6f8c2241fcd836bad836861c4b3eb7d43504a780.tar.bz2
merchant-6f8c2241fcd836bad836861c4b3eb7d43504a780.zip
fix idempotency issue for POST /private/orders (#6445)
Diffstat (limited to 'src/backenddb')
-rw-r--r--src/backenddb/merchant-0001.sql3
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c13
-rw-r--r--src/backenddb/test_merchantdb.c46
3 files changed, 47 insertions, 15 deletions
diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql
index 0adc495b..f75cbb28 100644
--- a/src/backenddb/merchant-0001.sql
+++ b/src/backenddb/merchant-0001.sql
@@ -190,6 +190,7 @@ CREATE TABLE IF NOT EXISTS merchant_orders
REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE
,order_id VARCHAR NOT NULL
,claim_token BYTEA NOT NULL CHECK (LENGTH(claim_token)=16)
+ ,h_post_data BYTEA NOT NULL CHECK (LENGTH(h_post_data)=64)
,pay_deadline INT8 NOT NULL
,creation_time INT8 NOT NULL
,contract_terms BYTEA NOT NULL
@@ -199,6 +200,8 @@ COMMENT ON TABLE merchant_orders
IS 'Orders we offered to a customer, but that have not yet been claimed';
COMMENT ON COLUMN merchant_orders.contract_terms
IS 'Claiming changes the contract_terms, hence we have no hash of the terms in this table';
+COMMENT ON COLUMN merchant_orders.h_post_data
+ IS 'Hash of the POST request that created this order, for idempotency checks';
COMMENT ON COLUMN merchant_orders.claim_token
IS 'Token optionally used to authorize the wallet to claim the order. All zeros (not NULL) if not used';
COMMENT ON COLUMN merchant_orders.merchant_serial
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
index ce87ecd7..b965b0e6 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -1103,6 +1103,7 @@ postgres_delete_order (void *cls,
* @param order_id order id used to perform the lookup
* @param[out] claim_token the claim token generated for the order,
* NULL to only test if the order exists
+ * @param[out] h_post_data set to the hash of the POST data that created the order
* @param[out] contract_terms where to store the retrieved contract terms,
* NULL to only test if the order exists
* @return transaction status
@@ -1112,6 +1113,7 @@ postgres_lookup_order (void *cls,
const char *instance_id,
const char *order_id,
struct TALER_ClaimTokenP *claim_token,
+ struct GNUNET_HashCode *h_post_data,
json_t **contract_terms)
{
struct PostgresClosure *pg = cls;
@@ -1128,6 +1130,8 @@ postgres_lookup_order (void *cls,
&j),
GNUNET_PQ_result_spec_auto_from_type ("claim_token",
&ct),
+ GNUNET_PQ_result_spec_auto_from_type ("h_post_data",
+ h_post_data),
GNUNET_PQ_result_spec_end
};
@@ -1340,6 +1344,7 @@ postgres_lookup_orders (void *cls,
* @param cls closure
* @param instance_id identifies the instance responsible for the order
* @param order_id alphanumeric string that uniquely identifies the proposal
+ * @param h_post_order hash of the POST data for idempotency checks
* @param pay_deadline how long does the customer have to pay for the order
* @param claim_token token to use for access control
* @param contract_terms proposal data to store
@@ -1349,6 +1354,7 @@ static enum GNUNET_DB_QueryStatus
postgres_insert_order (void *cls,
const char *instance_id,
const char *order_id,
+ const struct GNUNET_HashCode *h_post_order,
struct GNUNET_TIME_Absolute pay_deadline,
const struct TALER_ClaimTokenP *claim_token,
const json_t *contract_terms)
@@ -1360,6 +1366,7 @@ postgres_insert_order (void *cls,
GNUNET_PQ_query_param_string (order_id),
GNUNET_PQ_query_param_absolute_time (&pay_deadline),
GNUNET_PQ_query_param_auto_from_type (claim_token),
+ GNUNET_PQ_query_param_auto_from_type (h_post_order),
GNUNET_PQ_query_param_absolute_time (&now),
TALER_PQ_query_param_json (contract_terms),
GNUNET_PQ_query_param_end
@@ -6285,6 +6292,7 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
"SELECT"
" contract_terms"
",claim_token"
+ ",h_post_data"
" FROM merchant_orders"
" WHERE merchant_orders.merchant_serial="
" (SELECT merchant_serial "
@@ -7098,13 +7106,14 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
",order_id"
",pay_deadline"
",claim_token"
+ ",h_post_data"
",creation_time"
",contract_terms)"
" SELECT merchant_serial,"
- " $2, $3, $4, $5, $6"
+ " $2, $3, $4, $5, $6, $7"
" FROM merchant_instances"
" WHERE merchant_id=$1",
- 5),
+ 7),
/* for postgres_unlock_inventory() */
GNUNET_PQ_make_prepare ("unlock_inventory",
"DELETE"
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index afb1a380..e0ec18d4 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -1352,10 +1352,16 @@ test_insert_order (const struct InstanceData *instance,
const struct OrderData *order,
enum GNUNET_DB_QueryStatus expected_result)
{
+ struct GNUNET_HashCode h_post;
+
+ memset (&h_post,
+ 42,
+ sizeof (h_post));
TEST_COND_RET_ON_FAIL (expected_result ==
plugin->insert_order (plugin->cls,
instance->instance.id,
order->id,
+ &h_post,
order->pay_deadline,
&order->claim_token,
order->contract),
@@ -1377,11 +1383,18 @@ test_lookup_order (const struct InstanceData *instance,
{
struct TALER_ClaimTokenP ct;
json_t *lookup_terms = NULL;
+ struct GNUNET_HashCode oh;
+ struct GNUNET_HashCode wh;
+
+ memset (&wh,
+ 42,
+ sizeof (wh));
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->lookup_order (plugin->cls,
instance->instance.id,
order->id,
&ct,
+ &oh,
&lookup_terms))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1390,10 +1403,12 @@ test_lookup_order (const struct InstanceData *instance,
json_decref (lookup_terms);
return 1;
}
- if ((1 != json_equal (order->contract,
- lookup_terms)) ||
- (0 != GNUNET_memcmp (&order->claim_token,
- &ct)))
+ if ( (1 != json_equal (order->contract,
+ lookup_terms)) ||
+ (0 != GNUNET_memcmp (&order->claim_token,
+ &ct)) ||
+ (0 != GNUNET_memcmp (&oh,
+ &wh)) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Lookup order failed: incorrect order returned\n");
@@ -2002,16 +2017,21 @@ run_test_orders (struct TestOrders_Closure *cls)
TEST_RET_ON_FAIL (test_lookup_order (&cls->instance,
&cls->orders[0]));
/* Make sure it fails correctly for nonexistent orders */
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
- plugin->lookup_order (plugin->cls,
- cls->instance.instance.id,
- cls->orders[1].id,
- NULL,
- NULL))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Lookup order failed\n");
- return 1;
+ struct GNUNET_HashCode unused;
+
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+ plugin->lookup_order (plugin->cls,
+ cls->instance.instance.id,
+ cls->orders[1].id,
+ NULL,
+ &unused,
+ NULL))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Lookup order failed\n");
+ return 1;
+ }
}
/* Test lookups on multiple orders */
TEST_RET_ON_FAIL (test_insert_order (&cls->instance,