diff options
author | Christian Grothoff <christian@grothoff.org> | 2015-01-29 00:45:07 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2015-01-29 00:45:07 +0100 |
commit | 758ce80519f17b580807f432192eae9ff13cb418 (patch) | |
tree | 376867e94f59dd34caa0856eed104deed9b25dc3 | |
parent | 8adde040ab5c3fdc40b06537d3bab1aa60ecc5e5 (diff) | |
download | exchange-758ce80519f17b580807f432192eae9ff13cb418.tar.gz exchange-758ce80519f17b580807f432192eae9ff13cb418.zip |
clean up /withdraw/sign logic
-rw-r--r-- | src/mint/taler-mint-httpd_db.c | 170 | ||||
-rw-r--r-- | src/mint/taler-mint-httpd_responses.c | 78 | ||||
-rw-r--r-- | src/mint/taler-mint-httpd_responses.h | 14 |
3 files changed, 191 insertions, 71 deletions
diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index bf10cd29e..c3ecf0101 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c | |||
@@ -22,11 +22,6 @@ | |||
22 | * - actually abstract DB implementation (i.e. via plugin logic) | 22 | * - actually abstract DB implementation (i.e. via plugin logic) |
23 | * (this file should remain largely unchanged with the exception | 23 | * (this file should remain largely unchanged with the exception |
24 | * of the PQ-specific DB handle types) | 24 | * of the PQ-specific DB handle types) |
25 | * - /withdraw/sign: all | ||
26 | * + properly check all conditions and handle errors | ||
27 | * + properly check transaction logic | ||
28 | * + check for leaks | ||
29 | * + check low-level API | ||
30 | * - /refresh/melt: all | 25 | * - /refresh/melt: all |
31 | * + properly check all conditions and handle errors | 26 | * + properly check all conditions and handle errors |
32 | * + properly check transaction logic | 27 | * + properly check transaction logic |
@@ -264,11 +259,17 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, | |||
264 | { | 259 | { |
265 | PGconn *db_conn; | 260 | PGconn *db_conn; |
266 | struct ReserveHistory *rh; | 261 | struct ReserveHistory *rh; |
262 | const struct ReserveHistory *pos; | ||
267 | struct MintKeyState *key_state; | 263 | struct MintKeyState *key_state; |
268 | struct CollectableBlindcoin collectable; | 264 | struct CollectableBlindcoin collectable; |
269 | struct TALER_MINT_DenomKeyIssuePriv *dki; | 265 | struct TALER_MINT_DenomKeyIssuePriv *dki; |
266 | struct TALER_MINT_DenomKeyIssuePriv *tdki; | ||
270 | struct GNUNET_CRYPTO_rsa_Signature *sig; | 267 | struct GNUNET_CRYPTO_rsa_Signature *sig; |
271 | struct TALER_Amount amount_required; | 268 | struct TALER_Amount amount_required; |
269 | struct TALER_Amount deposit_total; | ||
270 | struct TALER_Amount withdraw_total; | ||
271 | struct TALER_Amount balance; | ||
272 | struct TALER_Amount value; | ||
272 | struct GNUNET_HashCode h_blind; | 273 | struct GNUNET_HashCode h_blind; |
273 | int res; | 274 | int res; |
274 | 275 | ||
@@ -299,63 +300,102 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, | |||
299 | return res; | 300 | return res; |
300 | } | 301 | } |
301 | GNUNET_assert (GNUNET_NO == res); | 302 | GNUNET_assert (GNUNET_NO == res); |
302 | rh = TALER_MINT_DB_get_reserve_history (db_conn, | ||
303 | reserve); | ||
304 | if (NULL == rh) | ||
305 | return TALER_MINT_reply_json_pack (connection, | ||
306 | MHD_HTTP_NOT_FOUND, | ||
307 | "{s:s}", | ||
308 | "error", | ||
309 | "Reserve not found"); | ||
310 | 303 | ||
304 | /* Check if balance is sufficient */ | ||
311 | key_state = TALER_MINT_key_state_acquire (); | 305 | key_state = TALER_MINT_key_state_acquire (); |
312 | dki = TALER_MINT_get_denom_key (key_state, | 306 | dki = TALER_MINT_get_denom_key (key_state, |
313 | denomination_pub); | 307 | denomination_pub); |
314 | TALER_MINT_key_state_release (key_state); | ||
315 | if (NULL == dki) | 308 | if (NULL == dki) |
309 | { | ||
310 | TALER_MINT_key_state_release (key_state); | ||
316 | return TALER_MINT_reply_json_pack (connection, | 311 | return TALER_MINT_reply_json_pack (connection, |
317 | MHD_HTTP_NOT_FOUND, | 312 | MHD_HTTP_NOT_FOUND, |
318 | "{s:s}", | 313 | "{s:s}", |
319 | "error", | 314 | "error", |
320 | "Denomination not found"); | 315 | "Denomination not found"); |
316 | } | ||
317 | if (GNUNET_OK != | ||
318 | TALER_MINT_DB_transaction (db_conn)) | ||
319 | { | ||
320 | GNUNET_break (0); | ||
321 | TALER_MINT_key_state_release (key_state); | ||
322 | return TALER_MINT_reply_internal_db_error (connection); | ||
323 | } | ||
321 | 324 | ||
322 | amount_required = TALER_amount_add (TALER_amount_ntoh (dki->issue.value), | 325 | rh = TALER_MINT_DB_get_reserve_history (db_conn, |
323 | TALER_amount_ntoh (dki->issue.fee_withdraw)); | 326 | reserve); |
324 | // FIX LOGIC! | 327 | if (NULL == rh) |
325 | #if 0 | 328 | { |
326 | if (0 < TALER_amount_cmp (amount_required, | 329 | TALER_MINT_DB_rollback (db_conn); |
327 | TALER_amount_ntoh (db_reserve.balance))) | 330 | TALER_MINT_key_state_release (key_state); |
328 | return TALER_MINT_reply_json_pack (connection, | 331 | return TALER_MINT_reply_json_pack (connection, |
329 | MHD_HTTP_PAYMENT_REQUIRED, | 332 | MHD_HTTP_NOT_FOUND, |
330 | "{s:s}", | 333 | "{s:s}", |
331 | "error", | 334 | "error", |
332 | "Insufficient funds"); | 335 | "Reserve not found"); |
336 | } | ||
337 | |||
338 | /* calculate amount required including fees */ | ||
339 | amount_required = TALER_amount_add (TALER_amount_ntoh (dki->issue.value), | ||
340 | TALER_amount_ntoh (dki->issue.fee_withdraw)); | ||
333 | 341 | ||
334 | db_reserve.balance = TALER_amount_hton | 342 | /* calculate balance of the reserve */ |
335 | (TALER_amount_subtract (TALER_amount_ntoh (db_reserve.balance), | 343 | res = 0; |
336 | amount_required)); | 344 | for (pos = rh; NULL != pos; pos = pos->next) |
345 | { | ||
346 | switch (pos->type) | ||
347 | { | ||
348 | case TALER_MINT_DB_RO_BANK_TO_MINT: | ||
349 | if (0 == (res & 1)) | ||
350 | deposit_total = pos->details.bank->amount; | ||
351 | else | ||
352 | deposit_total = TALER_amount_add (deposit_total, | ||
353 | pos->details.bank->amount); | ||
354 | res |= 1; | ||
355 | break; | ||
356 | case TALER_MINT_DB_RO_WITHDRAW_COIN: | ||
357 | tdki = TALER_MINT_get_denom_key (key_state, | ||
358 | pos->details.withdraw->denom_pub); | ||
359 | value = TALER_amount_ntoh (tdki->issue.value); | ||
360 | if (0 == (res & 2)) | ||
361 | withdraw_total = value; | ||
362 | else | ||
363 | withdraw_total = TALER_amount_add (withdraw_total, | ||
364 | value); | ||
365 | res |= 2; | ||
366 | break; | ||
367 | } | ||
368 | } | ||
337 | 369 | ||
370 | /* FIXME: good place to assert deposit_total > withdraw_total... */ | ||
371 | balance = TALER_amount_subtract (deposit_total, | ||
372 | withdraw_total); | ||
373 | if (0 < TALER_amount_cmp (amount_required, | ||
374 | balance)) | ||
375 | { | ||
376 | TALER_MINT_key_state_release (key_state); | ||
377 | TALER_MINT_DB_rollback (db_conn); | ||
378 | res = TALER_MINT_reply_withdraw_sign_insufficient_funds (connection, | ||
379 | rh); | ||
380 | TALER_MINT_DB_free_reserve_history (rh); | ||
381 | return res; | ||
382 | } | ||
383 | TALER_MINT_DB_free_reserve_history (rh); | ||
384 | |||
385 | /* Balance is good, sign the coin! */ | ||
338 | sig = GNUNET_CRYPTO_rsa_sign (dki->denom_priv, | 386 | sig = GNUNET_CRYPTO_rsa_sign (dki->denom_priv, |
339 | blinded_msg, | 387 | blinded_msg, |
340 | blinded_msg_len); | 388 | blinded_msg_len); |
389 | TALER_MINT_key_state_release (key_state); | ||
341 | if (NULL == sig) | 390 | if (NULL == sig) |
342 | { | 391 | { |
343 | GNUNET_break (0); | 392 | GNUNET_break (0); |
393 | TALER_MINT_DB_rollback (db_conn); | ||
344 | return TALER_MINT_reply_internal_error (connection, | 394 | return TALER_MINT_reply_internal_error (connection, |
345 | "Internal error"); | 395 | "Internal error"); |
346 | } | 396 | } |
347 | 397 | ||
348 | /* transaction start */ | 398 | // FIXME: can we avoid the cast? |
349 | if (GNUNET_OK != | ||
350 | TALER_MINT_DB_update_reserve (db_conn, | ||
351 | &db_reserve, | ||
352 | GNUNET_YES)) | ||
353 | { | ||
354 | GNUNET_break (0); | ||
355 | return TALER_MINT_reply_internal_db_error (connection); | ||
356 | } | ||
357 | #endif | ||
358 | |||
359 | collectable.denom_pub = (struct GNUNET_CRYPTO_rsa_PublicKey *) denomination_pub; | 399 | collectable.denom_pub = (struct GNUNET_CRYPTO_rsa_PublicKey *) denomination_pub; |
360 | collectable.sig = sig; | 400 | collectable.sig = sig; |
361 | collectable.reserve_pub = *reserve; | 401 | collectable.reserve_pub = *reserve; |
@@ -367,17 +407,27 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, | |||
367 | { | 407 | { |
368 | GNUNET_break (0); | 408 | GNUNET_break (0); |
369 | GNUNET_CRYPTO_rsa_signature_free (sig); | 409 | GNUNET_CRYPTO_rsa_signature_free (sig); |
410 | TALER_MINT_DB_rollback (db_conn); | ||
370 | return TALER_MINT_reply_internal_db_error (connection); | 411 | return TALER_MINT_reply_internal_db_error (connection); |
371 | } | 412 | } |
372 | /* transaction end */ | 413 | if (GNUNET_OK != |
414 | TALER_MINT_DB_commit (db_conn)) | ||
415 | { | ||
416 | LOG_WARNING ("/withdraw/sign transaction commit failed\n"); | ||
417 | return TALER_MINT_reply_commit_error (connection); | ||
418 | } | ||
419 | res = TALER_MINT_reply_withdraw_sign_success (connection, | ||
420 | &collectable); | ||
373 | GNUNET_CRYPTO_rsa_signature_free (sig); | 421 | GNUNET_CRYPTO_rsa_signature_free (sig); |
374 | return TALER_MINT_reply_withdraw_sign_success (connection, | 422 | return res; |
375 | &collectable); | ||
376 | } | 423 | } |
377 | 424 | ||
378 | 425 | ||
426 | |||
427 | |||
428 | |||
379 | /** | 429 | /** |
380 | * Insert all requested denominations into the db, and compute the | 430 | * Insert all requested denominations into the DB, and compute the |
381 | * required cost of the denominations, including fees. | 431 | * required cost of the denominations, including fees. |
382 | * | 432 | * |
383 | * @param connection the connection to send an error response to | 433 | * @param connection the connection to send an error response to |
@@ -615,11 +665,11 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, | |||
615 | } | 665 | } |
616 | 666 | ||
617 | 667 | ||
618 | if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn)) | 668 | if (GNUNET_OK != |
669 | TALER_MINT_DB_transaction (db_conn)) | ||
619 | { | 670 | { |
620 | // FIXME: return 'internal error'? | ||
621 | GNUNET_break (0); | 671 | GNUNET_break (0); |
622 | return MHD_NO; | 672 | return TALER_MINT_reply_internal_db_error (connection); |
623 | } | 673 | } |
624 | 674 | ||
625 | if (GNUNET_OK != TALER_MINT_DB_create_refresh_session (db_conn, | 675 | if (GNUNET_OK != TALER_MINT_DB_create_refresh_session (db_conn, |
@@ -678,10 +728,11 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, | |||
678 | "not enough coins melted"); | 728 | "not enough coins melted"); |
679 | } | 729 | } |
680 | 730 | ||
681 | if (GNUNET_OK != TALER_MINT_DB_commit (db_conn)) | 731 | if (GNUNET_OK != |
732 | TALER_MINT_DB_commit (db_conn)) | ||
682 | { | 733 | { |
683 | GNUNET_break (0); | 734 | LOG_WARNING ("/refresh/melt transaction commit failed\n"); |
684 | return MHD_NO; | 735 | return TALER_MINT_reply_commit_error (connection); |
685 | } | 736 | } |
686 | if (GNUNET_OK != | 737 | if (GNUNET_OK != |
687 | (res = TALER_MINT_DB_get_refresh_session (db_conn, | 738 | (res = TALER_MINT_DB_get_refresh_session (db_conn, |
@@ -795,11 +846,11 @@ TALER_MINT_db_execute_refresh_commit (struct MHD_Connection *connection, | |||
795 | return MHD_NO; | 846 | return MHD_NO; |
796 | } | 847 | } |
797 | 848 | ||
798 | if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn)) | 849 | if (GNUNET_OK != |
850 | TALER_MINT_DB_transaction (db_conn)) | ||
799 | { | 851 | { |
800 | // FIXME: return 'internal error'? | ||
801 | GNUNET_break (0); | 852 | GNUNET_break (0); |
802 | return MHD_NO; | 853 | return TALER_MINT_reply_internal_db_error (connection); |
803 | } | 854 | } |
804 | 855 | ||
805 | /* Re-fetch the session information from the database, | 856 | /* Re-fetch the session information from the database, |
@@ -816,11 +867,11 @@ TALER_MINT_db_execute_refresh_commit (struct MHD_Connection *connection, | |||
816 | return MHD_NO; | 867 | return MHD_NO; |
817 | } | 868 | } |
818 | 869 | ||
819 | if (GNUNET_OK != TALER_MINT_DB_commit (db_conn)) | 870 | if (GNUNET_OK != |
871 | TALER_MINT_DB_commit (db_conn)) | ||
820 | { | 872 | { |
821 | // FIXME: return 'internal error'? | 873 | LOG_WARNING ("/refresh/commit transaction commit failed\n"); |
822 | GNUNET_break (0); | 874 | return TALER_MINT_reply_commit_error (connection); |
823 | return MHD_NO; | ||
824 | } | 875 | } |
825 | 876 | ||
826 | return TALER_MINT_reply_refresh_commit_success (connection, &refresh_session); | 877 | return TALER_MINT_reply_refresh_commit_success (connection, &refresh_session); |
@@ -1095,11 +1146,11 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, | |||
1095 | } | 1146 | } |
1096 | 1147 | ||
1097 | 1148 | ||
1098 | if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn)) | 1149 | if (GNUNET_OK != |
1150 | TALER_MINT_DB_transaction (db_conn)) | ||
1099 | { | 1151 | { |
1100 | GNUNET_break (0); | 1152 | GNUNET_break (0); |
1101 | // FIXME: return error code! | 1153 | return TALER_MINT_reply_internal_db_error (connection); |
1102 | return MHD_NO; | ||
1103 | } | 1154 | } |
1104 | 1155 | ||
1105 | for (j = 0; j < refresh_session.num_newcoins; j++) | 1156 | for (j = 0; j < refresh_session.num_newcoins; j++) |
@@ -1169,10 +1220,11 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, | |||
1169 | return MHD_NO; | 1220 | return MHD_NO; |
1170 | } | 1221 | } |
1171 | 1222 | ||
1172 | if (GNUNET_OK != TALER_MINT_DB_commit (db_conn)) | 1223 | if (GNUNET_OK != |
1224 | TALER_MINT_DB_commit (db_conn)) | ||
1173 | { | 1225 | { |
1174 | GNUNET_break (0); | 1226 | LOG_WARNING ("/refresh/reveal transaction commit failed\n"); |
1175 | return MHD_NO; | 1227 | return TALER_MINT_reply_commit_error (connection); |
1176 | } | 1228 | } |
1177 | 1229 | ||
1178 | return helper_refresh_reveal_send_response (connection, | 1230 | return helper_refresh_reveal_send_response (connection, |
diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index bad87429c..ffb764a1d 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c | |||
@@ -351,26 +351,25 @@ TALER_MINT_reply_insufficient_funds (struct MHD_Connection *connection, | |||
351 | 351 | ||
352 | 352 | ||
353 | /** | 353 | /** |
354 | * Send reserve status information to client. | 354 | * Compile the history of a reserve into a JSON object |
355 | * and calculate the total balance. | ||
355 | * | 356 | * |
356 | * @param connection connection to the client | 357 | * @param rh reserve history to JSON-ify |
357 | * @param rh reserve history to return | 358 | * @param balance[OUT] set to current reserve balance |
358 | * @return MHD result code | 359 | * @return json representation of the @a rh |
359 | */ | 360 | */ |
360 | int | 361 | static json_t * |
361 | TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection, | 362 | compile_reserve_history (const struct ReserveHistory *rh, |
362 | const struct ReserveHistory *rh) | 363 | struct TALER_Amount *balance) |
363 | { | 364 | { |
364 | struct TALER_Amount deposit_total; | 365 | struct TALER_Amount deposit_total; |
365 | struct TALER_Amount withdraw_total; | 366 | struct TALER_Amount withdraw_total; |
366 | struct TALER_Amount balance; | ||
367 | struct TALER_Amount value; | 367 | struct TALER_Amount value; |
368 | json_t *json_balance; | ||
369 | json_t *json_history; | 368 | json_t *json_history; |
370 | int ret; | 369 | int ret; |
371 | struct MintKeyState *key_state; | ||
372 | const struct ReserveHistory *pos; | 370 | const struct ReserveHistory *pos; |
373 | struct TALER_MINT_DenomKeyIssuePriv *dki; | 371 | struct TALER_MINT_DenomKeyIssuePriv *dki; |
372 | struct MintKeyState *key_state; | ||
374 | 373 | ||
375 | json_history = json_array (); | 374 | json_history = json_array (); |
376 | ret = 0; | 375 | ret = 0; |
@@ -425,8 +424,30 @@ TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection, | |||
425 | } | 424 | } |
426 | TALER_MINT_key_state_release (key_state); | 425 | TALER_MINT_key_state_release (key_state); |
427 | 426 | ||
428 | balance = TALER_amount_subtract (deposit_total, | 427 | *balance = TALER_amount_subtract (deposit_total, |
429 | withdraw_total); | 428 | withdraw_total); |
429 | return json_history; | ||
430 | } | ||
431 | |||
432 | |||
433 | /** | ||
434 | * Send reserve status information to client. | ||
435 | * | ||
436 | * @param connection connection to the client | ||
437 | * @param rh reserve history to return | ||
438 | * @return MHD result code | ||
439 | */ | ||
440 | int | ||
441 | TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection, | ||
442 | const struct ReserveHistory *rh) | ||
443 | { | ||
444 | json_t *json_balance; | ||
445 | json_t *json_history; | ||
446 | struct TALER_Amount balance; | ||
447 | int ret; | ||
448 | |||
449 | json_history = compile_reserve_history (rh, | ||
450 | &balance); | ||
430 | json_balance = TALER_JSON_from_amount (balance); | 451 | json_balance = TALER_JSON_from_amount (balance); |
431 | ret = TALER_MINT_reply_json_pack (connection, | 452 | ret = TALER_MINT_reply_json_pack (connection, |
432 | MHD_HTTP_OK, | 453 | MHD_HTTP_OK, |
@@ -440,6 +461,39 @@ TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection, | |||
440 | 461 | ||
441 | 462 | ||
442 | /** | 463 | /** |
464 | * Send reserve status information to client with the | ||
465 | * message that we have insufficient funds for the | ||
466 | * requested /withdraw/sign operation. | ||
467 | * | ||
468 | * @param connection connection to the client | ||
469 | * @param rh reserve history to return | ||
470 | * @return MHD result code | ||
471 | */ | ||
472 | int | ||
473 | TALER_MINT_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connection, | ||
474 | const struct ReserveHistory *rh) | ||
475 | { | ||
476 | json_t *json_balance; | ||
477 | json_t *json_history; | ||
478 | struct TALER_Amount balance; | ||
479 | int ret; | ||
480 | |||
481 | json_history = compile_reserve_history (rh, | ||
482 | &balance); | ||
483 | json_balance = TALER_JSON_from_amount (balance); | ||
484 | ret = TALER_MINT_reply_json_pack (connection, | ||
485 | MHD_HTTP_PAYMENT_REQUIRED, | ||
486 | "{s:s, s:o, s:o}", | ||
487 | "error", "Insufficient funds" | ||
488 | "balance", json_balance, | ||
489 | "history", json_history); | ||
490 | json_decref (json_history); | ||
491 | json_decref (json_balance); | ||
492 | return ret; | ||
493 | } | ||
494 | |||
495 | |||
496 | /** | ||
443 | * Send blinded coin information to client. | 497 | * Send blinded coin information to client. |
444 | * | 498 | * |
445 | * @param connection connection to the client | 499 | * @param connection connection to the client |
diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h index 5e2f98638..ebe1038fd 100644 --- a/src/mint/taler-mint-httpd_responses.h +++ b/src/mint/taler-mint-httpd_responses.h | |||
@@ -209,6 +209,20 @@ TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection, | |||
209 | 209 | ||
210 | 210 | ||
211 | /** | 211 | /** |
212 | * Send reserve status information to client with the | ||
213 | * message that we have insufficient funds for the | ||
214 | * requested /withdraw/sign operation. | ||
215 | * | ||
216 | * @param connection connection to the client | ||
217 | * @param rh reserve history to return | ||
218 | * @return MHD result code | ||
219 | */ | ||
220 | int | ||
221 | TALER_MINT_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connection, | ||
222 | const struct ReserveHistory *rh); | ||
223 | |||
224 | |||
225 | /** | ||
212 | * Send blinded coin information to client. | 226 | * Send blinded coin information to client. |
213 | * | 227 | * |
214 | * @param connection connection to the client | 228 | * @param connection connection to the client |