diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-09-05 13:13:19 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-09-05 13:13:19 +0200 |
commit | 6f8c2241fcd836bad836861c4b3eb7d43504a780 (patch) | |
tree | ce75d371761d655a8eb040b4e620267a8c116c45 /src/backenddb | |
parent | c0f992290a61e67f08e720649673951ef86638b5 (diff) | |
download | merchant-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.sql | 3 | ||||
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 13 | ||||
-rw-r--r-- | src/backenddb/test_merchantdb.c | 46 |
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, |