aboutsummaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_deposit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange/taler-exchange-httpd_deposit.c')
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c173
1 files changed, 95 insertions, 78 deletions
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index 65251863a..fe8fdf061 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -47,7 +47,7 @@
47 * @param coin_pub public key of the coin 47 * @param coin_pub public key of the coin
48 * @param h_wire hash of wire details 48 * @param h_wire hash of wire details
49 * @param h_contract_terms hash of contract details 49 * @param h_contract_terms hash of contract details
50 * @param timestamp client's timestamp 50 * @param exchange_timestamp exchange's timestamp
51 * @param refund_deadline until when this deposit be refunded 51 * @param refund_deadline until when this deposit be refunded
52 * @param merchant merchant public key 52 * @param merchant merchant public key
53 * @param amount_without_fee fraction of coin value to deposit, without the fee 53 * @param amount_without_fee fraction of coin value to deposit, without the fee
@@ -58,7 +58,7 @@ reply_deposit_success (struct MHD_Connection *connection,
58 const struct TALER_CoinSpendPublicKeyP *coin_pub, 58 const struct TALER_CoinSpendPublicKeyP *coin_pub,
59 const struct GNUNET_HashCode *h_wire, 59 const struct GNUNET_HashCode *h_wire,
60 const struct GNUNET_HashCode *h_contract_terms, 60 const struct GNUNET_HashCode *h_contract_terms,
61 struct GNUNET_TIME_Absolute timestamp, 61 struct GNUNET_TIME_Absolute exchange_timestamp,
62 struct GNUNET_TIME_Absolute refund_deadline, 62 struct GNUNET_TIME_Absolute refund_deadline,
63 const struct TALER_MerchantPublicKeyP *merchant, 63 const struct TALER_MerchantPublicKeyP *merchant,
64 const struct TALER_Amount *amount_without_fee) 64 const struct TALER_Amount *amount_without_fee)
@@ -70,7 +70,7 @@ reply_deposit_success (struct MHD_Connection *connection,
70 .purpose.size = htonl (sizeof (dc)), 70 .purpose.size = htonl (sizeof (dc)),
71 .h_contract_terms = *h_contract_terms, 71 .h_contract_terms = *h_contract_terms,
72 .h_wire = *h_wire, 72 .h_wire = *h_wire,
73 .timestamp = GNUNET_TIME_absolute_hton (timestamp), 73 .exchange_timestamp = GNUNET_TIME_absolute_hton (exchange_timestamp),
74 .refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline), 74 .refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline),
75 .coin_pub = *coin_pub, 75 .coin_pub = *coin_pub,
76 .merchant = *merchant 76 .merchant = *merchant
@@ -88,13 +88,16 @@ reply_deposit_success (struct MHD_Connection *connection,
88 TALER_EC_EXCHANGE_BAD_CONFIGURATION, 88 TALER_EC_EXCHANGE_BAD_CONFIGURATION,
89 "no keys"); 89 "no keys");
90 } 90 }
91 return TALER_MHD_reply_json_pack (connection, 91 return TALER_MHD_reply_json_pack (
92 MHD_HTTP_OK, 92 connection,
93 "{s:o, s:o}", 93 MHD_HTTP_OK,
94 "exchange_sig", 94 "{s:o, s:o, s:o}",
95 GNUNET_JSON_from_data_auto (&sig), 95 "exchange_timestamp",
96 "exchange_pub", 96 GNUNET_JSON_from_time_abs (exchange_timestamp),
97 GNUNET_JSON_from_data_auto (&pub)); 97 "exchange_sig",
98 GNUNET_JSON_from_data_auto (&sig),
99 "exchange_pub",
100 GNUNET_JSON_from_data_auto (&pub));
98} 101}
99 102
100 103
@@ -109,6 +112,11 @@ struct DepositContext
109 const struct TALER_EXCHANGEDB_Deposit *deposit; 112 const struct TALER_EXCHANGEDB_Deposit *deposit;
110 113
111 /** 114 /**
115 * Our timestamp (when we received the request).
116 */
117 struct GNUNET_TIME_Absolute exchange_timestamp;
118
119 /**
112 * Value of the coin. 120 * Value of the coin.
113 */ 121 */
114 struct TALER_Amount value; 122 struct TALER_Amount value;
@@ -117,12 +125,11 @@ struct DepositContext
117 125
118 126
119/** 127/**
120 * Execute database transaction for /deposit. Runs the transaction 128 * Check if /deposit is already in the database. IF it returns a non-error
121 * logic; IF it returns a non-error code, the transaction logic MUST 129 * code, the transaction logic MUST NOT queue a MHD response. IF it returns
122 * NOT queue a MHD response. IF it returns an hard error, the 130 * an hard error, the transaction logic MUST queue a MHD response and set @a
123 * transaction logic MUST queue a MHD response and set @a mhd_ret. IF 131 * mhd_ret. We do return a "hard" error also if we found the deposit in the
124 * it returns the soft error code, the function MAY be called again to 132 * database and generated a regular response.
125 * retry and MUST not queue a MHD response.
126 * 133 *
127 * @param cls a `struct DepositContext` 134 * @param cls a `struct DepositContext`
128 * @param connection MHD request context 135 * @param connection MHD request context
@@ -131,20 +138,22 @@ struct DepositContext
131 * @return transaction status 138 * @return transaction status
132 */ 139 */
133static enum GNUNET_DB_QueryStatus 140static enum GNUNET_DB_QueryStatus
134deposit_transaction (void *cls, 141deposit_precheck (void *cls,
135 struct MHD_Connection *connection, 142 struct MHD_Connection *connection,
136 struct TALER_EXCHANGEDB_Session *session, 143 struct TALER_EXCHANGEDB_Session *session,
137 MHD_RESULT *mhd_ret) 144 MHD_RESULT *mhd_ret)
138{ 145{
139 struct DepositContext *dc = cls; 146 struct DepositContext *dc = cls;
140 const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit; 147 const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit;
141 struct TALER_Amount spent; 148 struct TALER_Amount deposit_fee;
142 enum GNUNET_DB_QueryStatus qs; 149 enum GNUNET_DB_QueryStatus qs;
143 150
144 qs = TEH_plugin->have_deposit (TEH_plugin->cls, 151 qs = TEH_plugin->have_deposit (TEH_plugin->cls,
145 session, 152 session,
146 deposit, 153 deposit,
147 GNUNET_YES /* check refund deadline */); 154 GNUNET_YES /* check refund deadline */,
155 &deposit_fee,
156 &dc->exchange_timestamp);
148 if (qs < 0) 157 if (qs < 0)
149 { 158 {
150 if (GNUNET_DB_STATUS_HARD_ERROR == qs) 159 if (GNUNET_DB_STATUS_HARD_ERROR == qs)
@@ -166,12 +175,12 @@ deposit_transaction (void *cls,
166 GNUNET_assert (0 <= 175 GNUNET_assert (0 <=
167 TALER_amount_subtract (&amount_without_fee, 176 TALER_amount_subtract (&amount_without_fee,
168 &deposit->amount_with_fee, 177 &deposit->amount_with_fee,
169 &deposit->deposit_fee)); 178 &deposit_fee));
170 *mhd_ret = reply_deposit_success (connection, 179 *mhd_ret = reply_deposit_success (connection,
171 &deposit->coin.coin_pub, 180 &deposit->coin.coin_pub,
172 &deposit->h_wire, 181 &deposit->h_wire,
173 &deposit->h_contract_terms, 182 &deposit->h_contract_terms,
174 deposit->timestamp, 183 dc->exchange_timestamp,
175 deposit->refund_deadline, 184 deposit->refund_deadline,
176 &deposit->merchant_pub, 185 &deposit->merchant_pub,
177 &amount_without_fee); 186 &amount_without_fee);
@@ -179,6 +188,44 @@ deposit_transaction (void *cls,
179 never try again. */ 188 never try again. */
180 return GNUNET_DB_STATUS_HARD_ERROR; 189 return GNUNET_DB_STATUS_HARD_ERROR;
181 } 190 }
191 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
192}
193
194
195/**
196 * Execute database transaction for /deposit. Runs the transaction
197 * logic; IF it returns a non-error code, the transaction logic MUST
198 * NOT queue a MHD response. IF it returns an hard error, the
199 * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
200 * it returns the soft error code, the function MAY be called again to
201 * retry and MUST not queue a MHD response.
202 *
203 * @param cls a `struct DepositContext`
204 * @param connection MHD request context
205 * @param session database session and transaction to use
206 * @param[out] mhd_ret set to MHD status on error
207 * @return transaction status
208 */
209static enum GNUNET_DB_QueryStatus
210deposit_transaction (void *cls,
211 struct MHD_Connection *connection,
212 struct TALER_EXCHANGEDB_Session *session,
213 MHD_RESULT *mhd_ret)
214{
215 struct DepositContext *dc = cls;
216 const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit;
217 struct TALER_Amount spent;
218 enum GNUNET_DB_QueryStatus qs;
219
220 /* Theoretically, someone other threat may have received
221 and committed the deposit in the meantime. Check now
222 that we are in the transaction scope. */
223 qs = deposit_precheck (cls,
224 connection,
225 session,
226 mhd_ret);
227 if (qs < 0)
228 return qs;
182 229
183 /* Start with fee for THIS transaction */ 230 /* Start with fee for THIS transaction */
184 spent = deposit->amount_with_fee; 231 spent = deposit->amount_with_fee;
@@ -238,6 +285,7 @@ deposit_transaction (void *cls,
238 } 285 }
239 qs = TEH_plugin->insert_deposit (TEH_plugin->cls, 286 qs = TEH_plugin->insert_deposit (TEH_plugin->cls,
240 session, 287 session,
288 dc->exchange_timestamp,
241 deposit); 289 deposit);
242 if (GNUNET_DB_STATUS_HARD_ERROR == qs) 290 if (GNUNET_DB_STATUS_HARD_ERROR == qs)
243 { 291 {
@@ -252,45 +300,6 @@ deposit_transaction (void *cls,
252 300
253 301
254/** 302/**
255 * Check that @a ts is reasonably close to our own RTC.
256 *
257 * @param ts timestamp to check
258 * @return #GNUNET_OK if @a ts is reasonable
259 */
260static int
261check_timestamp_current (struct GNUNET_TIME_Absolute ts)
262{
263 struct GNUNET_TIME_Relative r;
264 struct GNUNET_TIME_Relative tolerance;
265
266 /* Let's be VERY generous (after all, this is basically about
267 which year the deposit counts for in terms of tax purposes) */
268 tolerance = GNUNET_TIME_UNIT_MONTHS;
269 r = GNUNET_TIME_absolute_get_duration (ts);
270 if (r.rel_value_us > tolerance.rel_value_us)
271 {
272 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
273 "Deposit timestamp too old: %llu vs %llu > %llu\n",
274 (unsigned long long) ts.abs_value_us,
275 (unsigned long long) GNUNET_TIME_absolute_get ().abs_value_us,
276 (unsigned long long) tolerance.rel_value_us);
277 return GNUNET_SYSERR;
278 }
279 r = GNUNET_TIME_absolute_get_remaining (ts);
280 if (r.rel_value_us > tolerance.rel_value_us)
281 {
282 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
283 "Deposit timestamp too new: %llu vs %llu < - %llu\n",
284 (unsigned long long) ts.abs_value_us,
285 (unsigned long long) GNUNET_TIME_absolute_get ().abs_value_us,
286 (unsigned long long) tolerance.rel_value_us);
287 return GNUNET_SYSERR;
288 }
289 return GNUNET_OK;
290}
291
292
293/**
294 * Handle a "/coins/$COIN_PUB/deposit" request. Parses the JSON, and, if 303 * Handle a "/coins/$COIN_PUB/deposit" request. Parses the JSON, and, if
295 * successful, passes the JSON data to #deposit_transaction() to 304 * successful, passes the JSON data to #deposit_transaction() to
296 * further check the details of the operation specified. If everything checks 305 * further check the details of the operation specified. If everything checks
@@ -367,17 +376,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
367 TALER_EC_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE, 376 TALER_EC_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE,
368 "refund_deadline"); 377 "refund_deadline");
369 } 378 }
370
371 if (GNUNET_OK !=
372 check_timestamp_current (deposit.timestamp))
373 {
374 GNUNET_break_op (0);
375 GNUNET_JSON_parse_free (spec);
376 return TALER_MHD_reply_with_error (connection,
377 MHD_HTTP_BAD_REQUEST,
378 TALER_EC_DEPOSIT_INVALID_TIMESTAMP,
379 "timestamp");
380 }
381 if (GNUNET_OK != 379 if (GNUNET_OK !=
382 TALER_JSON_merchant_wire_signature_hash (wire, 380 TALER_JSON_merchant_wire_signature_hash (wire,
383 &my_h_wire)) 381 &my_h_wire))
@@ -401,6 +399,26 @@ TEH_handler_deposit (struct MHD_Connection *connection,
401 "h_wire"); 399 "h_wire");
402 } 400 }
403 401
402 /* Check for idempotency: did we get this request before? */
403 dc.deposit = &deposit;
404 {
405 MHD_RESULT mhd_ret;
406
407 if (GNUNET_OK !=
408 TEH_DB_run_transaction (connection,
409 "precheck deposit",
410 &mhd_ret,
411 &deposit_precheck,
412 &dc))
413 {
414 GNUNET_JSON_parse_free (spec);
415 return mhd_ret;
416 }
417 }
418
419 /* new deposit */
420 dc.exchange_timestamp = GNUNET_TIME_absolute_get ();
421 (void) GNUNET_TIME_round_abs (&dc.exchange_timestamp);
404 /* check denomination exists and is valid */ 422 /* check denomination exists and is valid */
405 { 423 {
406 struct TEH_KS_StateHandle *key_state; 424 struct TEH_KS_StateHandle *key_state;
@@ -408,7 +426,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
408 enum TALER_ErrorCode ec; 426 enum TALER_ErrorCode ec;
409 unsigned int hc; 427 unsigned int hc;
410 428
411 key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); 429 key_state = TEH_KS_acquire (dc.exchange_timestamp);
412 if (NULL == key_state) 430 if (NULL == key_state)
413 { 431 {
414 TALER_LOG_ERROR ("Lacking keys to operate\n"); 432 TALER_LOG_ERROR ("Lacking keys to operate\n");
@@ -502,7 +520,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
502 .purpose.size = htonl (sizeof (dr)), 520 .purpose.size = htonl (sizeof (dr)),
503 .h_contract_terms = deposit.h_contract_terms, 521 .h_contract_terms = deposit.h_contract_terms,
504 .h_wire = deposit.h_wire, 522 .h_wire = deposit.h_wire,
505 .timestamp = GNUNET_TIME_absolute_hton (deposit.timestamp), 523 .wallet_timestamp = GNUNET_TIME_absolute_hton (deposit.timestamp),
506 .refund_deadline = GNUNET_TIME_absolute_hton (deposit.refund_deadline), 524 .refund_deadline = GNUNET_TIME_absolute_hton (deposit.refund_deadline),
507 .merchant = deposit.merchant_pub, 525 .merchant = deposit.merchant_pub,
508 .coin_pub = deposit.coin.coin_pub 526 .coin_pub = deposit.coin.coin_pub
@@ -528,7 +546,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
528 } 546 }
529 547
530 /* execute transaction */ 548 /* execute transaction */
531 dc.deposit = &deposit;
532 { 549 {
533 MHD_RESULT mhd_ret; 550 MHD_RESULT mhd_ret;
534 551
@@ -557,7 +574,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
557 &deposit.coin.coin_pub, 574 &deposit.coin.coin_pub,
558 &deposit.h_wire, 575 &deposit.h_wire,
559 &deposit.h_contract_terms, 576 &deposit.h_contract_terms,
560 deposit.timestamp, 577 dc.exchange_timestamp,
561 deposit.refund_deadline, 578 deposit.refund_deadline,
562 &deposit.merchant_pub, 579 &deposit.merchant_pub,
563 &amount_without_fee); 580 &amount_without_fee);