summaryrefslogtreecommitdiff
path: root/src/exchangedb
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchangedb')
-rw-r--r--src/exchangedb/.gitignore20
-rw-r--r--src/exchangedb/0002-account_merges.sql18
-rw-r--r--src/exchangedb/0002-age_withdraw.sql157
-rw-r--r--src/exchangedb/0002-aggregation_tracking.sql19
-rw-r--r--src/exchangedb/0002-aggregation_transient.sql9
-rw-r--r--src/exchangedb/0002-aml_history.sql147
-rw-r--r--src/exchangedb/0002-aml_staff.sql40
-rw-r--r--src/exchangedb/0002-aml_status.sql101
-rw-r--r--src/exchangedb/0002-auditors.sql4
-rw-r--r--src/exchangedb/0002-batch_deposits.sql172
-rw-r--r--src/exchangedb/0002-close_requests.sql59
-rw-r--r--src/exchangedb/0002-coin_deposits.sql157
-rw-r--r--src/exchangedb/0002-coin_history.sql138
-rw-r--r--src/exchangedb/0002-contracts.sql8
-rw-r--r--src/exchangedb/0002-cs_nonce_locks.sql6
-rw-r--r--src/exchangedb/0002-denominations.sql15
-rw-r--r--src/exchangedb/0002-deposits.sql427
-rw-r--r--src/exchangedb/0002-extensions.sql2
-rw-r--r--src/exchangedb/0002-global_fee.sql9
-rw-r--r--src/exchangedb/0002-history_requests.sql76
-rw-r--r--src/exchangedb/0002-known_coins.sql15
-rw-r--r--src/exchangedb/0002-kyc_attributes.sql162
-rw-r--r--src/exchangedb/0002-legitimization_processes.sql36
-rw-r--r--src/exchangedb/0002-legitimization_requirements.sql17
-rw-r--r--src/exchangedb/0002-partner_accounts.sql2
-rw-r--r--src/exchangedb/0002-partners.sql6
-rw-r--r--src/exchangedb/0002-policy_details.sql194
-rw-r--r--src/exchangedb/0002-policy_fulfillments.sql100
-rw-r--r--src/exchangedb/0002-prewire.sql24
-rw-r--r--src/exchangedb/0002-profit_drains.sql9
-rw-r--r--src/exchangedb/0002-purse_actions.sql121
-rw-r--r--src/exchangedb/0002-purse_decision.sql65
-rw-r--r--src/exchangedb/0002-purse_deletion.sql110
-rw-r--r--src/exchangedb/0002-purse_deposits.sql58
-rw-r--r--src/exchangedb/0002-purse_merges.sql26
-rw-r--r--src/exchangedb/0002-purse_requests.sql32
-rw-r--r--src/exchangedb/0002-recoup.sql49
-rw-r--r--src/exchangedb/0002-recoup_refresh.sql74
-rw-r--r--src/exchangedb/0002-refresh_commitments.sql53
-rw-r--r--src/exchangedb/0002-refresh_revealed_coins.sql16
-rw-r--r--src/exchangedb/0002-refresh_transfer_keys.sql10
-rw-r--r--src/exchangedb/0002-refunds.sql61
-rw-r--r--src/exchangedb/0002-reserve_history.sql138
-rw-r--r--src/exchangedb/0002-reserves.sql24
-rw-r--r--src/exchangedb/0002-reserves_close.sql54
-rw-r--r--src/exchangedb/0002-reserves_in.sql92
-rw-r--r--src/exchangedb/0002-reserves_open_deposits.sql49
-rw-r--r--src/exchangedb/0002-reserves_open_requests.sql53
-rw-r--r--src/exchangedb/0002-reserves_out.sql117
-rw-r--r--src/exchangedb/0002-revolving_work_shards.sql2
-rw-r--r--src/exchangedb/0002-wad_in_entries.sql35
-rw-r--r--src/exchangedb/0002-wad_out_entries.sql31
-rw-r--r--src/exchangedb/0002-wads_in.sql13
-rw-r--r--src/exchangedb/0002-wads_out.sql15
-rw-r--r--src/exchangedb/0002-wire_accounts.sql13
-rw-r--r--src/exchangedb/0002-wire_fee.sql8
-rw-r--r--src/exchangedb/0002-wire_out.sql13
-rw-r--r--src/exchangedb/0002-wire_targets.sql8
-rw-r--r--src/exchangedb/0002-work_shards.sql12
-rw-r--r--src/exchangedb/0003-purse_actions.sql74
-rw-r--r--src/exchangedb/0003-purse_deletion.sql76
-rw-r--r--src/exchangedb/0003-wire_accounts.sql25
-rw-r--r--src/exchangedb/0004-refunds.sql35
-rw-r--r--src/exchangedb/Makefile.am137
-rw-r--r--src/exchangedb/auditor-triggers-0001.sql (renamed from src/exchangedb/shard-0001.sql.in)26
-rw-r--r--src/exchangedb/bench_db.c12
-rw-r--r--src/exchangedb/drop.sql17
-rw-r--r--src/exchangedb/exchange-0001.sql31
-rw-r--r--src/exchangedb/exchange-0002.sql.in51
-rw-r--r--src/exchangedb/exchange-0003.sql.in4
-rw-r--r--src/exchangedb/exchange-0004.sql.in (renamed from src/exchangedb/shard-0002.sql.in)15
-rw-r--r--src/exchangedb/exchange_do_age_withdraw.sql165
-rw-r--r--src/exchangedb/exchange_do_amount_specific.sql92
-rw-r--r--src/exchangedb/exchange_do_batch_coin_known.sql469
-rw-r--r--src/exchangedb/exchange_do_batch_reserves_in.sql130
-rw-r--r--src/exchangedb/exchange_do_batch_reserves_update.sql72
-rw-r--r--src/exchangedb/exchange_do_batch_withdraw.sql92
-rw-r--r--src/exchangedb/exchange_do_batch_withdraw_insert.sql19
-rw-r--r--src/exchangedb/exchange_do_deposit.sql229
-rw-r--r--src/exchangedb/exchange_do_expire_purse.sql27
-rw-r--r--src/exchangedb/exchange_do_gc.sql78
-rw-r--r--src/exchangedb/exchange_do_get_link_data.sql59
-rw-r--r--src/exchangedb/exchange_do_history_request.sql85
-rw-r--r--src/exchangedb/exchange_do_insert_aml_decision.sql127
-rw-r--r--src/exchangedb/exchange_do_insert_aml_officer.sql74
-rw-r--r--src/exchangedb/exchange_do_insert_kyc_attributes.sql114
-rw-r--r--src/exchangedb/exchange_do_insert_or_update_policy_details.sql100
-rw-r--r--src/exchangedb/exchange_do_melt.sql32
-rw-r--r--src/exchangedb/exchange_do_purse_delete.sql118
-rw-r--r--src/exchangedb/exchange_do_purse_deposit.sql127
-rw-r--r--src/exchangedb/exchange_do_purse_merge.sql137
-rw-r--r--src/exchangedb/exchange_do_recoup_by_reserve.sql35
-rw-r--r--src/exchangedb/exchange_do_recoup_to_coin.sql43
-rw-r--r--src/exchangedb/exchange_do_recoup_to_reserve.sql64
-rw-r--r--src/exchangedb/exchange_do_refund.sql102
-rw-r--r--src/exchangedb/exchange_do_reserve_open.sql150
-rw-r--r--src/exchangedb/exchange_do_reserve_open_deposit.sql27
-rw-r--r--src/exchangedb/exchange_do_reserve_purse.sql43
-rw-r--r--src/exchangedb/exchange_do_reserves_in_insert.sql122
-rw-r--r--src/exchangedb/exchange_do_select_deposits_missing_wire.sql73
-rw-r--r--src/exchangedb/exchange_do_select_justification_for_missing_wire.sql102
-rw-r--r--src/exchangedb/exchange_do_withdraw.sql199
-rw-r--r--src/exchangedb/exchangedb-postgres.conf4
-rw-r--r--src/exchangedb/exchangedb_accounts.c33
-rwxr-xr-xsrc/exchangedb/perf-exchangedb-reserves-in-insert-postgres210
-rw-r--r--src/exchangedb/perf_deposits_get_ready.c565
-rw-r--r--src/exchangedb/perf_get_link_data.c543
-rw-r--r--src/exchangedb/perf_reserves_in_insert.c (renamed from src/exchangedb/perf_exchangedb_reserves_in_insert.c)125
-rw-r--r--src/exchangedb/perf_select_refunds_by_coin.c619
-rw-r--r--src/exchangedb/pg_abort_shard.c13
-rw-r--r--src/exchangedb/pg_abort_shard.h5
-rw-r--r--src/exchangedb/pg_add_denomination_key.c35
-rw-r--r--src/exchangedb/pg_add_policy_fulfillment_proof.c41
-rw-r--r--src/exchangedb/pg_aggregate.c154
-rw-r--r--src/exchangedb/pg_batch2_reserves_in_insert.c282
-rw-r--r--src/exchangedb/pg_batch4_reserves_in_insert.c266
-rw-r--r--src/exchangedb/pg_batch4_reserves_in_insert.h34
-rw-r--r--src/exchangedb/pg_batch_ensure_coin_known.c462
-rw-r--r--src/exchangedb/pg_batch_ensure_coin_known.h47
-rw-r--r--src/exchangedb/pg_batch_reserves_in_insert.c251
-rw-r--r--src/exchangedb/pg_begin_revolving_shard.c36
-rw-r--r--src/exchangedb/pg_begin_revolving_shard.h9
-rw-r--r--src/exchangedb/pg_begin_shard.c30
-rw-r--r--src/exchangedb/pg_begin_shard.h8
-rw-r--r--src/exchangedb/pg_commit.c2
-rw-r--r--src/exchangedb/pg_complete_shard.h5
-rw-r--r--src/exchangedb/pg_count_known_coins.c4
-rw-r--r--src/exchangedb/pg_count_known_coins.h2
-rw-r--r--src/exchangedb/pg_create_aggregation_transient.c24
-rw-r--r--src/exchangedb/pg_create_tables.c1
-rw-r--r--src/exchangedb/pg_delete_aggregation_transient.c2
-rw-r--r--src/exchangedb/pg_delete_shard_locks.c1
-rw-r--r--src/exchangedb/pg_do_age_withdraw.c108
-rw-r--r--src/exchangedb/pg_do_age_withdraw.h57
-rw-r--r--src/exchangedb/pg_do_batch_withdraw.c23
-rw-r--r--src/exchangedb/pg_do_batch_withdraw.h8
-rw-r--r--src/exchangedb/pg_do_batch_withdraw_insert.c12
-rw-r--r--src/exchangedb/pg_do_batch_withdraw_insert.h2
-rw-r--r--src/exchangedb/pg_do_deposit.c105
-rw-r--r--src/exchangedb/pg_do_deposit.h17
-rw-r--r--src/exchangedb/pg_do_melt.c6
-rw-r--r--src/exchangedb/pg_do_purse_delete.c (renamed from src/exchangedb/pg_insert_history_request.c)50
-rw-r--r--src/exchangedb/pg_do_purse_delete.h49
-rw-r--r--src/exchangedb/pg_do_purse_deposit.c14
-rw-r--r--src/exchangedb/pg_do_purse_deposit.h2
-rw-r--r--src/exchangedb/pg_do_purse_merge.c1
-rw-r--r--src/exchangedb/pg_do_recoup.c3
-rw-r--r--src/exchangedb/pg_do_recoup.h2
-rw-r--r--src/exchangedb/pg_do_recoup_refresh.c2
-rw-r--r--src/exchangedb/pg_do_recoup_refresh.h3
-rw-r--r--src/exchangedb/pg_do_refund.c15
-rw-r--r--src/exchangedb/pg_do_reserve_open.c42
-rw-r--r--src/exchangedb/pg_do_reserve_open.h2
-rw-r--r--src/exchangedb/pg_do_reserve_purse.c20
-rw-r--r--src/exchangedb/pg_do_withdraw.c86
-rw-r--r--src/exchangedb/pg_do_withdraw.h53
-rw-r--r--src/exchangedb/pg_drain_kyc_alert.c4
-rw-r--r--src/exchangedb/pg_drain_kyc_alert.h2
-rw-r--r--src/exchangedb/pg_drop_tables.c1
-rw-r--r--src/exchangedb/pg_ensure_coin_known.c56
-rw-r--r--src/exchangedb/pg_ensure_coin_known.h6
-rw-r--r--src/exchangedb/pg_event_listen.c8
-rw-r--r--src/exchangedb/pg_event_listen.h6
-rw-r--r--src/exchangedb/pg_event_listen_cancel.c3
-rw-r--r--src/exchangedb/pg_event_notify.c6
-rw-r--r--src/exchangedb/pg_event_notify.h4
-rw-r--r--src/exchangedb/pg_find_aggregation_transient.c5
-rw-r--r--src/exchangedb/pg_gc.h1
-rw-r--r--src/exchangedb/pg_get_age_withdraw.c119
-rw-r--r--src/exchangedb/pg_get_age_withdraw.h45
-rw-r--r--src/exchangedb/pg_get_coin_denomination.c8
-rw-r--r--src/exchangedb/pg_get_coin_transactions.c519
-rw-r--r--src/exchangedb/pg_get_coin_transactions.h27
-rw-r--r--src/exchangedb/pg_get_denomination_info.c20
-rw-r--r--src/exchangedb/pg_get_denomination_revocation.c18
-rw-r--r--src/exchangedb/pg_get_denomination_revocation.h1
-rw-r--r--src/exchangedb/pg_get_drain_profit.c4
-rw-r--r--src/exchangedb/pg_get_expired_reserves.c8
-rw-r--r--src/exchangedb/pg_get_extension_manifest.c9
-rw-r--r--src/exchangedb/pg_get_extension_manifest.h3
-rw-r--r--src/exchangedb/pg_get_global_fee.c51
-rw-r--r--src/exchangedb/pg_get_global_fee.h14
-rw-r--r--src/exchangedb/pg_get_global_fees.c20
-rw-r--r--src/exchangedb/pg_get_global_fees.h3
-rw-r--r--src/exchangedb/pg_get_known_coin.c8
-rw-r--r--src/exchangedb/pg_get_known_coin.h2
-rw-r--r--src/exchangedb/pg_get_link_data.c224
-rw-r--r--src/exchangedb/pg_get_melt.c22
-rw-r--r--src/exchangedb/pg_get_melt.h4
-rw-r--r--src/exchangedb/pg_get_old_coin_by_h_blind.c3
-rw-r--r--src/exchangedb/pg_get_old_coin_by_h_blind.h1
-rw-r--r--src/exchangedb/pg_get_pending_kyc_requirement_process.c66
-rw-r--r--src/exchangedb/pg_get_pending_kyc_requirement_process.h (renamed from src/exchangedb/pg_insert_deposit.h)31
-rw-r--r--src/exchangedb/pg_get_policy_details.c1
-rw-r--r--src/exchangedb/pg_get_purse_deposit.c19
-rw-r--r--src/exchangedb/pg_get_purse_request.c10
-rw-r--r--src/exchangedb/pg_get_ready_deposit.c42
-rw-r--r--src/exchangedb/pg_get_ready_deposit.h6
-rw-r--r--src/exchangedb/pg_get_refresh_reveal.c17
-rw-r--r--src/exchangedb/pg_get_refresh_reveal.h4
-rw-r--r--src/exchangedb/pg_get_reserve_balance.c10
-rw-r--r--src/exchangedb/pg_get_reserve_balance.h2
-rw-r--r--src/exchangedb/pg_get_reserve_by_h_blind.c2
-rw-r--r--src/exchangedb/pg_get_reserve_history.c852
-rw-r--r--src/exchangedb/pg_get_reserve_history.h44
-rw-r--r--src/exchangedb/pg_get_signature_for_known_coin.c63
-rw-r--r--src/exchangedb/pg_get_signature_for_known_coin.h43
-rw-r--r--src/exchangedb/pg_get_unfinished_close_requests.c3
-rw-r--r--src/exchangedb/pg_get_wire_accounts.c66
-rw-r--r--src/exchangedb/pg_get_wire_accounts.h2
-rw-r--r--src/exchangedb/pg_get_wire_fee.c41
-rw-r--r--src/exchangedb/pg_get_wire_fee.h10
-rw-r--r--src/exchangedb/pg_get_wire_fees.c13
-rw-r--r--src/exchangedb/pg_get_wire_fees.h4
-rw-r--r--src/exchangedb/pg_get_wire_hash_for_contract.c81
-rw-r--r--src/exchangedb/pg_get_wire_hash_for_contract.h46
-rw-r--r--src/exchangedb/pg_get_withdraw_info.c10
-rw-r--r--src/exchangedb/pg_have_deposit2.c45
-rw-r--r--src/exchangedb/pg_helper.h21
-rw-r--r--src/exchangedb/pg_inject_auditor_triggers.c57
-rw-r--r--src/exchangedb/pg_inject_auditor_triggers.h (renamed from src/exchangedb/pg_batch_reserves_in_insert.h)24
-rw-r--r--src/exchangedb/pg_insert_aml_decision.c97
-rw-r--r--src/exchangedb/pg_insert_aml_decision.h64
-rw-r--r--src/exchangedb/pg_insert_aml_officer.c66
-rw-r--r--src/exchangedb/pg_insert_aml_officer.h56
-rw-r--r--src/exchangedb/pg_insert_auditor.c10
-rw-r--r--src/exchangedb/pg_insert_auditor.h6
-rw-r--r--src/exchangedb/pg_insert_close_request.c14
-rw-r--r--src/exchangedb/pg_insert_contract.c30
-rw-r--r--src/exchangedb/pg_insert_denomination_info.c32
-rw-r--r--src/exchangedb/pg_insert_denomination_revocation.c2
-rw-r--r--src/exchangedb/pg_insert_deposit.c106
-rw-r--r--src/exchangedb/pg_insert_drain_profit.c11
-rw-r--r--src/exchangedb/pg_insert_global_fee.c52
-rw-r--r--src/exchangedb/pg_insert_global_fee.h12
-rw-r--r--src/exchangedb/pg_insert_history_request.h53
-rw-r--r--src/exchangedb/pg_insert_kyc_attributes.c110
-rw-r--r--src/exchangedb/pg_insert_kyc_attributes.h69
-rw-r--r--src/exchangedb/pg_insert_kyc_failure.c82
-rw-r--r--src/exchangedb/pg_insert_kyc_failure.h50
-rw-r--r--src/exchangedb/pg_insert_kyc_requirement_for_account.c9
-rw-r--r--src/exchangedb/pg_insert_kyc_requirement_for_account.h3
-rw-r--r--src/exchangedb/pg_insert_kyc_requirement_process.c12
-rw-r--r--src/exchangedb/pg_insert_kyc_requirement_process.h1
-rw-r--r--src/exchangedb/pg_insert_partner.c25
-rw-r--r--src/exchangedb/pg_insert_partner.h12
-rw-r--r--src/exchangedb/pg_insert_purse_request.c14
-rw-r--r--src/exchangedb/pg_insert_records_by_table.c651
-rw-r--r--src/exchangedb/pg_insert_refresh_reveal.c35
-rw-r--r--src/exchangedb/pg_insert_refund.c25
-rw-r--r--src/exchangedb/pg_insert_reserve_closed.c33
-rw-r--r--src/exchangedb/pg_insert_reserve_open_deposit.c5
-rw-r--r--src/exchangedb/pg_insert_wire.c30
-rw-r--r--src/exchangedb/pg_insert_wire.h17
-rw-r--r--src/exchangedb/pg_insert_wire_fee.c38
-rw-r--r--src/exchangedb/pg_insert_wire_fee.h8
-rw-r--r--src/exchangedb/pg_iterate_active_auditors.h3
-rw-r--r--src/exchangedb/pg_iterate_active_signkeys.h2
-rw-r--r--src/exchangedb/pg_iterate_auditor_denominations.h1
-rw-r--r--src/exchangedb/pg_iterate_denomination_info.c16
-rw-r--r--src/exchangedb/pg_iterate_denomination_info.h2
-rw-r--r--src/exchangedb/pg_iterate_denominations.c23
-rw-r--r--src/exchangedb/pg_iterate_denominations.h2
-rw-r--r--src/exchangedb/pg_iterate_reserve_close_info.c3
-rw-r--r--src/exchangedb/pg_kyc_provider_account_lookup.c5
-rw-r--r--src/exchangedb/pg_kyc_provider_account_lookup.h1
-rw-r--r--src/exchangedb/pg_lookup_aml_officer.c71
-rw-r--r--src/exchangedb/pg_lookup_aml_officer.h51
-rw-r--r--src/exchangedb/pg_lookup_auditor_status.c2
-rw-r--r--src/exchangedb/pg_lookup_auditor_timestamp.c2
-rw-r--r--src/exchangedb/pg_lookup_denomination_key.c16
-rw-r--r--src/exchangedb/pg_lookup_global_fee_by_time.c9
-rw-r--r--src/exchangedb/pg_lookup_global_fee_by_time.h1
-rw-r--r--src/exchangedb/pg_lookup_kyc_process_by_account.c9
-rw-r--r--src/exchangedb/pg_lookup_kyc_process_by_account.h1
-rw-r--r--src/exchangedb/pg_lookup_kyc_requirement_by_row.c21
-rw-r--r--src/exchangedb/pg_lookup_kyc_requirement_by_row.h3
-rw-r--r--src/exchangedb/pg_lookup_records_by_table.c939
-rw-r--r--src/exchangedb/pg_lookup_serial_by_table.c65
-rw-r--r--src/exchangedb/pg_lookup_signing_key.c1
-rw-r--r--src/exchangedb/pg_lookup_signkey_revocation.c2
-rw-r--r--src/exchangedb/pg_lookup_transfer_by_deposit.c110
-rw-r--r--src/exchangedb/pg_lookup_transfer_by_deposit.h4
-rw-r--r--src/exchangedb/pg_lookup_wire_fee_by_time.c6
-rw-r--r--src/exchangedb/pg_lookup_wire_timestamp.c4
-rw-r--r--src/exchangedb/pg_lookup_wire_timestamp.h2
-rw-r--r--src/exchangedb/pg_lookup_wire_transfer.c38
-rw-r--r--src/exchangedb/pg_persist_policy_details.c21
-rw-r--r--src/exchangedb/pg_persist_policy_details.h2
-rw-r--r--src/exchangedb/pg_preflight.c14
-rw-r--r--src/exchangedb/pg_profit_drains_get_pending.c5
-rw-r--r--src/exchangedb/pg_profit_drains_set_finished.c16
-rw-r--r--src/exchangedb/pg_release_revolving_shard.c6
-rw-r--r--src/exchangedb/pg_release_revolving_shard.h4
-rw-r--r--src/exchangedb/pg_reserves_get.c10
-rw-r--r--src/exchangedb/pg_reserves_get_origin.c2
-rw-r--r--src/exchangedb/pg_reserves_in_insert.c484
-rw-r--r--src/exchangedb/pg_reserves_in_insert.h28
-rw-r--r--src/exchangedb/pg_reserves_update.c10
-rw-r--r--src/exchangedb/pg_rollback.c2
-rw-r--r--src/exchangedb/pg_select_account_merges_above_serial_id.c6
-rw-r--r--src/exchangedb/pg_select_aggregation_amounts_for_kyc_check.c6
-rw-r--r--src/exchangedb/pg_select_aggregation_amounts_for_kyc_check.h1
-rw-r--r--src/exchangedb/pg_select_aggregation_transient.c5
-rw-r--r--src/exchangedb/pg_select_aggregations_above_serial.c137
-rw-r--r--src/exchangedb/pg_select_aggregations_above_serial.h47
-rw-r--r--src/exchangedb/pg_select_aml_history.c157
-rw-r--r--src/exchangedb/pg_select_aml_history.h (renamed from src/exchangedb/pg_select_history_requests_above_serial_id.h)26
-rw-r--r--src/exchangedb/pg_select_aml_process.c170
-rw-r--r--src/exchangedb/pg_select_aml_process.h52
-rw-r--r--src/exchangedb/pg_select_aml_threshold.c70
-rw-r--r--src/exchangedb/pg_select_aml_threshold.h48
-rw-r--r--src/exchangedb/pg_select_auditor_denom_sig.c1
-rw-r--r--src/exchangedb/pg_select_batch_deposits_missing_wire.c144
-rw-r--r--src/exchangedb/pg_select_batch_deposits_missing_wire.h (renamed from src/exchangedb/pg_select_deposits_missing_wire.h)24
-rw-r--r--src/exchangedb/pg_select_coin_deposits_above_serial_id.c (renamed from src/exchangedb/pg_select_deposits_above_serial_id.c)85
-rw-r--r--src/exchangedb/pg_select_coin_deposits_above_serial_id.h (renamed from src/exchangedb/pg_select_deposits_above_serial_id.h)6
-rw-r--r--src/exchangedb/pg_select_contract.c26
-rw-r--r--src/exchangedb/pg_select_contract.h8
-rw-r--r--src/exchangedb/pg_select_contract_by_purse.c2
-rw-r--r--src/exchangedb/pg_select_deposits_missing_wire.c176
-rw-r--r--src/exchangedb/pg_select_history_requests_above_serial_id.c159
-rw-r--r--src/exchangedb/pg_select_justification_for_missing_wire.c89
-rw-r--r--src/exchangedb/pg_select_justification_for_missing_wire.h49
-rw-r--r--src/exchangedb/pg_select_kyc_attributes.c156
-rw-r--r--src/exchangedb/pg_select_kyc_attributes.h (renamed from src/exchangedb/pg_insert_aggregation_tracking.h)26
-rw-r--r--src/exchangedb/pg_select_merge_amounts_for_kyc_check.c7
-rw-r--r--src/exchangedb/pg_select_purse.c37
-rw-r--r--src/exchangedb/pg_select_purse.h7
-rw-r--r--src/exchangedb/pg_select_purse_by_merge_pub.c8
-rw-r--r--src/exchangedb/pg_select_purse_decisions_above_serial_id.c4
-rw-r--r--src/exchangedb/pg_select_purse_decisions_above_serial_id.h1
-rw-r--r--src/exchangedb/pg_select_purse_deposits_above_serial_id.c9
-rw-r--r--src/exchangedb/pg_select_purse_deposits_by_purse.c10
-rw-r--r--src/exchangedb/pg_select_purse_merge.c28
-rw-r--r--src/exchangedb/pg_select_purse_merge.h4
-rw-r--r--src/exchangedb/pg_select_purse_merges_above_serial_id.c6
-rw-r--r--src/exchangedb/pg_select_purse_requests_above_serial_id.c3
-rw-r--r--src/exchangedb/pg_select_recoup_above_serial_id.c8
-rw-r--r--src/exchangedb/pg_select_recoup_refresh_above_serial_id.c8
-rw-r--r--src/exchangedb/pg_select_refreshes_above_serial_id.c10
-rw-r--r--src/exchangedb/pg_select_refunds_above_serial_id.c28
-rw-r--r--src/exchangedb/pg_select_refunds_by_coin.c52
-rw-r--r--src/exchangedb/pg_select_refunds_by_coin.h1
-rw-r--r--src/exchangedb/pg_select_reserve_close_info.c6
-rw-r--r--src/exchangedb/pg_select_reserve_closed_above_serial_id.c6
-rw-r--r--src/exchangedb/pg_select_reserve_open_above_serial_id.c3
-rw-r--r--src/exchangedb/pg_select_reserves_in_above_serial_id.c5
-rw-r--r--src/exchangedb/pg_select_reserves_in_above_serial_id_by_account.c13
-rw-r--r--src/exchangedb/pg_select_satisfied_kyc_processes.c15
-rw-r--r--src/exchangedb/pg_select_similar_kyc_attributes.c154
-rw-r--r--src/exchangedb/pg_select_similar_kyc_attributes.h45
-rw-r--r--src/exchangedb/pg_select_wire_out_above_serial_id.c6
-rw-r--r--src/exchangedb/pg_select_wire_out_above_serial_id_by_account.c5
-rw-r--r--src/exchangedb/pg_select_wire_out_above_serial_id_by_account.h1
-rw-r--r--src/exchangedb/pg_select_withdraw_amounts_for_kyc_check.c28
-rw-r--r--src/exchangedb/pg_select_withdraw_amounts_for_kyc_check.h1
-rw-r--r--src/exchangedb/pg_select_withdrawals_above_serial_id.c9
-rw-r--r--src/exchangedb/pg_select_withdrawals_above_serial_id.h1
-rw-r--r--src/exchangedb/pg_set_extension_manifest.c4
-rw-r--r--src/exchangedb/pg_set_extension_manifest.h2
-rw-r--r--src/exchangedb/pg_set_purse_balance.c9
-rw-r--r--src/exchangedb/pg_setup_wire_target.c2
-rw-r--r--src/exchangedb/pg_setup_wire_target.h2
-rw-r--r--src/exchangedb/pg_start.c3
-rw-r--r--src/exchangedb/pg_store_wire_transfer_out.c10
-rw-r--r--src/exchangedb/pg_template.c2
-rw-r--r--src/exchangedb/pg_template.h2
-rw-r--r--src/exchangedb/pg_test_aml_officer.c (renamed from src/exchangedb/pg_insert_aggregation_tracking.c)28
-rw-r--r--src/exchangedb/pg_test_aml_officer.h (renamed from src/exchangedb/pg_batch2_reserves_in_insert.h)26
-rw-r--r--src/exchangedb/pg_trigger_aml_process.c58
-rw-r--r--src/exchangedb/pg_trigger_aml_process.h45
-rw-r--r--src/exchangedb/pg_update_aggregation_transient.c15
-rw-r--r--src/exchangedb/pg_update_auditor.c30
-rw-r--r--src/exchangedb/pg_update_auditor.h8
-rw-r--r--src/exchangedb/pg_update_kyc_process_by_row.c23
-rw-r--r--src/exchangedb/pg_update_kyc_process_by_row.h2
-rw-r--r--src/exchangedb/pg_update_wire.c39
-rw-r--r--src/exchangedb/pg_update_wire.h18
-rw-r--r--src/exchangedb/pg_wire_prepare_data_get.c10
-rw-r--r--src/exchangedb/pg_wire_prepare_data_get.h7
-rw-r--r--src/exchangedb/pg_wire_prepare_data_insert.c22
-rw-r--r--src/exchangedb/pg_wire_prepare_data_insert.h5
-rw-r--r--src/exchangedb/pg_wire_prepare_data_mark_failed.c2
-rw-r--r--src/exchangedb/pg_wire_prepare_data_mark_failed.h1
-rw-r--r--src/exchangedb/pg_wire_prepare_data_mark_finished.c10
-rw-r--r--src/exchangedb/pg_wire_prepare_data_mark_finished.h1
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c251
-rw-r--r--src/exchangedb/procedures.sql.in18
-rw-r--r--src/exchangedb/spi/Makefile9
-rw-r--r--src/exchangedb/spi/README.md37
-rw-r--r--src/exchangedb/spi/own_test.c873
-rw-r--r--src/exchangedb/spi/own_test.control4
-rw-r--r--src/exchangedb/spi/own_test.sql201
-rw-r--r--src/exchangedb/spi/perf_own_test.c25
-rw-r--r--src/exchangedb/spi/pg_aggregate.c411
-rwxr-xr-xsrc/exchangedb/test-exchangedb-batch-reserves-in-insert-postgres210
-rwxr-xr-xsrc/exchangedb/test-exchangedb-by-j-postgres210
-rwxr-xr-xsrc/exchangedb/test-exchangedb-populate-link-data-postgres210
-rwxr-xr-xsrc/exchangedb/test-exchangedb-populate-ready-deposit-postgres210
-rwxr-xr-xsrc/exchangedb/test-exchangedb-populate-select-refunds-by-coin-postgres210
-rw-r--r--src/exchangedb/test_exchangedb.c437
-rw-r--r--src/exchangedb/test_exchangedb_batch_reserves_in_insert.c201
-rw-r--r--src/exchangedb/test_exchangedb_by_j.c123
-rwxr-xr-xsrc/exchangedb/test_idempotency.sh12
-rw-r--r--src/exchangedb/versioning.sql3
406 files changed, 17729 insertions, 7051 deletions
diff --git a/src/exchangedb/.gitignore b/src/exchangedb/.gitignore
index abe505527..6e67fadb1 100644
--- a/src/exchangedb/.gitignore
+++ b/src/exchangedb/.gitignore
@@ -1,12 +1,16 @@
-test-exchangedb-auditors
-test-exchangedb-denomkeys
-test-exchangedb-fees
test-exchangedb-postgres
-test-exchangedb-signkeys
-test-perf-taler-exchangedb
bench-db-postgres
-shard-drop0001.sqltest-exchangedb-by-j-postgres
-test-exchangedb-by-j-postgres
-perf-exchangedb-reserves-in-insert-postgres
+perf_deposits_get_ready-postgres
+perf_get_link_data-postgres
+perf_reserves_in_insert-postgres
+perf_select_refunds_by_coin-postgres
exchange-0002.sql
procedures.sql
+exchange-0003.sql
+perf-exchangedb-reserves-in-insert-postgres
+test-exchangedb-batch-reserves-in-insert-postgres
+test-exchangedb-by-j-postgres
+test-exchangedb-populate-link-data-postgres
+test-exchangedb-populate-ready-deposit-postgres
+test-exchangedb-populate-select-refunds-by-coin-postgres
+exchange-0004.sql
diff --git a/src/exchangedb/0002-account_merges.sql b/src/exchangedb/0002-account_merges.sql
index b1995f204..1dd7e5bf0 100644
--- a/src/exchangedb/0002-account_merges.sql
+++ b/src/exchangedb/0002-account_merges.sql
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_account_merges(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'account_merges';
+ table_name TEXT DEFAULT 'account_merges';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE IF NOT EXISTS %I '
@@ -64,17 +64,23 @@ $$;
CREATE FUNCTION constrain_table_account_merges(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'account_merges';
+ table_name TEXT DEFAULT 'account_merges';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
- -- FIXME: change to materialized index by reserve_pub!?
+ -- Note: this index *may* be useful in
+ -- pg_get_reserve_history depending on how
+ -- smart the DB is when computing the JOIN.
+ -- Removing it MAY boost performance slightly, at
+ -- the expense of trouble if the "merge_by_reserve"
+ -- query planner goes off the rails. Needs benchmarking
+ -- to be sure.
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_by_reserve_pub '
'ON ' || table_name || ' '
@@ -94,7 +100,7 @@ RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'account_merges';
+ table_name TEXT DEFAULT 'account_merges';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
diff --git a/src/exchangedb/0002-age_withdraw.sql b/src/exchangedb/0002-age_withdraw.sql
new file mode 100644
index 000000000..87cac7816
--- /dev/null
+++ b/src/exchangedb/0002-age_withdraw.sql
@@ -0,0 +1,157 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2023 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/>
+--
+-- @author Özgür Kesim
+
+CREATE FUNCTION create_table_age_withdraw(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'age_withdraw';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE %I'
+ '(age_withdraw_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',h_commitment BYTEA NOT NULL CONSTRAINT h_commitment_length CHECK(LENGTH(h_commitment)=64)'
+ ',max_age SMALLINT NOT NULL CONSTRAINT max_age_positive CHECK(max_age>=0)'
+ ',amount_with_fee taler_amount NOT NULL'
+ ',reserve_pub BYTEA NOT NULL CONSTRAINT reserve_pub_length CHECK(LENGTH(reserve_pub)=32)'
+ ',reserve_sig BYTEA NOT NULL CONSTRAINT reserve_sig_length CHECK(LENGTH(reserve_sig)=64)'
+ ',noreveal_index SMALLINT NOT NULL CONSTRAINT noreveal_index_positive CHECK(noreveal_index>=0)'
+ ',h_blind_evs BYTEA[] NOT NULL CONSTRAINT h_blind_evs_length CHECK(cardinality(h_blind_evs)=cardinality(denom_serials))'
+ ',denom_serials INT8[] NOT NULL CONSTRAINT denom_serials_array_length CHECK(cardinality(denom_serials)=cardinality(denom_sigs))'
+ ',denom_sigs BYTEA[] NOT NULL CONSTRAINT denom_sigs_array_length CHECK(cardinality(denom_sigs)=cardinality(denom_serials))'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (reserve_pub)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'Commitments made when withdrawing coins with age restriction and the gamma value chosen by the exchange. '
+ 'It also contains the blindly signed coins, their signatures and denominations.'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The gamma value chosen by the exchange in the cut-and-choose protocol'
+ ,'noreveal_index'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The maximum age (in years) that the client commits to with this request'
+ ,'max_age'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Commitment made by the client, hash over the various client inputs in the cut-and-choose protocol'
+ ,'h_commitment'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Reference to the public key of the reserve from which the coins are going to be withdrawn'
+ ,'reserve_pub'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Signature of the reserve''s private key over the age-withdraw request'
+ ,'reserve_sig'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Array of references to the denominations'
+ ,'denom_serials'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Array of the blinded envelopes of the chosen fresh coins, with value as given by the denomination in the corresponding slot in denom_serials'
+ ,'h_blind_evs'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Array of signatures over each blinded envelope'
+ ,'denom_sigs'
+ ,table_name
+ ,partition_suffix
+ );
+END
+$$;
+
+
+CREATE FUNCTION constrain_table_age_withdraw(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'age_withdraw';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD PRIMARY KEY (h_commitment);'
+ );
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_h_commitment_reserve_pub_key'
+ ' UNIQUE (h_commitment, reserve_pub);'
+ );
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_age_withdraw_id_key'
+ ' UNIQUE (age_withdraw_id);'
+ );
+END
+$$;
+
+
+CREATE FUNCTION foreign_table_age_withdraw()
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'age_withdraw';
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
+ ' FOREIGN KEY (reserve_pub)'
+ ' REFERENCES reserves(reserve_pub) ON DELETE CASCADE;'
+ );
+END
+$$;
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+VALUES
+ ('age_withdraw', 'exchange-0002', 'create', TRUE ,FALSE),
+ ('age_withdraw', 'exchange-0002', 'constrain',TRUE ,FALSE),
+ ('age_withdraw', 'exchange-0002', 'foreign', TRUE ,FALSE);
+
diff --git a/src/exchangedb/0002-aggregation_tracking.sql b/src/exchangedb/0002-aggregation_tracking.sql
index f6135c5ac..d07960247 100644
--- a/src/exchangedb/0002-aggregation_tracking.sql
+++ b/src/exchangedb/0002-aggregation_tracking.sql
@@ -15,22 +15,22 @@
--
CREATE FUNCTION create_table_aggregation_tracking(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'aggregation_tracking';
+ table_name TEXT DEFAULT 'aggregation_tracking';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
'(aggregation_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',deposit_serial_id INT8 PRIMARY KEY'
+ ',batch_deposit_serial_id INT8 PRIMARY KEY'
',wtid_raw BYTEA NOT NULL'
') %s ;'
,table_name
- ,'PARTITION BY HASH (deposit_serial_id)'
+ ,'PARTITION BY HASH (batch_deposit_serial_id)'
,partition_suffix
);
PERFORM comment_partitioned_table(
@@ -49,13 +49,13 @@ $$;
CREATE FUNCTION constrain_table_aggregation_tracking(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'aggregation_tracking';
+ table_name TEXT DEFAULT 'aggregation_tracking';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -81,13 +81,14 @@ RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'aggregation_tracking';
+ table_name TEXT DEFAULT 'aggregation_tracking';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_foreign_deposit'
- ' FOREIGN KEY (deposit_serial_id) '
- ' REFERENCES deposits (deposit_serial_id) ON DELETE CASCADE' -- FIXME change to coin_pub + deposit_serial_id for more efficient deposit???
+ ' FOREIGN KEY (batch_deposit_serial_id)'
+ ' REFERENCES batch_deposits (batch_deposit_serial_id)'
+ ' ON DELETE CASCADE'
);
END
$$;
diff --git a/src/exchangedb/0002-aggregation_transient.sql b/src/exchangedb/0002-aggregation_transient.sql
index 2d77e63ca..8e46450f0 100644
--- a/src/exchangedb/0002-aggregation_transient.sql
+++ b/src/exchangedb/0002-aggregation_transient.sql
@@ -15,18 +15,17 @@
--
CREATE FUNCTION create_table_aggregation_transient(
- IN shard_suffix VARCHAR DEFAULT NULL
+ IN shard_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'aggregation_transient';
+ table_name TEXT DEFAULT 'aggregation_transient';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
- '(amount_val INT8 NOT NULL'
- ',amount_frac INT4 NOT NULL'
+ '(amount taler_amount NOT NULL'
',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)'
',merchant_pub BYTEA CHECK (LENGTH(merchant_pub)=32)'
',exchange_account_section TEXT NOT NULL'
@@ -44,7 +43,7 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Sum of all of the aggregated deposits (without deposit fees)'
- ,'amount_val'
+ ,'amount'
,table_name
,shard_suffix
);
diff --git a/src/exchangedb/0002-aml_history.sql b/src/exchangedb/0002-aml_history.sql
new file mode 100644
index 000000000..af81be9d8
--- /dev/null
+++ b/src/exchangedb/0002-aml_history.sql
@@ -0,0 +1,147 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--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/>
+--
+
+CREATE OR REPLACE FUNCTION create_table_aml_history(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'aml_history';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE IF NOT EXISTS %I'
+ '(aml_history_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',h_payto BYTEA CHECK (LENGTH(h_payto)=32)'
+ ',new_threshold taler_amount NOT NULL DEFAULT(0,0)'
+ ',new_status INT4 NOT NULL DEFAULT(0)'
+ ',decision_time INT8 NOT NULL DEFAULT(0)'
+ ',justification TEXT NOT NULL'
+ ',kyc_requirements TEXT'
+ ',kyc_req_row INT8 NOT NULL DEFAULT(0)'
+ ',decider_pub BYTEA CHECK (LENGTH(decider_pub)=32)'
+ ',decider_sig BYTEA CHECK (LENGTH(decider_sig)=64)'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (h_payto)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'AML decision history for a particular payment destination'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'hash of the payto://-URI this AML history is about'
+ ,'h_payto'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'new monthly inbound transaction limit below which we are OK'
+ ,'new_threshold'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ '0 for all OK, 1 for AML decision required, 2 for account is frozen (prevents further transactions)'
+ ,'new_status'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'when was the status changed'
+ ,'decision_time'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'human-readable justification for the status change'
+ ,'justification'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Public key of the staff member who made the AML decision'
+ ,'decider_pub'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Additional KYC requirements imposed by the AML staff member. Serialized JSON array of strings.'
+ ,'kyc_requirements'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Row in the KYC table for this KYC requirement, 0 for none.'
+ ,'kyc_req_row'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Signature key of the staff member affirming the AML decision; of type AML_DECISION'
+ ,'decider_sig'
+ ,table_name
+ ,partition_suffix
+ );
+END $$;
+
+COMMENT ON FUNCTION create_table_aml_history
+ IS 'Creates the aml_history table';
+
+
+CREATE OR REPLACE FUNCTION constrain_table_aml_history(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'aml_history';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_serial_key '
+ 'UNIQUE (aml_history_serial_id)'
+ );
+ EXECUTE FORMAT (
+ 'CREATE INDEX ' || table_name || '_main_index '
+ 'ON ' || table_name || ' '
+ '(h_payto, decision_time DESC);'
+ );
+END $$;
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+ VALUES
+ ('aml_history'
+ ,'exchange-0002'
+ ,'create'
+ ,TRUE
+ ,FALSE),
+ ('aml_history'
+ ,'exchange-0002'
+ ,'constrain'
+ ,TRUE
+ ,FALSE);
diff --git a/src/exchangedb/0002-aml_staff.sql b/src/exchangedb/0002-aml_staff.sql
new file mode 100644
index 000000000..cec18c62b
--- /dev/null
+++ b/src/exchangedb/0002-aml_staff.sql
@@ -0,0 +1,40 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--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/>
+--
+
+
+CREATE TABLE aml_staff
+ (aml_staff_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
+ ,decider_pub BYTEA PRIMARY KEY CHECK (LENGTH(decider_pub)=32)
+ ,master_sig BYTEA CHECK (LENGTH(master_sig)=64)
+ ,decider_name TEXT NOT NULL
+ ,is_active BOOLEAN NOT NULL
+ ,read_only BOOLEAN NOT NULL
+ ,last_change INT8 NOT NULL
+ );
+COMMENT ON TABLE aml_staff
+ IS 'Table with AML staff members the exchange uses or has used in the past. Entries never expire as we need to remember the last_change column indefinitely.';
+COMMENT ON COLUMN aml_staff.decider_pub
+ IS 'Public key of the AML staff member.';
+COMMENT ON COLUMN aml_staff.master_sig
+ IS 'The master public key signature on the AML staff member status, of type TALER_SIGNATURE_MASTER_AML_KEY.';
+COMMENT ON COLUMN aml_staff.decider_name
+ IS 'Name of the staff member.';
+COMMENT ON COLUMN aml_staff.is_active
+ IS 'true if we are currently supporting the use of this AML staff member.';
+COMMENT ON COLUMN aml_staff.is_active
+ IS 'true if the member has read-only access.';
+COMMENT ON COLUMN aml_staff.last_change
+ IS 'Latest time when active status changed. Used to detect replays of old messages.';
diff --git a/src/exchangedb/0002-aml_status.sql b/src/exchangedb/0002-aml_status.sql
new file mode 100644
index 000000000..a8b567a82
--- /dev/null
+++ b/src/exchangedb/0002-aml_status.sql
@@ -0,0 +1,101 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--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/>
+--
+
+CREATE OR REPLACE FUNCTION create_table_aml_status(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'aml_status';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE IF NOT EXISTS %I'
+ '(aml_status_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',h_payto BYTEA PRIMARY KEY CHECK (LENGTH(h_payto)=32)'
+ ',threshold taler_amount NOT NULL DEFAULT(0,0)'
+ ',status INT4 NOT NULL DEFAULT(0)'
+ ',kyc_requirement INT8 NOT NULL DEFAULT(0)'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (h_payto)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'AML status for a particular payment destination'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'hash of the payto://-URI this AML status is about'
+ ,'h_payto'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'monthly inbound transaction limit below which we are OK (if status is 1)'
+ ,'threshold'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ '0 for all OK, 1 for AML decision required, 2 for account is frozen (prevents further transactions)'
+ ,'status'
+ ,table_name
+ ,partition_suffix
+ );
+END $$;
+
+COMMENT ON FUNCTION create_table_aml_status
+ IS 'Creates the aml_status table';
+
+
+CREATE OR REPLACE FUNCTION constrain_table_aml_status(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'aml_status';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_serial_key '
+ 'UNIQUE (aml_status_serial_id)'
+ );
+END $$;
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+ VALUES
+ ('aml_status'
+ ,'exchange-0002'
+ ,'create'
+ ,TRUE
+ ,FALSE),
+ ('aml_status'
+ ,'exchange-0002'
+ ,'constrain'
+ ,TRUE
+ ,FALSE);
diff --git a/src/exchangedb/0002-auditors.sql b/src/exchangedb/0002-auditors.sql
index 32ec8446a..76e43b183 100644
--- a/src/exchangedb/0002-auditors.sql
+++ b/src/exchangedb/0002-auditors.sql
@@ -18,8 +18,8 @@
CREATE TABLE auditors
(auditor_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,auditor_pub BYTEA PRIMARY KEY CHECK (LENGTH(auditor_pub)=32)
- ,auditor_name VARCHAR NOT NULL
- ,auditor_url VARCHAR NOT NULL
+ ,auditor_name TEXT NOT NULL
+ ,auditor_url TEXT NOT NULL
,is_active BOOLEAN NOT NULL
,last_change INT8 NOT NULL
);
diff --git a/src/exchangedb/0002-batch_deposits.sql b/src/exchangedb/0002-batch_deposits.sql
new file mode 100644
index 000000000..71a4b4205
--- /dev/null
+++ b/src/exchangedb/0002-batch_deposits.sql
@@ -0,0 +1,172 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--2023 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/>
+--
+
+CREATE FUNCTION create_table_batch_deposits(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'batch_deposits';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE %I'
+ '(batch_deposit_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY'
+ ',shard INT8 NOT NULL'
+ ',merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)'
+ ',wallet_timestamp INT8 NOT NULL'
+ ',exchange_timestamp INT8 NOT NULL'
+ ',refund_deadline INT8 NOT NULL'
+ ',wire_deadline INT8 NOT NULL'
+ ',h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)'
+ ',wallet_data_hash BYTEA CHECK (LENGTH(wallet_data_hash)=64) DEFAULT NULL'
+ ',wire_salt BYTEA NOT NULL CHECK (LENGTH(wire_salt)=16)'
+ ',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)'
+ ',policy_details_serial_id INT8'
+ ',policy_blocked BOOLEAN NOT NULL DEFAULT FALSE'
+ ',done BOOLEAN NOT NULL DEFAULT FALSE'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (shard)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'Information about the contracts for which we have received (batch) deposits.'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Used for load sharding in the materialized indices. Should be set based on merchant_pub. 64-bit value because we need an *unsigned* 32-bit value.'
+ ,'shard'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Unsalted hash of the target bank account; also used to lookup the KYC status'
+ ,'wire_target_h_payto'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'hash over data provided by the wallet upon payment to select a more specific contract'
+ ,'wallet_data_hash'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Salt used when hashing the payto://-URI to get the h_wire that was used by the coin deposit signatures; not used to calculate wire_target_h_payto (as that one is unsalted)'
+ ,'wire_salt'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Set to TRUE once we have included this (batch) deposit (and all associated coins) in some aggregate wire transfer to the merchant'
+ ,'done'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'True if the aggregation of the (batch) deposit is currently blocked by some policy extension mechanism. Used to filter out deposits that must not be processed by the canonical deposit logic.'
+ ,'policy_blocked'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'References policy extensions table, NULL if extensions are not used'
+ ,'policy_details_serial_id'
+ ,table_name
+ ,partition_suffix
+ );
+END
+$$;
+
+
+CREATE FUNCTION constrain_table_batch_deposits(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'batch_deposits';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_batch_deposit_serial_id_pkey'
+ ' PRIMARY KEY (batch_deposit_serial_id) '
+ ',ADD CONSTRAINT ' || table_name || '_merchant_pub_h_contract_terms'
+ ' UNIQUE (shard, merchant_pub, h_contract_terms)'
+ );
+ EXECUTE FORMAT (
+ 'CREATE INDEX ' || table_name || '_by_ready '
+ 'ON ' || table_name || ' '
+ '(shard ASC'
+ ',wire_deadline ASC'
+ ') WHERE NOT (done OR policy_blocked);'
+ );
+ EXECUTE FORMAT (
+ 'CREATE INDEX ' || table_name || '_for_matching '
+ 'ON ' || table_name || ' '
+ '(shard ASC'
+ ',refund_deadline ASC'
+ ',wire_target_h_payto'
+ ') WHERE NOT (done OR policy_blocked);'
+ );
+END
+$$;
+
+CREATE OR REPLACE FUNCTION foreign_table_batch_deposits()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'batch_deposits';
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_foreign_policy_details'
+ ' FOREIGN KEY (policy_details_serial_id) '
+ ' REFERENCES policy_details (policy_details_serial_id) ON DELETE RESTRICT'
+ );
+END
+$$;
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+ VALUES
+ ('batch_deposits'
+ ,'exchange-0002'
+ ,'create'
+ ,TRUE
+ ,FALSE),
+ ('batch_deposits'
+ ,'exchange-0002'
+ ,'constrain'
+ ,TRUE
+ ,FALSE),
+ ('batch_deposits'
+ ,'exchange-0002'
+ ,'foreign'
+ ,TRUE
+ ,FALSE)
+ ;
diff --git a/src/exchangedb/0002-close_requests.sql b/src/exchangedb/0002-close_requests.sql
index 32149b1b0..6a7028095 100644
--- a/src/exchangedb/0002-close_requests.sql
+++ b/src/exchangedb/0002-close_requests.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_close_requests(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'close_requests';
+ table_name TEXT DEFAULT 'close_requests';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
@@ -29,11 +29,9 @@ BEGIN
',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)'
',close_timestamp INT8 NOT NULL'
',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
- ',close_val INT8 NOT NULL'
- ',close_frac INT4 NOT NULL'
- ',close_fee_val INT8 NOT NULL'
- ',close_fee_frac INT4 NOT NULL'
- ',payto_uri VARCHAR NOT NULL'
+ ',close taler_amount NOT NULL'
+ ',close_fee taler_amount NOT NULL'
+ ',payto_uri TEXT NOT NULL'
',done BOOL NOT NULL DEFAULT(FALSE)'
',PRIMARY KEY (reserve_pub,close_timestamp)'
') %s ;'
@@ -60,7 +58,7 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Balance of the reserve at the time of closing, to be wired to the associated bank account (minus the closing fee)'
- ,'close_val'
+ ,'close'
,table_name
,partition_suffix
);
@@ -74,13 +72,13 @@ END $$;
CREATE FUNCTION constrain_table_close_requests(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'close_requests';
+ table_name TEXT DEFAULT 'close_requests';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -107,7 +105,7 @@ RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'close_requests';
+ table_name TEXT DEFAULT 'close_requests';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -119,6 +117,38 @@ END
$$;
+CREATE OR REPLACE FUNCTION close_requests_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO reserve_history
+ (reserve_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.reserve_pub
+ ,'close_requests'
+ ,NEW.close_request_serial_id);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION close_requests_insert_trigger()
+ IS 'Automatically generate reserve history entry.';
+
+
+CREATE FUNCTION master_table_close_requests()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER close_requests_on_insert
+ AFTER INSERT
+ ON close_requests
+ FOR EACH ROW EXECUTE FUNCTION close_requests_insert_trigger();
+END $$;
+
+
+
INSERT INTO exchange_tables
(name
,version
@@ -140,4 +170,9 @@ INSERT INTO exchange_tables
,'exchange-0002'
,'foreign'
,TRUE
+ ,FALSE),
+ ('close_requests'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
,FALSE);
diff --git a/src/exchangedb/0002-coin_deposits.sql b/src/exchangedb/0002-coin_deposits.sql
new file mode 100644
index 000000000..c3eef6e5a
--- /dev/null
+++ b/src/exchangedb/0002-coin_deposits.sql
@@ -0,0 +1,157 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--2023 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/>
+--
+
+CREATE FUNCTION create_table_coin_deposits(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'coin_deposits';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE %I'
+ '(coin_deposit_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY'
+ ',batch_deposit_serial_id INT8 NOT NULL'
+ ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)'
+ ',coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)'
+ ',amount_with_fee taler_amount NOT NULL'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (coin_pub)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'Coins which have been deposited with the respective per-coin signatures.'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Link to information about the batch deposit this coin was used for'
+ ,'batch_deposit_serial_id'
+ ,table_name
+ ,partition_suffix
+ );
+END
+$$;
+
+
+CREATE FUNCTION constrain_table_coin_deposits(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'coin_deposits';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_coin_deposit_serial_id_pkey'
+ ' PRIMARY KEY (coin_deposit_serial_id) '
+ ',ADD CONSTRAINT ' || table_name || '_unique_coin_sig'
+ ' UNIQUE (coin_pub, coin_sig)'
+ );
+ EXECUTE FORMAT (
+ 'CREATE INDEX ' || table_name || '_by_batch '
+ 'ON ' || table_name || ' '
+ '(batch_deposit_serial_id);'
+ );
+END
+$$;
+
+
+CREATE FUNCTION foreign_table_coin_deposits()
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'coin_deposits';
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_foreign_coin_pub'
+ ' FOREIGN KEY (coin_pub) '
+ ' REFERENCES known_coins (coin_pub) ON DELETE CASCADE'
+ ',ADD CONSTRAINT ' || table_name || '_foreign_batch_deposits_id'
+ ' FOREIGN KEY (batch_deposit_serial_id) '
+ ' REFERENCES batch_deposits (batch_deposit_serial_id) ON DELETE CASCADE'
+ );
+END
+$$;
+
+
+CREATE OR REPLACE FUNCTION coin_deposits_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO exchange.coin_history
+ (coin_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.coin_pub
+ ,'coin_deposits'
+ ,NEW.coin_deposit_serial_id);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION coin_deposits_insert_trigger()
+ IS 'Automatically generate coin history entry.';
+
+
+CREATE FUNCTION master_table_coin_deposits()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER coin_deposits_on_insert
+ AFTER INSERT
+ ON coin_deposits
+ FOR EACH ROW EXECUTE FUNCTION coin_deposits_insert_trigger();
+END $$;
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+ VALUES
+ ('coin_deposits'
+ ,'exchange-0002'
+ ,'create'
+ ,TRUE
+ ,FALSE),
+ ('coin_deposits'
+ ,'exchange-0002'
+ ,'constrain'
+ ,TRUE
+ ,FALSE),
+ ('coin_deposits'
+ ,'exchange-0002'
+ ,'foreign'
+ ,TRUE
+ ,FALSE),
+ ('coin_deposits'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
+ ,FALSE)
+ ;
diff --git a/src/exchangedb/0002-coin_history.sql b/src/exchangedb/0002-coin_history.sql
new file mode 100644
index 000000000..9b5efdcb6
--- /dev/null
+++ b/src/exchangedb/0002-coin_history.sql
@@ -0,0 +1,138 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--2023 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/>
+--
+
+CREATE FUNCTION create_table_coin_history (
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'coin_history';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE %I'
+ '(coin_history_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY'
+ ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)'
+ ',table_name TEXT NOT NULL'
+ ',serial_id INT8 NOT NULL'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (coin_pub)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'Links to tables with entries that affected the transaction history of a coin.'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'For which coin is this a history entry'
+ ,'coin_pub'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'In which table is the history entry'
+ ,'table_name'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Which is the generated serial ID of the entry in the table'
+ ,'serial_id'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Monotonic counter, used to generate Etags for caching'
+ ,'coin_history_serial_id'
+ ,table_name
+ ,partition_suffix
+ );
+END
+$$;
+
+
+CREATE FUNCTION constrain_table_coin_history(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'coin_history';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_coin_history_serial_id_pkey'
+ ' PRIMARY KEY (coin_history_serial_id) '
+ ',ADD CONSTRAINT ' || table_name || '_coin_entry_key'
+ ' UNIQUE (coin_pub, table_name, serial_id)'
+ );
+ EXECUTE FORMAT (
+ 'CREATE INDEX ' || table_name || '_coin_by_time'
+ ' ON ' || table_name || ' '
+ '(coin_pub'
+ ',coin_history_serial_id DESC'
+ ');'
+ );
+END
+$$;
+
+
+CREATE FUNCTION foreign_table_coin_history()
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'coin_history';
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_foreign_coin_pub'
+ ' FOREIGN KEY (coin_pub) '
+ ' REFERENCES known_coins (coin_pub) ON DELETE CASCADE'
+ );
+END
+$$;
+
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+ VALUES
+ ('coin_history'
+ ,'exchange-0002'
+ ,'create'
+ ,TRUE
+ ,FALSE),
+ ('coin_history'
+ ,'exchange-0002'
+ ,'constrain'
+ ,TRUE
+ ,FALSE),
+ ('coin_history'
+ ,'exchange-0002'
+ ,'foreign'
+ ,TRUE
+ ,FALSE)
+ ;
diff --git a/src/exchangedb/0002-contracts.sql b/src/exchangedb/0002-contracts.sql
index 409653060..c1f92c9aa 100644
--- a/src/exchangedb/0002-contracts.sql
+++ b/src/exchangedb/0002-contracts.sql
@@ -16,13 +16,13 @@
CREATE FUNCTION create_table_contracts(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'contracts';
+ table_name TEXT DEFAULT 'contracts';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
@@ -72,13 +72,13 @@ $$;
CREATE FUNCTION constrain_table_contracts(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'contracts';
+ table_name TEXT DEFAULT 'contracts';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
diff --git a/src/exchangedb/0002-cs_nonce_locks.sql b/src/exchangedb/0002-cs_nonce_locks.sql
index 0cb88b3f8..36c0c7a5f 100644
--- a/src/exchangedb/0002-cs_nonce_locks.sql
+++ b/src/exchangedb/0002-cs_nonce_locks.sql
@@ -15,7 +15,7 @@
--
CREATE FUNCTION create_table_cs_nonce_locks(
- partition_suffix VARCHAR DEFAULT NULL
+ partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
@@ -60,13 +60,13 @@ $$;
CREATE FUNCTION constrain_table_cs_nonce_locks(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'cs_nonce_locks';
+ table_name TEXT DEFAULT 'cs_nonce_locks';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
diff --git a/src/exchangedb/0002-denominations.sql b/src/exchangedb/0002-denominations.sql
index d468a3875..a3de2b149 100644
--- a/src/exchangedb/0002-denominations.sql
+++ b/src/exchangedb/0002-denominations.sql
@@ -25,16 +25,11 @@ CREATE TABLE denominations
,expire_withdraw INT8 NOT NULL
,expire_deposit INT8 NOT NULL
,expire_legal INT8 NOT NULL
- ,coin_val INT8 NOT NULL
- ,coin_frac INT4 NOT NULL
- ,fee_withdraw_val INT8 NOT NULL
- ,fee_withdraw_frac INT4 NOT NULL
- ,fee_deposit_val INT8 NOT NULL
- ,fee_deposit_frac INT4 NOT NULL
- ,fee_refresh_val INT8 NOT NULL
- ,fee_refresh_frac INT4 NOT NULL
- ,fee_refund_val INT8 NOT NULL
- ,fee_refund_frac INT4 NOT NULL
+ ,coin taler_amount NOT NULL
+ ,fee_withdraw taler_amount NOT NULL
+ ,fee_deposit taler_amount NOT NULL
+ ,fee_refresh taler_amount NOT NULL
+ ,fee_refund taler_amount NOT NULL
);
COMMENT ON TABLE denominations
IS 'Main denominations table. All the valid denominations the exchange knows about.';
diff --git a/src/exchangedb/0002-deposits.sql b/src/exchangedb/0002-deposits.sql
deleted file mode 100644
index 92210afa3..000000000
--- a/src/exchangedb/0002-deposits.sql
+++ /dev/null
@@ -1,427 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2014--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/>
---
-
-CREATE FUNCTION create_table_deposits(
- IN partition_suffix VARCHAR DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'deposits';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(deposit_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',shard INT8 NOT NULL'
- ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)'
- ',known_coin_id INT8 NOT NULL' -- FIXME: column needed!?
- ',amount_with_fee_val INT8 NOT NULL'
- ',amount_with_fee_frac INT4 NOT NULL'
- ',wallet_timestamp INT8 NOT NULL'
- ',exchange_timestamp INT8 NOT NULL'
- ',refund_deadline INT8 NOT NULL'
- ',wire_deadline INT8 NOT NULL'
- ',merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)'
- ',h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)'
- ',coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)'
- ',wire_salt BYTEA NOT NULL CHECK (LENGTH(wire_salt)=16)'
- ',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)'
- ',done BOOLEAN NOT NULL DEFAULT FALSE'
- ',policy_blocked BOOLEAN NOT NULL DEFAULT FALSE'
- ',policy_details_serial_id INT8'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (coin_pub)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Deposits we have received and for which we need to make (aggregate) wire transfers (and manage refunds).'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Used for load sharding in the materialized indices. Should be set based on merchant_pub. 64-bit value because we need an *unsigned* 32-bit value.'
- ,'shard'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Used for garbage collection'
- ,'known_coin_id'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Identifies the target bank account and KYC status'
- ,'wire_target_h_payto'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Salt used when hashing the payto://-URI to get the h_wire'
- ,'wire_salt'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Set to TRUE once we have included this deposit in some aggregate wire transfer to the merchant'
- ,'done'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'True if the aggregation of the deposit is currently blocked by some policy extension mechanism. Used to filter out deposits that must not be processed by the canonical deposit logic.'
- ,'policy_blocked'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'References policy extensions table, NULL if extensions are not used'
- ,'policy_details_serial_id'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_deposits(
- IN partition_suffix VARCHAR
-)
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'deposits';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_deposit_serial_id_pkey'
- ' PRIMARY KEY (deposit_serial_id) '
- ',ADD CONSTRAINT ' || table_name || '_coin_pub_merchant_pub_h_contract_terms_key'
- ' UNIQUE (coin_pub, merchant_pub, h_contract_terms)'
- );
-END
-$$;
-
-
-CREATE FUNCTION foreign_table_deposits()
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'deposits';
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_foreign_coin_pub'
- ' FOREIGN KEY (coin_pub) '
- ' REFERENCES known_coins (coin_pub) ON DELETE CASCADE'
- ',ADD CONSTRAINT ' || table_name || '_foreign_coin_id'
- ' FOREIGN KEY (known_coin_id) '
- ' REFERENCES known_coins (known_coin_id) ON DELETE CASCADE'
- ',ADD CONSTRAINT ' || table_name || '_foreign_policy_details'
- ' FOREIGN KEY (policy_details_serial_id) '
- ' REFERENCES policy_details (policy_details_serial_id) ON DELETE CASCADE'
- );
-END
-$$;
-
-
-CREATE FUNCTION create_table_deposits_by_ready(
- IN partition_suffix VARCHAR DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'deposits_by_ready';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(wire_deadline INT8 NOT NULL'
- ',shard INT8 NOT NULL'
- ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)'
- ',deposit_serial_id INT8'
- ') %s ;'
- ,table_name
- ,'PARTITION BY RANGE (wire_deadline)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Enables fast lookups for deposits_get_ready, auto-populated via TRIGGER below'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_deposits_by_ready(
- IN partition_suffix VARCHAR DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'deposits_by_ready';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_main_index '
- 'ON ' || table_name || ' '
- '(wire_deadline ASC, shard ASC, coin_pub);'
- );
-END
-$$;
-
-
-CREATE FUNCTION create_table_deposits_for_matching(
- IN partition_suffix VARCHAR DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'deposits_for_matching';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(refund_deadline INT8 NOT NULL'
- ',merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)'
- ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)' -- REFERENCES known_coins (coin_pub) ON DELETE CASCADE
- ',deposit_serial_id INT8'
- ') %s ;'
- ,table_name
- ,'PARTITION BY RANGE (refund_deadline)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Enables fast lookups for deposits_iterate_matching, auto-populated via TRIGGER below'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_deposits_for_matching(
- IN partition_suffix VARCHAR DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'deposits_for_matching';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_main_index'
- ' ON ' || table_name || ' '
- '(refund_deadline ASC, merchant_pub, coin_pub);'
- );
-END
-$$;
-
-
-CREATE OR REPLACE FUNCTION deposits_insert_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-DECLARE
- is_ready BOOLEAN;
-BEGIN
- is_ready = NOT (NEW.done OR NEW.policy_blocked);
-
- IF (is_ready)
- THEN
- INSERT INTO exchange.deposits_by_ready
- (wire_deadline
- ,shard
- ,coin_pub
- ,deposit_serial_id)
- VALUES
- (NEW.wire_deadline
- ,NEW.shard
- ,NEW.coin_pub
- ,NEW.deposit_serial_id);
- INSERT INTO exchange.deposits_for_matching
- (refund_deadline
- ,merchant_pub
- ,coin_pub
- ,deposit_serial_id)
- VALUES
- (NEW.refund_deadline
- ,NEW.merchant_pub
- ,NEW.coin_pub
- ,NEW.deposit_serial_id);
- END IF;
- RETURN NEW;
-END $$;
-COMMENT ON FUNCTION deposits_insert_trigger()
- IS 'Replicate deposit inserts into materialized indices.';
-
-
-CREATE OR REPLACE FUNCTION deposits_update_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-DECLARE
- was_ready BOOLEAN;
-DECLARE
- is_ready BOOLEAN;
-BEGIN
- was_ready = NOT (OLD.done OR OLD.policy_blocked);
- is_ready = NOT (NEW.done OR NEW.policy_blocked);
- IF (was_ready AND NOT is_ready)
- THEN
- DELETE FROM exchange.deposits_by_ready
- WHERE wire_deadline = OLD.wire_deadline
- AND shard = OLD.shard
- AND coin_pub = OLD.coin_pub
- AND deposit_serial_id = OLD.deposit_serial_id;
- DELETE FROM exchange.deposits_for_matching
- WHERE refund_deadline = OLD.refund_deadline
- AND merchant_pub = OLD.merchant_pub
- AND coin_pub = OLD.coin_pub
- AND deposit_serial_id = OLD.deposit_serial_id;
- END IF;
- IF (is_ready AND NOT was_ready)
- THEN
- INSERT INTO exchange.deposits_by_ready
- (wire_deadline
- ,shard
- ,coin_pub
- ,deposit_serial_id)
- VALUES
- (NEW.wire_deadline
- ,NEW.shard
- ,NEW.coin_pub
- ,NEW.deposit_serial_id);
- INSERT INTO exchange.deposits_for_matching
- (refund_deadline
- ,merchant_pub
- ,coin_pub
- ,deposit_serial_id)
- VALUES
- (NEW.refund_deadline
- ,NEW.merchant_pub
- ,NEW.coin_pub
- ,NEW.deposit_serial_id);
- END IF;
- RETURN NEW;
-END $$;
-COMMENT ON FUNCTION deposits_update_trigger()
- IS 'Replicate deposits changes into materialized indices.';
-
-
-CREATE OR REPLACE FUNCTION deposits_delete_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-DECLARE
- was_ready BOOLEAN;
-BEGIN
- was_ready = NOT (OLD.done OR OLD.policy_blocked);
-
- IF (was_ready)
- THEN
- DELETE FROM exchange.deposits_by_ready
- WHERE wire_deadline = OLD.wire_deadline
- AND shard = OLD.shard
- AND coin_pub = OLD.coin_pub
- AND deposit_serial_id = OLD.deposit_serial_id;
- DELETE FROM exchange.deposits_for_matching
- WHERE refund_deadline = OLD.refund_deadline
- AND merchant_pub = OLD.merchant_pub
- AND coin_pub = OLD.coin_pub
- AND deposit_serial_id = OLD.deposit_serial_id;
- END IF;
- RETURN NEW;
-END $$;
-COMMENT ON FUNCTION deposits_delete_trigger()
- IS 'Replicate deposit deletions into materialized indices.';
-
-
-CREATE FUNCTION master_table_deposits()
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-BEGIN
- CREATE TRIGGER deposits_on_insert
- AFTER INSERT
- ON deposits
- FOR EACH ROW EXECUTE FUNCTION deposits_insert_trigger();
- CREATE TRIGGER deposits_on_update
- AFTER UPDATE
- ON deposits
- FOR EACH ROW EXECUTE FUNCTION deposits_update_trigger();
- CREATE TRIGGER deposits_on_delete
- AFTER DELETE
- ON deposits
- FOR EACH ROW EXECUTE FUNCTION deposits_delete_trigger();
-END $$;
-
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
- VALUES
- ('deposits'
- ,'exchange-0002'
- ,'create'
- ,TRUE
- ,FALSE),
- ('deposits'
- ,'exchange-0002'
- ,'constrain'
- ,TRUE
- ,FALSE),
- ('deposits'
- ,'exchange-0002'
- ,'foreign'
- ,TRUE
- ,FALSE),
- ('deposits_by_ready'
- ,'exchange-0002'
- ,'create'
- ,TRUE
- ,TRUE),
- ('deposits_by_ready'
- ,'exchange-0002'
- ,'constrain'
- ,TRUE
- ,TRUE),
- ('deposits_for_matching'
- ,'exchange-0002'
- ,'create'
- ,TRUE
- ,TRUE),
- ('deposits_for_matching'
- ,'exchange-0002'
- ,'constrain'
- ,TRUE
- ,TRUE),
- ('deposits'
- ,'exchange-0002'
- ,'master'
- ,TRUE
- ,FALSE);
diff --git a/src/exchangedb/0002-extensions.sql b/src/exchangedb/0002-extensions.sql
index 5642ea13a..df9e50090 100644
--- a/src/exchangedb/0002-extensions.sql
+++ b/src/exchangedb/0002-extensions.sql
@@ -16,7 +16,7 @@
CREATE TABLE extensions
(extension_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
- ,name VARCHAR NOT NULL UNIQUE
+ ,name TEXT NOT NULL UNIQUE
,manifest BYTEA
);
COMMENT ON TABLE extensions
diff --git a/src/exchangedb/0002-global_fee.sql b/src/exchangedb/0002-global_fee.sql
index 0a2f9b495..f6526798d 100644
--- a/src/exchangedb/0002-global_fee.sql
+++ b/src/exchangedb/0002-global_fee.sql
@@ -18,12 +18,9 @@ CREATE TABLE global_fee
(global_fee_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,start_date INT8 NOT NULL
,end_date INT8 NOT NULL
- ,history_fee_val INT8 NOT NULL
- ,history_fee_frac INT4 NOT NULL
- ,account_fee_val INT8 NOT NULL
- ,account_fee_frac INT4 NOT NULL
- ,purse_fee_val INT8 NOT NULL
- ,purse_fee_frac INT4 NOT NULL
+ ,history_fee taler_amount NOT NULL
+ ,account_fee taler_amount NOT NULL
+ ,purse_fee taler_amount NOT NULL
,purse_timeout INT8 NOT NULL
,history_expiration INT8 NOT NULL
,purse_account_limit INT4 NOT NULL
diff --git a/src/exchangedb/0002-history_requests.sql b/src/exchangedb/0002-history_requests.sql
index 5cd5c7b7b..0714e1bea 100644
--- a/src/exchangedb/0002-history_requests.sql
+++ b/src/exchangedb/0002-history_requests.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -16,22 +16,21 @@
CREATE FUNCTION create_table_history_requests(
- IN shard_suffix VARCHAR DEFAULT NULL
+ IN shard_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'history_requests';
+ table_name TEXT DEFAULT 'history_requests';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
- '(history_request_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' --UNIQUE
+ '(history_request_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)'
',request_timestamp INT8 NOT NULL'
',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
- ',history_fee_val INT8 NOT NULL'
- ',history_fee_frac INT4 NOT NULL'
+ ',history_fee taler_amount NOT NULL'
',PRIMARY KEY (reserve_pub,request_timestamp)'
') %s ;'
,table_name
@@ -57,19 +56,39 @@ BEGIN
);
PERFORM comment_partitioned_column(
'History fee approved by the signature'
- ,'history_fee_val'
+ ,'history_fee'
,table_name
,shard_suffix
);
END $$;
+CREATE FUNCTION constrain_table_history_requests(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ partition_name TEXT;
+BEGIN
+ partition_name = concat_ws('_', 'history_requests', partition_suffix);
+
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || partition_name ||
+ ' ADD CONSTRAINT ' || partition_name || '_serial_id'
+ ' UNIQUE (history_request_serial_id)'
+ );
+END
+$$;
+
+
CREATE FUNCTION foreign_table_history_requests()
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'history_requests';
+ table_name TEXT DEFAULT 'history_requests';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -80,6 +99,37 @@ BEGIN
END $$;
+CREATE OR REPLACE FUNCTION history_requests_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO reserve_history
+ (reserve_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.reserve_pub
+ ,'history_requests'
+ ,NEW.history_request_serial_id);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION history_requests_insert_trigger()
+ IS 'Automatically generate reserve history entry.';
+
+
+CREATE FUNCTION master_table_history_requests()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER history_requests_on_insert
+ AFTER INSERT
+ ON history_requests
+ FOR EACH ROW EXECUTE FUNCTION history_requests_insert_trigger();
+END $$;
+
+
INSERT INTO exchange_tables
(name
,version
@@ -94,6 +144,16 @@ INSERT INTO exchange_tables
,FALSE),
('history_requests'
,'exchange-0002'
+ ,'constrain'
+ ,TRUE
+ ,FALSE),
+ ('history_requests'
+ ,'exchange-0002'
,'foreign'
,TRUE
+ ,FALSE),
+ ('history_requests'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
,FALSE);
diff --git a/src/exchangedb/0002-known_coins.sql b/src/exchangedb/0002-known_coins.sql
index 4cdb974ea..a13beff6f 100644
--- a/src/exchangedb/0002-known_coins.sql
+++ b/src/exchangedb/0002-known_coins.sql
@@ -16,13 +16,13 @@
CREATE FUNCTION create_table_known_coins(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'known_coins';
+ table_name TEXT default 'known_coins';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
@@ -31,8 +31,7 @@ BEGIN
',coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (LENGTH(coin_pub)=32)'
',age_commitment_hash BYTEA CHECK (LENGTH(age_commitment_hash)=32)'
',denom_sig BYTEA NOT NULL'
- ',remaining_val INT8 NOT NULL DEFAULT(0)'
- ',remaining_frac INT4 NOT NULL DEFAULT(0)'
+ ',remaining taler_amount NOT NULL DEFAULT(0,0)'
') %s ;'
,table_name
,'PARTITION BY HASH (coin_pub)'
@@ -57,7 +56,7 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Value of the coin that remains to be spent'
- ,'remaining_val'
+ ,'remaining'
,table_name
,partition_suffix
);
@@ -78,13 +77,13 @@ $$;
CREATE FUNCTION constrain_table_known_coins(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'known_coins';
+ table_name TEXT default 'known_coins';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -101,7 +100,7 @@ RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'known_coins';
+ table_name TEXT default 'known_coins';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
diff --git a/src/exchangedb/0002-kyc_attributes.sql b/src/exchangedb/0002-kyc_attributes.sql
new file mode 100644
index 000000000..66f3fc315
--- /dev/null
+++ b/src/exchangedb/0002-kyc_attributes.sql
@@ -0,0 +1,162 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--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/>
+--
+
+CREATE OR REPLACE FUNCTION create_table_kyc_attributes(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'kyc_attributes';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE IF NOT EXISTS %I'
+ '(kyc_attributes_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',h_payto BYTEA PRIMARY KEY CHECK (LENGTH(h_payto)=32)'
+ ',kyc_prox BYTEA NOT NULL CHECK (LENGTH(kyc_prox)=32)'
+ ',provider TEXT NOT NULL'
+ ',satisfied_checks TEXT[] NOT NULL'
+ ',collection_time INT8 NOT NULL'
+ ',expiration_time INT8 NOT NULL'
+ ',encrypted_attributes BYTEA NOT NULL'
+ ',legitimization_serial INT8 NOT NULL'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (h_payto)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'KYC data about particular payment addresses'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'hash of payto://-URI the attributes are about'
+ ,'h_payto'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'short hash of normalized full name and birthdate; used to efficiently find likely duplicate users'
+ ,'kyc_prox'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'time when the attributes were collected by the provider'
+ ,'collection_time'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'time when the attributes should no longer be considered validated'
+ ,'expiration_time'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'configuration section name of the provider that affirmed the attributes'
+ ,'provider'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ '(encrypted) JSON object (as string) with the attributes'
+ ,'encrypted_attributes'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Reference the legitimization process for which these attributes are gathered for.'
+ ,'legitimization_serial'
+ ,table_name
+ ,partition_suffix
+ );
+END $$;
+
+COMMENT ON FUNCTION create_table_kyc_attributes
+ IS 'Creates the kyc_attributes table';
+
+
+CREATE OR REPLACE FUNCTION constrain_table_kyc_attributes(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'kyc_attributes';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_serial_key '
+ 'UNIQUE (kyc_attributes_serial_id)'
+ );
+ -- To search similar users (e.g. during AML checks)
+ EXECUTE FORMAT (
+ 'CREATE INDEX ' || table_name || '_similarity_index '
+ 'ON ' || table_name || ' '
+ '(kyc_prox);'
+ );
+ -- For garbage collection
+ EXECUTE FORMAT (
+ 'CREATE INDEX ' || table_name || '_expiration_time '
+ 'ON ' || table_name || ' '
+ '(expiration_time ASC);'
+ );
+END $$;
+
+
+CREATE OR REPLACE FUNCTION foreign_table_kyc_attributes()
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'kyc_attributes';
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_foreign_legitimization_processes'
+ ' FOREIGN KEY (legitimization_serial) '
+ ' REFERENCES legitimization_processes (legitimization_process_serial_id)' -- ON DELETE CASCADE
+ );
+END $$;
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+ VALUES
+ ('kyc_attributes'
+ ,'exchange-0002'
+ ,'create'
+ ,TRUE
+ ,FALSE),
+ ('kyc_attributes'
+ ,'exchange-0002'
+ ,'constrain'
+ ,TRUE
+ ,FALSE),
+ ('kyc_attributes'
+ ,'exchange-0002'
+ ,'foreign'
+ ,TRUE
+ ,FALSE);
diff --git a/src/exchangedb/0002-legitimization_processes.sql b/src/exchangedb/0002-legitimization_processes.sql
index 6248da1f0..3212b1c06 100644
--- a/src/exchangedb/0002-legitimization_processes.sql
+++ b/src/exchangedb/0002-legitimization_processes.sql
@@ -15,7 +15,7 @@
--
CREATE FUNCTION create_table_legitimization_processes(
- IN shard_suffix VARCHAR DEFAULT NULL
+ IN shard_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
@@ -23,13 +23,15 @@ AS $$
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
- '(legitimization_process_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE'
+ '(legitimization_process_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
',h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=32)'
+ ',start_time INT8 NOT NULL'
',expiration_time INT8 NOT NULL DEFAULT (0)'
- ',provider_section VARCHAR NOT NULL'
- ',provider_user_id VARCHAR DEFAULT NULL'
- ',provider_legitimization_id VARCHAR DEFAULT NULL'
- ',UNIQUE (h_payto, provider_section)'
+ ',provider_section TEXT NOT NULL'
+ ',provider_user_id TEXT DEFAULT NULL'
+ ',provider_legitimization_id TEXT DEFAULT NULL'
+ ',redirect_url TEXT DEFAULT NULL'
+ ',finished BOOLEAN DEFAULT (FALSE)'
') %s ;'
,'legitimization_processes'
,'PARTITION BY HASH (h_payto)'
@@ -53,6 +55,18 @@ BEGIN
,shard_suffix
);
PERFORM comment_partitioned_column(
+ 'time when the KYC check was initiated, useful for garbage collection'
+ ,'expiration_time'
+ ,'legitimization_processes'
+ ,shard_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'URL where the user should go to begin the KYC process'
+ ,'redirect_url'
+ ,'legitimization_processes'
+ ,shard_suffix
+ );
+ PERFORM comment_partitioned_column(
'in the future if the respective KYC check was passed successfully'
,'expiration_time'
,'legitimization_processes'
@@ -76,19 +90,25 @@ BEGIN
,'legitimization_processes'
,shard_suffix
);
+ PERFORM comment_partitioned_column(
+ 'Set to TRUE when the specific legitimization process is finished.'
+ ,'finished'
+ ,'legitimization_processes'
+ ,shard_suffix
+ );
END
$$;
-- We need a separate function for this, as we call create_table only once but need to add
-- those constraints to each partition which gets created
CREATE FUNCTION constrain_table_legitimization_processes(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- partition_name VARCHAR;
+ partition_name TEXT;
BEGIN
partition_name = concat_ws('_', 'legitimization_processes', partition_suffix);
diff --git a/src/exchangedb/0002-legitimization_requirements.sql b/src/exchangedb/0002-legitimization_requirements.sql
index 7aaf7b799..d806eb424 100644
--- a/src/exchangedb/0002-legitimization_requirements.sql
+++ b/src/exchangedb/0002-legitimization_requirements.sql
@@ -15,7 +15,7 @@
--
CREATE FUNCTION create_table_legitimization_requirements(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
@@ -23,9 +23,10 @@ AS $$
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
- '(legitimization_requirement_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE'
+ '(legitimization_requirement_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
',h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=32)'
- ',required_checks VARCHAR NOT NULL'
+ ',reserve_pub BYTEA'
+ ',required_checks TEXT NOT NULL'
',UNIQUE (h_payto, required_checks)'
') %s ;'
,'legitimization_requirements'
@@ -50,6 +51,12 @@ BEGIN
,partition_suffix
);
PERFORM comment_partitioned_column(
+ 'if h_payto refers to a reserve, this is its public key, NULL otherwise. It allows to lookup the corresponding reserve when the KYC process is done.'
+ ,'reserve_pub'
+ ,'legitimization_requirements'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
'space-separated list of required checks'
,'required_checks'
,'legitimization_requirements'
@@ -61,13 +68,13 @@ $$;
-- We need a separate function for this, as we call create_table only once but need to add
-- those constraints to each partition which gets created
CREATE FUNCTION constrain_table_legitimization_requirements(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- partition_name VARCHAR;
+ partition_name TEXT;
BEGIN
partition_name = concat_ws('_', 'legitimization_requirements', partition_suffix);
EXECUTE FORMAT (
diff --git a/src/exchangedb/0002-partner_accounts.sql b/src/exchangedb/0002-partner_accounts.sql
index 0f4af92c8..b12dc06e6 100644
--- a/src/exchangedb/0002-partner_accounts.sql
+++ b/src/exchangedb/0002-partner_accounts.sql
@@ -16,7 +16,7 @@
CREATE TABLE partner_accounts
- (payto_uri VARCHAR PRIMARY KEY
+ (payto_uri TEXT PRIMARY KEY
,partner_serial_id INT8 REFERENCES partners(partner_serial_id) ON DELETE CASCADE
,partner_master_sig BYTEA CHECK (LENGTH(partner_master_sig)=64)
,last_seen INT8 NOT NULL
diff --git a/src/exchangedb/0002-partners.sql b/src/exchangedb/0002-partners.sql
index ff57f8fc1..ed09378d0 100644
--- a/src/exchangedb/0002-partners.sql
+++ b/src/exchangedb/0002-partners.sql
@@ -21,10 +21,10 @@ CREATE TABLE partners
,end_date INT8 NOT NULL
,next_wad INT8 NOT NULL DEFAULT (0)
,wad_frequency INT8 NOT NULL
- ,wad_fee_val INT8 NOT NULL
- ,wad_fee_frac INT4 NOT NULL
+ ,wad_fee taler_amount NOT NULL
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
,partner_base_url TEXT NOT NULL
+ ,PRIMARY KEY (partner_master_pub, start_date)
);
COMMENT ON TABLE partners
IS 'exchanges we do wad transfers to';
@@ -38,7 +38,7 @@ COMMENT ON COLUMN partners.next_wad
IS 'at what time should we do the next wad transfer to this partner (frequently updated); set to forever after the end_date';
COMMENT ON COLUMN partners.wad_frequency
IS 'how often do we promise to do wad transfers';
-COMMENT ON COLUMN partners.wad_fee_val
+COMMENT ON COLUMN partners.wad_fee
IS 'how high is the fee for a wallet to be added to a wad to this partner';
COMMENT ON COLUMN partners.partner_base_url
IS 'base URL of the REST API for this partner';
diff --git a/src/exchangedb/0002-policy_details.sql b/src/exchangedb/0002-policy_details.sql
index c9bfd1575..3acbb5c10 100644
--- a/src/exchangedb/0002-policy_details.sql
+++ b/src/exchangedb/0002-policy_details.sql
@@ -14,46 +14,162 @@
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
--- FIXME: this table should be sharded!
-
-CREATE TABLE policy_details
- (policy_details_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
- ,policy_hash_code BYTEA PRIMARY KEY CHECK(LENGTH(policy_hash_code)=16)
- ,policy_json VARCHAR
- ,deadline INT8 NOT NULL
- ,commitment_val INT8 NOT NULL
- ,commitment_frac INT4 NOT NULL
- ,accumulated_total_val INT8 NOT NULL
- ,accumulated_total_frac INT4 NOT NULL
- ,fee_val INT8 NOT NULL
- ,fee_frac INT4 NOT NULL
- ,transferable_val INT8 NOT NULL
- ,transferable_frac INT8 NOT NULL
- ,fulfillment_state smallint NOT NULL CHECK(fulfillment_state between 0 and 5)
- ,fulfillment_id BIGINT NULL REFERENCES policy_fulfillments (fulfillment_id) ON DELETE CASCADE
- );
-COMMENT ON TABLE policy_details
- IS 'Policies that were provided with deposits via policy extensions.';
-COMMENT ON COLUMN policy_details.policy_hash_code
- IS 'ID (GNUNET_HashCode) that identifies a policy. Will be calculated by the policy extension based on the content';
-COMMENT ON COLUMN policy_details.policy_json
- IS 'JSON object with options set that the exchange needs to consider when executing a deposit. Supported details depend on the policy extensions supported by the exchange.';
-COMMENT ON COLUMN policy_details.deadline
- IS 'Deadline until the policy must be marked as fulfilled (maybe "forever")';
-COMMENT ON COLUMN policy_details.commitment_val
- IS 'The amount that this policy commits to. Invariant: commitment >= fee';
-COMMENT ON COLUMN policy_details.accumulated_total_val
- IS 'The sum of all contributions of all deposit that reference this policy. Invariant: The fulfilment_state must be Insufficient as long as accumulated_total < commitment';
-COMMENT ON COLUMN policy_details.fee_val
- IS 'The fee for this policy, due when the policy is fulfilled or timed out';
-COMMENT ON COLUMN policy_details.transferable_val
- IS 'The amount that on fulfillment or timeout will be transferred to the payto-URI''s of the corresponding deposit''s. The policy fees must have been already deducted from it. Invariant: fee+transferable <= accumulated_total. The remaining amount (accumulated_total - fee - transferable) can be refreshed by the owner of the coins when the state is Timeout or Success.';
-COMMENT ON COLUMN policy_details.fulfillment_state
- IS 'State of the fulfillment:
+-- @author: Özgür Kesim
+
+CREATE FUNCTION create_table_policy_details(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'policy_details';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE %I '
+ '(policy_details_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',policy_hash_code gnunet_hashcode NOT NULL'
+ ',policy_json TEXT NOT NULL'
+ ',deadline INT8 NOT NULL'
+ ',commitment taler_amount NOT NULL'
+ ',accumulated_total taler_amount NOT NULL'
+ ',fee taler_amount NOT NULL'
+ ',transferable taler_amount NOT NULL'
+ ',fulfillment_state SMALLINT NOT NULL CHECK(fulfillment_state between 0 and 5)'
+ ',h_fulfillment_proof gnunet_hashcode'
+ ') %s;'
+ ,table_name
+ ,'PARTITION BY HASH (h_fulfillment_proof)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'Policies that were provided with deposits via policy extensions.'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'ID (GNUNET_HashCode) that identifies a policy. Will be calculated by the policy extension based on the content'
+ ,'policy_hash_code'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'JSON object with options set that the exchange needs to consider when executing a deposit. Supported details depend on the policy extensions supported by the exchange.'
+ ,'policy_json'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Deadline until the policy must be marked as fulfilled (maybe "forever")'
+ ,'deadline'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The amount that this policy commits to. Invariant: commitment >= fee'
+ ,'commitment'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The sum of all contributions of all deposit that reference this policy. Invariant: The fulfilment_state must be Insufficient as long as accumulated_total < commitment'
+ ,'accumulated_total'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The fee for this policy, due when the policy is fulfilled or timed out'
+ ,'fee'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The amount that on fulfillment or timeout will be transferred to the payto-URI''s of the corresponding deposit''s. The policy fees must have been already deducted from it. Invariant: fee+transferable <= accumulated_total. The remaining amount (accumulated_total - fee - transferable) can be refreshed by the owner of the coins when the state is Timeout or Success.'
+ ,'transferable'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'State of the fulfillment:
- 0 (Failure)
- 1 (Insufficient)
- 2 (Ready)
- 4 (Success)
- - 5 (Timeout)';
-COMMENT ON COLUMN policy_details.fulfillment_id
- IS 'Reference to the proof of the fulfillment of this policy, if it exists. Invariant: If not NULL, this entry''s .hash_code MUST be part of the corresponding policy_fulfillments.policy_hash_codes array.';
+ - 5 (Timeout)'
+ ,'fulfillment_state'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Reference to the proof of the fulfillment of this policy, if it exists. Invariant: If not NULL, this entry''s .hash_code MUST be part of the corresponding policy_fulfillments.policy_hash_codes array.'
+ ,'h_fulfillment_proof'
+ ,table_name
+ ,partition_suffix
+ );
+END
+$$;
+
+COMMENT ON FUNCTION create_table_policy_details
+ IS 'Creates the policy_details table';
+
+
+
+
+CREATE FUNCTION constrain_table_policy_details(
+ IN partition_suffix TEXT
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ partition_name TEXT;
+BEGIN
+ partition_name = concat_ws('_', 'policy_details', partition_suffix);
+
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || partition_name ||
+ ' ADD CONSTRAINT ' || partition_name || '_unique_serial_id '
+ ' UNIQUE (policy_details_serial_id)'
+ );
+
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || partition_name ||
+ ' ADD CONSTRAINT ' || partition_name || '_unique_hash_fulfillment_proof '
+ ' UNIQUE (policy_hash_code, h_fulfillment_proof)'
+ );
+
+ EXECUTE FORMAT (
+ 'CREATE INDEX ' || partition_name || '_policy_hash_code'
+ ' ON ' || partition_name ||
+ ' (policy_hash_code);'
+ );
+END
+$$;
+
+CREATE OR REPLACE FUNCTION foreign_table_policy_details()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'policy_details';
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_foreign_policy_fulfillments'
+ ' FOREIGN KEY (h_fulfillment_proof) '
+ ' REFERENCES policy_fulfillments (h_fulfillment_proof) ON DELETE RESTRICT'
+ );
+END
+$$;
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+VALUES
+ ('policy_details', 'exchange-0002', 'create', TRUE ,FALSE),
+ ('policy_details', 'exchange-0002', 'constrain', TRUE ,FALSE),
+ ('policy_details', 'exchange-0002', 'foreign', TRUE ,FALSE);
diff --git a/src/exchangedb/0002-policy_fulfillments.sql b/src/exchangedb/0002-policy_fulfillments.sql
index 54f44df52..c00947019 100644
--- a/src/exchangedb/0002-policy_fulfillments.sql
+++ b/src/exchangedb/0002-policy_fulfillments.sql
@@ -14,22 +14,88 @@
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
--- FIXME: this table should be sharded!
+-- @author: Özgür Kesim
-CREATE TABLE policy_fulfillments
- (fulfillment_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY
- ,fulfillment_timestamp INT8 NOT NULL
- ,fulfillment_proof VARCHAR
- ,h_fulfillment_proof BYTEA NOT NULL CHECK(LENGTH(h_fulfillment_proof) = 64) UNIQUE
- ,policy_hash_codes BYTEA NOT NULL CHECK(0 = MOD(LENGTH(policy_hash_codes), 16))
+CREATE FUNCTION create_table_policy_fulfillments(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'policy_fulfillments';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE %I '
+ '(h_fulfillment_proof gnunet_hashcode PRIMARY KEY'
+ ',fulfillment_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',fulfillment_timestamp INT8 NOT NULL'
+ ',fulfillment_proof TEXT'
+ ',policy_hash_codes gnunet_hashcode[] NOT NULL'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (h_fulfillment_proof)'
+ ,partition_suffix
);
-COMMENT ON TABLE policy_fulfillments
- IS 'Proofs of fulfillment of policies that were set in deposits';
-COMMENT ON COLUMN policy_fulfillments.fulfillment_timestamp
- IS 'Timestamp of the arrival of a proof of fulfillment';
-COMMENT ON COLUMN policy_fulfillments.fulfillment_proof
- IS 'JSON object with a proof of the fulfillment of a policy. Supported details depend on the policy extensions supported by the exchange.';
-COMMENT ON COLUMN policy_fulfillments.h_fulfillment_proof
- IS 'Hash of the fulfillment_proof';
-COMMENT ON COLUMN policy_fulfillments.policy_hash_codes
- IS 'Concatenation of the policy_hash_code of all policy_details that are fulfilled by this proof';
+ PERFORM comment_partitioned_table(
+ 'Proofs of fulfillment of policies that were set in deposits'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Timestamp of the arrival of a proof of fulfillment'
+ ,'fulfillment_timestamp'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'JSON object with a proof of the fulfillment of a policy. Supported details depend on the policy extensions supported by the exchange.'
+ ,'fulfillment_proof'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Hash of the fulfillment_proof'
+ ,'h_fulfillment_proof'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Array of the policy_hash_code''s of all policy_details that are fulfilled by this proof'
+ ,'policy_hash_codes'
+ ,table_name
+ ,partition_suffix
+ );
+END
+$$;
+
+COMMENT ON FUNCTION create_table_policy_fulfillments
+ IS 'Creates the policy_fulfillments table';
+
+CREATE FUNCTION constrain_table_policy_fulfillments(
+ IN partition_suffix TEXT
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ partition_name TEXT;
+BEGIN
+ partition_name = concat_ws('_', 'policy_fulfillments', partition_suffix);
+
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || partition_name ||
+ ' ADD CONSTRAINT ' || partition_name || '_serial_id '
+ ' UNIQUE (h_fulfillment_proof, fulfillment_id)'
+ );
+END
+$$;
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+VALUES
+ ('policy_fulfillments', 'exchange-0002', 'create', TRUE ,FALSE),
+ ('policy_fulfillments', 'exchange-0002', 'constrain', TRUE ,FALSE);
diff --git a/src/exchangedb/0002-prewire.sql b/src/exchangedb/0002-prewire.sql
index fb8dc2212..396a27608 100644
--- a/src/exchangedb/0002-prewire.sql
+++ b/src/exchangedb/0002-prewire.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,20 +15,20 @@
--
CREATE FUNCTION create_table_prewire(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'prewire';
+ table_name TEXT DEFAULT 'prewire';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
'(prewire_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY'
',wire_method TEXT NOT NULL'
- ',finished BOOLEAN NOT NULL DEFAULT false'
- ',failed BOOLEAN NOT NULL DEFAULT false'
+ ',finished BOOLEAN NOT NULL DEFAULT FALSE'
+ ',failed BOOLEAN NOT NULL DEFAULT FALSE'
',buf BYTEA NOT NULL'
') %s ;'
,table_name
@@ -63,29 +63,31 @@ $$;
CREATE FUNCTION constrain_table_prewire(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'prewire';
+ table_name TEXT DEFAULT 'prewire';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_by_finished_index '
'ON ' || table_name || ' '
- '(finished);'
+ '(finished)'
+ ' WHERE finished;'
);
EXECUTE FORMAT (
'COMMENT ON INDEX ' || table_name || '_by_finished_index '
- 'IS ' || quote_literal('for gc_prewire') || ';'
+ 'IS ' || quote_literal('for do_gc') || ';'
);
- -- FIXME: find a way to combine these two indices?
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_by_failed_finished_index '
'ON ' || table_name || ' '
- '(failed,finished);'
+ '(prewire_uuid)'
+ ' WHERE finished=FALSE'
+ ' AND failed=FALSE;'
);
EXECUTE FORMAT (
'COMMENT ON INDEX ' || table_name || '_by_failed_finished_index '
diff --git a/src/exchangedb/0002-profit_drains.sql b/src/exchangedb/0002-profit_drains.sql
index 4aba9b46e..c4f3a7bd0 100644
--- a/src/exchangedb/0002-profit_drains.sql
+++ b/src/exchangedb/0002-profit_drains.sql
@@ -17,11 +17,10 @@
CREATE TABLE profit_drains
(profit_drain_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,wtid BYTEA PRIMARY KEY CHECK (LENGTH(wtid)=32)
- ,account_section VARCHAR NOT NULL
- ,payto_uri VARCHAR NOT NULL
+ ,account_section TEXT NOT NULL
+ ,payto_uri TEXT NOT NULL
,trigger_date INT8 NOT NULL
- ,amount_val INT8 NOT NULL
- ,amount_frac INT4 NOT NULL
+ ,amount taler_amount NOT NULL
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
,executed BOOLEAN NOT NULL DEFAULT FALSE
);
@@ -35,7 +34,7 @@ COMMENT ON COLUMN profit_drains.payto_uri
IS 'specifies the account to be credited';
COMMENT ON COLUMN profit_drains.trigger_date
IS 'set by taler-exchange-offline at the time of making the signature; not necessarily the exact date of execution of the wire transfer, just for orientation';
-COMMENT ON COLUMN profit_drains.amount_val
+COMMENT ON COLUMN profit_drains.amount
IS 'amount to be transferred';
COMMENT ON COLUMN profit_drains.master_sig
IS 'EdDSA signature of type TALER_SIGNATURE_MASTER_DRAIN_PROFIT';
diff --git a/src/exchangedb/0002-purse_actions.sql b/src/exchangedb/0002-purse_actions.sql
new file mode 100644
index 000000000..0dd6cfc4d
--- /dev/null
+++ b/src/exchangedb/0002-purse_actions.sql
@@ -0,0 +1,121 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--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/>
+--
+
+
+CREATE OR REPLACE FUNCTION create_table_purse_actions(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'purse_actions';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE IF NOT EXISTS %I'
+ '(purse_pub BYTEA NOT NULL PRIMARY KEY CHECK(LENGTH(purse_pub)=32)'
+ ',action_date INT8 NOT NULL'
+ ',partner_serial_id INT8'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (purse_pub)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'purses awaiting some action by the router'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'public (contract) key of the purse'
+ ,'purse_pub'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'when is the purse ready for action'
+ ,'action_date'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'wad target of an outgoing wire transfer, 0 for local, NULL if the purse is unmerged and thus the target is still unknown'
+ ,'partner_serial_id'
+ ,table_name
+ ,partition_suffix
+ );
+END $$;
+
+
+CREATE OR REPLACE FUNCTION purse_requests_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO
+ purse_actions
+ (purse_pub
+ ,action_date)
+ VALUES
+ (NEW.purse_pub
+ ,NEW.purse_expiration);
+ RETURN NEW;
+END $$;
+
+COMMENT ON FUNCTION purse_requests_insert_trigger()
+ IS 'When a purse is created, insert it into the purse_action table to take action when the purse expires.';
+
+
+CREATE OR REPLACE FUNCTION master_table_purse_actions()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'purse_actions';
+BEGIN
+ -- Create global index
+ CREATE INDEX IF NOT EXISTS purse_action_by_target
+ ON purse_actions
+ (partner_serial_id,action_date);
+
+ -- Setup trigger
+ CREATE TRIGGER purse_requests_on_insert
+ AFTER INSERT
+ ON purse_requests
+ FOR EACH ROW EXECUTE FUNCTION purse_requests_insert_trigger();
+ COMMENT ON TRIGGER purse_requests_on_insert
+ ON purse_requests
+ IS 'Here we install an entry for the purse expiration.';
+END $$;
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+ VALUES
+ ('purse_actions'
+ ,'exchange-0002'
+ ,'create'
+ ,TRUE
+ ,FALSE),
+ ('purse_actions'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
+ ,FALSE);
diff --git a/src/exchangedb/0002-purse_decision.sql b/src/exchangedb/0002-purse_decision.sql
index e738292cd..091bd468b 100644
--- a/src/exchangedb/0002-purse_decision.sql
+++ b/src/exchangedb/0002-purse_decision.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -16,13 +16,13 @@
CREATE FUNCTION create_table_purse_decision(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'purse_decision';
+ table_name TEXT DEFAULT 'purse_decision';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
@@ -51,13 +51,13 @@ END
$$;
CREATE FUNCTION constrain_table_purse_decision(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'purse_decision';
+ table_name TEXT DEFAULT 'purse_decision';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -69,6 +69,56 @@ END
$$;
+CREATE OR REPLACE FUNCTION purse_decision_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ UPDATE purse_requests
+ SET was_decided=TRUE
+ WHERE purse_pub=NEW.purse_pub;
+ IF NEW.refunded
+ THEN
+ INSERT INTO coin_history
+ (coin_pub
+ ,table_name
+ ,serial_id)
+ SELECT
+ pd.coin_pub
+ ,'purse_decision'
+ ,NEW.purse_decision_serial_id
+ FROM purse_deposits pd
+ WHERE purse_pub = NEW.purse_pub;
+ ELSE
+ INSERT INTO reserve_history
+ (reserve_pub
+ ,table_name
+ ,serial_id)
+ SELECT
+ reserve_pub
+ ,'purse_decision'
+ ,NEW.purse_decision_serial_id
+ FROM purse_merges
+ WHERE purse_pub=NEW.purse_pub;
+ END IF;
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION purse_decision_insert_trigger()
+ IS 'Automatically generate coin history entry and update decision status for the purse.';
+
+
+CREATE FUNCTION master_table_purse_decision()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER purse_decision_on_insert
+ AFTER INSERT
+ ON purse_decision
+ FOR EACH ROW EXECUTE FUNCTION purse_decision_insert_trigger();
+END $$;
+
+
INSERT INTO exchange_tables
(name
,version
@@ -85,4 +135,9 @@ INSERT INTO exchange_tables
,'exchange-0002'
,'constrain'
,TRUE
+ ,FALSE),
+ ('purse_decision'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
,FALSE);
diff --git a/src/exchangedb/0002-purse_deletion.sql b/src/exchangedb/0002-purse_deletion.sql
new file mode 100644
index 000000000..45b2e85a9
--- /dev/null
+++ b/src/exchangedb/0002-purse_deletion.sql
@@ -0,0 +1,110 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--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/>
+--
+
+CREATE OR REPLACE FUNCTION create_table_purse_deletion(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'purse_deletion';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE IF NOT EXISTS %I'
+ '(purse_deletion_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',purse_sig BYTEA CHECK (LENGTH(purse_sig)=64)'
+ ',purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32)'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (purse_pub)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'signatures affirming explicit purse deletions'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'signature of type WALLET_PURSE_DELETE'
+ ,'purse_sig'
+ ,table_name
+ ,partition_suffix
+ );
+END $$;
+
+COMMENT ON FUNCTION create_table_purse_deletion
+ IS 'Creates the purse_deletion table';
+
+
+CREATE OR REPLACE FUNCTION constrain_table_purse_deletion(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'purse_deletion';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_delete_serial_key '
+ 'UNIQUE (purse_deletion_serial_id)'
+ );
+END $$;
+
+
+CREATE OR REPLACE FUNCTION master_table_purse_requests_was_deleted (
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'purse_requests';
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE exchange.' || table_name ||
+ ' ADD COLUMN'
+ ' was_deleted BOOLEAN NOT NULL DEFAULT(FALSE)'
+ );
+ COMMENT ON COLUMN purse_requests.was_deleted
+ IS 'TRUE if the purse was explicitly deleted (purse must have an entry in the purse_deletion table)';
+END $$;
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+ VALUES
+ ('purse_deletion'
+ ,'exchange-0002'
+ ,'create'
+ ,TRUE
+ ,FALSE),
+ ('purse_deletion'
+ ,'exchange-0002'
+ ,'constrain'
+ ,TRUE
+ ,FALSE),
+ ('purse_requests_was_deleted'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
+ ,FALSE);
diff --git a/src/exchangedb/0002-purse_deposits.sql b/src/exchangedb/0002-purse_deposits.sql
index 9452f4344..6a07c4b62 100644
--- a/src/exchangedb/0002-purse_deposits.sql
+++ b/src/exchangedb/0002-purse_deposits.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_purse_deposits(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'purse_deposits';
+ table_name TEXT DEFAULT 'purse_deposits';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
@@ -29,8 +29,7 @@ BEGIN
',partner_serial_id INT8'
',purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32)'
',coin_pub BYTEA NOT NULL'
- ',amount_with_fee_val INT8 NOT NULL'
- ',amount_with_fee_frac INT4 NOT NULL'
+ ',amount_with_fee taler_amount NOT NULL'
',coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64)'
',PRIMARY KEY (purse_pub,coin_pub)'
') %s ;'
@@ -63,7 +62,7 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Total amount being deposited'
- ,'amount_with_fee_val'
+ ,'amount_with_fee'
,table_name
,partition_suffix
);
@@ -78,21 +77,16 @@ $$;
CREATE FUNCTION constrain_table_purse_deposits(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'purse_deposits';
+ table_name TEXT DEFAULT 'purse_deposits';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
- -- FIXME: change to materialized index by coin_pub!
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_by_coin_pub'
- ' ON ' || table_name || ' (coin_pub);'
- );
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_purse_deposit_serial_id_key'
@@ -107,7 +101,7 @@ RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'purse_deposits';
+ table_name TEXT DEFAULT 'purse_deposits';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -122,6 +116,37 @@ END
$$;
+CREATE OR REPLACE FUNCTION purse_deposits_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO exchange.coin_history
+ (coin_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.coin_pub
+ ,'purse_deposits'
+ ,NEW.purse_deposit_serial_id);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION purse_deposits_insert_trigger()
+ IS 'Automatically generate coin history entry.';
+
+
+CREATE FUNCTION master_table_purse_deposits()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER purse_deposits_on_insert
+ AFTER INSERT
+ ON purse_deposits
+ FOR EACH ROW EXECUTE FUNCTION purse_deposits_insert_trigger();
+END $$;
+
+
INSERT INTO exchange_tables
(name
,version
@@ -143,4 +168,9 @@ INSERT INTO exchange_tables
,'exchange-0002'
,'foreign'
,TRUE
+ ,FALSE),
+ ('purse_deposits'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
,FALSE);
diff --git a/src/exchangedb/0002-purse_merges.sql b/src/exchangedb/0002-purse_merges.sql
index df369514b..0b4d230b3 100644
--- a/src/exchangedb/0002-purse_merges.sql
+++ b/src/exchangedb/0002-purse_merges.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_purse_merges(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'purse_merges';
+ table_name TEXT DEFAULT 'purse_merges';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
@@ -77,25 +77,16 @@ $$;
CREATE FUNCTION constrain_table_purse_merges(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'purse_merges';
+ table_name TEXT DEFAULT 'purse_merges';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
- -- FIXME: change to materialized index by reserve_pub!
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_reserve_pub '
- 'ON ' || table_name || ' '
- '(reserve_pub);'
- );
- EXECUTE FORMAT (
- 'COMMENT ON INDEX ' || table_name || '_reserve_pub '
- 'IS ' || quote_literal('needed in reserve history computation') || ';'
- );
+
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_purse_merge_request_serial_id_key'
@@ -110,16 +101,13 @@ RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'purse_merges';
+ table_name TEXT DEFAULT 'purse_merges';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_foreign_partner_serial_id'
' FOREIGN KEY (partner_serial_id) '
' REFERENCES partners(partner_serial_id) ON DELETE CASCADE'
- ',ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
- ' FOREIGN KEY (reserve_pub) '
- ' REFERENCES reserves (reserve_pub) ON DELETE CASCADE'
',ADD CONSTRAINT ' || table_name || '_foreign_purse_pub'
' FOREIGN KEY (purse_pub) '
' REFERENCES purse_requests (purse_pub) ON DELETE CASCADE'
diff --git a/src/exchangedb/0002-purse_requests.sql b/src/exchangedb/0002-purse_requests.sql
index 5038c2417..0fa076338 100644
--- a/src/exchangedb/0002-purse_requests.sql
+++ b/src/exchangedb/0002-purse_requests.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_purse_requests(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'purse_requests';
+ table_name TEXT DEFAULT 'purse_requests';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
@@ -34,12 +34,10 @@ BEGIN
',age_limit INT4 NOT NULL'
',flags INT4 NOT NULL'
',in_reserve_quota BOOLEAN NOT NULL DEFAULT(FALSE)'
- ',amount_with_fee_val INT8 NOT NULL'
- ',amount_with_fee_frac INT4 NOT NULL'
- ',purse_fee_val INT8 NOT NULL'
- ',purse_fee_frac INT4 NOT NULL'
- ',balance_val INT8 NOT NULL DEFAULT (0)'
- ',balance_frac INT4 NOT NULL DEFAULT (0)'
+ ',was_decided BOOLEAN NOT NULL DEFAULT(FALSE)'
+ ',amount_with_fee taler_amount NOT NULL'
+ ',purse_fee taler_amount NOT NULL'
+ ',balance taler_amount NOT NULL DEFAULT (0,0)'
',purse_sig BYTEA NOT NULL CHECK(LENGTH(purse_sig)=64)'
',PRIMARY KEY (purse_pub)'
') %s ;'
@@ -90,19 +88,19 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Total amount expected to be in the purse'
- ,'amount_with_fee_val'
+ ,'amount_with_fee'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Purse fee the client agreed to pay from the reserve (accepted by the exchange at the time the purse was created). Zero if in_reserve_quota is TRUE.'
- ,'purse_fee_val'
+ ,'purse_fee'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Total amount actually in the purse (updated)'
- ,'balance_val'
+ ,'balance'
,table_name
,partition_suffix
);
@@ -116,28 +114,26 @@ END
$$;
CREATE FUNCTION constrain_table_purse_requests(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'purse_requests';
+ table_name TEXT DEFAULT 'purse_requests';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
- -- FIXME: change to materialized index by merge_pub!
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_merge_pub '
'ON ' || table_name || ' '
'(merge_pub);'
);
- -- FIXME: drop index on master (crosses partitions)?
- -- Or use materialized index? (needed?)
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_purse_expiration '
'ON ' || table_name || ' '
- '(purse_expiration);'
+ '(purse_expiration) ' ||
+ 'WHERE NOT was_decided;'
);
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
diff --git a/src/exchangedb/0002-recoup.sql b/src/exchangedb/0002-recoup.sql
index 36e36d9d9..4b3452498 100644
--- a/src/exchangedb/0002-recoup.sql
+++ b/src/exchangedb/0002-recoup.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_recoup(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'recoup';
+ table_name TEXT DEFAULT 'recoup';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
@@ -29,8 +29,7 @@ BEGIN
',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)'
',coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64)'
',coin_blind BYTEA NOT NULL CHECK(LENGTH(coin_blind)=32)'
- ',amount_val INT8 NOT NULL'
- ',amount_frac INT4 NOT NULL'
+ ',amount taler_amount NOT NULL'
',recoup_timestamp INT8 NOT NULL'
',reserve_out_serial_id INT8 NOT NULL'
') %s ;'
@@ -72,13 +71,13 @@ $$;
CREATE FUNCTION constrain_table_recoup(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'recoup';
+ table_name TEXT DEFAULT 'recoup';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -100,7 +99,7 @@ RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'recoup';
+ table_name TEXT DEFAULT 'recoup';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -116,13 +115,13 @@ $$;
CREATE FUNCTION create_table_recoup_by_reserve(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'recoup_by_reserve';
+ table_name TEXT DEFAULT 'recoup_by_reserve';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
@@ -143,13 +142,13 @@ $$;
CREATE FUNCTION constrain_table_recoup_by_reserve(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'recoup_by_reserve';
+ table_name TEXT DEFAULT 'recoup_by_reserve';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -166,16 +165,36 @@ CREATE FUNCTION recoup_insert_trigger()
LANGUAGE plpgsql
AS $$
BEGIN
- INSERT INTO exchange.recoup_by_reserve
+ INSERT INTO recoup_by_reserve
(reserve_out_serial_id
,coin_pub)
VALUES
(NEW.reserve_out_serial_id
,NEW.coin_pub);
+ INSERT INTO coin_history
+ (coin_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.coin_pub
+ ,'recoup'
+ ,NEW.recoup_uuid);
+ INSERT INTO reserve_history
+ (reserve_pub
+ ,table_name
+ ,serial_id)
+ SELECT
+ res.reserve_pub
+ ,'recoup'
+ ,NEW.recoup_uuid
+ FROM reserves_out rout
+ JOIN reserves res
+ USING (reserve_uuid)
+ WHERE rout.reserve_out_serial_id = NEW.reserve_out_serial_id;
RETURN NEW;
END $$;
COMMENT ON FUNCTION recoup_insert_trigger()
- IS 'Replicate recoup inserts into recoup_by_reserve table.';
+ IS 'Replicates recoup inserts into recoup_by_reserve table and updates the coin_history table.';
CREATE FUNCTION recoup_delete_trigger()
@@ -183,7 +202,7 @@ CREATE FUNCTION recoup_delete_trigger()
LANGUAGE plpgsql
AS $$
BEGIN
- DELETE FROM exchange.recoup_by_reserve
+ DELETE FROM recoup_by_reserve
WHERE reserve_out_serial_id = OLD.reserve_out_serial_id
AND coin_pub = OLD.coin_pub;
RETURN OLD;
diff --git a/src/exchangedb/0002-recoup_refresh.sql b/src/exchangedb/0002-recoup_refresh.sql
index bfcfb3d8d..8b979a49f 100644
--- a/src/exchangedb/0002-recoup_refresh.sql
+++ b/src/exchangedb/0002-recoup_refresh.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -16,13 +16,13 @@
CREATE FUNCTION create_table_recoup_refresh(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'recoup_refresh';
+ table_name TEXT DEFAULT 'recoup_refresh';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
@@ -31,8 +31,7 @@ BEGIN
',known_coin_id BIGINT NOT NULL'
',coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64)'
',coin_blind BYTEA NOT NULL CHECK(LENGTH(coin_blind)=32)'
- ',amount_val INT8 NOT NULL'
- ',amount_frac INT4 NOT NULL'
+ ',amount taler_amount NOT NULL'
',recoup_timestamp INT8 NOT NULL'
',rrc_serial INT8 NOT NULL'
') %s ;'
@@ -52,7 +51,7 @@ BEGIN
,partition_suffix
);
PERFORM comment_partitioned_column(
- 'FIXME: (To be) used for garbage collection (in the absence of foreign constraints, in the future)'
+ 'Used for garbage collection (in the absence of foreign constraints, in the future)'
,'known_coin_id'
,table_name
,partition_suffix
@@ -74,23 +73,27 @@ $$;
CREATE FUNCTION constrain_table_recoup_refresh(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'recoup_refresh';
+ table_name TEXT DEFAULT 'recoup_refresh';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
- -- FIXME: any query using this index will be slow. Materialize index or change query?
- -- Also: which query uses this index?
+
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_by_rrc_serial_index'
' ON ' || table_name || ' '
'(rrc_serial);'
);
EXECUTE FORMAT (
+ 'COMMENT ON INDEX ' || table_name || '_by_rrc_serial_index '
+ 'IS ' || quote_literal('used in exchange_do_melt for zombie coins (rare)') || ';'
+ );
+
+ EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_by_coin_pub_index'
' ON ' || table_name || ' '
'(coin_pub);'
@@ -109,7 +112,7 @@ RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'recoup_refresh';
+ table_name TEXT DEFAULT 'recoup_refresh';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -127,6 +130,50 @@ END
$$;
+CREATE OR REPLACE FUNCTION recoup_refresh_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO exchange.coin_history
+ (coin_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.coin_pub
+ ,'recoup_refresh::NEW'
+ ,NEW.recoup_refresh_uuid);
+ INSERT INTO exchange.coin_history
+ (coin_pub
+ ,table_name
+ ,serial_id)
+ SELECT
+ melt.old_coin_pub
+ ,'recoup_refresh::OLD'
+ ,NEW.recoup_refresh_uuid
+ FROM refresh_revealed_coins rrc
+ JOIN refresh_commitments melt
+ USING (melt_serial_id)
+ WHERE rrc.rrc_serial = NEW.rrc_serial;
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION coin_deposits_insert_trigger()
+ IS 'Automatically generate coin history entry.';
+
+
+CREATE FUNCTION master_table_recoup_refresh()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER recoup_refresh_on_insert
+ AFTER INSERT
+ ON recoup_refresh
+ FOR EACH ROW EXECUTE FUNCTION recoup_refresh_insert_trigger();
+END $$;
+
+
+
INSERT INTO exchange_tables
(name
,version
@@ -148,4 +195,9 @@ INSERT INTO exchange_tables
,'exchange-0002'
,'foreign'
,TRUE
+ ,FALSE),
+ ('recoup_refresh'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
,FALSE);
diff --git a/src/exchangedb/0002-refresh_commitments.sql b/src/exchangedb/0002-refresh_commitments.sql
index c63995c74..e577f1e1c 100644
--- a/src/exchangedb/0002-refresh_commitments.sql
+++ b/src/exchangedb/0002-refresh_commitments.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,22 +15,21 @@
--
CREATE FUNCTION create_table_refresh_commitments(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refresh_commitments';
+ table_name TEXT DEFAULT 'refresh_commitments';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
- '(melt_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE'
+ '(melt_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
',rc BYTEA PRIMARY KEY CHECK (LENGTH(rc)=64)'
',old_coin_pub BYTEA NOT NULL'
',old_coin_sig BYTEA NOT NULL CHECK(LENGTH(old_coin_sig)=64)'
- ',amount_with_fee_val INT8 NOT NULL'
- ',amount_with_fee_frac INT4 NOT NULL'
+ ',amount_with_fee taler_amount NOT NULL'
',noreveal_index INT4 NOT NULL'
') %s ;'
,table_name
@@ -65,13 +64,13 @@ $$;
CREATE FUNCTION constrain_table_refresh_commitments(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refresh_commitments';
+ table_name TEXT DEFAULT 'refresh_commitments';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
@@ -95,7 +94,7 @@ RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refresh_commitments';
+ table_name TEXT DEFAULT 'refresh_commitments';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -107,6 +106,37 @@ END
$$;
+CREATE OR REPLACE FUNCTION refresh_commitments_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO exchange.coin_history
+ (coin_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.old_coin_pub
+ ,'refresh_commitments'
+ ,NEW.melt_serial_id);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION refresh_commitments_insert_trigger()
+ IS 'Automatically generate coin history entry.';
+
+
+CREATE FUNCTION master_table_refresh_commitments()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER refresh_commitments_on_insert
+ AFTER INSERT
+ ON refresh_commitments
+ FOR EACH ROW EXECUTE FUNCTION refresh_commitments_insert_trigger();
+END $$;
+
+
INSERT INTO exchange_tables
(name
,version
@@ -128,4 +158,9 @@ INSERT INTO exchange_tables
,'exchange-0002'
,'foreign'
,TRUE
+ ,FALSE),
+ ('refresh_commitments'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
,FALSE);
diff --git a/src/exchangedb/0002-refresh_revealed_coins.sql b/src/exchangedb/0002-refresh_revealed_coins.sql
index 912e4bbbd..ad65c9942 100644
--- a/src/exchangedb/0002-refresh_revealed_coins.sql
+++ b/src/exchangedb/0002-refresh_revealed_coins.sql
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_refresh_revealed_coins(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refresh_revealed_coins';
+ table_name TEXT DEFAULT 'refresh_revealed_coins';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
@@ -63,6 +63,12 @@ BEGIN
,partition_suffix
);
PERFORM comment_partitioned_column(
+ 'Signature of type WALLET_COIN_LINK, proves exchange did not tamper with the link data'
+ ,'link_sig'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
'envelope of the new coin to be signed'
,'coin_ev'
,table_name
@@ -91,13 +97,13 @@ $$;
CREATE FUNCTION constrain_table_refresh_revealed_coins(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refresh_revealed_coins';
+ table_name TEXT DEFAULT 'refresh_revealed_coins';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -124,7 +130,7 @@ RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refresh_revealed_coins';
+ table_name TEXT DEFAULT 'refresh_revealed_coins';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
diff --git a/src/exchangedb/0002-refresh_transfer_keys.sql b/src/exchangedb/0002-refresh_transfer_keys.sql
index 4d10dda1b..9bcb912da 100644
--- a/src/exchangedb/0002-refresh_transfer_keys.sql
+++ b/src/exchangedb/0002-refresh_transfer_keys.sql
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_refresh_transfer_keys(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refresh_transfer_keys';
+ table_name TEXT DEFAULT 'refresh_transfer_keys';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
@@ -68,13 +68,13 @@ $$;
CREATE FUNCTION constrain_table_refresh_transfer_keys(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refresh_transfer_keys';
+ table_name TEXT DEFAULT 'refresh_transfer_keys';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -91,7 +91,7 @@ RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refresh_transfer_keys';
+ table_name TEXT DEFAULT 'refresh_transfer_keys';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
diff --git a/src/exchangedb/0002-refunds.sql b/src/exchangedb/0002-refunds.sql
index 88af42db3..2a40bc192 100644
--- a/src/exchangedb/0002-refunds.sql
+++ b/src/exchangedb/0002-refunds.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,23 +15,22 @@
--
CREATE FUNCTION create_table_refunds(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refunds';
+ table_name TEXT DEFAULT 'refunds';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
'(refund_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)'
- ',deposit_serial_id INT8 NOT NULL'
+ ',batch_deposit_serial_id INT8 NOT NULL'
',merchant_sig BYTEA NOT NULL CHECK(LENGTH(merchant_sig)=64)'
',rtransaction_id INT8 NOT NULL'
- ',amount_with_fee_val INT8 NOT NULL'
- ',amount_with_fee_frac INT4 NOT NULL'
+ ',amount_with_fee taler_amount NOT NULL'
') %s ;'
,table_name
,'PARTITION BY HASH (coin_pub)'
@@ -44,7 +43,7 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Identifies ONLY the merchant_pub, h_contract_terms and coin_pub. Multiple deposits may match a refund, this only identifies one of them.'
- ,'deposit_serial_id'
+ ,'batch_deposit_serial_id'
,table_name
,partition_suffix
);
@@ -59,13 +58,13 @@ $$;
CREATE FUNCTION constrain_table_refunds (
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refunds';
+ table_name TEXT DEFAULT 'refunds';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -77,7 +76,7 @@ BEGIN
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_refund_serial_id_key'
' UNIQUE (refund_serial_id) '
- ',ADD PRIMARY KEY (deposit_serial_id, rtransaction_id) '
+ ',ADD PRIMARY KEY (batch_deposit_serial_id, rtransaction_id) '
);
END
$$;
@@ -88,7 +87,7 @@ RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'refunds';
+ table_name TEXT DEFAULT 'refunds';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -96,13 +95,44 @@ BEGIN
' FOREIGN KEY (coin_pub) '
' REFERENCES known_coins (coin_pub) ON DELETE CASCADE'
',ADD CONSTRAINT ' || table_name || '_foreign_deposit'
- ' FOREIGN KEY (deposit_serial_id) '
- ' REFERENCES deposits (deposit_serial_id) ON DELETE CASCADE'
+ ' FOREIGN KEY (batch_deposit_serial_id) '
+ ' REFERENCES batch_deposits (batch_deposit_serial_id) ON DELETE CASCADE'
);
END
$$;
+CREATE OR REPLACE FUNCTION refunds_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO exchange.coin_history
+ (coin_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.coin_pub
+ ,'refunds'
+ ,NEW.refund_serial_id);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION refunds_insert_trigger()
+ IS 'Automatically generate coin history entry.';
+
+
+CREATE FUNCTION master_table_refunds()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER refunds_on_insert
+ AFTER INSERT
+ ON refunds
+ FOR EACH ROW EXECUTE FUNCTION refunds_insert_trigger();
+END $$;
+
+
INSERT INTO exchange_tables
(name
,version
@@ -124,4 +154,9 @@ INSERT INTO exchange_tables
,'exchange-0002'
,'foreign'
,TRUE
+ ,FALSE),
+ ('refunds'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
,FALSE);
diff --git a/src/exchangedb/0002-reserve_history.sql b/src/exchangedb/0002-reserve_history.sql
new file mode 100644
index 000000000..b0c764306
--- /dev/null
+++ b/src/exchangedb/0002-reserve_history.sql
@@ -0,0 +1,138 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2023 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/>
+--
+
+CREATE FUNCTION create_table_reserve_history (
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'reserve_history';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE %I'
+ '(reserve_history_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY'
+ ',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)'
+ ',table_name TEXT NOT NULL'
+ ',serial_id INT8 NOT NULL'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (reserve_pub)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'Links to tables with entries that affected the transaction history of a reserve.'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'For which reserve is this a history entry'
+ ,'reserve_pub'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'In which table is the history entry'
+ ,'table_name'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Which is the generated serial ID of the entry in the table'
+ ,'serial_id'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Monotonic counter, used to generate Etags for caching'
+ ,'reserve_history_serial_id'
+ ,table_name
+ ,partition_suffix
+ );
+END
+$$;
+
+
+CREATE FUNCTION constrain_table_reserve_history(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'reserve_history';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_reserve_history_serial_id_pkey'
+ ' PRIMARY KEY (reserve_history_serial_id) '
+ ',ADD CONSTRAINT ' || table_name || '_reserve_entry_key'
+ ' UNIQUE (reserve_pub, table_name, serial_id)'
+ );
+ EXECUTE FORMAT (
+ 'CREATE INDEX ' || table_name || '_reserve_by_time'
+ ' ON ' || table_name || ' '
+ '(reserve_pub'
+ ',reserve_history_serial_id DESC'
+ ');'
+ );
+END
+$$;
+
+
+CREATE FUNCTION foreign_table_reserve_history()
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'reserve_history';
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
+ ' FOREIGN KEY (reserve_pub) '
+ ' REFERENCES reserves (reserve_pub) ON DELETE CASCADE'
+ );
+END
+$$;
+
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+ VALUES
+ ('reserve_history'
+ ,'exchange-0002'
+ ,'create'
+ ,TRUE
+ ,FALSE),
+ ('reserve_history'
+ ,'exchange-0002'
+ ,'constrain'
+ ,TRUE
+ ,FALSE),
+ ('reserve_history'
+ ,'exchange-0002'
+ ,'foreign'
+ ,TRUE
+ ,FALSE)
+ ;
diff --git a/src/exchangedb/0002-reserves.sql b/src/exchangedb/0002-reserves.sql
index 03d17aee2..d710dd01b 100644
--- a/src/exchangedb/0002-reserves.sql
+++ b/src/exchangedb/0002-reserves.sql
@@ -15,23 +15,22 @@
--
CREATE FUNCTION create_table_reserves(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'reserves';
+ table_name TEXT DEFAULT 'reserves';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
'(reserve_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY'
',reserve_pub BYTEA PRIMARY KEY CHECK(LENGTH(reserve_pub)=32)'
- ',current_balance_val INT8 NOT NULL DEFAULT(0)'
- ',current_balance_frac INT4 NOT NULL DEFAULT(0)'
+ ',current_balance taler_amount NOT NULL DEFAULT (0, 0)'
',purses_active INT8 NOT NULL DEFAULT(0)'
',purses_allowed INT8 NOT NULL DEFAULT(0)'
- ',max_age INT4 NOT NULL DEFAULT(120)'
+ ',birthday INT4 NOT NULL DEFAULT(0)'
',expiration_date INT8 NOT NULL'
',gc_date INT8 NOT NULL'
') %s ;'
@@ -52,7 +51,7 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Current balance remaining with the reserve.'
- ,'current_balance_val'
+ ,'current_balance'
,table_name
,partition_suffix
);
@@ -80,18 +79,24 @@ BEGIN
,table_name
,partition_suffix
);
+ PERFORM comment_partitioned_column(
+ 'Birthday of the user in days after 1970, or 0 if user is an adult and is not subject to age restrictions'
+ ,'birthday'
+ ,table_name
+ ,partition_suffix
+ );
END
$$;
CREATE FUNCTION constrain_table_reserves(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'reserves';
+ table_name TEXT DEFAULT 'reserves';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -103,8 +108,7 @@ BEGIN
'CREATE INDEX ' || table_name || '_by_expiration_index '
'ON ' || table_name || ' '
'(expiration_date'
- ',current_balance_val'
- ',current_balance_frac'
+ ',current_balance'
');'
);
EXECUTE FORMAT (
diff --git a/src/exchangedb/0002-reserves_close.sql b/src/exchangedb/0002-reserves_close.sql
index 52931b877..16669768d 100644
--- a/src/exchangedb/0002-reserves_close.sql
+++ b/src/exchangedb/0002-reserves_close.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_reserves_close(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_close';
+ table_name TEXT default 'reserves_close';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
@@ -30,10 +30,8 @@ BEGIN
',execution_date INT8 NOT NULL'
',wtid BYTEA NOT NULL CHECK (LENGTH(wtid)=32)'
',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)'
- ',amount_val INT8 NOT NULL'
- ',amount_frac INT4 NOT NULL'
- ',closing_fee_val INT8 NOT NULL'
- ',closing_fee_frac INT4 NOT NULL'
+ ',amount taler_amount NOT NULL'
+ ',closing_fee taler_amount NOT NULL'
',close_request_row INT8 NOT NULL DEFAULT(0)'
') %s ;'
,table_name
@@ -56,13 +54,13 @@ $$;
CREATE FUNCTION constrain_table_reserves_close(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_close';
+ table_name TEXT default 'reserves_close';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -82,7 +80,7 @@ RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_close';
+ table_name TEXT default 'reserves_close';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -93,6 +91,37 @@ BEGIN
END $$;
+CREATE OR REPLACE FUNCTION reserves_close_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO reserve_history
+ (reserve_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.reserve_pub
+ ,'reserves_close'
+ ,NEW.close_uuid);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION reserves_close_insert_trigger()
+ IS 'Automatically generate reserve history entry.';
+
+
+CREATE FUNCTION master_table_reserves_close()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER reserves_close_on_insert
+ AFTER INSERT
+ ON reserves_close
+ FOR EACH ROW EXECUTE FUNCTION reserves_close_insert_trigger();
+END $$;
+
+
INSERT INTO exchange_tables
(name
,version
@@ -114,4 +143,9 @@ INSERT INTO exchange_tables
,'exchange-0002'
,'foreign'
,TRUE
+ ,FALSE),
+ ('reserves_close'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
,FALSE);
diff --git a/src/exchangedb/0002-reserves_in.sql b/src/exchangedb/0002-reserves_in.sql
index d722a49e8..197a815b3 100644
--- a/src/exchangedb/0002-reserves_in.sql
+++ b/src/exchangedb/0002-reserves_in.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,21 +15,20 @@
--
CREATE FUNCTION create_table_reserves_in(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_in';
+ table_name TEXT default 'reserves_in';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
- '(reserve_in_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE'
- ',reserve_pub BYTEA PRIMARY KEY' -- REFERENCES reserves (reserve_pub) ON DELETE CASCADE'
+ '(reserve_in_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',reserve_pub BYTEA PRIMARY KEY'
',wire_reference INT8 NOT NULL'
- ',credit_val INT8 NOT NULL'
- ',credit_frac INT4 NOT NULL'
+ ',credit taler_amount NOT NULL'
',wire_source_h_payto BYTEA CHECK (LENGTH(wire_source_h_payto)=32)'
',exchange_account_section TEXT NOT NULL'
',execution_date INT8 NOT NULL'
@@ -57,7 +56,7 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Amount that was transferred into the reserve'
- ,'credit_val'
+ ,'credit'
,table_name
,partition_suffix
);
@@ -65,13 +64,13 @@ END $$;
CREATE FUNCTION constrain_table_reserves_in(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_in';
+ table_name TEXT default 'reserves_in';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -84,25 +83,68 @@ BEGIN
'ON ' || table_name || ' '
'(reserve_in_serial_id);'
);
- -- FIXME: where do we need this index? Can we do better?
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_by_exch_accnt_section_execution_date_idx '
- 'ON ' || table_name || ' '
- '(exchange_account_section '
- ',execution_date'
- ');'
- );
- -- FIXME: where do we need this index? Can we do better?
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_by_exch_accnt_reserve_in_serial_id_idx '
'ON ' || table_name || ' '
'(exchange_account_section'
- ',reserve_in_serial_id DESC'
+ ',reserve_in_serial_id ASC'
');'
);
+ EXECUTE FORMAT (
+ 'COMMENT ON INDEX ' || table_name || '_by_exch_accnt_reserve_in_serial_id_idx '
+ 'IS ' || quote_literal ('for pg_select_reserves_in_above_serial_id_by_account') || ';'
+ );
+
END
$$;
+CREATE FUNCTION foreign_table_reserves_in()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'reserves_in';
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
+ ' FOREIGN KEY (reserve_pub) '
+ ' REFERENCES reserves(reserve_pub) ON DELETE CASCADE'
+ );
+END $$;
+
+
+
+CREATE OR REPLACE FUNCTION reserves_in_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO reserve_history
+ (reserve_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.reserve_pub
+ ,'reserves_in'
+ ,NEW.reserve_in_serial_id);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION reserves_in_insert_trigger()
+ IS 'Automatically generate reserve history entry.';
+
+
+CREATE FUNCTION master_table_reserves_in()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER reserves_in_on_insert
+ AFTER INSERT
+ ON reserves_in
+ FOR EACH ROW EXECUTE FUNCTION reserves_in_insert_trigger();
+END $$;
+
INSERT INTO exchange_tables
(name
@@ -120,4 +162,14 @@ INSERT INTO exchange_tables
,'exchange-0002'
,'constrain'
,TRUE
+ ,FALSE),
+ ('reserves_in'
+ ,'exchange-0002'
+ ,'foreign'
+ ,TRUE
+ ,FALSE),
+ ('reserves_in'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
,FALSE);
diff --git a/src/exchangedb/0002-reserves_open_deposits.sql b/src/exchangedb/0002-reserves_open_deposits.sql
index 35605d360..776859df8 100644
--- a/src/exchangedb/0002-reserves_open_deposits.sql
+++ b/src/exchangedb/0002-reserves_open_deposits.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_reserves_open_deposits(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_open_deposits';
+ table_name TEXT default 'reserves_open_deposits';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
@@ -30,8 +30,7 @@ BEGIN
',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)'
',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)'
',coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)'
- ',contribution_val INT8 NOT NULL'
- ',contribution_frac INT4 NOT NULL'
+ ',contribution taler_amount NOT NULL'
') %s ;'
,table_name
,'PARTITION BY HASH (coin_pub)'
@@ -53,13 +52,13 @@ $$;
CREATE FUNCTION constrain_table_reserves_open_deposits(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_open_deposits';
+ table_name TEXT default 'reserves_open_deposits';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -81,6 +80,37 @@ END
$$;
+CREATE OR REPLACE FUNCTION reserves_open_deposits_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO exchange.coin_history
+ (coin_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.coin_pub
+ ,'reserves_open_deposits'
+ ,NEW.reserve_open_deposit_uuid);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION reserves_open_deposits_insert_trigger()
+ IS 'Automatically generate coin history entry.';
+
+
+CREATE FUNCTION master_table_reserves_open_deposits()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER reserves_open_deposits_on_insert
+ AFTER INSERT
+ ON reserves_open_deposits
+ FOR EACH ROW EXECUTE FUNCTION reserves_open_deposits_insert_trigger();
+END $$;
+
+
INSERT INTO exchange_tables
(name
,version
@@ -97,4 +127,9 @@ INSERT INTO exchange_tables
,'exchange-0002'
,'constrain'
,TRUE
+ ,FALSE),
+ ('reserves_open_deposits'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
,FALSE);
diff --git a/src/exchangedb/0002-reserves_open_requests.sql b/src/exchangedb/0002-reserves_open_requests.sql
index bbd5ec90f..b51168dc0 100644
--- a/src/exchangedb/0002-reserves_open_requests.sql
+++ b/src/exchangedb/0002-reserves_open_requests.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_reserves_open_requests(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_open_requests';
+ table_name TEXT default 'reserves_open_requests';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
@@ -30,8 +30,7 @@ BEGIN
',request_timestamp INT8 NOT NULL'
',expiration_date INT8 NOT NULL'
',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
- ',reserve_payment_val INT8 NOT NULL'
- ',reserve_payment_frac INT4 NOT NULL'
+ ',reserve_payment taler_amount NOT NULL'
',requested_purse_limit INT4 NOT NULL'
') %s ;'
,table_name
@@ -45,7 +44,7 @@ BEGIN
);
PERFORM comment_partitioned_column (
'Fee to pay for the request from the reserve balance itself.'
- ,'reserve_payment_val'
+ ,'reserve_payment'
,table_name
,partition_suffix
);
@@ -54,13 +53,13 @@ $$;
CREATE FUNCTION constrain_table_reserves_open_requests(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_open_requests';
+ table_name TEXT default 'reserves_open_requests';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -79,7 +78,7 @@ RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_open_requests';
+ table_name TEXT default 'reserves_open_requests';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -91,6 +90,37 @@ END
$$;
+CREATE OR REPLACE FUNCTION reserves_open_requests_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO reserve_history
+ (reserve_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.reserve_pub
+ ,'reserves_open_requests'
+ ,NEW.open_request_uuid);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION reserves_open_requests_insert_trigger()
+ IS 'Automatically generate reserve history entry.';
+
+
+CREATE FUNCTION master_table_reserves_open_requests()
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ CREATE TRIGGER reserves_open_requests_on_insert
+ AFTER INSERT
+ ON reserves_open_requests
+ FOR EACH ROW EXECUTE FUNCTION reserves_open_requests_insert_trigger();
+END $$;
+
+
INSERT INTO exchange_tables
(name
,version
@@ -112,4 +142,9 @@ INSERT INTO exchange_tables
,'exchange-0002'
,'foreign'
,TRUE
+ ,FALSE),
+ ('reserves_open_requests'
+ ,'exchange-0002'
+ ,'master'
+ ,TRUE
,FALSE);
diff --git a/src/exchangedb/0002-reserves_out.sql b/src/exchangedb/0002-reserves_out.sql
index 25d717a52..f0965d222 100644
--- a/src/exchangedb/0002-reserves_out.sql
+++ b/src/exchangedb/0002-reserves_out.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_reserves_out(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_out';
+ table_name TEXT default 'reserves_out';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
@@ -32,8 +32,7 @@ BEGIN
',reserve_uuid INT8 NOT NULL'
',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
',execution_date INT8 NOT NULL'
- ',amount_with_fee_val INT8 NOT NULL'
- ',amount_with_fee_frac INT4 NOT NULL'
+ ',amount_with_fee taler_amount NOT NULL'
') %s ;'
,'reserves_out'
,'PARTITION BY HASH (h_blind_ev)'
@@ -61,13 +60,13 @@ $$;
CREATE FUNCTION constrain_table_reserves_out(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_out';
+ table_name TEXT default 'reserves_out';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -75,7 +74,6 @@ BEGIN
' ADD CONSTRAINT ' || table_name || '_reserve_out_serial_id_key'
' UNIQUE (reserve_out_serial_id)'
);
- -- FIXME: change query to use reserves_out_by_reserve instead and materialize execution_date there as well???
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_by_reserve_uuid_and_execution_date_index '
'ON ' || table_name || ' '
@@ -83,7 +81,7 @@ BEGIN
);
EXECUTE FORMAT (
'COMMENT ON INDEX ' || table_name || '_by_reserve_uuid_and_execution_date_index '
- 'IS ' || quote_literal('for get_reserves_out and exchange_do_withdraw_limit_check') || ';'
+ 'IS ' || quote_literal('for do_gc, do_recoup_by_reserve, select_kyc_relevant_withdraw_events and a few others') || ';'
);
END
$$;
@@ -94,7 +92,7 @@ RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR default 'reserves_out';
+ table_name TEXT default 'reserves_out';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
@@ -109,78 +107,26 @@ END
$$;
-CREATE FUNCTION create_table_reserves_out_by_reserve(
- IN partition_suffix VARCHAR DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'reserves_out_by_reserve';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(reserve_uuid INT8 NOT NULL' -- REFERENCES reserves (reserve_uuid) ON DELETE CASCADE
- ',h_blind_ev BYTEA CHECK (LENGTH(h_blind_ev)=64)'
- ') %s '
- ,table_name
- ,'PARTITION BY HASH (reserve_uuid)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table (
- 'Information in this table is strictly redundant with that of reserves_out, but saved by a different primary key for fast lookups by reserve public key/uuid.'
- ,table_name
- ,partition_suffix
- );
-END $$;
-
-
-CREATE FUNCTION constrain_table_reserves_out_by_reserve(
- IN partition_suffix VARCHAR DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'reserves_out_by_reserve';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_main_index '
- 'ON ' || table_name || ' '
- '(reserve_uuid);'
- );
-END $$;
-
-
-CREATE FUNCTION reserves_out_by_reserve_insert_trigger()
+CREATE FUNCTION reserves_out_insert_trigger()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
- INSERT INTO exchange.reserves_out_by_reserve
- (reserve_uuid
- ,h_blind_ev)
- VALUES
- (NEW.reserve_uuid
- ,NEW.h_blind_ev);
+ INSERT INTO reserve_history
+ (reserve_pub
+ ,table_name
+ ,serial_id)
+ SELECT
+ res.reserve_pub
+ ,'reserves_out'
+ ,NEW.reserve_out_serial_id
+ FROM
+ reserves res
+ WHERE res.reserve_uuid = NEW.reserve_uuid;
RETURN NEW;
END $$;
-COMMENT ON FUNCTION reserves_out_by_reserve_insert_trigger()
- IS 'Replicate reserve_out inserts into reserve_out_by_reserve table.';
-
-
-CREATE FUNCTION reserves_out_by_reserve_delete_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- DELETE FROM exchange.reserves_out_by_reserve
- WHERE reserve_uuid = OLD.reserve_uuid;
- RETURN OLD;
-END $$;
-COMMENT ON FUNCTION reserves_out_by_reserve_delete_trigger()
- IS 'Replicate reserve_out deletions into reserve_out_by_reserve table.';
+COMMENT ON FUNCTION reserves_out_insert_trigger()
+ IS 'Replicate reserve_out inserts into reserve_history table.';
CREATE FUNCTION master_table_reserves_out()
@@ -191,14 +137,11 @@ BEGIN
CREATE TRIGGER reserves_out_on_insert
AFTER INSERT
ON reserves_out
- FOR EACH ROW EXECUTE FUNCTION reserves_out_by_reserve_insert_trigger();
- CREATE TRIGGER reserves_out_on_delete
- AFTER DELETE
- ON reserves_out
- FOR EACH ROW EXECUTE FUNCTION reserves_out_by_reserve_delete_trigger();
+ FOR EACH ROW EXECUTE FUNCTION reserves_out_insert_trigger();
END $$;
COMMENT ON FUNCTION master_table_reserves_out()
- IS 'Setup triggers to replicate reserve_out into reserve_out_by_reserve.';
+ IS 'Setup triggers to replicate reserve_out into reserve_history.';
+
INSERT INTO exchange_tables
@@ -223,16 +166,6 @@ INSERT INTO exchange_tables
,'foreign'
,TRUE
,FALSE),
- ('reserves_out_by_reserve'
- ,'exchange-0002'
- ,'create'
- ,TRUE
- ,FALSE),
- ('reserves_out_by_reserve'
- ,'exchange-0002'
- ,'constrain'
- ,TRUE
- ,FALSE),
('reserves_out'
,'exchange-0002'
,'master'
diff --git a/src/exchangedb/0002-revolving_work_shards.sql b/src/exchangedb/0002-revolving_work_shards.sql
index 83094297e..8cfff09b4 100644
--- a/src/exchangedb/0002-revolving_work_shards.sql
+++ b/src/exchangedb/0002-revolving_work_shards.sql
@@ -20,7 +20,7 @@ CREATE UNLOGGED TABLE revolving_work_shards
,start_row INT4 NOT NULL
,end_row INT4 NOT NULL
,active BOOLEAN NOT NULL DEFAULT FALSE
- ,job_name VARCHAR NOT NULL
+ ,job_name TEXT NOT NULL
,PRIMARY KEY (job_name, start_row)
);
COMMENT ON TABLE revolving_work_shards
diff --git a/src/exchangedb/0002-wad_in_entries.sql b/src/exchangedb/0002-wad_in_entries.sql
index 63c8bca2b..3ef1f1b8e 100644
--- a/src/exchangedb/0002-wad_in_entries.sql
+++ b/src/exchangedb/0002-wad_in_entries.sql
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_wad_in_entries(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wad_in_entries';
+ table_name TEXT DEFAULT 'wad_in_entries';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
@@ -32,12 +32,9 @@ BEGIN
',h_contract BYTEA NOT NULL CHECK(LENGTH(h_contract)=64)'
',purse_expiration INT8 NOT NULL'
',merge_timestamp INT8 NOT NULL'
- ',amount_with_fee_val INT8 NOT NULL'
- ',amount_with_fee_frac INT4 NOT NULL'
- ',wad_fee_val INT8 NOT NULL'
- ',wad_fee_frac INT4 NOT NULL'
- ',deposit_fees_val INT8 NOT NULL'
- ',deposit_fees_frac INT4 NOT NULL'
+ ',amount_with_fee taler_amount NOT NULL'
+ ',wad_fee taler_amount NOT NULL'
+ ',deposit_fees taler_amount NOT NULL'
',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
',purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)'
') %s ;'
@@ -88,19 +85,19 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Total amount in the purse'
- ,'amount_with_fee_val'
+ ,'amount_with_fee'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Total wad fees paid by the purse'
- ,'wad_fee_val'
+ ,'wad_fee'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Total deposit fees paid when depositing coins into the purse'
- ,'deposit_fees_val'
+ ,'deposit_fees'
,table_name
,partition_suffix
);
@@ -120,26 +117,16 @@ END $$;
CREATE FUNCTION constrain_table_wad_in_entries(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wad_in_entries';
+ table_name TEXT DEFAULT 'wad_in_entries';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
- -- FIXME: change to materialized index by reserve_pub!
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_reserve_pub '
- 'ON ' || table_name || ' '
- '(reserve_pub);'
- );
- EXECUTE FORMAT (
- 'COMMENT ON INDEX ' || table_name || '_reserve_pub '
- 'IS ' || quote_literal('needed in reserve history computation') || ';'
- );
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_wad_in_entry_serial_id_key'
@@ -153,7 +140,7 @@ RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wad_in_entries';
+ table_name TEXT DEFAULT 'wad_in_entries';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
diff --git a/src/exchangedb/0002-wad_out_entries.sql b/src/exchangedb/0002-wad_out_entries.sql
index 45a4813cb..de921637b 100644
--- a/src/exchangedb/0002-wad_out_entries.sql
+++ b/src/exchangedb/0002-wad_out_entries.sql
@@ -16,13 +16,13 @@
CREATE FUNCTION create_table_wad_out_entries(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wad_out_entries';
+ table_name TEXT DEFAULT 'wad_out_entries';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
@@ -33,12 +33,9 @@ BEGIN
',h_contract BYTEA NOT NULL CHECK(LENGTH(h_contract)=64)'
',purse_expiration INT8 NOT NULL'
',merge_timestamp INT8 NOT NULL'
- ',amount_with_fee_val INT8 NOT NULL'
- ',amount_with_fee_frac INT4 NOT NULL'
- ',wad_fee_val INT8 NOT NULL'
- ',wad_fee_frac INT4 NOT NULL'
- ',deposit_fees_val INT8 NOT NULL'
- ',deposit_fees_frac INT4 NOT NULL'
+ ',amount_with_fee taler_amount NOT NULL'
+ ',wad_fee taler_amount NOT NULL'
+ ',deposit_fees taler_amount NOT NULL'
',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
',purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)'
') %s ;'
@@ -89,19 +86,19 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Total amount in the purse'
- ,'amount_with_fee_val'
+ ,'amount_with_fee'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Wad fee charged to the purse'
- ,'wad_fee_val'
+ ,'wad_fee'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Total deposit fees charged to the purse'
- ,'deposit_fees_val'
+ ,'deposit_fees'
,table_name
,partition_suffix
);
@@ -122,22 +119,16 @@ $$;
CREATE FUNCTION constrain_table_wad_out_entries(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wad_out_entries';
+ table_name TEXT DEFAULT 'wad_out_entries';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
- -- FIXME: change to materialized index by reserve_pub!
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_by_reserve_pub '
- 'ON ' || table_name || ' '
- '(reserve_pub);'
- );
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_wad_out_entry_serial_id_key'
@@ -152,7 +143,7 @@ RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wad_out_entries';
+ table_name TEXT DEFAULT 'wad_out_entries';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
diff --git a/src/exchangedb/0002-wads_in.sql b/src/exchangedb/0002-wads_in.sql
index 013b16350..479589ba4 100644
--- a/src/exchangedb/0002-wads_in.sql
+++ b/src/exchangedb/0002-wads_in.sql
@@ -15,21 +15,20 @@
--
CREATE FUNCTION create_table_wads_in(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wads_in';
+ table_name TEXT DEFAULT 'wads_in';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
'(wad_in_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
',wad_id BYTEA PRIMARY KEY CHECK (LENGTH(wad_id)=24)'
',origin_exchange_url TEXT NOT NULL'
- ',amount_val INT8 NOT NULL'
- ',amount_frac INT4 NOT NULL'
+ ',amount taler_amount NOT NULL'
',arrival_time INT8 NOT NULL'
',UNIQUE (wad_id, origin_exchange_url)'
') %s ;'
@@ -56,7 +55,7 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Actual amount that was received by our exchange'
- ,'amount_val'
+ ,'amount'
,table_name
,partition_suffix
);
@@ -70,13 +69,13 @@ END $$;
CREATE FUNCTION constrain_table_wads_in(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wads_in';
+ table_name TEXT DEFAULT 'wads_in';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
diff --git a/src/exchangedb/0002-wads_out.sql b/src/exchangedb/0002-wads_out.sql
index edad4a68d..e52010e96 100644
--- a/src/exchangedb/0002-wads_out.sql
+++ b/src/exchangedb/0002-wads_out.sql
@@ -15,21 +15,20 @@
--
CREATE FUNCTION create_table_wads_out(
- IN shard_suffix VARCHAR DEFAULT NULL
+ IN shard_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wads_out';
+ table_name TEXT DEFAULT 'wads_out';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I '
'(wad_out_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
',wad_id BYTEA PRIMARY KEY CHECK (LENGTH(wad_id)=24)'
',partner_serial_id INT8 NOT NULL'
- ',amount_val INT8 NOT NULL'
- ',amount_frac INT4 NOT NULL'
+ ',amount taler_amount NOT NULL'
',execution_time INT8 NOT NULL'
') %s ;'
,table_name
@@ -55,7 +54,7 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Amount that was wired'
- ,'amount_val'
+ ,'amount'
,table_name
,shard_suffix
);
@@ -70,13 +69,13 @@ $$;
CREATE FUNCTION constrain_table_wads_out(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wads_out';
+ table_name TEXT DEFAULT 'wads_out';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@@ -93,7 +92,7 @@ RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wads_out';
+ table_name TEXT DEFAULT 'wads_out';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
diff --git a/src/exchangedb/0002-wire_accounts.sql b/src/exchangedb/0002-wire_accounts.sql
index 628bc599b..dba522d7b 100644
--- a/src/exchangedb/0002-wire_accounts.sql
+++ b/src/exchangedb/0002-wire_accounts.sql
@@ -15,10 +15,13 @@
--
CREATE TABLE wire_accounts
- (payto_uri VARCHAR PRIMARY KEY
+ (payto_uri TEXT PRIMARY KEY
,master_sig BYTEA CHECK (LENGTH(master_sig)=64)
,is_active BOOLEAN NOT NULL
,last_change INT8 NOT NULL
+ ,conversion_url TEXT DEFAULT (NULL)
+ ,debit_restrictions TEXT DEFAULT (NULL)
+ ,credit_restrictions TEXT DEFAULT (NULL)
);
COMMENT ON TABLE wire_accounts
IS 'Table with current and historic bank accounts of the exchange. Entries never expire as we need to remember the last_change column indefinitely.';
@@ -30,5 +33,13 @@ COMMENT ON COLUMN wire_accounts.is_active
IS 'true if we are currently supporting the use of this account.';
COMMENT ON COLUMN wire_accounts.last_change
IS 'Latest time when active status changed. Used to detect replays of old messages.';
+COMMENT ON COLUMN wire_accounts.conversion_url
+ IS 'URL of a currency conversion service if conversion is needed when this account is used; NULL if there is no conversion.';
+COMMENT ON COLUMN wire_accounts.debit_restrictions
+ IS 'JSON array describing restrictions imposed when debiting this account. Empty for no restrictions, NULL if account was migrated from previous database revision or account is disabled.';
+COMMENT ON COLUMN wire_accounts.credit_restrictions
+ IS 'JSON array describing restrictions imposed when crediting this account. Empty for no restrictions, NULL if account was migrated from previous database revision or account is disabled.';
+
+
-- "wire_accounts" has no sequence because it is a 'mutable' table
-- and is of no concern to the auditor
diff --git a/src/exchangedb/0002-wire_fee.sql b/src/exchangedb/0002-wire_fee.sql
index deb26ceff..12cb91b98 100644
--- a/src/exchangedb/0002-wire_fee.sql
+++ b/src/exchangedb/0002-wire_fee.sql
@@ -16,13 +16,11 @@
CREATE TABLE wire_fee
(wire_fee_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
- ,wire_method VARCHAR NOT NULL
+ ,wire_method TEXT NOT NULL
,start_date INT8 NOT NULL
,end_date INT8 NOT NULL
- ,wire_fee_val INT8 NOT NULL
- ,wire_fee_frac INT4 NOT NULL
- ,closing_fee_val INT8 NOT NULL
- ,closing_fee_frac INT4 NOT NULL
+ ,wire_fee taler_amount NOT NULL
+ ,closing_fee taler_amount NOT NULL
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
,PRIMARY KEY (wire_method, start_date)
);
diff --git a/src/exchangedb/0002-wire_out.sql b/src/exchangedb/0002-wire_out.sql
index 9c459fe95..c0f471b56 100644
--- a/src/exchangedb/0002-wire_out.sql
+++ b/src/exchangedb/0002-wire_out.sql
@@ -15,13 +15,13 @@
--
CREATE FUNCTION create_table_wire_out(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wire_out';
+ table_name TEXT DEFAULT 'wire_out';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE IF NOT EXISTS %I'
@@ -30,9 +30,8 @@ BEGIN
',wtid_raw BYTEA UNIQUE NOT NULL CHECK (LENGTH(wtid_raw)=32)'
',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)'
',exchange_account_section TEXT NOT NULL'
- ',amount_val INT8 NOT NULL'
- ',amount_frac INT4 NOT NULL'
- ') %s ;'
+ ',amount taler_amount NOT NULL'
+ ') %s ;'
,table_name
,'PARTITION BY HASH (wtid_raw)'
,partition_suffix
@@ -59,13 +58,13 @@ $$;
CREATE FUNCTION constrain_table_wire_out(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wire_out';
+ table_name TEXT DEFAULT 'wire_out';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
diff --git a/src/exchangedb/0002-wire_targets.sql b/src/exchangedb/0002-wire_targets.sql
index 5e5421085..88d67d9a5 100644
--- a/src/exchangedb/0002-wire_targets.sql
+++ b/src/exchangedb/0002-wire_targets.sql
@@ -15,7 +15,7 @@
--
CREATE FUNCTION create_table_wire_targets(
- IN partition_suffix VARCHAR DEFAULT NULL
+ IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
@@ -25,7 +25,7 @@ BEGIN
'CREATE TABLE %I'
'(wire_target_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
',wire_target_h_payto BYTEA PRIMARY KEY CHECK (LENGTH(wire_target_h_payto)=32)'
- ',payto_uri VARCHAR NOT NULL'
+ ',payto_uri TEXT NOT NULL'
') %s ;'
,'wire_targets'
,'PARTITION BY HASH (wire_target_h_payto)'
@@ -52,13 +52,13 @@ END $$;
CREATE FUNCTION constrain_table_wire_targets(
- IN partition_suffix VARCHAR
+ IN partition_suffix TEXT
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
- table_name VARCHAR DEFAULT 'wire_targets';
+ table_name TEXT DEFAULT 'wire_targets';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
diff --git a/src/exchangedb/0002-work_shards.sql b/src/exchangedb/0002-work_shards.sql
index fbe7e7086..6347d42c5 100644
--- a/src/exchangedb/0002-work_shards.sql
+++ b/src/exchangedb/0002-work_shards.sql
@@ -20,7 +20,7 @@ CREATE TABLE work_shards
,start_row INT8 NOT NULL
,end_row INT8 NOT NULL
,completed BOOLEAN NOT NULL DEFAULT FALSE
- ,job_name VARCHAR NOT NULL
+ ,job_name TEXT NOT NULL
,PRIMARY KEY (job_name, start_row)
);
COMMENT ON TABLE work_shards
@@ -44,3 +44,13 @@ CREATE INDEX work_shards_by_job_name_completed_last_attempt_index
,completed
,last_attempt ASC
);
+
+CREATE INDEX work_shards_by_end_row_index
+ ON work_shards
+ (end_row DESC);
+
+CREATE INDEX work_shards_by_rows
+ ON work_shards
+ (job_name
+ ,start_row
+ ,end_row);
diff --git a/src/exchangedb/0003-purse_actions.sql b/src/exchangedb/0003-purse_actions.sql
deleted file mode 100644
index c77dfb3c5..000000000
--- a/src/exchangedb/0003-purse_actions.sql
+++ /dev/null
@@ -1,74 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2014--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/>
---
-
-
-CREATE TABLE IF NOT EXISTS purse_actions
- (purse_pub BYTEA NOT NULL PRIMARY KEY CHECK(LENGTH(purse_pub)=32)
- ,action_date INT8 NOT NULL
- ,partner_serial_id INT8
- );
-COMMENT ON TABLE purse_actions
- IS 'purses awaiting some action by the router';
-COMMENT ON COLUMN purse_actions.purse_pub
- IS 'public (contract) key of the purse';
-COMMENT ON COLUMN purse_actions.action_date
- IS 'when is the purse ready for action';
-COMMENT ON COLUMN purse_actions.partner_serial_id
- IS 'wad target of an outgoing wire transfer, 0 for local, NULL if the purse is unmerged and thus the target is still unknown';
-
-CREATE INDEX IF NOT EXISTS purse_action_by_target
- ON purse_actions
- (partner_serial_id,action_date);
-
-
-CREATE OR REPLACE FUNCTION purse_requests_insert_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- INSERT INTO
- purse_actions
- (purse_pub
- ,action_date)
- VALUES
- (NEW.purse_pub
- ,NEW.purse_expiration);
- RETURN NEW;
-END $$;
-COMMENT ON FUNCTION purse_requests_insert_trigger()
- IS 'When a purse is created, insert it into the purse_action table to take action when the purse expires.';
-
-CREATE TRIGGER purse_requests_on_insert
- AFTER INSERT
- ON purse_requests
- FOR EACH ROW EXECUTE FUNCTION purse_requests_insert_trigger();
-COMMENT ON TRIGGER purse_requests_on_insert
- ON purse_requests
- IS 'Here we install an entry for the purse expiration.';
-
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
- VALUES
- ('purse_actions'
- ,'exchange-0002'
- ,'create'
- ,FALSE
- ,FALSE);
diff --git a/src/exchangedb/0003-purse_deletion.sql b/src/exchangedb/0003-purse_deletion.sql
index e655ee613..66a95ff03 100644
--- a/src/exchangedb/0003-purse_deletion.sql
+++ b/src/exchangedb/0003-purse_deletion.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2024 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
@@ -14,58 +14,26 @@
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
-CREATE OR REPLACE FUNCTION create_table_purse_deletion(
- IN shard_suffix VARCHAR DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE IF NOT EXISTS %I'
- '(purse_deletion_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE'
- ',purse_sig BYTEA CHECK (LENGTH(purse_sig)=64)'
- ',XXX VARCHAR NOT NULL'
- ') %s ;'
- ,'purse_deletion'
- ,'PARTITION BY HASH (XXX)'
- ,shard_suffix
- );
- COMMENT ON TABLE purse_deletion
- IS 'signatures affirming explicit purse deletions';
- COMMENT ON COLUMN purse_deletion.purse_sig
- IS 'signature of type XXX';
-END
-$$;
-COMMENT ON FUNCTION create_table_purse_deletion
- IS 'Creates the purse_deletion table';
-
-CREATE OR REPLACE FUNCTION constrain_table_purse_deletion(
- IN partition_suffix VARCHAR
-)
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE purse_deletion_' || partition_suffix || ' '
- 'ADD CONSTRAINT purse_deletion_' || partition_suffix || '_XXX '
- 'UNIQUE (XXX)'
- );
-END
-$$;
+-- Adds a 'unique' constraint to the 'purse_pub'.
+-- This is not only semantically correct, but also
+-- creates a dramatic speed-up on the
+-- pg_select_purse query (which otherwise fails to
+-- use indices correctly).
-CREATE OR REPLACE FUNCTION alter_create_was_deleted_table_purse_requests (
- IN partition_suffix VARCHAR
+CREATE FUNCTION constrain_table_purse_decision3(
+ IN partition_suffix TEXT
)
-RETURNS void
+RETURNS VOID
LANGUAGE plpgsql
AS $$
+DECLARE
+ table_name TEXT DEFAULT 'purse_decision';
BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
- 'ALTER TABLE purse_requests_' || partition_suffix ||
- ' ADD COLUMN'
- ' was_deleted BOOLEAN NOT NULL DEFAULT(FALSE)'
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_purse_decision_purse_pub'
+ ' UNIQUE (purse_pub) '
);
END
$$;
@@ -77,18 +45,8 @@ INSERT INTO exchange_tables
,partitioned
,by_range)
VALUES
- ('purse_deletion'
- ,'exchange-0002'
- ,'create'
- ,TRUE
- ,FALSE),
- ('purse_deletion'
- ,'exchange-0002'
+ ('purse_decision3'
+ ,'exchange-0003'
,'constrain'
,TRUE
- ,FALSE),
- ('purse_requests'
- ,'exchange-0002'
- ,'alter_create_was_deleted'
- ,TRUE
,FALSE);
diff --git a/src/exchangedb/0003-wire_accounts.sql b/src/exchangedb/0003-wire_accounts.sql
new file mode 100644
index 000000000..51fc86c80
--- /dev/null
+++ b/src/exchangedb/0003-wire_accounts.sql
@@ -0,0 +1,25 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2024 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/>
+--
+
+-- new columns for #8000
+ALTER TABLE wire_accounts
+ ADD COLUMN priority INT8 NOT NULL DEFAULT (0),
+ ADD COLUMN bank_label TEXT DEFAULT (NULL);
+
+COMMENT ON COLUMN wire_accounts.priority
+ IS 'priority determines the order in which wallets should display wire accounts';
+COMMENT ON COLUMN wire_accounts.bank_label
+ IS 'label to show in the selector for this bank account in the wallet UI';
diff --git a/src/exchangedb/0004-refunds.sql b/src/exchangedb/0004-refunds.sql
new file mode 100644
index 000000000..eb9e7ad6e
--- /dev/null
+++ b/src/exchangedb/0004-refunds.sql
@@ -0,0 +1,35 @@
+
+CREATE FUNCTION constrain_table_refunds4 (
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'refunds';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' DROP CONSTRAINT ' || table_name || '_pkey'
+ );
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD PRIMARY KEY (batch_deposit_serial_id, coin_pub, rtransaction_id) '
+ );
+END
+$$;
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+ VALUES
+ ('refunds4'
+ ,'exchange-0004'
+ ,'constrain'
+ ,TRUE
+ ,FALSE);
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index a6eb6747c..fd993f968 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -18,15 +18,20 @@ sqlinputs = \
exchange_do_*.sql \
procedures.sql.in \
0002-*.sql \
- exchange-0002.sql.in \
0003-*.sql \
- exchange-0003.sql.in
+ 0004-*.sql \
+ exchange-0002.sql.in \
+ exchange-0003.sql.in \
+ exchange-0004.sql.in
sql_DATA = \
benchmark-0001.sql \
versioning.sql \
+ auditor-triggers-0001.sql \
exchange-0001.sql \
exchange-0002.sql \
+ exchange-0003.sql \
+ exchange-0004.sql \
drop.sql \
procedures.sql
@@ -38,23 +43,32 @@ BUILT_SOURCES = \
CLEANFILES = \
exchange-0002.sql \
- exchange-0003.sql
+ exchange-0003.sql \
+ procedures.sql
procedures.sql: procedures.sql.in exchange_do_*.sql
- chmod +w $@ || true
+ chmod +w $@ 2> /dev/null || true
gcc -E -P -undef - < procedures.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
chmod ugo-w $@
exchange-0002.sql: exchange-0002.sql.in 0002-*.sql
- chmod +w $@ || true
+ chmod +w $@ 2> /dev/null || true
gcc -E -P -undef - < exchange-0002.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
chmod ugo-w $@
exchange-0003.sql: exchange-0003.sql.in 0003-*.sql
- chmod +w $@ || true
+ chmod +w $@ 2> /dev/null || true
gcc -E -P -undef - < exchange-0003.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
chmod ugo-w $@
+exchange-0004.sql: exchange-0004.sql.in 0004-*.sql
+ chmod +w $@ 2> /dev/null || true
+ gcc -E -P -undef - < exchange-0004.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
+ chmod ugo-w $@
+
+check_SCRIPTS = \
+ test_idempotency.sh
+
EXTRA_DIST = \
exchangedb.conf \
exchangedb-postgres.conf \
@@ -62,6 +76,7 @@ EXTRA_DIST = \
test-exchange-db-postgres.conf \
$(sqlinputs) \
$(sql_DATA) \
+ $(check_SCRIPTS) \
pg_template.h pg_template.c \
pg_template.sh
@@ -78,10 +93,10 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_compute_shard.h pg_compute_shard.c \
plugin_exchangedb_postgres.c pg_helper.h \
pg_reserves_update.h pg_reserves_update.c \
- pg_insert_aggregation_tracking.h pg_insert_aggregation_tracking.c \
pg_select_aggregation_amounts_for_kyc_check.h pg_select_aggregation_amounts_for_kyc_check.c \
pg_lookup_wire_fee_by_time.h pg_lookup_wire_fee_by_time.c \
pg_select_satisfied_kyc_processes.h pg_select_satisfied_kyc_processes.c \
+ pg_get_pending_kyc_requirement_process.h pg_get_pending_kyc_requirement_process.c \
pg_kyc_provider_account_lookup.h pg_kyc_provider_account_lookup.c \
pg_lookup_kyc_requirement_by_row.h pg_lookup_kyc_requirement_by_row.c \
pg_insert_kyc_requirement_for_account.h pg_insert_kyc_requirement_for_account.c \
@@ -95,10 +110,13 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_get_drain_profit.h pg_get_drain_profit.c \
pg_get_purse_deposit.h pg_get_purse_deposit.c \
pg_insert_contract.h pg_insert_contract.c \
+ pg_select_aml_threshold.h pg_select_aml_threshold.c \
pg_select_contract.h pg_select_contract.c \
pg_select_purse_merge.h pg_select_purse_merge.c \
pg_select_contract_by_purse.h pg_select_contract_by_purse.c \
pg_insert_drain_profit.h pg_insert_drain_profit.c \
+ pg_insert_kyc_failure.h pg_insert_kyc_failure.c \
+ pg_inject_auditor_triggers.h pg_inject_auditor_triggers.c \
pg_create_tables.h pg_create_tables.c \
pg_event_listen.h pg_event_listen.c \
pg_event_listen_cancel.h pg_event_listen_cancel.c \
@@ -113,10 +131,14 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_drain_kyc_alert.h pg_drain_kyc_alert.c \
pg_reserves_in_insert.h pg_reserves_in_insert.c \
pg_get_withdraw_info.h pg_get_withdraw_info.c \
+ pg_do_age_withdraw.h pg_do_age_withdraw.c \
+ pg_get_age_withdraw.h pg_get_age_withdraw.c \
+ pg_batch_ensure_coin_known.h pg_batch_ensure_coin_known.c \
pg_do_batch_withdraw.h pg_do_batch_withdraw.c \
pg_get_policy_details.h pg_get_policy_details.c \
pg_persist_policy_details.h pg_persist_policy_details.c \
pg_do_deposit.h pg_do_deposit.c \
+ pg_get_wire_hash_for_contract.h pg_get_wire_hash_for_contract.c \
pg_add_policy_fulfillment_proof.h pg_add_policy_fulfillment_proof.c \
pg_do_melt.h pg_do_melt.c \
pg_do_refund.h pg_do_refund.c \
@@ -126,15 +148,25 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_count_known_coins.h pg_count_known_coins.c \
pg_ensure_coin_known.h pg_ensure_coin_known.c \
pg_get_known_coin.h pg_get_known_coin.c \
+ pg_get_signature_for_known_coin.h pg_get_signature_for_known_coin.c \
pg_get_coin_denomination.h pg_get_coin_denomination.c \
pg_have_deposit2.h pg_have_deposit2.c \
pg_aggregate.h pg_aggregate.c \
pg_create_aggregation_transient.h pg_create_aggregation_transient.c \
+ pg_insert_kyc_attributes.h pg_insert_kyc_attributes.c \
+ pg_select_similar_kyc_attributes.h pg_select_similar_kyc_attributes.c \
+ pg_select_kyc_attributes.h pg_select_kyc_attributes.c \
+ pg_insert_aml_officer.h pg_insert_aml_officer.c \
+ pg_test_aml_officer.h pg_test_aml_officer.c \
+ pg_lookup_aml_officer.h pg_lookup_aml_officer.c \
+ pg_trigger_aml_process.h pg_trigger_aml_process.c \
+ pg_select_aml_process.h pg_select_aml_process.c \
+ pg_select_aml_history.h pg_select_aml_history.c \
+ pg_insert_aml_decision.h pg_insert_aml_decision.c \
pg_select_aggregation_transient.h pg_select_aggregation_transient.c \
pg_find_aggregation_transient.h pg_find_aggregation_transient.c \
pg_update_aggregation_transient.h pg_update_aggregation_transient.c \
pg_get_ready_deposit.h pg_get_ready_deposit.c \
- pg_insert_deposit.h pg_insert_deposit.c \
pg_insert_refund.h pg_insert_refund.c \
pg_select_refunds_by_coin.h pg_select_refunds_by_coin.c \
pg_get_melt.h pg_get_melt.c \
@@ -155,8 +187,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_start_deferred_wire_out.h pg_start_deferred_wire_out.c \
pg_store_wire_transfer_out.h pg_store_wire_transfer_out.c \
pg_gc.h pg_gc.c \
- pg_select_deposits_above_serial_id.h pg_select_deposits_above_serial_id.c \
- pg_select_history_requests_above_serial_id.h pg_select_history_requests_above_serial_id.c \
+ pg_select_coin_deposits_above_serial_id.h pg_select_coin_deposits_above_serial_id.c \
pg_select_purse_decisions_above_serial_id.h pg_select_purse_decisions_above_serial_id.c \
pg_select_purse_deposits_by_purse.h pg_select_purse_deposits_by_purse.c \
pg_select_refreshes_above_serial_id.h pg_select_refreshes_above_serial_id.c \
@@ -172,7 +203,9 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_get_old_coin_by_h_blind.h pg_get_old_coin_by_h_blind.c \
pg_insert_denomination_revocation.h pg_insert_denomination_revocation.c \
pg_get_denomination_revocation.h pg_get_denomination_revocation.c \
- pg_select_deposits_missing_wire.h pg_select_deposits_missing_wire.c \
+ pg_select_batch_deposits_missing_wire.h pg_select_batch_deposits_missing_wire.c \
+ pg_select_justification_for_missing_wire.h pg_select_justification_for_missing_wire.c \
+ pg_select_aggregations_above_serial.h pg_select_aggregations_above_serial.c \
pg_lookup_auditor_timestamp.h pg_lookup_auditor_timestamp.c \
pg_lookup_auditor_status.h pg_lookup_auditor_status.c \
pg_insert_auditor.h pg_insert_auditor.c \
@@ -205,14 +238,13 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_update_auditor.h pg_update_auditor.c \
pg_begin_revolving_shard.h pg_begin_revolving_shard.c \
pg_get_extension_manifest.h pg_get_extension_manifest.c \
- pg_insert_history_request.h pg_insert_history_request.c \
pg_do_purse_merge.h pg_do_purse_merge.c \
pg_start_read_committed.h pg_start_read_committed.c \
pg_start_read_only.h pg_start_read_only.c \
pg_insert_denomination_info.h pg_insert_denomination_info.c \
pg_do_batch_withdraw_insert.h pg_do_batch_withdraw_insert.c \
pg_do_reserve_open.c pg_do_reserve_open.h \
- pg_do_withdraw.h pg_do_withdraw.c \
+ pg_do_purse_delete.c pg_do_purse_delete.h \
pg_preflight.h pg_preflight.c \
pg_iterate_active_signkeys.h pg_iterate_active_signkeys.c \
pg_commit.h pg_commit.c \
@@ -242,21 +274,17 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_select_purse_deposits_above_serial_id.h pg_select_purse_deposits_above_serial_id.c \
pg_select_account_merges_above_serial_id.h pg_select_account_merges_above_serial_id.c \
pg_select_all_purse_decisions_above_serial_id.h pg_select_all_purse_decisions_above_serial_id.c \
- pg_batch_reserves_in_insert.h pg_batch_reserves_in_insert.c \
- pg_batch2_reserves_in_insert.h pg_batch2_reserves_in_insert.c \
pg_select_reserve_open_above_serial_id.c pg_select_reserve_open_above_serial_id.h
-libtaler_plugin_exchangedb_postgres_la_LIBADD = \
- $(LTLIBINTL)
libtaler_plugin_exchangedb_postgres_la_LDFLAGS = \
- $(TALER_PLUGIN_LDFLAGS) \
+ $(TALER_PLUGIN_LDFLAGS)
+libtaler_plugin_exchangedb_postgres_la_LIBADD = \
+ $(LTLIBINTL) \
$(top_builddir)/src/pq/libtalerpq.la \
- $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
- -lpq \
- -lpthread \
-lgnunetpq \
-lgnunetutil \
-ljansson \
+ -lpq \
$(XLIB)
lib_LTLIBRARIES = \
@@ -278,18 +306,19 @@ libtalerexchangedb_la_LDFLAGS = \
check_PROGRAMS = \
- test-exchangedb-postgres \
+ test-exchangedb-postgres
+
+noinst_PROGRAMS = \
bench-db-postgres\
- perf-exchangedb-reserves-in-insert-postgres\
- test-exchangedb-by-j-postgres\
- test-exchangedb-batch-reserves-in-insert-postgres
+ perf_get_link_data-postgres\
+ perf_select_refunds_by_coin-postgres\
+ perf_reserves_in_insert-postgres \
+ perf_deposits_get_ready-postgres
+
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
TESTS = \
- test-exchangedb-postgres\
- test-exchangedb-by-j-postgres\
- perf-exchangedb-reserves-in-insert-postgres\
- test-exchangedb-batch-reserves-in-insert-postgres
-
+ $(check_SCRIPTS) \
+ $(check_PROGRAMS)
test_exchangedb_postgres_SOURCES = \
test_exchangedb.c
@@ -303,22 +332,19 @@ test_exchangedb_postgres_LDADD = \
-lgnunetutil \
$(XLIB)
-test_exchangedb_by_j_postgres_SOURCES = \
- test_exchangedb_by_j.c
-test_exchangedb_by_j_postgres_LDADD = \
+bench_db_postgres_SOURCES = \
+ bench_db.c
+bench_db_postgres_LDADD = \
libtalerexchangedb.la \
- $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \
- -ljansson \
- -lgnunetjson \
+ -lgnunetpq \
-lgnunetutil \
$(XLIB)
-
-perf_exchangedb_reserves_in_insert_postgres_SOURCES = \
- perf_exchangedb_reserves_in_insert.c
-perf_exchangedb_reserves_in_insert_postgres_LDADD = \
+perf_reserves_in_insert_postgres_SOURCES = \
+ perf_reserves_in_insert.c
+perf_reserves_in_insert_postgres_LDADD = \
libtalerexchangedb.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
@@ -326,22 +352,25 @@ perf_exchangedb_reserves_in_insert_postgres_LDADD = \
-ljansson \
-lgnunetjson \
-lgnunetutil \
+ -lm \
$(XLIB)
-bench_db_postgres_SOURCES = \
- bench_db.c
-bench_db_postgres_LDADD = \
+perf_select_refunds_by_coin_postgres_SOURCES = \
+ perf_select_refunds_by_coin.c
+perf_select_refunds_by_coin_postgres_LDADD = \
libtalerexchangedb.la \
+ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \
- -lgnunetpq \
+ -ljansson \
+ -lgnunetjson \
-lgnunetutil \
+ -lm \
$(XLIB)
-
-test_exchangedb_batch_reserves_in_insert_postgres_SOURCES = \
- test_exchangedb_batch_reserves_in_insert.c
-test_exchangedb_batch_reserves_in_insert_postgres_LDADD = \
+perf_get_link_data_postgres_SOURCES = \
+ perf_get_link_data.c
+perf_get_link_data_postgres_LDADD = \
libtalerexchangedb.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
@@ -349,16 +378,20 @@ test_exchangedb_batch_reserves_in_insert_postgres_LDADD = \
-ljansson \
-lgnunetjson \
-lgnunetutil \
+ -lm \
$(XLIB)
-bench_db_postgres_SOURCES = \
- bench_db.c
-bench_db_postgres_LDADD = \
+perf_deposits_get_ready_postgres_SOURCES = \
+ perf_deposits_get_ready.c
+perf_deposits_get_ready_postgres_LDADD = \
libtalerexchangedb.la \
+ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \
- -lgnunetpq \
+ -ljansson \
+ -lgnunetjson \
-lgnunetutil \
+ -lm \
$(XLIB)
diff --git a/src/exchangedb/shard-0001.sql.in b/src/exchangedb/auditor-triggers-0001.sql
index 5a849a8ae..4e2ea66ce 100644
--- a/src/exchangedb/shard-0001.sql.in
+++ b/src/exchangedb/auditor-triggers-0001.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2024 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
@@ -17,17 +17,25 @@
-- Everything in one big transaction
BEGIN;
--- Check patch versioning is in place.
-SELECT _v.register_patch('shard-0001', NULL, NULL);
+SELECT _v.register_patch('auditor-triggers-0001');
--------------------- Schema ----------------------------
+SET search_path TO exchange;
-CREATE SCHEMA exchange;
-COMMENT ON SCHEMA exchange IS 'taler-exchange data';
+CREATE OR REPLACE FUNCTION auditor_new_deposits_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+AS $$
+BEGIN
+ NOTIFY XFIXME;
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION auditor_new_deposits_trigger()
+ IS 'Call XXX on new entry';
-SET search_path TO exchange;
+CREATE TRIGGER auditor_notify_helper_insert_deposits
+ AFTER INSERT
+ ON exchange.batch_deposits
+EXECUTE PROCEDURE auditor_new_deposits_trigger();
-#include "common-0001.sql"
-#include "shard-0001-part.sql"
COMMIT;
diff --git a/src/exchangedb/bench_db.c b/src/exchangedb/bench_db.c
index a85834d13..302d23062 100644
--- a/src/exchangedb/bench_db.c
+++ b/src/exchangedb/bench_db.c
@@ -169,9 +169,9 @@ bem_insert (struct GNUNET_PQ_Context *conn,
GNUNET_CRYPTO_hash (&b,
sizeof (b),
&hc);
- memcpy (&ihc,
- &hc,
- sizeof (ihc));
+ GNUNET_memcpy (&ihc,
+ &hc,
+ sizeof (ihc));
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&hc),
@@ -265,9 +265,9 @@ bem_select (struct GNUNET_PQ_Context *conn,
GNUNET_CRYPTO_hash (&b,
sizeof (b),
&hc);
- memcpy (&ihc,
- &hc,
- sizeof (ihc));
+ GNUNET_memcpy (&ihc,
+ &hc,
+ sizeof (ihc));
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint32 (&ihc),
diff --git a/src/exchangedb/drop.sql b/src/exchangedb/drop.sql
index ff383d743..b7583f794 100644
--- a/src/exchangedb/drop.sql
+++ b/src/exchangedb/drop.sql
@@ -17,9 +17,22 @@
-- Everything in one big transaction
BEGIN;
+WITH xpatches AS (
+ SELECT patch_name
+ FROM _v.patches
+ WHERE starts_with(patch_name,'exchange-')
+)
+ SELECT _v.unregister_patch(xpatches.patch_name)
+ FROM xpatches;
-SELECT _v.unregister_patch('exchange-0001');
-SELECT _v.unregister_patch('exchange-0002');
+
+WITH xpatches AS (
+ SELECT patch_name
+ FROM _v.patches
+ WHERE starts_with(patch_name,'auditor-triggers-')
+)
+ SELECT _v.unregister_patch(xpatches.patch_name)
+ FROM xpatches;
DROP SCHEMA exchange CASCADE;
diff --git a/src/exchangedb/exchange-0001.sql b/src/exchangedb/exchange-0001.sql
index 5ce38c286..a4b1c8b9f 100644
--- a/src/exchangedb/exchange-0001.sql
+++ b/src/exchangedb/exchange-0001.sql
@@ -29,9 +29,9 @@ SET search_path TO exchange;
CREATE TABLE exchange_tables
(table_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
- ,name VARCHAR NOT NULL
- ,version VARCHAR NOT NULL
- ,action VARCHAR NOT NULL
+ ,name TEXT NOT NULL
+ ,version TEXT NOT NULL
+ ,action TEXT NOT NULL
,partitioned BOOL NOT NULL
,by_range BOOL NOT NULL
,finished BOOL NOT NULL DEFAULT(FALSE));
@@ -42,7 +42,7 @@ COMMENT ON COLUMN exchange_tables.name
COMMENT ON COLUMN exchange_tables.version
IS 'Version of the DB in which the given action happened';
COMMENT ON COLUMN exchange_tables.action
- IS 'Action to take on the table (e.g. create, constrain, foreign, or drop). Create is done for the master table and each partition; constrain is only for partitions or for master if there are no partitions; master only on master (takes no argument); foreign only on master if there are no partitions.';
+ IS 'Action to take on the table (e.g. create, constrain, or foreign). Create is done for the master table and each partition; constrain is only for partitions or for master if there are no partitions; master only on master (takes no argument); foreign only on master if there are no partitions.';
COMMENT ON COLUMN exchange_tables.partitioned
IS 'TRUE if the table is partitioned';
COMMENT ON COLUMN exchange_tables.by_range
@@ -52,10 +52,10 @@ COMMENT ON COLUMN exchange_tables.finished
CREATE FUNCTION create_partitioned_table(
- IN table_definition VARCHAR -- SQL template for table creation
- ,IN table_name VARCHAR -- base name of the table
- ,IN main_table_partition_str VARCHAR -- declaration for how to partition the table
- ,IN partition_suffix VARCHAR DEFAULT NULL -- NULL: no partitioning, 0: yes partitioning, no sharding, >0: sharding
+ IN table_definition TEXT -- SQL template for table creation
+ ,IN table_name TEXT -- base name of the table
+ ,IN main_table_partition_str TEXT -- declaration for how to partition the table
+ ,IN partition_suffix TEXT DEFAULT NULL -- NULL: no partitioning, 0: yes partitioning, no sharding, >0: sharding
)
RETURNS VOID
LANGUAGE plpgsql
@@ -84,9 +84,9 @@ COMMENT ON FUNCTION create_partitioned_table
CREATE FUNCTION comment_partitioned_table(
- IN table_comment VARCHAR
- ,IN table_name VARCHAR
- ,IN partition_suffix VARCHAR DEFAULT NULL
+ IN table_comment TEXT
+ ,IN table_name TEXT
+ ,IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
@@ -110,10 +110,10 @@ COMMENT ON FUNCTION comment_partitioned_table
CREATE FUNCTION comment_partitioned_column(
- IN table_comment VARCHAR
- ,IN column_name VARCHAR
- ,IN table_name VARCHAR
- ,IN partition_suffix VARCHAR DEFAULT NULL
+ IN table_comment TEXT
+ ,IN column_name TEXT
+ ,IN table_name TEXT
+ ,IN partition_suffix TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
@@ -141,7 +141,6 @@ COMMENT ON FUNCTION comment_partitioned_column
-- Main DB setup loop
---------------------------------------------------------------------------
-
CREATE FUNCTION do_create_tables(
num_partitions INTEGER
-- NULL: no partitions, add foreign constraints
diff --git a/src/exchangedb/exchange-0002.sql.in b/src/exchangedb/exchange-0002.sql.in
index 1d28f63a4..ab13b28af 100644
--- a/src/exchangedb/exchange-0002.sql.in
+++ b/src/exchangedb/exchange-0002.sql.in
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -19,6 +19,38 @@ BEGIN;
SELECT _v.register_patch('exchange-0002', NULL, NULL);
SET search_path TO exchange;
+CREATE DOMAIN gnunet_hashcode
+ AS BYTEA
+ CHECK(LENGTH(VALUE) = 32);
+
+CREATE TYPE taler_amount
+ AS
+ (val INT8
+ ,frac INT4
+ );
+COMMENT ON TYPE taler_amount
+ IS 'Stores an amount, fraction is in units of 1/100000000 of the base value';
+
+CREATE TYPE exchange_do_array_reserve_insert_return_type
+ AS
+ (transaction_duplicate BOOLEAN
+ ,ruuid INT8
+ );
+COMMENT ON TYPE exchange_do_array_reserve_insert_return_type
+ IS 'Return type for exchange_do_array_reserves_insert() stored procedure';
+
+CREATE TYPE exchange_do_select_deposits_missing_wire_return_type
+ AS
+ (
+ batch_deposit_serial_id INT8,
+ total_amount taler_amount,
+ wire_target_h_payto BYTEA,
+ deadline INT8
+ );
+COMMENT ON TYPE exchange_do_select_deposits_missing_wire_return_type
+ IS 'Return type for exchange_do_select_deposits_missing_wire';
+
+
#include "0002-denominations.sql"
#include "0002-denomination_revocations.sql"
#include "0002-wire_targets.sql"
@@ -31,10 +63,13 @@ SET search_path TO exchange;
#include "0002-exchange_sign_keys.sql"
#include "0002-signkey_revocations.sql"
#include "0002-extensions.sql"
+#include "0002-policy_fulfillments.sql"
+#include "0002-policy_details.sql"
#include "0002-profit_drains.sql"
#include "0002-legitimization_processes.sql"
#include "0002-legitimization_requirements.sql"
#include "0002-reserves.sql"
+#include "0002-reserve_history.sql"
#include "0002-reserves_in.sql"
#include "0002-reserves_close.sql"
#include "0002-close_requests.sql"
@@ -42,10 +77,12 @@ SET search_path TO exchange;
#include "0002-reserves_open_requests.sql"
#include "0002-reserves_out.sql"
#include "0002-known_coins.sql"
+#include "0002-coin_history.sql"
#include "0002-refresh_commitments.sql"
#include "0002-refresh_revealed_coins.sql"
#include "0002-refresh_transfer_keys.sql"
-#include "0002-deposits.sql"
+#include "0002-batch_deposits.sql"
+#include "0002-coin_deposits.sql"
#include "0002-refunds.sql"
#include "0002-wire_out.sql"
#include "0002-aggregation_transient.sql"
@@ -65,11 +102,17 @@ SET search_path TO exchange;
#include "0002-wad_in_entries.sql"
#include "0002-wads_out.sql"
#include "0002-wad_out_entries.sql"
-#include "0002-policy_fulfillments.sql"
-#include "0002-policy_details.sql"
#include "0002-work_shards.sql"
#include "0002-revolving_work_shards.sql"
#include "0002-partners.sql"
#include "0002-partner_accounts.sql"
+#include "0002-purse_actions.sql"
+#include "0002-purse_deletion.sql"
+#include "0002-kyc_attributes.sql"
+#include "0002-aml_status.sql"
+#include "0002-aml_staff.sql"
+#include "0002-aml_history.sql"
+#include "0002-age_withdraw.sql"
+
COMMIT;
diff --git a/src/exchangedb/exchange-0003.sql.in b/src/exchangedb/exchange-0003.sql.in
index 7f0a9ef94..c94497531 100644
--- a/src/exchangedb/exchange-0003.sql.in
+++ b/src/exchangedb/exchange-0003.sql.in
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2024 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
@@ -19,7 +19,7 @@ BEGIN;
SELECT _v.register_patch('exchange-0003', NULL, NULL);
SET search_path TO exchange;
-#include "0003-purse_actions.sql"
+#include "0003-wire_accounts.sql"
#include "0003-purse_deletion.sql"
COMMIT;
diff --git a/src/exchangedb/shard-0002.sql.in b/src/exchangedb/exchange-0004.sql.in
index 552fe447f..c966aedc5 100644
--- a/src/exchangedb/shard-0002.sql.in
+++ b/src/exchangedb/exchange-0004.sql.in
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2024 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
@@ -14,20 +14,11 @@
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
--- Everything in one big transaction
BEGIN;
--- Check patch versioning is in place.
-SELECT _v.register_patch('shard-0002', NULL, NULL);
-
--------------------- Schema ----------------------------
-
-CREATE SCHEMA exchange;
-COMMENT ON SCHEMA exchange IS 'taler-exchange data';
-
+SELECT _v.register_patch('exchange-0004', NULL, NULL);
SET search_path TO exchange;
-#include "common-0002.sql"
-#include "shard-0002-part.sql"
+#include "0004-refunds.sql"
COMMIT;
diff --git a/src/exchangedb/exchange_do_age_withdraw.sql b/src/exchangedb/exchange_do_age_withdraw.sql
new file mode 100644
index 000000000..89a291445
--- /dev/null
+++ b/src/exchangedb/exchange_do_age_withdraw.sql
@@ -0,0 +1,165 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2023 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/>
+--
+-- @author Özgür Kesim
+
+CREATE OR REPLACE FUNCTION exchange_do_age_withdraw(
+ IN amount_with_fee taler_amount,
+ IN rpub BYTEA,
+ IN rsig BYTEA,
+ IN now INT8,
+ IN min_reserve_gc INT8,
+ IN h_commitment BYTEA,
+ IN maximum_age_committed INT2, -- in years ϵ [0,1..)
+ IN noreveal_index INT2,
+ IN blinded_evs BYTEA[],
+ IN denom_serials INT8[],
+ IN denom_sigs BYTEA[],
+ OUT reserve_found BOOLEAN,
+ OUT balance_ok BOOLEAN,
+ OUT reserve_balance taler_amount,
+ OUT age_ok BOOLEAN,
+ OUT required_age INT2, -- in years ϵ [0,1..)
+ OUT reserve_birthday INT4,
+ OUT conflict BOOLEAN)
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ reserve RECORD;
+ difference RECORD;
+ balance taler_amount;
+ not_before date;
+ earliest_date date;
+BEGIN
+-- Shards: reserves by reserve_pub (SELECT)
+-- reserves_out (INSERT, with CONFLICT detection) by wih
+-- reserves by reserve_pub (UPDATE)
+-- reserves_in by reserve_pub (SELECT)
+-- wire_targets by wire_target_h_payto
+
+SELECT current_balance
+ ,birthday
+ ,gc_date
+ INTO reserve
+ FROM exchange.reserves
+ WHERE reserves.reserve_pub=rpub;
+
+IF NOT FOUND
+THEN
+ reserve_found=FALSE;
+ age_ok = FALSE;
+ required_age=-1;
+ conflict=FALSE;
+ reserve_balance.val = 0;
+ reserve_balance.frac = 0;
+ balance_ok=FALSE;
+ RETURN;
+END IF;
+
+reserve_found = TRUE;
+conflict=FALSE; -- not really yet determined
+
+reserve_balance = reserve.current_balance;
+reserve_birthday = reserve.birthday;
+
+-- Check age requirements
+IF (reserve.birthday <> 0)
+THEN
+ not_before=date '1970-01-01' + reserve.birthday;
+ earliest_date = current_date - make_interval(maximum_age_committed);
+ --
+ -- 1970-01-01 + birthday == not_before now
+ -- | | |
+ -- <.......not allowed......>[<.....allowed range......>]
+ -- | | |
+ -- ____*_____________________*_________*________________* timeline
+ -- |
+ -- earliest_date ==
+ -- now - maximum_age_committed*year
+ --
+ IF (earliest_date < not_before)
+ THEN
+ required_age = extract(year from age(current_date, not_before));
+ age_ok = FALSE;
+ balance_ok=TRUE; -- NOT REALLY
+ RETURN;
+ END IF;
+END IF;
+
+age_ok = TRUE;
+required_age=0;
+
+-- Check reserve balance is sufficient.
+SELECT *
+INTO difference
+FROM amount_left_minus_right(reserve_balance
+ ,amount_with_fee);
+
+balance_ok = difference.ok;
+
+IF NOT balance_ok
+THEN
+ RETURN;
+END IF;
+
+balance = difference.diff;
+
+-- Calculate new expiration dates.
+min_reserve_gc=GREATEST(min_reserve_gc,reserve.gc_date);
+
+-- Update reserve balance.
+UPDATE reserves SET
+ gc_date=min_reserve_gc
+ ,current_balance=balance
+WHERE
+ reserves.reserve_pub=rpub;
+
+-- Write the commitment into the age-withdraw table
+INSERT INTO exchange.age_withdraw
+ (h_commitment
+ ,max_age
+ ,amount_with_fee
+ ,reserve_pub
+ ,reserve_sig
+ ,noreveal_index
+ ,denom_serials
+ ,h_blind_evs
+ ,denom_sigs)
+VALUES
+ (h_commitment
+ ,maximum_age_committed
+ ,amount_with_fee
+ ,rpub
+ ,rsig
+ ,noreveal_index
+ ,denom_serials
+ ,blinded_evs
+ ,denom_sigs)
+ON CONFLICT DO NOTHING;
+
+IF NOT FOUND
+THEN
+ -- Signal a conflict so that the caller
+ -- can fetch the actual data from the DB.
+ conflict=TRUE;
+ RETURN;
+ELSE
+ conflict=FALSE;
+END IF;
+
+END $$;
+
+COMMENT ON FUNCTION exchange_do_age_withdraw(taler_amount, BYTEA, BYTEA, INT8, INT8, BYTEA, INT2, INT2, BYTEA[], INT8[], BYTEA[])
+ IS 'Checks whether the reserve has sufficient balance for an age-withdraw operation (or the request is repeated and was previously approved) and that age requirements are met. If so updates the database with the result. Includes storing the blinded planchets and denomination signatures, or signaling conflict';
diff --git a/src/exchangedb/exchange_do_amount_specific.sql b/src/exchangedb/exchange_do_amount_specific.sql
new file mode 100644
index 000000000..9b305a3ec
--- /dev/null
+++ b/src/exchangedb/exchange_do_amount_specific.sql
@@ -0,0 +1,92 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--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/>
+--
+
+--------------------------------------------------------------
+-- Taler amounts and helper functions
+-------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION amount_normalize(
+ IN amount taler_amount
+ ,OUT normalized taler_amount
+)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ normalized.val = amount.val + amount.frac / 100000000;
+ normalized.frac = amount.frac % 100000000;
+END $$;
+
+COMMENT ON FUNCTION amount_normalize
+ IS 'Returns the normalized amount by adding to the .val the value of (.frac / 100000000) and removing the modulus 100000000 from .frac.';
+
+CREATE OR REPLACE FUNCTION amount_add(
+ IN a taler_amount
+ ,IN b taler_amount
+ ,OUT sum taler_amount
+)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ sum = (a.val + b.val, a.frac + b.frac);
+ CALL amount_normalize(sum ,sum);
+
+ IF (sum.val > (1<<52))
+ THEN
+ RAISE EXCEPTION 'addition overflow';
+ END IF;
+END $$;
+
+COMMENT ON FUNCTION amount_add
+ IS 'Returns the normalized sum of two amounts. It raises an exception when the resulting .val is larger than 2^52';
+
+CREATE OR REPLACE FUNCTION amount_left_minus_right(
+ IN l taler_amount
+ ,IN r taler_amount
+ ,OUT diff taler_amount
+ ,OUT ok BOOLEAN
+)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+
+IF (l.val > r.val)
+THEN
+ ok = TRUE;
+ IF (l.frac >= r.frac)
+ THEN
+ diff.val = l.val - r.val;
+ diff.frac = l.frac - r.frac;
+ ELSE
+ diff.val = l.val - r.val - 1;
+ diff.frac = l.frac + 100000000 - r.frac;
+ END IF;
+ELSE
+ IF (l.val = r.val) AND (l.frac >= r.frac)
+ THEN
+ diff.val = 0;
+ diff.frac = l.frac - r.frac;
+ ok = TRUE;
+ ELSE
+ diff = (-1, -1);
+ ok = FALSE;
+ END IF;
+END IF;
+
+RETURN;
+END $$;
+
+COMMENT ON FUNCTION amount_left_minus_right
+ IS 'Subtracts the right amount from the left and returns the difference and TRUE, if the left amount is larger than the right, or an invalid amount and FALSE otherwise.';
diff --git a/src/exchangedb/exchange_do_batch_coin_known.sql b/src/exchangedb/exchange_do_batch_coin_known.sql
new file mode 100644
index 000000000..db96cb08c
--- /dev/null
+++ b/src/exchangedb/exchange_do_batch_coin_known.sql
@@ -0,0 +1,469 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--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/>
+--
+
+CREATE OR REPLACE FUNCTION exchange_do_batch4_known_coin(
+ IN in_coin_pub1 BYTEA,
+ IN in_denom_pub_hash1 BYTEA,
+ IN in_h_age_commitment1 BYTEA,
+ IN in_denom_sig1 BYTEA,
+ IN in_coin_pub2 BYTEA,
+ IN in_denom_pub_hash2 BYTEA,
+ IN in_h_age_commitment2 BYTEA,
+ IN in_denom_sig2 BYTEA,
+ IN in_coin_pub3 BYTEA,
+ IN in_denom_pub_hash3 BYTEA,
+ IN in_h_age_commitment3 BYTEA,
+ IN in_denom_sig3 BYTEA,
+ IN in_coin_pub4 BYTEA,
+ IN in_denom_pub_hash4 BYTEA,
+ IN in_h_age_commitment4 BYTEA,
+ IN in_denom_sig4 BYTEA,
+ OUT existed1 BOOLEAN,
+ OUT existed2 BOOLEAN,
+ OUT existed3 BOOLEAN,
+ OUT existed4 BOOLEAN,
+ OUT known_coin_id1 INT8,
+ OUT known_coin_id2 INT8,
+ OUT known_coin_id3 INT8,
+ OUT known_coin_id4 INT8,
+ OUT denom_pub_hash1 BYTEA,
+ OUT denom_pub_hash2 BYTEA,
+ OUT denom_pub_hash3 BYTEA,
+ OUT denom_pub_hash4 BYTEA,
+ OUT age_commitment_hash1 BYTEA,
+ OUT age_commitment_hash2 BYTEA,
+ OUT age_commitment_hash3 BYTEA,
+ OUT age_commitment_hash4 BYTEA)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+WITH dd AS (
+SELECT
+ denominations_serial,
+ coin
+ FROM denominations
+ WHERE denom_pub_hash
+ IN
+ (in_denom_pub_hash1,
+ in_denom_pub_hash2,
+ in_denom_pub_hash3,
+ in_denom_pub_hash4)
+ ),--dd
+ input_rows AS (
+ VALUES
+ (in_coin_pub1,
+ in_denom_pub_hash1,
+ in_h_age_commitment1,
+ in_denom_sig1),
+ (in_coin_pub2,
+ in_denom_pub_hash2,
+ in_h_age_commitment2,
+ in_denom_sig2),
+ (in_coin_pub3,
+ in_denom_pub_hash3,
+ in_h_age_commitment3,
+ in_denom_sig3),
+ (in_coin_pub4,
+ in_denom_pub_hash4,
+ in_h_age_commitment4,
+ in_denom_sig4)
+ ),--ir
+ ins AS (
+ INSERT INTO known_coins (
+ coin_pub,
+ denominations_serial,
+ age_commitment_hash,
+ denom_sig,
+ remaining
+ )
+ SELECT
+ ir.coin_pub,
+ dd.denominations_serial,
+ ir.age_commitment_hash,
+ ir.denom_sig,
+ dd.coin
+ FROM input_rows ir
+ JOIN dd
+ ON dd.denom_pub_hash = ir.denom_pub_hash
+ ON CONFLICT DO NOTHING
+ RETURNING known_coin_id
+ ),--kc
+ exists AS (
+ SELECT
+ CASE
+ WHEN
+ ins.known_coin_id IS NOT NULL
+ THEN
+ FALSE
+ ELSE
+ TRUE
+ END AS existed,
+ ins.known_coin_id,
+ dd.denom_pub_hash,
+ kc.age_commitment_hash
+ FROM input_rows ir
+ LEFT JOIN ins
+ ON ins.coin_pub = ir.coin_pub
+ LEFT JOIN known_coins kc
+ ON kc.coin_pub = ir.coin_pub
+ LEFT JOIN dd
+ ON dd.denom_pub_hash = ir.denom_pub_hash
+ )--exists
+SELECT
+ exists.existed AS existed1,
+ exists.known_coin_id AS known_coin_id1,
+ exists.denom_pub_hash AS denom_pub_hash1,
+ exists.age_commitment_hash AS age_commitment_hash1,
+ (
+ SELECT exists.existed
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash2
+ ) AS existed2,
+ (
+ SELECT exists.known_coin_id
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash2
+ ) AS known_coin_id2,
+ (
+ SELECT exists.denom_pub_hash
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash2
+ ) AS denom_pub_hash2,
+ (
+ SELECT exists.age_commitment_hash
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash2
+ )AS age_commitment_hash2,
+ (
+ SELECT exists.existed
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash3
+ ) AS existed3,
+ (
+ SELECT exists.known_coin_id
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash3
+ ) AS known_coin_id3,
+ (
+ SELECT exists.denom_pub_hash
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash3
+ ) AS denom_pub_hash3,
+ (
+ SELECT exists.age_commitment_hash
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash3
+ )AS age_commitment_hash3,
+ (
+ SELECT exists.existed
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash4
+ ) AS existed4,
+ (
+ SELECT exists.known_coin_id
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash4
+ ) AS known_coin_id4,
+ (
+ SELECT exists.denom_pub_hash
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash4
+ ) AS denom_pub_hash4,
+ (
+ SELECT exists.age_commitment_hash
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash4
+ )AS age_commitment_hash4
+FROM exists;
+
+RETURN;
+END $$;
+
+
+CREATE OR REPLACE FUNCTION exchange_do_batch2_known_coin(
+ IN in_coin_pub1 BYTEA,
+ IN in_denom_pub_hash1 BYTEA,
+ IN in_h_age_commitment1 BYTEA,
+ IN in_denom_sig1 BYTEA,
+ IN in_coin_pub2 BYTEA,
+ IN in_denom_pub_hash2 BYTEA,
+ IN in_h_age_commitment2 BYTEA,
+ IN in_denom_sig2 BYTEA,
+ OUT existed1 BOOLEAN,
+ OUT existed2 BOOLEAN,
+ OUT known_coin_id1 INT8,
+ OUT known_coin_id2 INT8,
+ OUT denom_pub_hash1 BYTEA,
+ OUT denom_pub_hash2 BYTEA,
+ OUT age_commitment_hash1 BYTEA,
+ OUT age_commitment_hash2 BYTEA)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+WITH dd AS (
+SELECT
+ denominations_serial,
+ coin
+ FROM denominations
+ WHERE denom_pub_hash
+ IN
+ (in_denom_pub_hash1,
+ in_denom_pub_hash2)
+ ),--dd
+ input_rows AS (
+ VALUES
+ (in_coin_pub1,
+ in_denom_pub_hash1,
+ in_h_age_commitment1,
+ in_denom_sig1),
+ (in_coin_pub2,
+ in_denom_pub_hash2,
+ in_h_age_commitment2,
+ in_denom_sig2)
+ ),--ir
+ ins AS (
+ INSERT INTO known_coins (
+ coin_pub,
+ denominations_serial,
+ age_commitment_hash,
+ denom_sig,
+ remaining
+ )
+ SELECT
+ ir.coin_pub,
+ dd.denominations_serial,
+ ir.age_commitment_hash,
+ ir.denom_sig,
+ dd.coin
+ FROM input_rows ir
+ JOIN dd
+ ON dd.denom_pub_hash = ir.denom_pub_hash
+ ON CONFLICT DO NOTHING
+ RETURNING known_coin_id
+ ),--kc
+ exists AS (
+ SELECT
+ CASE
+ WHEN ins.known_coin_id IS NOT NULL
+ THEN
+ FALSE
+ ELSE
+ TRUE
+ END AS existed,
+ ins.known_coin_id,
+ dd.denom_pub_hash,
+ kc.age_commitment_hash
+ FROM input_rows ir
+ LEFT JOIN ins
+ ON ins.coin_pub = ir.coin_pub
+ LEFT JOIN known_coins kc
+ ON kc.coin_pub = ir.coin_pub
+ LEFT JOIN dd
+ ON dd.denom_pub_hash = ir.denom_pub_hash
+ )--exists
+SELECT
+ exists.existed AS existed1,
+ exists.known_coin_id AS known_coin_id1,
+ exists.denom_pub_hash AS denom_pub_hash1,
+ exists.age_commitment_hash AS age_commitment_hash1,
+ (
+ SELECT exists.existed
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash2
+ ) AS existed2,
+ (
+ SELECT exists.known_coin_id
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash2
+ ) AS known_coin_id2,
+ (
+ SELECT exists.denom_pub_hash
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash2
+ ) AS denom_pub_hash2,
+ (
+ SELECT exists.age_commitment_hash
+ FROM exists
+ WHERE exists.denom_pub_hash = in_denom_pub_hash2
+ )AS age_commitment_hash2
+FROM exists;
+
+RETURN;
+END $$;
+
+
+CREATE OR REPLACE FUNCTION exchange_do_batch1_known_coin(
+ IN in_coin_pub1 BYTEA,
+ IN in_denom_pub_hash1 BYTEA,
+ IN in_h_age_commitment1 BYTEA,
+ IN in_denom_sig1 BYTEA,
+ OUT existed1 BOOLEAN,
+ OUT known_coin_id1 INT8,
+ OUT denom_pub_hash1 BYTEA,
+ OUT age_commitment_hash1 BYTEA)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+WITH dd AS (
+SELECT
+ denominations_serial,
+ coin
+ FROM denominations
+ WHERE denom_pub_hash
+ IN
+ (in_denom_pub_hash1,
+ in_denom_pub_hash2)
+ ),--dd
+ input_rows AS (
+ VALUES
+ (in_coin_pub1,
+ in_denom_pub_hash1,
+ in_h_age_commitment1,
+ in_denom_sig1)
+ ),--ir
+ ins AS (
+ INSERT INTO known_coins (
+ coin_pub,
+ denominations_serial,
+ age_commitment_hash,
+ denom_sig,
+ remaining
+ )
+ SELECT
+ ir.coin_pub,
+ dd.denominations_serial,
+ ir.age_commitment_hash,
+ ir.denom_sig,
+ dd.coin
+ FROM input_rows ir
+ JOIN dd
+ ON dd.denom_pub_hash = ir.denom_pub_hash
+ ON CONFLICT DO NOTHING
+ RETURNING known_coin_id
+ ),--kc
+ exists AS (
+ SELECT
+ CASE
+ WHEN ins.known_coin_id IS NOT NULL
+ THEN
+ FALSE
+ ELSE
+ TRUE
+ END AS existed,
+ ins.known_coin_id,
+ dd.denom_pub_hash,
+ kc.age_commitment_hash
+ FROM input_rows ir
+ LEFT JOIN ins
+ ON ins.coin_pub = ir.coin_pub
+ LEFT JOIN known_coins kc
+ ON kc.coin_pub = ir.coin_pub
+ LEFT JOIN dd
+ ON dd.denom_pub_hash = ir.denom_pub_hash
+ )--exists
+SELECT
+ exists.existed AS existed1,
+ exists.known_coin_id AS known_coin_id1,
+ exists.denom_pub_hash AS denom_pub_hash1,
+ exists.age_commitment_hash AS age_commitment_hash1
+FROM exists;
+
+RETURN;
+END $$;
+
+/*** Experiment using a loop ***/
+/*
+CREATE OR REPLACE FUNCTION exchange_do_batch2_known_coin(
+ IN in_coin_pub1 BYTEA,
+ IN in_denom_pub_hash1 TEXT,
+ IN in_h_age_commitment1 TEXT,
+ IN in_denom_sig1 TEXT,
+ IN in_coin_pub2 BYTEA,
+ IN in_denom_pub_hash2 TEXT,
+ IN in_h_age_commitment2 TEXT,
+ IN in_denom_sig2 TEXT,
+ OUT existed1 BOOLEAN,
+ OUT existed2 BOOLEAN,
+ OUT known_coin_id1 INT8,
+ OUT known_coin_id2 INT8,
+ OUT denom_pub_hash1 TEXT,
+ OUT denom_pub_hash2 TEXT,
+ OUT age_commitment_hash1 TEXT,
+ OUT age_commitment_hash2 TEXT)
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ ins_values RECORD;
+BEGIN
+ FOR i IN 1..2 LOOP
+ ins_values := (
+ SELECT
+ in_coin_pub1 AS coin_pub,
+ in_denom_pub_hash1 AS denom_pub_hash,
+ in_h_age_commitment1 AS age_commitment_hash,
+ in_denom_sig1 AS denom_sig
+ WHERE i = 1
+ UNION
+ SELECT
+ in_coin_pub2 AS coin_pub,
+ in_denom_pub_hash2 AS denom_pub_hash,
+ in_h_age_commitment2 AS age_commitment_hash,
+ in_denom_sig2 AS denom_sig
+ WHERE i = 2
+ );
+ WITH dd (denominations_serial, coin) AS (
+ SELECT denominations_serial, coin
+ FROM denominations
+ WHERE denom_pub_hash = ins_values.denom_pub_hash
+ ),
+ input_rows(coin_pub) AS (
+ VALUES (ins_values.coin_pub)
+ ),
+ ins AS (
+ INSERT INTO known_coins (
+ coin_pub,
+ denominations_serial,
+ age_commitment_hash,
+ denom_sig,
+ remaining
+ ) SELECT
+ input_rows.coin_pub,
+ dd.denominations_serial,
+ ins_values.age_commitment_hash,
+ ins_values.denom_sig,
+ coin
+ FROM dd
+ CROSS JOIN input_rows
+ ON CONFLICT DO NOTHING
+ RETURNING known_coin_id, denom_pub_hash
+ )
+ SELECT
+ CASE i
+ WHEN 1 THEN
+ COALESCE(ins.known_coin_id, 0) <> 0 AS existed1,
+ ins.known_coin_id AS known_coin_id1,
+ ins.denom_pub_hash AS denom_pub_hash1,
+ ins.age_commitment_hash AS age_commitment_hash1
+ WHEN 2 THEN
+ COALESCE(ins.known_coin_id, 0) <> 0 AS existed2,
+ ins.known_coin_id AS known_coin_id2,
+ ins.denom_pub_hash AS denom_pub_hash2,
+ ins.age_commitment_hash AS age_commitment_hash2
+ END
+ FROM ins;
+ END LOOP;
+END;
+$$;*/
diff --git a/src/exchangedb/exchange_do_batch_reserves_in.sql b/src/exchangedb/exchange_do_batch_reserves_in.sql
deleted file mode 100644
index faad2ca84..000000000
--- a/src/exchangedb/exchange_do_batch_reserves_in.sql
+++ /dev/null
@@ -1,130 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2014--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/>
---
-CREATE OR REPLACE FUNCTION batch_reserves_in(
- IN in_reserve_pub BYTEA,
- IN in_expiration_date INT8,
- IN in_gc_date INT8,
- IN in_wire_ref INT8,
- IN in_credit_val INT8,
- IN in_credit_frac INT4,
- IN in_exchange_account_name VARCHAR,
- IN in_exectution_date INT8,
- IN in_wire_source_h_payto BYTEA, ---h_payto
- IN in_payto_uri VARCHAR,
- IN in_reserve_expiration INT8,
- OUT out_reserve_found BOOLEAN,
- OUT transaction_duplicate BOOLEAN,
- OUT ruuid INT8)
-LANGUAGE plpgsql
-AS $$
-DECLARE
- my_amount_val INT8;
-DECLARE
- my_amount_frac INT4;
-BEGIN
-
- INSERT INTO reserves
- (reserve_pub
- ,current_balance_val
- ,current_balance_frac
- ,expiration_date
- ,gc_date)
- VALUES
- (in_reserve_pub
- ,in_credit_val
- ,in_credit_frac
- ,in_expiration_date
- ,in_gc_date)
- ON CONFLICT DO NOTHING
- RETURNING reserve_uuid INTO ruuid;
-
- IF FOUND
- THEN
- -- We made a change, so the reserve did not previously exist.
- out_reserve_found = FALSE;
- ELSE
- -- We made no change, which means the reserve existed.
- out_reserve_found = TRUE;
- END IF;
-
- --SIMPLE INSERT ON CONFLICT DO NOTHING
- INSERT INTO wire_targets
- (wire_target_h_payto
- ,payto_uri)
- VALUES
- (in_wire_source_h_payto
- ,in_payto_uri)
- ON CONFLICT DO NOTHING;
-
- INSERT INTO reserves_in
- (reserve_pub
- ,wire_reference
- ,credit_val
- ,credit_frac
- ,exchange_account_section
- ,wire_source_h_payto
- ,execution_date)
- VALUES
- (in_reserve_pub
- ,in_wire_ref
- ,in_credit_val
- ,in_credit_frac
- ,in_exchange_account_name
- ,in_wire_source_h_payto
- ,in_expiration_date);
-
- --IF THE INSERTION WAS A SUCCESS IT MEANS NO DUPLICATED TRANSACTION
- IF FOUND
- THEN
- transaction_duplicate = FALSE;
- IF out_reserve_found
- THEN
- UPDATE reserves
- SET
- current_balance_frac = current_balance_frac+in_credit_frac
- - CASE
- WHEN current_balance_frac + in_credit_frac >= 100000000
- THEN 100000000
- ELSE 1
- END
- ,current_balance_val = current_balance_val+in_credit_val
- + CASE
- WHEN current_balance_frac + in_credit_frac >= 100000000
- THEN 1
- ELSE 0
- END
- ,expiration_date=GREATEST(expiration_date,in_expiration_date)
- ,gc_date=GREATEST(gc_date,in_expiration_date)
- WHERE reserves.reserve_pub=in_reserve_pub;
- out_reserve_found = TRUE;
- RETURN;
- ELSE
- out_reserve_found=FALSE;
- RETURN;
- END IF;
- out_reserve_found = TRUE;
- ELSE
- transaction_duplicate = TRUE;
- IF out_reserve_found
- THEN
- out_reserve_found = TRUE;
- RETURN;
- ELSE
- out_reserve_found = FALSE;
- RETURN;
- END IF;
- END IF;
-END $$;
diff --git a/src/exchangedb/exchange_do_batch_reserves_update.sql b/src/exchangedb/exchange_do_batch_reserves_update.sql
new file mode 100644
index 000000000..ebb58a149
--- /dev/null
+++ b/src/exchangedb/exchange_do_batch_reserves_update.sql
@@ -0,0 +1,72 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--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/>
+--
+
+CREATE OR REPLACE FUNCTION exchange_do_batch_reserves_update(
+ IN in_reserve_pub BYTEA,
+ IN in_expiration_date INT8,
+ IN in_wire_ref INT8,
+ IN in_credit taler_amount,
+ IN in_exchange_account_name TEXT,
+ IN in_wire_source_h_payto BYTEA,
+ IN in_notify text,
+ OUT out_duplicate BOOLEAN)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ INSERT INTO reserves_in
+ (reserve_pub
+ ,wire_reference
+ ,credit
+ ,exchange_account_section
+ ,wire_source_h_payto
+ ,execution_date)
+ VALUES
+ (in_reserve_pub
+ ,in_wire_ref
+ ,in_credit
+ ,in_exchange_account_name
+ ,in_wire_source_h_payto
+ ,in_expiration_date)
+ ON CONFLICT DO NOTHING;
+ IF FOUND
+ THEN
+ --IF THE INSERTION WAS A SUCCESS IT MEANS NO DUPLICATED TRANSACTION
+ out_duplicate = FALSE;
+ UPDATE reserves rs
+ SET
+ current_balance.frac = (rs.current_balance).frac+in_credit.frac
+ - CASE
+ WHEN (rs.current_balance).frac + in_credit.frac >= 100000000
+ THEN 100000000
+ ELSE 1
+ END
+ ,current_balance.val = (rs.current_balance).val+in_credit.val
+ + CASE
+ WHEN (rs.current_balance).frac + in_credit.frac >= 100000000
+ THEN 1
+ ELSE 0
+ END
+ ,expiration_date=GREATEST(expiration_date,in_expiration_date)
+ ,gc_date=GREATEST(gc_date,in_expiration_date)
+ WHERE reserve_pub=in_reserve_pub;
+ EXECUTE FORMAT (
+ 'NOTIFY %s'
+ ,in_notify);
+ ELSE
+ out_duplicate = TRUE;
+ END IF;
+ RETURN;
+END $$;
diff --git a/src/exchangedb/exchange_do_batch_withdraw.sql b/src/exchangedb/exchange_do_batch_withdraw.sql
index fedb7e912..a48561a9a 100644
--- a/src/exchangedb/exchange_do_batch_withdraw.sql
+++ b/src/exchangedb/exchange_do_batch_withdraw.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -13,24 +13,27 @@
-- 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/>
--
+-- @author Christian Grothoff
+-- @author Özgür Kesim
CREATE OR REPLACE FUNCTION exchange_do_batch_withdraw(
- IN amount_val INT8,
- IN amount_frac INT4,
+ IN amount taler_amount,
IN rpub BYTEA,
IN now INT8,
IN min_reserve_gc INT8,
+ IN do_age_check BOOLEAN,
OUT reserve_found BOOLEAN,
OUT balance_ok BOOLEAN,
+ OUT reserve_balance taler_amount,
+ OUT age_ok BOOLEAN,
+ OUT allowed_maximum_age INT2, -- in years
OUT ruuid INT8)
LANGUAGE plpgsql
AS $$
DECLARE
- reserve_gc INT8;
-DECLARE
- reserve_val INT8;
-DECLARE
- reserve_frac INT4;
+ reserve RECORD;
+ balance taler_amount;
+ not_before date;
BEGIN
-- Shards: reserves by reserve_pub (SELECT)
-- reserves_out (INSERT, with CONFLICT detection) by wih
@@ -38,16 +41,11 @@ BEGIN
-- reserves_in by reserve_pub (SELECT)
-- wire_targets by wire_target_h_payto
-SELECT
- current_balance_val
- ,current_balance_frac
- ,gc_date
- ,reserve_uuid
- INTO
- reserve_val
- ,reserve_frac
- ,reserve_gc
- ,ruuid
+SELECT current_balance
+ ,reserve_uuid
+ ,birthday
+ ,gc_date
+ INTO reserve
FROM exchange.reserves
WHERE reserves.reserve_pub=rpub;
@@ -56,51 +54,73 @@ THEN
-- reserve unknown
reserve_found=FALSE;
balance_ok=FALSE;
+ reserve_balance.frac = 0;
+ reserve_balance.val = 0;
+ age_ok=FALSE;
+ allowed_maximum_age=0;
ruuid=2;
RETURN;
END IF;
+reserve_found=TRUE;
+reserve_balance = reserve.current_balance;
+ruuid = reserve.reserve_uuid;
+
+-- Check if age requirements are present
+IF ((NOT do_age_check) OR (reserve.birthday = 0))
+THEN
+ age_ok = TRUE;
+ allowed_maximum_age = -1;
+ELSE
+ -- Age requirements are formally not met: The exchange is setup to support
+ -- age restrictions (do_age_check == TRUE) and the reserve has a
+ -- birthday set (reserve_birthday != 0), but the client called the
+ -- batch-withdraw endpoint instead of the age-withdraw endpoint, which it
+ -- should have.
+ not_before=date '1970-01-01' + reserve.birthday;
+ allowed_maximum_age = extract(year from age(current_date, not_before));
+
+ balance_ok=FALSE;
+ age_ok = FALSE;
+ RETURN;
+END IF;
+
-- Check reserve balance is sufficient.
-IF (reserve_val > amount_val)
+IF (reserve_balance.val > amount.val)
THEN
- IF (reserve_frac >= amount_frac)
+ IF (reserve_balance.frac >= amount.frac)
THEN
- reserve_val=reserve_val - amount_val;
- reserve_frac=reserve_frac - amount_frac;
+ balance.val=reserve_balance.val - amount.val;
+ balance.frac=reserve_balance.frac - amount.frac;
ELSE
- reserve_val=reserve_val - amount_val - 1;
- reserve_frac=reserve_frac + 100000000 - amount_frac;
+ balance.val=reserve_balance.val - amount.val - 1;
+ balance.frac=reserve_balance.frac + 100000000 - amount.frac;
END IF;
ELSE
- IF (reserve_val = amount_val) AND (reserve_frac >= amount_frac)
+ IF (reserve_balance.val = amount.val) AND (reserve_balance.frac >= amount.frac)
THEN
- reserve_val=0;
- reserve_frac=reserve_frac - amount_frac;
+ balance.val=0;
+ balance.frac=reserve_balance.frac - amount.frac;
ELSE
- reserve_found=TRUE;
balance_ok=FALSE;
RETURN;
END IF;
END IF;
-- Calculate new expiration dates.
-min_reserve_gc=GREATEST(min_reserve_gc,reserve_gc);
+min_reserve_gc=GREATEST(min_reserve_gc,reserve.gc_date);
-- Update reserve balance.
UPDATE reserves SET
gc_date=min_reserve_gc
- ,current_balance_val=reserve_val
- ,current_balance_frac=reserve_frac
+ ,current_balance=balance
WHERE
reserves.reserve_pub=rpub;
-reserve_found=TRUE;
balance_ok=TRUE;
END $$;
-COMMENT ON FUNCTION exchange_do_batch_withdraw(INT8, INT4, BYTEA, INT8, INT8)
- IS 'Checks whether the reserve has sufficient balance for a withdraw operation (or the request is repeated and was previously approved) and if so updates the database with the result. Excludes storing the planchets.';
-
-
+COMMENT ON FUNCTION exchange_do_batch_withdraw(taler_amount, BYTEA, INT8, INT8, BOOLEAN)
+ IS 'Checks whether the reserve has sufficient balance for a withdraw operation (or the request is repeated and was previously approved) and that age requirements are formally met. If so updates the database with the result. Excludes storing the planchets.';
diff --git a/src/exchangedb/exchange_do_batch_withdraw_insert.sql b/src/exchangedb/exchange_do_batch_withdraw_insert.sql
index 98db840f9..d36181a6b 100644
--- a/src/exchangedb/exchange_do_batch_withdraw_insert.sql
+++ b/src/exchangedb/exchange_do_batch_withdraw_insert.sql
@@ -14,14 +14,10 @@
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
-
-
-
CREATE OR REPLACE FUNCTION exchange_do_batch_withdraw_insert(
IN cs_nonce BYTEA,
- IN amount_val INT8,
- IN amount_frac INT4,
- IN h_denom_pub BYTEA,
+ IN amount taler_amount,
+ IN h_denom_pub BYTEA, -- FIXME: denom_serials should really be a parameter to this FUNCTION.
IN ruuid INT8,
IN reserve_sig BYTEA,
IN h_coin_envelope BYTEA,
@@ -45,6 +41,8 @@ out_denom_unknown=TRUE;
out_conflict=TRUE;
out_nonce_reuse=TRUE;
+-- FIXME: denom_serials should really be a parameter to this FUNCTION.
+
SELECT denominations_serial
INTO denom_serial
FROM exchange.denominations
@@ -66,8 +64,7 @@ INSERT INTO exchange.reserves_out
,reserve_uuid
,reserve_sig
,execution_date
- ,amount_with_fee_val
- ,amount_with_fee_frac)
+ ,amount_with_fee)
VALUES
(h_coin_envelope
,denom_serial
@@ -75,8 +72,7 @@ VALUES
,ruuid
,reserve_sig
,now
- ,amount_val
- ,amount_frac)
+ ,amount)
ON CONFLICT DO NOTHING;
IF NOT FOUND
@@ -120,6 +116,5 @@ END IF;
END $$;
-COMMENT ON FUNCTION exchange_do_batch_withdraw_insert(BYTEA, INT8, INT4, BYTEA, INT8, BYTEA, BYTEA, BYTEA, INT8)
+COMMENT ON FUNCTION exchange_do_batch_withdraw_insert(BYTEA, taler_amount, BYTEA, INT8, BYTEA, BYTEA, BYTEA, INT8)
IS 'Stores information about a planchet for a batch withdraw operation. Checks if the planchet already exists, and in that case indicates a conflict';
-
diff --git a/src/exchangedb/exchange_do_deposit.sql b/src/exchangedb/exchange_do_deposit.sql
index a2f5ba53a..c89e9e470 100644
--- a/src/exchangedb/exchange_do_deposit.sql
+++ b/src/exchangedb/exchange_do_deposit.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -14,158 +14,193 @@
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
CREATE OR REPLACE FUNCTION exchange_do_deposit(
- IN in_amount_with_fee_val INT8,
- IN in_amount_with_fee_frac INT4,
- IN in_h_contract_terms BYTEA,
- IN in_wire_salt BYTEA,
+ -- For batch_deposits
+ IN in_shard INT8,
+ IN in_merchant_pub BYTEA,
IN in_wallet_timestamp INT8,
IN in_exchange_timestamp INT8,
IN in_refund_deadline INT8,
IN in_wire_deadline INT8,
- IN in_merchant_pub BYTEA,
- IN in_receiver_wire_account VARCHAR,
- IN in_h_payto BYTEA,
- IN in_known_coin_id INT8,
- IN in_coin_pub BYTEA,
- IN in_coin_sig BYTEA,
- IN in_shard INT8,
+ IN in_h_contract_terms BYTEA,
+ IN in_wallet_data_hash BYTEA, -- can be NULL
+ IN in_wire_salt BYTEA,
+ IN in_wire_target_h_payto BYTEA,
+ IN in_policy_details_serial_id INT8, -- can be NULL
IN in_policy_blocked BOOLEAN,
- IN in_policy_details_serial_id INT8,
+ -- For wire_targets
+ IN in_receiver_wire_account TEXT,
+ -- For coin_deposits
+ IN ina_coin_pub BYTEA[],
+ IN ina_coin_sig BYTEA[],
+ IN ina_amount_with_fee taler_amount[],
OUT out_exchange_timestamp INT8,
- OUT out_balance_ok BOOLEAN,
- OUT out_conflict BOOLEAN)
+ OUT out_insufficient_balance_coin_index INT4, -- index of coin with bad balance, NULL if none
+ OUT out_conflict BOOL
+ )
LANGUAGE plpgsql
AS $$
DECLARE
wtsi INT8; -- wire target serial id
+ bdsi INT8; -- batch_deposits serial id
+ i INT4;
+ ini_amount_with_fee taler_amount;
+ ini_coin_pub BYTEA;
+ ini_coin_sig BYTEA;
BEGIN
--- Shards: INSERT policy_details (by policy_details_serial_id)
--- INSERT wire_targets (by h_payto), on CONFLICT DO NOTHING;
--- INSERT deposits (by coin_pub, shard), ON CONFLICT DO NOTHING;
--- UPDATE known_coins (by coin_pub)
+-- Shards:
+-- INSERT wire_targets (by h_payto), ON CONFLICT DO NOTHING;
+-- INSERT batch_deposits (by shard, merchant_pub), ON CONFLICT idempotency check;
+-- INSERT[] coin_deposits (by coin_pub), ON CONFLICT idempotency check;
+-- UPDATE[] known_coins (by coin_pub)
+
-INSERT INTO exchange.wire_targets
- (wire_target_h_payto
- ,payto_uri)
+-- First, get or create the 'wtsi'
+INSERT INTO wire_targets
+ (wire_target_h_payto
+ ,payto_uri)
VALUES
- (in_h_payto
- ,in_receiver_wire_account)
-ON CONFLICT DO NOTHING -- for CONFLICT ON (wire_target_h_payto)
- RETURNING wire_target_serial_id INTO wtsi;
+ (in_wire_target_h_payto
+ ,in_receiver_wire_account)
+ ON CONFLICT DO NOTHING -- for CONFLICT ON (wire_target_h_payto)
+ RETURNING
+ wire_target_serial_id
+ INTO
+ wtsi;
IF NOT FOUND
THEN
- SELECT wire_target_serial_id
- INTO wtsi
- FROM exchange.wire_targets
- WHERE wire_target_h_payto=in_h_payto;
+ SELECT
+ wire_target_serial_id
+ INTO
+ wtsi
+ FROM wire_targets
+ WHERE
+ wire_target_h_payto=in_wire_target_h_payto;
END IF;
-INSERT INTO exchange.deposits
+-- Second, create the batch_deposits entry
+INSERT INTO batch_deposits
(shard
- ,coin_pub
- ,known_coin_id
- ,amount_with_fee_val
- ,amount_with_fee_frac
+ ,merchant_pub
,wallet_timestamp
,exchange_timestamp
,refund_deadline
,wire_deadline
- ,merchant_pub
,h_contract_terms
- ,coin_sig
+ ,wallet_data_hash
,wire_salt
,wire_target_h_payto
- ,policy_blocked
,policy_details_serial_id
+ ,policy_blocked
)
VALUES
(in_shard
- ,in_coin_pub
- ,in_known_coin_id
- ,in_amount_with_fee_val
- ,in_amount_with_fee_frac
+ ,in_merchant_pub
,in_wallet_timestamp
,in_exchange_timestamp
,in_refund_deadline
,in_wire_deadline
- ,in_merchant_pub
,in_h_contract_terms
- ,in_coin_sig
+ ,in_wallet_data_hash
,in_wire_salt
- ,in_h_payto
- ,in_policy_blocked
- ,in_policy_details_serial_id)
- ON CONFLICT DO NOTHING;
+ ,in_wire_target_h_payto
+ ,in_policy_details_serial_id
+ ,in_policy_blocked)
+ ON CONFLICT DO NOTHING -- for CONFLICT ON (merchant_pub, h_contract_terms)
+ RETURNING
+ batch_deposit_serial_id
+ INTO
+ bdsi;
IF NOT FOUND
THEN
-- Idempotency check: see if an identical record exists.
- -- Note that by checking 'coin_sig', we implicitly check
- -- identity over everything that the signature covers.
- -- We do select over merchant_pub and wire_target_h_payto
- -- primarily here to maximally use the existing index.
+ -- We do select over merchant_pub, h_contract_terms and wire_target_h_payto
+ -- first to maximally increase the chance of using the existing index.
SELECT
- exchange_timestamp
+ exchange_timestamp
+ ,batch_deposit_serial_id
INTO
- out_exchange_timestamp
- FROM exchange.deposits
+ out_exchange_timestamp
+ ,bdsi
+ FROM batch_deposits
WHERE shard=in_shard
AND merchant_pub=in_merchant_pub
- AND wire_target_h_payto=in_h_payto
- AND coin_pub=in_coin_pub
- AND coin_sig=in_coin_sig;
- -- AND policy_details_serial_id=in_policy_details_serial_id; -- FIXME: is this required for idempotency?
-
+ AND h_contract_terms=in_h_contract_terms
+ AND wire_target_h_payto=in_wire_target_h_payto
+ -- now check the rest, too
+ AND ( (wallet_data_hash=in_wallet_data_hash) OR
+ (wallet_data_hash IS NULL AND in_wallet_data_hash IS NULL) )
+ AND wire_salt=in_wire_salt
+ AND wallet_timestamp=in_wallet_timestamp
+ AND refund_deadline=in_refund_deadline
+ AND wire_deadline=in_wire_deadline
+ AND ( (policy_details_serial_id=in_policy_details_serial_id) OR
+ (policy_details_serial_id IS NULL AND in_policy_details_serial_id IS NULL) );
IF NOT FOUND
THEN
- -- Deposit exists, but with differences. Not allowed.
- out_balance_ok=FALSE;
+ -- Deposit exists, but with *strange* differences. Not allowed.
out_conflict=TRUE;
RETURN;
END IF;
+END IF;
- -- Idempotent request known, return success.
- out_balance_ok=TRUE;
- out_conflict=FALSE;
+out_conflict=FALSE;
- RETURN;
-END IF;
+-- Deposit each coin
+FOR i IN 1..array_length(ina_coin_pub,1)
+LOOP
+ ini_coin_pub = ina_coin_pub[i];
+ ini_coin_sig = ina_coin_sig[i];
+ ini_amount_with_fee = ina_amount_with_fee[i];
-out_exchange_timestamp=in_exchange_timestamp;
+ INSERT INTO coin_deposits
+ (batch_deposit_serial_id
+ ,coin_pub
+ ,coin_sig
+ ,amount_with_fee
+ )
+ VALUES
+ (bdsi
+ ,ini_coin_pub
+ ,ini_coin_sig
+ ,ini_amount_with_fee
+ )
+ ON CONFLICT DO NOTHING;
--- Check and update balance of the coin.
-UPDATE known_coins
- SET
- remaining_frac=remaining_frac-in_amount_with_fee_frac
- + CASE
- WHEN remaining_frac < in_amount_with_fee_frac
- THEN 100000000
- ELSE 0
- END,
- remaining_val=remaining_val-in_amount_with_fee_val
- - CASE
- WHEN remaining_frac < in_amount_with_fee_frac
- THEN 1
- ELSE 0
- END
- WHERE coin_pub=in_coin_pub
- AND ( (remaining_val > in_amount_with_fee_val) OR
- ( (remaining_frac >= in_amount_with_fee_frac) AND
- (remaining_val >= in_amount_with_fee_val) ) );
+ IF FOUND
+ THEN
+ -- Insert did happen, update balance in known_coins!
-IF NOT FOUND
-THEN
- -- Insufficient balance.
- out_balance_ok=FALSE;
- out_conflict=FALSE;
- RETURN;
-END IF;
+ UPDATE known_coins kc
+ SET
+ remaining.frac=(kc.remaining).frac-ini_amount_with_fee.frac
+ + CASE
+ WHEN (kc.remaining).frac < ini_amount_with_fee.frac
+ THEN 100000000
+ ELSE 0
+ END,
+ remaining.val=(kc.remaining).val-ini_amount_with_fee.val
+ - CASE
+ WHEN (kc.remaining).frac < ini_amount_with_fee.frac
+ THEN 1
+ ELSE 0
+ END
+ WHERE coin_pub=ini_coin_pub
+ AND ( ((kc.remaining).val > ini_amount_with_fee.val) OR
+ ( ((kc.remaining).frac >= ini_amount_with_fee.frac) AND
+ ((kc.remaining).val >= ini_amount_with_fee.val) ) );
--- Everything fine, return success!
-out_balance_ok=TRUE;
-out_conflict=FALSE;
+ IF NOT FOUND
+ THEN
+ -- Insufficient balance.
+ -- Note: C arrays are 0 indexed, but i started at 1
+ out_insufficient_balance_coin_index=i-1;
+ RETURN;
+ END IF;
+ END IF;
+END LOOP; -- end FOR all coins
END $$;
diff --git a/src/exchangedb/exchange_do_expire_purse.sql b/src/exchangedb/exchange_do_expire_purse.sql
index 82756abc5..ee9757f03 100644
--- a/src/exchangedb/exchange_do_expire_purse.sql
+++ b/src/exchangedb/exchange_do_expire_purse.sql
@@ -35,12 +35,11 @@ SELECT purse_pub
,in_reserve_quota
INTO my_purse_pub
,my_in_reserve_quota
- FROM exchange.purse_requests
+ FROM purse_requests
WHERE (purse_expiration >= in_start_time) AND
(purse_expiration < in_end_time) AND
- purse_pub NOT IN (SELECT purse_pub
- FROM purse_decision)
- ORDER BY purse_expiration ASC
+ NOT was_decided
+ ORDER BY purse_expiration ASC
LIMIT 1;
out_found = FOUND;
IF NOT FOUND
@@ -57,6 +56,9 @@ VALUES
,in_now
,TRUE);
+-- Code for 'TALER_DBEVENT_EXCHANGE_PURSE_REFUNDED'
+NOTIFY X8DJSPNYJMNZDAP7GN6YQ4EZVSQXMF3HRP4VAR347WP9SZYP1C200;
+
IF (my_in_reserve_quota)
THEN
UPDATE reserves
@@ -71,21 +73,20 @@ END IF;
-- restore balance to each coin deposited into the purse
FOR my_deposit IN
SELECT coin_pub
- ,amount_with_fee_val
- ,amount_with_fee_frac
- FROM exchange.purse_deposits
+ ,amount_with_fee
+ FROM purse_deposits
WHERE purse_pub = my_purse_pub
LOOP
- UPDATE exchange.known_coins SET
- remaining_frac=remaining_frac+my_deposit.amount_with_fee_frac
+ UPDATE known_coins kc SET
+ remaining.frac=(kc.remaining).frac+(my_deposit.amount_with_fee).frac
- CASE
- WHEN remaining_frac+my_deposit.amount_with_fee_frac >= 100000000
+ WHEN (kc.remaining).frac+(my_deposit.amount_with_fee).frac >= 100000000
THEN 100000000
ELSE 0
END,
- remaining_val=remaining_val+my_deposit.amount_with_fee_val
+ remaining.val=(kc.remaining).val+(my_deposit.amount_with_fee).val
+ CASE
- WHEN remaining_frac+my_deposit.amount_with_fee_frac >= 100000000
+ WHEN (kc.remaining).frac+(my_deposit.amount_with_fee).frac >= 100000000
THEN 1
ELSE 0
END
@@ -95,5 +96,3 @@ END $$;
COMMENT ON FUNCTION exchange_do_expire_purse(INT8,INT8,INT8)
IS 'Finds an expired purse in the given time range and refunds the coins (if any).';
-
-
diff --git a/src/exchangedb/exchange_do_gc.sql b/src/exchangedb/exchange_do_gc.sql
index c6331c18e..d4ecb3024 100644
--- a/src/exchangedb/exchange_do_gc.sql
+++ b/src/exchangedb/exchange_do_gc.sql
@@ -21,124 +21,120 @@ LANGUAGE plpgsql
AS $$
DECLARE
reserve_uuid_min INT8; -- minimum reserve UUID still alive
-DECLARE
melt_min INT8; -- minimum melt still alive
-DECLARE
coin_min INT8; -- minimum known_coin still alive
-DECLARE
- deposit_min INT8; -- minimum deposit still alive
-DECLARE
+ batch_deposit_min INT8; -- minimum deposit still alive
reserve_out_min INT8; -- minimum reserve_out still alive
-DECLARE
denom_min INT8; -- minimum denomination still alive
BEGIN
-DELETE FROM exchange.prewire
+DELETE FROM prewire
WHERE finished=TRUE;
-DELETE FROM exchange.wire_fee
+DELETE FROM wire_fee
WHERE end_date < in_ancient_date;
--- TODO: use closing fee as threshold?
-DELETE FROM exchange.reserves
+-- FIXME: use closing fee as threshold?
+DELETE FROM reserves
WHERE gc_date < in_now
- AND current_balance_val = 0
- AND current_balance_frac = 0;
+ AND current_balance = (0, 0);
SELECT
reserve_out_serial_id
INTO
reserve_out_min
- FROM exchange.reserves_out
+ FROM reserves_out
ORDER BY reserve_out_serial_id ASC
LIMIT 1;
-DELETE FROM exchange.recoup
+DELETE FROM recoup
WHERE reserve_out_serial_id < reserve_out_min;
--- FIXME: recoup_refresh lacks GC!
SELECT
reserve_uuid
INTO
reserve_uuid_min
- FROM exchange.reserves
+ FROM reserves
ORDER BY reserve_uuid ASC
LIMIT 1;
-DELETE FROM exchange.reserves_out
+DELETE FROM reserves_out
WHERE reserve_uuid < reserve_uuid_min;
-- FIXME: this query will be horribly slow;
-- need to find another way to formulate it...
-DELETE FROM exchange.denominations
+DELETE FROM denominations
WHERE expire_legal < in_now
AND denominations_serial NOT IN
(SELECT DISTINCT denominations_serial
- FROM exchange.reserves_out)
+ FROM reserves_out)
AND denominations_serial NOT IN
(SELECT DISTINCT denominations_serial
- FROM exchange.known_coins
+ FROM known_coins
WHERE coin_pub IN
(SELECT DISTINCT coin_pub
- FROM exchange.recoup))
+ FROM recoup))
AND denominations_serial NOT IN
(SELECT DISTINCT denominations_serial
- FROM exchange.known_coins
+ FROM known_coins
WHERE coin_pub IN
(SELECT DISTINCT coin_pub
- FROM exchange.recoup_refresh));
+ FROM recoup_refresh));
SELECT
melt_serial_id
INTO
melt_min
- FROM exchange.refresh_commitments
+ FROM refresh_commitments
ORDER BY melt_serial_id ASC
LIMIT 1;
-DELETE FROM exchange.refresh_revealed_coins
+DELETE FROM refresh_revealed_coins
WHERE melt_serial_id < melt_min;
-DELETE FROM exchange.refresh_transfer_keys
+DELETE FROM refresh_transfer_keys
WHERE melt_serial_id < melt_min;
SELECT
known_coin_id
INTO
coin_min
- FROM exchange.known_coins
+ FROM known_coins
ORDER BY known_coin_id ASC
LIMIT 1;
-DELETE FROM exchange.deposits
+DELETE FROM recoup_refresh
WHERE known_coin_id < coin_min;
+DELETE FROM batch_deposits
+ WHERE wire_deadline < in_ancient_date;
+
SELECT
- deposit_serial_id
+ batch_deposit_serial_id
INTO
- deposit_min
- FROM exchange.deposits
- ORDER BY deposit_serial_id ASC
+ batch_deposit_min
+ FROM coin_deposits
+ ORDER BY batch_deposit_serial_id ASC
LIMIT 1;
-DELETE FROM exchange.refunds
- WHERE deposit_serial_id < deposit_min;
+DELETE FROM refunds
+ WHERE batch_deposit_serial_id < batch_deposit_min;
+DELETE FROM aggregation_tracking
+ WHERE batch_deposit_serial_id < batch_deposit_min;
+DELETE FROM coin_deposits
+ WHERE batch_deposit_serial_id < batch_deposit_min;
+
-DELETE FROM exchange.aggregation_tracking
- WHERE deposit_serial_id < deposit_min;
SELECT
denominations_serial
INTO
denom_min
- FROM exchange.denominations
+ FROM denominations
ORDER BY denominations_serial ASC
LIMIT 1;
-DELETE FROM exchange.cs_nonce_locks
+DELETE FROM cs_nonce_locks
WHERE max_denomination_serial <= denom_min;
END $$;
-
-
-
diff --git a/src/exchangedb/exchange_do_get_link_data.sql b/src/exchangedb/exchange_do_get_link_data.sql
new file mode 100644
index 000000000..08611a5ea
--- /dev/null
+++ b/src/exchangedb/exchange_do_get_link_data.sql
@@ -0,0 +1,59 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--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/>
+--
+
+CREATE OR REPLACE FUNCTION exchange_do_get_link_data(
+ IN in_coin_pub BYTEA
+)
+RETURNS SETOF record
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ curs CURSOR
+ FOR
+ SELECT
+ melt_serial_id
+ FROM refresh_commitments
+ WHERE old_coin_pub=in_coin_pub;
+
+DECLARE
+ i RECORD;
+BEGIN
+OPEN curs;
+LOOP
+ FETCH NEXT FROM curs INTO i;
+ EXIT WHEN NOT FOUND;
+ RETURN QUERY
+ SELECT
+ tp.transfer_pub
+ ,denoms.denom_pub
+ ,rrc.ev_sig
+ ,rrc.ewv
+ ,rrc.link_sig
+ ,rrc.freshcoin_index
+ ,rrc.coin_ev
+ FROM refresh_revealed_coins rrc
+ JOIN refresh_transfer_keys tp
+ ON (tp.melt_serial_id=rrc.melt_serial_id)
+ JOIN denominations denoms
+ ON (rrc.denominations_serial=denoms.denominations_serial)
+ WHERE rrc.melt_serial_id =i.melt_serial_id
+/* GROUP BY tp.transfer_pub, denoms.denom_pub, rrc.ev_sig,rrc.ewv,rrc.link_sig,rrc.freshcoin_index, rrc.coin_ev*/
+ ORDER BY tp.transfer_pub,
+ rrc.freshcoin_index ASC
+ ;
+END LOOP;
+CLOSE curs;
+END $$;
diff --git a/src/exchangedb/exchange_do_history_request.sql b/src/exchangedb/exchange_do_history_request.sql
deleted file mode 100644
index 2f6041741..000000000
--- a/src/exchangedb/exchange_do_history_request.sql
+++ /dev/null
@@ -1,85 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2014--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/>
---
-
-
-CREATE OR REPLACE FUNCTION exchange_do_history_request(
- IN in_reserve_pub BYTEA,
- IN in_reserve_sig BYTEA,
- IN in_request_timestamp INT8,
- IN in_history_fee_val INT8,
- IN in_history_fee_frac INT4,
- OUT out_balance_ok BOOLEAN,
- OUT out_idempotent BOOLEAN)
-LANGUAGE plpgsql
-AS $$
-BEGIN
-
- -- Insert and check for idempotency.
- INSERT INTO exchange.history_requests
- (reserve_pub
- ,request_timestamp
- ,reserve_sig
- ,history_fee_val
- ,history_fee_frac)
- VALUES
- (in_reserve_pub
- ,in_request_timestamp
- ,in_reserve_sig
- ,in_history_fee_val
- ,in_history_fee_frac)
- ON CONFLICT DO NOTHING;
-
- IF NOT FOUND
- THEN
- out_balance_ok=TRUE;
- out_idempotent=TRUE;
- RETURN;
- END IF;
-
- out_idempotent=FALSE;
-
- -- Update reserve balance.
- UPDATE exchange.reserves
- SET
- current_balance_frac=current_balance_frac-in_history_fee_frac
- + CASE
- WHEN current_balance_frac < in_history_fee_frac
- THEN 100000000
- ELSE 0
- END,
- current_balance_val=current_balance_val-in_history_fee_val
- - CASE
- WHEN current_balance_frac < in_history_fee_frac
- THEN 1
- ELSE 0
- END
- WHERE
- reserve_pub=in_reserve_pub
- AND ( (current_balance_val > in_history_fee_val) OR
- ( (current_balance_frac >= in_history_fee_frac) AND
- (current_balance_val >= in_history_fee_val) ) );
-
- IF NOT FOUND
- THEN
- -- Either reserve does not exist, or balance insufficient.
- -- Both we treat the same here as balance insufficient.
- out_balance_ok=FALSE;
- RETURN;
- END IF;
-
- out_balance_ok=TRUE;
-END $$;
-
diff --git a/src/exchangedb/exchange_do_insert_aml_decision.sql b/src/exchangedb/exchange_do_insert_aml_decision.sql
new file mode 100644
index 000000000..c8ed7e928
--- /dev/null
+++ b/src/exchangedb/exchange_do_insert_aml_decision.sql
@@ -0,0 +1,127 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2023 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/>
+--
+
+CREATE OR REPLACE FUNCTION exchange_do_insert_aml_decision(
+ IN in_h_payto BYTEA,
+ IN in_new_threshold taler_amount,
+ IN in_new_status INT4,
+ IN in_decision_time INT8,
+ IN in_justification TEXT,
+ IN in_decider_pub BYTEA,
+ IN in_decider_sig BYTEA,
+ IN in_notify_s TEXT,
+ IN in_kyc_requirements TEXT,
+ IN in_requirement_row INT8,
+ OUT out_invalid_officer BOOLEAN,
+ OUT out_last_date INT8)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+-- Check officer is eligible to make decisions.
+PERFORM
+ FROM aml_staff
+ WHERE decider_pub=in_decider_pub
+ AND is_active
+ AND NOT read_only;
+IF NOT FOUND
+THEN
+ out_invalid_officer=TRUE;
+ out_last_date=0;
+ RETURN;
+END IF;
+out_invalid_officer=FALSE;
+
+-- Check no more recent decision exists.
+SELECT decision_time
+ INTO out_last_date
+ FROM aml_history
+ WHERE h_payto=in_h_payto
+ ORDER BY decision_time DESC;
+IF FOUND
+THEN
+ IF out_last_date >= in_decision_time
+ THEN
+ -- Refuse to insert older decision.
+ RETURN;
+ END IF;
+ UPDATE aml_status
+ SET threshold=in_new_threshold
+ ,status=in_new_status
+ ,kyc_requirement=in_requirement_row
+ WHERE h_payto=in_h_payto;
+ ASSERT FOUND, 'cannot have AML decision history but no AML status';
+ELSE
+ out_last_date = 0;
+ INSERT INTO aml_status
+ (h_payto
+ ,threshold
+ ,status
+ ,kyc_requirement)
+ VALUES
+ (in_h_payto
+ ,in_new_threshold
+ ,in_new_status
+ ,in_requirement_row)
+ ON CONFLICT (h_payto) DO
+ UPDATE SET
+ threshold=in_new_threshold
+ ,status=in_new_status;
+END IF;
+
+
+INSERT INTO aml_history
+ (h_payto
+ ,new_threshold
+ ,new_status
+ ,decision_time
+ ,justification
+ ,kyc_requirements
+ ,kyc_req_row
+ ,decider_pub
+ ,decider_sig
+ ) VALUES
+ (in_h_payto
+ ,in_new_threshold
+ ,in_new_status
+ ,in_decision_time
+ ,in_justification
+ ,in_kyc_requirements
+ ,in_requirement_row
+ ,in_decider_pub
+ ,in_decider_sig);
+
+
+-- wake up taler-exchange-aggregator
+IF 0 = in_new_status
+THEN
+ INSERT INTO kyc_alerts
+ (h_payto
+ ,trigger_type)
+ VALUES
+ (in_h_payto,1);
+
+ EXECUTE FORMAT (
+ 'NOTIFY %s'
+ ,in_notify_s);
+
+END IF;
+
+
+END $$;
+
+
+COMMENT ON FUNCTION exchange_do_insert_aml_decision(BYTEA, taler_amount, INT4, INT8, TEXT, BYTEA, BYTEA, TEXT, TEXT, INT8)
+ IS 'Checks whether the AML officer is eligible to make AML decisions and if so inserts the decision into the table';
diff --git a/src/exchangedb/exchange_do_insert_aml_officer.sql b/src/exchangedb/exchange_do_insert_aml_officer.sql
new file mode 100644
index 000000000..429ba11c7
--- /dev/null
+++ b/src/exchangedb/exchange_do_insert_aml_officer.sql
@@ -0,0 +1,74 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2023 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/>
+--
+
+CREATE OR REPLACE FUNCTION exchange_do_insert_aml_officer(
+ IN in_decider_pub BYTEA,
+ IN in_master_sig BYTEA,
+ IN in_decider_name TEXT,
+ IN in_is_active BOOLEAN,
+ IN in_read_only BOOLEAN,
+ IN in_last_change INT8,
+ OUT out_last_change INT8)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+INSERT INTO exchange.aml_staff
+ (decider_pub
+ ,master_sig
+ ,decider_name
+ ,is_active
+ ,read_only
+ ,last_change
+ ) VALUES
+ (in_decider_pub
+ ,in_master_sig
+ ,in_decider_name
+ ,in_is_active
+ ,in_read_only
+ ,in_last_change)
+ ON CONFLICT DO NOTHING;
+IF FOUND
+THEN
+ out_last_change=0;
+ RETURN;
+END IF;
+
+-- Check update is most recent...
+SELECT last_change
+ INTO out_last_change
+ FROM exchange.aml_staff
+ WHERE decider_pub=in_decider_pub;
+ASSERT FOUND, 'cannot have INSERT conflict but no AML staff record';
+
+IF out_last_change >= in_last_change
+THEN
+ -- Refuse to insert older status
+ RETURN;
+END IF;
+
+-- We are more recent, update existing record.
+UPDATE exchange.aml_staff
+ SET master_sig=in_master_sig
+ ,decider_name=in_decider_name
+ ,is_active=in_is_active
+ ,read_only=in_read_only
+ ,last_change=in_last_change
+ WHERE decider_pub=in_decider_pub;
+END $$;
+
+
+COMMENT ON FUNCTION exchange_do_insert_aml_officer(BYTEA, BYTEA, TEXT, BOOL, BOOL, INT8)
+ IS 'Inserts or updates AML staff record, making sure the update is more recent than the previous change';
diff --git a/src/exchangedb/exchange_do_insert_kyc_attributes.sql b/src/exchangedb/exchange_do_insert_kyc_attributes.sql
new file mode 100644
index 000000000..7db4d80c0
--- /dev/null
+++ b/src/exchangedb/exchange_do_insert_kyc_attributes.sql
@@ -0,0 +1,114 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2023 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/>
+--
+
+CREATE OR REPLACE FUNCTION exchange_do_insert_kyc_attributes(
+ IN in_process_row INT8,
+ IN in_h_payto BYTEA,
+ IN in_kyc_prox BYTEA,
+ IN in_provider_section TEXT,
+ IN in_satisfied_checks TEXT[],
+ IN in_birthday INT4,
+ IN in_provider_account_id TEXT,
+ IN in_provider_legitimization_id TEXT,
+ IN in_collection_time_ts INT8,
+ IN in_expiration_time INT8,
+ IN in_expiration_time_ts INT8,
+ IN in_enc_attributes BYTEA,
+ IN in_require_aml BOOLEAN,
+ IN in_kyc_completed_notify_s TEXT,
+ OUT out_ok BOOLEAN)
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ orig_reserve_pub BYTEA;
+ orig_reserve_found BOOLEAN;
+BEGIN
+
+INSERT INTO exchange.kyc_attributes
+ (h_payto
+ ,kyc_prox
+ ,provider
+ ,satisfied_checks
+ ,collection_time
+ ,expiration_time
+ ,encrypted_attributes
+ ,legitimization_serial
+ ) VALUES
+ (in_h_payto
+ ,in_kyc_prox
+ ,in_provider_section
+ ,in_satisfied_checks
+ ,in_collection_time_ts
+ ,in_expiration_time_ts
+ ,in_enc_attributes
+ ,in_process_row);
+
+UPDATE legitimization_processes
+ SET provider_user_id=in_provider_account_id
+ ,provider_legitimization_id=in_provider_legitimization_id
+ ,expiration_time=GREATEST(expiration_time,in_expiration_time)
+ ,finished=TRUE
+ WHERE h_payto=in_h_payto
+ AND legitimization_process_serial_id=in_process_row
+ AND provider_section=in_provider_section;
+out_ok = FOUND;
+
+
+-- If the h_payto refers to a reserve in the original requirements
+-- update the originating reserve's birthday.
+SELECT reserve_pub
+ INTO orig_reserve_pub
+ FROM exchange.legitimization_requirements
+ WHERE h_payto=in_h_payto
+ AND NOT reserve_pub IS NULL;
+orig_reserve_found = FOUND;
+
+IF orig_reserve_found
+THEN
+ UPDATE exchange.reserves
+ SET birthday=in_birthday
+ WHERE reserve_pub=orig_reserve_pub;
+END IF;
+
+IF in_require_aml
+THEN
+ INSERT INTO exchange.aml_status
+ (h_payto
+ ,status)
+ VALUES
+ (in_h_payto
+ ,1)
+ ON CONFLICT (h_payto) DO
+ UPDATE SET status=EXCLUDED.status | 1;
+END IF;
+
+EXECUTE FORMAT (
+ 'NOTIFY %s'
+ ,in_kyc_completed_notify_s);
+
+
+INSERT INTO kyc_alerts
+ (h_payto
+ ,trigger_type)
+ VALUES
+ (in_h_payto,1);
+
+
+END $$;
+
+
+COMMENT ON FUNCTION exchange_do_insert_kyc_attributes(INT8, BYTEA, BYTEA, TEXT, TEXT[], INT4, TEXT, TEXT, INT8, INT8, INT8, BYTEA, BOOL, TEXT)
+ IS 'Inserts new KYC attributes and updates the status of the legitimization process and the AML status for the account';
diff --git a/src/exchangedb/exchange_do_insert_or_update_policy_details.sql b/src/exchangedb/exchange_do_insert_or_update_policy_details.sql
index c7fe64d14..85e52d3d3 100644
--- a/src/exchangedb/exchange_do_insert_or_update_policy_details.sql
+++ b/src/exchangedb/exchange_do_insert_or_update_policy_details.sql
@@ -16,54 +16,42 @@
CREATE OR REPLACE FUNCTION exchange_do_insert_or_update_policy_details(
IN in_policy_hash_code BYTEA,
- IN in_policy_json VARCHAR,
+ IN in_policy_json TEXT,
IN in_deadline INT8,
- IN in_commitment_val INT8,
- IN in_commitment_frac INT4,
- IN in_accumulated_total_val INT8,
- IN in_accumulated_total_frac INT4,
- IN in_fee_val INT8,
- IN in_fee_frac INT4,
- IN in_transferable_val INT8,
- IN in_transferable_frac INT4,
+ IN in_commitment taler_amount,
+ IN in_accumulated_total taler_amount,
+ IN in_fee taler_amount,
+ IN in_transferable taler_amount,
IN in_fulfillment_state SMALLINT,
OUT out_policy_details_serial_id INT8,
- OUT out_accumulated_total_val INT8,
- OUT out_accumulated_total_frac INT4,
+ OUT out_accumulated_total taler_amount,
OUT out_fulfillment_state SMALLINT)
LANGUAGE plpgsql
AS $$
DECLARE
- cur_commitment_val INT8;
- cur_commitment_frac INT4;
- cur_accumulated_total_val INT8;
- cur_accumulated_total_frac INT4;
+ cur_commitment taler_amount;
+DECLARE
+ cur_accumulated_total taler_amount;
+DECLARE
+ rval RECORD;
BEGIN
-- First, try to create a new entry.
INSERT INTO policy_details
(policy_hash_code,
policy_json,
deadline,
- commitment_val,
- commitment_frac,
- accumulated_total_val,
- accumulated_total_frac,
- fee_val,
- fee_frac,
- transferable_val,
- transferable_frac,
+ commitment,
+ accumulated_total,
+ fee,
+ transferable,
fulfillment_state)
VALUES (in_policy_hash_code,
in_policy_json,
in_deadline,
- in_commitment_val,
- in_commitment_frac,
- in_accumulated_total_val,
- in_accumulated_total_frac,
- in_fee_val,
- in_fee_frac,
- in_transferable_val,
- in_transferable_frac,
+ in_commitment,
+ in_accumulated_total,
+ in_fee,
+ in_transferable,
in_fulfillment_state)
ON CONFLICT (policy_hash_code) DO NOTHING
RETURNING policy_details_serial_id INTO out_policy_details_serial_id;
@@ -71,34 +59,33 @@ BEGIN
-- If the insert was successful, return
-- We assume that the fullfilment_state was correct in first place.
IF FOUND THEN
- out_accumulated_total_val = in_accumulated_total_val;
- out_accumulated_total_frac = in_accumulated_total_frac;
- out_fulfillment_state = in_fulfillment_state;
+ out_accumulated_total = in_accumulated_total;
+ out_fulfillment_state = in_fulfillment_state;
RETURN;
END IF;
-- We had a conflict, grab the parts we need to update.
- SELECT policy_details_serial_id,
- commitment_val,
- commitment_frac,
- accumulated_total_val,
- accumulated_total_frac
- INTO out_policy_details_serial_id,
- cur_commitment_val,
- cur_commitment_frac,
- cur_accumulated_total_val,
- cur_accumulated_total_frac
+ SELECT policy_details_serial_id
+ ,commitment
+ ,accumulated_total
+ INTO rval
FROM policy_details
WHERE policy_hash_code = in_policy_hash_code;
+ -- We use rval as workaround as we cannot select
+ -- directly into the amount due to Postgres limitations.
+ out_policy_details_serial_id := rval.policy_details_serial_id;
+ cur_commitment := rval.commitment;
+ cur_accumulated_total := rval.accumulated_total;
+
-- calculate the new values (overflows throws exception)
- out_accumulated_total_val = cur_accumulated_total_val + in_accumulated_total_val;
- out_accumulated_total_frac = cur_accumulated_total_frac + in_accumulated_total_frac;
+ out_accumulated_total.val = cur_accumulated_total.val + in_accumulated_total.val;
+ out_accumulated_total.frac = cur_accumulated_total.frac + in_accumulated_total.frac;
-- normalize
- out_accumulated_total_val = out_accumulated_total_val + out_accumulated_total_frac / 100000000;
- out_accumulated_total_frac = out_accumulated_total_frac % 100000000;
+ out_accumulated_total.val = out_accumulated_total.val + out_accumulated_total.frac / 100000000;
+ out_accumulated_total.frac = out_accumulated_total.frac % 100000000;
- IF (out_accumulated_total_val > (1 << 52))
+ IF (out_accumulated_total.val > (1 << 52))
THEN
RAISE EXCEPTION 'accumulation overflow';
END IF;
@@ -106,22 +93,21 @@ BEGIN
-- Set the fulfillment_state according to the values.
-- For now, we only update the state when it was INSUFFICIENT.
- -- FIXME: What to do in case of Failure or other state?
- IF (out_fullfillment_state = 1) -- INSUFFICIENT
+ -- FIXME[oec] #7999: What to do in case of Failure or other state?
+ IF (out_fullfillment_state = 2) -- INSUFFICIENT
THEN
- IF (out_accumulated_total_val >= cur_commitment_val OR
- (out_accumulated_total_val = cur_commitment_val AND
- out_accumulated_total_frac >= cur_commitment_frac))
+ IF (out_accumulated_total.val >= cur_commitment.val OR
+ (out_accumulated_total.val = cur_commitment.val AND
+ out_accumulated_total.frac >= cur_commitment.frac))
THEN
- out_fulfillment_state = 2; -- READY
+ out_fulfillment_state = 3; -- READY
END IF;
END IF;
-- Now, update the record
UPDATE exchange.policy_details
SET
- accumulated_val = out_accumulated_total_val,
- accumulated_frac = out_accumulated_total_frac,
+ accumulated = out_accumulated_total,
fulfillment_state = out_fulfillment_state
WHERE
policy_details_serial_id = out_policy_details_serial_id;
diff --git a/src/exchangedb/exchange_do_melt.sql b/src/exchangedb/exchange_do_melt.sql
index c0290b561..0200986fa 100644
--- a/src/exchangedb/exchange_do_melt.sql
+++ b/src/exchangedb/exchange_do_melt.sql
@@ -19,8 +19,7 @@
CREATE OR REPLACE FUNCTION exchange_do_melt(
IN in_cs_rms BYTEA,
- IN in_amount_with_fee_val INT8,
- IN in_amount_with_fee_frac INT4,
+ IN in_amount_with_fee taler_amount,
IN in_rc BYTEA,
IN in_old_coin_pub BYTEA,
IN in_old_coin_sig BYTEA,
@@ -45,16 +44,14 @@ INSERT INTO exchange.refresh_commitments
(rc
,old_coin_pub
,old_coin_sig
- ,amount_with_fee_val
- ,amount_with_fee_frac
+ ,amount_with_fee
,noreveal_index
)
VALUES
(in_rc
,in_old_coin_pub
,in_old_coin_sig
- ,in_amount_with_fee_val
- ,in_amount_with_fee_frac
+ ,in_amount_with_fee
,in_noreveal_index)
ON CONFLICT DO NOTHING;
@@ -84,13 +81,13 @@ THEN
-- operations, and then see if any of these
-- reveal operations was involved in a recoup.
PERFORM
- FROM exchange.recoup_refresh
+ FROM recoup_refresh
WHERE rrc_serial IN
(SELECT rrc_serial
- FROM exchange.refresh_revealed_coins
+ FROM refresh_revealed_coins
WHERE melt_serial_id IN
(SELECT melt_serial_id
- FROM exchange.refresh_commitments
+ FROM refresh_commitments
WHERE old_coin_pub=in_old_coin_pub));
IF NOT FOUND
THEN
@@ -104,24 +101,24 @@ out_zombie_bad=FALSE; -- zombie is OK
-- Check and update balance of the coin.
-UPDATE known_coins
+UPDATE known_coins kc
SET
- remaining_frac=remaining_frac-in_amount_with_fee_frac
+ remaining.frac=(kc.remaining).frac-in_amount_with_fee.frac
+ CASE
- WHEN remaining_frac < in_amount_with_fee_frac
+ WHEN (kc.remaining).frac < in_amount_with_fee.frac
THEN 100000000
ELSE 0
END,
- remaining_val=remaining_val-in_amount_with_fee_val
+ remaining.val=(kc.remaining).val-in_amount_with_fee.val
- CASE
- WHEN remaining_frac < in_amount_with_fee_frac
+ WHEN (kc.remaining).frac < in_amount_with_fee.frac
THEN 1
ELSE 0
END
WHERE coin_pub=in_old_coin_pub
- AND ( (remaining_val > in_amount_with_fee_val) OR
- ( (remaining_frac >= in_amount_with_fee_frac) AND
- (remaining_val >= in_amount_with_fee_val) ) );
+ AND ( ((kc.remaining).val > in_amount_with_fee.val) OR
+ ( ((kc.remaining).frac >= in_amount_with_fee.frac) AND
+ ((kc.remaining).val >= in_amount_with_fee.val) ) );
IF NOT FOUND
THEN
@@ -183,4 +180,3 @@ out_balance_ok=TRUE;
out_noreveal_index=in_noreveal_index;
END $$;
-
diff --git a/src/exchangedb/exchange_do_purse_delete.sql b/src/exchangedb/exchange_do_purse_delete.sql
new file mode 100644
index 000000000..5668f7bec
--- /dev/null
+++ b/src/exchangedb/exchange_do_purse_delete.sql
@@ -0,0 +1,118 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--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/>
+--
+
+CREATE OR REPLACE FUNCTION exchange_do_purse_delete(
+ IN in_purse_pub BYTEA,
+ IN in_purse_sig BYTEA,
+ IN in_now INT8,
+ OUT out_decided BOOLEAN,
+ OUT out_found BOOLEAN)
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ my_deposit record;
+DECLARE
+ my_in_reserve_quota BOOLEAN;
+BEGIN
+
+PERFORM refunded FROM purse_decision
+ WHERE purse_pub=in_purse_pub;
+IF FOUND
+THEN
+ out_found=TRUE;
+ out_decided=TRUE;
+ RETURN;
+END IF;
+out_decided=FALSE;
+
+SELECT in_reserve_quota
+ INTO my_in_reserve_quota
+ FROM exchange.purse_requests
+ WHERE purse_pub=in_purse_pub;
+out_found=FOUND;
+IF NOT FOUND
+THEN
+ RETURN;
+END IF;
+
+-- store reserve deletion
+INSERT INTO exchange.purse_deletion
+ (purse_pub
+ ,purse_sig)
+VALUES
+ (in_purse_pub
+ ,in_purse_sig)
+ON CONFLICT DO NOTHING;
+
+IF NOT FOUND
+THEN
+ RETURN;
+END IF;
+
+-- Delete contract associated with purse, if it exists.
+DELETE FROM contracts
+ WHERE purse_pub=in_purse_pub;
+
+-- store purse decision
+INSERT INTO purse_decision
+ (purse_pub
+ ,action_timestamp
+ ,refunded)
+VALUES
+ (in_purse_pub
+ ,in_now
+ ,TRUE);
+
+-- update purse quota at reserve
+IF (my_in_reserve_quota)
+THEN
+ UPDATE reserves
+ SET purses_active=purses_active-1
+ WHERE reserve_pub IN
+ (SELECT reserve_pub
+ FROM exchange.purse_merges
+ WHERE purse_pub=in_purse_pub
+ LIMIT 1);
+END IF;
+
+-- restore balance to each coin deposited into the purse
+FOR my_deposit IN
+ SELECT coin_pub
+ ,amount_with_fee
+ FROM exchange.purse_deposits
+ WHERE purse_pub = in_purse_pub
+LOOP
+ UPDATE known_coins kc SET
+ remaining.frac=(kc.remaining).frac+(my_deposit.amount_with_fee).frac
+ - CASE
+ WHEN (kc.remaining).frac+(my_deposit.amount_with_fee).frac >= 100000000
+ THEN 100000000
+ ELSE 0
+ END,
+ remaining.val=(kc.remaining).val+(my_deposit.amount_with_fee).val
+ + CASE
+ WHEN (kc.remaining).frac+(my_deposit.amount_with_fee).frac >= 100000000
+ THEN 1
+ ELSE 0
+ END
+ WHERE coin_pub = my_deposit.coin_pub;
+END LOOP;
+
+
+END $$;
+
+COMMENT ON FUNCTION exchange_do_purse_delete(BYTEA,BYTEA,INT8)
+ IS 'Delete a previously undecided purse and refund the coins (if any).';
diff --git a/src/exchangedb/exchange_do_purse_deposit.sql b/src/exchangedb/exchange_do_purse_deposit.sql
index cddbd8d46..49d3c919b 100644
--- a/src/exchangedb/exchange_do_purse_deposit.sql
+++ b/src/exchangedb/exchange_do_purse_deposit.sql
@@ -17,15 +17,14 @@
CREATE OR REPLACE FUNCTION exchange_do_purse_deposit(
IN in_partner_id INT8,
IN in_purse_pub BYTEA,
- IN in_amount_with_fee_val INT8,
- IN in_amount_with_fee_frac INT4,
+ IN in_amount_with_fee taler_amount,
IN in_coin_pub BYTEA,
IN in_coin_sig BYTEA,
- IN in_amount_without_fee_val INT8,
- IN in_amount_without_fee_frac INT4,
+ IN in_amount_without_fee taler_amount,
IN in_reserve_expiration INT8,
IN in_now INT8,
OUT out_balance_ok BOOLEAN,
+ OUT out_late BOOLEAN,
OUT out_conflict BOOLEAN)
LANGUAGE plpgsql
AS $$
@@ -34,31 +33,29 @@ DECLARE
DECLARE
psi INT8; -- partner's serial ID (set if merged)
DECLARE
- my_amount_val INT8; -- total in purse
-DECLARE
- my_amount_frac INT4; -- total in purse
+ my_amount taler_amount; -- total in purse
DECLARE
was_paid BOOLEAN;
DECLARE
my_in_reserve_quota BOOLEAN;
DECLARE
my_reserve_pub BYTEA;
+DECLARE
+ rval RECORD;
BEGIN
-- Store the deposit request.
-INSERT INTO exchange.purse_deposits
+INSERT INTO purse_deposits
(partner_serial_id
,purse_pub
,coin_pub
- ,amount_with_fee_val
- ,amount_with_fee_frac
+ ,amount_with_fee
,coin_sig)
VALUES
(in_partner_id
,in_purse_pub
,in_coin_pub
- ,in_amount_with_fee_val
- ,in_amount_with_fee_frac
+ ,in_amount_with_fee
,in_coin_sig)
ON CONFLICT DO NOTHING;
@@ -66,63 +63,85 @@ IF NOT FOUND
THEN
-- Idempotency check: check if coin_sig is the same,
-- if so, success, otherwise conflict!
+
PERFORM
- FROM exchange.purse_deposits
- WHERE coin_pub = in_coin_pub
- AND purse_pub = in_purse_pub
- AND coin_sig = in_cion_sig;
+ FROM purse_deposits
+ WHERE purse_pub = in_purse_pub
+ AND coin_pub = in_coin_pub
+ AND coin_sig = in_coin_sig;
IF NOT FOUND
THEN
-- Deposit exists, but with differences. Not allowed.
out_balance_ok=FALSE;
+ out_late=FALSE;
out_conflict=TRUE;
RETURN;
+ ELSE
+ -- Deposit exists, do not count for balance. Allow.
+ out_late=FALSE;
+ out_balance_ok=TRUE;
+ out_conflict=FALSE;
+ RETURN;
END IF;
END IF;
+-- Check if purse was deleted, if so, abort and prevent deposit.
+PERFORM
+ FROM exchange.purse_deletion
+ WHERE purse_pub = in_purse_pub;
+IF FOUND
+THEN
+ out_late=TRUE;
+ out_balance_ok=FALSE;
+ out_conflict=FALSE;
+ RETURN;
+END IF;
+
+
-- Debit the coin
-- Check and update balance of the coin.
-UPDATE known_coins
+UPDATE known_coins kc
SET
- remaining_frac=remaining_frac-in_amount_with_fee_frac
+ remaining.frac=(kc.remaining).frac-in_amount_with_fee.frac
+ CASE
- WHEN remaining_frac < in_amount_with_fee_frac
+ WHEN (kc.remaining).frac < in_amount_with_fee.frac
THEN 100000000
ELSE 0
END,
- remaining_val=remaining_val-in_amount_with_fee_val
+ remaining.val=(kc.remaining).val-in_amount_with_fee.val
- CASE
- WHEN remaining_frac < in_amount_with_fee_frac
+ WHEN (kc.remaining).frac < in_amount_with_fee.frac
THEN 1
ELSE 0
END
WHERE coin_pub=in_coin_pub
- AND ( (remaining_val > in_amount_with_fee_val) OR
- ( (remaining_frac >= in_amount_with_fee_frac) AND
- (remaining_val >= in_amount_with_fee_val) ) );
+ AND ( ((kc.remaining).val > in_amount_with_fee.val) OR
+ ( ((kc.remaining).frac >= in_amount_with_fee.frac) AND
+ ((kc.remaining).val >= in_amount_with_fee.val) ) );
IF NOT FOUND
THEN
-- Insufficient balance.
out_balance_ok=FALSE;
+ out_late=FALSE;
out_conflict=FALSE;
RETURN;
END IF;
-- Credit the purse.
-UPDATE purse_requests
+UPDATE purse_requests pr
SET
- balance_frac=balance_frac+in_amount_without_fee_frac
+ balance.frac=(pr.balance).frac+in_amount_without_fee.frac
- CASE
- WHEN balance_frac+in_amount_without_fee_frac >= 100000000
+ WHEN (pr.balance).frac+in_amount_without_fee.frac >= 100000000
THEN 100000000
ELSE 0
END,
- balance_val=balance_val+in_amount_without_fee_val
+ balance.val=(pr.balance).val+in_amount_without_fee.val
+ CASE
- WHEN balance_frac+in_amount_without_fee_frac >= 100000000
+ WHEN (pr.balance).frac+in_amount_without_fee.frac >= 100000000
THEN 1
ELSE 0
END
@@ -136,32 +155,37 @@ SELECT COALESCE(partner_serial_id,0)
,reserve_pub
INTO psi
,my_reserve_pub
- FROM exchange.purse_merges
+ FROM purse_merges
WHERE purse_pub=in_purse_pub;
IF NOT FOUND
THEN
+ -- Purse was not yet merged. We are done.
+ out_late=FALSE;
RETURN;
END IF;
SELECT
- amount_with_fee_val
- ,amount_with_fee_frac
+ amount_with_fee
,in_reserve_quota
INTO
- my_amount_val
- ,my_amount_frac
- ,my_in_reserve_quota
- FROM exchange.purse_requests
+ rval
+ FROM exchange.purse_requests preq
WHERE (purse_pub=in_purse_pub)
- AND ( ( ( (amount_with_fee_val <= balance_val)
- AND (amount_with_fee_frac <= balance_frac) )
- OR (amount_with_fee_val < balance_val) ) );
+ AND ( ( ( ((preq.amount_with_fee).val <= (preq.balance).val)
+ AND ((preq.amount_with_fee).frac <= (preq.balance).frac) )
+ OR ((preq.amount_with_fee).val < (preq.balance).val) ) );
IF NOT FOUND
THEN
+ out_late=FALSE;
RETURN;
END IF;
+-- We use rval as workaround as we cannot select
+-- directly into the amount due to Postgres limitations.
+my_amount := rval.amount_with_fee;
+my_in_reserve_quota := rval.in_reserve_quota;
+
-- Remember how this purse was finished.
INSERT INTO purse_decision
(purse_pub
@@ -175,17 +199,20 @@ ON CONFLICT DO NOTHING;
IF NOT FOUND
THEN
- out_conflict=TRUE;
+ -- Purse already decided, likely expired.
+ out_late=TRUE;
RETURN;
END IF;
+out_late=FALSE;
+
IF (my_in_reserve_quota)
THEN
UPDATE reserves
SET purses_active=purses_active-1
WHERE reserve_pub IN
(SELECT reserve_pub
- FROM exchange.purse_merges
+ FROM purse_merges
WHERE purse_pub=my_purse_pub
LIMIT 1);
END IF;
@@ -202,32 +229,30 @@ ELSE
-- This is a local reserve, update balance immediately.
INSERT INTO reserves
(reserve_pub
- ,current_balance_frac
- ,current_balance_val
+ ,current_balance
,expiration_date
,gc_date)
VALUES
(my_reserve_pub
- ,my_amount_frac
- ,my_amount_val
+ ,my_amount
,in_reserve_expiration
,in_reserve_expiration)
ON CONFLICT DO NOTHING;
IF NOT FOUND
THEN
-
+ -- Reserve existed, thus UPDATE instead of INSERT.
UPDATE reserves
SET
- current_balance_frac=current_balance_frac+my_amount_frac
+ current_balance.frac=(current_balance).frac+my_amount.frac
- CASE
- WHEN current_balance_frac + my_amount_frac >= 100000000
+ WHEN (current_balance).frac + my_amount.frac >= 100000000
THEN 100000000
ELSE 0
END
- ,current_balance_val=current_balance_val+my_amount_val
+ ,current_balance.val=(current_balance).val+my_amount.val
+ CASE
- WHEN current_balance_frac + my_amount_frac >= 100000000
+ WHEN (current_balance).frac + my_amount.frac >= 100000000
THEN 1
ELSE 0
END
@@ -240,5 +265,3 @@ END IF;
END $$;
-
-
diff --git a/src/exchangedb/exchange_do_purse_merge.sql b/src/exchangedb/exchange_do_purse_merge.sql
index 2e799c9f2..946fd7e97 100644
--- a/src/exchangedb/exchange_do_purse_merge.sql
+++ b/src/exchangedb/exchange_do_purse_merge.sql
@@ -19,7 +19,7 @@ CREATE OR REPLACE FUNCTION exchange_do_purse_merge(
IN in_merge_sig BYTEA,
IN in_merge_timestamp INT8,
IN in_reserve_sig BYTEA,
- IN in_partner_url VARCHAR,
+ IN in_partner_url TEXT,
IN in_reserve_pub BYTEA,
IN in_wallet_h_payto BYTEA,
IN in_expiration_date INT8,
@@ -29,19 +29,33 @@ CREATE OR REPLACE FUNCTION exchange_do_purse_merge(
LANGUAGE plpgsql
AS $$
DECLARE
- my_amount_val INT8;
+ my_amount taler_amount;
DECLARE
- my_amount_frac INT4;
-DECLARE
- my_purse_fee_val INT8;
-DECLARE
- my_purse_fee_frac INT4;
+ my_purse_fee taler_amount;
DECLARE
my_partner_serial_id INT8;
DECLARE
my_in_reserve_quota BOOLEAN;
+DECLARE
+ rval RECORD;
+DECLARE
+ reserve RECORD;
+DECLARE
+ balance taler_amount;
BEGIN
+-- Initialize reserve, if not yet exists.
+INSERT INTO reserves
+ (reserve_pub
+ ,expiration_date
+ ,gc_date)
+ VALUES
+ (in_reserve_pub
+ ,in_expiration_date
+ ,in_expiration_date)
+ ON CONFLICT DO NOTHING;
+
+
IF in_partner_url IS NULL
THEN
my_partner_serial_id=NULL;
@@ -64,33 +78,33 @@ END IF;
out_no_partner=FALSE;
-
-- Check purse is 'full'.
-SELECT amount_with_fee_val
- ,amount_with_fee_frac
- ,purse_fee_val
- ,purse_fee_frac
+SELECT amount_with_fee
+ ,purse_fee
,in_reserve_quota
- INTO my_amount_val
- ,my_amount_frac
- ,my_purse_fee_val
- ,my_purse_fee_frac
- ,my_in_reserve_quota
- FROM exchange.purse_requests
+ INTO rval
+ FROM purse_requests pr
WHERE purse_pub=in_purse_pub
- AND balance_val >= amount_with_fee_val
- AND ( (balance_frac >= amount_with_fee_frac) OR
- (balance_val > amount_with_fee_val) );
+ AND (pr.balance).val >= (pr.amount_with_fee).val
+ AND ( (pr.balance).frac >= (pr.amount_with_fee).frac OR
+ (pr.balance).val > (pr.amount_with_fee).val );
IF NOT FOUND
THEN
out_no_balance=TRUE;
out_conflict=FALSE;
RETURN;
END IF;
+
+-- We use rval as workaround as we cannot select
+-- directly into the amount due to Postgres limitations.
+my_amount := rval.amount_with_fee;
+my_purse_fee := rval.purse_fee;
+my_in_reserve_quota := rval.in_reserve_quota;
+
out_no_balance=FALSE;
-- Store purse merge signature, checks for purse_pub uniqueness
-INSERT INTO exchange.purse_merges
+INSERT INTO purse_merges
(partner_serial_id
,reserve_pub
,purse_pub
@@ -110,7 +124,7 @@ THEN
-- Note that by checking 'merge_sig', we implicitly check
-- identity over everything that the signature covers.
PERFORM
- FROM exchange.purse_merges
+ FROM purse_merges
WHERE purse_pub=in_purse_pub
AND merge_sig=in_merge_sig;
IF NOT FOUND
@@ -124,21 +138,10 @@ THEN
out_conflict=FALSE;
RETURN;
END IF;
-out_conflict=FALSE;
--- Initialize reserve, if not yet exists.
-INSERT INTO reserves
- (reserve_pub
- ,expiration_date
- ,gc_date)
- VALUES
- (in_reserve_pub
- ,in_expiration_date
- ,in_expiration_date)
- ON CONFLICT DO NOTHING;
-
--- Remember how this purse was finished.
+-- Remember how this purse was finished. This will conflict
+-- if the purse was already decided previously.
INSERT INTO purse_decision
(purse_pub
,action_timestamp
@@ -146,7 +149,19 @@ INSERT INTO purse_decision
VALUES
(in_purse_pub
,in_merge_timestamp
- ,FALSE);
+ ,FALSE)
+ON CONFLICT DO NOTHING;
+
+IF NOT FOUND
+THEN
+ -- Purse was already decided (possibly deleted or merged differently).
+ out_conflict=TRUE;
+ RETURN;
+END IF;
+
+out_conflict=FALSE;
+
+
IF (my_in_reserve_quota)
THEN
@@ -154,13 +169,13 @@ THEN
SET purses_active=purses_active-1
WHERE reserve_pub IN
(SELECT reserve_pub
- FROM exchange.purse_merges
+ FROM purse_merges
WHERE purse_pub=my_purse_pub
LIMIT 1);
END IF;
-- Store account merge signature.
-INSERT INTO exchange.account_merges
+INSERT INTO account_merges
(reserve_pub
,reserve_sig
,purse_pub
@@ -183,26 +198,33 @@ ELSE
-- This is a local reserve, update reserve balance immediately.
-- Refund the purse fee, by adding it to the purse value:
- my_amount_val = my_amount_val + my_purse_fee_val;
- my_amount_frac = my_amount_frac + my_purse_fee_frac;
+ my_amount.val = my_amount.val + my_purse_fee.val;
+ my_amount.frac = my_amount.frac + my_purse_fee.frac;
-- normalize result
- my_amount_val = my_amount_val + my_amount_frac / 100000000;
- my_amount_frac = my_amount_frac % 100000000;
+ my_amount.val = my_amount.val + my_amount.frac / 100000000;
+ my_amount.frac = my_amount.frac % 100000000;
+
+ SELECT *
+ INTO reserve
+ FROM exchange.reserves
+ WHERE reserve_pub=in_reserve_pub;
+
+ balance = reserve.current_balance;
+ balance.frac=balance.frac+my_amount.frac
+ - CASE
+ WHEN balance.frac + my_amount.frac >= 100000000
+ THEN 100000000
+ ELSE 0
+ END;
+ balance.val=balance.val+my_amount.val
+ + CASE
+ WHEN balance.frac + my_amount.frac >= 100000000
+ THEN 1
+ ELSE 0
+ END;
UPDATE exchange.reserves
- SET
- current_balance_frac=current_balance_frac+my_amount_frac
- - CASE
- WHEN current_balance_frac + my_amount_frac >= 100000000
- THEN 100000000
- ELSE 0
- END,
- current_balance_val=current_balance_val+my_amount_val
- + CASE
- WHEN current_balance_frac + my_amount_frac >= 100000000
- THEN 1
- ELSE 0
- END
+ SET current_balance=balance
WHERE reserve_pub=in_reserve_pub;
END IF;
@@ -211,6 +233,5 @@ RETURN;
END $$;
-COMMENT ON FUNCTION exchange_do_purse_merge(BYTEA, BYTEA, INT8, BYTEA, VARCHAR, BYTEA, BYTEA, INT8)
+COMMENT ON FUNCTION exchange_do_purse_merge(BYTEA, BYTEA, INT8, BYTEA, TEXT, BYTEA, BYTEA, INT8)
IS 'Checks that the partner exists, the purse has not been merged with a different reserve and that the purse is full. If so, persists the merge data and either merges the purse with the reserve or marks it as ready for the taler-exchange-router. Caller MUST abort the transaction on failures so as to not persist data by accident.';
-
diff --git a/src/exchangedb/exchange_do_recoup_by_reserve.sql b/src/exchangedb/exchange_do_recoup_by_reserve.sql
index 6a7ea725e..80f953c4a 100644
--- a/src/exchangedb/exchange_do_recoup_by_reserve.sql
+++ b/src/exchangedb/exchange_do_recoup_by_reserve.sql
@@ -25,8 +25,7 @@ RETURNS TABLE
coin_pub BYTEA,
coin_sig BYTEA,
coin_blind BYTEA,
- amount_val BIGINT,
- amount_frac INTEGER,
+ amount taler_amount,
recoup_timestamp BIGINT
)
LANGUAGE plpgsql
@@ -37,22 +36,25 @@ DECLARE
c_pub BYTEA;
BEGIN
SELECT reserve_uuid
- INTO res_uuid
- FROM exchange.reserves
- WHERE reserves.reserve_pub = res_pub;
+ INTO res_uuid
+ FROM reserves
+ WHERE reserve_pub = res_pub;
FOR blind_ev IN
SELECT h_blind_ev
- FROM exchange.reserves_out_by_reserve
- WHERE reserves_out_by_reserve.reserve_uuid = res_uuid
+ FROM reserves_out ro
+ JOIN reserve_history rh
+ ON (rh.serial_id = ro.reserve_out_serial_id)
+ WHERE rh.reserve_pub = res_pub
+ AND rh.table_name='reserves_out'
LOOP
SELECT robr.coin_pub
INTO c_pub
FROM exchange.recoup_by_reserve robr
WHERE robr.reserve_out_serial_id = (
- SELECT reserves_out.reserve_out_serial_id
- FROM exchange.reserves_out
- WHERE reserves_out.h_blind_ev = blind_ev
+ SELECT reserve_out_serial_id
+ FROM reserves_out
+ WHERE h_blind_ev = blind_ev
);
RETURN QUERY
SELECT kc.denom_sig,
@@ -60,16 +62,20 @@ BEGIN
rc.coin_pub,
rc.coin_sig,
rc.coin_blind,
- rc.amount_val,
- rc.amount_frac,
+ rc.amount,
rc.recoup_timestamp
FROM (
- SELECT *
+ SELECT denom_sig
+ ,denominations_serial
FROM exchange.known_coins
WHERE known_coins.coin_pub = c_pub
) kc
JOIN (
- SELECT *
+ SELECT coin_pub
+ ,coin_sig
+ ,coin_blind
+ ,amount
+ ,recoup_timestamp
FROM exchange.recoup
WHERE recoup.coin_pub = c_pub
) rc USING (coin_pub);
@@ -79,4 +85,3 @@ $$;
COMMENT ON FUNCTION exchange_do_recoup_by_reserve
IS 'Recoup by reserve as a function to make sure we hit only the needed partition and not all when joining as joins on distributed tables fetch ALL rows from the shards';
-
diff --git a/src/exchangedb/exchange_do_recoup_to_coin.sql b/src/exchangedb/exchange_do_recoup_to_coin.sql
index 5598ec20c..6cecfb7f8 100644
--- a/src/exchangedb/exchange_do_recoup_to_coin.sql
+++ b/src/exchangedb/exchange_do_recoup_to_coin.sql
@@ -31,9 +31,9 @@ CREATE OR REPLACE FUNCTION exchange_do_recoup_to_coin(
LANGUAGE plpgsql
AS $$
DECLARE
- tmp_val INT8; -- amount recouped
+ rval RECORD;
DECLARE
- tmp_frac INT8; -- amount recouped
+ tmp taler_amount; -- amount recouped
BEGIN
-- Shards: UPDATE known_coins (by coin_pub)
@@ -41,17 +41,13 @@ BEGIN
-- UPDATE known_coins (by coin_pub)
-- INSERT recoup_refresh (by coin_pub)
-
out_internal_failure=FALSE;
-
-- Check remaining balance of the coin.
SELECT
- remaining_frac
- ,remaining_val
+ remaining
INTO
- tmp_frac
- ,tmp_val
+ rval
FROM exchange.known_coins
WHERE coin_pub=in_coin_pub;
@@ -62,14 +58,16 @@ THEN
RETURN;
END IF;
-IF tmp_val + tmp_frac = 0
+tmp := rval.remaining;
+
+IF tmp.val + tmp.frac = 0
THEN
-- Check for idempotency
SELECT
recoup_timestamp
INTO
out_recoup_timestamp
- FROM exchange.recoup_refresh
+ FROM recoup_refresh
WHERE coin_pub=in_coin_pub;
out_recoup_ok=FOUND;
RETURN;
@@ -78,29 +76,27 @@ END IF;
-- Update balance of the coin.
UPDATE known_coins
SET
- remaining_frac=0
- ,remaining_val=0
+ remaining.val = 0
+ ,remaining.frac = 0
WHERE coin_pub=in_coin_pub;
-
-- Credit the old coin.
-UPDATE known_coins
+UPDATE known_coins kc
SET
- remaining_frac=remaining_frac+tmp_frac
+ remaining.frac=(kc.remaining).frac+tmp.frac
- CASE
- WHEN remaining_frac+tmp_frac >= 100000000
+ WHEN (kc.remaining).frac+tmp.frac >= 100000000
THEN 100000000
ELSE 0
END,
- remaining_val=remaining_val+tmp_val
+ remaining.val=(kc.remaining).val+tmp.val
+ CASE
- WHEN remaining_frac+tmp_frac >= 100000000
+ WHEN (kc.remaining).frac+tmp.frac >= 100000000
THEN 1
ELSE 0
END
WHERE coin_pub=in_old_coin_pub;
-
IF NOT FOUND
THEN
RAISE NOTICE 'failed to increase old coin balance from recoup';
@@ -110,13 +106,12 @@ THEN
END IF;
-INSERT INTO exchange.recoup_refresh
+INSERT INTO recoup_refresh
(coin_pub
,known_coin_id
,coin_sig
,coin_blind
- ,amount_val
- ,amount_frac
+ ,amount
,recoup_timestamp
,rrc_serial
)
@@ -125,8 +120,7 @@ VALUES
,in_known_coin_id
,in_coin_sig
,in_coin_blind
- ,tmp_val
- ,tmp_frac
+ ,tmp
,in_recoup_timestamp
,in_rrc_serial);
@@ -139,4 +133,3 @@ END $$;
-- COMMENT ON FUNCTION exchange_do_recoup_to_coin(INT8, INT4, BYTEA, BOOLEAN, BOOLEAN)
-- IS 'Executes a recoup-refresh of a coin that was obtained from a refresh-reveal process';
-
diff --git a/src/exchangedb/exchange_do_recoup_to_reserve.sql b/src/exchangedb/exchange_do_recoup_to_reserve.sql
index 39baba8fa..10ae063bd 100644
--- a/src/exchangedb/exchange_do_recoup_to_reserve.sql
+++ b/src/exchangedb/exchange_do_recoup_to_reserve.sql
@@ -31,9 +31,11 @@ CREATE OR REPLACE FUNCTION exchange_do_recoup_to_reserve(
LANGUAGE plpgsql
AS $$
DECLARE
- tmp_val INT8; -- amount recouped
-DECLARE
- tmp_frac INT8; -- amount recouped
+ tmp taler_amount; -- amount recouped
+ balance taler_amount; -- current balance of the reserve
+ new_balance taler_amount; -- new balance of the reserve
+ reserve RECORD;
+ rval RECORD;
BEGIN
-- Shards: SELECT known_coins (by coin_pub)
-- SELECT recoup (by coin_pub)
@@ -46,11 +48,9 @@ out_internal_failure=FALSE;
-- Check remaining balance of the coin.
SELECT
- remaining_frac
- ,remaining_val
+ remaining
INTO
- tmp_frac
- ,tmp_val
+ rval
FROM exchange.known_coins
WHERE coin_pub=in_coin_pub;
@@ -61,7 +61,9 @@ THEN
RETURN;
END IF;
-IF tmp_val + tmp_frac = 0
+tmp := rval.remaining;
+
+IF tmp.val + tmp.frac = 0
THEN
-- Check for idempotency
SELECT
@@ -79,26 +81,35 @@ END IF;
-- Update balance of the coin.
UPDATE known_coins
SET
- remaining_frac=0
- ,remaining_val=0
+ remaining.val = 0
+ ,remaining.frac = 0
WHERE coin_pub=in_coin_pub;
+-- Get current balance
+SELECT current_balance
+ INTO reserve
+ FROM reserves
+ WHERE reserve_pub=in_reserve_pub;
+
+balance = reserve.current_balance;
+new_balance.frac=balance.frac+tmp.frac
+ - CASE
+ WHEN balance.frac+tmp.frac >= 100000000
+ THEN 100000000
+ ELSE 0
+ END;
+
+new_balance.val=balance.val+tmp.val
+ + CASE
+ WHEN balance.frac+tmp.frac >= 100000000
+ THEN 1
+ ELSE 0
+ END;
-- Credit the reserve and update reserve timers.
UPDATE reserves
SET
- current_balance_frac=current_balance_frac+tmp_frac
- - CASE
- WHEN current_balance_frac+tmp_frac >= 100000000
- THEN 100000000
- ELSE 0
- END,
- current_balance_val=current_balance_val+tmp_val
- + CASE
- WHEN current_balance_frac+tmp_frac >= 100000000
- THEN 1
- ELSE 0
- END,
+ current_balance = new_balance,
gc_date=GREATEST(gc_date, in_reserve_gc),
expiration_date=GREATEST(expiration_date, in_reserve_expiration)
WHERE reserve_pub=in_reserve_pub;
@@ -117,8 +128,7 @@ INSERT INTO exchange.recoup
(coin_pub
,coin_sig
,coin_blind
- ,amount_val
- ,amount_frac
+ ,amount
,recoup_timestamp
,reserve_out_serial_id
)
@@ -126,8 +136,7 @@ VALUES
(in_coin_pub
,in_coin_sig
,in_coin_blind
- ,tmp_val
- ,tmp_frac
+ ,tmp
,in_recoup_timestamp
,in_reserve_out_serial_id);
@@ -139,6 +148,3 @@ END $$;
-- COMMENT ON FUNCTION exchange_do_recoup_to_reserve(INT8, INT4, BYTEA, BOOLEAN, BOOLEAN)
-- IS 'Executes a recoup of a coin that was withdrawn from a reserve';
-
-
-
diff --git a/src/exchangedb/exchange_do_refund.sql b/src/exchangedb/exchange_do_refund.sql
index ceaabfe16..a95746127 100644
--- a/src/exchangedb/exchange_do_refund.sql
+++ b/src/exchangedb/exchange_do_refund.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
+-- Copyright (C) 2014--2023 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
@@ -15,12 +15,9 @@
--
CREATE OR REPLACE FUNCTION exchange_do_refund(
- IN in_amount_with_fee_val INT8,
- IN in_amount_with_fee_frac INT4,
- IN in_amount_val INT8,
- IN in_amount_frac INT4,
- IN in_deposit_fee_val INT8,
- IN in_deposit_fee_frac INT4,
+ IN in_amount_with_fee taler_amount,
+ IN in_amount taler_amount,
+ IN in_deposit_fee taler_amount,
IN in_h_contract_terms BYTEA,
IN in_rtransaction_id INT8,
IN in_deposit_shard INT8,
@@ -35,15 +32,15 @@ CREATE OR REPLACE FUNCTION exchange_do_refund(
LANGUAGE plpgsql
AS $$
DECLARE
- dsi INT8; -- ID of deposit being refunded
+ bdsi INT8; -- ID of deposit being refunded
DECLARE
tmp_val INT8; -- total amount refunded
DECLARE
- tmp_frac INT8; -- total amount refunded
+ tmp_frac INT8; -- total amount refunded, large fraction to deal with overflows!
DECLARE
- deposit_val INT8; -- amount that was originally deposited
+ tmp taler_amount; -- total amount refunded, normalized
DECLARE
- deposit_frac INT8; -- amount that was originally deposited
+ deposit taler_amount; -- amount that was originally deposited
BEGIN
-- Shards: SELECT deposits (coin_pub, shard, h_contract_terms, merchant_pub)
-- INSERT refunds (by coin_pub, rtransaction_id) ON CONFLICT DO NOTHING
@@ -51,17 +48,19 @@ BEGIN
-- UPDATE known_coins (by coin_pub)
SELECT
- deposit_serial_id
- ,amount_with_fee_val
- ,amount_with_fee_frac
- ,done
-INTO
- dsi
- ,deposit_val
- ,deposit_frac
+ bdep.batch_deposit_serial_id
+ ,(cdep.amount_with_fee).val
+ ,(cdep.amount_with_fee).frac
+ ,bdep.done
+ INTO
+ bdsi
+ ,deposit.val
+ ,deposit.frac
,out_gone
-FROM exchange.deposits
- WHERE coin_pub=in_coin_pub
+ FROM batch_deposits bdep
+ JOIN coin_deposits cdep
+ USING (batch_deposit_serial_id)
+ WHERE cdep.coin_pub=in_coin_pub
AND shard=in_deposit_shard
AND merchant_pub=in_merchant_pub
AND h_contract_terms=in_h_contract_terms;
@@ -76,21 +75,20 @@ THEN
RETURN;
END IF;
-INSERT INTO exchange.refunds
- (deposit_serial_id
+INSERT INTO refunds
+ (batch_deposit_serial_id
,coin_pub
,merchant_sig
,rtransaction_id
- ,amount_with_fee_val
- ,amount_with_fee_frac
+ ,amount_with_fee
)
VALUES
- (dsi
+ (bdsi
,in_coin_pub
,in_merchant_sig
,in_rtransaction_id
- ,in_amount_with_fee_val
- ,in_amount_with_fee_frac)
+ ,in_amount_with_fee
+ )
ON CONFLICT DO NOTHING;
IF NOT FOUND
@@ -103,10 +101,9 @@ THEN
PERFORM
FROM exchange.refunds
WHERE coin_pub=in_coin_pub
- AND deposit_serial_id=dsi
+ AND batch_deposit_serial_id=bdsi
AND rtransaction_id=in_rtransaction_id
- AND amount_with_fee_val=in_amount_with_fee_val
- AND amount_with_fee_frac=in_amount_with_fee_frac;
+ AND amount_with_fee=in_amount_with_fee;
IF NOT FOUND
THEN
@@ -136,14 +133,14 @@ END IF;
-- Check refund balance invariant.
SELECT
- SUM(amount_with_fee_val) -- overflow here is not plausible
- ,SUM(CAST(amount_with_fee_frac AS INT8)) -- compute using 64 bits
+ SUM((refs.amount_with_fee).val) -- overflow here is not plausible
+ ,SUM(CAST((refs.amount_with_fee).frac AS INT8)) -- compute using 64 bits
INTO
tmp_val
,tmp_frac
- FROM exchange.refunds
+ FROM refunds refs
WHERE coin_pub=in_coin_pub
- AND deposit_serial_id=dsi;
+ AND batch_deposit_serial_id=bdsi;
IF tmp_val IS NULL
THEN
RAISE NOTICE 'failed to sum up existing refunds';
@@ -154,15 +151,15 @@ THEN
END IF;
-- Normalize result before continuing
-tmp_val = tmp_val + tmp_frac / 100000000;
-tmp_frac = tmp_frac % 100000000;
+tmp.val = tmp_val + tmp_frac / 100000000;
+tmp.frac = tmp_frac % 100000000;
-- Actually check if the deposits are sufficient for the refund. Verbosely. ;-)
-IF (tmp_val < deposit_val)
+IF (tmp.val < deposit.val)
THEN
out_refund_ok=TRUE;
ELSE
- IF (tmp_val = deposit_val) AND (tmp_frac <= deposit_frac)
+ IF (tmp.val = deposit.val) AND (tmp.frac <= deposit.frac)
THEN
out_refund_ok=TRUE;
ELSE
@@ -170,42 +167,39 @@ ELSE
END IF;
END IF;
-IF (tmp_val = deposit_val) AND (tmp_frac = deposit_frac)
+IF (tmp.val = deposit.val) AND (tmp.frac = deposit.frac)
THEN
-- Refunds have reached the full value of the original
-- deposit. Also refund the deposit fee.
- in_amount_frac = in_amount_frac + in_deposit_fee_frac;
- in_amount_val = in_amount_val + in_deposit_fee_val;
+ in_amount.frac = in_amount.frac + in_deposit_fee.frac;
+ in_amount.val = in_amount.val + in_deposit_fee.val;
-- Normalize result before continuing
- in_amount_val = in_amount_val + in_amount_frac / 100000000;
- in_amount_frac = in_amount_frac % 100000000;
+ in_amount.val = in_amount.val + in_amount.frac / 100000000;
+ in_amount.frac = in_amount.frac % 100000000;
END IF;
-- Update balance of the coin.
-UPDATE known_coins
+UPDATE known_coins kc
SET
- remaining_frac=remaining_frac+in_amount_frac
+ remaining.frac=(kc.remaining).frac+in_amount.frac
- CASE
- WHEN remaining_frac+in_amount_frac >= 100000000
+ WHEN (kc.remaining).frac+in_amount.frac >= 100000000
THEN 100000000
ELSE 0
END,
- remaining_val=remaining_val+in_amount_val
+ remaining.val=(kc.remaining).val+in_amount.val
+ CASE
- WHEN remaining_frac+in_amount_frac >= 100000000
+ WHEN (kc.remaining).frac+in_amount.frac >= 100000000
THEN 1
ELSE 0
END
WHERE coin_pub=in_coin_pub;
-
out_conflict=FALSE;
out_not_found=FALSE;
END $$;
--- COMMENT ON FUNCTION exchange_do_refund(INT8, INT4, BYTEA, BOOLEAN, BOOLEAN)
--- IS 'Executes a refund operation, checking that the corresponding deposit was sufficient to cover the refunded amount';
-
-
+COMMENT ON FUNCTION exchange_do_refund(taler_amount, taler_amount, taler_amount, BYTEA, INT8, INT8, INT8, BYTEA, BYTEA, BYTEA)
+ IS 'Executes a refund operation, checking that the corresponding deposit was sufficient to cover the refunded amount';
diff --git a/src/exchangedb/exchange_do_reserve_open.sql b/src/exchangedb/exchange_do_reserve_open.sql
index 5e80f713f..dd7a578ee 100644
--- a/src/exchangedb/exchange_do_reserve_open.sql
+++ b/src/exchangedb/exchange_do_reserve_open.sql
@@ -16,80 +16,66 @@
CREATE OR REPLACE FUNCTION exchange_do_reserve_open(
IN in_reserve_pub BYTEA,
- IN in_total_paid_val INT8,
- IN in_total_paid_frac INT4,
- IN in_reserve_payment_val INT8,
- IN in_reserve_payment_frac INT4,
+ IN in_total_paid taler_amount,
+ IN in_reserve_payment taler_amount,
IN in_min_purse_limit INT4,
IN in_default_purse_limit INT4,
IN in_reserve_sig BYTEA,
IN in_desired_expiration INT8,
IN in_reserve_gc_delay INT8,
IN in_now INT8,
- IN in_open_fee_val INT8,
- IN in_open_fee_frac INT4,
- OUT out_open_cost_val INT8,
- OUT out_open_cost_frac INT4,
+ IN in_open_fee taler_amount,
+ OUT out_open_cost taler_amount,
OUT out_final_expiration INT8,
- OUT out_no_funds BOOLEAN)
+ OUT out_no_reserve BOOLEAN,
+ OUT out_no_funds BOOLEAN,
+ OUT out_reserve_balance taler_amount)
LANGUAGE plpgsql
AS $$
DECLARE
- my_balance_val INT8;
-DECLARE
- my_balance_frac INT4;
-DECLARE
- my_cost_val INT8;
-DECLARE
+ my_balance taler_amount;
+ my_cost taler_amount;
my_cost_tmp INT8;
-DECLARE
- my_cost_frac INT4;
-DECLARE
my_years_tmp INT4;
-DECLARE
my_years INT4;
-DECLARE
my_needs_update BOOL;
-DECLARE
- my_purses_allowed INT8;
-DECLARE
my_expiration_date INT8;
-DECLARE
- my_reserve_expiration INT8;
+ reserve RECORD;
BEGIN
--- FIXME: use SELECT FOR UPDATE?
-SELECT
- purses_allowed
- ,expiration_date
- ,current_balance_val
- ,current_balance_frac
-INTO
- my_purses_allowed
- ,my_reserve_expiration
- ,my_balance_val
- ,my_balance_frac
-FROM reserves
-WHERE
- reserve_pub=in_reserve_pub;
+SELECT current_balance
+ ,expiration_date
+ ,purses_allowed
+ INTO reserve
+ FROM reserves
+ WHERE reserve_pub=in_reserve_pub;
IF NOT FOUND
THEN
- -- FIXME: do we need to set a 'not found'?
RAISE NOTICE 'reserve not found';
+ out_no_reserve = TRUE;
+ out_no_funds = TRUE;
+ out_reserve_balance.val = 0;
+ out_reserve_balance.frac = 0;
+ out_open_cost.val = 0;
+ out_open_cost.frac = 0;
+ out_final_expiration = 0;
RETURN;
END IF;
+out_no_reserve = FALSE;
+out_reserve_balance = reserve.current_balance;
+
-- Do not allow expiration time to start in the past already
-IF (my_reserve_expiration < in_now)
+IF (reserve.expiration_date < in_now)
THEN
my_expiration_date = in_now;
ELSE
- my_expiration_date = my_reserve_expiration;
+ my_expiration_date = reserve.expiration_date;
END IF;
-my_cost_val = 0;
-my_cost_frac = 0;
+my_cost.val = 0;
+my_cost.frac = 0;
my_needs_update = FALSE;
my_years = 0;
@@ -97,62 +83,62 @@ my_years = 0;
IF (my_expiration_date < in_desired_expiration)
THEN
my_years = (31535999999999 + in_desired_expiration - my_expiration_date) / 31536000000000;
- my_purses_allowed = in_default_purse_limit;
+ reserve.purses_allowed = in_default_purse_limit;
my_expiration_date = my_expiration_date + 31536000000000 * my_years;
END IF;
-- Increase years based on purses requested
-IF (my_purses_allowed < in_min_purse_limit)
+IF (reserve.purses_allowed < in_min_purse_limit)
THEN
my_years = (31535999999999 + in_desired_expiration - in_now) / 31536000000000;
my_expiration_date = in_now + 31536000000000 * my_years;
- my_years_tmp = (in_min_purse_limit + in_default_purse_limit - my_purses_allowed - 1) / in_default_purse_limit;
+ my_years_tmp = (in_min_purse_limit + in_default_purse_limit - reserve.purses_allowed - 1) / in_default_purse_limit;
my_years = my_years + my_years_tmp;
- my_purses_allowed = my_purses_allowed + (in_default_purse_limit * my_years_tmp);
+ reserve.purses_allowed = reserve.purses_allowed + (in_default_purse_limit * my_years_tmp);
END IF;
-- Compute cost based on annual fees
IF (my_years > 0)
THEN
- my_cost_val = my_years * in_open_fee_val;
- my_cost_tmp = my_years * in_open_fee_frac / 100000000;
- IF (CAST (my_cost_val + my_cost_tmp AS INT8) < my_cost_val)
+ my_cost.val = my_years * in_open_fee.val;
+ my_cost_tmp = my_years * in_open_fee.frac / 100000000;
+ IF (CAST (my_cost.val + my_cost_tmp AS INT8) < my_cost.val)
THEN
- out_open_cost_val=9223372036854775807;
- out_open_cost_frac=2147483647;
+ out_open_cost.val=9223372036854775807;
+ out_open_cost.frac=2147483647;
out_final_expiration=my_expiration_date;
out_no_funds=FALSE;
RAISE NOTICE 'arithmetic issue computing amount';
RETURN;
END IF;
- my_cost_val = CAST (my_cost_val + my_cost_tmp AS INT8);
- my_cost_frac = my_years * in_open_fee_frac % 100000000;
+ my_cost.val = CAST (my_cost.val + my_cost_tmp AS INT8);
+ my_cost.frac = my_years * in_open_fee.frac % 100000000;
my_needs_update = TRUE;
END IF;
-- check if we actually have something to do
IF NOT my_needs_update
THEN
- out_final_expiration = my_reserve_expiration;
- out_open_cost_val = 0;
- out_open_cost_frac = 0;
+ out_final_expiration = reserve.expiration_date;
+ out_open_cost.val = 0;
+ out_open_cost.frac = 0;
out_no_funds=FALSE;
RAISE NOTICE 'no change required';
RETURN;
END IF;
-- Check payment (coins and reserve) would be sufficient.
-IF ( (in_total_paid_val < my_cost_val) OR
- ( (in_total_paid_val = my_cost_val) AND
- (in_total_paid_frac < my_cost_frac) ) )
+IF ( (in_total_paid.val < my_cost.val) OR
+ ( (in_total_paid.val = my_cost.val) AND
+ (in_total_paid.frac < my_cost.frac) ) )
THEN
- out_open_cost_val = my_cost_val;
- out_open_cost_frac = my_cost_frac;
+ out_open_cost.val = my_cost.val;
+ out_open_cost.frac = my_cost.frac;
out_no_funds=FALSE;
-- We must return a failure, which is indicated by
-- the expiration being below the desired expiration.
- IF (my_reserve_expiration >= in_desired_expiration)
+ IF (reserve.expiration_date >= in_desired_expiration)
THEN
-- This case is relevant especially if the purse
-- count was to be increased and the payment was
@@ -160,32 +146,32 @@ THEN
RAISE NOTICE 'forcing low expiration time';
out_final_expiration = 0;
ELSE
- out_final_expiration = my_reserve_expiration;
+ out_final_expiration = reserve.expiration_date;
END IF;
RAISE NOTICE 'amount paid too low';
RETURN;
END IF;
-- Check reserve balance is sufficient.
-IF (my_balance_val > in_reserve_payment_val)
+IF (out_reserve_balance.val > in_reserve_payment.val)
THEN
- IF (my_balance_frac >= in_reserve_payment_frac)
+ IF (out_reserve_balance.frac >= in_reserve_payment.frac)
THEN
- my_balance_val=my_balance_val - in_reserve_payment_val;
- my_balance_frac=my_balance_frac - in_reserve_payment_frac;
+ my_balance.val=out_reserve_balance.val - in_reserve_payment.val;
+ my_balance.frac=out_reserve_balance.frac - in_reserve_payment.frac;
ELSE
- my_balance_val=my_balance_val - in_reserve_payment_val - 1;
- my_balance_frac=my_balance_frac + 100000000 - in_reserve_payment_frac;
+ my_balance.val=out_reserve_balance.val - in_reserve_payment.val - 1;
+ my_balance.frac=out_reserve_balance.frac + 100000000 - in_reserve_payment.frac;
END IF;
ELSE
- IF (my_balance_val = in_reserve_payment_val) AND (my_balance_frac >= in_reserve_payment_frac)
+ IF (out_reserve_balance.val = in_reserve_payment.val) AND (out_reserve_balance.frac >= in_reserve_payment.frac)
THEN
- my_balance_val=0;
- my_balance_frac=my_balance_frac - in_reserve_payment_frac;
+ my_balance.val=0;
+ my_balance.frac=out_reserve_balance.frac - in_reserve_payment.frac;
ELSE
- out_final_expiration = my_reserve_expiration;
- out_open_cost_val = my_cost_val;
- out_open_cost_frac = my_cost_frac;
+ out_final_expiration = reserve.expiration_date;
+ out_open_cost.val = my_cost.val;
+ out_open_cost.frac = my_cost.frac;
out_no_funds=TRUE;
RAISE NOTICE 'reserve balance too low';
RETURN;
@@ -193,17 +179,15 @@ ELSE
END IF;
UPDATE reserves SET
- current_balance_val=my_balance_val
- ,current_balance_frac=my_balance_frac
- ,gc_date=my_reserve_expiration + in_reserve_gc_delay
+ current_balance=my_balance
+ ,gc_date=reserve.expiration_date + in_reserve_gc_delay
,expiration_date=my_expiration_date
- ,purses_allowed=my_purses_allowed
+ ,purses_allowed=reserve.purses_allowed
WHERE
reserve_pub=in_reserve_pub;
out_final_expiration=my_expiration_date;
-out_open_cost_val = my_cost_val;
-out_open_cost_frac = my_cost_frac;
+out_open_cost = my_cost;
out_no_funds=FALSE;
RETURN;
diff --git a/src/exchangedb/exchange_do_reserve_open_deposit.sql b/src/exchangedb/exchange_do_reserve_open_deposit.sql
index 725122702..aa6f86a9b 100644
--- a/src/exchangedb/exchange_do_reserve_open_deposit.sql
+++ b/src/exchangedb/exchange_do_reserve_open_deposit.sql
@@ -21,8 +21,7 @@ CREATE OR REPLACE FUNCTION exchange_do_reserve_open_deposit(
IN in_coin_sig BYTEA,
IN in_reserve_sig BYTEA,
IN in_reserve_pub BYTEA,
- IN in_coin_total_val INT8,
- IN in_coin_total_frac INT4,
+ IN in_coin_total taler_amount,
OUT out_insufficient_funds BOOLEAN)
LANGUAGE plpgsql
AS $$
@@ -33,16 +32,15 @@ INSERT INTO exchange.reserves_open_deposits
,reserve_pub
,coin_pub
,coin_sig
- ,contribution_val
- ,contribution_frac
+ ,contribution
)
VALUES
(in_reserve_sig
,in_reserve_pub
,in_coin_pub
,in_coin_sig
- ,in_coin_total_val
- ,in_coin_total_frac)
+ ,in_coin_total
+ )
ON CONFLICT DO NOTHING;
IF NOT FOUND
@@ -54,24 +52,24 @@ END IF;
-- Check and update balance of the coin.
-UPDATE exchange.known_coins
+UPDATE exchange.known_coins kc
SET
- remaining_frac=remaining_frac-in_coin_total_frac
+ remaining.frac=(kc.remaining).frac-in_coin_total.frac
+ CASE
- WHEN remaining_frac < in_coin_total_frac
+ WHEN (kc.remaining).frac < in_coin_total.frac
THEN 100000000
ELSE 0
END,
- remaining_val=remaining_val-in_coin_total_val
+ remaining.val=(kc.remaining).val-in_coin_total.val
- CASE
- WHEN remaining_frac < in_coin_total_frac
+ WHEN (kc.remaining).frac < in_coin_total.frac
THEN 1
ELSE 0
END
WHERE coin_pub=in_coin_pub
- AND ( (remaining_val > in_coin_total_val) OR
- ( (remaining_frac >= in_coin_total_frac) AND
- (remaining_val >= in_coin_total_val) ) );
+ AND ( ((kc.remaining).val > in_coin_total.val) OR
+ ( ((kc.remaining).frac >= in_coin_total.frac) AND
+ ((kc.remaining).val >= in_coin_total.val) ) );
IF NOT FOUND
THEN
@@ -84,4 +82,3 @@ END IF;
out_insufficient_funds=FALSE;
END $$;
-
diff --git a/src/exchangedb/exchange_do_reserve_purse.sql b/src/exchangedb/exchange_do_reserve_purse.sql
index a110c85a3..8ae652e69 100644
--- a/src/exchangedb/exchange_do_reserve_purse.sql
+++ b/src/exchangedb/exchange_do_reserve_purse.sql
@@ -18,10 +18,11 @@ CREATE OR REPLACE FUNCTION exchange_do_reserve_purse(
IN in_purse_pub BYTEA,
IN in_merge_sig BYTEA,
IN in_merge_timestamp INT8,
+ IN in_reserve_expiration INT8,
+ IN in_reserve_gc INT8,
IN in_reserve_sig BYTEA,
IN in_reserve_quota BOOLEAN,
- IN in_purse_fee_val INT8,
- IN in_purse_fee_frac INT4,
+ IN in_purse_fee taler_amount,
IN in_reserve_pub BYTEA,
IN in_wallet_h_payto BYTEA,
OUT out_no_funds BOOLEAN,
@@ -32,7 +33,7 @@ AS $$
BEGIN
-- Store purse merge signature, checks for purse_pub uniqueness
-INSERT INTO exchange.purse_merges
+INSERT INTO purse_merges
(partner_serial_id
,reserve_pub
,purse_pub
@@ -52,7 +53,7 @@ THEN
-- Note that by checking 'merge_sig', we implicitly check
-- identity over everything that the signature covers.
PERFORM
- FROM exchange.purse_merges
+ FROM purse_merges
WHERE purse_pub=in_purse_pub
AND merge_sig=in_merge_sig;
IF NOT FOUND
@@ -99,31 +100,39 @@ ELSE
-- UPDATE reserves balance (and check if balance is enough to pay the fee)
IF (out_no_reserve)
THEN
- IF ( (0 != in_purse_fee_val) OR
- (0 != in_purse_fee_frac) )
+ IF ( (0 != in_purse_fee.val) OR
+ (0 != in_purse_fee.frac) )
THEN
out_no_funds=TRUE;
RETURN;
END IF;
+ INSERT INTO exchange.reserves
+ (reserve_pub
+ ,expiration_date
+ ,gc_date)
+ VALUES
+ (in_reserve_pub
+ ,in_reserve_expiration
+ ,in_reserve_gc);
ELSE
UPDATE exchange.reserves
SET
- current_balance_frac=current_balance_frac-in_purse_fee_frac
+ current_balance.frac=(current_balance).frac-in_purse_fee.frac
+ CASE
- WHEN current_balance_frac < in_purse_fee_frac
+ WHEN (current_balance).frac < in_purse_fee.frac
THEN 100000000
ELSE 0
END,
- current_balance_val=current_balance_val-in_purse_fee_val
+ current_balance.val=(current_balance).val-in_purse_fee.val
- CASE
- WHEN current_balance_frac < in_purse_fee_frac
+ WHEN (current_balance).frac < in_purse_fee.frac
THEN 1
ELSE 0
END
WHERE reserve_pub=in_reserve_pub
- AND ( (current_balance_val > in_purse_fee_val) OR
- ( (current_balance_frac >= in_purse_fee_frac) AND
- (current_balance_val >= in_purse_fee_val) ) );
+ AND ( ((current_balance).val > in_purse_fee.val) OR
+ ( ((current_balance).frac >= in_purse_fee.frac) AND
+ ((current_balance).val >= in_purse_fee.val) ) );
IF NOT FOUND
THEN
out_no_funds=TRUE;
@@ -136,7 +145,7 @@ out_no_funds=FALSE;
-- Store account merge signature.
-INSERT INTO exchange.account_merges
+INSERT INTO account_merges
(reserve_pub
,reserve_sig
,purse_pub
@@ -149,9 +158,5 @@ INSERT INTO exchange.account_merges
END $$;
-COMMENT ON FUNCTION exchange_do_reserve_purse(BYTEA, BYTEA, INT8, BYTEA, BOOLEAN, INT8, INT4, BYTEA, BYTEA)
+COMMENT ON FUNCTION exchange_do_reserve_purse(BYTEA, BYTEA, INT8, INT8, INT8, BYTEA, BOOLEAN, taler_amount, BYTEA, BYTEA)
IS 'Create a purse for a reserve.';
-
-
-
-
diff --git a/src/exchangedb/exchange_do_reserves_in_insert.sql b/src/exchangedb/exchange_do_reserves_in_insert.sql
new file mode 100644
index 000000000..1be06f063
--- /dev/null
+++ b/src/exchangedb/exchange_do_reserves_in_insert.sql
@@ -0,0 +1,122 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2014--2023 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/>
+--
+
+
+CREATE OR REPLACE FUNCTION exchange_do_array_reserves_insert(
+ IN in_gc_date INT8,
+ IN in_reserve_expiration INT8,
+ IN ina_reserve_pub BYTEA[],
+ IN ina_wire_ref INT8[],
+ IN ina_credit taler_amount[],
+ IN ina_exchange_account_name TEXT[],
+ IN ina_execution_date INT8[],
+ IN ina_wire_source_h_payto BYTEA[],
+ IN ina_payto_uri TEXT[],
+ IN ina_notify TEXT[])
+RETURNS SETOF exchange_do_array_reserve_insert_return_type
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ conflict BOOL;
+ dup BOOL;
+ uuid INT8;
+ i INT4;
+ ini_reserve_pub BYTEA;
+ ini_wire_ref INT8;
+ ini_credit taler_amount;
+ ini_exchange_account_name TEXT;
+ ini_execution_date INT8;
+ ini_wire_source_h_payto BYTEA;
+ ini_payto_uri TEXT;
+ ini_notify TEXT;
+BEGIN
+
+ FOR i IN 1..array_length(ina_reserve_pub,1)
+ LOOP
+ ini_reserve_pub = ina_reserve_pub[i];
+ ini_wire_ref = ina_wire_ref[i];
+ ini_credit = ina_credit[i];
+ ini_exchange_account_name = ina_exchange_account_name[i];
+ ini_execution_date = ina_execution_date[i];
+ ini_wire_source_h_payto = ina_wire_source_h_payto[i];
+ ini_payto_uri = ina_payto_uri[i];
+ ini_notify = ina_notify[i];
+
+-- RAISE WARNING 'Starting loop on %', ini_notify;
+
+ INSERT INTO wire_targets
+ (wire_target_h_payto
+ ,payto_uri
+ ) VALUES (
+ ini_wire_source_h_payto
+ ,ini_payto_uri
+ )
+ ON CONFLICT DO NOTHING;
+
+ INSERT INTO reserves
+ (reserve_pub
+ ,current_balance
+ ,expiration_date
+ ,gc_date
+ ) VALUES (
+ ini_reserve_pub
+ ,ini_credit
+ ,in_reserve_expiration
+ ,in_gc_date
+ )
+ ON CONFLICT DO NOTHING
+ RETURNING reserve_uuid
+ INTO uuid;
+ conflict = NOT FOUND;
+
+ INSERT INTO reserves_in
+ (reserve_pub
+ ,wire_reference
+ ,credit
+ ,exchange_account_section
+ ,wire_source_h_payto
+ ,execution_date
+ ) VALUES (
+ ini_reserve_pub
+ ,ini_wire_ref
+ ,ini_credit
+ ,ini_exchange_account_name
+ ,ini_wire_source_h_payto
+ ,ini_execution_date
+ )
+ ON CONFLICT DO NOTHING;
+
+ IF NOT FOUND
+ THEN
+ IF conflict
+ THEN
+ dup = TRUE;
+ else
+ dup = FALSE;
+ END IF;
+ ELSE
+ IF NOT conflict
+ THEN
+ EXECUTE FORMAT (
+ 'NOTIFY %s'
+ ,ini_notify);
+ END IF;
+ dup = FALSE;
+ END IF;
+ RETURN NEXT (dup,uuid);
+ END LOOP;
+ RETURN;
+END $$;
diff --git a/src/exchangedb/exchange_do_select_deposits_missing_wire.sql b/src/exchangedb/exchange_do_select_deposits_missing_wire.sql
new file mode 100644
index 000000000..3d44a58c9
--- /dev/null
+++ b/src/exchangedb/exchange_do_select_deposits_missing_wire.sql
@@ -0,0 +1,73 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2023 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/>
+--
+-- @author: Christian Grothoff
+
+CREATE OR REPLACE FUNCTION exchange_do_select_deposits_missing_wire(
+ IN in_min_serial_id INT8)
+RETURNS SETOF exchange_do_select_deposits_missing_wire_return_type
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ missing CURSOR
+ FOR
+ SELECT
+ batch_deposit_serial_id
+ ,wire_target_h_payto
+ ,wire_deadline
+ FROM batch_deposits
+ WHERE batch_deposit_serial_id > in_min_serial_id
+ ORDER BY batch_deposit_serial_id ASC;
+DECLARE
+ my_total_val INT8; -- all deposits without wire
+DECLARE
+ my_total_frac INT8; -- all deposits without wire (fraction, not normalized)
+DECLARE
+ my_total taler_amount; -- amount that was originally deposited
+DECLARE
+ my_batch_record RECORD;
+DECLARE
+ i RECORD;
+BEGIN
+
+OPEN missing;
+LOOP
+ FETCH NEXT FROM missing INTO i;
+ EXIT WHEN NOT FOUND;
+
+ SELECT
+ SUM((cdep.amount_with_fee).val) AS total_val
+ ,SUM((cdep.amount_with_fee).frac::INT8) AS total_frac
+ INTO
+ my_batch_record
+ FROM coin_deposits cdep
+ WHERE cdep.batch_deposit_serial_id = i.batch_deposit_serial_id;
+
+ my_total_val=my_batch_record.total_val;
+ my_total_frac=my_batch_record.total_frac;
+
+ -- Normalize total amount
+ my_total.val = my_total_val + my_total_frac / 100000000;
+ my_total.frac = my_total_frac % 100000000;
+ RETURN NEXT (
+ i.batch_deposit_serial_id
+ ,my_total
+ ,i.wire_target_h_payto
+ ,i.wire_deadline);
+
+END LOOP;
+CLOSE missing;
+RETURN;
+END $$;
diff --git a/src/exchangedb/exchange_do_select_justification_for_missing_wire.sql b/src/exchangedb/exchange_do_select_justification_for_missing_wire.sql
new file mode 100644
index 000000000..f02a51d3d
--- /dev/null
+++ b/src/exchangedb/exchange_do_select_justification_for_missing_wire.sql
@@ -0,0 +1,102 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2023 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/>
+--
+-- @author: Christian Grothoff
+
+CREATE OR REPLACE FUNCTION exchange_do_select_justification_missing_wire(
+ IN in_wire_target_h_payto BYTEA,
+ IN in_current_time INT8,
+ OUT out_payto_uri TEXT, -- NULL allowed
+ OUT out_kyc_pending TEXT, -- NULL allowed
+ OUT out_aml_status INT4, -- NULL allowed
+ OUT out_aml_limit taler_amount) -- NULL allowed!
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ my_required_checks TEXT[];
+DECLARE
+ my_aml_data RECORD;
+DECLARE
+ satisfied CURSOR FOR
+ SELECT satisfied_checks
+ FROM kyc_attributes
+ WHERE h_payto=in_wire_target_h_payto
+ AND expiration_time < in_current_time;
+DECLARE
+ i RECORD;
+BEGIN
+
+ -- Fetch payto URI
+ out_payto_uri = NULL;
+ SELECT payto_uri
+ INTO out_payto_uri
+ FROM wire_targets
+ WHERE wire_target_h_payto=my_wire_target_h_payto;
+
+ -- Check KYC status
+ my_required_checks = NULL;
+ SELECT string_to_array (required_checks, ' ')
+ INTO my_required_checks
+ FROM legitimization_requirements
+ WHERE h_payto=my_wire_target_h_payto;
+
+ -- Get last AML decision
+ SELECT
+ new_threshold
+ ,kyc_requirements
+ ,new_status
+ INTO
+ my_aml_data
+ FROM aml_history
+ WHERE h_payto=in_wire_target_h_payto
+ ORDER BY aml_history_serial_id -- get last decision
+ DESC LIMIT 1;
+ IF FOUND
+ THEN
+ out_aml_limit=my_aml_data.new_threshold;
+ out_aml_status=my_aml_data.kyc_status;
+ -- Combine KYC requirements
+ my_required_checks
+ = array_cat (my_required_checks,
+ my_aml_data.kyc_requirements);
+ ELSE
+ out_aml_limit=NULL;
+ out_aml_status=0; -- or NULL? Style question!
+ END IF;
+
+ OPEN satisfied;
+ LOOP
+ FETCH NEXT FROM satisfied INTO i;
+ EXIT WHEN NOT FOUND;
+
+ -- remove all satisfied checks from the list
+ FOR i in 1..array_length(i.satisfied_checks)
+ LOOP
+ my_required_checks
+ = array_remove (my_required_checks,
+ i.satisfied_checks[i]);
+ END LOOP;
+ END LOOP;
+
+ -- Return remaining required checks as one string
+ IF ( (my_required_checks IS NOT NULL) AND
+ (0 < array_length(my_satisfied_checks)) )
+ THEN
+ out_kyc_pending
+ = array_to_string (my_required_checks, ' ');
+ END IF;
+
+ RETURN;
+END $$;
diff --git a/src/exchangedb/exchange_do_withdraw.sql b/src/exchangedb/exchange_do_withdraw.sql
deleted file mode 100644
index 9689bae5a..000000000
--- a/src/exchangedb/exchange_do_withdraw.sql
+++ /dev/null
@@ -1,199 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2014--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/>
---
-
-
-CREATE OR REPLACE FUNCTION exchange_do_withdraw(
- IN cs_nonce BYTEA,
- IN amount_val INT8,
- IN amount_frac INT4,
- IN h_denom_pub BYTEA,
- IN rpub BYTEA,
- IN reserve_sig BYTEA,
- IN h_coin_envelope BYTEA,
- IN denom_sig BYTEA,
- IN now INT8,
- IN min_reserve_gc INT8,
- OUT reserve_found BOOLEAN,
- OUT balance_ok BOOLEAN,
- OUT nonce_ok BOOLEAN,
- OUT ruuid INT8)
-LANGUAGE plpgsql
-AS $$
-DECLARE
- reserve_gc INT8;
-DECLARE
- denom_serial INT8;
-DECLARE
- reserve_val INT8;
-DECLARE
- reserve_frac INT4;
-BEGIN
--- Shards: reserves by reserve_pub (SELECT)
--- reserves_out (INSERT, with CONFLICT detection) by wih
--- reserves by reserve_pub (UPDATE)
--- reserves_in by reserve_pub (SELECT)
--- wire_targets by wire_target_h_payto
-
-SELECT denominations_serial
- INTO denom_serial
- FROM exchange.denominations
- WHERE denom_pub_hash=h_denom_pub;
-
-IF NOT FOUND
-THEN
- -- denomination unknown, should be impossible!
- reserve_found=FALSE;
- balance_ok=FALSE;
- ruuid=0;
- ASSERT false, 'denomination unknown';
- RETURN;
-END IF;
-
-
-SELECT
- current_balance_val
- ,current_balance_frac
- ,gc_date
- ,reserve_uuid
- INTO
- reserve_val
- ,reserve_frac
- ,reserve_gc
- ,ruuid
- FROM exchange.reserves
- WHERE reserves.reserve_pub=rpub;
-
-IF NOT FOUND
-THEN
- -- reserve unknown
- reserve_found=FALSE;
- balance_ok=FALSE;
- nonce_ok=TRUE;
- ruuid=2;
- RETURN;
-END IF;
-
--- We optimistically insert, and then on conflict declare
--- the query successful due to idempotency.
-INSERT INTO exchange.reserves_out
- (h_blind_ev
- ,denominations_serial
- ,denom_sig
- ,reserve_uuid
- ,reserve_sig
- ,execution_date
- ,amount_with_fee_val
- ,amount_with_fee_frac)
-VALUES
- (h_coin_envelope
- ,denom_serial
- ,denom_sig
- ,ruuid
- ,reserve_sig
- ,now
- ,amount_val
- ,amount_frac)
-ON CONFLICT DO NOTHING;
-
-IF NOT FOUND
-THEN
- -- idempotent query, all constraints must be satisfied
- reserve_found=TRUE;
- balance_ok=TRUE;
- nonce_ok=TRUE;
- RETURN;
-END IF;
-
--- Check reserve balance is sufficient.
-IF (reserve_val > amount_val)
-THEN
- IF (reserve_frac >= amount_frac)
- THEN
- reserve_val=reserve_val - amount_val;
- reserve_frac=reserve_frac - amount_frac;
- ELSE
- reserve_val=reserve_val - amount_val - 1;
- reserve_frac=reserve_frac + 100000000 - amount_frac;
- END IF;
-ELSE
- IF (reserve_val = amount_val) AND (reserve_frac >= amount_frac)
- THEN
- reserve_val=0;
- reserve_frac=reserve_frac - amount_frac;
- ELSE
- reserve_found=TRUE;
- nonce_ok=TRUE; -- we do not really know
- balance_ok=FALSE;
- RETURN;
- END IF;
-END IF;
-
--- Calculate new expiration dates.
-min_reserve_gc=GREATEST(min_reserve_gc,reserve_gc);
-
--- Update reserve balance.
-UPDATE reserves SET
- gc_date=min_reserve_gc
- ,current_balance_val=reserve_val
- ,current_balance_frac=reserve_frac
-WHERE
- reserves.reserve_pub=rpub;
-
-reserve_found=TRUE;
-balance_ok=TRUE;
-
-
-
--- Special actions needed for a CS withdraw?
-IF NOT NULL cs_nonce
-THEN
- -- Cache CS signature to prevent replays in the future
- -- (and check if cached signature exists at the same time).
- INSERT INTO exchange.cs_nonce_locks
- (nonce
- ,max_denomination_serial
- ,op_hash)
- VALUES
- (cs_nonce
- ,denom_serial
- ,h_coin_envelope)
- ON CONFLICT DO NOTHING;
-
- IF NOT FOUND
- THEN
- -- See if the existing entry is identical.
- SELECT 1
- FROM exchange.cs_nonce_locks
- WHERE nonce=cs_nonce
- AND op_hash=h_coin_envelope;
- IF NOT FOUND
- THEN
- reserve_found=FALSE;
- balance_ok=FALSE;
- nonce_ok=FALSE;
- RETURN;
- END IF;
- END IF;
-ELSE
- nonce_ok=TRUE; -- no nonce, hence OK!
-END IF;
-
-END $$;
-
-
-COMMENT ON FUNCTION exchange_do_withdraw(BYTEA, INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA, INT8, INT8)
- IS 'Checks whether the reserve has sufficient balance for a withdraw operation (or the request is repeated and was previously approved) and if so updates the database with the result';
-
diff --git a/src/exchangedb/exchangedb-postgres.conf b/src/exchangedb/exchangedb-postgres.conf
index e481940ce..726d28576 100644
--- a/src/exchangedb/exchangedb-postgres.conf
+++ b/src/exchangedb/exchangedb-postgres.conf
@@ -1,9 +1,9 @@
[exchangedb-postgres]
-CONFIG = "postgres:///taler"
+CONFIG = "postgres:///taler-exchange"
# Where are the SQL files to setup our tables?
# Important: this MUST end with a "/"!
-SQL_DIR = $DATADIR/sql/exchange/
+SQL_DIR = ${DATADIR}sql/exchange/
# Number of purses per account by default.
DEFAULT_PURSE_LIMIT = 1 \ No newline at end of file
diff --git a/src/exchangedb/exchangedb_accounts.c b/src/exchangedb/exchangedb_accounts.c
index e41074822..3f0e47afb 100644
--- a/src/exchangedb/exchangedb_accounts.c
+++ b/src/exchangedb/exchangedb_accounts.c
@@ -190,28 +190,35 @@ add_account_cb (void *cls,
( (credit) &&
(lc->credit) ) ) )
return; /* not enabled for us, skip */
- if (GNUNET_OK !=
+ if (GNUNET_OK ==
GNUNET_CONFIGURATION_get_value_string (cfg,
section,
"PAYTO_URI",
&payto_uri))
{
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
- section,
- "PAYTO_URI");
- lc->res = GNUNET_SYSERR;
- return;
+ method = TALER_payto_get_method (payto_uri);
+ GNUNET_free (payto_uri);
+ if (NULL == method)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "payto URI in config ([%s]/PAYTO_URI) malformed\n",
+ section);
+ lc->res = GNUNET_SYSERR;
+ return;
+ }
}
- method = TALER_payto_get_method (payto_uri);
- GNUNET_free (payto_uri);
- if (NULL == method)
+ else if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "WIRE_METHOD",
+ &method))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "payto URI in config ([%s]/PAYTO_URI) malformed\n",
- section);
- lc->res = GNUNET_SYSERR;
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
+ section,
+ "WIRE_METHOD");
return;
}
+ GNUNET_assert (NULL != method);
wa = GNUNET_new (struct WireAccount);
wa->section_name = GNUNET_strdup (section);
wa->method = method;
diff --git a/src/exchangedb/perf-exchangedb-reserves-in-insert-postgres b/src/exchangedb/perf-exchangedb-reserves-in-insert-postgres
new file mode 100755
index 000000000..8eafde3ce
--- /dev/null
+++ b/src/exchangedb/perf-exchangedb-reserves-in-insert-postgres
@@ -0,0 +1,210 @@
+#! /bin/bash
+
+# perf-exchangedb-reserves-in-insert-postgres - temporary wrapper script for .libs/perf-exchangedb-reserves-in-insert-postgres
+# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15
+#
+# The perf-exchangedb-reserves-in-insert-postgres program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=""
+
+# This environment variable determines our operation mode.
+if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='2.4.6'
+ notinst_deplibs=' libtalerexchangedb.la ../../src/json/libtalerjson.la ../../src/util/libtalerutil.la ../../src/pq/libtalerpq.la'
+else
+ # When we are sourced in execute mode, $file and $ECHO are already set.
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ file="$0"
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+ ECHO="printf %s\\n"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string --lt-
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's ../../libtool value, followed by no.
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=$0
+ shift
+ for lt_opt
+ do
+ case "$lt_opt" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
+ test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
+ lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'`
+ cat "$lt_dump_D/$lt_dump_F"
+ exit 0
+ ;;
+ --lt-*)
+ $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n "$lt_option_debug"; then
+ echo "perf-exchangedb-reserves-in-insert-postgres:perf-exchangedb-reserves-in-insert-postgres:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-15" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ $ECHO "perf-exchangedb-reserves-in-insert-postgres:perf-exchangedb-reserves-in-insert-postgres:$LINENO: newargv[$lt_dump_args_N]: $lt_arg"
+ lt_dump_args_N=`expr $lt_dump_args_N + 1`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+
+ if test -n "$lt_option_debug"; then
+ $ECHO "perf-exchangedb-reserves-in-insert-postgres:perf-exchangedb-reserves-in-insert-postgres:$LINENO: newargv[0]: $progdir/$program" 1>&2
+ func_lt_dump_args ${1+"$@"} 1>&2
+ fi
+ exec "$progdir/$program" ${1+"$@"}
+
+ $ECHO "$0: cannot exec $program $*" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from $@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case " $* " in
+ *\ --lt-*)
+ for lt_wr_arg
+ do
+ case $lt_wr_arg in
+ --lt-*) ;;
+ *) set x "$@" "$lt_wr_arg"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core ${1+"$@"}
+}
+
+ # Parse options
+ func_parse_lt_options "$0" ${1+"$@"}
+
+ # Find the directory that this script lives in.
+ thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+ test "x$thisdir" = "x$file" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
+ while test -n "$file"; do
+ destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+
+ # If there was a directory component, then change thisdir.
+ if test "x$destdir" != "x$file"; then
+ case "$destdir" in
+ [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
+ *) thisdir="$thisdir/$destdir" ;;
+ esac
+ fi
+
+ file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'`
+ file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
+ if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
+ # special case for '.'
+ if test "$thisdir" = "."; then
+ thisdir=`pwd`
+ fi
+ # remove .libs from thisdir
+ case "$thisdir" in
+ *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;;
+ .libs ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=`cd "$thisdir" && pwd`
+ test -n "$absdir" && thisdir="$absdir"
+
+ program='perf-exchangedb-reserves-in-insert-postgres'
+ progdir="$thisdir/.libs"
+
+
+ if test -f "$progdir/$program"; then
+ # Add our own library path to LD_LIBRARY_PATH
+ LD_LIBRARY_PATH="/home/priscilla/exchange/src/exchangedb/.libs:/home/priscilla/exchange/src/json/.libs:/home/priscilla/exchange/src/util/.libs:/home/priscilla/exchange/src/pq/.libs:$LD_LIBRARY_PATH"
+
+ # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /usr/bin/sed 's/::*$//'`
+
+ export LD_LIBRARY_PATH
+
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ # Run the actual program with our arguments.
+ func_exec_program ${1+"$@"}
+ fi
+ else
+ # The program doesn't exist.
+ $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2
+ $ECHO "This script is just a wrapper for $program." 1>&2
+ $ECHO "See the libtool documentation for more information." 1>&2
+ exit 1
+ fi
+fi
diff --git a/src/exchangedb/perf_deposits_get_ready.c b/src/exchangedb/perf_deposits_get_ready.c
new file mode 100644
index 000000000..005ea6843
--- /dev/null
+++ b/src/exchangedb/perf_deposits_get_ready.c
@@ -0,0 +1,565 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2023 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 exchangedb/perf_deposits_get_ready.c
+ * @brief benchmark for deposits_get_ready
+git * @author Joseph Xu
+ */
+#include "platform.h"
+#include "taler_exchangedb_lib.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+#include "math.h"
+
+/**
+ * Global result from the testcase.
+ */
+static int result;
+
+/**
+ * Report line of error if @a cond is true, and jump to label "drop".
+ */
+#define FAILIF(cond) \
+ do { \
+ if (! (cond)) {break;} \
+ GNUNET_break (0); \
+ goto drop; \
+ } while (0)
+
+
+/**
+ * Initializes @a ptr with random data.
+ */
+#define RND_BLK(ptr) \
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, \
+ sizeof (*ptr))
+
+/**
+ * Initializes @a ptr with zeros.
+ */
+#define ZR_BLK(ptr) \
+ memset (ptr, 0, sizeof (*ptr))
+
+/**
+ * Currency we use. Must match test-exchange-db-*.conf.
+ */
+#define CURRENCY "EUR"
+#define RSA_KEY_SIZE 1024
+#define NUM_ROWS 1000
+#define ROUNDS 100
+#define MELT_NEW_COINS 5
+#define MELT_NOREVEAL_INDEX 1
+
+/**
+ * Database plugin under test.
+ */
+static struct TALER_EXCHANGEDB_Plugin *plugin;
+
+static struct TALER_DenomFeeSet fees;
+
+static struct TALER_MerchantWireHashP h_wire_wt;
+
+/**
+ * Denomination keys used for fresh coins in melt test.
+ */
+static struct DenomKeyPair **new_dkp;
+
+static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins;
+
+struct DenomKeyPair
+{
+ struct TALER_DenominationPrivateKey priv;
+ struct TALER_DenominationPublicKey pub;
+};
+
+
+/**
+ * Destroy a denomination key pair. The key is not necessarily removed from the DB.
+ *
+ * @param dkp the key pair to destroy
+ */
+static void
+destroy_denom_key_pair (struct DenomKeyPair *dkp)
+{
+ TALER_denom_pub_free (&dkp->pub);
+ TALER_denom_priv_free (&dkp->priv);
+ GNUNET_free (dkp);
+}
+
+
+/**
+ * Create a denomination key pair by registering the denomination in the DB.
+ *
+ * @param size the size of the denomination key
+ * @param now time to use for key generation, legal expiration will be 3h later.
+ * @param fees fees to use
+ * @return the denominaiton key pair; NULL upon error
+ */
+static struct DenomKeyPair *
+create_denom_key_pair (unsigned int size,
+ struct GNUNET_TIME_Timestamp now,
+ const struct TALER_Amount *value,
+ const struct TALER_DenomFeeSet *fees)
+{
+ struct DenomKeyPair *dkp;
+ struct TALER_EXCHANGEDB_DenominationKey dki;
+ struct TALER_EXCHANGEDB_DenominationKeyInformation issue2;
+
+ dkp = GNUNET_new (struct DenomKeyPair);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_priv_create (&dkp->priv,
+ &dkp->pub,
+ GNUNET_CRYPTO_BSA_RSA,
+ size));
+ memset (&dki,
+ 0,
+ sizeof (struct TALER_EXCHANGEDB_DenominationKey));
+ dki.denom_pub = dkp->pub;
+ dki.issue.start = now;
+ dki.issue.expire_withdraw
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (
+ now.abs_time,
+ GNUNET_TIME_UNIT_HOURS));
+ dki.issue.expire_deposit
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (
+ now.abs_time,
+ GNUNET_TIME_relative_multiply (
+ GNUNET_TIME_UNIT_HOURS, 2)));
+ dki.issue.expire_legal
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (
+ now.abs_time,
+ GNUNET_TIME_relative_multiply (
+ GNUNET_TIME_UNIT_HOURS, 3)));
+ dki.issue.value = *value;
+ dki.issue.fees = *fees;
+ TALER_denom_pub_hash (&dkp->pub,
+ &dki.issue.denom_hash);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->insert_denomination_info (plugin->cls,
+ &dki.denom_pub,
+ &dki.issue))
+ {
+ GNUNET_break (0);
+ destroy_denom_key_pair (dkp);
+ return NULL;
+ }
+ memset (&issue2, 0, sizeof (issue2));
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->get_denomination_info (plugin->cls,
+ &dki.issue.denom_hash,
+ &issue2))
+ {
+ GNUNET_break (0);
+ destroy_denom_key_pair (dkp);
+ return NULL;
+ }
+ if (0 != GNUNET_memcmp (&dki.issue,
+ &issue2))
+ {
+ GNUNET_break (0);
+ destroy_denom_key_pair (dkp);
+ return NULL;
+ }
+ return dkp;
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure with config
+ */
+
+static void
+run (void *cls)
+{
+ struct TALER_EXCHANGEDB_Refresh refresh;
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ const uint32_t num_partitions = 10;
+ struct TALER_Amount value;
+ struct TALER_EXCHANGEDB_CollectableBlindcoin cbc;
+ struct TALER_DenominationPublicKey *new_denom_pubs = NULL;
+ struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO;
+ unsigned long long sqrs = 0;
+ struct TALER_EXCHANGEDB_CoinDepositInformation *depos;
+ struct TALER_EXCHANGEDB_BatchDeposit bd;
+ struct TALER_EXCHANGEDB_Refund *ref;
+ unsigned int *perm;
+ unsigned long long duration_sq;
+ struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin;
+ struct GNUNET_CRYPTO_BlindingInputValues bi = {
+ .cipher = GNUNET_CRYPTO_BSA_RSA,
+ .rc = 0
+ };
+ struct TALER_ExchangeWithdrawValues alg_values = {
+ .blinding_inputs = &bi
+ };
+
+ ref = GNUNET_new_array (ROUNDS + 1,
+ struct TALER_EXCHANGEDB_Refund);
+ depos = GNUNET_new_array (ROUNDS + 1,
+ struct TALER_EXCHANGEDB_CoinDepositInformation);
+
+ if (NULL ==
+ (plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
+ {
+ GNUNET_break (0);
+ result = 77;
+ return;
+ }
+ (void) plugin->drop_tables (plugin->cls);
+ if (GNUNET_OK !=
+ plugin->create_tables (plugin->cls,
+ true,
+ num_partitions))
+ {
+ GNUNET_break (0);
+ result = 77;
+ goto cleanup;
+ }
+ if (GNUNET_OK !=
+ plugin->preflight (plugin->cls))
+ {
+ GNUNET_break (0);
+ goto cleanup;
+ }
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":1.000010",
+ &value));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.withdraw));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.deposit));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.refresh));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.refund));
+ {
+ ZR_BLK (&cbc);
+ new_dkp = GNUNET_new_array (MELT_NEW_COINS,
+ struct DenomKeyPair *);
+ new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS,
+ struct TALER_DenominationPublicKey);
+ revealed_coins
+ = GNUNET_new_array (MELT_NEW_COINS,
+ struct TALER_EXCHANGEDB_RefreshRevealedCoin);
+ for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
+ {
+ struct GNUNET_TIME_Timestamp now;
+ struct GNUNET_CRYPTO_RsaBlindedMessage *rp;
+ struct TALER_BlindedPlanchet *bp;
+
+ now = GNUNET_TIME_timestamp_get ();
+ new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE,
+ now,
+ &value,
+ &fees);
+ GNUNET_assert (NULL != new_dkp[cnt]);
+ new_denom_pubs[cnt] = new_dkp[cnt]->pub;
+ ccoin = &revealed_coins[cnt];
+ bp = &ccoin->blinded_planchet;
+ bp->blinded_message = GNUNET_new (struct GNUNET_CRYPTO_BlindedMessage);
+ bp->blinded_message->cipher = GNUNET_CRYPTO_BSA_RSA;
+ bp->blinded_message->rc = 1;
+ rp = &bp->blinded_message->details.rsa_blinded_message;
+ rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 (
+ GNUNET_CRYPTO_QUALITY_WEAK,
+ (RSA_KEY_SIZE / 8) - 1);
+ rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size);
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ rp->blinded_msg,
+ rp->blinded_msg_size);
+ TALER_denom_pub_hash (&new_dkp[cnt]->pub,
+ &ccoin->h_denom_pub);
+ ccoin->exchange_vals = alg_values;
+ TALER_coin_ev_hash (bp,
+ &ccoin->h_denom_pub,
+ &ccoin->coin_envelope_hash);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_sign_blinded (&ccoin->coin_sig,
+ &new_dkp[cnt]->priv,
+ true,
+ bp));
+ TALER_coin_ev_hash (bp,
+ &cbc.denom_pub_hash,
+ &cbc.h_coin_envelope);
+ GNUNET_assert (
+ GNUNET_OK ==
+ TALER_denom_sign_blinded (
+ &cbc.sig,
+ &new_dkp[cnt]->priv,
+ false,
+ bp));
+ }
+ }
+ perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE,
+ NUM_ROWS);
+ FAILIF (GNUNET_OK !=
+ plugin->start (plugin->cls,
+ "Transaction"));
+ for (unsigned int j = 0; j < NUM_ROWS; j++)
+ {
+ /*** NEED TO INSERT REFRESH COMMITMENTS + ENSURECOIN ***/
+ union GNUNET_CRYPTO_BlindingSecretP bks;
+ struct GNUNET_TIME_Timestamp deadline;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ struct TALER_ReservePublicKeyP reserve_pub;
+ struct TALER_CoinPubHashP c_hash;
+ unsigned int k = (unsigned int) rand () % 5;
+ unsigned int i = perm[j];
+
+ if (i >= ROUNDS)
+ i = ROUNDS; /* throw-away slot, do not keep around */
+ RND_BLK (&coin_pub);
+ RND_BLK (&c_hash);
+ RND_BLK (&reserve_pub);
+ RND_BLK (&cbc.reserve_sig);
+ TALER_denom_pub_hash (&new_dkp[k]->pub,
+ &cbc.denom_pub_hash);
+ deadline = GNUNET_TIME_timestamp_get ();
+ depos[i].coin.coin_pub = coin_pub;
+ TALER_denom_pub_hash (&new_dkp[k]->pub,
+ &depos[i].coin.denom_pub_hash);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_sig_unblind (&depos[i].coin.denom_sig,
+ &ccoin->coin_sig,
+ &bks,
+ &c_hash,
+ &alg_values,
+ &new_dkp[k]->pub));
+ RND_BLK (&bd.merchant_pub);
+ RND_BLK (&depos[i].csig);
+ RND_BLK (&bd.h_contract_terms);
+ RND_BLK (&bd.wire_salt);
+ depos[i].amount_with_fee = value;
+ bd.refund_deadline = deadline;
+ bd.wire_deadline = deadline;
+ bd.receiver_wire_account =
+ "payto://iban/DE67830654080004822650?receiver-name=Test";
+ TALER_merchant_wire_signature_hash (
+ bd.receiver_wire_account,
+ &bd.wire_salt,
+ &h_wire_wt);
+ bd.num_cdis = 1;
+ bd.cdis = &depos[i];
+ cbc.reserve_pub = reserve_pub;
+ cbc.amount_with_fee = value;
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (CURRENCY,
+ &cbc.withdraw_fee));
+ {
+ bool found;
+ bool nonce_reuse;
+ bool balance_ok;
+ bool age_ok;
+ bool conflict;
+ bool denom_unknown;
+ struct TALER_Amount reserve_balance;
+ uint16_t allowed_minimum_age;
+ uint64_t ruuid;
+ struct GNUNET_TIME_Timestamp now;
+
+ now = GNUNET_TIME_timestamp_get ();
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->do_batch_withdraw (plugin->cls,
+ now,
+ &reserve_pub,
+ &value,
+ true,
+ &found,
+ &balance_ok,
+ &reserve_balance,
+ &age_ok,
+ &allowed_minimum_age,
+ &ruuid));
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->do_batch_withdraw_insert (plugin->cls,
+ NULL,
+ &cbc,
+ now,
+ ruuid,
+ &denom_unknown,
+ &conflict,
+ &nonce_reuse));
+ }
+ {
+ /* ENSURE_COIN_KNOWN */
+ uint64_t known_coin_id;
+ struct TALER_DenominationHashP dph;
+ struct TALER_AgeCommitmentHash agh;
+ FAILIF (TALER_EXCHANGEDB_CKS_ADDED !=
+ plugin->ensure_coin_known (plugin->cls,
+ &depos[i].coin,
+ &known_coin_id,
+ &dph,
+ &agh));
+ refresh.coin = depos[i].coin;
+ RND_BLK (&refresh.coin_sig);
+ RND_BLK (&refresh.rc);
+ refresh.amount_with_fee = value;
+ refresh.noreveal_index = MELT_NOREVEAL_INDEX;
+ }
+ {
+ struct GNUNET_TIME_Timestamp now;
+ bool balance_ok;
+ uint32_t bad_idx;
+ bool ctr_conflict;
+
+ now = GNUNET_TIME_timestamp_get ();
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->do_deposit (plugin->cls,
+ &bd,
+ &now,
+ &balance_ok,
+ &bad_idx,
+ &ctr_conflict));
+ }
+ if (ROUNDS == i)
+ TALER_denom_sig_free (&depos[i].coin.denom_sig);
+ }
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+ plugin->commit (plugin->cls));
+ GNUNET_free (perm);
+ /* End of benchmark setup */
+
+ /**** CALL GET READY DEPOSIT ****/
+ for (unsigned int r = 0; r< ROUNDS; r++)
+ {
+ struct GNUNET_TIME_Absolute time;
+ struct GNUNET_TIME_Relative duration;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ char *payto_uri;
+ enum GNUNET_DB_QueryStatus qs;
+
+ time = GNUNET_TIME_absolute_get ();
+ qs = plugin->get_ready_deposit (plugin->cls,
+ 0,
+ INT32_MAX,
+ &merchant_pub,
+ &payto_uri);
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
+ duration = GNUNET_TIME_absolute_get_duration (time);
+ times = GNUNET_TIME_relative_add (times,
+ duration);
+ duration_sq = duration.rel_value_us * duration.rel_value_us;
+ GNUNET_assert (duration_sq / duration.rel_value_us ==
+ duration.rel_value_us);
+ GNUNET_assert (sqrs + duration_sq >= sqrs);
+ sqrs += duration_sq;
+ }
+
+ /* evaluation of performance */
+ {
+ struct GNUNET_TIME_Relative avg;
+ double avg_dbl;
+ double variance;
+
+ avg = GNUNET_TIME_relative_divide (times,
+ ROUNDS);
+ avg_dbl = avg.rel_value_us;
+ variance = sqrs - (avg_dbl * avg_dbl * ROUNDS);
+ fprintf (stdout,
+ "%8llu ± %6.0f\n",
+ (unsigned long long) avg.rel_value_us,
+ sqrt (variance / (ROUNDS - 1)));
+ }
+ result = 0;
+drop:
+ // GNUNET_break (GNUNET_OK == plugin->drop_tables (plugin->cls));
+cleanup:
+ if (NULL != revealed_coins)
+ {
+ for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
+ {
+ TALER_blinded_denom_sig_free (&revealed_coins[cnt].coin_sig);
+ TALER_blinded_planchet_free (&revealed_coins[cnt].blinded_planchet);
+ }
+ GNUNET_free (revealed_coins);
+ revealed_coins = NULL;
+ }
+ GNUNET_free (new_denom_pubs);
+ for (unsigned int cnt = 0;
+ (NULL != new_dkp) && (cnt < MELT_NEW_COINS) && (NULL != new_dkp[cnt]);
+ cnt++)
+ destroy_denom_key_pair (new_dkp[cnt]);
+ GNUNET_free (new_dkp);
+ for (unsigned int i = 0; i< ROUNDS; i++)
+ {
+ TALER_denom_sig_free (&depos[i].coin.denom_sig);
+ }
+ GNUNET_free (depos);
+ GNUNET_free (ref);
+ TALER_EXCHANGEDB_plugin_unload (plugin);
+ plugin = NULL;
+}
+
+
+int
+main (int argc,
+ char *const argv[])
+{
+ const char *plugin_name;
+ char *config_filename;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ (void) argc;
+ result = -1;
+ if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
+ {
+ GNUNET_break (0);
+ return -1;
+ }
+ GNUNET_log_setup (argv[0],
+ "WARNING",
+ NULL);
+ plugin_name++;
+ {
+ char *testname;
+
+ GNUNET_asprintf (&testname,
+ "test-exchange-db-%s",
+ plugin_name);
+ GNUNET_asprintf (&config_filename,
+ "%s.conf",
+ testname);
+ GNUNET_free (testname);
+ }
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_parse (cfg,
+ config_filename))
+ {
+ GNUNET_break (0);
+ GNUNET_free (config_filename);
+ return 2;
+ }
+ GNUNET_SCHEDULER_run (&run,
+ cfg);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_free (config_filename);
+ return result;
+}
+
+
+/* end of perf_deposits_get_ready.c */
diff --git a/src/exchangedb/perf_get_link_data.c b/src/exchangedb/perf_get_link_data.c
new file mode 100644
index 000000000..817789afc
--- /dev/null
+++ b/src/exchangedb/perf_get_link_data.c
@@ -0,0 +1,543 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2023 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 exchangedb/perf_get_link_data.c
+ * @brief benchmark for get_link_data
+ * @author Joseph Xu
+ */
+#include "platform.h"
+#include "taler_exchangedb_lib.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+#include "math.h"
+
+/**
+ * Report line of error if @a cond is true, and jump to label "drop".
+ */
+#define FAILIF(cond) \
+ do { \
+ if (! (cond)) {break;} \
+ GNUNET_break (0); \
+ goto drop; \
+ } while (0)
+
+
+/**
+ * Initializes @a ptr with random data.
+ */
+#define RND_BLK(ptr) \
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr))
+
+/**
+ * Initializes @a ptr with zeros.
+ */
+#define ZR_BLK(ptr) \
+ memset (ptr, 0, sizeof (*ptr))
+
+#define CURRENCY "EUR"
+#define RSA_KEY_SIZE 1024
+#define ROUNDS 100
+#define NUM_ROWS 1000
+#define MELT_NEW_COINS 5
+#define DENOMINATIONS 5
+#define MELT_NOREVEAL_INDEX 1
+/**
+ * Database plugin under test.
+ */
+static struct TALER_EXCHANGEDB_Plugin *plugin;
+static struct TALER_DenomFeeSet fees;
+/**
+ * Denomination keys used for fresh coins in melt test.
+ */
+static struct DenomKeyPair **new_dkp;
+static int result;
+
+static struct TALER_TransferPrivateKeyP tprivs[TALER_CNC_KAPPA];
+static struct TALER_TransferPublicKeyP tpub;
+struct DenomKeyPair
+{
+ struct TALER_DenominationPrivateKey priv;
+ struct TALER_DenominationPublicKey pub;
+};
+
+
+/**
+ * Destroy a denomination key pair. The key is not necessarily removed from the DB.
+ *
+ * @param dkp the key pair to destroy
+ */
+static void
+destroy_denom_key_pair (struct DenomKeyPair *dkp)
+{
+ TALER_denom_pub_free (&dkp->pub);
+ TALER_denom_priv_free (&dkp->priv);
+ GNUNET_free (dkp);
+}
+
+
+/**
+ * Create a denomination key pair by registering the denomination in the DB.
+ *
+ * @param size the size of the denomination key
+ * @param now time to use for key generation, legal expiration will be 3h later.
+ * @param fees fees to use
+ * @return the denominaiton key pair; NULL upon error
+ */
+static struct DenomKeyPair *
+create_denom_key_pair (unsigned int size,
+ struct GNUNET_TIME_Timestamp now,
+ const struct TALER_Amount *value,
+ const struct TALER_DenomFeeSet *fees)
+{
+ struct DenomKeyPair *dkp;
+ struct TALER_EXCHANGEDB_DenominationKey dki;
+ struct TALER_EXCHANGEDB_DenominationKeyInformation issue2;
+
+ dkp = GNUNET_new (struct DenomKeyPair);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_priv_create (&dkp->priv,
+ &dkp->pub,
+ GNUNET_CRYPTO_BSA_RSA,
+ size));
+ memset (&dki,
+ 0,
+ sizeof (struct TALER_EXCHANGEDB_DenominationKey));
+ dki.denom_pub = dkp->pub;
+ dki.issue.start = now;
+ dki.issue.expire_withdraw
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (
+ now.abs_time,
+ GNUNET_TIME_UNIT_HOURS));
+ dki.issue.expire_deposit
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (
+ now.abs_time,
+ GNUNET_TIME_relative_multiply (
+ GNUNET_TIME_UNIT_HOURS, 2)));
+ dki.issue.expire_legal
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (
+ now.abs_time,
+ GNUNET_TIME_relative_multiply (
+ GNUNET_TIME_UNIT_HOURS, 3)));
+ dki.issue.value = *value;
+ dki.issue.fees = *fees;
+ TALER_denom_pub_hash (&dkp->pub,
+ &dki.issue.denom_hash);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->insert_denomination_info (plugin->cls,
+ &dki.denom_pub,
+ &dki.issue))
+ {
+ GNUNET_break (0);
+ destroy_denom_key_pair (dkp);
+ return NULL;
+ }
+ memset (&issue2, 0, sizeof (issue2));
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->get_denomination_info (plugin->cls,
+ &dki.issue.denom_hash,
+ &issue2))
+ {
+ GNUNET_break (0);
+ destroy_denom_key_pair (dkp);
+ return NULL;
+ }
+ if (0 != GNUNET_memcmp (&dki.issue,
+ &issue2))
+ {
+ GNUNET_break (0);
+ destroy_denom_key_pair (dkp);
+ return NULL;
+ }
+ return dkp;
+}
+
+
+/**
+ * Function called with the session hashes and transfer secret
+ * information for a given coin.
+ *
+ * @param cls closure
+ * @param transfer_pub public transfer key for the session
+ * @param ldl link data for @a transfer_pub
+ */
+static void
+handle_link_data_cb (void *cls,
+ const struct TALER_TransferPublicKeyP *transfer_pub,
+ const struct TALER_EXCHANGEDB_LinkList *ldl)
+{
+ (void) cls;
+ (void) transfer_pub;
+ (void) ldl;
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure with config
+ */
+
+static void
+run (void *cls)
+{
+ struct TALER_EXCHANGEDB_Refresh *refresh;
+ uint64_t melt_serial_id;
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ const uint32_t num_partitions = 10;
+ struct DenomKeyPair *dkp = NULL;
+ struct TALER_EXCHANGEDB_Deposit *depos = NULL;
+ struct TALER_Amount value;
+ struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO;
+ unsigned long long sqrs = 0;
+ struct TALER_EXCHANGEDB_Refund *ref = NULL;
+ unsigned int *perm;
+ unsigned long long duration_sq;
+ struct GNUNET_CRYPTO_BlindingInputValues bi = {
+ .cipher = GNUNET_CRYPTO_BSA_RSA,
+ .rc = 0
+ };
+ struct TALER_ExchangeWithdrawValues alg_values = {
+ .blinding_inputs = &bi
+ };
+
+ ref = GNUNET_new_array (ROUNDS + 1,
+ struct TALER_EXCHANGEDB_Refund);
+ depos = GNUNET_new_array (ROUNDS + 1,
+ struct TALER_EXCHANGEDB_Deposit);
+ refresh = GNUNET_new_array (ROUNDS + 1,
+ struct TALER_EXCHANGEDB_Refresh);
+
+ if (NULL ==
+ (plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
+ {
+ GNUNET_break (0);
+ result = 77;
+ return;
+ }
+ (void) plugin->drop_tables (plugin->cls);
+ if (GNUNET_OK !=
+ plugin->create_tables (plugin->cls,
+ true,
+ num_partitions))
+ {
+ GNUNET_break (0);
+ result = 77;
+ goto cleanup;
+ }
+ if (GNUNET_OK !=
+ plugin->preflight (plugin->cls))
+ {
+ GNUNET_break (0);
+ goto cleanup;
+ }
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":1.000010",
+ &value));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.withdraw));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.deposit));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.refresh));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.refund));
+ {
+ new_dkp = GNUNET_new_array (MELT_NEW_COINS,
+ struct DenomKeyPair *);
+
+ for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
+ {
+ struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
+
+ new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE,
+ now,
+ &value,
+ &fees);
+ GNUNET_assert (NULL != new_dkp[cnt]);
+ }
+ }
+ perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE,
+ NUM_ROWS);
+ FAILIF (GNUNET_OK !=
+ plugin->start (plugin->cls,
+ "Transaction"));
+ for (unsigned int j = 0; j < NUM_ROWS; j++)
+ {
+ union GNUNET_CRYPTO_BlindingSecretP bks;
+ struct TALER_CoinPubHashP c_hash;
+ unsigned int i = perm[j];
+ uint64_t known_coin_id;
+ struct TALER_EXCHANGEDB_CollectableBlindcoin cbc;
+ if (i >= ROUNDS)
+ i = ROUNDS; /* throw-away slot, do not keep around */
+ RND_BLK (&depos[i].coin.coin_pub);
+ ZR_BLK (&cbc);
+ TALER_denom_pub_hash (&new_dkp[(unsigned int) rand ()
+ % MELT_NEW_COINS]->pub,
+ &depos[i].coin.denom_pub_hash);
+
+
+ {
+ struct TALER_EXCHANGEDB_RefreshRevealedCoin
+ revealed_coins[MELT_NEW_COINS];
+
+ for (unsigned int p = 0; p<MELT_NEW_COINS; p++)
+ {
+ struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coin =
+ &revealed_coins[p];
+ struct TALER_BlindedPlanchet *bp = &revealed_coin->blinded_planchet;
+ bp->blinded_message = GNUNET_new (struct GNUNET_CRYPTO_BlindedMessage);
+ struct GNUNET_CRYPTO_RsaBlindedMessage *rp =
+ &bp->blinded_message->details.rsa_blinded_message;
+
+ /* h_coin_ev must be unique, but we only have MELT_NEW_COINS created
+ above for NUM_ROWS iterations; instead of making "all new" coins,
+ we simply randomize the hash here as nobody is checking for consistency
+ anyway ;-) */
+ bp->blinded_message->rc = 1;
+ bp->blinded_message->cipher = GNUNET_CRYPTO_BSA_RSA;
+ rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 (
+ GNUNET_CRYPTO_QUALITY_WEAK,
+ (RSA_KEY_SIZE / 8) - 1);
+ rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size);
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ rp->blinded_msg,
+ rp->blinded_msg_size);
+ TALER_denom_pub_hash (&new_dkp[(unsigned int) rand ()
+ % MELT_NEW_COINS]->pub,
+ &revealed_coin->h_denom_pub);
+ revealed_coin->exchange_vals = alg_values;
+ TALER_coin_ev_hash (bp,
+ &revealed_coin->h_denom_pub,
+ &revealed_coin->coin_envelope_hash);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_sign_blinded (&revealed_coin->coin_sig,
+ &new_dkp[(unsigned
+ int) rand ()
+ % MELT_NEW_COINS]->
+ priv,
+ true,
+ bp));
+ GNUNET_assert (
+ GNUNET_OK ==
+ TALER_denom_sign_blinded (
+ &cbc.sig,
+ &new_dkp[(unsigned int) rand () % MELT_NEW_COINS]->priv,
+ false,
+ bp));
+ }
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_sig_unblind (&depos[i].coin.denom_sig,
+ &cbc.sig,
+ &bks,
+ &c_hash,
+ &alg_values,
+ &new_dkp[(unsigned int) rand ()
+ % MELT_NEW_COINS]->pub));
+ {
+ /* ENSURE_COIN_KNOWN */
+ struct TALER_DenominationHashP dph;
+ struct TALER_AgeCommitmentHash agh;
+ bool zombie_required = false;
+ bool balance_ok;
+
+ FAILIF (TALER_EXCHANGEDB_CKS_ADDED !=
+ plugin->ensure_coin_known (plugin->cls,
+ &depos[i].coin,
+ &known_coin_id,
+ &dph,
+ &agh));
+ /**** INSERT REFRESH COMMITMENTS ****/
+ refresh[i].coin = depos[i].coin;
+ RND_BLK (&refresh[i].coin_sig);
+ RND_BLK (&refresh[i].rc);
+ refresh[i].amount_with_fee = value;
+ refresh[i].noreveal_index = MELT_NOREVEAL_INDEX;
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->do_melt (plugin->cls,
+ NULL,
+ &refresh[i],
+ known_coin_id,
+ &zombie_required,
+ &balance_ok));
+ }
+ /****GET melt_serial_id generated by default****/
+ {
+ struct TALER_EXCHANGEDB_Melt ret_refresh_session;
+
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->get_melt (plugin->cls,
+ &refresh[i].rc,
+ &ret_refresh_session,
+ &melt_serial_id));
+ }
+ /**** INSERT REFRESH_REVEAL + TRANSFER_KEYS *****/
+ {
+ static unsigned int cnt;
+
+ RND_BLK (&tprivs);
+ RND_BLK (&tpub);
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->insert_refresh_reveal (plugin->cls,
+ melt_serial_id,
+ MELT_NEW_COINS,
+ revealed_coins,
+ TALER_CNC_KAPPA - 1,
+ tprivs,
+ &tpub));
+ cnt++;
+ // fprintf (stderr, "CNT: %u - %llu\n", cnt, (unsigned long long) melt_serial_id);
+ }
+ for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
+ {
+ TALER_blinded_denom_sig_free (&revealed_coins[cnt].coin_sig);
+ TALER_blinded_planchet_free (&revealed_coins[cnt].blinded_planchet);
+ }
+
+ /* {
+ struct TALER_CoinSpendPublicKeyP ocp;
+ uint64_t rrc_serial;
+
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->get_old_coin_by_h_blind (plugin->cls,
+ &revealed_coins->coin_envelope_hash,
+ &ocp,
+ &rrc_serial));
+ }*/
+ }
+ if (ROUNDS == i)
+ TALER_denom_sig_free (&depos[i].coin.denom_sig);
+ }
+ /* End of benchmark setup */
+ GNUNET_free (perm);
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+ plugin->commit (plugin->cls));
+ for (unsigned int r = 0; r< ROUNDS; r++)
+ {
+ struct GNUNET_TIME_Absolute time;
+ struct GNUNET_TIME_Relative duration;
+ enum GNUNET_DB_QueryStatus qs;
+ time = GNUNET_TIME_absolute_get ();
+
+ qs = plugin->get_link_data (plugin->cls,
+ &refresh[r].coin.coin_pub,
+ &handle_link_data_cb,
+ NULL);
+ FAILIF (qs < 0);
+
+ duration = GNUNET_TIME_absolute_get_duration (time);
+ times = GNUNET_TIME_relative_add (times,
+ duration);
+ duration_sq = duration.rel_value_us * duration.rel_value_us;
+ GNUNET_assert (duration_sq / duration.rel_value_us ==
+ duration.rel_value_us);
+ GNUNET_assert (sqrs + duration_sq >= sqrs);
+ sqrs += duration_sq;
+ }
+
+ /* evaluation of performance */
+ {
+ struct GNUNET_TIME_Relative avg;
+ double avg_dbl;
+ double variance;
+
+ avg = GNUNET_TIME_relative_divide (times,
+ ROUNDS);
+ avg_dbl = avg.rel_value_us;
+ variance = sqrs - (avg_dbl * avg_dbl * ROUNDS);
+ fprintf (stdout,
+ "%8llu ± %6.0f\n",
+ (unsigned long long) avg.rel_value_us,
+ sqrt (variance / (ROUNDS - 1)));
+ }
+ result = 0;
+drop:
+ // GNUNET_break (GNUNET_OK == plugin->drop_tables (plugin->cls));
+cleanup:
+ if (NULL != dkp)
+ destroy_denom_key_pair (dkp);
+ for (unsigned int cnt = 0;
+ (NULL != new_dkp) && (cnt < MELT_NEW_COINS) && (NULL != new_dkp[cnt]);
+ cnt++)
+ destroy_denom_key_pair (new_dkp[cnt]);
+ GNUNET_free (new_dkp);
+ for (unsigned int i = 0; i< ROUNDS; i++)
+ {
+ TALER_denom_sig_free (&depos[i].coin.denom_sig);
+ }
+ GNUNET_free (depos);
+ GNUNET_free (ref);
+ GNUNET_free (refresh);
+ dkp = NULL;
+ TALER_EXCHANGEDB_plugin_unload (plugin);
+ plugin = NULL;
+}
+
+
+int
+main (int argc,
+ char *const argv[])
+{
+ const char *plugin_name;
+ char *config_filename;
+ char *testname;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ (void) argc;
+ result = -1;
+ if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
+ {
+ GNUNET_break (0);
+ return -1;
+ }
+ GNUNET_log_setup (argv[0],
+ "WARNING",
+ NULL);
+ plugin_name++;
+ (void) GNUNET_asprintf (&testname,
+ "test-exchange-db-%s",
+ plugin_name);
+ (void) GNUNET_asprintf (&config_filename,
+ "%s.conf",
+ testname);
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_parse (cfg,
+ config_filename))
+ {
+ GNUNET_break (0);
+ GNUNET_free (config_filename);
+ GNUNET_free (testname);
+ return 2;
+ }
+ GNUNET_SCHEDULER_run (&run,
+ cfg);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_free (config_filename);
+ GNUNET_free (testname);
+ return result;
+}
+
+
+/* end of test_exchangedb_by_j.c */
diff --git a/src/exchangedb/perf_exchangedb_reserves_in_insert.c b/src/exchangedb/perf_reserves_in_insert.c
index fc2a00089..09c4a43c5 100644
--- a/src/exchangedb/perf_exchangedb_reserves_in_insert.c
+++ b/src/exchangedb/perf_reserves_in_insert.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2022 Taler Systems SA
+ Copyright (C) 2014-2023 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
@@ -14,8 +14,8 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file exchangedb/test_exchangedb_by_j.c
- * @brief test cases for DB interaction functions
+ * @file exchangedb/perf_reserves_in_insert.c
+ * @brief benchmark for 'reserves_in_insert'
* @author Joseph Xu
*/
#include "platform.h"
@@ -23,6 +23,7 @@
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+
/**
* Global result from the testcase.
*/
@@ -33,7 +34,7 @@ static int result;
*/
#define FAILIF(cond) \
do { \
- if (! (cond)) { break;} \
+ if (! (cond)) {break;} \
GNUNET_break (0); \
goto drop; \
} while (0)
@@ -51,6 +52,10 @@ static int result;
#define ZR_BLK(ptr) \
memset (ptr, 0, sizeof (*ptr))
+/**
+ * How many rounds do we average over?
+ */
+#define ROUNDS 5
/**
* Currency we use. Must match test-exchange-db-*.conf.
@@ -73,6 +78,11 @@ run (void *cls)
{
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
const uint32_t num_partitions = 10;
+ static unsigned int batches[] = {1, 1, 2, 3, 4, 16, 32, 64, 128, 256, 512,
+ 1024, 1024, 512, 256, 128, 64, 32,
+ 16, 4, 3, 2, 1 };
+ struct GNUNET_TIME_Relative times[sizeof (batches) / sizeof(*batches)];
+ unsigned long long sqrs[sizeof (batches) / sizeof(*batches)];
if (NULL ==
(plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
@@ -91,46 +101,78 @@ run (void *cls)
result = 77;
goto cleanup;
}
- for (unsigned int i = 0; i< 8; i++)
+
+ memset (times, 0, sizeof (times));
+ memset (sqrs, 0, sizeof (sqrs));
+ for (unsigned int r = 0; r < ROUNDS; r++)
{
- static unsigned int batches[] = {1, 1, 0, 2, 4, 16, 64, 256};
- const char *sndr = "payto://x-taler-bank/localhost:8080/1";
- struct TALER_Amount value;
- unsigned int batch_size = batches[i];
- struct GNUNET_TIME_Absolute now;
- struct GNUNET_TIME_Timestamp ts;
- struct GNUNET_TIME_Relative duration;
- struct TALER_ReservePublicKeyP reserve_pub;
-
- GNUNET_assert (GNUNET_OK ==
- TALER_string_to_amount (CURRENCY ":1.000010",
- &value));
- now = GNUNET_TIME_absolute_get ();
- ts = GNUNET_TIME_timestamp_get ();
- for (unsigned int r = 0; r<10; r++)
+ for (unsigned int i = 0;
+ i< sizeof(batches) / sizeof(*batches);
+ i++)
{
- plugin->start (plugin->cls,
- "test_by_exchange_j");
- for (unsigned int k = 0; k<batch_size; k++)
+ unsigned int lcm = batches[i];
+ struct TALER_Amount value;
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_TIME_Timestamp ts;
+ unsigned long long duration_sq;
+ struct GNUNET_TIME_Relative duration;
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":1.000010",
+ &value));
+ now = GNUNET_TIME_absolute_get ();
+ ts = GNUNET_TIME_timestamp_get ();
{
- RND_BLK (&reserve_pub);
- FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ const char *sndr = "payto://x-taler-bank/localhost:8080/1";
+ struct TALER_ReservePublicKeyP reserve_pubs[lcm];
+ struct TALER_EXCHANGEDB_ReserveInInfo reserves[lcm];
+ enum GNUNET_DB_QueryStatus results[lcm];
+
+ for (unsigned int k = 0; k<lcm; k++)
+ {
+ RND_BLK (&reserve_pubs[k]);
+ reserves[k].reserve_pub = &reserve_pubs[k];
+ reserves[k].balance = &value;
+ reserves[k].execution_time = ts;
+ reserves[k].sender_account_details = sndr;
+ reserves[k].exchange_account_name = "name";
+ reserves[k].wire_reference = k;
+ }
+ FAILIF (lcm !=
plugin->reserves_in_insert (plugin->cls,
- &reserve_pub,
- &value,
- ts,
- sndr,
- "section",
- 4));
+ reserves,
+ lcm,
+ results));
}
- plugin->commit (plugin->cls);
- }
- duration = GNUNET_TIME_absolute_get_duration (now);
+ duration = GNUNET_TIME_absolute_get_duration (now);
+ times[i] = GNUNET_TIME_relative_add (times[i],
+ duration);
+ duration_sq = duration.rel_value_us * duration.rel_value_us;
+ GNUNET_assert (duration_sq / duration.rel_value_us ==
+ duration.rel_value_us);
+ GNUNET_assert (sqrs[i] + duration_sq >= sqrs[i]);
+ sqrs[i] += duration_sq;
+ } /* for 'i' batch size */
+ } /* for 'r' ROUNDS */
+
+ for (unsigned int i = 0;
+ i< sizeof(batches) / sizeof(*batches);
+ i++)
+ {
+ unsigned int lcm = batches[i];
+ struct GNUNET_TIME_Relative avg;
+ double avg_dbl;
+ double variance;
+
+ avg = GNUNET_TIME_relative_divide (times[i],
+ ROUNDS);
+ avg_dbl = avg.rel_value_us;
+ variance = sqrs[i] - (avg_dbl * avg_dbl * ROUNDS);
fprintf (stdout,
- "for a batchsize equal to %d it took %s\n",
- batch_size,
- GNUNET_STRINGS_relative_time_to_string (duration,
- GNUNET_NO) );
+ "Batch[%4u]: %8llu us/entry ± %6.0f\n",
+ batches[i],
+ (unsigned long long) avg.rel_value_us / lcm,
+ sqrt (variance / lcm / (ROUNDS - 1)));
}
result = 0;
drop:
@@ -150,7 +192,6 @@ main (int argc,
char *config_filename;
char *testname;
struct GNUNET_CONFIGURATION_Handle *cfg;
-
(void) argc;
result = -1;
if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
@@ -158,6 +199,7 @@ main (int argc,
GNUNET_break (0);
return -1;
}
+
GNUNET_log_setup (argv[0],
"WARNING",
NULL);
@@ -168,9 +210,6 @@ main (int argc,
(void) GNUNET_asprintf (&config_filename,
"%s.conf",
testname);
- fprintf (stdout,
- "Using config: %s\n",
- config_filename);
cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK !=
GNUNET_CONFIGURATION_parse (cfg,
@@ -190,4 +229,4 @@ main (int argc,
}
-/* end of test_exchangedb_by_j.c */
+/* end of perf_reserves_in_insert.c */
diff --git a/src/exchangedb/perf_select_refunds_by_coin.c b/src/exchangedb/perf_select_refunds_by_coin.c
new file mode 100644
index 000000000..84825d6d7
--- /dev/null
+++ b/src/exchangedb/perf_select_refunds_by_coin.c
@@ -0,0 +1,619 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2023 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 exchangedb/perf_select_refunds_by_coin.c
+ * @brief benchmark for select_refunds_by_coin
+ * @author Joseph Xu
+ */
+#include "platform.h"
+#include "taler_exchangedb_lib.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+#include "math.h"
+
+/**
+ * Global result from the testcase.
+ */
+static int result;
+
+/**
+ * Report line of error if @a cond is true, and jump to label "drop".
+ */
+#define FAILIF(cond) \
+ do { \
+ if (! (cond)) {break;} \
+ GNUNET_break (0); \
+ goto drop; \
+ } while (0)
+
+/**
+ * Initializes @a ptr with random data.
+ */
+#define RND_BLK(ptr) \
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, \
+ sizeof (*ptr))
+
+/**
+ * Initializes @a ptr with zeros.
+ */
+#define ZR_BLK(ptr) \
+ memset (ptr, 0, sizeof (*ptr))
+
+/**
+ * Currency we use. Must match test-exchange-db-*.conf.
+ */
+#define CURRENCY "EUR"
+#define RSA_KEY_SIZE 1024
+#define ROUNDS 100
+#define NUM_ROWS 1000
+#define MELT_NEW_COINS 5
+#define MELT_NOREVEAL_INDEX 1
+
+/**
+ * Database plugin under test.
+ */
+static struct TALER_EXCHANGEDB_Plugin *plugin;
+
+static struct TALER_DenomFeeSet fees;
+
+static struct TALER_MerchantWireHashP h_wire_wt;
+
+static struct DenomKeyPair **new_dkp;
+
+static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins;
+
+struct DenomKeyPair
+{
+ struct TALER_DenominationPrivateKey priv;
+ struct TALER_DenominationPublicKey pub;
+};
+
+
+/**
+ * Destroy a denomination key pair. The key is not necessarily removed from the DB.
+ *
+ * @param dkp the key pair to destroy
+ */
+static void
+destroy_denom_key_pair (struct DenomKeyPair *dkp)
+{
+ TALER_denom_pub_free (&dkp->pub);
+ TALER_denom_priv_free (&dkp->priv);
+ GNUNET_free (dkp);
+}
+
+
+/**
+ * Create a denomination key pair by registering the denomination in the DB.
+ *
+ * @param size the size of the denomination key
+ * @param now time to use for key generation, legal expiration will be 3h later.
+ * @param fees fees to use
+ * @return the denominaiton key pair; NULL upon error
+ */
+static struct DenomKeyPair *
+create_denom_key_pair (unsigned int size,
+ struct GNUNET_TIME_Timestamp now,
+ const struct TALER_Amount *value,
+ const struct TALER_DenomFeeSet *fees)
+{
+ struct DenomKeyPair *dkp;
+ struct TALER_EXCHANGEDB_DenominationKey dki;
+ struct TALER_EXCHANGEDB_DenominationKeyInformation issue2;
+
+ dkp = GNUNET_new (struct DenomKeyPair);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_priv_create (&dkp->priv,
+ &dkp->pub,
+ GNUNET_CRYPTO_BSA_RSA,
+ size));
+ memset (&dki,
+ 0,
+ sizeof (struct TALER_EXCHANGEDB_DenominationKey));
+ dki.denom_pub = dkp->pub;
+ dki.issue.start = now;
+ dki.issue.expire_withdraw
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (
+ now.abs_time,
+ GNUNET_TIME_UNIT_HOURS));
+ dki.issue.expire_deposit
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (
+ now.abs_time,
+ GNUNET_TIME_relative_multiply (
+ GNUNET_TIME_UNIT_HOURS, 2)));
+ dki.issue.expire_legal
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (
+ now.abs_time,
+ GNUNET_TIME_relative_multiply (
+ GNUNET_TIME_UNIT_HOURS, 3)));
+ dki.issue.value = *value;
+ dki.issue.fees = *fees;
+ TALER_denom_pub_hash (&dkp->pub,
+ &dki.issue.denom_hash);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->insert_denomination_info (plugin->cls,
+ &dki.denom_pub,
+ &dki.issue))
+ {
+ GNUNET_break (0);
+ destroy_denom_key_pair (dkp);
+ return NULL;
+ }
+ memset (&issue2, 0, sizeof (issue2));
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->get_denomination_info (plugin->cls,
+ &dki.issue.denom_hash,
+ &issue2))
+ {
+ GNUNET_break (0);
+ destroy_denom_key_pair (dkp);
+ return NULL;
+ }
+ if (0 != GNUNET_memcmp (&dki.issue,
+ &issue2))
+ {
+ GNUNET_break (0);
+ destroy_denom_key_pair (dkp);
+ return NULL;
+ }
+ return dkp;
+}
+
+
+/**
+ * Callback invoked with information about refunds applicable
+ * to a particular coin.
+ *
+ * @param cls closure with the `struct TALER_EXCHANGEDB_Refund *` we expect to get
+ * @param amount_with_fee amount being refunded
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+static enum GNUNET_GenericReturnValue
+check_refund_cb (void *cls,
+ const struct TALER_Amount *amount_with_fee)
+{
+ const struct TALER_EXCHANGEDB_Refund *refund = cls;
+
+ if (0 != TALER_amount_cmp (amount_with_fee,
+ &refund->details.refund_amount))
+ {
+ GNUNET_break (0);
+ result = 66;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure with config
+ */
+
+static void
+run (void *cls)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ const uint32_t num_partitions = 10;
+ struct GNUNET_TIME_Timestamp ts;
+ struct TALER_EXCHANGEDB_CoinDepositInformation *depos = NULL;
+ struct GNUNET_TIME_Timestamp deadline;
+ struct TALER_Amount value;
+ union GNUNET_CRYPTO_BlindingSecretP bks;
+ struct TALER_EXCHANGEDB_CollectableBlindcoin cbc;
+ struct GNUNET_CRYPTO_BlindingInputValues bi = {
+ .cipher = GNUNET_CRYPTO_BSA_RSA,
+ .rc = 0
+ };
+ struct TALER_ExchangeWithdrawValues alg_values = {
+ .blinding_inputs = &bi
+ };
+ struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO;
+ unsigned long long sqrs = 0;
+ struct TALER_EXCHANGEDB_Refund *ref = NULL;
+ unsigned int *perm;
+ unsigned long long duration_sq;
+ struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin;
+ struct TALER_DenominationPublicKey *new_denom_pubs = NULL;
+ unsigned int count = 0;
+
+ ref = GNUNET_new_array (ROUNDS + 1,
+ struct TALER_EXCHANGEDB_Refund);
+ depos = GNUNET_new_array (ROUNDS + 1,
+ struct TALER_EXCHANGEDB_CoinDepositInformation);
+ ZR_BLK (&cbc);
+
+ if (NULL ==
+ (plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
+ {
+ GNUNET_break (0);
+ result = 77;
+ return;
+ }
+ (void) plugin->drop_tables (plugin->cls);
+ if (GNUNET_OK !=
+ plugin->create_tables (plugin->cls,
+ true,
+ num_partitions))
+ {
+ GNUNET_break (0);
+ result = 77;
+ goto cleanup;
+ }
+ if (GNUNET_OK !=
+ plugin->preflight (plugin->cls))
+ {
+ GNUNET_break (0);
+ goto cleanup;
+ }
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":1.000010",
+ &value));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.withdraw));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.deposit));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.refresh));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fees.refund));
+ GNUNET_assert (NUM_ROWS >= ROUNDS);
+
+ ts = GNUNET_TIME_timestamp_get ();
+ deadline = GNUNET_TIME_timestamp_get ();
+ {
+ new_dkp = GNUNET_new_array (MELT_NEW_COINS,
+ struct DenomKeyPair *);
+ new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS,
+ struct TALER_DenominationPublicKey);
+ revealed_coins
+ = GNUNET_new_array (MELT_NEW_COINS,
+ struct TALER_EXCHANGEDB_RefreshRevealedCoin);
+ for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
+ {
+ struct GNUNET_TIME_Timestamp now;
+ struct GNUNET_CRYPTO_RsaBlindedMessage *rp;
+ struct TALER_BlindedPlanchet *bp;
+
+ now = GNUNET_TIME_timestamp_get ();
+ new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE,
+ now,
+ &value,
+ &fees);
+ GNUNET_assert (NULL != new_dkp[cnt]);
+ new_denom_pubs[cnt] = new_dkp[cnt]->pub;
+ ccoin = &revealed_coins[cnt];
+ bp = &ccoin->blinded_planchet;
+ bp->blinded_message = GNUNET_new (struct GNUNET_CRYPTO_BlindedMessage);
+ bp->blinded_message->rc = 1;
+ bp->blinded_message->cipher = GNUNET_CRYPTO_BSA_RSA;
+ rp = &bp->blinded_message->details.rsa_blinded_message;
+ rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 (
+ GNUNET_CRYPTO_QUALITY_WEAK,
+ (RSA_KEY_SIZE / 8) - 1);
+ rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size);
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ rp->blinded_msg,
+ rp->blinded_msg_size);
+ TALER_denom_pub_hash (&new_dkp[cnt]->pub,
+ &ccoin->h_denom_pub);
+ ccoin->exchange_vals = alg_values;
+ TALER_coin_ev_hash (bp,
+ &ccoin->h_denom_pub,
+ &ccoin->coin_envelope_hash);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_sign_blinded (&ccoin->coin_sig,
+ &new_dkp[cnt]->priv,
+ true,
+ bp));
+ TALER_coin_ev_hash (bp,
+ &cbc.denom_pub_hash,
+ &cbc.h_coin_envelope);
+ GNUNET_assert (
+ GNUNET_OK ==
+ TALER_denom_sign_blinded (
+ &cbc.sig,
+ &new_dkp[cnt]->priv,
+ false,
+ bp));
+ }
+ }
+
+ perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE,
+ NUM_ROWS);
+ FAILIF (GNUNET_OK !=
+ plugin->start (plugin->cls,
+ "Transaction"));
+ for (unsigned int j = 0; j< NUM_ROWS; j++)
+ {
+ unsigned int i = perm[j];
+ unsigned int k = (unsigned int) rand () % 5;
+ struct TALER_CoinPubHashP c_hash;
+ uint64_t known_coin_id;
+ struct TALER_EXCHANGEDB_CoinDepositInformation *cdi
+ = &depos[i];
+ struct TALER_EXCHANGEDB_BatchDeposit bd = {
+ .cdis = cdi,
+ .num_cdis = 1,
+ .wallet_timestamp = ts,
+ .refund_deadline = deadline,
+ .wire_deadline = deadline,
+ .receiver_wire_account
+ = "payto://iban/DE67830654080004822650?receiver-name=Test"
+ };
+
+ if (i >= ROUNDS)
+ i = ROUNDS; /* throw-away slot, do not keep around */
+ RND_BLK (&bd.merchant_pub);
+ RND_BLK (&bd.h_contract_terms);
+ RND_BLK (&bd.wire_salt);
+ TALER_merchant_wire_signature_hash (
+ bd.receiver_wire_account,
+ &bd.wire_salt,
+ &h_wire_wt);
+ RND_BLK (&cdi->coin.coin_pub);
+ RND_BLK (&cdi->csig);
+ RND_BLK (&c_hash);
+ TALER_denom_pub_hash (&new_dkp[k]->pub,
+ &cdi->coin.denom_pub_hash);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_sig_unblind (&cdi->coin.denom_sig,
+ &cbc.sig,
+ &bks,
+ &c_hash,
+ &alg_values,
+ &new_dkp[k]->pub));
+ cdi->amount_with_fee = value;
+
+ {
+ struct TALER_DenominationHashP dph;
+ struct TALER_AgeCommitmentHash agh;
+
+ FAILIF (TALER_EXCHANGEDB_CKS_ADDED !=
+ plugin->ensure_coin_known (plugin->cls,
+ &cdi->coin,
+ &known_coin_id,
+ &dph,
+ &agh));
+ }
+ {
+ struct GNUNET_TIME_Timestamp now;
+ bool balance_ok;
+ uint32_t bad_idx;
+ bool in_conflict;
+
+ now = GNUNET_TIME_timestamp_get ();
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->do_deposit (plugin->cls,
+ &bd,
+ &now,
+ &balance_ok,
+ &bad_idx,
+ &in_conflict));
+ }
+ {
+ bool not_found;
+ bool refund_ok;
+ bool gone;
+ bool conflict;
+ unsigned int refund_percent = 0;
+ switch (refund_percent)
+ {
+ case 2: // 100% refund
+ ref[i].coin = depos[i].coin;
+ ref[i].details.merchant_pub = bd.merchant_pub;
+ RND_BLK (&ref[i].details.merchant_sig);
+ ref[i].details.h_contract_terms = bd.h_contract_terms;
+ ref[i].coin.coin_pub = depos[i].coin.coin_pub;
+ ref[i].details.rtransaction_id = i;
+ ref[i].details.refund_amount = value;
+ ref[i].details.refund_fee = fees.refund;
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->do_refund (plugin->cls,
+ &ref[i],
+ &fees.deposit,
+ known_coin_id,
+ &not_found,
+ &refund_ok,
+ &gone,
+ &conflict));
+ break;
+ case 1:// 10% refund
+ if (count < (NUM_ROWS / 10))
+ {
+ ref[i].coin = depos[i].coin;
+ ref[i].details.merchant_pub = bd.merchant_pub;
+ RND_BLK (&ref[i].details.merchant_sig);
+ ref[i].details.h_contract_terms = bd.h_contract_terms;
+ ref[i].coin.coin_pub = depos[i].coin.coin_pub;
+ ref[i].details.rtransaction_id = i;
+ ref[i].details.refund_amount = value;
+ ref[i].details.refund_fee = fees.refund;
+ }
+ else
+ {
+ ref[i].coin = depos[i].coin;
+ RND_BLK (&ref[i].details.merchant_pub);
+ RND_BLK (&ref[i].details.merchant_sig);
+ RND_BLK (&ref[i].details.h_contract_terms);
+ RND_BLK (&ref[i].coin.coin_pub);
+ ref[i].details.rtransaction_id = i;
+ ref[i].details.refund_amount = value;
+ ref[i].details.refund_fee = fees.refund;
+ }
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->do_refund (plugin->cls,
+ &ref[i],
+ &fees.deposit,
+ known_coin_id,
+ &not_found,
+ &refund_ok,
+ &gone,
+ &conflict));
+ count++;
+ break;
+ case 0:// no refund
+ ref[i].coin = depos[i].coin;
+ RND_BLK (&ref[i].details.merchant_pub);
+ RND_BLK (&ref[i].details.merchant_sig);
+ RND_BLK (&ref[i].details.h_contract_terms);
+ RND_BLK (&ref[i].coin.coin_pub);
+ ref[i].details.rtransaction_id = i;
+ ref[i].details.refund_amount = value;
+ ref[i].details.refund_fee = fees.refund;
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->do_refund (plugin->cls,
+ &ref[i],
+ &fees.deposit,
+ known_coin_id,
+ &not_found,
+ &refund_ok,
+ &gone,
+ &conflict));
+ break;
+ }/* END OF SWITCH CASE */
+ }
+ if (ROUNDS == i)
+ TALER_denom_sig_free (&depos[i].coin.denom_sig);
+ }
+ /* End of benchmark setup */
+ GNUNET_free (perm);
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+ plugin->commit (plugin->cls));
+ for (unsigned int r = 0; r < ROUNDS; r++)
+ {
+ struct GNUNET_TIME_Absolute time;
+ struct GNUNET_TIME_Relative duration;
+
+ time = GNUNET_TIME_absolute_get ();
+ FAILIF (0 >
+ plugin->select_refunds_by_coin (plugin->cls,
+ &ref[r].coin.coin_pub,
+ &ref[r].details.merchant_pub,
+ &ref[r].details.h_contract_terms,
+ &check_refund_cb,
+ &ref[r]));
+ duration = GNUNET_TIME_absolute_get_duration (time);
+ times = GNUNET_TIME_relative_add (times,
+ duration);
+ duration_sq = duration.rel_value_us * duration.rel_value_us;
+ GNUNET_assert (duration_sq / duration.rel_value_us ==
+ duration.rel_value_us);
+ GNUNET_assert (sqrs + duration_sq >= sqrs);
+ sqrs += duration_sq;
+ }
+ /* evaluation of performance */
+ {
+ struct GNUNET_TIME_Relative avg;
+ double avg_dbl;
+ double variance;
+
+ avg = GNUNET_TIME_relative_divide (times,
+ ROUNDS);
+ avg_dbl = avg.rel_value_us;
+ variance = sqrs - (avg_dbl * avg_dbl * ROUNDS);
+ fprintf (stdout,
+ "%8llu ± %6.0f\n",
+ (unsigned long long) avg.rel_value_us,
+ sqrt (variance / (ROUNDS - 1)));
+ }
+ result = 0;
+drop:
+ GNUNET_break (GNUNET_OK ==
+ plugin->drop_tables (plugin->cls));
+cleanup:
+ if (NULL != revealed_coins)
+ {
+ for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
+ {
+ TALER_blinded_denom_sig_free (&revealed_coins[cnt].coin_sig);
+ TALER_blinded_planchet_free (&revealed_coins[cnt].blinded_planchet);
+ }
+ GNUNET_free (revealed_coins);
+ revealed_coins = NULL;
+ }
+ GNUNET_free (new_denom_pubs);
+ for (unsigned int cnt = 0;
+ (NULL != new_dkp) && (cnt < MELT_NEW_COINS) && (NULL != new_dkp[cnt]);
+ cnt++)
+ destroy_denom_key_pair (new_dkp[cnt]);
+ GNUNET_free (new_dkp);
+ for (unsigned int i = 0; i< ROUNDS + 1; i++)
+ {
+ TALER_denom_sig_free (&depos[i].coin.denom_sig);
+ }
+ GNUNET_free (depos);
+ GNUNET_free (ref);
+ TALER_EXCHANGEDB_plugin_unload (plugin);
+ plugin = NULL;
+}
+
+
+int
+main (int argc,
+ char *const argv[])
+{
+ const char *plugin_name;
+ char *config_filename;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ (void) argc;
+ result = -1;
+ if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
+ {
+ GNUNET_break (0);
+ return -1;
+ }
+ GNUNET_log_setup (argv[0],
+ "WARNING",
+ NULL);
+ plugin_name++;
+ {
+ char *testname;
+
+ GNUNET_asprintf (&testname,
+ "test-exchange-db-%s",
+ plugin_name);
+ GNUNET_asprintf (&config_filename,
+ "%s.conf",
+ testname);
+ GNUNET_free (testname);
+ }
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_parse (cfg,
+ config_filename))
+ {
+ GNUNET_break (0);
+ GNUNET_free (config_filename);
+ return 2;
+ }
+ GNUNET_SCHEDULER_run (&run,
+ cfg);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_free (config_filename);
+ return result;
+}
+
+
+/* end of perf_select_refunds_by_coin.c */
diff --git a/src/exchangedb/pg_abort_shard.c b/src/exchangedb/pg_abort_shard.c
index de10f7d3f..d04680a81 100644
--- a/src/exchangedb/pg_abort_shard.c
+++ b/src/exchangedb/pg_abort_shard.c
@@ -28,9 +28,9 @@
enum GNUNET_DB_QueryStatus
TEH_PG_abort_shard (void *cls,
- const char *job_name,
- uint64_t start_row,
- uint64_t end_row)
+ const char *job_name,
+ uint64_t start_row,
+ uint64_t end_row)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -40,14 +40,13 @@ TEH_PG_abort_shard (void *cls,
GNUNET_PQ_query_param_end
};
-
PREPARE (pg,
"abort_shard",
"UPDATE work_shards"
" SET last_attempt=0"
- " WHERE job_name = $1 "
- " AND start_row = $2 "
- " AND end_row = $3;");
+ " WHERE job_name=$1"
+ " AND start_row=$2"
+ " AND end_row=$3;");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"abort_shard",
params);
diff --git a/src/exchangedb/pg_abort_shard.h b/src/exchangedb/pg_abort_shard.h
index 070b48dab..e52ace5f6 100644
--- a/src/exchangedb/pg_abort_shard.h
+++ b/src/exchangedb/pg_abort_shard.h
@@ -36,7 +36,8 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_abort_shard (void *cls,
- const char *job_name,
- uint64_t start_row,
+ const char *job_name,
+ uint64_t start_row,
uint64_t end_row);
+
#endif
diff --git a/src/exchangedb/pg_add_denomination_key.c b/src/exchangedb/pg_add_denomination_key.c
index e115a44ec..eb50304d3 100644
--- a/src/exchangedb/pg_add_denomination_key.c
+++ b/src/exchangedb/pg_add_denomination_key.c
@@ -43,11 +43,16 @@ TEH_PG_add_denomination_key (
GNUNET_PQ_query_param_timestamp (&meta->expire_withdraw),
GNUNET_PQ_query_param_timestamp (&meta->expire_deposit),
GNUNET_PQ_query_param_timestamp (&meta->expire_legal),
- TALER_PQ_query_param_amount (&meta->value),
- TALER_PQ_query_param_amount (&meta->fees.withdraw),
- TALER_PQ_query_param_amount (&meta->fees.deposit),
- TALER_PQ_query_param_amount (&meta->fees.refresh),
- TALER_PQ_query_param_amount (&meta->fees.refund),
+ TALER_PQ_query_param_amount (pg->conn,
+ &meta->value),
+ TALER_PQ_query_param_amount (pg->conn,
+ &meta->fees.withdraw),
+ TALER_PQ_query_param_amount (pg->conn,
+ &meta->fees.deposit),
+ TALER_PQ_query_param_amount (pg->conn,
+ &meta->fees.refresh),
+ TALER_PQ_query_param_amount (pg->conn,
+ &meta->fees.refund),
GNUNET_PQ_query_param_uint32 (&meta->age_mask.bits),
GNUNET_PQ_query_param_end
};
@@ -56,8 +61,6 @@ TEH_PG_add_denomination_key (
GNUNET_assert (GNUNET_YES ==
TALER_denom_fee_check_currency (meta->value.currency,
&meta->fees));
- /* Used in #postgres_insert_denomination_info() and
- #postgres_add_denomination_key() */
PREPARE (pg,
"denomination_insert",
"INSERT INTO denominations "
@@ -68,22 +71,16 @@ TEH_PG_add_denomination_key (
",expire_withdraw"
",expire_deposit"
",expire_legal"
- ",coin_val" /* value of this denom */
- ",coin_frac" /* fractional value of this denom */
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refund_val"
- ",fee_refund_frac"
+ ",coin" /* value of this denom */
+ ",fee_withdraw"
+ ",fee_deposit"
+ ",fee_refresh"
+ ",fee_refund"
",age_mask"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
- " $11, $12, $13, $14, $15, $16, $17, $18);");
+ " $11, $12, $13);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"denomination_insert",
iparams);
}
-
diff --git a/src/exchangedb/pg_add_policy_fulfillment_proof.c b/src/exchangedb/pg_add_policy_fulfillment_proof.c
index bb06206ae..93d070712 100644
--- a/src/exchangedb/pg_add_policy_fulfillment_proof.c
+++ b/src/exchangedb/pg_add_policy_fulfillment_proof.c
@@ -55,6 +55,7 @@ TEH_PG_add_policy_fulfillment_proof (
enum GNUNET_DB_QueryStatus qs;
struct PostgresClosure *pg = cls;
size_t count = fulfillment->details_count;
+ /* FIXME: this seems to be prone to VLA attacks */
struct GNUNET_HashCode hcs[count];
/* Create the sorted policy_hash_codes */
@@ -84,8 +85,7 @@ TEH_PG_add_policy_fulfillment_proof (
GNUNET_PQ_query_param_timestamp (&fulfillment->timestamp),
TALER_PQ_query_param_json (fulfillment->proof),
GNUNET_PQ_query_param_auto_from_type (&fulfillment->h_proof),
- GNUNET_PQ_query_param_fixed_size (hcs,
- count * sizeof(struct GNUNET_HashCode)),
+ TALER_PQ_query_param_array_hash_code (count, hcs, pg->conn),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@@ -94,7 +94,15 @@ TEH_PG_add_policy_fulfillment_proof (
GNUNET_PQ_result_spec_end
};
-
+ PREPARE (pg,
+ "insert_proof_into_policy_fulfillments",
+ "INSERT INTO policy_fulfillments"
+ "(fulfillment_timestamp"
+ ",fulfillment_proof"
+ ",h_fulfillment_proof"
+ ",policy_hash_codes"
+ ") VALUES ($1, $2, $3, $4)"
+ " ON CONFLICT DO NOTHING;");
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"insert_proof_into_policy_fulfillments",
params,
@@ -112,14 +120,28 @@ TEH_PG_add_policy_fulfillment_proof (
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&pos->hash_code),
GNUNET_PQ_query_param_timestamp (&pos->deadline),
- TALER_PQ_query_param_amount (&pos->commitment),
- TALER_PQ_query_param_amount (&pos->accumulated_total),
- TALER_PQ_query_param_amount (&pos->policy_fee),
- TALER_PQ_query_param_amount (&pos->transferable_amount),
+ TALER_PQ_query_param_amount (pg->conn,
+ &pos->commitment),
+ TALER_PQ_query_param_amount (pg->conn,
+ &pos->accumulated_total),
+ TALER_PQ_query_param_amount (pg->conn,
+ &pos->policy_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ &pos->transferable_amount),
GNUNET_PQ_query_param_auto_from_type (&pos->fulfillment_state),
GNUNET_PQ_query_param_end
};
+ PREPARE (pg,
+ "update_policy_details",
+ "UPDATE policy_details SET"
+ " deadline=$2"
+ ",commitment=$3"
+ ",accumulated_total=$4"
+ ",fee=$5"
+ ",transferable=$6"
+ ",fulfillment_state=$7"
+ " WHERE policy_hash_code=$1;");
qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
"update_policy_details",
params);
@@ -128,5 +150,10 @@ TEH_PG_add_policy_fulfillment_proof (
}
}
+ /*
+ * FIXME[oec]-#7999: When all policies of a deposit are fulfilled,
+ * unblock it and trigger a wire-transfer.
+ */
+
return qs;
}
diff --git a/src/exchangedb/pg_aggregate.c b/src/exchangedb/pg_aggregate.c
index f1c4d6776..ba03e4a9c 100644
--- a/src/exchangedb/pg_aggregate.c
+++ b/src/exchangedb/pg_aggregate.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022, 2023 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
@@ -22,9 +22,12 @@
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
+#include "pg_compute_shard.h"
+#include "pg_event_notify.h"
#include "pg_aggregate.h"
#include "pg_helper.h"
+
enum GNUNET_DB_QueryStatus
TEH_PG_aggregate (
void *cls,
@@ -34,35 +37,14 @@ TEH_PG_aggregate (
struct TALER_Amount *total)
{
struct PostgresClosure *pg = cls;
+ uint64_t deposit_shard = TEH_PG_compute_shard (merchant_pub);
struct GNUNET_TIME_Absolute now = {0};
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_absolute_time (&now),
- GNUNET_PQ_query_param_auto_from_type (merchant_pub),
- GNUNET_PQ_query_param_auto_from_type (h_payto),
- GNUNET_PQ_query_param_auto_from_type (wtid),
- GNUNET_PQ_query_param_end
- };
uint64_t sum_deposit_value;
uint64_t sum_deposit_frac;
uint64_t sum_refund_value;
uint64_t sum_refund_frac;
uint64_t sum_fee_value;
uint64_t sum_fee_frac;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("sum_deposit_value",
- &sum_deposit_value),
- GNUNET_PQ_result_spec_uint64 ("sum_deposit_fraction",
- &sum_deposit_frac),
- GNUNET_PQ_result_spec_uint64 ("sum_refund_value",
- &sum_refund_value),
- GNUNET_PQ_result_spec_uint64 ("sum_refund_fraction",
- &sum_refund_frac),
- GNUNET_PQ_result_spec_uint64 ("sum_fee_value",
- &sum_fee_value),
- GNUNET_PQ_result_spec_uint64 ("sum_fee_fraction",
- &sum_fee_frac),
- GNUNET_PQ_result_spec_end
- };
enum GNUNET_DB_QueryStatus qs;
struct TALER_Amount sum_deposit;
struct TALER_Amount sum_refund;
@@ -71,71 +53,63 @@ TEH_PG_aggregate (
now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
pg->aggregator_shift);
-
- /* Used in #postgres_aggregate() */
PREPARE (pg,
"aggregate",
- "WITH rdy AS (" /* find deposits ready by merchant */
- " SELECT"
- " coin_pub"
- " FROM deposits_for_matching"
- " WHERE refund_deadline<$1" /* filter by shard, only actually executable deposits */
- " AND merchant_pub=$2" /* filter by target merchant */
- " ORDER BY refund_deadline ASC" /* ordering is not critical */
- " LIMIT "
- TALER_QUOTE (TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT) /* limits transaction size */
- " )"
- " ,dep AS (" /* restrict to our merchant and account and mark as done */
- " UPDATE deposits"
+ "WITH bdep AS (" /* restrict to our merchant and account and mark as done */
+ " UPDATE batch_deposits"
" SET done=TRUE"
- " WHERE coin_pub IN (SELECT coin_pub FROM rdy)"
- " AND merchant_pub=$2" /* theoretically, same coin could be spent at another merchant */
+ " WHERE NOT (done OR policy_blocked)" /* only actually executable deposits */
+ " AND refund_deadline<$1"
+ " AND shard=$5" /* only for efficiency, merchant_pub is what we really filter by */
+ " AND merchant_pub=$2" /* filter by target merchant */
" AND wire_target_h_payto=$3" /* merchant could have a 2nd bank account */
- " AND done=FALSE" /* theoretically, same coin could be spend at the same merchant a 2nd time */
" RETURNING"
- " deposit_serial_id"
+ " batch_deposit_serial_id)"
+ " ,cdep AS ("
+ " SELECT"
+ " coin_deposit_serial_id"
+ " ,batch_deposit_serial_id"
" ,coin_pub"
- " ,amount_with_fee_val AS amount_val"
- " ,amount_with_fee_frac AS amount_frac)"
+ " ,amount_with_fee AS amount"
+ " FROM coin_deposits"
+ " WHERE batch_deposit_serial_id IN (SELECT batch_deposit_serial_id FROM bdep))"
" ,ref AS (" /* find applicable refunds -- NOTE: may do a full join on the master, maybe find a left-join way to integrate with query above to push it to the shards? */
" SELECT"
- " amount_with_fee_val AS refund_val"
- " ,amount_with_fee_frac AS refund_frac"
+ " amount_with_fee AS refund"
" ,coin_pub"
- " ,deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */
+ " ,batch_deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */
" FROM refunds"
- " WHERE coin_pub IN (SELECT coin_pub FROM dep)"
- " AND deposit_serial_id IN (SELECT deposit_serial_id FROM dep))"
+ " WHERE coin_pub IN (SELECT coin_pub FROM cdep)"
+ " AND batch_deposit_serial_id IN (SELECT batch_deposit_serial_id FROM bdep))"
" ,ref_by_coin AS (" /* total up refunds by coin */
" SELECT"
- " SUM(refund_val) AS sum_refund_val"
- " ,SUM(refund_frac) AS sum_refund_frac"
+ " SUM((ref.refund).val) AS sum_refund_val"
+ " ,SUM((ref.refund).frac) AS sum_refund_frac"
" ,coin_pub"
- " ,deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */
+ " ,batch_deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */
" FROM ref"
- " GROUP BY coin_pub, deposit_serial_id)"
+ " GROUP BY coin_pub, batch_deposit_serial_id)"
" ,norm_ref_by_coin AS (" /* normalize */
" SELECT"
" sum_refund_val + sum_refund_frac / 100000000 AS norm_refund_val"
" ,sum_refund_frac % 100000000 AS norm_refund_frac"
" ,coin_pub"
- " ,deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */
+ " ,batch_deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */
" FROM ref_by_coin)"
" ,fully_refunded_coins AS (" /* find applicable refunds -- NOTE: may do a full join on the master, maybe find a left-join way to integrate with query above to push it to the shards? */
" SELECT"
- " dep.coin_pub"
+ " cdep.coin_pub"
" FROM norm_ref_by_coin norm"
- " JOIN dep"
- " ON (norm.coin_pub = dep.coin_pub"
- " AND norm.deposit_serial_id = dep.deposit_Serial_id"
- " AND norm.norm_refund_val = dep.amount_val"
- " AND norm.norm_refund_frac = dep.amount_frac))"
+ " JOIN cdep"
+ " ON (norm.coin_pub = cdep.coin_pub"
+ " AND norm.batch_deposit_serial_id = cdep.batch_deposit_serial_id"
+ " AND norm.norm_refund_val = (cdep.amount).val"
+ " AND norm.norm_refund_frac = (cdep.amount).frac))"
" ,fees AS (" /* find deposit fees for not fully refunded deposits */
" SELECT"
- " denom.fee_deposit_val AS fee_val"
- " ,denom.fee_deposit_frac AS fee_frac"
- " ,cs.deposit_serial_id" /* ensures we get the fee for each coin, not once per denomination */
- " FROM dep cs"
+ " denom.fee_deposit AS fee"
+ " ,cs.batch_deposit_serial_id" /* ensures we get the fee for each coin, not once per denomination */
+ " FROM cdep cs"
" JOIN known_coins kc" /* NOTE: may do a full join on the master, maybe find a left-join way to integrate with query above to push it to the shards? */
" USING (coin_pub)"
" JOIN denominations denom"
@@ -143,26 +117,52 @@ TEH_PG_aggregate (
" WHERE coin_pub NOT IN (SELECT coin_pub FROM fully_refunded_coins))"
" ,dummy AS (" /* add deposits to aggregation_tracking */
" INSERT INTO aggregation_tracking"
- " (deposit_serial_id"
+ " (batch_deposit_serial_id"
" ,wtid_raw)"
- " SELECT deposit_serial_id,$4"
- " FROM dep)"
+ " SELECT batch_deposit_serial_id,$4"
+ " FROM bdep)"
"SELECT" /* calculate totals (deposits, refunds and fees) */
- " CAST(COALESCE(SUM(dep.amount_val),0) AS INT8) AS sum_deposit_value" /* cast needed, otherwise we get NUMBER */
- " ,COALESCE(SUM(dep.amount_frac),0) AS sum_deposit_fraction" /* SUM over INT returns INT8 */
- " ,CAST(COALESCE(SUM(ref.refund_val),0) AS INT8) AS sum_refund_value"
- " ,COALESCE(SUM(ref.refund_frac),0) AS sum_refund_fraction"
- " ,CAST(COALESCE(SUM(fees.fee_val),0) AS INT8) AS sum_fee_value"
- " ,COALESCE(SUM(fees.fee_frac),0) AS sum_fee_fraction"
- " FROM dep "
+ " CAST(COALESCE(SUM((cdep.amount).val),0) AS INT8) AS sum_deposit_value"
+ /* cast needed, otherwise we get NUMBER */
+ " ,COALESCE(SUM((cdep.amount).frac),0) AS sum_deposit_fraction" /* SUM over INT returns INT8 */
+ " ,CAST(COALESCE(SUM((ref.refund).val),0) AS INT8) AS sum_refund_value"
+ " ,COALESCE(SUM((ref.refund).frac),0) AS sum_refund_fraction"
+ " ,CAST(COALESCE(SUM((fees.fee).val),0) AS INT8) AS sum_fee_value"
+ " ,COALESCE(SUM((fees.fee).frac),0) AS sum_fee_fraction"
+ " FROM cdep "
" FULL OUTER JOIN ref ON (FALSE)" /* We just want all sums */
" FULL OUTER JOIN fees ON (FALSE);");
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_absolute_time (&now),
+ GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_auto_from_type (wtid),
+ GNUNET_PQ_query_param_uint64 (&deposit_shard),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("sum_deposit_value",
+ &sum_deposit_value),
+ GNUNET_PQ_result_spec_uint64 ("sum_deposit_fraction",
+ &sum_deposit_frac),
+ GNUNET_PQ_result_spec_uint64 ("sum_refund_value",
+ &sum_refund_value),
+ GNUNET_PQ_result_spec_uint64 ("sum_refund_fraction",
+ &sum_refund_frac),
+ GNUNET_PQ_result_spec_uint64 ("sum_fee_value",
+ &sum_fee_value),
+ GNUNET_PQ_result_spec_uint64 ("sum_fee_fraction",
+ &sum_fee_frac),
+ GNUNET_PQ_result_spec_end
+ };
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "aggregate",
- params,
- rs);
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "aggregate",
+ params,
+ rs);
+ }
if (qs < 0)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
diff --git a/src/exchangedb/pg_batch2_reserves_in_insert.c b/src/exchangedb/pg_batch2_reserves_in_insert.c
deleted file mode 100644
index 8aca11de0..000000000
--- a/src/exchangedb/pg_batch2_reserves_in_insert.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- 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 exchangedb/pg_batch_reserves_in_insert.c
- * @brief Implementation of the reserves_in_insert function for Postgres
- * @author Joseph Xu
- */
-#include "platform.h"
-#include "taler_error_codes.h"
-#include "taler_dbevents.h"
-#include "taler_pq_lib.h"
-#include "pg_batch_reserves_in_insert.h"
-#include "pg_helper.h"
-#include "pg_start.h"
-#include "pg_rollback.h"
-#include "pg_start_read_committed.h"
-#include "pg_commit.h"
-#include "pg_reserves_get.h"
-#include "pg_reserves_update.h"
-#include "pg_setup_wire_target.h"
-#include "pg_event_notify.h"
-#include "pg_preflight.h"
-
-/**
- * Generate event notification for the reserve change.
- *
- * @param reserve_pub reserve to notfiy on
- */
-static char *
-compute_notify_on_reserve (const struct TALER_ReservePublicKeyP *reserve_pub)
-{
- struct TALER_ReserveEventP rep = {
- .header.size = htons (sizeof (rep)),
- .header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
- .reserve_pub = *reserve_pub
- };
-
- return GNUNET_PG_get_event_notify_channel (&rep.header);
-}
-
-
-enum GNUNET_DB_QueryStatus
-TEH_PG_batch2_reserves_in_insert (void *cls,
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
- unsigned int reserves_length,
- enum GNUNET_DB_QueryStatus *results)
-{
- struct PostgresClosure *pg = cls;
- enum GNUNET_DB_QueryStatus qs1;
- struct GNUNET_TIME_Timestamp expiry;
- struct GNUNET_TIME_Timestamp gc;
- struct TALER_PaytoHashP h_payto;
- uint64_t reserve_uuid;
- uint64_t reserve_uuid2;
- bool conflicted;
- bool conflicted2;
- bool transaction_duplicate;
- bool transaction_duplicate2;
- bool need_update = false;
- bool need_update2 = false;
- struct GNUNET_TIME_Timestamp reserve_expiration
- = GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time);
- bool conflicts[reserves_length];
- bool conflicts2[reserves_length];
- char *notify_s[reserves_length];
-
- if (GNUNET_OK !=
- TEH_PG_preflight (pg))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- PREPARE (pg,
- "reserve_create",
- "SELECT "
- "out_reserve_found AS conflicted"
- ",out_reserve_found2 AS conflicted2"
- ",transaction_duplicate"
- ",transaction_duplicate2"
- ",ruuid AS reserve_uuid"
- ",ruuid2 AS reserve_uuid2"
- " FROM batch2_reserves_insert"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21);");
- expiry = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_add (reserves->execution_time.abs_time,
- pg->idle_reserve_expiration_time));
- gc = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
- pg->legal_reserve_expiration_time));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Creating reserve %s with expiration in %s\n",
- TALER_B2S (&(reserves->reserve_pub)),
- GNUNET_STRINGS_relative_time_to_string (
- pg->idle_reserve_expiration_time,
- GNUNET_NO));
-
- {
- if (GNUNET_OK !=
- TEH_PG_start_read_committed(pg,
- "READ_COMMITED"))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- }
- /* Optimistically assume this is a new reserve, create balance for the first
- time; we do this before adding the actual transaction to "reserves_in",
- as for a new reserve it can't be a duplicate 'add' operation, and as
- the 'add' operation needs the reserve entry as a foreign key. */
- for (unsigned int i=0;i<reserves_length;i++)
- {
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
- notify_s[i] = compute_notify_on_reserve (&reserve->reserve_pub);
- }
-
- for (unsigned int i=0;i<(reserves_length & ~1);i+=2)
- {
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserve0 = &reserves[i];
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserve1 = &reserves[i+1];
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&reserve0->reserve_pub),
- GNUNET_PQ_query_param_timestamp (&expiry),
- GNUNET_PQ_query_param_timestamp (&gc),
- GNUNET_PQ_query_param_uint64 (&reserve0->wire_reference),
- TALER_PQ_query_param_amount (&reserve0->balance),
- GNUNET_PQ_query_param_string (reserve0->exchange_account_name),
- GNUNET_PQ_query_param_timestamp (&reserve0->execution_time),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- GNUNET_PQ_query_param_string (reserve0->sender_account_details),
- GNUNET_PQ_query_param_timestamp (&reserve_expiration),
- GNUNET_PQ_query_param_string (notify_s[i]),
- GNUNET_PQ_query_param_auto_from_type (&reserve1->reserve_pub),
- GNUNET_PQ_query_param_uint64 (&reserve1->wire_reference),
- TALER_PQ_query_param_amount (&reserve1->balance),
- GNUNET_PQ_query_param_string (reserve1->exchange_account_name),
- GNUNET_PQ_query_param_timestamp (&reserve1->execution_time),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- GNUNET_PQ_query_param_string (reserve1->sender_account_details),
- GNUNET_PQ_query_param_timestamp (&reserve_expiration),
-
- GNUNET_PQ_query_param_end
- };
-
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_bool ("conflicted",
- &conflicted),
- GNUNET_PQ_result_spec_bool ("conflicted2",
- &conflicted2),
- GNUNET_PQ_result_spec_bool ("transaction_duplicate",
- &transaction_duplicate),
- GNUNET_PQ_result_spec_bool ("transaction_duplicate2",
- &transaction_duplicate2),
- GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
- &reserve_uuid),
- GNUNET_PQ_result_spec_uint64 ("reserve_uuid2",
- &reserve_uuid2),
- GNUNET_PQ_result_spec_end
- };
-
- TALER_payto_hash (reserve0->sender_account_details,
- &h_payto);
- TALER_payto_hash (reserve1->sender_account_details,
- &h_payto);
-
- /* Note: query uses 'on conflict do nothing' */
- qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "reserve_create",
- params,
- rs);
-
- if (qs1 < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to create reserves (%d)\n",
- qs1);
- return qs1;
- }
- if (reserves_length & 1)
- {
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[reserves_length-1];
- // single insert logic here
- }
- GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1);
- results[i] = (transaction_duplicate)
- ? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
- : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- conflicts[i] = conflicted;
- conflicts2[i] = conflicted2;
- // fprintf(stdout, "%d", conflicts[i]);
- // fprintf(stdout, "%d", conflicts2[i]);
- if ((!conflicts[i] && transaction_duplicate) ||(!conflicts2[i] && transaction_duplicate2))
- {
- GNUNET_break (0);
- TEH_PG_rollback (pg);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- need_update |= conflicted |= conflicted2;
- }
- // commit
- {
- enum GNUNET_DB_QueryStatus cs;
-
- cs = TEH_PG_commit (pg);
- if (cs < 0)
- return cs;
- }
-
- if (!need_update)
- goto exit;
- // begin serializable
- {
- if (GNUNET_OK !=
- TEH_PG_start(pg,
- "reserve-insert-continued"))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- }
-
- enum GNUNET_DB_QueryStatus qs2;
- PREPARE (pg,
- "reserves_in_add_transaction",
- "SELECT batch_reserves_update"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9);");
- for (unsigned int i=0;i<reserves_length;i++)
- {
- if (! conflicts[i])
- continue;
- {
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub),
- GNUNET_PQ_query_param_timestamp (&expiry),
- GNUNET_PQ_query_param_uint64 (&reserve->wire_reference),
- TALER_PQ_query_param_amount (&reserve->balance),
- GNUNET_PQ_query_param_string (reserve->exchange_account_name),
- GNUNET_PQ_query_param_bool (conflicted),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- GNUNET_PQ_query_param_string (notify_s[i]),
- GNUNET_PQ_query_param_end
- };
-
- qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "reserves_in_add_transaction",
- params);
- if (qs2<0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to update reserves (%d)\n",
- qs2);
- return qs2;
- }
- }
- }
- {
- enum GNUNET_DB_QueryStatus cs;
-
- cs = TEH_PG_commit (pg);
- if (cs < 0)
- return cs;
- }
-
- exit:
- for (unsigned int i=0;i<reserves_length;i++)
- GNUNET_free (notify_s[i]);
-
- return reserves_length;
-}
diff --git a/src/exchangedb/pg_batch4_reserves_in_insert.c b/src/exchangedb/pg_batch4_reserves_in_insert.c
deleted file mode 100644
index 604a31e78..000000000
--- a/src/exchangedb/pg_batch4_reserves_in_insert.c
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- 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 exchangedb/pg_batch4_reserves_in_insert.c
- * @brief Implementation of the reserves_in_insert function for Postgres
- * @author Joseph Xu
- */
-#include "platform.h"
-#include "taler_error_codes.h"
-#include "taler_dbevents.h"
-#include "taler_pq_lib.h"
-#include "pg_batch_reserves_in_insert.h"
-#include "pg_helper.h"
-#include "pg_start.h"
-#include "pg_rollback.h"
-#include "pg_start_read_committed.h"
-#include "pg_commit.h"
-#include "pg_reserves_get.h"
-#include "pg_reserves_update.h"
-#include "pg_setup_wire_target.h"
-#include "pg_event_notify.h"
-#include "pg_preflight.h"
-
-/**
- * Generate event notification for the reserve change.
- *
- * @param reserve_pub reserve to notfiy on
- */
-static char *
-compute_notify_on_reserve (const struct TALER_ReservePublicKeyP *reserve_pub)
-{
- struct TALER_ReserveEventP rep = {
- .header.size = htons (sizeof (rep)),
- .header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
- .reserve_pub = *reserve_pub
- };
-
- return GNUNET_PG_get_event_notify_channel (&rep.header);
-}
-
-
-enum GNUNET_DB_QueryStatus
-TEH_PG_batch4_reserves_in_insert (void *cls,
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
- unsigned int reserves_length,
- enum GNUNET_DB_QueryStatus *results)
-{
- struct PostgresClosure *pg = cls;
- enum GNUNET_DB_QueryStatus qs1;
- struct GNUNET_TIME_Timestamp expiry;
- struct GNUNET_TIME_Timestamp gc;
- struct TALER_PaytoHashP h_payto;
- uint64_t reserve_uuid;
- bool conflicted;
- bool conflicted2;
- bool transaction_duplicate;
- bool transaction_duplicate2;
- bool need_update = false;
- bool need_update2 = false;
- struct GNUNET_TIME_Timestamp reserve_expiration
- = GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time);
- bool conflicts[reserves_length];
- bool conflicts2[reserves_length];
- char *notify_s[reserves_length];
-
- if (GNUNET_OK !=
- TEH_PG_preflight (pg))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- PREPARE (pg,
- "reserve_create",
- "SELECT "
- "out_reserve_found AS conflicted"
- ",out_reserve_found2 AS conflicted2"
- ",transaction_duplicate"
- ",transaction_duplicate2"
- ",ruuid AS reserve_uuid"
- " FROM batch2_reserves_insert"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21);");
- expiry = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_add (reserves->execution_time.abs_time,
- pg->idle_reserve_expiration_time));
- gc = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
- pg->legal_reserve_expiration_time));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Creating reserve %s with expiration in %s\n",
- TALER_B2S (&(reserves->reserve_pub)),
- GNUNET_STRINGS_relative_time_to_string (
- pg->idle_reserve_expiration_time,
- GNUNET_NO));
-
- {
- if (GNUNET_OK !=
- TEH_PG_start_read_committed(pg,
- "READ_COMMITED"))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- }
- /* Optimistically assume this is a new reserve, create balance for the first
- time; we do this before adding the actual transaction to "reserves_in",
- as for a new reserve it can't be a duplicate 'add' operation, and as
- the 'add' operation needs the reserve entry as a foreign key. */
- for (unsigned int i=0;i<reserves_length;i++)
- {
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
- notify_s[i] = compute_notify_on_reserve (&reserve->reserve_pub);
- }
-
- for (unsigned int i=0;i<reserves_length;i++)
- {
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub),
- GNUNET_PQ_query_param_timestamp (&expiry),
- GNUNET_PQ_query_param_timestamp (&gc),
- GNUNET_PQ_query_param_uint64 (&reserve->wire_reference),
- TALER_PQ_query_param_amount (&reserve->balance),
- GNUNET_PQ_query_param_string (reserve->exchange_account_name),
- GNUNET_PQ_query_param_timestamp (&reserve->execution_time),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- GNUNET_PQ_query_param_string (reserve->sender_account_details),
- GNUNET_PQ_query_param_timestamp (&reserve_expiration),
- GNUNET_PQ_query_param_string (notify_s[i]),
- GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub),
- GNUNET_PQ_query_param_uint64 (&reserve->wire_reference),
- TALER_PQ_query_param_amount (&reserve->balance),
- GNUNET_PQ_query_param_string (reserve->exchange_account_name),
- GNUNET_PQ_query_param_timestamp (&reserve->execution_time),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- GNUNET_PQ_query_param_string (reserve->sender_account_details),
- GNUNET_PQ_query_param_timestamp (&reserve_expiration),
- GNUNET_PQ_query_param_end
- };
-
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_bool ("conflicted",
- &conflicted),
- GNUNET_PQ_result_spec_bool ("conflicted2",
- &conflicted2),
- GNUNET_PQ_result_spec_bool ("transaction_duplicate",
- &transaction_duplicate),
- GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
- &reserve_uuid),
- GNUNET_PQ_result_spec_end
- };
-
- TALER_payto_hash (reserve->sender_account_details,
- &h_payto);
-
- /* Note: query uses 'on conflict do nothing' */
- qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "reserve_create",
- params,
- rs);
-
- if (qs1 < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to create reserves (%d)\n",
- qs1);
- return qs1;
- }
- GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1);
- results[i] = (transaction_duplicate)
- ? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
- : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- conflicts[i] = conflicted;
- conflicts2[i] = conflicted2;
- // fprintf(stdout, "%d", conflicts[i]);
- if (!conflicts[i] && !conflicts2[i]&& transaction_duplicate)
- {
- GNUNET_break (0);
- TEH_PG_rollback (pg);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- need_update |= conflicted |= conflicted2;
- }
- // commit
- {
- enum GNUNET_DB_QueryStatus cs;
-
- cs = TEH_PG_commit (pg);
- if (cs < 0)
- return cs;
- }
-
- if (!need_update)
- goto exit;
- // begin serializable
- {
- if (GNUNET_OK !=
- TEH_PG_start(pg,
- "reserve-insert-continued"))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- }
-
- enum GNUNET_DB_QueryStatus qs2;
- PREPARE (pg,
- "reserves_in_add_transaction",
- "SELECT batch_reserves_update"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9);");
- for (unsigned int i=0;i<reserves_length;i++)
- {
- if (! conflicts[i])
- continue;
- {
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub),
- GNUNET_PQ_query_param_timestamp (&expiry),
- GNUNET_PQ_query_param_uint64 (&reserve->wire_reference),
- TALER_PQ_query_param_amount (&reserve->balance),
- GNUNET_PQ_query_param_string (reserve->exchange_account_name),
- GNUNET_PQ_query_param_bool (conflicted),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- GNUNET_PQ_query_param_string (notify_s[i]),
- GNUNET_PQ_query_param_end
- };
-
- qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "reserves_in_add_transaction",
- params);
- if (qs2<0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to update reserves (%d)\n",
- qs2);
- return qs2;
- }
- }
- }
- {
- enum GNUNET_DB_QueryStatus cs;
-
- cs = TEH_PG_commit (pg);
- if (cs < 0)
- return cs;
- }
-
- exit:
- for (unsigned int i=0;i<reserves_length;i++)
- GNUNET_free (notify_s[i]);
-
- return reserves_length;
-}
diff --git a/src/exchangedb/pg_batch4_reserves_in_insert.h b/src/exchangedb/pg_batch4_reserves_in_insert.h
deleted file mode 100644
index 3b3a629f9..000000000
--- a/src/exchangedb/pg_batch4_reserves_in_insert.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- 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 exchangedb/pg_batch4_reserves_in_insert.h
- * @brief implementation of the batch4_reserves_in_insert function for Postgres
- * @author XU Joseph
- */
-#ifndef PG_BATCH4_RESERVES_IN_INSERT_H
-#define PG_BATCH4_RESERVES_IN_INSERT_H
-
-#include "taler_util.h"
-#include "taler_json_lib.h"
-#include "taler_exchangedb_plugin.h"
-
-enum GNUNET_DB_QueryStatus
-TEH_PG_batch4_reserves_in_insert (void *cls,
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
- unsigned int reserves_length,
- enum GNUNET_DB_QueryStatus *results);
-
-#endif
diff --git a/src/exchangedb/pg_batch_ensure_coin_known.c b/src/exchangedb/pg_batch_ensure_coin_known.c
new file mode 100644
index 000000000..aca2732c6
--- /dev/null
+++ b/src/exchangedb/pg_batch_ensure_coin_known.c
@@ -0,0 +1,462 @@
+/*
+ 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 exchangedb/pg_batch_ensure_coin_known.c
+ * @brief Implementation of the batch_ensure_coin_known function for Postgres
+ * @author Christian Grothoff
+ *
+ * FIXME: use the array support for postgres to simplify this code!
+ *
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_exchangedb_plugin.h"
+#include "taler_pq_lib.h"
+#include "pg_batch_ensure_coin_known.h"
+#include "pg_helper.h"
+
+
+static enum GNUNET_DB_QueryStatus
+insert1 (struct PostgresClosure *pg,
+ const struct TALER_CoinPublicInfo coin[1],
+ struct TALER_EXCHANGEDB_CoinInfo result[1])
+{
+ enum GNUNET_DB_QueryStatus qs;
+ bool is_denom_pub_hash_null = false;
+ bool is_age_hash_null = false;
+ PREPARE (pg,
+ "batch1_known_coin",
+ "SELECT"
+ " existed1 AS existed"
+ ",known_coin_id1 AS known_coin_id"
+ ",denom_pub_hash1 AS denom_hash"
+ ",age_commitment_hash1 AS h_age_commitment"
+ " FROM exchange_do_batch1_known_coin"
+ " ($1, $2, $3, $4);"
+ );
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("existed",
+ &result[0].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id",
+ &result[0].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+ &result[0].denom_hash),
+ &is_denom_pub_hash_null),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
+ &result[0].h_age_commitment),
+ &is_age_hash_null),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "batch1_known_coin",
+ params,
+ rs);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break (0); /* should be impossible */
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break; /* continued below */
+ }
+
+ if ( (! is_denom_pub_hash_null) &&
+ (0 != GNUNET_memcmp (&result[0].denom_hash,
+ &coin->denom_pub_hash)) )
+ {
+ GNUNET_break_op (0);
+ result[0].denom_conflict = true;
+ }
+
+ if ( (! is_denom_pub_hash_null) &&
+ (0 != GNUNET_memcmp (&result[0].denom_hash,
+ &coin[0].denom_pub_hash)) )
+ {
+ GNUNET_break_op (0);
+ result[0].denom_conflict = true;
+ }
+
+ result[0].age_conflict = TALER_AgeCommitmentHash_NoConflict;
+
+ if (is_age_hash_null != coin[0].no_age_commitment)
+ {
+ if (is_age_hash_null)
+ {
+ GNUNET_break_op (0);
+ result[0].age_conflict = TALER_AgeCommitmentHash_NullExpected;
+ }
+ else
+ {
+ GNUNET_break_op (0);
+ result[0].age_conflict = TALER_AgeCommitmentHash_ValueExpected;
+ }
+ }
+ else if ( (! is_age_hash_null) &&
+ (0 != GNUNET_memcmp (&result[0].h_age_commitment,
+ &coin[0].h_age_commitment)) )
+ {
+ GNUNET_break_op (0);
+ result[0].age_conflict = TALER_AgeCommitmentHash_ValueDiffers;
+ }
+
+ return qs;
+}
+
+
+static enum GNUNET_DB_QueryStatus
+insert2 (struct PostgresClosure *pg,
+ const struct TALER_CoinPublicInfo coin[2],
+ struct TALER_EXCHANGEDB_CoinInfo result[2])
+{
+ enum GNUNET_DB_QueryStatus qs;
+ bool is_denom_pub_hash_null[2] = {false, false};
+ bool is_age_hash_null[2] = {false, false};
+
+ PREPARE (pg,
+ "batch2_known_coin",
+ "SELECT"
+ " existed1 AS existed"
+ ",known_coin_id1 AS known_coin_id"
+ ",denom_pub_hash1 AS denom_hash"
+ ",age_commitment_hash1 AS h_age_commitment"
+ ",existed2 AS existed2"
+ ",known_coin_id2 AS known_coin_id2"
+ ",denom_pub_hash2 AS denom_hash2"
+ ",age_commitment_hash2 AS h_age_commitment2"
+ " FROM exchange_do_batch2_known_coin"
+ " ($1, $2, $3, $4, $5, $6, $7, $8);"
+ );
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
+
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("existed",
+ &result[0].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id",
+ &result[0].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+ &result[0].denom_hash),
+ &is_denom_pub_hash_null[0]),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
+ &result[0].h_age_commitment),
+ &is_age_hash_null[0]),
+ GNUNET_PQ_result_spec_bool ("existed2",
+ &result[1].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id2",
+ &result[1].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash2",
+ &result[1].denom_hash),
+ &is_denom_pub_hash_null[1]),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash2",
+ &result[1].h_age_commitment),
+ &is_age_hash_null[1]),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "batch2_known_coin",
+ params,
+ rs);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break (0); /* should be impossible */
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break; /* continued below */
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ if ( (! is_denom_pub_hash_null[i]) &&
+ (0 != GNUNET_memcmp (&result[i].denom_hash,
+ &coin[i].denom_pub_hash)) )
+ {
+ GNUNET_break_op (0);
+ result[i].denom_conflict = true;
+ }
+
+ result[i].age_conflict = TALER_AgeCommitmentHash_NoConflict;
+
+ if (is_age_hash_null[i] != coin[i].no_age_commitment)
+ {
+ if (is_age_hash_null[i])
+ {
+ GNUNET_break_op (0);
+ result[i].age_conflict = TALER_AgeCommitmentHash_NullExpected;
+ }
+ else
+ {
+ GNUNET_break_op (0);
+ result[i].age_conflict = TALER_AgeCommitmentHash_ValueExpected;
+ }
+ }
+ else if ( (! is_age_hash_null[i]) &&
+ (0 != GNUNET_memcmp (&result[i].h_age_commitment,
+ &coin[i].h_age_commitment)) )
+ {
+ GNUNET_break_op (0);
+ result[i].age_conflict = TALER_AgeCommitmentHash_ValueDiffers;
+ }
+ }
+
+ return qs;
+}
+
+
+static enum GNUNET_DB_QueryStatus
+insert4 (struct PostgresClosure *pg,
+ const struct TALER_CoinPublicInfo coin[4],
+ struct TALER_EXCHANGEDB_CoinInfo result[4])
+{
+ enum GNUNET_DB_QueryStatus qs;
+ bool is_denom_pub_hash_null[4] = {false, false, false, false};
+ bool is_age_hash_null[4] = {false, false, false, false};
+ PREPARE (pg,
+ "batch4_known_coin",
+ "SELECT"
+ " existed1 AS existed"
+ ",known_coin_id1 AS known_coin_id"
+ ",denom_pub_hash1 AS denom_hash"
+ ",age_commitment_hash1 AS h_age_commitment"
+ ",existed2 AS existed2"
+ ",known_coin_id2 AS known_coin_id2"
+ ",denom_pub_hash2 AS denom_hash2"
+ ",age_commitment_hash2 AS h_age_commitment2"
+ ",existed3 AS existed3"
+ ",known_coin_id3 AS known_coin_id3"
+ ",denom_pub_hash3 AS denom_hash3"
+ ",age_commitment_hash3 AS h_age_commitment3"
+ ",existed4 AS existed4"
+ ",known_coin_id4 AS known_coin_id4"
+ ",denom_pub_hash4 AS denom_hash4"
+ ",age_commitment_hash4 AS h_age_commitment4"
+ " FROM exchange_do_batch2_known_coin"
+ " ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16);"
+ );
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
+
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
+
+ GNUNET_PQ_query_param_auto_from_type (&coin[2].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[2].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[2].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[2].denom_sig),
+
+ GNUNET_PQ_query_param_auto_from_type (&coin[3].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[3].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[3].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[3].denom_sig),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("existed",
+ &result[0].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id",
+ &result[0].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+ &result[0].denom_hash),
+ &is_denom_pub_hash_null[0]),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
+ &result[0].h_age_commitment),
+ &is_age_hash_null[0]),
+ GNUNET_PQ_result_spec_bool ("existed2",
+ &result[1].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id2",
+ &result[1].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash2",
+ &result[1].denom_hash),
+ &is_denom_pub_hash_null[1]),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash2",
+ &result[1].h_age_commitment),
+ &is_age_hash_null[1]),
+ GNUNET_PQ_result_spec_bool ("existed3",
+ &result[2].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id3",
+ &result[2].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash3",
+ &result[2].denom_hash),
+ &is_denom_pub_hash_null[2]),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash3",
+ &result[2].h_age_commitment),
+ &is_age_hash_null[2]),
+ GNUNET_PQ_result_spec_bool ("existed4",
+ &result[3].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id4",
+ &result[3].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash4",
+ &result[3].denom_hash),
+ &is_denom_pub_hash_null[3]),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash4",
+ &result[3].h_age_commitment),
+ &is_age_hash_null[3]),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "batch4_known_coin",
+ params,
+ rs);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break (0); /* should be impossible */
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break; /* continued below */
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ if ( (! is_denom_pub_hash_null[i]) &&
+ (0 != GNUNET_memcmp (&result[i].denom_hash,
+ &coin[i].denom_pub_hash)) )
+ {
+ GNUNET_break_op (0);
+ result[i].denom_conflict = true;
+ }
+
+ result[i].age_conflict = TALER_AgeCommitmentHash_NoConflict;
+
+ if (is_age_hash_null[i] != coin[i].no_age_commitment)
+ {
+ if (is_age_hash_null[i])
+ {
+ GNUNET_break_op (0);
+ result[i].age_conflict = TALER_AgeCommitmentHash_NullExpected;
+ }
+ else
+ {
+ GNUNET_break_op (0);
+ result[i].age_conflict = TALER_AgeCommitmentHash_ValueExpected;
+ }
+ }
+ else if ( (! is_age_hash_null[i]) &&
+ (0 != GNUNET_memcmp (&result[i].h_age_commitment,
+ &coin[i].h_age_commitment)) )
+ {
+ GNUNET_break_op (0);
+ result[i].age_conflict = TALER_AgeCommitmentHash_ValueDiffers;
+ }
+ }
+
+ return qs;
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_batch_ensure_coin_known (
+ void *cls,
+ const struct TALER_CoinPublicInfo *coin,
+ struct TALER_EXCHANGEDB_CoinInfo *result,
+ unsigned int coin_length,
+ unsigned int batch_size)
+{
+ struct PostgresClosure *pg = cls;
+ enum GNUNET_DB_QueryStatus qs = 0;
+ unsigned int i = 0;
+
+ while ( (qs >= 0) &&
+ (i < coin_length) )
+ {
+ unsigned int bs = GNUNET_MIN (batch_size,
+ coin_length - i);
+ if (bs >= 4)
+ {
+ qs = insert4 (pg,
+ &coin[i],
+ &result[i]);
+ i += 4;
+ continue;
+ }
+ switch (bs)
+ {
+ case 3:
+ case 2:
+ qs = insert2 (pg,
+ &coin[i],
+ &result[i]);
+ i += 2;
+ break;
+ case 1:
+ qs = insert1 (pg,
+ &coin[i],
+ &result[i]);
+ i += 1;
+ break;
+ case 0:
+ GNUNET_assert (0);
+ break;
+ }
+ } /* end while */
+ if (qs < 0)
+ return qs;
+ return i;
+}
diff --git a/src/exchangedb/pg_batch_ensure_coin_known.h b/src/exchangedb/pg_batch_ensure_coin_known.h
new file mode 100644
index 000000000..2c53676d9
--- /dev/null
+++ b/src/exchangedb/pg_batch_ensure_coin_known.h
@@ -0,0 +1,47 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022, 2023 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 exchangedb/pg_batch_ensure_coin_known.h
+ * @brief implementation of the batch_ensure_coin_known function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_BATCH_ENSURE_COIN_KNOWN_H
+#define PG_BATCH_ENSURE_COIN_KNOWN_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Make sure the array of given @a coin is known to the database.
+ *
+ * @param cls database connection plugin state
+ * @param coin array of coins that must be made known
+ * @param[out] result array where to store information about each coin
+ * @param coin_length length of the @a coin and @a result arraysf
+ * @param batch_size desired (maximum) batch size
+ * @return database transaction status, non-negative on success
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_batch_ensure_coin_known (
+ void *cls,
+ const struct TALER_CoinPublicInfo *coin,
+ struct TALER_EXCHANGEDB_CoinInfo *result,
+ unsigned int coin_length,
+ unsigned int batch_size);
+
+#endif
diff --git a/src/exchangedb/pg_batch_reserves_in_insert.c b/src/exchangedb/pg_batch_reserves_in_insert.c
deleted file mode 100644
index 8a14022e7..000000000
--- a/src/exchangedb/pg_batch_reserves_in_insert.c
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- 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 exchangedb/pg_batch_reserves_in_insert.c
- * @brief Implementation of the reserves_in_insert function for Postgres
- * @author Joseph Xu
- */
-#include "platform.h"
-#include "taler_error_codes.h"
-#include "taler_dbevents.h"
-#include "taler_pq_lib.h"
-#include "pg_batch_reserves_in_insert.h"
-#include "pg_helper.h"
-#include "pg_start.h"
-#include "pg_rollback.h"
-#include "pg_start_read_committed.h"
-#include "pg_commit.h"
-#include "pg_reserves_get.h"
-#include "pg_reserves_update.h"
-#include "pg_setup_wire_target.h"
-#include "pg_event_notify.h"
-#include "pg_preflight.h"
-
-
-/**
- * Generate event notification for the reserve change.
- *
- * @param reserve_pub reserve to notfiy on
- */
-static char *
-compute_notify_on_reserve (const struct TALER_ReservePublicKeyP *reserve_pub)
-{
- struct TALER_ReserveEventP rep = {
- .header.size = htons (sizeof (rep)),
- .header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
- .reserve_pub = *reserve_pub
- };
-
- return GNUNET_PG_get_event_notify_channel (&rep.header);
-}
-
-
-enum GNUNET_DB_QueryStatus
-TEH_PG_batch_reserves_in_insert (void *cls,
- const struct
- TALER_EXCHANGEDB_ReserveInInfo *reserves,
- unsigned int reserves_length,
- enum GNUNET_DB_QueryStatus *results)
-{
- struct PostgresClosure *pg = cls;
- enum GNUNET_DB_QueryStatus qs1;
- struct GNUNET_TIME_Timestamp expiry;
- struct GNUNET_TIME_Timestamp gc;
- struct TALER_PaytoHashP h_payto;
- uint64_t reserve_uuid;
- bool conflicted;
- bool transaction_duplicate;
- bool need_update = false;
- struct GNUNET_TIME_Timestamp reserve_expiration
- = GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time);
- bool conflicts[reserves_length];
- char *notify_s[reserves_length];
-
- if (GNUNET_OK !=
- TEH_PG_preflight (pg))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- PREPARE (pg,
- "reserve_create",
- "SELECT "
- "out_reserve_found AS conflicted"
- ",transaction_duplicate"
- ",ruuid AS reserve_uuid"
- " FROM batch_reserves_insert"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);");
- expiry = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_add (reserves->execution_time.abs_time,
- pg->idle_reserve_expiration_time));
- gc = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
- pg->legal_reserve_expiration_time));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Creating reserve %s with expiration in %s\n",
- TALER_B2S (&(reserves->reserve_pub)),
- GNUNET_STRINGS_relative_time_to_string (
- pg->idle_reserve_expiration_time,
- GNUNET_NO));
-
- {
- if (GNUNET_OK !=
- TEH_PG_start_read_committed(pg,
- "READ_COMMITED"))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- }
- /* Optimistically assume this is a new reserve, create balance for the first
- time; we do this before adding the actual transaction to "reserves_in",
- as for a new reserve it can't be a duplicate 'add' operation, and as
- the 'add' operation needs the reserve entry as a foreign key. */
- for (unsigned int i = 0; i<reserves_length; i++)
- {
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
- notify_s[i] = compute_notify_on_reserve (&reserve->reserve_pub);
- }
-
- for (unsigned int i=0;i<reserves_length;i++)
- {
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub),
- GNUNET_PQ_query_param_timestamp (&expiry),
- GNUNET_PQ_query_param_timestamp (&gc),
- GNUNET_PQ_query_param_uint64 (&reserve->wire_reference),
- TALER_PQ_query_param_amount (&reserve->balance),
- GNUNET_PQ_query_param_string (reserve->exchange_account_name),
- GNUNET_PQ_query_param_timestamp (&reserve->execution_time),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- GNUNET_PQ_query_param_string (reserve->sender_account_details),
- GNUNET_PQ_query_param_timestamp (&reserve_expiration),
- GNUNET_PQ_query_param_string (notify_s[i]),
- GNUNET_PQ_query_param_end
- };
-
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_bool ("conflicted",
- &conflicted),
- GNUNET_PQ_result_spec_bool ("transaction_duplicate",
- &transaction_duplicate),
- GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
- &reserve_uuid),
- GNUNET_PQ_result_spec_end
- };
-
- TALER_payto_hash (reserve->sender_account_details,
- &h_payto);
-
- /* Note: query uses 'on conflict do nothing' */
- qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "reserve_create",
- params,
- rs);
-
- if (qs1 < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to create reserves (%d)\n",
- qs1);
- return qs1;
- }
- GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1);
- results[i] = (transaction_duplicate)
- ? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
- : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- conflicts[i] = conflicted;
- // fprintf(stdout, "%d", conflicts[i]);
- if (!conflicts[i] && transaction_duplicate)
- {
- GNUNET_break (0);
- TEH_PG_rollback (pg);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- need_update |= conflicted;
- }
- // commit
- {
- enum GNUNET_DB_QueryStatus cs;
-
- cs = TEH_PG_commit (pg);
- if (cs < 0)
- return cs;
- }
-
- if (!need_update)
- goto exit;
- // begin serializable
- {
- if (GNUNET_OK !=
- TEH_PG_start(pg,
- "reserve-insert-continued"))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- }
-
- enum GNUNET_DB_QueryStatus qs2;
- PREPARE (pg,
- "reserves_in_add_transaction",
- "SELECT batch_reserves_update"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9);");
- for (unsigned int i=0;i<reserves_length;i++)
- {
- if (! conflicts[i])
- continue;
- {
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&reserve->reserve_pub),
- GNUNET_PQ_query_param_timestamp (&expiry),
- GNUNET_PQ_query_param_uint64 (&reserve->wire_reference),
- TALER_PQ_query_param_amount (&reserve->balance),
- GNUNET_PQ_query_param_string (reserve->exchange_account_name),
- GNUNET_PQ_query_param_bool (conflicted),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- GNUNET_PQ_query_param_string (notify_s[i]),
- GNUNET_PQ_query_param_end
- };
-
- qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "reserves_in_add_transaction",
- params);
- if (qs2<0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to update reserves (%d)\n",
- qs2);
- return qs2;
- }
- }
- }
- {
- enum GNUNET_DB_QueryStatus cs;
-
- cs = TEH_PG_commit (pg);
- if (cs < 0)
- return cs;
- }
-
- exit:
- for (unsigned int i=0;i<reserves_length;i++)
- GNUNET_free (notify_s[i]);
-
- return reserves_length;
-}
diff --git a/src/exchangedb/pg_begin_revolving_shard.c b/src/exchangedb/pg_begin_revolving_shard.c
index 888d7fd20..86cdf80fd 100644
--- a/src/exchangedb/pg_begin_revolving_shard.c
+++ b/src/exchangedb/pg_begin_revolving_shard.c
@@ -30,11 +30,11 @@
enum GNUNET_DB_QueryStatus
TEH_PG_begin_revolving_shard (void *cls,
- const char *job_name,
- uint32_t shard_size,
- uint32_t shard_limit,
- uint32_t *start_row,
- uint32_t *end_row)
+ const char *job_name,
+ uint32_t shard_size,
+ uint32_t shard_limit,
+ uint32_t *start_row,
+ uint32_t *end_row)
{
struct PostgresClosure *pg = cls;
@@ -45,7 +45,7 @@ TEH_PG_begin_revolving_shard (void *cls,
{
if (GNUNET_OK !=
TEH_PG_start (pg,
- "begin_revolving_shard"))
+ "begin_revolving_shard"))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
@@ -64,15 +64,15 @@ TEH_PG_begin_revolving_shard (void *cls,
&last_end),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_begin_revolving_shard() */
- PREPARE(pg,
- "get_last_revolving_shard",
- "SELECT"
- " end_row"
- " FROM revolving_work_shards"
- " WHERE job_name=$1"
- " ORDER BY end_row DESC"
- " LIMIT 1;");
+ /* Used in #postgres_begin_revolving_shard() */
+ PREPARE (pg,
+ "get_last_revolving_shard",
+ "SELECT"
+ " end_row"
+ " FROM revolving_work_shards"
+ " WHERE job_name=$1"
+ " ORDER BY end_row DESC"
+ " LIMIT 1;");
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_last_revolving_shard",
params,
@@ -116,7 +116,7 @@ TEH_PG_begin_revolving_shard (void *cls,
(unsigned long long) *start_row,
(unsigned long long) *end_row);
- /* Used in #postgres_claim_revolving_shard() */
+ /* Used in #postgres_claim_revolving_shard() */
PREPARE (pg,
"create_revolving_shard",
"INSERT INTO revolving_work_shards"
@@ -164,7 +164,7 @@ TEH_PG_begin_revolving_shard (void *cls,
end_row),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_begin_revolving_shard() */
+ /* Used in #postgres_begin_revolving_shard() */
PREPARE (pg,
"get_open_revolving_shard",
"SELECT"
@@ -206,7 +206,7 @@ TEH_PG_begin_revolving_shard (void *cls,
now = GNUNET_TIME_timestamp_get ();
- /* Used in #postgres_begin_revolving_shard() */
+ /* Used in #postgres_begin_revolving_shard() */
PREPARE (pg,
"reclaim_revolving_shard",
"UPDATE revolving_work_shards"
diff --git a/src/exchangedb/pg_begin_revolving_shard.h b/src/exchangedb/pg_begin_revolving_shard.h
index bdbca4f11..0755f886b 100644
--- a/src/exchangedb/pg_begin_revolving_shard.h
+++ b/src/exchangedb/pg_begin_revolving_shard.h
@@ -40,9 +40,10 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_begin_revolving_shard (void *cls,
- const char *job_name,
- uint32_t shard_size,
- uint32_t shard_limit,
- uint32_t *start_row,
+ const char *job_name,
+ uint32_t shard_size,
+ uint32_t shard_limit,
+ uint32_t *start_row,
uint32_t *end_row);
+
#endif
diff --git a/src/exchangedb/pg_begin_shard.c b/src/exchangedb/pg_begin_shard.c
index 2d4a33af8..48e077990 100644
--- a/src/exchangedb/pg_begin_shard.c
+++ b/src/exchangedb/pg_begin_shard.c
@@ -28,13 +28,14 @@
#include "pg_rollback.h"
#include "pg_commit.h"
+
enum GNUNET_DB_QueryStatus
TEH_PG_begin_shard (void *cls,
- const char *job_name,
- struct GNUNET_TIME_Relative delay,
- uint64_t shard_size,
- uint64_t *start_row,
- uint64_t *end_row)
+ const char *job_name,
+ struct GNUNET_TIME_Relative delay,
+ uint64_t shard_size,
+ uint64_t *start_row,
+ uint64_t *end_row)
{
struct PostgresClosure *pg = cls;
@@ -42,7 +43,7 @@ TEH_PG_begin_shard (void *cls,
{
if (GNUNET_OK !=
TEH_PG_start (pg,
- "begin_shard"))
+ "begin_shard"))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
@@ -65,7 +66,6 @@ TEH_PG_begin_shard (void *cls,
};
past = GNUNET_TIME_absolute_get ();
-
PREPARE (pg,
"get_open_shard",
"SELECT"
@@ -77,7 +77,6 @@ TEH_PG_begin_shard (void *cls,
" AND last_attempt<$2"
" ORDER BY last_attempt ASC"
" LIMIT 1;");
-
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_open_shard",
params,
@@ -89,6 +88,8 @@ TEH_PG_begin_shard (void *cls,
TEH_PG_rollback (pg);
return qs;
case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Serialization error on getting open shard\n");
TEH_PG_rollback (pg);
continue;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
@@ -104,8 +105,6 @@ TEH_PG_begin_shard (void *cls,
};
now = GNUNET_TIME_relative_to_absolute (delay);
-
-
PREPARE (pg,
"reclaim_shard",
"UPDATE work_shards"
@@ -113,7 +112,6 @@ TEH_PG_begin_shard (void *cls,
" WHERE job_name=$1"
" AND start_row=$3"
" AND end_row=$4");
-
qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
"reclaim_shard",
params);
@@ -124,6 +122,8 @@ TEH_PG_begin_shard (void *cls,
TEH_PG_rollback (pg);
return qs;
case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Serialization error on claiming open shard\n");
TEH_PG_rollback (pg);
continue;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
@@ -172,6 +172,8 @@ TEH_PG_begin_shard (void *cls,
TEH_PG_rollback (pg);
return qs;
case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Serialization error on getting last shard\n");
TEH_PG_rollback (pg);
continue;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
@@ -220,6 +222,8 @@ TEH_PG_begin_shard (void *cls,
TEH_PG_rollback (pg);
return qs;
case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Serialization error on claiming next shard\n");
TEH_PG_rollback (pg);
continue;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
@@ -246,10 +250,14 @@ commit:
TEH_PG_rollback (pg);
return qs;
case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Serialization error on commit for beginning shard\n");
TEH_PG_rollback (pg);
continue;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Claimed new shard\n");
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
}
diff --git a/src/exchangedb/pg_begin_shard.h b/src/exchangedb/pg_begin_shard.h
index 39bd834e9..16f19491d 100644
--- a/src/exchangedb/pg_begin_shard.h
+++ b/src/exchangedb/pg_begin_shard.h
@@ -38,10 +38,10 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_begin_shard (void *cls,
- const char *job_name,
- struct GNUNET_TIME_Relative delay,
- uint64_t shard_size,
- uint64_t *start_row,
+ const char *job_name,
+ struct GNUNET_TIME_Relative delay,
+ uint64_t shard_size,
+ uint64_t *start_row,
uint64_t *end_row);
#endif
diff --git a/src/exchangedb/pg_commit.c b/src/exchangedb/pg_commit.c
index f1e61ee34..8c4f87c90 100644
--- a/src/exchangedb/pg_commit.c
+++ b/src/exchangedb/pg_commit.c
@@ -45,7 +45,7 @@ TEH_PG_commit (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Committing transaction `%s'\n",
pg->transaction_name);
- /* used in #postgres_commit */
+ /* used in #postgres_commit */
PREPARE (pg,
"do_commit",
"COMMIT");
diff --git a/src/exchangedb/pg_complete_shard.h b/src/exchangedb/pg_complete_shard.h
index 8693f402e..f06c0483b 100644
--- a/src/exchangedb/pg_complete_shard.h
+++ b/src/exchangedb/pg_complete_shard.h
@@ -36,7 +36,8 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_complete_shard (void *cls,
- const char *job_name,
- uint64_t start_row,
+ const char *job_name,
+ uint64_t start_row,
uint64_t end_row);
+
#endif
diff --git a/src/exchangedb/pg_count_known_coins.c b/src/exchangedb/pg_count_known_coins.c
index 28c4612fc..872965ac9 100644
--- a/src/exchangedb/pg_count_known_coins.c
+++ b/src/exchangedb/pg_count_known_coins.c
@@ -27,8 +27,8 @@
long long
TEH_PG_count_known_coins (void *cls,
- const struct
- TALER_DenominationHashP *denom_pub_hash)
+ const struct
+ TALER_DenominationHashP *denom_pub_hash)
{
struct PostgresClosure *pg = cls;
uint64_t count;
diff --git a/src/exchangedb/pg_count_known_coins.h b/src/exchangedb/pg_count_known_coins.h
index f5dd4f27f..69f07bdf4 100644
--- a/src/exchangedb/pg_count_known_coins.h
+++ b/src/exchangedb/pg_count_known_coins.h
@@ -33,7 +33,7 @@
*/
long long
TEH_PG_count_known_coins (void *cls,
- const struct
+ const struct
TALER_DenominationHashP *denom_pub_hash);
#endif
diff --git a/src/exchangedb/pg_create_aggregation_transient.c b/src/exchangedb/pg_create_aggregation_transient.c
index 4ced9da04..4ab537d3a 100644
--- a/src/exchangedb/pg_create_aggregation_transient.c
+++ b/src/exchangedb/pg_create_aggregation_transient.c
@@ -38,7 +38,8 @@ TEH_PG_create_aggregation_transient (
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
- TALER_PQ_query_param_amount (total),
+ TALER_PQ_query_param_amount (pg->conn,
+ total),
GNUNET_PQ_query_param_auto_from_type (merchant_pub),
GNUNET_PQ_query_param_auto_from_type (h_payto),
GNUNET_PQ_query_param_uint64 (&kyc_requirement_row),
@@ -46,18 +47,17 @@ TEH_PG_create_aggregation_transient (
GNUNET_PQ_query_param_auto_from_type (wtid),
GNUNET_PQ_query_param_end
};
- /* Used in #postgres_create_aggregation_transient() */
+
PREPARE (pg,
- "create_aggregation_transient",
- "INSERT INTO aggregation_transient"
- " (amount_val"
- " ,amount_frac"
- " ,merchant_pub"
- " ,wire_target_h_payto"
- " ,legitimization_requirement_serial_id"
- " ,exchange_account_section"
- " ,wtid_raw)"
- " VALUES ($1, $2, $3, $4, $5, $6, $7);");
+ "create_aggregation_transient",
+ "INSERT INTO aggregation_transient"
+ " (amount"
+ " ,merchant_pub"
+ " ,wire_target_h_payto"
+ " ,legitimization_requirement_serial_id"
+ " ,exchange_account_section"
+ " ,wtid_raw)"
+ " VALUES ($1, $2, $3, $4, $5, $6);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"create_aggregation_transient",
params);
diff --git a/src/exchangedb/pg_create_tables.c b/src/exchangedb/pg_create_tables.c
index 1d5728d89..f6a061904 100644
--- a/src/exchangedb/pg_create_tables.c
+++ b/src/exchangedb/pg_create_tables.c
@@ -52,7 +52,6 @@ TEH_PG_create_tables (void *cls,
GNUNET_PQ_EXECUTE_STATEMENT_END
};
-
conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
"exchangedb-postgres",
"exchange-",
diff --git a/src/exchangedb/pg_delete_aggregation_transient.c b/src/exchangedb/pg_delete_aggregation_transient.c
index d0622c0f7..63c5c0a23 100644
--- a/src/exchangedb/pg_delete_aggregation_transient.c
+++ b/src/exchangedb/pg_delete_aggregation_transient.c
@@ -48,5 +48,3 @@ TEH_PG_delete_aggregation_transient (
"delete_aggregation_transient",
params);
}
-
-
diff --git a/src/exchangedb/pg_delete_shard_locks.c b/src/exchangedb/pg_delete_shard_locks.c
index e55cf25ff..dbb0f29ac 100644
--- a/src/exchangedb/pg_delete_shard_locks.c
+++ b/src/exchangedb/pg_delete_shard_locks.c
@@ -39,4 +39,3 @@ TEH_PG_delete_shard_locks (void *cls)
return GNUNET_PQ_exec_statements (pg->conn,
es);
}
-
diff --git a/src/exchangedb/pg_do_age_withdraw.c b/src/exchangedb/pg_do_age_withdraw.c
new file mode 100644
index 000000000..970e65b5d
--- /dev/null
+++ b/src/exchangedb/pg_do_age_withdraw.c
@@ -0,0 +1,108 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_do_batch_withdraw.c
+ * @brief Implementation of the do_batch_withdraw function for Postgres
+ * @author Özgür Kesim
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_exchangedb_plugin.h"
+#include "taler_pq_lib.h"
+#include "taler_pq_lib.h"
+#include "pg_do_batch_withdraw.h"
+#include "pg_helper.h"
+#include <gnunet/gnunet_time_lib.h>
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_do_age_withdraw (
+ void *cls,
+ const struct TALER_EXCHANGEDB_AgeWithdraw *commitment,
+ struct GNUNET_TIME_Timestamp now,
+ bool *found,
+ bool *balance_ok,
+ struct TALER_Amount *reserve_balance,
+ bool *age_ok,
+ uint16_t *required_age,
+ uint32_t *reserve_birthday,
+ bool *conflict)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_TIME_Timestamp gc;
+ struct GNUNET_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_amount (pg->conn,
+ &commitment->amount_with_fee),
+ GNUNET_PQ_query_param_auto_from_type (&commitment->reserve_pub),
+ GNUNET_PQ_query_param_auto_from_type (&commitment->reserve_sig),
+ GNUNET_PQ_query_param_timestamp (&now),
+ GNUNET_PQ_query_param_timestamp (&gc),
+ GNUNET_PQ_query_param_auto_from_type (&commitment->h_commitment),
+ GNUNET_PQ_query_param_uint16 (&commitment->max_age),
+ GNUNET_PQ_query_param_uint16 (&commitment->noreveal_index),
+ TALER_PQ_query_param_array_blinded_coin_hash (commitment->num_coins,
+ commitment->h_coin_evs,
+ pg->conn),
+ GNUNET_PQ_query_param_array_uint64 (commitment->num_coins,
+ commitment->denom_serials,
+ pg->conn),
+ TALER_PQ_query_param_array_blinded_denom_sig (commitment->num_coins,
+ commitment->denom_sigs,
+ pg->conn),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("reserve_found",
+ found),
+ GNUNET_PQ_result_spec_bool ("balance_ok",
+ balance_ok),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_balance",
+ reserve_balance),
+ GNUNET_PQ_result_spec_bool ("age_ok",
+ age_ok),
+ GNUNET_PQ_result_spec_uint16 ("required_age",
+ required_age),
+ GNUNET_PQ_result_spec_uint32 ("reserve_birthday",
+ reserve_birthday),
+ GNUNET_PQ_result_spec_bool ("conflict",
+ conflict),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ gc = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (now.abs_time,
+ pg->legal_reserve_expiration_time));
+ PREPARE (pg,
+ "call_age_withdraw",
+ "SELECT "
+ " reserve_found"
+ ",balance_ok"
+ ",reserve_balance"
+ ",age_ok"
+ ",required_age"
+ ",reserve_birthday"
+ ",conflict"
+ " FROM exchange_do_age_withdraw"
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11);");
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "call_age_withdraw",
+ params,
+ rs);
+ GNUNET_PQ_cleanup_query_params_closures (params);
+ return qs;
+}
diff --git a/src/exchangedb/pg_do_age_withdraw.h b/src/exchangedb/pg_do_age_withdraw.h
new file mode 100644
index 000000000..fb435a309
--- /dev/null
+++ b/src/exchangedb/pg_do_age_withdraw.h
@@ -0,0 +1,57 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_do_age_withdraw.h
+ * @brief implementation of the do_age_withdraw function for Postgres
+ * @author Özgür Kesim
+ */
+#ifndef PG_DO_AGE_WITHDRAW_H
+#define PG_DO_AGE_WITHDRAW_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+/**
+ * Perform reserve update as part of an age-withdraw operation, checking for
+ * sufficient balance and fulfillment of age requirements. Finally persisting
+ * the withdrawal details.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param commitment the commitment with all parameters
+ * @param now current time (rounded)
+ * @param[out] found set to true if the reserve was found
+ * @param[out] balance_ok set to true if the balance was sufficient
+ * @param[out] reserve_balance set to the original reserve balance (at the start of this transaction)
+ * @param[out] age_ok set to true if no age requirements are present on the reserve
+ * @param[out] required_age if @e age_ok is false, set to the maximum allowed age when withdrawing from this reserve
+ * @param[out] reserve_birthday if @e age_ok is false, set to the birthday of the reserve
+ * @param[out] conflict set to true if there already is an entry in the database for the given pair (h_commitment, reserve_pub)
+ * @return query execution status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_do_age_withdraw (
+ void *cls,
+ const struct TALER_EXCHANGEDB_AgeWithdraw *commitment,
+ const struct GNUNET_TIME_Timestamp now,
+ bool *found,
+ bool *balance_ok,
+ struct TALER_Amount *reserve_balance,
+ bool *age_ok,
+ uint16_t *required_age,
+ uint32_t *reserve_birthday,
+ bool *conflict);
+
+#endif
diff --git a/src/exchangedb/pg_do_batch_withdraw.c b/src/exchangedb/pg_do_batch_withdraw.c
index 8ef1be268..f5571ddbb 100644
--- a/src/exchangedb/pg_do_batch_withdraw.c
+++ b/src/exchangedb/pg_do_batch_withdraw.c
@@ -17,6 +17,7 @@
* @file exchangedb/pg_do_batch_withdraw.c
* @brief Implementation of the do_batch_withdraw function for Postgres
* @author Christian Grothoff
+ * @author Özgür Kesim
*/
#include "platform.h"
#include "taler_error_codes.h"
@@ -32,17 +33,23 @@ TEH_PG_do_batch_withdraw (
struct GNUNET_TIME_Timestamp now,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *amount,
+ bool do_age_check,
bool *found,
bool *balance_ok,
+ struct TALER_Amount *reserve_balance,
+ bool *age_ok,
+ uint16_t *allowed_maximum_age,
uint64_t *ruuid)
{
struct PostgresClosure *pg = cls;
struct GNUNET_TIME_Timestamp gc;
struct GNUNET_PQ_QueryParam params[] = {
- TALER_PQ_query_param_amount (amount),
+ TALER_PQ_query_param_amount (pg->conn,
+ amount),
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_timestamp (&now),
GNUNET_PQ_query_param_timestamp (&gc),
+ GNUNET_PQ_query_param_bool (do_age_check),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@@ -50,6 +57,12 @@ TEH_PG_do_batch_withdraw (
found),
GNUNET_PQ_result_spec_bool ("balance_ok",
balance_ok),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_balance",
+ reserve_balance),
+ GNUNET_PQ_result_spec_bool ("age_ok",
+ age_ok),
+ GNUNET_PQ_result_spec_uint16 ("allowed_maximum_age",
+ allowed_maximum_age),
GNUNET_PQ_result_spec_uint64 ("ruuid",
ruuid),
GNUNET_PQ_result_spec_end
@@ -58,15 +71,14 @@ TEH_PG_do_batch_withdraw (
gc = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_absolute_add (now.abs_time,
pg->legal_reserve_expiration_time));
-
-
- /* Used in #postgres_do_batch_withdraw() to
- update the reserve balance and check its status */
PREPARE (pg,
"call_batch_withdraw",
"SELECT "
" reserve_found"
",balance_ok"
+ ",reserve_balance"
+ ",age_ok"
+ ",allowed_maximum_age"
",ruuid"
" FROM exchange_do_batch_withdraw"
" ($1,$2,$3,$4,$5);");
@@ -75,4 +87,3 @@ TEH_PG_do_batch_withdraw (
params,
rs);
}
-
diff --git a/src/exchangedb/pg_do_batch_withdraw.h b/src/exchangedb/pg_do_batch_withdraw.h
index ee4bf2937..486f8d1b2 100644
--- a/src/exchangedb/pg_do_batch_withdraw.h
+++ b/src/exchangedb/pg_do_batch_withdraw.h
@@ -33,8 +33,12 @@
* @param now current time (rounded)
* @param reserve_pub public key of the reserve to debit
* @param amount total amount to withdraw
+ * @param age_check_required if true, fail if age requirements are set on the reserve
* @param[out] found set to true if the reserve was found
* @param[out] balance_ok set to true if the balance was sufficient
+ * @param[out] reserve_balance set to the original reserve balance (at the start of this transaction)
+ * @param[out] age_ok set to true if no age requirements are present on the reserve
+ * @param[out] allowed_maximum_age if @e age_ok is false, set to the maximum allowed age when withdrawing from this reserve (client needs to call age-withdraw)
* @param[out] ruuid set to the reserve's UUID (reserves table row)
* @return query execution status
*/
@@ -44,8 +48,12 @@ TEH_PG_do_batch_withdraw (
struct GNUNET_TIME_Timestamp now,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *amount,
+ bool age_check_required,
bool *found,
bool *balance_ok,
+ struct TALER_Amount *reserve_balance,
+ bool *age_ok,
+ uint16_t *allowed_maximum_age,
uint64_t *ruuid);
#endif
diff --git a/src/exchangedb/pg_do_batch_withdraw_insert.c b/src/exchangedb/pg_do_batch_withdraw_insert.c
index 8d3aac688..758f502f2 100644
--- a/src/exchangedb/pg_do_batch_withdraw_insert.c
+++ b/src/exchangedb/pg_do_batch_withdraw_insert.c
@@ -25,10 +25,11 @@
#include "pg_do_batch_withdraw_insert.h"
#include "pg_helper.h"
+
enum GNUNET_DB_QueryStatus
TEH_PG_do_batch_withdraw_insert (
void *cls,
- const struct TALER_CsNonce *nonce,
+ const union GNUNET_CRYPTO_BlindSessionNonce *nonce,
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
struct GNUNET_TIME_Timestamp now,
uint64_t ruuid,
@@ -41,7 +42,8 @@ TEH_PG_do_batch_withdraw_insert (
NULL == nonce
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_auto_from_type (nonce),
- TALER_PQ_query_param_amount (&collectable->amount_with_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ &collectable->amount_with_fee),
GNUNET_PQ_query_param_auto_from_type (&collectable->denom_pub_hash),
GNUNET_PQ_query_param_uint64 (&ruuid),
GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_sig),
@@ -59,9 +61,7 @@ TEH_PG_do_batch_withdraw_insert (
nonce_reuse),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_do_batch_withdraw_insert() to store
- the signature of a blinded coin with the blinded coin's
- details. */
+
PREPARE (pg,
"call_batch_withdraw_insert",
"SELECT "
@@ -69,7 +69,7 @@ TEH_PG_do_batch_withdraw_insert (
",out_conflict AS conflict"
",out_nonce_reuse AS nonce_reuse"
" FROM exchange_do_batch_withdraw_insert"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9);");
+ " ($1,$2,$3,$4,$5,$6,$7,$8);");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"call_batch_withdraw_insert",
params,
diff --git a/src/exchangedb/pg_do_batch_withdraw_insert.h b/src/exchangedb/pg_do_batch_withdraw_insert.h
index 6bc1a9a45..18fcfc9ae 100644
--- a/src/exchangedb/pg_do_batch_withdraw_insert.h
+++ b/src/exchangedb/pg_do_batch_withdraw_insert.h
@@ -41,7 +41,7 @@
enum GNUNET_DB_QueryStatus
TEH_PG_do_batch_withdraw_insert (
void *cls,
- const struct TALER_CsNonce *nonce,
+ const union GNUNET_CRYPTO_BlindSessionNonce *nonce,
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
struct GNUNET_TIME_Timestamp now,
uint64_t ruuid,
diff --git a/src/exchangedb/pg_do_deposit.c b/src/exchangedb/pg_do_deposit.c
index c8f25e5bc..0ba45b628 100644
--- a/src/exchangedb/pg_do_deposit.c
+++ b/src/exchangedb/pg_do_deposit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -25,62 +25,95 @@
#include "pg_do_deposit.h"
#include "pg_helper.h"
#include "pg_compute_shard.h"
+
+
enum GNUNET_DB_QueryStatus
TEH_PG_do_deposit (
void *cls,
- const struct TALER_EXCHANGEDB_Deposit *deposit,
- uint64_t known_coin_id,
- const struct TALER_PaytoHashP *h_payto,
- uint64_t *policy_details_serial_id,
+ const struct TALER_EXCHANGEDB_BatchDeposit *bd,
struct GNUNET_TIME_Timestamp *exchange_timestamp,
bool *balance_ok,
- bool *in_conflict)
+ uint32_t *bad_balance_index,
+ bool *ctr_conflict)
{
struct PostgresClosure *pg = cls;
- uint64_t deposit_shard = TEH_PG_compute_shard (&deposit->merchant_pub);
+ uint64_t deposit_shard = TEH_PG_compute_shard (&bd->merchant_pub);
+ const struct TALER_CoinSpendPublicKeyP *coin_pubs[GNUNET_NZL (bd->num_cdis)];
+ const struct TALER_CoinSpendSignatureP *coin_sigs[GNUNET_NZL (bd->num_cdis)];
+ struct TALER_Amount amounts_with_fee[GNUNET_NZL (bd->num_cdis)];
struct GNUNET_PQ_QueryParam params[] = {
- TALER_PQ_query_param_amount (&deposit->amount_with_fee),
- GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms),
- GNUNET_PQ_query_param_auto_from_type (&deposit->wire_salt),
- GNUNET_PQ_query_param_timestamp (&deposit->timestamp),
- GNUNET_PQ_query_param_timestamp (exchange_timestamp),
- GNUNET_PQ_query_param_timestamp (&deposit->refund_deadline),
- GNUNET_PQ_query_param_timestamp (&deposit->wire_deadline),
- GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
- GNUNET_PQ_query_param_string (deposit->receiver_wire_account),
- GNUNET_PQ_query_param_auto_from_type (h_payto),
- GNUNET_PQ_query_param_uint64 (&known_coin_id),
- GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
- GNUNET_PQ_query_param_auto_from_type (&deposit->csig),
+ /* data for batch_deposits */
GNUNET_PQ_query_param_uint64 (&deposit_shard),
- GNUNET_PQ_query_param_bool (deposit->has_policy),
- (NULL == policy_details_serial_id)
+ GNUNET_PQ_query_param_auto_from_type (&bd->merchant_pub),
+ GNUNET_PQ_query_param_timestamp (&bd->wallet_timestamp),
+ GNUNET_PQ_query_param_timestamp (exchange_timestamp),
+ GNUNET_PQ_query_param_timestamp (&bd->refund_deadline),
+ GNUNET_PQ_query_param_timestamp (&bd->wire_deadline),
+ GNUNET_PQ_query_param_auto_from_type (&bd->h_contract_terms),
+ (bd->no_wallet_data_hash)
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_auto_from_type (&bd->wallet_data_hash),
+ GNUNET_PQ_query_param_auto_from_type (&bd->wire_salt),
+ GNUNET_PQ_query_param_auto_from_type (&bd->wire_target_h_payto),
+ (0 == bd->policy_details_serial_id)
? GNUNET_PQ_query_param_null ()
- : GNUNET_PQ_query_param_uint64 (policy_details_serial_id),
+ : GNUNET_PQ_query_param_uint64 (&bd->policy_details_serial_id),
+ GNUNET_PQ_query_param_bool (bd->policy_blocked),
+ /* to create entry in wire_targets */
+ GNUNET_PQ_query_param_string (bd->receiver_wire_account),
+ /* arrays for coin_deposits */
+ GNUNET_PQ_query_param_array_ptrs_auto_from_type (bd->num_cdis,
+ coin_pubs,
+ pg->conn),
+ GNUNET_PQ_query_param_array_ptrs_auto_from_type (bd->num_cdis,
+ coin_sigs,
+ pg->conn),
+ TALER_PQ_query_param_array_amount (bd->num_cdis,
+ amounts_with_fee,
+ pg->conn),
GNUNET_PQ_query_param_end
};
+ bool no_time;
struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_bool ("balance_ok",
- balance_ok),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_timestamp ("exchange_timestamp",
+ exchange_timestamp),
+ &no_time),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_uint32 ("insufficient_balance_coin_index",
+ bad_balance_index),
+ balance_ok),
GNUNET_PQ_result_spec_bool ("conflicted",
- in_conflict),
- GNUNET_PQ_result_spec_timestamp ("exchange_timestamp",
- exchange_timestamp),
+ ctr_conflict),
GNUNET_PQ_result_spec_end
};
+ enum GNUNET_DB_QueryStatus qs;
+
+ for (unsigned int i = 0; i < bd->num_cdis; i++)
+ {
+ const struct TALER_EXCHANGEDB_CoinDepositInformation *cdi
+ = &bd->cdis[i];
- /* Used in #postgres_do_deposit() to execute a deposit,
- checking the coin's balance in the process as needed. */
+ amounts_with_fee[i] = cdi->amount_with_fee;
+ coin_pubs[i] = &cdi->coin.coin_pub;
+ coin_sigs[i] = &cdi->csig;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Do deposit %u = %s\n",
+ i,
+ TALER_B2S (&cdi->coin.coin_pub));
+ }
PREPARE (pg,
"call_deposit",
"SELECT "
" out_exchange_timestamp AS exchange_timestamp"
- ",out_balance_ok AS balance_ok"
+ ",out_insufficient_balance_coin_index AS insufficient_balance_coin_index"
",out_conflict AS conflicted"
" FROM exchange_do_deposit"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17);");
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "call_deposit",
- params,
- rs);
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16);");
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "call_deposit",
+ params,
+ rs);
+ GNUNET_PQ_cleanup_query_params_closures (params);
+ return qs;
}
diff --git a/src/exchangedb/pg_do_deposit.h b/src/exchangedb/pg_do_deposit.h
index e71cf0e4b..449ec04be 100644
--- a/src/exchangedb/pg_do_deposit.h
+++ b/src/exchangedb/pg_do_deposit.h
@@ -24,29 +24,28 @@
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+
+
/**
* Perform deposit operation, checking for sufficient balance
- * of the coin and possibly persisting the deposit details.
+ * of the coins and possibly persisting the deposit details.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param deposit deposit operation details
- * @param known_coin_id row of the coin in the known_coins table
- * @param h_payto hash of the merchant's bank account details
- * @param policy_details_serial_id pointer to the ID of the entry in policy_details, maybe NULL
+ * @param bd batch deposit operation details
* @param[in,out] exchange_timestamp time to use for the deposit (possibly updated)
* @param[out] balance_ok set to true if the balance was sufficient
+ * @param[out] bad_balance_index set to the first index of a coin for which the balance was insufficient,
+ * only used if @a balance_ok is set to false.
* @param[out] in_conflict set to true if the deposit conflicted
* @return query execution status
*/
enum GNUNET_DB_QueryStatus
TEH_PG_do_deposit (
void *cls,
- const struct TALER_EXCHANGEDB_Deposit *deposit,
- uint64_t known_coin_id,
- const struct TALER_PaytoHashP *h_payto,
- uint64_t *policy_details_serial_id,
+ const struct TALER_EXCHANGEDB_BatchDeposit *bd,
struct GNUNET_TIME_Timestamp *exchange_timestamp,
bool *balance_ok,
+ uint32_t *bad_balance_index,
bool *in_conflict);
#endif
diff --git a/src/exchangedb/pg_do_melt.c b/src/exchangedb/pg_do_melt.c
index 6f81ff38b..0b26386d8 100644
--- a/src/exchangedb/pg_do_melt.c
+++ b/src/exchangedb/pg_do_melt.c
@@ -40,7 +40,8 @@ TEH_PG_do_melt (
NULL == rms
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_auto_from_type (rms),
- TALER_PQ_query_param_amount (&refresh->amount_with_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ &refresh->amount_with_fee),
GNUNET_PQ_query_param_auto_from_type (&refresh->rc),
GNUNET_PQ_query_param_auto_from_type (&refresh->coin.coin_pub),
GNUNET_PQ_query_param_auto_from_type (&refresh->coin_sig),
@@ -63,7 +64,6 @@ TEH_PG_do_melt (
};
enum GNUNET_DB_QueryStatus qs;
- /* Used in #postgres_do_melt() to melt a coin. */
PREPARE (pg,
"call_melt",
"SELECT "
@@ -71,7 +71,7 @@ TEH_PG_do_melt (
",out_zombie_bad AS zombie_required"
",out_noreveal_index AS noreveal_index"
" FROM exchange_do_melt"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9);");
+ " ($1,$2,$3,$4,$5,$6,$7,$8);");
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"call_melt",
params,
diff --git a/src/exchangedb/pg_insert_history_request.c b/src/exchangedb/pg_do_purse_delete.c
index 00270b1a1..27b81cab9 100644
--- a/src/exchangedb/pg_insert_history_request.c
+++ b/src/exchangedb/pg_do_purse_delete.c
@@ -14,53 +14,51 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file exchangedb/pg_insert_history_request.c
- * @brief Implementation of the insert_history_request function for Postgres
+ * @file exchangedb/pg_do_purse_delete.c
+ * @brief Implementation of the do_purse_delete function for Postgres
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
-#include "pg_insert_history_request.h"
+#include "pg_do_purse_delete.h"
#include "pg_helper.h"
enum GNUNET_DB_QueryStatus
-TEH_PG_insert_history_request (
+TEH_PG_do_purse_delete (
void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_ReserveSignatureP *reserve_sig,
- struct GNUNET_TIME_Timestamp request_timestamp,
- const struct TALER_Amount *history_fee,
- bool *balance_ok,
- bool *idempotent)
+ const struct TALER_PurseContractPublicKeyP *purse_pub,
+ const struct TALER_PurseContractSignatureP *purse_sig,
+ bool *decided,
+ bool *found)
{
struct PostgresClosure *pg = cls;
+ struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- GNUNET_PQ_query_param_auto_from_type (reserve_sig),
- GNUNET_PQ_query_param_timestamp (&request_timestamp),
- TALER_PQ_query_param_amount (history_fee),
+ GNUNET_PQ_query_param_auto_from_type (purse_pub),
+ GNUNET_PQ_query_param_auto_from_type (purse_sig),
+ GNUNET_PQ_query_param_timestamp (&now),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_bool ("balance_ok",
- balance_ok),
- GNUNET_PQ_result_spec_bool ("idempotent",
- idempotent),
+ GNUNET_PQ_result_spec_bool ("decided",
+ decided),
+ GNUNET_PQ_result_spec_bool ("found",
+ found),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_insert_history_request() */
+
PREPARE (pg,
- "call_history_request",
- "SELECT"
- " out_balance_ok AS balance_ok"
- " ,out_idempotent AS idempotent"
- " FROM exchange_do_history_request"
- " ($1, $2, $3, $4, $5)");
+ "call_purse_delete",
+ "SELECT "
+ " out_decided AS decided"
+ ",out_found AS found"
+ " FROM exchange_do_purse_delete"
+ " ($1,$2,$3);");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "call_history_request",
+ "call_purse_delete",
params,
rs);
}
diff --git a/src/exchangedb/pg_do_purse_delete.h b/src/exchangedb/pg_do_purse_delete.h
new file mode 100644
index 000000000..01c7dd91b
--- /dev/null
+++ b/src/exchangedb/pg_do_purse_delete.h
@@ -0,0 +1,49 @@
+/*
+ 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 exchangedb/pg_do_purse_delete.h
+ * @brief implementation of the do_purse_delete function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_DO_PURSE_DELETE_H
+#define PG_DO_PURSE_DELETE_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Function called to explicitly delete a purse.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param purse_pub purse to delete
+ * @param purse_sig signature affirming the deletion
+ * @param[out] decided set to true if the purse was
+ * already decided and thus could not be deleted
+ * @param[out] found set to true if the purse was found
+ * (if false, purse could not be deleted)
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_do_purse_delete (
+ void *cls,
+ const struct TALER_PurseContractPublicKeyP *purse_pub,
+ const struct TALER_PurseContractSignatureP *purse_sig,
+ bool *decided,
+ bool *found);
+
+#endif
diff --git a/src/exchangedb/pg_do_purse_deposit.c b/src/exchangedb/pg_do_purse_deposit.c
index 25496a262..bdb1f4749 100644
--- a/src/exchangedb/pg_do_purse_deposit.c
+++ b/src/exchangedb/pg_do_purse_deposit.c
@@ -35,6 +35,7 @@ TEH_PG_do_purse_deposit (
const struct TALER_CoinSpendSignatureP *coin_sig,
const struct TALER_Amount *amount_minus_fee,
bool *balance_ok,
+ bool *too_late,
bool *conflict)
{
struct PostgresClosure *pg = cls;
@@ -46,10 +47,14 @@ TEH_PG_do_purse_deposit (
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_uint64 (&partner_id),
GNUNET_PQ_query_param_auto_from_type (purse_pub),
- TALER_PQ_query_param_amount (amount),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ amount),
GNUNET_PQ_query_param_auto_from_type (coin_pub),
GNUNET_PQ_query_param_auto_from_type (coin_sig),
- TALER_PQ_query_param_amount (amount_minus_fee),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ amount_minus_fee),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
GNUNET_PQ_query_param_timestamp (&now),
GNUNET_PQ_query_param_end
@@ -57,6 +62,8 @@ TEH_PG_do_purse_deposit (
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("balance_ok",
balance_ok),
+ GNUNET_PQ_result_spec_bool ("too_late",
+ too_late),
GNUNET_PQ_result_spec_bool ("conflict",
conflict),
GNUNET_PQ_result_spec_end
@@ -72,8 +79,9 @@ TEH_PG_do_purse_deposit (
"SELECT "
" out_balance_ok AS balance_ok"
",out_conflict AS conflict"
+ ",out_late AS too_late"
" FROM exchange_do_purse_deposit"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);");
+ " ($1,$2,$3,$4,$5,$6,$7,$8);");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"call_purse_deposit",
diff --git a/src/exchangedb/pg_do_purse_deposit.h b/src/exchangedb/pg_do_purse_deposit.h
index b4b9c35c8..779b6c0c8 100644
--- a/src/exchangedb/pg_do_purse_deposit.h
+++ b/src/exchangedb/pg_do_purse_deposit.h
@@ -43,6 +43,7 @@
* remaining balance is below @a amount;
* in this case, the return value will be
* #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT despite the failure
+ * @param[out] too_late set to true if it is too late to deposit into the purse
* @param[out] conflict set to true if the deposit failed due to a conflict (coin already spent,
* or deposited into this purse with a different amount)
* @return transaction status code
@@ -56,6 +57,7 @@ TEH_PG_do_purse_deposit (
const struct TALER_CoinSpendSignatureP *coin_sig,
const struct TALER_Amount *amount_minus_fee,
bool *balance_ok,
+ bool *too_late,
bool *conflict);
#endif
diff --git a/src/exchangedb/pg_do_purse_merge.c b/src/exchangedb/pg_do_purse_merge.c
index 518b66bf6..5a174ed02 100644
--- a/src/exchangedb/pg_do_purse_merge.c
+++ b/src/exchangedb/pg_do_purse_merge.c
@@ -75,7 +75,6 @@ TEH_PG_do_purse_merge (
&h_payto);
GNUNET_free (payto_uri);
}
- /* Used in #postgres_do_purse_merge() */
PREPARE (pg,
"call_purse_merge",
"SELECT"
diff --git a/src/exchangedb/pg_do_recoup.c b/src/exchangedb/pg_do_recoup.c
index 00f7bdd8b..07566a607 100644
--- a/src/exchangedb/pg_do_recoup.c
+++ b/src/exchangedb/pg_do_recoup.c
@@ -31,7 +31,7 @@ TEH_PG_do_recoup (
void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
uint64_t reserve_out_serial_id,
- const union TALER_DenominationBlindingKeyP *coin_bks,
+ const union GNUNET_CRYPTO_BlindingSecretP *coin_bks,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t known_coin_id,
const struct TALER_CoinSpendSignatureP *coin_sig,
@@ -70,7 +70,6 @@ TEH_PG_do_recoup (
};
-
PREPARE (pg,
"call_recoup",
"SELECT "
diff --git a/src/exchangedb/pg_do_recoup.h b/src/exchangedb/pg_do_recoup.h
index 07a350789..2cf3eb976 100644
--- a/src/exchangedb/pg_do_recoup.h
+++ b/src/exchangedb/pg_do_recoup.h
@@ -45,7 +45,7 @@ TEH_PG_do_recoup (
void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
uint64_t reserve_out_serial_id,
- const union TALER_DenominationBlindingKeyP *coin_bks,
+ const union GNUNET_CRYPTO_BlindingSecretP *coin_bks,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t known_coin_id,
const struct TALER_CoinSpendSignatureP *coin_sig,
diff --git a/src/exchangedb/pg_do_recoup_refresh.c b/src/exchangedb/pg_do_recoup_refresh.c
index be5e4705d..7d099bcd5 100644
--- a/src/exchangedb/pg_do_recoup_refresh.c
+++ b/src/exchangedb/pg_do_recoup_refresh.c
@@ -30,7 +30,7 @@ TEH_PG_do_recoup_refresh (
void *cls,
const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
uint64_t rrc_serial,
- const union TALER_DenominationBlindingKeyP *coin_bks,
+ const union GNUNET_CRYPTO_BlindingSecretP *coin_bks,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t known_coin_id,
const struct TALER_CoinSpendSignatureP *coin_sig,
diff --git a/src/exchangedb/pg_do_recoup_refresh.h b/src/exchangedb/pg_do_recoup_refresh.h
index 3ac0f0a07..16b0fd208 100644
--- a/src/exchangedb/pg_do_recoup_refresh.h
+++ b/src/exchangedb/pg_do_recoup_refresh.h
@@ -46,11 +46,12 @@ TEH_PG_do_recoup_refresh (
void *cls,
const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
uint64_t rrc_serial,
- const union TALER_DenominationBlindingKeyP *coin_bks,
+ const union GNUNET_CRYPTO_BlindingSecretP *coin_bks,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t known_coin_id,
const struct TALER_CoinSpendSignatureP *coin_sig,
struct GNUNET_TIME_Timestamp *recoup_timestamp,
bool *recoup_ok,
bool *internal_failure);
+
#endif
diff --git a/src/exchangedb/pg_do_refund.c b/src/exchangedb/pg_do_refund.c
index 1059f9cbb..194dab18d 100644
--- a/src/exchangedb/pg_do_refund.c
+++ b/src/exchangedb/pg_do_refund.c
@@ -25,6 +25,8 @@
#include "pg_do_refund.h"
#include "pg_helper.h"
#include "pg_compute_shard.h"
+
+
enum GNUNET_DB_QueryStatus
TEH_PG_do_refund (
void *cls,
@@ -40,9 +42,12 @@ TEH_PG_do_refund (
uint64_t deposit_shard = TEH_PG_compute_shard (&refund->details.merchant_pub);
struct TALER_Amount amount_without_fee;
struct GNUNET_PQ_QueryParam params[] = {
- TALER_PQ_query_param_amount (&refund->details.refund_amount),
- TALER_PQ_query_param_amount (&amount_without_fee),
- TALER_PQ_query_param_amount (deposit_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ &refund->details.refund_amount),
+ TALER_PQ_query_param_amount (pg->conn,
+ &amount_without_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ deposit_fee),
GNUNET_PQ_query_param_auto_from_type (&refund->details.h_contract_terms),
GNUNET_PQ_query_param_uint64 (&refund->details.rtransaction_id),
GNUNET_PQ_query_param_uint64 (&deposit_shard),
@@ -72,7 +77,6 @@ TEH_PG_do_refund (
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- /* Used in #postgres_do_refund() to refund a deposit. */
PREPARE (pg,
"call_refund",
"SELECT "
@@ -81,8 +85,7 @@ TEH_PG_do_refund (
",out_gone AS gone"
",out_conflict AS conflict"
" FROM exchange_do_refund"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13);");
-
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"call_refund",
params,
diff --git a/src/exchangedb/pg_do_reserve_open.c b/src/exchangedb/pg_do_reserve_open.c
index 542d1f468..b15c96dd1 100644
--- a/src/exchangedb/pg_do_reserve_open.c
+++ b/src/exchangedb/pg_do_reserve_open.c
@@ -38,44 +38,64 @@ TEH_PG_do_reserve_open (
struct GNUNET_TIME_Timestamp now,
const struct TALER_Amount *open_fee,
bool *no_funds,
+ struct TALER_Amount *reserve_balance,
struct TALER_Amount *open_cost,
struct GNUNET_TIME_Timestamp *final_expiration)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- TALER_PQ_query_param_amount (total_paid),
- TALER_PQ_query_param_amount (reserve_payment),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ total_paid),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ reserve_payment),
GNUNET_PQ_query_param_uint32 (&min_purse_limit),
GNUNET_PQ_query_param_uint32 (&pg->def_purse_limit),
GNUNET_PQ_query_param_auto_from_type (reserve_sig),
GNUNET_PQ_query_param_timestamp (&desired_expiration),
GNUNET_PQ_query_param_relative_time (&pg->legal_reserve_expiration_time),
GNUNET_PQ_query_param_timestamp (&now),
- TALER_PQ_query_param_amount (open_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ open_fee),
GNUNET_PQ_query_param_end
};
+ bool no_reserve = true;
struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("out_open_cost",
+ TALER_PQ_result_spec_amount ("out_open_cost",
+ pg->currency,
open_cost),
+ TALER_PQ_result_spec_amount ("out_reserve_balance",
+ pg->currency,
+ reserve_balance),
GNUNET_PQ_result_spec_timestamp ("out_final_expiration",
final_expiration),
+ GNUNET_PQ_result_spec_bool ("out_no_reserve",
+ &no_reserve),
GNUNET_PQ_result_spec_bool ("out_no_funds",
no_funds),
GNUNET_PQ_result_spec_end
};
+ enum GNUNET_DB_QueryStatus qs;
PREPARE (pg,
"do_reserve_open",
"SELECT "
- " out_open_cost_val"
- ",out_open_cost_frac"
+ " out_open_cost"
",out_final_expiration"
",out_no_funds"
+ ",out_no_reserve"
+ ",out_reserve_balance"
" FROM exchange_do_reserve_open"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13);");
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "do_reserve_open",
- params,
- rs);
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);");
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "do_reserve_open",
+ params,
+ rs);
+ if (qs <= 0)
+ return qs;
+ if (no_reserve)
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ return qs;
}
diff --git a/src/exchangedb/pg_do_reserve_open.h b/src/exchangedb/pg_do_reserve_open.h
index acf2d67ee..432f3f664 100644
--- a/src/exchangedb/pg_do_reserve_open.h
+++ b/src/exchangedb/pg_do_reserve_open.h
@@ -39,6 +39,7 @@
* @param now when did we the client initiate the action
* @param open_fee annual fee to be charged for the open operation by the exchange
* @param[out] no_funds set to true if reserve balance is insufficient
+ * @param[out] reserve_balance set to the original reserve balance (at the start of this transaction)
* @param[out] open_cost set to the actual cost
* @param[out] final_expiration when will the reserve expire now
* @return transaction status code
@@ -55,6 +56,7 @@ TEH_PG_do_reserve_open (
struct GNUNET_TIME_Timestamp now,
const struct TALER_Amount *open_fee,
bool *no_funds,
+ struct TALER_Amount *reserve_balance,
struct TALER_Amount *open_cost,
struct GNUNET_TIME_Timestamp *final_expiration);
diff --git a/src/exchangedb/pg_do_reserve_purse.c b/src/exchangedb/pg_do_reserve_purse.c
index cb8f83d41..e03e23fec 100644
--- a/src/exchangedb/pg_do_reserve_purse.c
+++ b/src/exchangedb/pg_do_reserve_purse.c
@@ -57,15 +57,27 @@ TEH_PG_do_reserve_purse (
struct PostgresClosure *pg = cls;
struct TALER_Amount zero_fee;
struct TALER_PaytoHashP h_payto;
+ struct GNUNET_TIME_Timestamp reserve_expiration
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
+ pg->idle_reserve_expiration_time));
+ struct GNUNET_TIME_Timestamp reserve_gc
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
+ pg->legal_reserve_expiration_time));
+
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (purse_pub),
GNUNET_PQ_query_param_auto_from_type (merge_sig),
GNUNET_PQ_query_param_timestamp (&merge_timestamp),
+ GNUNET_PQ_query_param_timestamp (&reserve_expiration),
+ GNUNET_PQ_query_param_timestamp (&reserve_gc),
GNUNET_PQ_query_param_auto_from_type (reserve_sig),
GNUNET_PQ_query_param_bool (NULL == purse_fee),
- TALER_PQ_query_param_amount (NULL == purse_fee
- ? &zero_fee
- : purse_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ NULL == purse_fee
+ ? &zero_fee
+ : purse_fee),
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_end
@@ -100,7 +112,7 @@ TEH_PG_do_reserve_purse (
",out_no_reserve AS no_reserve"
",out_conflict AS conflict"
" FROM exchange_do_reserve_purse"
- " ($1, $2, $3, $4, $5, $6, $7, $8, $9);");
+ " ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"call_reserve_purse",
diff --git a/src/exchangedb/pg_do_withdraw.c b/src/exchangedb/pg_do_withdraw.c
deleted file mode 100644
index 87e4dd1d0..000000000
--- a/src/exchangedb/pg_do_withdraw.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- 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 exchangedb/pg_do_withdraw.c
- * @brief Implementation of the do_withdraw function for Postgres
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "taler_error_codes.h"
-#include "taler_dbevents.h"
-#include "taler_pq_lib.h"
-#include "pg_do_withdraw.h"
-#include "pg_helper.h"
-
-
-enum GNUNET_DB_QueryStatus
-TEH_PG_do_withdraw (
- void *cls,
- const struct TALER_CsNonce *nonce,
- const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
- struct GNUNET_TIME_Timestamp now,
- bool *found,
- bool *balance_ok,
- bool *nonce_ok,
- uint64_t *ruuid)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_TIME_Timestamp gc;
- struct GNUNET_PQ_QueryParam params[] = {
- NULL == nonce
- ? GNUNET_PQ_query_param_null ()
- : GNUNET_PQ_query_param_auto_from_type (nonce),
- TALER_PQ_query_param_amount (&collectable->amount_with_fee),
- GNUNET_PQ_query_param_auto_from_type (&collectable->denom_pub_hash),
- GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_pub),
- GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_sig),
- GNUNET_PQ_query_param_auto_from_type (&collectable->h_coin_envelope),
- TALER_PQ_query_param_blinded_denom_sig (&collectable->sig),
- GNUNET_PQ_query_param_timestamp (&now),
- GNUNET_PQ_query_param_timestamp (&gc),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_bool ("reserve_found",
- found),
- GNUNET_PQ_result_spec_bool ("balance_ok",
- balance_ok),
- GNUNET_PQ_result_spec_bool ("nonce_ok",
- nonce_ok),
- GNUNET_PQ_result_spec_uint64 ("ruuid",
- ruuid),
- GNUNET_PQ_result_spec_end
- };
-
- PREPARE (pg,
- "call_withdraw",
- "SELECT "
- " reserve_found"
- ",balance_ok"
- ",nonce_ok"
- ",ruuid"
- " FROM exchange_do_withdraw"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);");
- gc = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_add (now.abs_time,
- pg->legal_reserve_expiration_time));
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "call_withdraw",
- params,
- rs);
-}
-
-
diff --git a/src/exchangedb/pg_do_withdraw.h b/src/exchangedb/pg_do_withdraw.h
deleted file mode 100644
index 406785c42..000000000
--- a/src/exchangedb/pg_do_withdraw.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- 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 exchangedb/pg_do_withdraw.h
- * @brief implementation of the do_withdraw function for Postgres
- * @author Christian Grothoff
- */
-#ifndef PG_DO_WITHDRAW_H
-#define PG_DO_WITHDRAW_H
-
-#include "taler_util.h"
-#include "taler_json_lib.h"
-#include "taler_exchangedb_plugin.h"
-
-/**
- * Perform withdraw operation, checking for sufficient balance
- * and possibly persisting the withdrawal details.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param nonce client-contributed input for CS denominations that must be checked for idempotency, or NULL for non-CS withdrawals
- * @param[in,out] collectable corresponding collectable coin (blind signature) if a coin is found; possibly updated if a (different) signature exists already
- * @param now current time (rounded)
- * @param[out] found set to true if the reserve was found
- * @param[out] balance_ok set to true if the balance was sufficient
- * @param[out] nonce_ok set to false if the nonce was reused
- * @param[out] ruuid set to the reserve's UUID (reserves table row)
- * @return query execution status
- */
-enum GNUNET_DB_QueryStatus
-TEH_PG_do_withdraw (
- void *cls,
- const struct TALER_CsNonce *nonce,
- const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
- struct GNUNET_TIME_Timestamp now,
- bool *found,
- bool *balance_ok,
- bool *nonce_ok,
- uint64_t *ruuid);
-
-#endif
diff --git a/src/exchangedb/pg_drain_kyc_alert.c b/src/exchangedb/pg_drain_kyc_alert.c
index d635d95e9..4388334e9 100644
--- a/src/exchangedb/pg_drain_kyc_alert.c
+++ b/src/exchangedb/pg_drain_kyc_alert.c
@@ -28,8 +28,8 @@
enum GNUNET_DB_QueryStatus
TEH_PG_drain_kyc_alert (void *cls,
- uint32_t trigger_type,
- struct TALER_PaytoHashP *h_payto)
+ uint32_t trigger_type,
+ struct TALER_PaytoHashP *h_payto)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
diff --git a/src/exchangedb/pg_drain_kyc_alert.h b/src/exchangedb/pg_drain_kyc_alert.h
index bfaf04892..7425f472d 100644
--- a/src/exchangedb/pg_drain_kyc_alert.h
+++ b/src/exchangedb/pg_drain_kyc_alert.h
@@ -34,7 +34,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_drain_kyc_alert (void *cls,
- uint32_t trigger_type,
+ uint32_t trigger_type,
struct TALER_PaytoHashP *h_payto);
#endif
diff --git a/src/exchangedb/pg_drop_tables.c b/src/exchangedb/pg_drop_tables.c
index 4693e1154..55857018b 100644
--- a/src/exchangedb/pg_drop_tables.c
+++ b/src/exchangedb/pg_drop_tables.c
@@ -43,7 +43,6 @@ TEH_PG_drop_tables (void *cls)
{
GNUNET_PQ_disconnect (pg->conn);
pg->conn = NULL;
- pg->init = false;
}
conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
"exchangedb-postgres",
diff --git a/src/exchangedb/pg_ensure_coin_known.c b/src/exchangedb/pg_ensure_coin_known.c
index 6b2385adb..307b8df52 100644
--- a/src/exchangedb/pg_ensure_coin_known.c
+++ b/src/exchangedb/pg_ensure_coin_known.c
@@ -21,6 +21,7 @@
#include "platform.h"
#include "taler_error_codes.h"
#include "taler_dbevents.h"
+#include "taler_exchangedb_plugin.h"
#include "taler_pq_lib.h"
#include "pg_ensure_coin_known.h"
#include "pg_helper.h"
@@ -28,10 +29,10 @@
enum TALER_EXCHANGEDB_CoinKnownStatus
TEH_PG_ensure_coin_known (void *cls,
- const struct TALER_CoinPublicInfo *coin,
- uint64_t *known_coin_id,
- struct TALER_DenominationHashP *denom_hash,
- struct TALER_AgeCommitmentHash *h_age_commitment)
+ const struct TALER_CoinPublicInfo *coin,
+ uint64_t *known_coin_id,
+ struct TALER_DenominationHashP *denom_hash,
+ struct TALER_AgeCommitmentHash *h_age_commitment)
{
struct PostgresClosure *pg = cls;
enum GNUNET_DB_QueryStatus qs;
@@ -41,7 +42,9 @@ TEH_PG_ensure_coin_known (void *cls,
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub),
GNUNET_PQ_query_param_auto_from_type (&coin->denom_pub_hash),
- GNUNET_PQ_query_param_auto_from_type (&coin->h_age_commitment),
+ coin->no_age_commitment
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_auto_from_type (&coin->h_age_commitment),
TALER_PQ_query_param_denom_sig (&coin->denom_sig),
GNUNET_PQ_query_param_end
};
@@ -60,23 +63,20 @@ TEH_PG_ensure_coin_known (void *cls,
&is_age_hash_null),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_insert_known_coin() to store the denomination public
- key and signature for a coin known to the exchange.
- See also:
- https://stackoverflow.com/questions/34708509/how-to-use-returning-with-on-conflict-in-postgresql/37543015#37543015
- */
+ /*
+ See also:
+ https://stackoverflow.com/questions/34708509/how-to-use-returning-with-on-conflict-in-postgresql/37543015#37543015
+ */
PREPARE (pg,
"insert_known_coin",
"WITH dd"
" (denominations_serial"
- " ,coin_val"
- " ,coin_frac"
+ " ,coin"
" ) AS ("
" SELECT "
" denominations_serial"
- " ,coin_val"
- " ,coin_frac"
+ " ,coin"
" FROM denominations"
" WHERE denom_pub_hash=$2"
" ), input_rows"
@@ -88,15 +88,13 @@ TEH_PG_ensure_coin_known (void *cls,
" ,denominations_serial"
" ,age_commitment_hash"
" ,denom_sig"
- " ,remaining_val"
- " ,remaining_frac"
+ " ,remaining"
" ) SELECT "
" $1"
" ,denominations_serial"
" ,$3"
" ,$4"
- " ,coin_val"
- " ,coin_frac"
+ " ,coin"
" FROM dd"
" ON CONFLICT DO NOTHING" /* CONFLICT on (coin_pub) */
" RETURNING "
@@ -146,13 +144,25 @@ TEH_PG_ensure_coin_known (void *cls,
return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
}
- if ( (! is_age_hash_null) &&
- (0 != GNUNET_memcmp (h_age_commitment,
- &coin->h_age_commitment)) )
+ if (is_age_hash_null != coin->no_age_commitment)
+ {
+ if (is_age_hash_null)
+ {
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NULL;
+ }
+ else
+ {
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NON_NULL;
+ }
+ }
+ else if ( (! is_age_hash_null) &&
+ (0 != GNUNET_memcmp (h_age_commitment,
+ &coin->h_age_commitment)) )
{
- GNUNET_break (GNUNET_is_zero (h_age_commitment));
GNUNET_break_op (0);
- return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
+ return TALER_EXCHANGEDB_CKS_AGE_CONFLICT_VALUE_DIFFERS;
}
return TALER_EXCHANGEDB_CKS_PRESENT;
diff --git a/src/exchangedb/pg_ensure_coin_known.h b/src/exchangedb/pg_ensure_coin_known.h
index 76581d4b7..68c101735 100644
--- a/src/exchangedb/pg_ensure_coin_known.h
+++ b/src/exchangedb/pg_ensure_coin_known.h
@@ -37,9 +37,9 @@
*/
enum TALER_EXCHANGEDB_CoinKnownStatus
TEH_PG_ensure_coin_known (void *cls,
- const struct TALER_CoinPublicInfo *coin,
- uint64_t *known_coin_id,
- struct TALER_DenominationHashP *denom_hash,
+ const struct TALER_CoinPublicInfo *coin,
+ uint64_t *known_coin_id,
+ struct TALER_DenominationHashP *denom_hash,
struct TALER_AgeCommitmentHash *h_age_commitment);
#endif
diff --git a/src/exchangedb/pg_event_listen.c b/src/exchangedb/pg_event_listen.c
index c557ed3c5..6e1d32843 100644
--- a/src/exchangedb/pg_event_listen.c
+++ b/src/exchangedb/pg_event_listen.c
@@ -38,10 +38,10 @@
*/
struct GNUNET_DB_EventHandler *
TEH_PG_event_listen (void *cls,
- struct GNUNET_TIME_Relative timeout,
- const struct GNUNET_DB_EventHeaderP *es,
- GNUNET_DB_EventCallback cb,
- void *cb_cls)
+ struct GNUNET_TIME_Relative timeout,
+ const struct GNUNET_DB_EventHeaderP *es,
+ GNUNET_DB_EventCallback cb,
+ void *cb_cls)
{
struct PostgresClosure *pg = cls;
diff --git a/src/exchangedb/pg_event_listen.h b/src/exchangedb/pg_event_listen.h
index 1be140776..7e1e83a0e 100644
--- a/src/exchangedb/pg_event_listen.h
+++ b/src/exchangedb/pg_event_listen.h
@@ -37,9 +37,9 @@
*/
struct GNUNET_DB_EventHandler *
TEH_PG_event_listen (void *cls,
- struct GNUNET_TIME_Relative timeout,
- const struct GNUNET_DB_EventHeaderP *es,
- GNUNET_DB_EventCallback cb,
+ struct GNUNET_TIME_Relative timeout,
+ const struct GNUNET_DB_EventHeaderP *es,
+ GNUNET_DB_EventCallback cb,
void *cb_cls);
#endif
diff --git a/src/exchangedb/pg_event_listen_cancel.c b/src/exchangedb/pg_event_listen_cancel.c
index 5d65827c2..9d776684d 100644
--- a/src/exchangedb/pg_event_listen_cancel.c
+++ b/src/exchangedb/pg_event_listen_cancel.c
@@ -26,10 +26,9 @@
#include "pg_helper.h"
-
void
TEH_PG_event_listen_cancel (void *cls,
- struct GNUNET_DB_EventHandler *eh)
+ struct GNUNET_DB_EventHandler *eh)
{
(void) cls;
diff --git a/src/exchangedb/pg_event_notify.c b/src/exchangedb/pg_event_notify.c
index 577f4acb8..188855775 100644
--- a/src/exchangedb/pg_event_notify.c
+++ b/src/exchangedb/pg_event_notify.c
@@ -28,9 +28,9 @@
void
TEH_PG_event_notify (void *cls,
- const struct GNUNET_DB_EventHeaderP *es,
- const void *extra,
- size_t extra_size)
+ const struct GNUNET_DB_EventHeaderP *es,
+ const void *extra,
+ size_t extra_size)
{
struct PostgresClosure *pg = cls;
diff --git a/src/exchangedb/pg_event_notify.h b/src/exchangedb/pg_event_notify.h
index 3b937cbab..85069659b 100644
--- a/src/exchangedb/pg_event_notify.h
+++ b/src/exchangedb/pg_event_notify.h
@@ -35,8 +35,8 @@
*/
void
TEH_PG_event_notify (void *cls,
- const struct GNUNET_DB_EventHeaderP *es,
- const void *extra,
+ const struct GNUNET_DB_EventHeaderP *es,
+ const void *extra,
size_t extra_size);
#endif
diff --git a/src/exchangedb/pg_find_aggregation_transient.c b/src/exchangedb/pg_find_aggregation_transient.c
index a5f367b5f..b931188a8 100644
--- a/src/exchangedb/pg_find_aggregation_transient.c
+++ b/src/exchangedb/pg_find_aggregation_transient.c
@@ -128,12 +128,11 @@ TEH_PG_find_aggregation_transient (
.pg = pg,
.status = GNUNET_OK
};
- /* Used in #postgres_find_aggregation_transient() */
+
PREPARE (pg,
"find_transient_aggregations",
"SELECT"
- " amount_val"
- " ,amount_frac"
+ " amount"
" ,wtid_raw"
" ,merchant_pub"
" ,payto_uri"
diff --git a/src/exchangedb/pg_gc.h b/src/exchangedb/pg_gc.h
index 9e6ffbc3d..803581488 100644
--- a/src/exchangedb/pg_gc.h
+++ b/src/exchangedb/pg_gc.h
@@ -35,4 +35,5 @@
*/
enum GNUNET_GenericReturnValue
TEH_PG_gc (void *cls);
+
#endif
diff --git a/src/exchangedb/pg_get_age_withdraw.c b/src/exchangedb/pg_get_age_withdraw.c
new file mode 100644
index 000000000..ea4d3b909
--- /dev/null
+++ b/src/exchangedb/pg_get_age_withdraw.c
@@ -0,0 +1,119 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_get_age_withdraw.c
+ * @brief Implementation of the get_age_withdraw function for Postgres
+ * @author Özgür Kesim
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_get_age_withdraw.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_age_withdraw (
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_AgeWithdrawCommitmentHashP *ach,
+ struct TALER_EXCHANGEDB_AgeWithdraw *aw)
+{
+ enum GNUNET_DB_QueryStatus ret;
+ struct PostgresClosure *pg = cls;
+ size_t num_sigs;
+ size_t num_hashes;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+ GNUNET_PQ_query_param_auto_from_type (ach),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("h_commitment",
+ &aw->h_commitment),
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
+ &aw->reserve_sig),
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+ &aw->reserve_pub),
+ GNUNET_PQ_result_spec_uint16 ("max_age",
+ &aw->max_age),
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ pg->currency,
+ &aw->amount_with_fee),
+ GNUNET_PQ_result_spec_uint16 ("noreveal_index",
+ &aw->noreveal_index),
+ TALER_PQ_result_spec_array_blinded_coin_hash (
+ pg->conn,
+ "h_blind_evs",
+ &aw->num_coins,
+ &aw->h_coin_evs),
+ TALER_PQ_result_spec_array_blinded_denom_sig (
+ pg->conn,
+ "denom_sigs",
+ &num_sigs,
+ &aw->denom_sigs),
+ TALER_PQ_result_spec_array_denom_hash (
+ pg->conn,
+ "denom_pub_hashes",
+ &num_hashes,
+ &aw->denom_pub_hashes),
+ GNUNET_PQ_result_spec_end
+ };
+
+ PREPARE (pg,
+ "get_age_withdraw",
+ "SELECT"
+ " h_commitment"
+ ",reserve_sig"
+ ",reserve_pub"
+ ",max_age"
+ ",amount_with_fee"
+ ",noreveal_index"
+ ",h_blind_evs"
+ ",denom_sigs"
+ ",ARRAY("
+ " SELECT denominations.denom_pub_hash FROM ("
+ " SELECT UNNEST(denom_serials) AS id,"
+ " generate_subscripts(denom_serials, 1) AS nr" /* for order */
+ " ) AS denoms"
+ " LEFT JOIN denominations ON denominations.denominations_serial=denoms.id"
+ ") AS denom_pub_hashes"
+ " FROM age_withdraw"
+ " WHERE reserve_pub=$1 and h_commitment=$2;");
+
+ ret = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "get_age_withdraw",
+ params,
+ rs);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ret)
+ return ret;
+
+ if ((aw->num_coins != num_sigs) ||
+ (aw->num_coins != num_hashes))
+ {
+ GNUNET_break (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "got inconsistent number of entries from DB: "
+ "num_coins=%ld, num_sigs=%ld, num_hashes=%ld\n",
+ aw->num_coins,
+ num_sigs,
+ num_hashes);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ return ret;
+}
diff --git a/src/exchangedb/pg_get_age_withdraw.h b/src/exchangedb/pg_get_age_withdraw.h
new file mode 100644
index 000000000..2257aa43c
--- /dev/null
+++ b/src/exchangedb/pg_get_age_withdraw.h
@@ -0,0 +1,45 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_get_age_withdraw.h
+ * @brief implementation of the get_age_withdraw function for Postgres
+ * @author Özgür KESIM
+ */
+#ifndef PG_GET_AGE_WITHDRAW_H
+#define PG_GET_AGE_WITHDRAW_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+/**
+ * Locate the response for a age-withdraw request under a hash that uniquely
+ * identifies the age-withdraw operation. Used to ensure idempotency of the
+ * request.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param reserve_pub public key of the reserve for which the age-withdraw request is made
+ * @param ach hash that uniquely identifies the age-withdraw operation
+ * @param[out] aw corresponding details of the previous age-withdraw request if an entry was found
+ * @return statement execution status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_age_withdraw (
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_AgeWithdrawCommitmentHashP *ach,
+ struct TALER_EXCHANGEDB_AgeWithdraw *aw);
+#endif
diff --git a/src/exchangedb/pg_get_coin_denomination.c b/src/exchangedb/pg_get_coin_denomination.c
index e82b86aa9..9f9256f6f 100644
--- a/src/exchangedb/pg_get_coin_denomination.c
+++ b/src/exchangedb/pg_get_coin_denomination.c
@@ -49,9 +49,9 @@ TEH_PG_get_coin_denomination (
"Getting coin denomination of coin %s\n",
TALER_B2S (coin_pub));
- /* Used in #postgres_get_coin_denomination() to fetch
- the denomination public key hash for
- a coin known to the exchange. */
+ /* Used in #postgres_get_coin_denomination() to fetch
+ the denomination public key hash for
+ a coin known to the exchange. */
PREPARE (pg,
"get_coin_denomination",
"SELECT"
@@ -67,5 +67,3 @@ TEH_PG_get_coin_denomination (
params,
rs);
}
-
-
diff --git a/src/exchangedb/pg_get_coin_transactions.c b/src/exchangedb/pg_get_coin_transactions.c
index f24c9be4a..fef33a486 100644
--- a/src/exchangedb/pg_get_coin_transactions.c
+++ b/src/exchangedb/pg_get_coin_transactions.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -21,11 +21,21 @@
#include "platform.h"
#include "taler_error_codes.h"
#include "taler_dbevents.h"
+#include "taler_exchangedb_plugin.h"
#include "taler_pq_lib.h"
#include "pg_get_coin_transactions.h"
#include "pg_helper.h"
+#include "pg_start_read_committed.h"
+#include "pg_commit.h"
+#include "pg_rollback.h"
#include "plugin_exchangedb_common.h"
+/**
+ * How often do we re-try when encountering DB serialization issues?
+ * (We are read-only, so can only happen due to concurrent insert,
+ * which should be very rare.)
+ */
+#define RETRIES 3
/**
* Closure for callbacks called from #postgres_get_coin_transactions()
@@ -43,11 +53,6 @@ struct CoinHistoryContext
const struct TALER_CoinSpendPublicKeyP *coin_pub;
/**
- * Closure for all callbacks of this database plugin.
- */
- void *db_cls;
-
- /**
* Plugin context.
*/
struct PostgresClosure *pg;
@@ -57,10 +62,6 @@ struct CoinHistoryContext
*/
bool failed;
- /**
- * Set to 'true' if we found a deposit or melt (for invariant check).
- */
- bool have_deposit_or_melt;
};
@@ -86,7 +87,6 @@ add_coin_deposit (void *cls,
struct TALER_EXCHANGEDB_TransactionList *tl;
uint64_t serial_id;
- chc->have_deposit_or_melt = true;
deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
{
struct GNUNET_PQ_ResultSpec rs[] = {
@@ -100,6 +100,10 @@ add_coin_deposit (void *cls,
GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
&deposit->h_age_commitment),
&deposit->no_age_commitment),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("wallet_data_hash",
+ &deposit->wallet_data_hash),
+ &deposit->no_wallet_data_hash),
GNUNET_PQ_result_spec_timestamp ("wallet_timestamp",
&deposit->timestamp),
GNUNET_PQ_result_spec_timestamp ("refund_deadline",
@@ -116,7 +120,7 @@ add_coin_deposit (void *cls,
&deposit->receiver_wire_account),
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
&deposit->csig),
- GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
+ GNUNET_PQ_result_spec_uint64 ("coin_deposit_serial_id",
&serial_id),
GNUNET_PQ_result_spec_auto_from_type ("done",
&deposit->done),
@@ -166,7 +170,6 @@ add_coin_purse_deposit (void *cls,
struct TALER_EXCHANGEDB_TransactionList *tl;
uint64_t serial_id;
- chc->have_deposit_or_melt = true;
deposit = GNUNET_new (struct TALER_EXCHANGEDB_PurseDepositListEntry);
{
bool not_finished;
@@ -185,8 +188,10 @@ add_coin_purse_deposit (void *cls,
NULL),
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
&deposit->coin_sig),
- GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
- &deposit->h_age_commitment),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
+ &deposit->h_age_commitment),
+ &deposit->no_age_commitment),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_bool ("refunded",
&deposit->refunded),
@@ -240,7 +245,6 @@ add_coin_melt (void *cls,
struct TALER_EXCHANGEDB_TransactionList *tl;
uint64_t serial_id;
- chc->have_deposit_or_melt = true;
melt = GNUNET_new (struct TALER_EXCHANGEDB_MeltListEntry);
{
struct GNUNET_PQ_ResultSpec rs[] = {
@@ -663,6 +667,11 @@ add_coin_reserve_open (void *cls,
struct Work
{
/**
+ * Name of the table.
+ */
+ const char *table;
+
+ /**
* SQL prepared statement name.
*/
const char *statement;
@@ -674,92 +683,218 @@ struct Work
};
-enum GNUNET_DB_QueryStatus
-TEH_PG_get_coin_transactions (
- void *cls,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- struct TALER_EXCHANGEDB_TransactionList **tlp)
+/**
+ * We found a coin history entry. Lookup details
+ * from the respective table and store in @a cls.
+ *
+ * @param[in,out] cls a `struct CoinHistoryContext`
+ * @param result a coin history entry result set
+ * @param num_results total number of results in @a results
+ */
+static void
+handle_history_entry (void *cls,
+ PGresult *result,
+ unsigned int num_results)
{
- struct PostgresClosure *pg = cls;
+ struct CoinHistoryContext *chc = cls;
+ struct PostgresClosure *pg = chc->pg;
static const struct Work work[] = {
- /** #TALER_EXCHANGEDB_TT_DEPOSIT */
- { "get_deposit_with_coin_pub",
+ [TALER_EXCHANGEDB_TT_DEPOSIT] =
+ { "coin_deposits",
+ "get_deposit_with_coin_pub",
&add_coin_deposit },
- /** #TALER_EXCHANGEDB_TT_MELT */
- { "get_refresh_session_by_coin",
+ [TALER_EXCHANGEDB_TT_MELT] =
+ { "refresh_commitments",
+ "get_refresh_session_by_coin",
&add_coin_melt },
- /** #TALER_EXCHANGEDB_TT_PURSE_DEPOSIT */
- { "get_purse_deposit_by_coin_pub",
+ [TALER_EXCHANGEDB_TT_PURSE_DEPOSIT] =
+ { "purse_deposits",
+ "get_purse_deposit_by_coin_pub",
&add_coin_purse_deposit },
- /** #TALER_EXCHANGEDB_TT_PURSE_REFUND */
- { "get_purse_decision_by_coin_pub",
+ [TALER_EXCHANGEDB_TT_PURSE_REFUND] =
+ { "purse_decision",
+ "get_purse_decision_by_coin_pub",
&add_coin_purse_decision },
- /** #TALER_EXCHANGEDB_TT_REFUND */
- { "get_refunds_by_coin",
+ [TALER_EXCHANGEDB_TT_REFUND] =
+ { "refunds",
+ "get_refunds_by_coin",
&add_coin_refund },
- /** #TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP */
- { "recoup_by_old_coin",
+ [TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP] =
+ { "recoup_refresh::OLD",
+ "recoup_by_old_coin",
&add_old_coin_recoup },
- /** #TALER_EXCHANGEDB_TT_RECOUP */
- { "recoup_by_coin",
+ [TALER_EXCHANGEDB_TT_RECOUP] =
+ { "recoup",
+ "recoup_by_coin",
&add_coin_recoup },
- /** #TALER_EXCHANGEDB_TT_RECOUP_REFRESH */
- { "recoup_by_refreshed_coin",
+ [TALER_EXCHANGEDB_TT_RECOUP_REFRESH] =
+ { "recoup_refresh::NEW",
+ "recoup_by_refreshed_coin",
&add_coin_recoup_refresh },
- /** #TALER_EXCHANGEDB_TT_RESERVE_OPEN */
- { "reserve_open_by_coin",
+ [TALER_EXCHANGEDB_TT_RESERVE_OPEN] =
+ { "reserves_open_deposits",
+ "reserve_open_by_coin",
&add_coin_reserve_open },
- { NULL, NULL }
+ { NULL, NULL, NULL }
+ };
+ char *table_name;
+ uint64_t serial_id;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_string ("table_name",
+ &table_name),
+ GNUNET_PQ_result_spec_uint64 ("serial_id",
+ &serial_id),
+ GNUNET_PQ_result_spec_end
};
struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (chc->coin_pub),
+ GNUNET_PQ_query_param_uint64 (&serial_id),
+ GNUNET_PQ_query_param_end
+ };
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ enum GNUNET_DB_QueryStatus qs;
+ bool found = false;
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ chc->failed = true;
+ return;
+ }
+
+ for (unsigned int s = 0;
+ NULL != work[s].statement;
+ s++)
+ {
+ if (0 != strcmp (table_name,
+ work[s].table))
+ continue;
+ found = true;
+ qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ work[s].statement,
+ params,
+ work[s].cb,
+ chc);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Coin %s had %d transactions at %llu in table %s\n",
+ TALER_B2S (chc->coin_pub),
+ (int) qs,
+ (unsigned long long) serial_id,
+ table_name);
+ if (0 >= qs)
+ chc->failed = true;
+ break;
+ }
+ if (! found)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Coin history includes unsupported table `%s`\n",
+ table_name);
+ chc->failed = true;
+ }
+ GNUNET_PQ_cleanup_result (rs);
+ if (chc->failed)
+ break;
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_coin_transactions (
+ void *cls,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ uint64_t start_off,
+ uint64_t etag_in,
+ uint64_t *etag_out,
+ struct TALER_Amount *balance,
+ struct TALER_DenominationHashP *h_denom_pub,
+ struct TALER_EXCHANGEDB_TransactionList **tlp)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (coin_pub),
GNUNET_PQ_query_param_end
};
- enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_PQ_QueryParam lparams[] = {
+ GNUNET_PQ_query_param_auto_from_type (coin_pub),
+ GNUNET_PQ_query_param_uint64 (&start_off),
+ GNUNET_PQ_query_param_end
+ };
struct CoinHistoryContext chc = {
.head = NULL,
.coin_pub = coin_pub,
- .pg = pg,
- .db_cls = cls
+ .pg = pg
};
+ *tlp = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Getting transactions for coin %s\n",
+ TALER_B2S (coin_pub));
+ PREPARE (pg,
+ "get_coin_history_etag_balance",
+ "SELECT"
+ " ch.coin_history_serial_id"
+ ",kc.remaining"
+ ",denom.denom_pub_hash"
+ " FROM coin_history ch"
+ " JOIN known_coins kc"
+ " USING (coin_pub)"
+ " JOIN denominations denom"
+ " USING (denominations_serial)"
+ " WHERE coin_pub=$1"
+ " ORDER BY coin_history_serial_id DESC"
+ " LIMIT 1;");
+ PREPARE (pg,
+ "get_coin_history",
+ "SELECT"
+ " table_name"
+ ",serial_id"
+ " FROM coin_history"
+ " WHERE coin_pub=$1"
+ " AND coin_history_serial_id > $2"
+ " ORDER BY coin_history_serial_id DESC;");
PREPARE (pg,
"get_deposit_with_coin_pub",
"SELECT"
- " dep.amount_with_fee_val"
- ",dep.amount_with_fee_frac"
- ",denoms.fee_deposit_val"
- ",denoms.fee_deposit_frac"
+ " cdep.amount_with_fee"
+ ",denoms.fee_deposit"
",denoms.denom_pub_hash"
",kc.age_commitment_hash"
- ",dep.wallet_timestamp"
- ",dep.refund_deadline"
- ",dep.wire_deadline"
- ",dep.merchant_pub"
- ",dep.h_contract_terms"
- ",dep.wire_salt"
+ ",bdep.wallet_timestamp"
+ ",bdep.refund_deadline"
+ ",bdep.wire_deadline"
+ ",bdep.merchant_pub"
+ ",bdep.h_contract_terms"
+ ",bdep.wallet_data_hash"
+ ",bdep.wire_salt"
",wt.payto_uri"
- ",dep.coin_sig"
- ",dep.deposit_serial_id"
- ",dep.done"
- " FROM deposits dep"
- " JOIN wire_targets wt"
- " USING (wire_target_h_payto)"
- " JOIN known_coins kc"
- " ON (kc.coin_pub = dep.coin_pub)"
- " JOIN denominations denoms"
- " USING (denominations_serial)"
- " WHERE dep.coin_pub=$1;");
+ ",cdep.coin_sig"
+ ",cdep.coin_deposit_serial_id"
+ ",bdep.done"
+ " FROM coin_deposits cdep"
+ " JOIN batch_deposits bdep"
+ " USING (batch_deposit_serial_id)"
+ " JOIN wire_targets wt"
+ " USING (wire_target_h_payto)"
+ " JOIN known_coins kc"
+ " ON (kc.coin_pub = cdep.coin_pub)"
+ " JOIN denominations denoms"
+ " USING (denominations_serial)"
+ " WHERE cdep.coin_pub=$1"
+ " AND cdep.coin_deposit_serial_id=$2;");
PREPARE (pg,
"get_refresh_session_by_coin",
"SELECT"
" rc"
",old_coin_sig"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
",denoms.denom_pub_hash"
- ",denoms.fee_refresh_val"
- ",denoms.fee_refresh_frac"
+ ",denoms.fee_refresh"
",kc.age_commitment_hash"
",melt_serial_id"
" FROM refresh_commitments"
@@ -767,15 +902,14 @@ TEH_PG_get_coin_transactions (
" ON (refresh_commitments.old_coin_pub = kc.coin_pub)"
" JOIN denominations denoms"
" USING (denominations_serial)"
- " WHERE old_coin_pub=$1;");
+ " WHERE old_coin_pub=$1"
+ " AND melt_serial_id=$2;");
PREPARE (pg,
"get_purse_deposit_by_coin_pub",
"SELECT"
" partner_base_url"
- ",pd.amount_with_fee_val"
- ",pd.amount_with_fee_frac"
- ",denoms.fee_deposit_val"
- ",denoms.fee_deposit_frac"
+ ",pd.amount_with_fee"
+ ",denoms.fee_deposit"
",pd.purse_pub"
",kc.age_commitment_hash"
",pd.coin_sig"
@@ -792,110 +926,104 @@ TEH_PG_get_coin_transactions (
" ON (pd.coin_pub = kc.coin_pub)"
" JOIN denominations denoms"
" USING (denominations_serial)"
- // FIXME: use to-be-created materialized index
- // on coin_pub (query crosses partitions!)
- " WHERE pd.coin_pub=$1;");
- PREPARE (pg,
- "get_refunds_by_coin",
- "SELECT"
- " dep.merchant_pub"
- ",ref.merchant_sig"
- ",dep.h_contract_terms"
- ",ref.rtransaction_id"
- ",ref.amount_with_fee_val"
- ",ref.amount_with_fee_frac"
- ",denom.fee_refund_val "
- ",denom.fee_refund_frac "
- ",ref.refund_serial_id"
- " FROM refunds ref"
- " JOIN deposits dep"
- " ON (ref.coin_pub = dep.coin_pub AND ref.deposit_serial_id = dep.deposit_serial_id)"
- " JOIN known_coins kc"
- " ON (ref.coin_pub = kc.coin_pub)"
- " JOIN denominations denom"
- " USING (denominations_serial)"
- " WHERE ref.coin_pub=$1;");
+ " WHERE pd.purse_deposit_serial_id=$2"
+ " AND pd.coin_pub=$1;");
PREPARE (pg,
"get_purse_decision_by_coin_pub",
"SELECT"
" pdes.purse_pub"
- ",pd.amount_with_fee_val"
- ",pd.amount_with_fee_frac"
- ",denom.fee_refund_val "
- ",denom.fee_refund_frac "
+ ",pd.amount_with_fee"
+ ",denom.fee_refund"
",pdes.purse_decision_serial_id"
- " FROM purse_deposits pd"
- " JOIN purse_decision pdes"
+ " FROM purse_decision pdes"
+ " JOIN purse_deposits pd"
" USING (purse_pub)"
" JOIN known_coins kc"
" ON (pd.coin_pub = kc.coin_pub)"
" JOIN denominations denom"
" USING (denominations_serial)"
" WHERE pd.coin_pub=$1"
+ " AND pdes.purse_decision_serial_id=$2"
" AND pdes.refunded;");
PREPARE (pg,
+ "get_refunds_by_coin",
+ "SELECT"
+ " bdep.merchant_pub"
+ ",ref.merchant_sig"
+ ",bdep.h_contract_terms"
+ ",ref.rtransaction_id"
+ ",ref.amount_with_fee"
+ ",denom.fee_refund"
+ ",ref.refund_serial_id"
+ " FROM refunds ref"
+ " JOIN coin_deposits cdep"
+ " ON (ref.coin_pub = cdep.coin_pub AND ref.batch_deposit_serial_id = cdep.batch_deposit_serial_id)"
+ " JOIN batch_deposits bdep"
+ " ON (ref.batch_deposit_serial_id = bdep.batch_deposit_serial_id)"
+ " JOIN known_coins kc"
+ " ON (ref.coin_pub = kc.coin_pub)"
+ " JOIN denominations denom"
+ " USING (denominations_serial)"
+ " WHERE ref.coin_pub=$1"
+ " AND ref.refund_serial_id=$2;");
+ PREPARE (pg,
"recoup_by_old_coin",
"SELECT"
" coins.coin_pub"
- ",coin_sig"
- ",coin_blind"
- ",amount_val"
- ",amount_frac"
- ",recoup_timestamp"
+ ",rr.coin_sig"
+ ",rr.coin_blind"
+ ",rr.amount"
+ ",rr.recoup_timestamp"
",denoms.denom_pub_hash"
",coins.denom_sig"
- ",recoup_refresh_uuid"
- " FROM recoup_refresh"
+ ",rr.recoup_refresh_uuid"
+ " FROM recoup_refresh rr"
" JOIN known_coins coins"
" USING (coin_pub)"
" JOIN denominations denoms"
" USING (denominations_serial)"
- " WHERE rrc_serial IN"
+ " WHERE recoup_refresh_uuid=$2"
+ " AND rrc_serial IN"
" (SELECT rrc.rrc_serial"
- " FROM refresh_commitments"
- " JOIN refresh_revealed_coins rrc"
- " USING (melt_serial_id)"
- " WHERE old_coin_pub=$1);");
+ " FROM refresh_commitments melt"
+ " JOIN refresh_revealed_coins rrc"
+ " USING (melt_serial_id)"
+ " WHERE melt.old_coin_pub=$1);");
PREPARE (pg,
"recoup_by_coin",
"SELECT"
- " reserves.reserve_pub"
+ " res.reserve_pub"
",denoms.denom_pub_hash"
- ",coin_sig"
- ",coin_blind"
- ",amount_val"
- ",amount_frac"
- ",recoup_timestamp"
- ",recoup_uuid"
+ ",rcp.coin_sig"
+ ",rcp.coin_blind"
+ ",rcp.amount"
+ ",rcp.recoup_timestamp"
+ ",rcp.recoup_uuid"
" FROM recoup rcp"
- /* NOTE: suboptimal JOIN follows: crosses shards!
- Could theoretically be improved via a materialized
- index. But likely not worth it (query is rare and
- number of reserve shards might be limited) */
" JOIN reserves_out ro"
" USING (reserve_out_serial_id)"
- " JOIN reserves"
+ " JOIN reserves res"
" USING (reserve_uuid)"
" JOIN known_coins coins"
" USING (coin_pub)"
" JOIN denominations denoms"
" ON (denoms.denominations_serial = coins.denominations_serial)"
- " WHERE coins.coin_pub=$1;");
+ " WHERE rcp.recoup_uuid=$2"
+ " AND coins.coin_pub=$1;");
/* Used in #postgres_get_coin_transactions() to obtain recoup transactions
for a refreshed coin */
PREPARE (pg,
"recoup_by_refreshed_coin",
"SELECT"
" old_coins.coin_pub AS old_coin_pub"
- ",coin_sig"
- ",coin_blind"
- ",amount_val"
- ",amount_frac"
- ",recoup_timestamp"
+ ",rr.coin_sig"
+ ",rr.coin_blind"
+ ",rr.amount"
+ ",rr.recoup_timestamp"
",denoms.denom_pub_hash"
",coins.denom_sig"
",recoup_refresh_uuid"
- " FROM recoup_refresh"
+ " FROM recoup_refresh rr"
" JOIN refresh_revealed_coins rrc"
" USING (rrc_serial)"
" JOIN refresh_commitments rfc"
@@ -903,49 +1031,114 @@ TEH_PG_get_coin_transactions (
" JOIN known_coins old_coins"
" ON (rfc.old_coin_pub = old_coins.coin_pub)"
" JOIN known_coins coins"
- " ON (recoup_refresh.coin_pub = coins.coin_pub)"
+ " ON (rr.coin_pub = coins.coin_pub)"
" JOIN denominations denoms"
" ON (denoms.denominations_serial = coins.denominations_serial)"
- " WHERE coins.coin_pub=$1;");
+ " WHERE rr.recoup_refresh_uuid=$2"
+ " AND coins.coin_pub=$1;");
PREPARE (pg,
"reserve_open_by_coin",
"SELECT"
" reserve_open_deposit_uuid"
",coin_sig"
",reserve_sig"
- ",contribution_val"
- ",contribution_frac"
+ ",contribution"
" FROM reserves_open_deposits"
- " WHERE coin_pub=$1;");
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Getting transactions for coin %s\n",
- TALER_B2S (coin_pub));
- for (unsigned int i = 0; NULL != work[i].statement; i++)
+ " WHERE coin_pub=$1"
+ " AND reserve_open_deposit_uuid=$2;");
+
+ for (unsigned int i = 0; i<RETRIES; i++)
{
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- work[i].statement,
- params,
- work[i].cb,
- &chc);
+ enum GNUNET_DB_QueryStatus qs;
+ uint64_t end;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("coin_history_serial_id",
+ &end),
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+ h_denom_pub),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("remaining",
+ balance),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ TEH_PG_start_read_committed (pg,
+ "get-coin-transactions"))
+ {
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ /* First only check the last item, to see if
+ we even need to iterate */
+ qs = GNUNET_PQ_eval_prepared_singleton_select (
+ pg->conn,
+ "get_coin_history_etag_balance",
+ params,
+ rs);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ TEH_PG_rollback (pg);
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ TEH_PG_rollback (pg);
+ continue;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ TEH_PG_rollback (pg);
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ *etag_out = end;
+ if (end == etag_in)
+ return qs;
+ }
+ /* We indeed need to iterate over the history */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Coin %s yielded %d transactions of type %s\n",
+ "Current ETag for coin %s is %llu\n",
TALER_B2S (coin_pub),
- qs,
- work[i].statement);
- if ( (0 > qs) ||
- (chc.failed) )
+ (unsigned long long) end);
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (
+ pg->conn,
+ "get_coin_history",
+ lparams,
+ &handle_history_entry,
+ &chc);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ TEH_PG_rollback (pg);
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ TEH_PG_rollback (pg);
+ continue;
+ default:
+ break;
+ }
+ if (chc.failed)
+ {
+ TEH_PG_rollback (pg);
+ TEH_COMMON_free_coin_transaction_list (pg,
+ chc.head);
+ return GNUNET_DB_STATUS_SOFT_ERROR;
+ }
+ qs = TEH_PG_commit (pg);
+ switch (qs)
{
- if (NULL != chc.head)
- TEH_COMMON_free_coin_transaction_list (cls,
- chc.head);
- *tlp = NULL;
- if (chc.failed)
- qs = GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ TEH_COMMON_free_coin_transaction_list (pg,
+ chc.head);
+ chc.head = NULL;
return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ TEH_COMMON_free_coin_transaction_list (pg,
+ chc.head);
+ chc.head = NULL;
+ continue;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ *tlp = chc.head;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
}
- *tlp = chc.head;
- if (NULL == chc.head)
- return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ return GNUNET_DB_STATUS_SOFT_ERROR;
}
diff --git a/src/exchangedb/pg_get_coin_transactions.h b/src/exchangedb/pg_get_coin_transactions.h
index c95fd0947..46e32e094 100644
--- a/src/exchangedb/pg_get_coin_transactions.h
+++ b/src/exchangedb/pg_get_coin_transactions.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022, 2023 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
@@ -27,18 +27,35 @@
/**
- * Compile a list of all (historic) transactions performed with the given coin
- * (/refresh/melt, /deposit, /refund and /recoup operations).
+ * Compile a list of (historic) transactions performed with the given coin
+ * (melt, refund, recoup and deposit operations). Should return 0 if the @a
+ * coin_pub is unknown, otherwise determine @a etag_out and if it is past @a
+ * etag_in return the history after @a start_off. @a etag_out should be set
+ * to the last row ID of the given @a coin_pub in the coin history table.
*
- * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param cls the @e cls of this struct with the plugin-specific state
* @param coin_pub coin to investigate
- * @param[out] tlp set to list of transactions, NULL if coin is fresh
+ * @param start_off starting offset from which on to return entries
+ * @param etag_in up to this offset the client already has a response, do not
+ * return anything unless @a etag_out will be larger
+ * @param[out] etag_out set to the latest history offset known for this @a coin_pub
+ * @param[out] balance set to current balance of the coin
+ * @param[out] h_denom_pub set to denomination public key of the coin
+ * @param[out] tlp set to list of transactions, set to NULL if coin has no
+ * transaction history past @a start_off or if @a etag_in is equal
+ * to the value written to @a etag_out.
* @return database transaction status
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_coin_transactions (
void *cls,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ uint64_t start_off,
+ uint64_t etag_in,
+ uint64_t *etag_out,
+ struct TALER_Amount *balance,
+ struct TALER_DenominationHashP *h_denom_pub,
struct TALER_EXCHANGEDB_TransactionList **tlp);
+
#endif
diff --git a/src/exchangedb/pg_get_denomination_info.c b/src/exchangedb/pg_get_denomination_info.c
index 97250b621..4bae29795 100644
--- a/src/exchangedb/pg_get_denomination_info.c
+++ b/src/exchangedb/pg_get_denomination_info.c
@@ -64,8 +64,6 @@ TEH_PG_get_denomination_info (
GNUNET_PQ_result_spec_end
};
-
- /* Used in #postgres_get_denomination_info() */
PREPARE (pg,
"denomination_get",
"SELECT"
@@ -74,19 +72,14 @@ TEH_PG_get_denomination_info (
",expire_withdraw"
",expire_deposit"
",expire_legal"
- ",coin_val" /* value of this denom */
- ",coin_frac" /* fractional value of this denom */
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refund_val"
- ",fee_refund_frac"
+ ",coin" /* value of this denom */
+ ",fee_withdraw"
+ ",fee_deposit"
+ ",fee_refresh"
+ ",fee_refund"
",age_mask"
" FROM denominations"
- " WHERE denom_pub_hash=$1;");
+ " WHERE denom_pub_hash=$1;");
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"denomination_get",
params,
@@ -96,4 +89,3 @@ TEH_PG_get_denomination_info (
issue->denom_hash = *denom_pub_hash;
return qs;
}
-
diff --git a/src/exchangedb/pg_get_denomination_revocation.c b/src/exchangedb/pg_get_denomination_revocation.c
index 4d29d88c4..5e7a3a322 100644
--- a/src/exchangedb/pg_get_denomination_revocation.c
+++ b/src/exchangedb/pg_get_denomination_revocation.c
@@ -46,15 +46,15 @@ TEH_PG_get_denomination_revocation (
};
PREPARE (pg,
- "denomination_revocation_get",
- "SELECT"
- " master_sig"
- ",denom_revocations_serial_id"
- " FROM denomination_revocations"
- " WHERE denominations_serial="
- " (SELECT denominations_serial"
- " FROM denominations"
- " WHERE denom_pub_hash=$1);");
+ "denomination_revocation_get",
+ "SELECT"
+ " master_sig"
+ ",denom_revocations_serial_id"
+ " FROM denomination_revocations"
+ " WHERE denominations_serial="
+ " (SELECT denominations_serial"
+ " FROM denominations"
+ " WHERE denom_pub_hash=$1);");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"denomination_revocation_get",
diff --git a/src/exchangedb/pg_get_denomination_revocation.h b/src/exchangedb/pg_get_denomination_revocation.h
index d022c822d..5f7f27227 100644
--- a/src/exchangedb/pg_get_denomination_revocation.h
+++ b/src/exchangedb/pg_get_denomination_revocation.h
@@ -41,4 +41,5 @@ TEH_PG_get_denomination_revocation (
const struct TALER_DenominationHashP *denom_pub_hash,
struct TALER_MasterSignatureP *master_sig,
uint64_t *rowid);
+
#endif
diff --git a/src/exchangedb/pg_get_drain_profit.c b/src/exchangedb/pg_get_drain_profit.c
index d02802e1d..75fccefcd 100644
--- a/src/exchangedb/pg_get_drain_profit.c
+++ b/src/exchangedb/pg_get_drain_profit.c
@@ -58,7 +58,6 @@ TEH_PG_get_drain_profit (
GNUNET_PQ_result_spec_end
};
-
PREPARE (pg,
"get_profit_drain",
"SELECT"
@@ -66,8 +65,7 @@ TEH_PG_get_drain_profit (
",account_section"
",payto_uri"
",trigger_date"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",master_sig"
" FROM profit_drains"
" WHERE wtid=$1;");
diff --git a/src/exchangedb/pg_get_expired_reserves.c b/src/exchangedb/pg_get_expired_reserves.c
index c7162dc6b..be9ece98a 100644
--- a/src/exchangedb/pg_get_expired_reserves.c
+++ b/src/exchangedb/pg_get_expired_reserves.c
@@ -84,7 +84,8 @@ reserve_expired_cb (void *cls,
&account_details),
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
&reserve_pub),
- TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
+ TALER_PQ_result_spec_amount ("current_balance",
+ pg->currency,
&remaining_balance),
GNUNET_PQ_result_spec_end
};
@@ -137,7 +138,7 @@ TEH_PG_get_expired_reserves (void *cls,
" SELECT * "
" FROM reserves "
" WHERE expiration_date <= $1 "
- " AND (current_balance_val != 0 OR current_balance_frac != 0) "
+ " AND ((current_balance).val != 0 OR (current_balance).frac != 0) "
" ORDER BY expiration_date ASC "
" LIMIT 1 "
") "
@@ -145,8 +146,7 @@ TEH_PG_get_expired_reserves (void *cls,
" ed.expiration_date "
" ,payto_uri AS account_details "
" ,ed.reserve_pub "
- " ,current_balance_val "
- " ,current_balance_frac "
+ " ,current_balance "
"FROM ( "
" SELECT "
" * "
diff --git a/src/exchangedb/pg_get_extension_manifest.c b/src/exchangedb/pg_get_extension_manifest.c
index 5e95897d3..c6b5948cf 100644
--- a/src/exchangedb/pg_get_extension_manifest.c
+++ b/src/exchangedb/pg_get_extension_manifest.c
@@ -54,13 +54,12 @@ TEH_PG_get_extension_manifest (void *cls,
};
*manifest = NULL;
- /* Used in #postgres_get_extension_manifest */
PREPARE (pg,
"get_extension_manifest",
- "SELECT "
- " manifest "
- "FROM extensions"
- " WHERE name=$1;");
+ "SELECT"
+ " manifest"
+ " FROM extensions"
+ " WHERE name=$1;");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_extension_manifest",
params,
diff --git a/src/exchangedb/pg_get_extension_manifest.h b/src/exchangedb/pg_get_extension_manifest.h
index 3756b7f4c..e8331ad9b 100644
--- a/src/exchangedb/pg_get_extension_manifest.h
+++ b/src/exchangedb/pg_get_extension_manifest.h
@@ -36,6 +36,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_extension_manifest (void *cls,
- const char *extension_name,
+ const char *extension_name,
char **manifest);
+
#endif
diff --git a/src/exchangedb/pg_get_global_fee.c b/src/exchangedb/pg_get_global_fee.c
index 6f6bbafef..46addfa17 100644
--- a/src/exchangedb/pg_get_global_fee.c
+++ b/src/exchangedb/pg_get_global_fee.c
@@ -28,14 +28,14 @@
enum GNUNET_DB_QueryStatus
TEH_PG_get_global_fee (void *cls,
- struct GNUNET_TIME_Timestamp date,
- struct GNUNET_TIME_Timestamp *start_date,
- struct GNUNET_TIME_Timestamp *end_date,
- struct TALER_GlobalFeeSet *fees,
- struct GNUNET_TIME_Relative *purse_timeout,
- struct GNUNET_TIME_Relative *history_expiration,
- uint32_t *purse_account_limit,
- struct TALER_MasterSignatureP *master_sig)
+ struct GNUNET_TIME_Timestamp date,
+ struct GNUNET_TIME_Timestamp *start_date,
+ struct GNUNET_TIME_Timestamp *end_date,
+ struct TALER_GlobalFeeSet *fees,
+ struct GNUNET_TIME_Relative *purse_timeout,
+ struct GNUNET_TIME_Relative *history_expiration,
+ uint32_t *purse_account_limit,
+ struct TALER_MasterSignatureP *master_sig)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -64,26 +64,21 @@ TEH_PG_get_global_fee (void *cls,
GNUNET_PQ_result_spec_end
};
-
- /* Used in #postgres_get_global_fee() */
- PREPARE(pg,
- "get_global_fee",
- "SELECT "
- " start_date"
- ",end_date"
- ",history_fee_val"
- ",history_fee_frac"
- ",account_fee_val"
- ",account_fee_frac"
- ",purse_fee_val"
- ",purse_fee_frac"
- ",purse_timeout"
- ",history_expiration"
- ",purse_account_limit"
- ",master_sig"
- " FROM global_fee"
- " WHERE start_date <= $1"
- " AND end_date > $1;");
+ PREPARE (pg,
+ "get_global_fee",
+ "SELECT "
+ " start_date"
+ ",end_date"
+ ",history_fee"
+ ",account_fee"
+ ",purse_fee"
+ ",purse_timeout"
+ ",history_expiration"
+ ",purse_account_limit"
+ ",master_sig"
+ " FROM global_fee"
+ " WHERE start_date <= $1"
+ " AND end_date > $1;");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_global_fee",
params,
diff --git a/src/exchangedb/pg_get_global_fee.h b/src/exchangedb/pg_get_global_fee.h
index 0887d54d2..1e7c9e94b 100644
--- a/src/exchangedb/pg_get_global_fee.h
+++ b/src/exchangedb/pg_get_global_fee.h
@@ -40,13 +40,13 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_global_fee (void *cls,
- struct GNUNET_TIME_Timestamp date,
- struct GNUNET_TIME_Timestamp *start_date,
- struct GNUNET_TIME_Timestamp *end_date,
- struct TALER_GlobalFeeSet *fees,
- struct GNUNET_TIME_Relative *purse_timeout,
- struct GNUNET_TIME_Relative *history_expiration,
- uint32_t *purse_account_limit,
+ struct GNUNET_TIME_Timestamp date,
+ struct GNUNET_TIME_Timestamp *start_date,
+ struct GNUNET_TIME_Timestamp *end_date,
+ struct TALER_GlobalFeeSet *fees,
+ struct GNUNET_TIME_Relative *purse_timeout,
+ struct GNUNET_TIME_Relative *history_expiration,
+ uint32_t *purse_account_limit,
struct TALER_MasterSignatureP *master_sig);
#endif
diff --git a/src/exchangedb/pg_get_global_fees.c b/src/exchangedb/pg_get_global_fees.c
index 0e1736bd4..21be35989 100644
--- a/src/exchangedb/pg_get_global_fees.c
+++ b/src/exchangedb/pg_get_global_fees.c
@@ -121,11 +121,10 @@ global_fees_cb (void *cls,
}
-
enum GNUNET_DB_QueryStatus
TEH_PG_get_global_fees (void *cls,
- TALER_EXCHANGEDB_GlobalFeeCallback cb,
- void *cb_cls)
+ TALER_EXCHANGEDB_GlobalFeeCallback cb,
+ void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct GNUNET_TIME_Timestamp date
@@ -144,32 +143,23 @@ TEH_PG_get_global_fees (void *cls,
.status = GNUNET_OK
};
- /* Used in #postgres_get_global_fees() */
PREPARE (pg,
"get_global_fees",
"SELECT "
" start_date"
",end_date"
- ",history_fee_val"
- ",history_fee_frac"
- ",account_fee_val"
- ",account_fee_frac"
- ",purse_fee_val"
- ",purse_fee_frac"
+ ",history_fee"
+ ",account_fee"
+ ",purse_fee"
",purse_timeout"
",history_expiration"
",purse_account_limit"
",master_sig"
" FROM global_fee"
" WHERE start_date >= $1");
-
return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"get_global_fees",
params,
&global_fees_cb,
&gctx);
}
-
-
-
-
diff --git a/src/exchangedb/pg_get_global_fees.h b/src/exchangedb/pg_get_global_fees.h
index c8f6f02c2..80c9b812f 100644
--- a/src/exchangedb/pg_get_global_fees.h
+++ b/src/exchangedb/pg_get_global_fees.h
@@ -35,6 +35,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_global_fees (void *cls,
- TALER_EXCHANGEDB_GlobalFeeCallback cb,
+ TALER_EXCHANGEDB_GlobalFeeCallback cb,
void *cb_cls);
+
#endif
diff --git a/src/exchangedb/pg_get_known_coin.c b/src/exchangedb/pg_get_known_coin.c
index fe5c683bc..2c4a82d67 100644
--- a/src/exchangedb/pg_get_known_coin.c
+++ b/src/exchangedb/pg_get_known_coin.c
@@ -27,8 +27,8 @@
enum GNUNET_DB_QueryStatus
TEH_PG_get_known_coin (void *cls,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- struct TALER_CoinPublicInfo *coin_info)
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct TALER_CoinPublicInfo *coin_info)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -51,9 +51,6 @@ TEH_PG_get_known_coin (void *cls,
"Getting known coin data for coin %s\n",
TALER_B2S (coin_pub));
coin_info->coin_pub = *coin_pub;
- /* Used in #postgres_get_known_coin() to fetch
- the denomination public key and signature for
- a coin known to the exchange. */
PREPARE (pg,
"get_known_coin",
"SELECT"
@@ -63,7 +60,6 @@ TEH_PG_get_known_coin (void *cls,
" FROM known_coins"
" JOIN denominations USING (denominations_serial)"
" WHERE coin_pub=$1;");
-
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_known_coin",
params,
diff --git a/src/exchangedb/pg_get_known_coin.h b/src/exchangedb/pg_get_known_coin.h
index d7f55b33c..c34bd2a97 100644
--- a/src/exchangedb/pg_get_known_coin.h
+++ b/src/exchangedb/pg_get_known_coin.h
@@ -34,7 +34,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_known_coin (void *cls,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_CoinPublicInfo *coin_info);
#endif
diff --git a/src/exchangedb/pg_get_link_data.c b/src/exchangedb/pg_get_link_data.c
index f15bf35a2..1b0cb3e20 100644
--- a/src/exchangedb/pg_get_link_data.c
+++ b/src/exchangedb/pg_get_link_data.c
@@ -48,41 +48,53 @@ struct LinkDataContext
struct TALER_TransferPublicKeyP transfer_pub;
/**
- * Link data for @e transfer_pub
- */
- struct TALER_EXCHANGEDB_LinkList *last;
-
- /**
* Status, set to #GNUNET_SYSERR on errors,
*/
- int status;
+ enum GNUNET_GenericReturnValue status;
};
/**
* Free memory of the link data list.
*
- * @param cls the @e cls of this struct with the plugin-specific state (unused)
* @param ldl link data list to release
*/
static void
-free_link_data_list (void *cls,
- struct TALER_EXCHANGEDB_LinkList *ldl)
+free_link_data_list (struct TALER_EXCHANGEDB_LinkList *ldl)
{
struct TALER_EXCHANGEDB_LinkList *next;
- (void) cls;
while (NULL != ldl)
{
next = ldl->next;
TALER_denom_pub_free (&ldl->denom_pub);
TALER_blinded_denom_sig_free (&ldl->ev_sig);
+ TALER_denom_ewv_free (&ldl->alg_values);
GNUNET_free (ldl);
ldl = next;
}
}
+struct Results
+{
+ struct TALER_EXCHANGEDB_LinkList *pos;
+ struct TALER_TransferPublicKeyP transfer_pub;
+};
+
+
+static int
+transfer_pub_cmp (const void *a,
+ const void *b)
+{
+ const struct Results *ra = a;
+ const struct Results *rb = b;
+
+ return GNUNET_memcmp (&ra->transfer_pub,
+ &rb->transfer_pub);
+}
+
+
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results.
@@ -97,18 +109,20 @@ add_ldl (void *cls,
unsigned int num_results)
{
struct LinkDataContext *ldctx = cls;
+ struct Results *temp = GNUNET_new_array (num_results,
+ struct Results);
+ unsigned int temp_off = 0;
for (int i = num_results - 1; i >= 0; i--)
{
struct TALER_EXCHANGEDB_LinkList *pos;
- struct TALER_TransferPublicKeyP transfer_pub;
pos = GNUNET_new (struct TALER_EXCHANGEDB_LinkList);
{
struct TALER_BlindedPlanchet bp;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
- &transfer_pub),
+ &temp[temp_off].transfer_pub),
GNUNET_PQ_result_spec_auto_from_type ("link_sig",
&pos->orig_coin_link_sig),
TALER_PQ_result_spec_blinded_denom_sig ("ev_sig",
@@ -134,33 +148,52 @@ add_ldl (void *cls,
ldctx->status = GNUNET_SYSERR;
return;
}
- if (TALER_DENOMINATION_CS == bp.cipher)
+ if (GNUNET_CRYPTO_BSA_CS == bp.blinded_message->cipher)
{
- pos->nonce = bp.details.cs_blinded_planchet.nonce;
+ pos->nonce.cs_nonce
+ = bp.blinded_message->details.cs_blinded_message.nonce;
pos->have_nonce = true;
}
TALER_blinded_planchet_free (&bp);
}
- if ( (NULL != ldctx->last) &&
- (0 == GNUNET_memcmp (&transfer_pub,
- &ldctx->transfer_pub)) )
- {
- pos->next = ldctx->last;
- }
- else
+ temp[temp_off].pos = pos;
+ temp_off++;
+ }
+ qsort (temp,
+ temp_off,
+ sizeof (struct Results),
+ &transfer_pub_cmp);
+ if (temp_off > 0)
+ {
+ struct TALER_EXCHANGEDB_LinkList *head = NULL;
+
+ head = temp[0].pos;
+ for (unsigned int i = 1; i < temp_off; i++)
{
- if (NULL != ldctx->last)
+ struct TALER_EXCHANGEDB_LinkList *pos = temp[i].pos;
+ const struct TALER_TransferPublicKeyP *tp = &temp[i].transfer_pub;
+
+ if (0 == GNUNET_memcmp (tp,
+ &temp[i - 1].transfer_pub))
+ {
+ pos->next = head;
+ head = pos;
+ }
+ else
{
ldctx->ldc (ldctx->ldc_cls,
- &ldctx->transfer_pub,
- ldctx->last);
- free_link_data_list (cls,
- ldctx->last);
+ &temp[i - 1].transfer_pub,
+ head);
+ free_link_data_list (head);
+ head = pos;
}
- ldctx->transfer_pub = transfer_pub;
}
- ldctx->last = pos;
+ ldctx->ldc (ldctx->ldc_cls,
+ &temp[temp_off - 1].transfer_pub,
+ head);
+ free_link_data_list (head);
}
+ GNUNET_free (temp);
}
@@ -177,11 +210,33 @@ TEH_PG_get_link_data (void *cls,
};
enum GNUNET_DB_QueryStatus qs;
struct LinkDataContext ldctx;
+ static int percent_refund = -2;
+ const char *query;
- if (NULL == getenv ("NEW_LOGIC"))
+ if (-2 == percent_refund)
+ {
+ const char *mode = getenv ("TALER_POSTGRES_GET_LINK_DATA_LOGIC");
+ char dummy;
+
+ if ( (NULL==mode) ||
+ (1 != sscanf (mode,
+ "%d%c",
+ &percent_refund,
+ &dummy)) )
+ {
+ if (NULL != mode)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Bad mode `%s' specified\n",
+ mode);
+ percent_refund = 4; /* Fastest known */
+ }
+ }
+ switch (percent_refund)
{
+ case 0:
+ query = "get_link";
PREPARE (pg,
- "get_link",
+ query,
"SELECT "
" tp.transfer_pub"
",denoms.denom_pub"
@@ -199,16 +254,16 @@ TEH_PG_get_link_data (void *cls,
" ON (rrc.denominations_serial = denoms.denominations_serial)"
" WHERE old_coin_pub=$1"
" ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC");
- }
-
- else
- {
+ break;
+ case 1:
+ query = "get_link_v1";
PREPARE (pg,
- "get_link",
+ query,
"WITH rc AS MATERIALIZED ("
"SELECT"
- "* FROM refresh_commitments"
- "WHERE old_coin_pub=$1"
+ " melt_serial_id"
+ " FROM refresh_commitments"
+ " WHERE old_coin_pub=$1"
")"
"SELECT "
" tp.transfer_pub"
@@ -217,41 +272,96 @@ TEH_PG_get_link_data (void *cls,
",rrc.ewv"
",rrc.link_sig"
",rrc.freshcoin_index"
+ ",rrc.coin_ev "
+ "FROM "
+ "refresh_revealed_coins rrc"
+ " JOIN refresh_transfer_keys tp"
+ " USING (melt_serial_id)"
+ " JOIN denominations denoms"
+ " USING (denominations_serial)"
+ " WHERE rrc.melt_serial_id = (SELECT melt_serial_id FROM rc)"
+ " ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC");
+ break;
+ case 2:
+ query = "get_link_v2";
+ PREPARE (pg,
+ query,
+ "SELECT"
+ " *"
+ " FROM"
+ " exchange_do_get_link_data"
+ " ($1) "
+ " AS "
+ " (transfer_pub BYTEA"
+ " ,denom_pub BYTEA"
+ " ,ev_sig BYTEA"
+ " ,ewv BYTEA"
+ " ,link_sig BYTEA"
+ " ,freshcoin_index INT4"
+ " ,coin_ev BYTEA);");
+ break;
+ case 3:
+ query = "get_link_v3";
+ PREPARE (pg,
+ query,
+ "SELECT "
+ " tp.transfer_pub"
+ ",denoms.denom_pub"
+ ",rrc.ev_sig"
+ ",rrc.ewv"
+ ",rrc.link_sig"
+ ",rrc.freshcoin_index"
",rrc.coin_ev"
- " FROM refresh_revealed_coins rrc"
+ " FROM refresh_commitments"
+ " JOIN refresh_revealed_coins rrc"
" USING (melt_serial_id)"
" JOIN refresh_transfer_keys tp"
" USING (melt_serial_id)"
" JOIN denominations denoms"
- " USING (denominations_serial)"
- " ORDER BY tp.transfer_pub, rrc.freshcoin_index ASC");
+ " ON (rrc.denominations_serial = denoms.denominations_serial)"
+ " WHERE old_coin_pub=$1");
+ break;
+ case 4:
+ query = "get_link_v4";
+ PREPARE (pg,
+ query,
+ "WITH rc AS MATERIALIZED ("
+ "SELECT"
+ " melt_serial_id"
+ " FROM refresh_commitments"
+ " WHERE old_coin_pub=$1"
+ ")"
+ "SELECT "
+ " tp.transfer_pub"
+ ",denoms.denom_pub"
+ ",rrc.ev_sig"
+ ",rrc.ewv"
+ ",rrc.link_sig"
+ ",rrc.freshcoin_index"
+ ",rrc.coin_ev "
+ "FROM "
+ "refresh_revealed_coins rrc"
+ " JOIN refresh_transfer_keys tp"
+ " USING (melt_serial_id)"
+ " JOIN denominations denoms"
+ " USING (denominations_serial)"
+ " WHERE rrc.melt_serial_id = (SELECT melt_serial_id FROM rc)"
+ );
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
ldctx.ldc = ldc;
ldctx.ldc_cls = ldc_cls;
- ldctx.last = NULL;
ldctx.status = GNUNET_OK;
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "get_link",
+ query,
params,
&add_ldl,
&ldctx);
- if (NULL != ldctx.last)
- {
- if (GNUNET_OK == ldctx.status)
- {
- /* call callback one more time! */
- ldc (ldc_cls,
- &ldctx.transfer_pub,
- ldctx.last);
- }
- free_link_data_list (cls,
- ldctx.last);
- ldctx.last = NULL;
- }
if (GNUNET_OK != ldctx.status)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
-
-
diff --git a/src/exchangedb/pg_get_melt.c b/src/exchangedb/pg_get_melt.c
index f239c605b..2221054ba 100644
--- a/src/exchangedb/pg_get_melt.c
+++ b/src/exchangedb/pg_get_melt.c
@@ -28,9 +28,9 @@
enum GNUNET_DB_QueryStatus
TEH_PG_get_melt (void *cls,
- const struct TALER_RefreshCommitmentP *rc,
- struct TALER_EXCHANGEDB_Melt *melt,
- uint64_t *melt_serial_id)
+ const struct TALER_RefreshCommitmentP *rc,
+ struct TALER_EXCHANGEDB_Melt *melt,
+ uint64_t *melt_serial_id)
{
struct PostgresClosure *pg = cls;
bool h_age_commitment_is_null;
@@ -66,19 +66,17 @@ TEH_PG_get_melt (void *cls,
0,
sizeof (melt->session.coin.denom_sig));
- /* Used in #postgres_get_melt() to fetch
- high-level information about a melt operation */
+ /* Used in #postgres_get_melt() to fetch
+ high-level information about a melt operation */
PREPARE (pg,
"get_melt",
/* "SELECT"
" denoms.denom_pub_hash"
- ",denoms.fee_refresh_val"
- ",denoms.fee_refresh_frac"
+ ",denoms.fee_refresh"
",old_coin_pub"
",old_coin_sig"
",kc.age_commitment_hash"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
",noreveal_index"
",melt_serial_id"
" FROM refresh_commitments"
@@ -94,13 +92,11 @@ TEH_PG_get_melt (void *cls,
")"
"SELECT"
" denoms.denom_pub_hash"
- ",denoms.fee_refresh_val"
- ",denoms.fee_refresh_frac"
+ ",denoms.fee_refresh"
",rc.old_coin_pub"
",rc.old_coin_sig"
",kc.age_commitment_hash"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
",noreveal_index"
",melt_serial_id "
"FROM ("
diff --git a/src/exchangedb/pg_get_melt.h b/src/exchangedb/pg_get_melt.h
index 73d757a05..269960bad 100644
--- a/src/exchangedb/pg_get_melt.h
+++ b/src/exchangedb/pg_get_melt.h
@@ -37,8 +37,8 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_melt (void *cls,
- const struct TALER_RefreshCommitmentP *rc,
- struct TALER_EXCHANGEDB_Melt *melt,
+ const struct TALER_RefreshCommitmentP *rc,
+ struct TALER_EXCHANGEDB_Melt *melt,
uint64_t *melt_serial_id);
#endif
diff --git a/src/exchangedb/pg_get_old_coin_by_h_blind.c b/src/exchangedb/pg_get_old_coin_by_h_blind.c
index 385c3f1d1..dcce7b32f 100644
--- a/src/exchangedb/pg_get_old_coin_by_h_blind.c
+++ b/src/exchangedb/pg_get_old_coin_by_h_blind.c
@@ -26,7 +26,6 @@
#include "pg_helper.h"
-
enum GNUNET_DB_QueryStatus
TEH_PG_get_old_coin_by_h_blind (
void *cls,
@@ -47,7 +46,7 @@ TEH_PG_get_old_coin_by_h_blind (
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_get_old_coin_by_h_blind() */
+ /* Used in #postgres_get_old_coin_by_h_blind() */
PREPARE (pg,
"old_coin_by_h_blind",
"SELECT"
diff --git a/src/exchangedb/pg_get_old_coin_by_h_blind.h b/src/exchangedb/pg_get_old_coin_by_h_blind.h
index 1404990d9..93ed541b6 100644
--- a/src/exchangedb/pg_get_old_coin_by_h_blind.h
+++ b/src/exchangedb/pg_get_old_coin_by_h_blind.h
@@ -41,4 +41,5 @@ TEH_PG_get_old_coin_by_h_blind (
const struct TALER_BlindedCoinHashP *h_blind_ev,
struct TALER_CoinSpendPublicKeyP *old_coin_pub,
uint64_t *rrc_serial);
+
#endif
diff --git a/src/exchangedb/pg_get_pending_kyc_requirement_process.c b/src/exchangedb/pg_get_pending_kyc_requirement_process.c
new file mode 100644
index 000000000..b9acddad1
--- /dev/null
+++ b/src/exchangedb/pg_get_pending_kyc_requirement_process.c
@@ -0,0 +1,66 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_get_pending_kyc_requirement_process.c
+ * @brief Implementation of the get_pending_kyc_requirement_process function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_get_pending_kyc_requirement_process.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_pending_kyc_requirement_process (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ const char *provider_section,
+ char **redirect_url)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_string (provider_section),
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("redirect_url",
+ redirect_url),
+ NULL),
+ GNUNET_PQ_result_spec_end
+ };
+
+ *redirect_url = NULL;
+ PREPARE (pg,
+ "get_pending_kyc_requirement_process",
+ "SELECT"
+ " redirect_url"
+ " FROM legitimization_processes"
+ " WHERE provider_section=$1"
+ " AND h_payto=$2"
+ " AND NOT finished"
+ " ORDER BY start_time DESC"
+ " LIMIT 1");
+ return GNUNET_PQ_eval_prepared_singleton_select (
+ pg->conn,
+ "get_pending_kyc_requirement_process",
+ params,
+ rs);
+}
diff --git a/src/exchangedb/pg_insert_deposit.h b/src/exchangedb/pg_get_pending_kyc_requirement_process.h
index 15de39eff..738c4d65b 100644
--- a/src/exchangedb/pg_insert_deposit.h
+++ b/src/exchangedb/pg_get_pending_kyc_requirement_process.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2023 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
@@ -14,27 +14,32 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file exchangedb/pg_insert_deposit.h
- * @brief implementation of the insert_deposit function for Postgres
+ * @file exchangedb/pg_get_pending_kyc_requirement_process.h
+ * @brief implementation of the get_pending_kyc_requirement_process function for Postgres
* @author Christian Grothoff
*/
-#ifndef PG_INSERT_DEPOSIT_H
-#define PG_INSERT_DEPOSIT_H
+#ifndef PG_GET_PENDING_KYC_REQUIREMENT_PROCESS_H
+#define PG_GET_PENDING_KYC_REQUIREMENT_PROCESS_H
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+
+
/**
- * Insert information about deposited coin into the database.
+ * Fetch information about pending KYC requirement process.
*
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param exchange_timestamp time the exchange received the deposit request
- * @param deposit deposit information to store
- * @return query result status
+ * @param cls closure
+ * @param h_payto account that must be KYC'ed
+ * @param provider_section provider that must be checked
+ * @param[out] redirect_url set to redirect URL for the process
+ * @return database transaction status
*/
enum GNUNET_DB_QueryStatus
-TEH_PG_insert_deposit (void *cls,
- struct GNUNET_TIME_Timestamp exchange_timestamp,
- const struct TALER_EXCHANGEDB_Deposit *deposit);
+TEH_PG_get_pending_kyc_requirement_process (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ const char *provider_section,
+ char **redirect_url);
#endif
diff --git a/src/exchangedb/pg_get_policy_details.c b/src/exchangedb/pg_get_policy_details.c
index fafdca53c..6e1b5c5dc 100644
--- a/src/exchangedb/pg_get_policy_details.c
+++ b/src/exchangedb/pg_get_policy_details.c
@@ -57,7 +57,6 @@ TEH_PG_get_policy_details (
};
-
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_policy_details",
params,
diff --git a/src/exchangedb/pg_get_purse_deposit.c b/src/exchangedb/pg_get_purse_deposit.c
index 539bd5ece..cb24855a1 100644
--- a/src/exchangedb/pg_get_purse_deposit.c
+++ b/src/exchangedb/pg_get_purse_deposit.c
@@ -59,25 +59,24 @@ TEH_PG_get_purse_deposit (
GNUNET_PQ_result_spec_end
};
-
*partner_url = NULL;
- /* Used in #postgres_get_purse_deposit */
PREPARE (pg,
"select_purse_deposit_by_coin_pub",
"SELECT "
" coin_sig"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
",denom_pub_hash"
",age_commitment_hash"
",partner_base_url"
" FROM purse_deposits"
- " LEFT JOIN partners USING (partner_serial_id)"
- " JOIN known_coins kc USING (coin_pub)"
- " JOIN denominations USING (denominations_serial)"
- " WHERE coin_pub=$2"
- " AND purse_pub=$1;");
-
+ " LEFT JOIN partners"
+ " USING (partner_serial_id)"
+ " JOIN known_coins kc"
+ " USING (coin_pub)"
+ " JOIN denominations"
+ " USING (denominations_serial)"
+ " WHERE purse_pub=$1"
+ " AND coin_pub=$2;");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"select_purse_deposit_by_coin_pub",
params,
diff --git a/src/exchangedb/pg_get_purse_request.c b/src/exchangedb/pg_get_purse_request.c
index ba8182857..9d2ee5654 100644
--- a/src/exchangedb/pg_get_purse_request.c
+++ b/src/exchangedb/pg_get_purse_request.c
@@ -59,7 +59,7 @@ TEH_PG_get_purse_request (
purse_sig),
GNUNET_PQ_result_spec_end
};
-
+
PREPARE (pg,
"get_purse_request",
"SELECT "
@@ -67,17 +67,13 @@ TEH_PG_get_purse_request (
",purse_expiration"
",h_contract_terms"
",age_limit"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",balance_val"
- ",balance_frac"
+ ",amount_with_fee"
+ ",balance"
",purse_sig"
" FROM purse_requests"
" WHERE purse_pub=$1;");
-
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_purse_request",
params,
rs);
}
-
diff --git a/src/exchangedb/pg_get_ready_deposit.c b/src/exchangedb/pg_get_ready_deposit.c
index af1235293..d8344faf1 100644
--- a/src/exchangedb/pg_get_ready_deposit.c
+++ b/src/exchangedb/pg_get_ready_deposit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022, 2023 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
@@ -34,7 +34,8 @@ TEH_PG_get_ready_deposit (void *cls,
char **payto_uri)
{
struct PostgresClosure *pg = cls;
- struct GNUNET_TIME_Absolute now = {0};
+ struct GNUNET_TIME_Absolute now
+ = GNUNET_TIME_absolute_get ();
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_uint64 (&start_shard_row),
@@ -48,35 +49,26 @@ TEH_PG_get_ready_deposit (void *cls,
payto_uri),
GNUNET_PQ_result_spec_end
};
+ const char *query = "deposits_get_ready";
- now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
- pg->aggregator_shift);
- GNUNET_assert (start_shard_row < end_shard_row);
- GNUNET_assert (end_shard_row <= INT32_MAX);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Finding ready deposits by deadline %s (%llu)\n",
- GNUNET_TIME_absolute2s (now),
- (unsigned long long) now.abs_value_us);
PREPARE (pg,
- "deposits_get_ready",
+ query,
"SELECT"
- " payto_uri"
- ",merchant_pub"
- " FROM deposits_by_ready dbr"
- " JOIN deposits dep"
- " ON (dbr.coin_pub = dep.coin_pub AND"
- " dbr.deposit_serial_id = dep.deposit_serial_id)"
- " JOIN wire_targets wt"
- " USING (wire_target_h_payto)"
- " WHERE dbr.wire_deadline<=$1"
- " AND dbr.shard >= $2"
- " AND dbr.shard <= $3"
+ " wts.payto_uri"
+ ",bdep.merchant_pub"
+ " FROM batch_deposits bdep"
+ " JOIN wire_targets wts"
+ " USING (wire_target_h_payto)"
+ " WHERE NOT (bdep.done OR bdep.policy_blocked)"
+ " AND bdep.wire_deadline<=$1"
+ " AND bdep.shard >= $2"
+ " AND bdep.shard <= $3"
" ORDER BY "
- " dbr.wire_deadline ASC"
- " ,dbr.shard ASC"
+ " bdep.wire_deadline ASC"
+ " ,bdep.shard ASC"
" LIMIT 1;");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "deposits_get_ready",
+ query,
params,
rs);
}
diff --git a/src/exchangedb/pg_get_ready_deposit.h b/src/exchangedb/pg_get_ready_deposit.h
index 19b6dafe4..b1dd7a968 100644
--- a/src/exchangedb/pg_get_ready_deposit.h
+++ b/src/exchangedb/pg_get_ready_deposit.h
@@ -38,9 +38,9 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_ready_deposit (void *cls,
- uint64_t start_shard_row,
- uint64_t end_shard_row,
- struct TALER_MerchantPublicKeyP *merchant_pub,
+ uint64_t start_shard_row,
+ uint64_t end_shard_row,
+ struct TALER_MerchantPublicKeyP *merchant_pub,
char **payto_uri);
#endif
diff --git a/src/exchangedb/pg_get_refresh_reveal.c b/src/exchangedb/pg_get_refresh_reveal.c
index e2db082b7..08d4b21a5 100644
--- a/src/exchangedb/pg_get_refresh_reveal.c
+++ b/src/exchangedb/pg_get_refresh_reveal.c
@@ -112,7 +112,8 @@ add_revealed_coins (void *cls,
GNUNET_PQ_result_spec_end
};
- if (TALER_DENOMINATION_INVALID != rrc->blinded_planchet.cipher)
+ if (NULL !=
+ rrc->blinded_planchet.blinded_message)
{
/* duplicate offset, not allowed */
GNUNET_break (0);
@@ -133,14 +134,11 @@ add_revealed_coins (void *cls,
}
-
-
-
enum GNUNET_DB_QueryStatus
TEH_PG_get_refresh_reveal (void *cls,
- const struct TALER_RefreshCommitmentP *rc,
- TALER_EXCHANGEDB_RefreshCallback cb,
- void *cb_cls)
+ const struct TALER_RefreshCommitmentP *rc,
+ TALER_EXCHANGEDB_RefreshCallback cb,
+ void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct GetRevealContext grctx;
@@ -154,8 +152,8 @@ TEH_PG_get_refresh_reveal (void *cls,
0,
sizeof (grctx));
- /* Obtain information about the coins created in a refresh
- operation, used in #postgres_get_refresh_reveal() */
+ /* Obtain information about the coins created in a refresh
+ operation, used in #postgres_get_refresh_reveal() */
PREPARE (pg,
"get_refresh_revealed_coins",
"SELECT "
@@ -208,6 +206,7 @@ cleanup:
TALER_blinded_denom_sig_free (&rrc->coin_sig);
TALER_blinded_planchet_free (&rrc->blinded_planchet);
+ TALER_denom_ewv_free (&rrc->exchange_vals);
}
GNUNET_free (grctx.rrcs);
return qs;
diff --git a/src/exchangedb/pg_get_refresh_reveal.h b/src/exchangedb/pg_get_refresh_reveal.h
index 0fcea26cd..15b57b343 100644
--- a/src/exchangedb/pg_get_refresh_reveal.h
+++ b/src/exchangedb/pg_get_refresh_reveal.h
@@ -37,8 +37,8 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_refresh_reveal (void *cls,
- const struct TALER_RefreshCommitmentP *rc,
- TALER_EXCHANGEDB_RefreshCallback cb,
+ const struct TALER_RefreshCommitmentP *rc,
+ TALER_EXCHANGEDB_RefreshCallback cb,
void *cb_cls);
#endif
diff --git a/src/exchangedb/pg_get_reserve_balance.c b/src/exchangedb/pg_get_reserve_balance.c
index e08261fc0..140bf3b70 100644
--- a/src/exchangedb/pg_get_reserve_balance.c
+++ b/src/exchangedb/pg_get_reserve_balance.c
@@ -27,8 +27,8 @@
enum GNUNET_DB_QueryStatus
TEH_PG_get_reserve_balance (void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- struct TALER_Amount *balance)
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ struct TALER_Amount *balance)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -36,7 +36,8 @@ TEH_PG_get_reserve_balance (void *cls,
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
+ TALER_PQ_result_spec_amount ("current_balance",
+ pg->currency,
balance),
GNUNET_PQ_result_spec_end
};
@@ -44,8 +45,7 @@ TEH_PG_get_reserve_balance (void *cls,
PREPARE (pg,
"get_reserve_balance",
"SELECT"
- " current_balance_val"
- ",current_balance_frac"
+ " current_balance"
" FROM reserves"
" WHERE reserve_pub=$1;");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
diff --git a/src/exchangedb/pg_get_reserve_balance.h b/src/exchangedb/pg_get_reserve_balance.h
index fd15f0d80..6dc88d906 100644
--- a/src/exchangedb/pg_get_reserve_balance.h
+++ b/src/exchangedb/pg_get_reserve_balance.h
@@ -34,7 +34,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_reserve_balance (void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
struct TALER_Amount *balance);
#endif
diff --git a/src/exchangedb/pg_get_reserve_by_h_blind.c b/src/exchangedb/pg_get_reserve_by_h_blind.c
index 2105b4766..f87fe6cd4 100644
--- a/src/exchangedb/pg_get_reserve_by_h_blind.c
+++ b/src/exchangedb/pg_get_reserve_by_h_blind.c
@@ -45,7 +45,7 @@ TEH_PG_get_reserve_by_h_blind (
reserve_out_serial_id),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_get_reserve_by_h_blind() */
+ /* Used in #postgres_get_reserve_by_h_blind() */
PREPARE (pg,
"reserve_by_h_blind",
"SELECT"
diff --git a/src/exchangedb/pg_get_reserve_history.c b/src/exchangedb/pg_get_reserve_history.c
index 6042a620e..1f1ca95b5 100644
--- a/src/exchangedb/pg_get_reserve_history.c
+++ b/src/exchangedb/pg_get_reserve_history.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -15,7 +15,7 @@
*/
/**
* @file pg_get_reserve_history.c
- * @brief Low-level (statement-level) Postgres database access for the exchange
+ * @brief Obtain (parts of) the history of a reserve.
* @author Christian Grothoff
*/
#include "platform.h"
@@ -23,10 +23,21 @@
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
#include "pg_get_reserve_history.h"
+#include "pg_start_read_committed.h"
+#include "pg_commit.h"
+#include "pg_rollback.h"
#include "plugin_exchangedb_common.h"
#include "pg_helper.h"
/**
+ * How often do we re-try when encountering DB serialization issues?
+ * (We are read-only, so can only happen due to concurrent insert,
+ * which should be very rare.)
+ */
+#define RETRIES 3
+
+
+/**
* Closure for callbacks invoked via #TEH_PG_get_reserve_history().
*/
struct ReserveHistoryContext
@@ -63,10 +74,10 @@ struct ReserveHistoryContext
struct TALER_Amount balance_out;
/**
- * Set to #GNUNET_SYSERR on serious internal errors during
+ * Set to true on serious internal errors during
* the callbacks.
*/
- enum GNUNET_GenericReturnValue status;
+ bool failed;
};
@@ -138,7 +149,7 @@ add_bank_to_exchange (void *cls,
{
GNUNET_break (0);
GNUNET_free (bt);
- rhc->status = GNUNET_SYSERR;
+ rhc->failed = true;
return;
}
}
@@ -199,7 +210,7 @@ add_withdraw_coin (void *cls,
{
GNUNET_break (0);
GNUNET_free (cbc);
- rhc->status = GNUNET_SYSERR;
+ rhc->failed = true;
return;
}
}
@@ -263,7 +274,7 @@ add_recoup (void *cls,
{
GNUNET_break (0);
GNUNET_free (recoup);
- rhc->status = GNUNET_SYSERR;
+ rhc->failed = true;
return;
}
}
@@ -323,7 +334,7 @@ add_exchange_to_bank (void *cls,
{
GNUNET_break (0);
GNUNET_free (closing);
- rhc->status = GNUNET_SYSERR;
+ rhc->failed = true;
return;
}
}
@@ -397,7 +408,7 @@ add_p2p_merge (void *cls,
{
GNUNET_break (0);
GNUNET_free (merge);
- rhc->status = GNUNET_SYSERR;
+ rhc->failed = true;
return;
}
merge->flags = (enum TALER_WalletAccountMergeFlags) flags32;
@@ -433,62 +444,6 @@ add_p2p_merge (void *cls,
* @param num_results number of rows in @a result
*/
static void
-add_history_requests (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct ReserveHistoryContext *rhc = cls;
- struct PostgresClosure *pg = rhc->pg;
-
- while (0 < num_results)
- {
- struct TALER_EXCHANGEDB_HistoryRequest *history;
- struct TALER_EXCHANGEDB_ReserveHistory *tail;
-
- history = GNUNET_new (struct TALER_EXCHANGEDB_HistoryRequest);
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
- &history->history_fee),
- GNUNET_PQ_result_spec_timestamp ("request_timestamp",
- &history->request_timestamp),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
- &history->reserve_sig),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- --num_results))
- {
- GNUNET_break (0);
- GNUNET_free (history);
- rhc->status = GNUNET_SYSERR;
- return;
- }
- }
- GNUNET_assert (0 <=
- TALER_amount_add (&rhc->balance_out,
- &rhc->balance_out,
- &history->history_fee));
- history->reserve_pub = *rhc->reserve_pub;
- tail = append_rh (rhc);
- tail->type = TALER_EXCHANGEDB_RO_HISTORY_REQUEST;
- tail->details.history = history;
- }
-}
-
-
-/**
- * Add paid for history requests to result set for
- * #TEH_PG_get_reserve_history.
- *
- * @param cls a `struct ReserveHistoryContext *`
- * @param result SQL result
- * @param num_results number of rows in @a result
- */
-static void
add_open_requests (void *cls,
PGresult *result,
unsigned int num_results)
@@ -524,7 +479,7 @@ add_open_requests (void *cls,
{
GNUNET_break (0);
GNUNET_free (orq);
- rhc->status = GNUNET_SYSERR;
+ rhc->failed = true;
return;
}
}
@@ -580,7 +535,7 @@ add_close_requests (void *cls,
{
GNUNET_break (0);
GNUNET_free (crq);
- rhc->status = GNUNET_SYSERR;
+ rhc->failed = true;
return;
}
TALER_payto_hash (payto_uri,
@@ -595,17 +550,25 @@ add_close_requests (void *cls,
}
-enum GNUNET_DB_QueryStatus
-TEH_PG_get_reserve_history (void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- struct TALER_Amount *balance,
- struct TALER_EXCHANGEDB_ReserveHistory **rhp)
+/**
+ * Add reserve history entries found.
+ *
+ * @param cls a `struct ReserveHistoryContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+handle_history_entry (void *cls,
+ PGresult *result,
+ unsigned int num_results)
{
- struct PostgresClosure *pg = cls;
- struct ReserveHistoryContext rhc;
- struct
+ static const struct
{
/**
+ * Table with reserve history entry we are responsible for.
+ */
+ const char *table;
+ /**
* Name of the prepared statement to run.
*/
const char *statement;
@@ -615,470 +578,229 @@ TEH_PG_get_reserve_history (void *cls,
GNUNET_PQ_PostgresResultHandler cb;
} work[] = {
/** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
- { "reserves_in_get_transactions",
+ { "reserves_in",
+ "reserves_in_get_transactions",
add_bank_to_exchange },
/** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
- { "get_reserves_out",
+ { "reserves_out",
+ "get_reserves_out",
&add_withdraw_coin },
/** #TALER_EXCHANGEDB_RO_RECOUP_COIN */
- { "recoup_by_reserve",
+ { "recoup",
+ "recoup_by_reserve",
&add_recoup },
/** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
- { "close_by_reserve",
+ { "reserves_close",
+ "close_by_reserve",
&add_exchange_to_bank },
/** #TALER_EXCHANGEDB_RO_PURSE_MERGE */
- { "merge_by_reserve",
+ { "purse_decision",
+ "merge_by_reserve",
&add_p2p_merge },
- /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */
- { "history_by_reserve",
- &add_history_requests },
/** #TALER_EXCHANGEDB_RO_OPEN_REQUEST */
- { "open_request_by_reserve",
+ { "reserves_open_requests",
+ "open_request_by_reserve",
&add_open_requests },
/** #TALER_EXCHANGEDB_RO_CLOSE_REQUEST */
- { "close_request_by_reserve",
+ { "close_requests",
+ "close_request_by_reserve",
&add_close_requests },
/* List terminator */
- { NULL,
- NULL }
+ { NULL, NULL, NULL }
+ };
+ struct ReserveHistoryContext *rhc = cls;
+ char *table_name;
+ uint64_t serial_id;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_string ("table_name",
+ &table_name),
+ GNUNET_PQ_result_spec_uint64 ("serial_id",
+ &serial_id),
+ GNUNET_PQ_result_spec_end
};
- enum GNUNET_DB_QueryStatus qs;
struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+ GNUNET_PQ_query_param_auto_from_type (rhc->reserve_pub),
+ GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
- PREPARE (pg,
- "reserves_in_get_transactions",
- /*
- "SELECT"
- " wire_reference"
- ",credit_val"
- ",credit_frac"
- ",execution_date"
- ",payto_uri AS sender_account_details"
- " FROM reserves_in"
- " JOIN wire_targets"
- " ON (wire_source_h_payto = wire_target_h_payto)"
- " WHERE reserve_pub=$1;",
- */
- "WITH ri AS MATERIALIZED ( "
- " SELECT * "
- " FROM reserves_in "
- " WHERE reserve_pub = $1 "
- ") "
- "SELECT "
- " wire_reference "
- " ,credit_val "
- " ,credit_frac "
- " ,execution_date "
- " ,payto_uri AS sender_account_details "
- "FROM wire_targets "
- "JOIN ri "
- " ON (wire_target_h_payto = wire_source_h_payto) "
- "WHERE wire_target_h_payto = ( "
- " SELECT wire_source_h_payto FROM ri "
- "); ");
- PREPARE (pg,
- "get_reserves_out",
- /*
- "SELECT"
- " ro.h_blind_ev"
- ",denom.denom_pub_hash"
- ",ro.denom_sig"
- ",ro.reserve_sig"
- ",ro.execution_date"
- ",ro.amount_with_fee_val"
- ",ro.amount_with_fee_frac"
- ",denom.fee_withdraw_val"
- ",denom.fee_withdraw_frac"
- " FROM reserves res"
- " JOIN reserves_out_by_reserve ror"
- " ON (res.reserve_uuid = ror.reserve_uuid)"
- " JOIN reserves_out ro"
- " ON (ro.h_blind_ev = ror.h_blind_ev)"
- " JOIN denominations denom"
- " ON (ro.denominations_serial = denom.denominations_serial)"
- " WHERE res.reserve_pub=$1;",
- */
- "WITH robr AS MATERIALIZED ( "
- " SELECT h_blind_ev "
- " FROM reserves_out_by_reserve "
- " WHERE reserve_uuid= ( "
- " SELECT reserve_uuid "
- " FROM reserves "
- " WHERE reserve_pub = $1 "
- " ) "
- ") SELECT "
- " ro.h_blind_ev "
- " ,denom.denom_pub_hash "
- " ,ro.denom_sig "
- " ,ro.reserve_sig "
- " ,ro.execution_date "
- " ,ro.amount_with_fee_val "
- " ,ro.amount_with_fee_frac "
- " ,denom.fee_withdraw_val "
- " ,denom.fee_withdraw_frac "
- "FROM robr "
- "JOIN reserves_out ro "
- " ON (ro.h_blind_ev = robr.h_blind_ev) "
- "JOIN denominations denom "
- " ON (ro.denominations_serial = denom.denominations_serial);");
- PREPARE (pg,
- "recoup_by_reserve",
- /*
- "SELECT"
- " recoup.coin_pub"
- ",recoup.coin_sig"
- ",recoup.coin_blind"
- ",recoup.amount_val"
- ",recoup.amount_frac"
- ",recoup.recoup_timestamp"
- ",denominations.denom_pub_hash"
- ",known_coins.denom_sig"
- " FROM denominations"
- " JOIN (known_coins"
- " JOIN recoup "
- " ON (recoup.coin_pub = known_coins.coin_pub))"
- " ON (known_coins.denominations_serial = denominations.denominations_serial)"
- " WHERE recoup.coin_pub"
- " IN (SELECT coin_pub"
- " FROM recoup_by_reserve"
- " JOIN (reserves_out"
- " JOIN (reserves_out_by_reserve"
- " JOIN reserves"
- " ON (reserves.reserve_uuid = reserves_out_by_reserve.reserve_uuid))"
- " ON (reserves_out_by_reserve.h_blind_ev = reserves_out.h_blind_ev))"
- " ON (recoup_by_reserve.reserve_out_serial_id = reserves_out.reserve_out_serial_id)"
- " WHERE reserves.reserve_pub=$1);",
- */
- "SELECT robr.coin_pub "
- " ,robr.coin_sig "
- " ,robr.coin_blind "
- " ,robr.amount_val "
- " ,robr.amount_frac "
- " ,robr.recoup_timestamp "
- " ,denominations.denom_pub_hash "
- " ,robr.denom_sig "
- "FROM denominations "
- " JOIN exchange_do_recoup_by_reserve($1) robr"
- " USING (denominations_serial);");
- PREPARE (pg,
- "close_by_reserve",
- "SELECT"
- " amount_val"
- ",amount_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
- ",execution_date"
- ",payto_uri AS receiver_account"
- ",wtid"
- " FROM reserves_close"
- " JOIN wire_targets"
- " USING (wire_target_h_payto)"
- " WHERE reserve_pub=$1;");
- PREPARE (pg,
- "merge_by_reserve",
- "SELECT"
- " pr.amount_with_fee_val"
- ",pr.amount_with_fee_frac"
- ",pr.balance_val"
- ",pr.balance_frac"
- ",pr.purse_fee_val"
- ",pr.purse_fee_frac"
- ",pr.h_contract_terms"
- ",pr.merge_pub"
- ",am.reserve_sig"
- ",pm.purse_pub"
- ",pm.merge_timestamp"
- ",pr.purse_expiration"
- ",pr.age_limit"
- ",pr.flags"
- " FROM purse_merges pm"
- " JOIN purse_requests pr"
- " USING (purse_pub)"
- " JOIN purse_decision pdes"
- " USING (purse_pub)"
- " JOIN account_merges am"
- " ON (am.purse_pub = pm.purse_pub AND"
- " am.reserve_pub = pm.reserve_pub)"
- " WHERE pm.reserve_pub=$1"
- " AND COALESCE(pm.partner_serial_id,0)=0" /* must be local! */
- " AND NOT pdes.refunded;");
- PREPARE (pg,
- "history_by_reserve",
- "SELECT"
- " history_fee_val"
- ",history_fee_frac"
- ",request_timestamp"
- ",reserve_sig"
- " FROM history_requests"
- " WHERE reserve_pub=$1;");
- PREPARE (pg,
- "open_request_by_reserve",
- "SELECT"
- " reserve_payment_val"
- ",reserve_payment_frac"
- ",request_timestamp"
- ",expiration_date"
- ",requested_purse_limit"
- ",reserve_sig"
- " FROM reserves_open_requests"
- " WHERE reserve_pub=$1;");
- PREPARE (pg,
- "close_request_by_reserve",
- "SELECT"
- " close_timestamp"
- ",payto_uri"
- ",reserve_sig"
- " FROM close_requests"
- " WHERE reserve_pub=$1;");
-
- rhc.reserve_pub = reserve_pub;
- rhc.rh = NULL;
- rhc.rh_tail = NULL;
- rhc.pg = pg;
- rhc.status = GNUNET_OK;
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pg->currency,
- &rhc.balance_in));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pg->currency,
- &rhc.balance_out));
- qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
- for (unsigned int i = 0; NULL != work[i].cb; i++)
+ while (0 < num_results--)
{
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- work[i].statement,
- params,
- work[i].cb,
- &rhc);
- if ( (0 > qs) ||
- (GNUNET_OK != rhc.status) )
+ enum GNUNET_DB_QueryStatus qs;
+ bool found = false;
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ num_results))
+ {
+ GNUNET_break (0);
+ rhc->failed = true;
+ return;
+ }
+
+ for (unsigned int i = 0;
+ NULL != work[i].cb;
+ i++)
+ {
+ if (0 != strcmp (table_name,
+ work[i].table))
+ continue;
+ found = true;
+ qs = GNUNET_PQ_eval_prepared_multi_select (rhc->pg->conn,
+ work[i].statement,
+ params,
+ work[i].cb,
+ rhc);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Reserve %s had %d transactions at %llu in table %s\n",
+ TALER_B2S (rhc->reserve_pub),
+ (int) qs,
+ (unsigned long long) serial_id,
+ table_name);
+ if (0 >= qs)
+ rhc->failed = true;
break;
- }
- if ( (qs < 0) ||
- (rhc.status != GNUNET_OK) )
- {
- TEH_COMMON_free_reserve_history (cls,
- rhc.rh);
- rhc.rh = NULL;
- if (qs >= 0)
+ }
+ if (! found)
{
- /* status == SYSERR is a very hard error... */
- qs = GNUNET_DB_STATUS_HARD_ERROR;
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Coin history includes unsupported table `%s`\n",
+ table_name);
+ rhc->failed = true;
}
+ GNUNET_PQ_cleanup_result (rs);
+ if (rhc->failed)
+ break;
}
- *rhp = rhc.rh;
- GNUNET_assert (0 <=
- TALER_amount_subtract (balance,
- &rhc.balance_in,
- &rhc.balance_out));
- return qs;
}
enum GNUNET_DB_QueryStatus
-TEH_PG_get_reserve_status (void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- struct TALER_Amount *balance_in,
- struct TALER_Amount *balance_out,
- struct TALER_EXCHANGEDB_ReserveHistory **rhp)
+TEH_PG_get_reserve_history (
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ uint64_t start_off,
+ uint64_t etag_in,
+ uint64_t *etag_out,
+ struct TALER_Amount *balance,
+ struct TALER_EXCHANGEDB_ReserveHistory **rhp)
{
struct PostgresClosure *pg = cls;
- struct ReserveHistoryContext rhc;
- struct
- {
- /**
- * Name of the prepared statement to run.
- */
- const char *statement;
- /**
- * Function to use to process the results.
- */
- GNUNET_PQ_PostgresResultHandler cb;
- } work[] = {
- /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
- { "reserves_in_get_transactions_truncated",
- add_bank_to_exchange },
- /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
- { "get_reserves_out_truncated",
- &add_withdraw_coin },
- /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */
- { "recoup_by_reserve_truncated",
- &add_recoup },
- /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
- { "close_by_reserve_truncated",
- &add_exchange_to_bank },
- /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */
- { "merge_by_reserve_truncated",
- &add_p2p_merge },
- /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */
- { "history_by_reserve_truncated",
- &add_history_requests },
- /** #TALER_EXCHANGEDB_RO_OPEN_REQUEST */
- { "open_request_by_reserve_truncated",
- &add_open_requests },
- /** #TALER_EXCHANGEDB_RO_CLOSE_REQUEST */
- { "close_request_by_reserve_truncated",
- &add_close_requests },
- /* List terminator */
- { NULL,
- NULL }
+ struct ReserveHistoryContext rhc = {
+ .pg = pg,
+ .reserve_pub = reserve_pub
};
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_TIME_Absolute timelimit;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- GNUNET_PQ_query_param_absolute_time (&timelimit),
GNUNET_PQ_query_param_end
};
+ struct GNUNET_PQ_QueryParam lparams[] = {
+ GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+ GNUNET_PQ_query_param_uint64 (&start_off),
+ GNUNET_PQ_query_param_end
+ };
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (pg->currency,
+ &rhc.balance_in));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (pg->currency,
+ &rhc.balance_out));
+ *rhp = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Getting transactions for reserve %s\n",
+ TALER_B2S (reserve_pub));
PREPARE (pg,
- "reserves_in_get_transactions_truncated",
- /*
+ "get_reserve_history_etag",
"SELECT"
- " wire_reference"
- ",credit_val"
- ",credit_frac"
- ",execution_date"
- ",payto_uri AS sender_account_details"
- " FROM reserves_in"
- " JOIN wire_targets"
- " ON (wire_source_h_payto = wire_target_h_payto)"
+ " hist.reserve_history_serial_id"
+ ",r.current_balance"
+ " FROM reserve_history hist"
+ " JOIN reserves r USING (reserve_pub)"
+ " WHERE hist.reserve_pub=$1"
+ " ORDER BY reserve_history_serial_id DESC"
+ " LIMIT 1;");
+ PREPARE (pg,
+ "get_reserve_history",
+ "SELECT"
+ " table_name"
+ ",serial_id"
+ " FROM reserve_history"
" WHERE reserve_pub=$1"
- " AND execution_date>=$2;",
- */
- "WITH ri AS MATERIALIZED ( "
- " SELECT * "
- " FROM reserves_in "
- " WHERE reserve_pub = $1 "
- ") "
- "SELECT "
- " wire_reference "
- " ,credit_val "
- " ,credit_frac "
- " ,execution_date "
- " ,payto_uri AS sender_account_details "
- "FROM wire_targets "
- "JOIN ri "
- " ON (wire_target_h_payto = wire_source_h_payto) "
- "WHERE execution_date >= $2"
- " AND wire_target_h_payto = ( "
- " SELECT wire_source_h_payto FROM ri "
- "); ");
+ " AND reserve_history_serial_id > $2"
+ " ORDER BY reserve_history_serial_id DESC;");
+
PREPARE (pg,
- "get_reserves_out_truncated",
- /*
+ "reserves_in_get_transactions",
+ "SELECT"
+ " ri.wire_reference"
+ ",ri.credit"
+ ",ri.execution_date"
+ ",wt.payto_uri AS sender_account_details"
+ " FROM reserves_in ri"
+ " JOIN wire_targets wt"
+ " ON (wire_source_h_payto = wire_target_h_payto)"
+ " WHERE ri.reserve_pub=$1"
+ " AND ri.reserve_in_serial_id=$2;");
+ PREPARE (pg,
+ "get_reserves_out",
"SELECT"
" ro.h_blind_ev"
",denom.denom_pub_hash"
",ro.denom_sig"
",ro.reserve_sig"
",ro.execution_date"
- ",ro.amount_with_fee_val"
- ",ro.amount_with_fee_frac"
- ",denom.fee_withdraw_val"
- ",denom.fee_withdraw_frac"
- " FROM reserves res"
- " JOIN reserves_out_by_reserve ror"
- " ON (res.reserve_uuid = ror.reserve_uuid)"
- " JOIN reserves_out ro"
- " ON (ro.h_blind_ev = ror.h_blind_ev)"
+ ",ro.amount_with_fee"
+ ",denom.fee_withdraw"
+ " FROM reserves_out ro"
" JOIN denominations denom"
- " ON (ro.denominations_serial = denom.denominations_serial)"
- " WHERE res.reserve_pub=$1"
- " AND execution_date>=$2;",
- */
- "WITH robr AS MATERIALIZED ( "
- " SELECT h_blind_ev "
- " FROM reserves_out_by_reserve "
- " WHERE reserve_uuid= ( "
- " SELECT reserve_uuid "
- " FROM reserves "
- " WHERE reserve_pub = $1 "
- " ) "
- ") SELECT "
- " ro.h_blind_ev "
- " ,denom.denom_pub_hash "
- " ,ro.denom_sig "
- " ,ro.reserve_sig "
- " ,ro.execution_date "
- " ,ro.amount_with_fee_val "
- " ,ro.amount_with_fee_frac "
- " ,denom.fee_withdraw_val "
- " ,denom.fee_withdraw_frac "
- "FROM robr "
- "JOIN reserves_out ro "
- " ON (ro.h_blind_ev = robr.h_blind_ev) "
- "JOIN denominations denom "
- " ON (ro.denominations_serial = denom.denominations_serial)"
- " WHERE ro.execution_date>=$2;");
+ " USING (denominations_serial)"
+ " JOIN reserves res"
+ " USING (reserve_uuid)"
+ " WHERE ro.reserve_out_serial_id=$2"
+ " AND res.reserve_pub=$1;");
PREPARE (pg,
- "recoup_by_reserve_truncated",
- /*
+ "recoup_by_reserve",
"SELECT"
- " recoup.coin_pub"
- ",recoup.coin_sig"
- ",recoup.coin_blind"
- ",recoup.amount_val"
- ",recoup.amount_frac"
- ",recoup.recoup_timestamp"
- ",denominations.denom_pub_hash"
- ",known_coins.denom_sig"
- " FROM denominations"
- " JOIN (known_coins"
- " JOIN recoup "
- " ON (recoup.coin_pub = known_coins.coin_pub))"
- " ON (known_coins.denominations_serial = denominations.denominations_serial)"
- " WHERE recoup_timestamp>=$2"
- " AND recoup.coin_pub"
- " IN (SELECT coin_pub"
- " FROM recoup_by_reserve"
- " JOIN (reserves_out"
- " JOIN (reserves_out_by_reserve"
- " JOIN reserves"
- " ON (reserves.reserve_uuid = reserves_out_by_reserve.reserve_uuid))"
- " ON (reserves_out_by_reserve.h_blind_ev = reserves_out.h_blind_ev))"
- " ON (recoup_by_reserve.reserve_out_serial_id = reserves_out.reserve_out_serial_id)"
- " WHERE reserves.reserve_pub=$1);",
- */
- "SELECT robr.coin_pub "
- " ,robr.coin_sig "
- " ,robr.coin_blind "
- " ,robr.amount_val "
- " ,robr.amount_frac "
- " ,robr.recoup_timestamp "
- " ,denominations.denom_pub_hash "
- " ,robr.denom_sig "
- "FROM denominations "
- " JOIN exchange_do_recoup_by_reserve($1) robr"
- " USING (denominations_serial)"
- " WHERE recoup_timestamp>=$2;");
+ " rec.coin_pub"
+ ",rec.coin_sig"
+ ",rec.coin_blind"
+ ",rec.amount"
+ ",rec.recoup_timestamp"
+ ",denom.denom_pub_hash"
+ ",kc.denom_sig"
+ " FROM recoup rec"
+ " JOIN reserves_out ro"
+ " USING (reserve_out_serial_id)"
+ " JOIN reserves res"
+ " USING (reserve_uuid)"
+ " JOIN known_coins kc"
+ " USING (coin_pub)"
+ " JOIN denominations denom"
+ " ON (denom.denominations_serial = kc.denominations_serial)"
+ " WHERE rec.recoup_uuid=$2"
+ " AND res.reserve_pub=$1;");
PREPARE (pg,
- "close_by_reserve_truncated",
+ "close_by_reserve",
"SELECT"
- " amount_val"
- ",amount_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
- ",execution_date"
- ",payto_uri AS receiver_account"
- ",wtid"
- " FROM reserves_close"
- " JOIN wire_targets"
- " USING (wire_target_h_payto)"
+ " rc.amount"
+ ",rc.closing_fee"
+ ",rc.execution_date"
+ ",wt.payto_uri AS receiver_account"
+ ",rc.wtid"
+ " FROM reserves_close rc"
+ " JOIN wire_targets wt"
+ " USING (wire_target_h_payto)"
" WHERE reserve_pub=$1"
- " AND execution_date>=$2;");
+ " AND close_uuid=$2;");
PREPARE (pg,
- "merge_by_reserve_truncated",
+ "merge_by_reserve",
"SELECT"
- " pr.amount_with_fee_val"
- ",pr.amount_with_fee_frac"
- ",pr.balance_val"
- ",pr.balance_frac"
- ",pr.purse_fee_val"
- ",pr.purse_fee_frac"
+ " pr.amount_with_fee"
+ ",pr.balance"
+ ",pr.purse_fee"
",pr.h_contract_terms"
",pr.merge_pub"
",am.reserve_sig"
@@ -1087,92 +809,128 @@ TEH_PG_get_reserve_status (void *cls,
",pr.purse_expiration"
",pr.age_limit"
",pr.flags"
- " FROM purse_merges pm"
+ " FROM purse_decision pdes"
" JOIN purse_requests pr"
- " USING (purse_pub)"
- " JOIN purse_decision pdes"
- " USING (purse_pub)"
+ " ON (pr.purse_pub = pdes.purse_pub)"
+ " JOIN purse_merges pm"
+ " ON (pm.purse_pub = pdes.purse_pub)"
" JOIN account_merges am"
" ON (am.purse_pub = pm.purse_pub AND"
" am.reserve_pub = pm.reserve_pub)"
- " WHERE pm.reserve_pub=$1"
- " AND pm.merge_timestamp >= $2"
+ " WHERE pdes.purse_decision_serial_id=$2"
+ " AND pm.reserve_pub=$1"
" AND COALESCE(pm.partner_serial_id,0)=0" /* must be local! */
" AND NOT pdes.refunded;");
PREPARE (pg,
- "history_by_reserve_truncated",
- "SELECT"
- " history_fee_val"
- ",history_fee_frac"
- ",request_timestamp"
- ",reserve_sig"
- " FROM history_requests"
- " WHERE reserve_pub=$1"
- " AND request_timestamp>=$2;");
- PREPARE (pg,
- "open_request_by_reserve_truncated",
+ "open_request_by_reserve",
"SELECT"
- " reserve_payment_val"
- ",reserve_payment_frac"
+ " reserve_payment"
",request_timestamp"
",expiration_date"
",requested_purse_limit"
",reserve_sig"
" FROM reserves_open_requests"
" WHERE reserve_pub=$1"
- " AND request_timestamp>=$2;");
-
+ " AND open_request_uuid=$2;");
PREPARE (pg,
- "close_request_by_reserve_truncated",
+ "close_request_by_reserve",
"SELECT"
" close_timestamp"
",payto_uri"
",reserve_sig"
" FROM close_requests"
" WHERE reserve_pub=$1"
- " AND close_timestamp>=$2;");
-
- timelimit = GNUNET_TIME_absolute_subtract (
- GNUNET_TIME_absolute_get (),
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS,
- 5));
- rhc.reserve_pub = reserve_pub;
- rhc.rh = NULL;
- rhc.rh_tail = NULL;
- rhc.pg = pg;
- rhc.status = GNUNET_OK;
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pg->currency,
- &rhc.balance_in));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pg->currency,
- &rhc.balance_out));
- qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
- for (unsigned int i = 0; NULL != work[i].cb; i++)
+ " AND close_request_serial_id=$2;");
+
+ for (unsigned int i = 0; i<RETRIES; i++)
{
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- work[i].statement,
- params,
- work[i].cb,
- &rhc);
- if ( (0 > qs) ||
- (GNUNET_OK != rhc.status) )
+ enum GNUNET_DB_QueryStatus qs;
+ uint64_t end;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("reserve_history_serial_id",
+ &end),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
+ balance),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ TEH_PG_start_read_committed (pg,
+ "get-reserve-transactions"))
+ {
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ /* First only check the last item, to see if
+ we even need to iterate */
+ qs = GNUNET_PQ_eval_prepared_singleton_select (
+ pg->conn,
+ "get_reserve_history_etag",
+ params,
+ rs);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ TEH_PG_rollback (pg);
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ TEH_PG_rollback (pg);
+ continue;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ TEH_PG_rollback (pg);
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ *etag_out = end;
+ if (end == etag_in)
+ return qs;
+ }
+ /* We indeed need to iterate over the history */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Current ETag for reserve %s is %llu\n",
+ TALER_B2S (reserve_pub),
+ (unsigned long long) end);
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (
+ pg->conn,
+ "get_reserve_history",
+ lparams,
+ &handle_history_entry,
+ &rhc);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ TEH_PG_rollback (pg);
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ TEH_PG_rollback (pg);
+ continue;
+ default:
break;
- }
- if ( (qs < 0) ||
- (rhc.status != GNUNET_OK) )
- {
- TEH_COMMON_free_reserve_history (cls,
- rhc.rh);
- rhc.rh = NULL;
- if (qs >= 0)
+ }
+ if (rhc.failed)
+ {
+ TEH_PG_rollback (pg);
+ TEH_COMMON_free_reserve_history (pg,
+ rhc.rh);
+ return GNUNET_DB_STATUS_SOFT_ERROR;
+ }
+ qs = TEH_PG_commit (pg);
+ switch (qs)
{
- /* status == SYSERR is a very hard error... */
- qs = GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ TEH_COMMON_free_reserve_history (pg,
+ rhc.rh);
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ TEH_COMMON_free_reserve_history (pg,
+ rhc.rh);
+ rhc.rh = NULL;
+ continue;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ *rhp = rhc.rh;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
}
- *rhp = rhc.rh;
- *balance_in = rhc.balance_in;
- *balance_out = rhc.balance_out;
- return qs;
+ return GNUNET_DB_STATUS_SOFT_ERROR;
}
diff --git a/src/exchangedb/pg_get_reserve_history.h b/src/exchangedb/pg_get_reserve_history.h
index ee47996b2..15765f127 100644
--- a/src/exchangedb/pg_get_reserve_history.h
+++ b/src/exchangedb/pg_get_reserve_history.h
@@ -27,41 +27,31 @@
/**
- * Get all of the transaction history associated with the specified
- * reserve.
+ * Compile a list of (historic) transactions performed with the given reserve
+ * (withdraw, incoming wire, open, close operations). Should return 0 if the @a
+ * reserve_pub is unknown, otherwise determine @a etag_out and if it is past @a
+ * etag_in return the history after @a start_off. @a etag_out should be set
+ * to the last row ID of the given @a reserve_pub in the reserve history table.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param reserve_pub public key of the reserve
+ * @param start_off maximum starting offset in history to exclude from returning
+ * @param etag_in up to this offset the client already has a response, do not
+ * return anything unless @a etag_out will be larger
+ * @param[out] etag_out set to the latest history offset known for this @a coin_pub
* @param[out] balance set to the reserve balance
* @param[out] rhp set to known transaction history (NULL if reserve is unknown)
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
-TEH_PG_get_reserve_history (void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- struct TALER_Amount *balance,
- struct TALER_EXCHANGEDB_ReserveHistory **rhp);
-
-
-/**
- * Get a truncated transaction history associated with the specified
- * reserve.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param reserve_pub public key of the reserve
- * @param[out] balance_in set to the total of inbound
- * transactions in the returned history
- * @param[out] balance_out set to the total of outbound
- * transactions in the returned history
- * @param[out] rhp set to known transaction history (NULL if reserve is unknown)
- * @return transaction status
- */
-enum GNUNET_DB_QueryStatus
-TEH_PG_get_reserve_status (void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- struct TALER_Amount *balance_in,
- struct TALER_Amount *balance_out,
- struct TALER_EXCHANGEDB_ReserveHistory **rhp);
+TEH_PG_get_reserve_history (
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ uint64_t start_off,
+ uint64_t etag_in,
+ uint64_t *etag_out,
+ struct TALER_Amount *balance,
+ struct TALER_EXCHANGEDB_ReserveHistory **rhp);
#endif
diff --git a/src/exchangedb/pg_get_signature_for_known_coin.c b/src/exchangedb/pg_get_signature_for_known_coin.c
new file mode 100644
index 000000000..06074312f
--- /dev/null
+++ b/src/exchangedb/pg_get_signature_for_known_coin.c
@@ -0,0 +1,63 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_get_signature_for_known_coin.c
+ * @brief Implementation of the get_signature_for_known_coin function for Postgres
+ * @author Özgür Kesim
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_get_signature_for_known_coin.h"
+#include "pg_helper.h"
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_signature_for_known_coin (
+ void *cls,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct TALER_DenominationPublicKey *denom_pub,
+ struct TALER_DenominationSignature *denom_sig)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (coin_pub),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_denom_pub ("denom_pub",
+ denom_pub),
+ TALER_PQ_result_spec_denom_sig ("denom_sig",
+ denom_sig),
+ GNUNET_PQ_result_spec_end
+ };
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Getting denomination and signature for (potentially) known coin %s\n",
+ TALER_B2S (coin_pub));
+ PREPARE (pg,
+ "get_signature_for_known_coin",
+ "SELECT"
+ " denominations.denom_pub"
+ ",denom_sig"
+ " FROM known_coins"
+ " JOIN denominations USING (denominations_serial)"
+ " WHERE coin_pub=$1;");
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "get_signature_for_known_coin",
+ params,
+ rs);
+}
diff --git a/src/exchangedb/pg_get_signature_for_known_coin.h b/src/exchangedb/pg_get_signature_for_known_coin.h
new file mode 100644
index 000000000..ec389176b
--- /dev/null
+++ b/src/exchangedb/pg_get_signature_for_known_coin.h
@@ -0,0 +1,43 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_get_signature_for_known_coin.h
+ * @brief implementation of the get_signature_for_known_coin function for Postgres
+ * @author Özgür Kesim
+ */
+#ifndef PG_GET_SIGNATURE_FOR_KNOWN_COIN_H
+#define PG_GET_SIGNATURE_FOR_KNOWN_COIN_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+/**
+ * Retrieve the denomination and the corresponding signature for a known coin.
+ *
+ * @param cls the plugin closure
+ * @param coin_pub the public key of the coin to search for
+ * @param[out] denom_pub the denomination of the public key, if coin was present
+ * @param[out] denom_sig the signature with the denomination key of the coin, if coin was present
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_signature_for_known_coin (
+ void *cls,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct TALER_DenominationPublicKey *denom_pub,
+ struct TALER_DenominationSignature *denom_sig);
+
+#endif
diff --git a/src/exchangedb/pg_get_unfinished_close_requests.c b/src/exchangedb/pg_get_unfinished_close_requests.c
index fa8abdf8b..990e8e00e 100644
--- a/src/exchangedb/pg_get_unfinished_close_requests.c
+++ b/src/exchangedb/pg_get_unfinished_close_requests.c
@@ -142,8 +142,7 @@ TEH_PG_get_unfinished_close_requests (
" reserve_pub"
" ,close_request_serial_id"
" ,close_timestamp AS expiration_date"
- " ,close_val"
- " ,close_frac"
+ " ,close"
" ,(SELECT payto_uri"
" FROM reserves_in ri"
" JOIN wire_targets wt ON (ri.wire_source_h_payto = wt.wire_target_h_payto)"
diff --git a/src/exchangedb/pg_get_wire_accounts.c b/src/exchangedb/pg_get_wire_accounts.c
index 6986eaef2..9770be719 100644
--- a/src/exchangedb/pg_get_wire_accounts.c
+++ b/src/exchangedb/pg_get_wire_accounts.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022, 2024 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
@@ -66,10 +66,33 @@ get_wire_accounts_cb (void *cls,
for (unsigned int i = 0; i < num_results; i++)
{
char *payto_uri;
+ char *conversion_url = NULL;
+ json_t *debit_restrictions = NULL;
+ json_t *credit_restrictions = NULL;
struct TALER_MasterSignatureP master_sig;
+ char *bank_label = NULL;
+ int64_t priority;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_string ("payto_uri",
&payto_uri),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("conversion_url",
+ &conversion_url),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("bank_label",
+ &bank_label),
+ NULL),
+ GNUNET_PQ_result_spec_int64 ("priority",
+ &priority),
+ GNUNET_PQ_result_spec_allow_null (
+ TALER_PQ_result_spec_json ("debit_restrictions",
+ &debit_restrictions),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ TALER_PQ_result_spec_json ("credit_restrictions",
+ &credit_restrictions),
+ NULL),
GNUNET_PQ_result_spec_auto_from_type ("master_sig",
&master_sig),
GNUNET_PQ_result_spec_end
@@ -84,20 +107,33 @@ get_wire_accounts_cb (void *cls,
ctx->status = GNUNET_SYSERR;
return;
}
+ if (NULL == debit_restrictions)
+ {
+ debit_restrictions = json_array ();
+ GNUNET_assert (NULL != debit_restrictions);
+ }
+ if (NULL == credit_restrictions)
+ {
+ credit_restrictions = json_array ();
+ GNUNET_assert (NULL != credit_restrictions);
+ }
ctx->cb (ctx->cb_cls,
payto_uri,
- &master_sig);
+ conversion_url,
+ debit_restrictions,
+ credit_restrictions,
+ &master_sig,
+ bank_label,
+ priority);
GNUNET_PQ_cleanup_result (rs);
}
}
-
-
enum GNUNET_DB_QueryStatus
TEH_PG_get_wire_accounts (void *cls,
- TALER_EXCHANGEDB_WireAccountCallback cb,
- void *cb_cls)
+ TALER_EXCHANGEDB_WireAccountCallback cb,
+ void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct GetWireAccountsContext ctx = {
@@ -111,12 +147,17 @@ TEH_PG_get_wire_accounts (void *cls,
enum GNUNET_DB_QueryStatus qs;
PREPARE (pg,
- "get_wire_accounts",
- "SELECT"
- " payto_uri"
- ",master_sig"
- " FROM wire_accounts"
- " WHERE is_active");
+ "get_wire_accounts",
+ "SELECT"
+ " payto_uri"
+ ",conversion_url"
+ ",debit_restrictions"
+ ",credit_restrictions"
+ ",master_sig"
+ ",bank_label"
+ ",priority"
+ " FROM wire_accounts"
+ " WHERE is_active");
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"get_wire_accounts",
params,
@@ -125,5 +166,4 @@ TEH_PG_get_wire_accounts (void *cls,
if (GNUNET_OK != ctx.status)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
-
}
diff --git a/src/exchangedb/pg_get_wire_accounts.h b/src/exchangedb/pg_get_wire_accounts.h
index 4ddda0ed3..f4dc97ce0 100644
--- a/src/exchangedb/pg_get_wire_accounts.h
+++ b/src/exchangedb/pg_get_wire_accounts.h
@@ -36,7 +36,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_wire_accounts (void *cls,
- TALER_EXCHANGEDB_WireAccountCallback cb,
+ TALER_EXCHANGEDB_WireAccountCallback cb,
void *cb_cls);
#endif
diff --git a/src/exchangedb/pg_get_wire_fee.c b/src/exchangedb/pg_get_wire_fee.c
index 4b4324766..40f517f28 100644
--- a/src/exchangedb/pg_get_wire_fee.c
+++ b/src/exchangedb/pg_get_wire_fee.c
@@ -27,12 +27,12 @@
enum GNUNET_DB_QueryStatus
TEH_PG_get_wire_fee (void *cls,
- const char *type,
- struct GNUNET_TIME_Timestamp date,
- struct GNUNET_TIME_Timestamp *start_date,
- struct GNUNET_TIME_Timestamp *end_date,
- struct TALER_WireFeeSet *fees,
- struct TALER_MasterSignatureP *master_sig)
+ const char *type,
+ struct GNUNET_TIME_Timestamp date,
+ struct GNUNET_TIME_Timestamp *start_date,
+ struct GNUNET_TIME_Timestamp *end_date,
+ struct TALER_WireFeeSet *fees,
+ struct TALER_MasterSignatureP *master_sig)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -54,23 +54,18 @@ TEH_PG_get_wire_fee (void *cls,
GNUNET_PQ_result_spec_end
};
-
-
- /* Used in #postgres_get_wire_fee() */
- PREPARE(pg,
- "get_wire_fee",
- "SELECT "
- " start_date"
- ",end_date"
- ",wire_fee_val"
- ",wire_fee_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
- ",master_sig"
- " FROM wire_fee"
- " WHERE wire_method=$1"
- " AND start_date <= $2"
- " AND end_date > $2;");
+ PREPARE (pg,
+ "get_wire_fee",
+ "SELECT "
+ " start_date"
+ ",end_date"
+ ",wire_fee"
+ ",closing_fee"
+ ",master_sig"
+ " FROM wire_fee"
+ " WHERE wire_method=$1"
+ " AND start_date <= $2"
+ " AND end_date > $2;");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_wire_fee",
params,
diff --git a/src/exchangedb/pg_get_wire_fee.h b/src/exchangedb/pg_get_wire_fee.h
index 92107fe30..409a5c48b 100644
--- a/src/exchangedb/pg_get_wire_fee.h
+++ b/src/exchangedb/pg_get_wire_fee.h
@@ -39,11 +39,11 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_wire_fee (void *cls,
- const char *type,
- struct GNUNET_TIME_Timestamp date,
- struct GNUNET_TIME_Timestamp *start_date,
- struct GNUNET_TIME_Timestamp *end_date,
- struct TALER_WireFeeSet *fees,
+ const char *type,
+ struct GNUNET_TIME_Timestamp date,
+ struct GNUNET_TIME_Timestamp *start_date,
+ struct GNUNET_TIME_Timestamp *end_date,
+ struct TALER_WireFeeSet *fees,
struct TALER_MasterSignatureP *master_sig);
#endif
diff --git a/src/exchangedb/pg_get_wire_fees.c b/src/exchangedb/pg_get_wire_fees.c
index a83db151d..193ccff6a 100644
--- a/src/exchangedb/pg_get_wire_fees.c
+++ b/src/exchangedb/pg_get_wire_fees.c
@@ -109,9 +109,9 @@ get_wire_fees_cb (void *cls,
enum GNUNET_DB_QueryStatus
TEH_PG_get_wire_fees (void *cls,
- const char *wire_method,
- TALER_EXCHANGEDB_WireFeeCallback cb,
- void *cb_cls)
+ const char *wire_method,
+ TALER_EXCHANGEDB_WireFeeCallback cb,
+ void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -126,14 +126,11 @@ TEH_PG_get_wire_fees (void *cls,
};
enum GNUNET_DB_QueryStatus qs;
-
PREPARE (pg,
"get_wire_fees",
"SELECT"
- " wire_fee_val"
- ",wire_fee_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
+ " wire_fee"
+ ",closing_fee"
",start_date"
",end_date"
",master_sig"
diff --git a/src/exchangedb/pg_get_wire_fees.h b/src/exchangedb/pg_get_wire_fees.h
index 83bacd674..798a514db 100644
--- a/src/exchangedb/pg_get_wire_fees.h
+++ b/src/exchangedb/pg_get_wire_fees.h
@@ -37,8 +37,8 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_wire_fees (void *cls,
- const char *wire_method,
- TALER_EXCHANGEDB_WireFeeCallback cb,
+ const char *wire_method,
+ TALER_EXCHANGEDB_WireFeeCallback cb,
void *cb_cls);
#endif
diff --git a/src/exchangedb/pg_get_wire_hash_for_contract.c b/src/exchangedb/pg_get_wire_hash_for_contract.c
new file mode 100644
index 000000000..afd659b18
--- /dev/null
+++ b/src/exchangedb/pg_get_wire_hash_for_contract.c
@@ -0,0 +1,81 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_get_wire_hash_for_contract.c
+ * @brief Implementation of the get_wire_hash_for_contract function for Postgres
+ * @author Özgür Kesim
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_exchangedb_plugin.h"
+#include "taler_pq_lib.h"
+#include "pg_get_wire_hash_for_contract.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_wire_hash_for_contract (
+ void *cls,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ struct TALER_MerchantWireHashP *h_wire)
+{
+ struct PostgresClosure *pg = cls;
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+ GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+ GNUNET_PQ_query_param_end
+ };
+ char *payto_uri;
+ struct TALER_WireSaltP wire_salt;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
+ &wire_salt),
+ GNUNET_PQ_result_spec_string ("payto_uri",
+ &payto_uri),
+ GNUNET_PQ_result_spec_end
+ };
+
+ /* check if the necessary records exist and get them */
+ PREPARE (pg,
+ "get_wire_hash_for_contract",
+ "SELECT"
+ " bdep.wire_salt"
+ ",wt.payto_uri"
+ " FROM coin_deposits"
+ " JOIN batch_deposits bdep"
+ " USING (batch_deposit_serial_id)"
+ " JOIN wire_targets wt"
+ " USING (wire_target_h_payto)"
+ " WHERE bdep.merchant_pub=$1"
+ " AND bdep.h_contract_terms=$2");
+ /* NOTE: above query might be more efficient if we computed the shard
+ from the merchant_pub and included that in the query */
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "get_wire_hash_for_contract",
+ params,
+ rs);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ {
+ TALER_merchant_wire_signature_hash (payto_uri,
+ &wire_salt,
+ h_wire);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+ return qs;
+}
diff --git a/src/exchangedb/pg_get_wire_hash_for_contract.h b/src/exchangedb/pg_get_wire_hash_for_contract.h
new file mode 100644
index 000000000..f95cd29c1
--- /dev/null
+++ b/src/exchangedb/pg_get_wire_hash_for_contract.h
@@ -0,0 +1,46 @@
+/*
+ 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 exchangedb/pg_get_wire_hash_for_contract.h
+ * @brief implementation of the get_wire_hash_for_contract function for Postgres
+ * @author Özgür Kesim
+ */
+#ifndef PG_GET_WIRE_HASH_FOR_CONTRACT_H
+#define PG_GET_WIRE_HASH_FOR_CONTRACT_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+/**
+ * Try to get the salted hash of a merchant's bank account to a deposit
+ * contract. This is necessary in the event of a conflict with a given
+ * (merchant_pub, h_contract_terms) during deposit.
+ *
+ * @param cls closure
+ * @param merchant_pub merchant public key
+ * @param h_contract_terms hash of the proposal data
+ * @param[out] h_wire salted hash of a merchant's bank account
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_get_wire_hash_for_contract (
+ void *cls,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ struct TALER_MerchantWireHashP *h_wire);
+
+#endif
diff --git a/src/exchangedb/pg_get_withdraw_info.c b/src/exchangedb/pg_get_withdraw_info.c
index ef3936269..e06fa3764 100644
--- a/src/exchangedb/pg_get_withdraw_info.c
+++ b/src/exchangedb/pg_get_withdraw_info.c
@@ -55,10 +55,6 @@ TEH_PG_get_withdraw_info (
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_get_withdraw_info() to
- locate the response for a /reserve/withdraw request
- using the hash of the blinded message. Used to
- make sure /reserve/withdraw requests are idempotent. */
PREPARE (pg,
"get_withdraw_info",
"SELECT"
@@ -68,10 +64,8 @@ TEH_PG_get_withdraw_info (
",reserves.reserve_pub"
",execution_date"
",h_blind_ev"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",denom.fee_withdraw_val"
- ",denom.fee_withdraw_frac"
+ ",amount_with_fee"
+ ",denom.fee_withdraw"
" FROM reserves_out"
" JOIN reserves"
" USING (reserve_uuid)"
diff --git a/src/exchangedb/pg_have_deposit2.c b/src/exchangedb/pg_have_deposit2.c
index 1616858c5..e00ad7490 100644
--- a/src/exchangedb/pg_have_deposit2.c
+++ b/src/exchangedb/pg_have_deposit2.c
@@ -70,31 +70,28 @@ TEH_PG_have_deposit2 (
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Getting deposits for coin %s\n",
TALER_B2S (coin_pub));
-
- /* Fetch an existing deposit request, used to ensure idempotency
- during /deposit processing. Used in #postgres_have_deposit(). */
PREPARE (pg,
- "get_deposit",
- "SELECT"
- " dep.amount_with_fee_val"
- ",dep.amount_with_fee_frac"
- ",denominations.fee_deposit_val"
- ",denominations.fee_deposit_frac"
- ",dep.wallet_timestamp"
- ",dep.exchange_timestamp"
- ",dep.refund_deadline"
- ",dep.wire_deadline"
- ",dep.h_contract_terms"
- ",dep.wire_salt"
- ",wt.payto_uri AS receiver_wire_account"
- " FROM deposits dep"
- " JOIN known_coins kc ON (kc.coin_pub = dep.coin_pub)"
- " JOIN denominations USING (denominations_serial)"
- " JOIN wire_targets wt USING (wire_target_h_payto)"
- " WHERE dep.coin_pub=$1"
- " AND dep.merchant_pub=$3"
- " AND dep.h_contract_terms=$2;");
-
+ "get_deposit",
+ "SELECT"
+ " cdep.amount_with_fee"
+ ",denominations.fee_deposit"
+ ",bdep.wallet_timestamp"
+ ",bdep.exchange_timestamp"
+ ",bdep.refund_deadline"
+ ",bdep.wire_deadline"
+ ",bdep.h_contract_terms"
+ ",bdep.wire_salt"
+ ",wt.payto_uri AS receiver_wire_account"
+ " FROM coin_deposits cdep"
+ " JOIN batch_deposits bdep USING (batch_deposit_serial_id)"
+ " JOIN known_coins kc ON (kc.coin_pub = cdep.coin_pub)"
+ " JOIN denominations USING (denominations_serial)"
+ " JOIN wire_targets wt USING (wire_target_h_payto)"
+ " WHERE cdep.coin_pub=$1"
+ " AND bdep.merchant_pub=$3"
+ " AND bdep.h_contract_terms=$2;");
+ /* Note: query might be made more efficient if we computed the 'shard'
+ from merchant_pub and included that as a constraint on bdep! */
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_deposit",
params,
diff --git a/src/exchangedb/pg_helper.h b/src/exchangedb/pg_helper.h
index 4b5859662..c63c9111d 100644
--- a/src/exchangedb/pg_helper.h
+++ b/src/exchangedb/pg_helper.h
@@ -88,12 +88,6 @@ struct PostgresClosure
*/
uint32_t def_purse_limit;
- /**
- * Did we initialize the prepared statements
- * for this session? (To be replaced with @e prep_gen.)
- */
- bool init;
-
};
@@ -147,19 +141,8 @@ struct PostgresClosure
* @param field name of the database field to fetch amount from
* @param[out] amountp pointer to amount to set
*/
-#define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) TALER_PQ_result_spec_amount ( \
- field,pg->currency,amountp)
-
-
-/**
- * Wrapper macro to add the currency from the plugin's state
- * when fetching amounts from the database. NBO variant.
- *
- * @param field name of the database field to fetch amount from
- * @param[out] amountp pointer to amount to set
- */
-#define TALER_PQ_RESULT_SPEC_AMOUNT_NBO(field, \
- amountp) TALER_PQ_result_spec_amount_nbo ( \
+#define TALER_PQ_RESULT_SPEC_AMOUNT(field, \
+ amountp) TALER_PQ_result_spec_amount ( \
field,pg->currency,amountp)
diff --git a/src/exchangedb/pg_inject_auditor_triggers.c b/src/exchangedb/pg_inject_auditor_triggers.c
new file mode 100644
index 000000000..562a1a22c
--- /dev/null
+++ b/src/exchangedb/pg_inject_auditor_triggers.c
@@ -0,0 +1,57 @@
+/*
+ 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 exchangedb/pg_inject_auditor_triggers.c
+ * @brief Implementation of the inject_auditor_triggers function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_gc.h"
+#include "pg_helper.h"
+
+
+/**
+ * Function called to inject auditor triggers into the
+ * database, triggering the real-time auditor upon
+ * relevant INSERTs.
+ *
+ * @param cls closure
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on DB errors
+ */
+enum GNUNET_GenericReturnValue
+TEH_PG_inject_auditor_triggers (void *cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_Context *conn;
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
+
+ conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
+ "exchangedb-postgres",
+ "auditor-triggers-",
+ es,
+ NULL);
+ if (NULL == conn)
+ return GNUNET_SYSERR;
+ GNUNET_PQ_disconnect (conn);
+ return GNUNET_OK;
+}
diff --git a/src/exchangedb/pg_batch_reserves_in_insert.h b/src/exchangedb/pg_inject_auditor_triggers.h
index 766795672..2dfa9468c 100644
--- a/src/exchangedb/pg_batch_reserves_in_insert.h
+++ b/src/exchangedb/pg_inject_auditor_triggers.h
@@ -14,22 +14,28 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file exchangedb/pg_batch_reserves_in_insert.h
- * @brief implementation of the batch_reserves_in_insert function for Postgres
+ * @file exchangedb/pg_inject_auditor_triggers.h
+ * @brief implementation of the inject_auditor_triggers function for Postgres
* @author Christian Grothoff
*/
-#ifndef PG_BATCH_RESERVES_IN_INSERT_H
-#define PG_BATCH_RESERVES_IN_INSERT_H
+#ifndef PG_INJECT_AUDITOR_TRIGGERS_H
+#define PG_INJECT_AUDITOR_TRIGGERS_H
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+/**
+ * Function called to inject auditor triggers into the
+ * database, triggering the real-time auditor upon
+ * relevant INSERTs.
+ *
+ * @param cls closure
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on DB errors
+ */
+enum GNUNET_GenericReturnValue
+TEH_PG_inject_auditor_triggers (void *cls);
-enum GNUNET_DB_QueryStatus
-TEH_PG_batch_reserves_in_insert (void *cls,
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
- unsigned int reserves_length,
- enum GNUNET_DB_QueryStatus *results);
#endif
diff --git a/src/exchangedb/pg_insert_aml_decision.c b/src/exchangedb/pg_insert_aml_decision.c
new file mode 100644
index 000000000..39419be59
--- /dev/null
+++ b/src/exchangedb/pg_insert_aml_decision.c
@@ -0,0 +1,97 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022, 2023 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 exchangedb/pg_insert_aml_decision.c
+ * @brief Implementation of the insert_aml_decision function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_insert_aml_decision.h"
+#include "pg_helper.h"
+#include <gnunet/gnunet_pq_lib.h>
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_aml_decision (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ const struct TALER_Amount *new_threshold,
+ enum TALER_AmlDecisionState new_status,
+ struct GNUNET_TIME_Timestamp decision_time,
+ const char *justification,
+ const json_t *kyc_requirements,
+ uint64_t requirements_row,
+ const struct TALER_AmlOfficerPublicKeyP *decider_pub,
+ const struct TALER_AmlOfficerSignatureP *decider_sig,
+ bool *invalid_officer,
+ struct GNUNET_TIME_Timestamp *last_date)
+{
+ struct PostgresClosure *pg = cls;
+ uint32_t ns = (uint32_t) new_status;
+ struct TALER_KycCompletedEventP rep = {
+ .header.size = htons (sizeof (rep)),
+ .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
+ .h_payto = *h_payto
+ };
+ char *notify_s = GNUNET_PQ_get_event_notify_channel (&rep.header);
+ char *kyc_s = (NULL != kyc_requirements)
+ ? json_dumps (kyc_requirements, JSON_COMPACT)
+ : NULL;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ TALER_PQ_query_param_amount (pg->conn,
+ new_threshold),
+ GNUNET_PQ_query_param_uint32 (&ns),
+ GNUNET_PQ_query_param_timestamp (&decision_time),
+ GNUNET_PQ_query_param_string (justification),
+ GNUNET_PQ_query_param_auto_from_type (decider_pub),
+ GNUNET_PQ_query_param_auto_from_type (decider_sig),
+ GNUNET_PQ_query_param_string (notify_s),
+ NULL != kyc_requirements
+ ? GNUNET_PQ_query_param_string (kyc_s)
+ : GNUNET_PQ_query_param_null (),
+ GNUNET_PQ_query_param_uint64 (&requirements_row),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("out_invalid_officer",
+ invalid_officer),
+ GNUNET_PQ_result_spec_timestamp ("out_last_date",
+ last_date),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (pg,
+ "do_insert_aml_decision",
+ "SELECT"
+ " out_invalid_officer"
+ ",out_last_date"
+ " FROM exchange_do_insert_aml_decision"
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);");
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "do_insert_aml_decision",
+ params,
+ rs);
+ GNUNET_free (notify_s);
+ GNUNET_PQ_event_do_poll (pg->conn);
+ if (NULL != kyc_s)
+ free (kyc_s);
+ return qs;
+}
diff --git a/src/exchangedb/pg_insert_aml_decision.h b/src/exchangedb/pg_insert_aml_decision.h
new file mode 100644
index 000000000..073a2f50a
--- /dev/null
+++ b/src/exchangedb/pg_insert_aml_decision.h
@@ -0,0 +1,64 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022, 2023 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 exchangedb/pg_insert_aml_decision.h
+ * @brief implementation of the insert_aml_decision function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_AML_DECISION_H
+#define PG_INSERT_AML_DECISION_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Insert an AML decision. Inserts into AML history and insert or updates AML
+ * status.
+ *
+ * @param cls closure
+ * @param h_payto account for which the attribute data is stored
+ * @param new_threshold new monthly threshold that would trigger an AML check
+ * @param new_status AML decision status
+ * @param decision_time when was the decision made
+ * @param justification human-readable text justifying the decision
+ * @param kyc_requirements JSON array with KYC requirements
+ * @param requirements_row row in the KYC table for this process, 0 for none
+ * @param decider_pub public key of the staff member
+ * @param decider_sig signature of the staff member
+ * @param[out] invalid_officer set to TRUE if @a decider_pub is not allowed to make decisions right now
+ * @param[out] last_date set to the previous decision time;
+ * the INSERT is not performed if @a last_date is not before @a decision_time
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_aml_decision (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ const struct TALER_Amount *new_threshold,
+ enum TALER_AmlDecisionState new_status,
+ struct GNUNET_TIME_Timestamp decision_time,
+ const char *justification,
+ const json_t *kyc_requirements,
+ uint64_t requirements_row,
+ const struct TALER_AmlOfficerPublicKeyP *decider_pub,
+ const struct TALER_AmlOfficerSignatureP *decider_sig,
+ bool *invalid_officer,
+ struct GNUNET_TIME_Timestamp *last_date);
+
+
+#endif
diff --git a/src/exchangedb/pg_insert_aml_officer.c b/src/exchangedb/pg_insert_aml_officer.c
new file mode 100644
index 000000000..c1f635a64
--- /dev/null
+++ b/src/exchangedb/pg_insert_aml_officer.c
@@ -0,0 +1,66 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022, 2023 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 exchangedb/pg_insert_aml_officer.c
+ * @brief Implementation of the insert_aml_officer function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_insert_aml_officer.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_aml_officer (
+ void *cls,
+ const struct TALER_AmlOfficerPublicKeyP *decider_pub,
+ const struct TALER_MasterSignatureP *master_sig,
+ const char *decider_name,
+ bool is_active,
+ bool read_only,
+ struct GNUNET_TIME_Timestamp last_change,
+ struct GNUNET_TIME_Timestamp *previous_change)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (decider_pub),
+ GNUNET_PQ_query_param_auto_from_type (master_sig),
+ GNUNET_PQ_query_param_string (decider_name),
+ GNUNET_PQ_query_param_bool (is_active),
+ GNUNET_PQ_query_param_bool (read_only),
+ GNUNET_PQ_query_param_timestamp (&last_change),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_timestamp ("out_last_change",
+ previous_change),
+ GNUNET_PQ_result_spec_end
+ };
+
+ PREPARE (pg,
+ "do_insert_aml_staff",
+ "SELECT"
+ " out_last_change"
+ " FROM exchange_do_insert_aml_officer"
+ "($1, $2, $3, $4, $5, $6);");
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "do_insert_aml_staff",
+ params,
+ rs);
+}
diff --git a/src/exchangedb/pg_insert_aml_officer.h b/src/exchangedb/pg_insert_aml_officer.h
new file mode 100644
index 000000000..3c6f5d82e
--- /dev/null
+++ b/src/exchangedb/pg_insert_aml_officer.h
@@ -0,0 +1,56 @@
+/*
+ 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 exchangedb/pg_insert_aml_officer.h
+ * @brief implementation of the insert_aml_officer function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_AML_OFFICER_H
+#define PG_INSERT_AML_OFFICER_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Insert AML staff record. If the time given in
+ * @a last_change is before the previous change in the
+ * database, only @e previous_change is returned and
+ * no actual change is committed to the database.
+ *
+ * @param cls closure
+ * @param decider_pub public key of the staff member
+ * @param master_sig offline signature affirming the AML officer
+ * @param decider_name full name of the staff member
+ * @param is_active true to enable, false to set as inactive
+ * @param read_only true to set read-only access
+ * @param last_change when was the change made effective
+ * @param[out] previous_change set to the time of the previous change
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_aml_officer (
+ void *cls,
+ const struct TALER_AmlOfficerPublicKeyP *decider_pub,
+ const struct TALER_MasterSignatureP *master_sig,
+ const char *decider_name,
+ bool is_active,
+ bool read_only,
+ struct GNUNET_TIME_Timestamp last_change,
+ struct GNUNET_TIME_Timestamp *previous_change);
+
+#endif
diff --git a/src/exchangedb/pg_insert_auditor.c b/src/exchangedb/pg_insert_auditor.c
index 757dfa625..2f1de7ba7 100644
--- a/src/exchangedb/pg_insert_auditor.c
+++ b/src/exchangedb/pg_insert_auditor.c
@@ -27,10 +27,10 @@
enum GNUNET_DB_QueryStatus
TEH_PG_insert_auditor (void *cls,
- const struct TALER_AuditorPublicKeyP *auditor_pub,
- const char *auditor_url,
- const char *auditor_name,
- struct GNUNET_TIME_Timestamp start_date)
+ const struct TALER_AuditorPublicKeyP *auditor_pub,
+ const char *auditor_url,
+ const char *auditor_name,
+ struct GNUNET_TIME_Timestamp start_date)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -41,7 +41,7 @@ TEH_PG_insert_auditor (void *cls,
GNUNET_PQ_query_param_end
};
- /* used in #postgres_insert_auditor() */
+ /* used in #postgres_insert_auditor() */
PREPARE (pg,
"insert_auditor",
"INSERT INTO auditors "
diff --git a/src/exchangedb/pg_insert_auditor.h b/src/exchangedb/pg_insert_auditor.h
index 7523282e4..8de388f23 100644
--- a/src/exchangedb/pg_insert_auditor.h
+++ b/src/exchangedb/pg_insert_auditor.h
@@ -38,8 +38,8 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_insert_auditor (void *cls,
- const struct TALER_AuditorPublicKeyP *auditor_pub,
- const char *auditor_url,
- const char *auditor_name,
+ const struct TALER_AuditorPublicKeyP *auditor_pub,
+ const char *auditor_url,
+ const char *auditor_name,
struct GNUNET_TIME_Timestamp start_date);
#endif
diff --git a/src/exchangedb/pg_insert_close_request.c b/src/exchangedb/pg_insert_close_request.c
index 387dafd9a..b4bc5f4a7 100644
--- a/src/exchangedb/pg_insert_close_request.c
+++ b/src/exchangedb/pg_insert_close_request.c
@@ -41,8 +41,10 @@ TEH_PG_insert_close_request (
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_timestamp (&request_timestamp),
GNUNET_PQ_query_param_auto_from_type (reserve_sig),
- TALER_PQ_query_param_amount (balance),
- TALER_PQ_query_param_amount (closing_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ balance),
+ TALER_PQ_query_param_amount (pg->conn,
+ closing_fee),
GNUNET_PQ_query_param_string (payto_uri),
GNUNET_PQ_query_param_end
};
@@ -53,14 +55,12 @@ TEH_PG_insert_close_request (
"(reserve_pub"
",close_timestamp"
",reserve_sig"
- ",close_val"
- ",close_frac"
- ",close_fee_val"
- ",close_fee_frac"
+ ",close"
+ ",close_fee"
",payto_uri"
")"
"VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8)"
+ "($1, $2, $3, $4, $5, $6)"
" ON CONFLICT DO NOTHING;");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_account_close",
diff --git a/src/exchangedb/pg_insert_contract.c b/src/exchangedb/pg_insert_contract.c
index d3e6c23be..0274f8d93 100644
--- a/src/exchangedb/pg_insert_contract.c
+++ b/src/exchangedb/pg_insert_contract.c
@@ -45,20 +45,20 @@ TEH_PG_insert_contract (
};
*in_conflict = false;
- /* Used in #postgres_insert_contract() */
+ /* Used in #postgres_insert_contract() */
PREPARE (pg,
- "insert_contract",
- "INSERT INTO contracts"
- " (purse_pub"
- " ,pub_ckey"
- " ,e_contract"
- " ,contract_sig"
- " ,purse_expiration"
- " ) SELECT "
- " $1, $2, $3, $4, purse_expiration"
- " FROM purse_requests"
- " WHERE purse_pub=$1"
- " ON CONFLICT DO NOTHING;");
+ "insert_contract",
+ "INSERT INTO contracts"
+ " (purse_pub"
+ " ,pub_ckey"
+ " ,e_contract"
+ " ,contract_sig"
+ " ,purse_expiration"
+ " ) SELECT "
+ " $1, $2, $3, $4, purse_expiration"
+ " FROM purse_requests"
+ " WHERE purse_pub=$1"
+ " ON CONFLICT DO NOTHING;");
qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_contract",
params);
@@ -68,8 +68,8 @@ TEH_PG_insert_contract (
struct TALER_EncryptedContract econtract2;
qs = TEH_PG_select_contract_by_purse (pg,
- purse_pub,
- &econtract2);
+ purse_pub,
+ &econtract2);
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
GNUNET_break (0);
diff --git a/src/exchangedb/pg_insert_denomination_info.c b/src/exchangedb/pg_insert_denomination_info.c
index 301996116..878bc5d81 100644
--- a/src/exchangedb/pg_insert_denomination_info.c
+++ b/src/exchangedb/pg_insert_denomination_info.c
@@ -42,11 +42,16 @@ TEH_PG_insert_denomination_info (
GNUNET_PQ_query_param_timestamp (&issue->expire_withdraw),
GNUNET_PQ_query_param_timestamp (&issue->expire_deposit),
GNUNET_PQ_query_param_timestamp (&issue->expire_legal),
- TALER_PQ_query_param_amount (&issue->value),
- TALER_PQ_query_param_amount (&issue->fees.withdraw),
- TALER_PQ_query_param_amount (&issue->fees.deposit),
- TALER_PQ_query_param_amount (&issue->fees.refresh),
- TALER_PQ_query_param_amount (&issue->fees.refund),
+ TALER_PQ_query_param_amount (pg->conn,
+ &issue->value),
+ TALER_PQ_query_param_amount (pg->conn,
+ &issue->fees.withdraw),
+ TALER_PQ_query_param_amount (pg->conn,
+ &issue->fees.deposit),
+ TALER_PQ_query_param_amount (pg->conn,
+ &issue->fees.refresh),
+ TALER_PQ_query_param_amount (pg->conn,
+ &issue->fees.refund),
GNUNET_PQ_query_param_uint32 (&denom_pub->age_mask.bits),
GNUNET_PQ_query_param_end
};
@@ -81,20 +86,15 @@ TEH_PG_insert_denomination_info (
",expire_withdraw"
",expire_deposit"
",expire_legal"
- ",coin_val" /* value of this denom */
- ",coin_frac" /* fractional value of this denom */
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refund_val"
- ",fee_refund_frac"
+ ",coin" /* value of this denom */
+ ",fee_withdraw"
+ ",fee_deposit"
+ ",fee_refresh"
+ ",fee_refund"
",age_mask"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
- " $11, $12, $13, $14, $15, $16, $17, $18);");
+ " $11, $12, $13);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"denomination_insert",
params);
diff --git a/src/exchangedb/pg_insert_denomination_revocation.c b/src/exchangedb/pg_insert_denomination_revocation.c
index 061d7adc0..49445f262 100644
--- a/src/exchangedb/pg_insert_denomination_revocation.c
+++ b/src/exchangedb/pg_insert_denomination_revocation.c
@@ -39,7 +39,7 @@ TEH_PG_insert_denomination_revocation (
GNUNET_PQ_query_param_end
};
- /* Used in #postgres_insert_denomination_revocation() */
+ /* Used in #postgres_insert_denomination_revocation() */
PREPARE (pg,
"denomination_revocation_insert",
"INSERT INTO denomination_revocations "
diff --git a/src/exchangedb/pg_insert_deposit.c b/src/exchangedb/pg_insert_deposit.c
deleted file mode 100644
index ec4d49bf9..000000000
--- a/src/exchangedb/pg_insert_deposit.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- 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 exchangedb/pg_insert_deposit.c
- * @brief Implementation of the insert_deposit function for Postgres
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "taler_error_codes.h"
-#include "taler_dbevents.h"
-#include "taler_pq_lib.h"
-#include "pg_insert_deposit.h"
-#include "pg_helper.h"
-#include "pg_setup_wire_target.h"
-#include "pg_compute_shard.h"
-
-
-enum GNUNET_DB_QueryStatus
-TEH_PG_insert_deposit (void *cls,
- struct GNUNET_TIME_Timestamp exchange_timestamp,
- const struct TALER_EXCHANGEDB_Deposit *deposit)
-{
- struct PostgresClosure *pg = cls;
- struct TALER_PaytoHashP h_payto;
- enum GNUNET_DB_QueryStatus qs;
-
- qs = TEH_PG_setup_wire_target (pg,
- deposit->receiver_wire_account,
- &h_payto);
- if (qs < 0)
- return qs;
- if (GNUNET_TIME_timestamp_cmp (deposit->wire_deadline,
- <,
- deposit->refund_deadline))
- {
- GNUNET_break (0);
- }
- {
- uint64_t shard = TEH_PG_compute_shard (&deposit->merchant_pub);
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
- TALER_PQ_query_param_amount (&deposit->amount_with_fee),
- GNUNET_PQ_query_param_timestamp (&deposit->timestamp),
- GNUNET_PQ_query_param_timestamp (&deposit->refund_deadline),
- GNUNET_PQ_query_param_timestamp (&deposit->wire_deadline),
- GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
- GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms),
- GNUNET_PQ_query_param_auto_from_type (&deposit->wire_salt),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- GNUNET_PQ_query_param_auto_from_type (&deposit->csig),
- GNUNET_PQ_query_param_timestamp (&exchange_timestamp),
- GNUNET_PQ_query_param_uint64 (&shard),
- GNUNET_PQ_query_param_end
- };
-
- GNUNET_assert (shard <= INT32_MAX);
- GNUNET_log (
- GNUNET_ERROR_TYPE_INFO,
- "Inserting deposit to be executed at %s (%llu/%llu)\n",
- GNUNET_TIME_timestamp2s (deposit->wire_deadline),
- (unsigned long long) deposit->wire_deadline.abs_time.abs_value_us,
- (unsigned long long) deposit->refund_deadline.abs_time.abs_value_us);
- /* Store information about a /deposit the exchange is to execute.
- Used in #postgres_insert_deposit(). Only used in test cases. */
- PREPARE (pg,
- "insert_deposit",
- "INSERT INTO deposits "
- "(known_coin_id"
- ",coin_pub"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",wallet_timestamp"
- ",refund_deadline"
- ",wire_deadline"
- ",merchant_pub"
- ",h_contract_terms"
- ",wire_salt"
- ",wire_target_h_payto"
- ",coin_sig"
- ",exchange_timestamp"
- ",shard"
- ") SELECT known_coin_id, $1, $2, $3, $4, $5, $6, "
- " $7, $8, $9, $10, $11, $12, $13"
- " FROM known_coins"
- " WHERE coin_pub=$1"
- " ON CONFLICT DO NOTHING;");
-
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_deposit",
- params);
- }
-}
diff --git a/src/exchangedb/pg_insert_drain_profit.c b/src/exchangedb/pg_insert_drain_profit.c
index 19340eafb..a0de02e9b 100644
--- a/src/exchangedb/pg_insert_drain_profit.c
+++ b/src/exchangedb/pg_insert_drain_profit.c
@@ -41,11 +41,12 @@ TEH_PG_insert_drain_profit (
GNUNET_PQ_query_param_string (account_section),
GNUNET_PQ_query_param_string (payto_uri),
GNUNET_PQ_query_param_timestamp (&request_timestamp),
- TALER_PQ_query_param_amount (amount),
+ TALER_PQ_query_param_amount (pg->conn,
+ amount),
GNUNET_PQ_query_param_auto_from_type (master_sig),
GNUNET_PQ_query_param_end
};
- /* Used in #postgres_insert_drain_profit() */
+
PREPARE (pg,
"drain_profit_insert",
"INSERT INTO profit_drains "
@@ -53,11 +54,9 @@ TEH_PG_insert_drain_profit (
",account_section"
",payto_uri"
",trigger_date"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",master_sig"
- ") VALUES ($1, $2, $3, $4, $5, $6, $7);");
-
+ ") VALUES ($1, $2, $3, $4, $5, $6);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"drain_profit_insert",
params);
diff --git a/src/exchangedb/pg_insert_global_fee.c b/src/exchangedb/pg_insert_global_fee.c
index c08fc23bb..e78cd0b83 100644
--- a/src/exchangedb/pg_insert_global_fee.c
+++ b/src/exchangedb/pg_insert_global_fee.c
@@ -28,21 +28,24 @@
enum GNUNET_DB_QueryStatus
TEH_PG_insert_global_fee (void *cls,
- struct GNUNET_TIME_Timestamp start_date,
- struct GNUNET_TIME_Timestamp end_date,
- const struct TALER_GlobalFeeSet *fees,
- struct GNUNET_TIME_Relative purse_timeout,
- struct GNUNET_TIME_Relative history_expiration,
- uint32_t purse_account_limit,
- const struct TALER_MasterSignatureP *master_sig)
+ struct GNUNET_TIME_Timestamp start_date,
+ struct GNUNET_TIME_Timestamp end_date,
+ const struct TALER_GlobalFeeSet *fees,
+ struct GNUNET_TIME_Relative purse_timeout,
+ struct GNUNET_TIME_Relative history_expiration,
+ uint32_t purse_account_limit,
+ const struct TALER_MasterSignatureP *master_sig)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_timestamp (&start_date),
GNUNET_PQ_query_param_timestamp (&end_date),
- TALER_PQ_query_param_amount (&fees->history),
- TALER_PQ_query_param_amount (&fees->account),
- TALER_PQ_query_param_amount (&fees->purse),
+ TALER_PQ_query_param_amount (pg->conn,
+ &fees->history),
+ TALER_PQ_query_param_amount (pg->conn,
+ &fees->account),
+ TALER_PQ_query_param_amount (pg->conn,
+ &fees->purse),
GNUNET_PQ_query_param_relative_time (&purse_timeout),
GNUNET_PQ_query_param_relative_time (&history_expiration),
GNUNET_PQ_query_param_uint32 (&purse_account_limit),
@@ -59,14 +62,14 @@ TEH_PG_insert_global_fee (void *cls,
uint32_t pal;
qs = TEH_PG_get_global_fee (pg,
- start_date,
- &sd,
- &ed,
- &wx,
- &pt,
- &he,
- &pal,
- &sig);
+ start_date,
+ &sd,
+ &ed,
+ &wx,
+ &pt,
+ &he,
+ &pal,
+ &sig);
if (qs < 0)
return qs;
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
@@ -113,24 +116,21 @@ TEH_PG_insert_global_fee (void *cls,
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
- /* Used in #postgres_insert_global_fee */
+ /* Used in #postgres_insert_global_fee */
PREPARE (pg,
"insert_global_fee",
"INSERT INTO global_fee "
"(start_date"
",end_date"
- ",history_fee_val"
- ",history_fee_frac"
- ",account_fee_val"
- ",account_fee_frac"
- ",purse_fee_val"
- ",purse_fee_frac"
+ ",history_fee"
+ ",account_fee"
+ ",purse_fee"
",purse_timeout"
",history_expiration"
",purse_account_limit"
",master_sig"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);");
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_global_fee",
params);
diff --git a/src/exchangedb/pg_insert_global_fee.h b/src/exchangedb/pg_insert_global_fee.h
index 9780d5322..411345dc4 100644
--- a/src/exchangedb/pg_insert_global_fee.h
+++ b/src/exchangedb/pg_insert_global_fee.h
@@ -40,11 +40,11 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_insert_global_fee (void *cls,
- struct GNUNET_TIME_Timestamp start_date,
- struct GNUNET_TIME_Timestamp end_date,
- const struct TALER_GlobalFeeSet *fees,
- struct GNUNET_TIME_Relative purse_timeout,
- struct GNUNET_TIME_Relative history_expiration,
- uint32_t purse_account_limit,
+ struct GNUNET_TIME_Timestamp start_date,
+ struct GNUNET_TIME_Timestamp end_date,
+ const struct TALER_GlobalFeeSet *fees,
+ struct GNUNET_TIME_Relative purse_timeout,
+ struct GNUNET_TIME_Relative history_expiration,
+ uint32_t purse_account_limit,
const struct TALER_MasterSignatureP *master_sig);
#endif
diff --git a/src/exchangedb/pg_insert_history_request.h b/src/exchangedb/pg_insert_history_request.h
deleted file mode 100644
index 75004a7e4..000000000
--- a/src/exchangedb/pg_insert_history_request.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- 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 exchangedb/pg_insert_history_request.h
- * @brief implementation of the insert_history_request function for Postgres
- * @author Christian Grothoff
- */
-#ifndef PG_INSERT_HISTORY_REQUEST_H
-#define PG_INSERT_HISTORY_REQUEST_H
-
-#include "taler_util.h"
-#include "taler_json_lib.h"
-#include "taler_exchangedb_plugin.h"
-/**
- * Function called to persist a signature that
- * prove that the client requested an
- * account history. Debits the @a history_fee from
- * the reserve (if possible).
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param reserve_pub account that the history was requested for
- * @param reserve_sig signature affirming the request
- * @param request_timestamp when was the request made
- * @param history_fee how much should the @a reserve_pub be charged for the request
- * @param[out] balance_ok set to TRUE if the reserve balance
- * was sufficient
- * @param[out] idempotent set to TRUE if the request is already in the DB
- * @return transaction status code
- */
-enum GNUNET_DB_QueryStatus
-TEH_PG_insert_history_request (
- void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_ReserveSignatureP *reserve_sig,
- struct GNUNET_TIME_Timestamp request_timestamp,
- const struct TALER_Amount *history_fee,
- bool *balance_ok,
- bool *idempotent);
-
-#endif
diff --git a/src/exchangedb/pg_insert_kyc_attributes.c b/src/exchangedb/pg_insert_kyc_attributes.c
new file mode 100644
index 000000000..3c94abb85
--- /dev/null
+++ b/src/exchangedb/pg_insert_kyc_attributes.c
@@ -0,0 +1,110 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022, 2023 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 exchangedb/pg_insert_kyc_attributes.c
+ * @brief Implementation of the insert_kyc_attributes function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_insert_kyc_attributes.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_kyc_attributes (
+ void *cls,
+ uint64_t process_row,
+ const struct TALER_PaytoHashP *h_payto,
+ const struct GNUNET_ShortHashCode *kyc_prox,
+ const char *provider_section,
+ unsigned int num_checks,
+ const char *satisfied_checks[static num_checks],
+ uint32_t birthday,
+ struct GNUNET_TIME_Timestamp collection_time,
+ const char *provider_account_id,
+ const char *provider_legitimization_id,
+ struct GNUNET_TIME_Absolute expiration_time,
+ size_t enc_attributes_size,
+ const void *enc_attributes,
+ bool require_aml)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_TIME_Timestamp expiration
+ = GNUNET_TIME_absolute_to_timestamp (expiration_time);
+ struct TALER_KycCompletedEventP rep = {
+ .header.size = htons (sizeof (rep)),
+ .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
+ .h_payto = *h_payto
+ };
+ char *kyc_completed_notify_s
+ = GNUNET_PQ_get_event_notify_channel (&rep.header);
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&process_row),
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_auto_from_type (kyc_prox),
+ GNUNET_PQ_query_param_string (provider_section),
+ GNUNET_PQ_query_param_array_ptrs_string (num_checks,
+ satisfied_checks,
+ pg->conn),
+ GNUNET_PQ_query_param_uint32 (&birthday),
+ (NULL == provider_account_id)
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (provider_account_id),
+ (NULL == provider_legitimization_id)
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (provider_legitimization_id),
+ GNUNET_PQ_query_param_timestamp (&collection_time),
+ GNUNET_PQ_query_param_absolute_time (&expiration_time),
+ GNUNET_PQ_query_param_timestamp (&expiration),
+ GNUNET_PQ_query_param_fixed_size (enc_attributes,
+ enc_attributes_size),
+ GNUNET_PQ_query_param_bool (require_aml),
+ GNUNET_PQ_query_param_string (kyc_completed_notify_s),
+ GNUNET_PQ_query_param_end
+ };
+ bool ok;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("out_ok",
+ &ok),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Inserting KYC attributes, wake up on %s\n",
+ kyc_completed_notify_s);
+ PREPARE (pg,
+ "insert_kyc_attributes",
+ "SELECT "
+ " out_ok"
+ " FROM exchange_do_insert_kyc_attributes "
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14);");
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "insert_kyc_attributes",
+ params,
+ rs);
+ GNUNET_PQ_cleanup_query_params_closures (params);
+ GNUNET_free (kyc_completed_notify_s);
+ GNUNET_PQ_event_do_poll (pg->conn);
+ if (qs < 0)
+ return qs;
+ if (! ok)
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ return qs;
+}
diff --git a/src/exchangedb/pg_insert_kyc_attributes.h b/src/exchangedb/pg_insert_kyc_attributes.h
new file mode 100644
index 000000000..35b25bdc8
--- /dev/null
+++ b/src/exchangedb/pg_insert_kyc_attributes.h
@@ -0,0 +1,69 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022, 2023 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 exchangedb/pg_insert_kyc_attributes.h
+ * @brief implementation of the insert_kyc_attributes function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_KYC_ATTRIBUTES_H
+#define PG_INSERT_KYC_ATTRIBUTES_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Store KYC attribute data, update KYC process status and
+ * AML status for the given account.
+ *
+ * @param cls closure
+ * @param process_row KYC process row to update
+ * @param h_payto account for which the attribute data is stored
+ * @param kyc_prox key for similarity search
+ * @param provider_section provider that must be checked
+ * @param num_checks how many checks do these attributes satisfy
+ * @param satisfied_checks array of checks satisfied by these attributes
+ * @param provider_account_id provider account ID
+ * @param provider_legitimization_id provider legitimization ID
+ * @param birthday birthdate of user, in days after 1990, or 0 if unknown or definitively adult
+ * @param collection_time when was the data collected
+ * @param expiration_time when does the data expire
+ * @param enc_attributes_size number of bytes in @a enc_attributes
+ * @param enc_attributes encrypted attribute data
+ * @param require_aml true to trigger AML
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_kyc_attributes (
+ void *cls,
+ uint64_t process_row,
+ const struct TALER_PaytoHashP *h_payto,
+ const struct GNUNET_ShortHashCode *kyc_prox,
+ const char *provider_section,
+ unsigned int num_checks,
+ const char *satisfied_checks[static num_checks],
+ uint32_t birthday,
+ struct GNUNET_TIME_Timestamp collection_time,
+ const char *provider_account_id,
+ const char *provider_legitimization_id,
+ struct GNUNET_TIME_Absolute expiration_time,
+ size_t enc_attributes_size,
+ const void *enc_attributes,
+ bool require_aml);
+
+
+#endif
diff --git a/src/exchangedb/pg_insert_kyc_failure.c b/src/exchangedb/pg_insert_kyc_failure.c
new file mode 100644
index 000000000..568c39ca8
--- /dev/null
+++ b/src/exchangedb/pg_insert_kyc_failure.c
@@ -0,0 +1,82 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 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 exchangedb/pg_insert_kyc_failure.c
+ * @brief Implementation of the insert_kyc_failure function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_insert_kyc_failure.h"
+#include "pg_helper.h"
+#include "pg_event_notify.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_kyc_failure (
+ void *cls,
+ uint64_t process_row,
+ const struct TALER_PaytoHashP *h_payto,
+ const char *provider_section,
+ const char *provider_account_id,
+ const char *provider_legitimization_id)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&process_row),
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_string (provider_section),
+ NULL != provider_account_id
+ ? GNUNET_PQ_query_param_string (provider_account_id)
+ : GNUNET_PQ_query_param_null (),
+ NULL != provider_legitimization_id
+ ? GNUNET_PQ_query_param_string (provider_legitimization_id)
+ : GNUNET_PQ_query_param_null (),
+ GNUNET_PQ_query_param_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (pg,
+ "insert_kyc_failure",
+ "UPDATE legitimization_processes"
+ " SET"
+ " finished=TRUE"
+ " ,provider_user_id=$4"
+ " ,provider_legitimization_id=$5"
+ " WHERE h_payto=$2"
+ " AND legitimization_process_serial_id=$1"
+ " AND provider_section=$3;");
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_kyc_failure",
+ params);
+ if (qs > 0)
+ {
+ /* FIXME: might want to do this eventually in the same transaction... */
+ struct TALER_KycCompletedEventP rep = {
+ .header.size = htons (sizeof (rep)),
+ .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
+ .h_payto = *h_payto
+ };
+
+ TEH_PG_event_notify (pg,
+ &rep.header,
+ NULL,
+ 0);
+ }
+ return qs;
+}
diff --git a/src/exchangedb/pg_insert_kyc_failure.h b/src/exchangedb/pg_insert_kyc_failure.h
new file mode 100644
index 000000000..46d08df9c
--- /dev/null
+++ b/src/exchangedb/pg_insert_kyc_failure.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 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 exchangedb/pg_insert_kyc_failure.h
+ * @brief implementation of the insert_kyc_failure function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_KYC_FAILURE_H
+#define PG_INSERT_KYC_FAILURE_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Update KYC process status to finished (and failed).
+ *
+ * @param cls closure
+ * @param process_row KYC process row to update
+ * @param h_payto account for which the attribute data is stored
+ * @param provider_section provider that must be checked
+ * @param provider_account_id provider account ID
+ * @param provider_legitimization_id provider legitimization ID
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_kyc_failure (
+ void *cls,
+ uint64_t process_row,
+ const struct TALER_PaytoHashP *h_payto,
+ const char *provider_section,
+ const char *provider_account_id,
+ const char *provider_legitimization_id);
+
+
+#endif
diff --git a/src/exchangedb/pg_insert_kyc_requirement_for_account.c b/src/exchangedb/pg_insert_kyc_requirement_for_account.c
index be5cbac87..95f695297 100644
--- a/src/exchangedb/pg_insert_kyc_requirement_for_account.c
+++ b/src/exchangedb/pg_insert_kyc_requirement_for_account.c
@@ -30,11 +30,15 @@ TEH_PG_insert_kyc_requirement_for_account (
void *cls,
const char *provider_section,
const struct TALER_PaytoHashP *h_payto,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
uint64_t *requirement_row)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (h_payto),
+ (NULL == reserve_pub)
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_string (provider_section),
GNUNET_PQ_query_param_end
};
@@ -43,14 +47,15 @@ TEH_PG_insert_kyc_requirement_for_account (
requirement_row),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_insert_kyc_requirement_for_account() */
+ /* Used in #postgres_insert_kyc_requirement_for_account() */
PREPARE (pg,
"insert_legitimization_requirement",
"INSERT INTO legitimization_requirements"
" (h_payto"
+ " ,reserve_pub"
" ,required_checks"
" ) VALUES "
- " ($1, $2)"
+ " ($1, $2, $3)"
" ON CONFLICT (h_payto,required_checks) "
" DO UPDATE SET h_payto=$1" /* syntax requirement: dummy op */
" RETURNING legitimization_requirement_serial_id");
diff --git a/src/exchangedb/pg_insert_kyc_requirement_for_account.h b/src/exchangedb/pg_insert_kyc_requirement_for_account.h
index 5f9bf6a48..331c8ba0c 100644
--- a/src/exchangedb/pg_insert_kyc_requirement_for_account.h
+++ b/src/exchangedb/pg_insert_kyc_requirement_for_account.h
@@ -32,6 +32,7 @@
* @param cls closure
* @param provider_section provider that must be checked
* @param h_payto account that must be KYC'ed
+ * @param reserve_pub if the account is a reserve, its public key. Maybe NULL
* @param[out] requirement_row set to legitimization requirement row for this check
* @return database transaction status
*/
@@ -40,5 +41,7 @@ TEH_PG_insert_kyc_requirement_for_account (
void *cls,
const char *provider_section,
const struct TALER_PaytoHashP *h_payto,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
uint64_t *requirement_row);
+
#endif
diff --git a/src/exchangedb/pg_insert_kyc_requirement_process.c b/src/exchangedb/pg_insert_kyc_requirement_process.c
index d520ac59c..a20db3388 100644
--- a/src/exchangedb/pg_insert_kyc_requirement_process.c
+++ b/src/exchangedb/pg_insert_kyc_requirement_process.c
@@ -24,6 +24,7 @@
#include "taler_pq_lib.h"
#include "pg_insert_kyc_requirement_process.h"
#include "pg_helper.h"
+#include <gnunet/gnunet_pq_lib.h>
enum GNUNET_DB_QueryStatus
TEH_PG_insert_kyc_requirement_process (
@@ -35,8 +36,11 @@ TEH_PG_insert_kyc_requirement_process (
uint64_t *process_row)
{
struct PostgresClosure *pg = cls;
+ struct GNUNET_TIME_Absolute now
+ = GNUNET_TIME_absolute_get ();
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_string (provider_section),
(NULL != provider_account_id)
? GNUNET_PQ_query_param_string (provider_account_id)
@@ -52,20 +56,16 @@ TEH_PG_insert_kyc_requirement_process (
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_insert_kyc_requirement_process() */
PREPARE (pg,
"insert_legitimization_process",
"INSERT INTO legitimization_processes"
" (h_payto"
+ " ,start_time"
" ,provider_section"
" ,provider_user_id"
" ,provider_legitimization_id"
" ) VALUES "
- " ($1, $2, $3, $4)"
- " ON CONFLICT (h_payto,provider_section) "
- " DO UPDATE SET"
- " provider_user_id=$3"
- " ,provider_legitimization_id=$4"
+ " ($1, $2, $3, $4, $5)"
" RETURNING legitimization_process_serial_id");
return GNUNET_PQ_eval_prepared_singleton_select (
pg->conn,
diff --git a/src/exchangedb/pg_insert_kyc_requirement_process.h b/src/exchangedb/pg_insert_kyc_requirement_process.h
index 3f354472e..df21db8cd 100644
--- a/src/exchangedb/pg_insert_kyc_requirement_process.h
+++ b/src/exchangedb/pg_insert_kyc_requirement_process.h
@@ -45,4 +45,5 @@ TEH_PG_insert_kyc_requirement_process (
const char *provider_account_id,
const char *provider_legitimization_id,
uint64_t *process_row);
+
#endif
diff --git a/src/exchangedb/pg_insert_partner.c b/src/exchangedb/pg_insert_partner.c
index 567f37763..d1d6069de 100644
--- a/src/exchangedb/pg_insert_partner.c
+++ b/src/exchangedb/pg_insert_partner.c
@@ -28,13 +28,13 @@
enum GNUNET_DB_QueryStatus
TEH_PG_insert_partner (void *cls,
- const struct TALER_MasterPublicKeyP *master_pub,
- struct GNUNET_TIME_Timestamp start_date,
- struct GNUNET_TIME_Timestamp end_date,
- struct GNUNET_TIME_Relative wad_frequency,
- const struct TALER_Amount *wad_fee,
- const char *partner_base_url,
- const struct TALER_MasterSignatureP *master_sig)
+ const struct TALER_MasterPublicKeyP *master_pub,
+ struct GNUNET_TIME_Timestamp start_date,
+ struct GNUNET_TIME_Timestamp end_date,
+ struct GNUNET_TIME_Relative wad_frequency,
+ const struct TALER_Amount *wad_fee,
+ const char *partner_base_url,
+ const struct TALER_MasterSignatureP *master_sig)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -42,7 +42,8 @@ TEH_PG_insert_partner (void *cls,
GNUNET_PQ_query_param_timestamp (&start_date),
GNUNET_PQ_query_param_timestamp (&end_date),
GNUNET_PQ_query_param_relative_time (&wad_frequency),
- TALER_PQ_query_param_amount (wad_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ wad_fee),
GNUNET_PQ_query_param_auto_from_type (master_sig),
GNUNET_PQ_query_param_string (partner_base_url),
GNUNET_PQ_query_param_end
@@ -56,15 +57,13 @@ TEH_PG_insert_partner (void *cls,
" ,start_date"
" ,end_date"
" ,wad_frequency"
- " ,wad_fee_val"
- " ,wad_fee_frac"
+ " ,wad_fee"
" ,master_sig"
" ,partner_base_url"
" ) VALUES "
- " ($1, $2, $3, $4, $5, $6, $7, $8);");
+ " ($1, $2, $3, $4, $5, $6, $7)"
+ " ON CONFLICT DO NOTHING;");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_partner",
params);
}
-
-
diff --git a/src/exchangedb/pg_insert_partner.h b/src/exchangedb/pg_insert_partner.h
index eed40a80e..3ebae786c 100644
--- a/src/exchangedb/pg_insert_partner.h
+++ b/src/exchangedb/pg_insert_partner.h
@@ -40,12 +40,12 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_insert_partner (void *cls,
- const struct TALER_MasterPublicKeyP *master_pub,
- struct GNUNET_TIME_Timestamp start_date,
- struct GNUNET_TIME_Timestamp end_date,
- struct GNUNET_TIME_Relative wad_frequency,
- const struct TALER_Amount *wad_fee,
- const char *partner_base_url,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ struct GNUNET_TIME_Timestamp start_date,
+ struct GNUNET_TIME_Timestamp end_date,
+ struct GNUNET_TIME_Relative wad_frequency,
+ const struct TALER_Amount *wad_fee,
+ const char *partner_base_url,
const struct TALER_MasterSignatureP *master_sig);
#endif
diff --git a/src/exchangedb/pg_insert_purse_request.c b/src/exchangedb/pg_insert_purse_request.c
index f42129ec4..d8d68abe8 100644
--- a/src/exchangedb/pg_insert_purse_request.c
+++ b/src/exchangedb/pg_insert_purse_request.c
@@ -56,8 +56,10 @@ TEH_PG_insert_purse_request (
GNUNET_PQ_query_param_uint32 (&age_limit),
GNUNET_PQ_query_param_uint32 (&flags32),
GNUNET_PQ_query_param_bool (in_reserve_quota),
- TALER_PQ_query_param_amount (amount),
- TALER_PQ_query_param_amount (purse_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ amount),
+ TALER_PQ_query_param_amount (pg->conn,
+ purse_fee),
GNUNET_PQ_query_param_auto_from_type (purse_sig),
GNUNET_PQ_query_param_end
};
@@ -74,13 +76,11 @@ TEH_PG_insert_purse_request (
" ,age_limit"
" ,flags"
" ,in_reserve_quota"
- " ,amount_with_fee_val"
- " ,amount_with_fee_frac"
- " ,purse_fee_val"
- " ,purse_fee_frac"
+ " ,amount_with_fee"
+ " ,purse_fee"
" ,purse_sig"
" ) VALUES "
- " ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)"
+ " ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)"
" ON CONFLICT DO NOTHING;");
qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_purse_request",
diff --git a/src/exchangedb/pg_insert_records_by_table.c b/src/exchangedb/pg_insert_records_by_table.c
index d6630797a..6ecec5bcf 100644
--- a/src/exchangedb/pg_insert_records_by_table.c
+++ b/src/exchangedb/pg_insert_records_by_table.c
@@ -1,6 +1,6 @@
/*
This file is part of GNUnet
- Copyright (C) 2020, 2021, 2022 Taler Systems SA
+ Copyright (C) 2020-2023 Taler Systems SA
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
@@ -19,8 +19,9 @@
*/
/**
* @file exchangedb/pg_insert_records_by_table.c
- * @brief insert_records_by_table implementation
+ * @brief replicate_records_by_table implementation
* @author Christian Grothoff
+ * @author Özgür Kesim
*/
#include "platform.h"
#include "taler_error_codes.h"
@@ -28,6 +29,7 @@
#include "taler_pq_lib.h"
#include "pg_insert_records_by_table.h"
#include "pg_helper.h"
+#include <gnunet/gnunet_pq_lib.h>
/**
@@ -72,14 +74,20 @@ irbt_cb_table_denominations (struct PostgresClosure *pg,
&td->details.denominations.expire_deposit),
GNUNET_PQ_query_param_timestamp (
&td->details.denominations.expire_legal),
- TALER_PQ_query_param_amount (&td->details.denominations.coin),
TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.denominations.coin),
+ TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.denominations.fees.withdraw),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.denominations.fees.deposit),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.denominations.fees.refresh),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.denominations.fees.refund),
GNUNET_PQ_query_param_end
};
@@ -97,19 +105,14 @@ irbt_cb_table_denominations (struct PostgresClosure *pg,
",expire_withdraw"
",expire_deposit"
",expire_legal"
- ",coin_val"
- ",coin_frac"
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refund_val"
- ",fee_refund_frac"
+ ",coin"
+ ",fee_withdraw"
+ ",fee_deposit"
+ ",fee_refresh"
+ ",fee_refund"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
- " $11, $12, $13, $14, $15, $16, $17, $18, $19, $20);");
+ " $11, $12, $13, $14, $15);");
TALER_denom_pub_hash (
&td->details.denominations.denom_pub,
@@ -227,7 +230,7 @@ irbt_cb_table_legitimization_processes (struct PostgresClosure *pg,
",provider_user_id"
",provider_legitimization_id"
") VALUES "
- "($1, $2, $3, $4, $5, $6);");
+ "($1, $3, $4, $5, $6, %7);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_legitimization_processes",
params);
@@ -249,6 +252,10 @@ irbt_cb_table_legitimization_requirements (struct PostgresClosure *pg,
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_auto_from_type (
&td->details.legitimization_requirements.h_payto),
+ td->details.legitimization_requirements.no_reserve_pub
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_auto_from_type (
+ &td->details.legitimization_requirements.reserve_pub),
GNUNET_PQ_query_param_string (
td->details.legitimization_requirements.required_checks),
GNUNET_PQ_query_param_end
@@ -259,6 +266,7 @@ irbt_cb_table_legitimization_requirements (struct PostgresClosure *pg,
"INSERT INTO legitimization_requirements"
"(legitimization_requirement_serial_id"
",h_payto"
+ ",reserve_pub"
",required_checks"
") VALUES "
"($1, $2, $3);");
@@ -314,7 +322,9 @@ irbt_cb_table_reserves_in (struct PostgresClosure *pg,
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_uint64 (&td->details.reserves_in.wire_reference),
- TALER_PQ_query_param_amount (&td->details.reserves_in.credit),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.reserves_in.credit),
GNUNET_PQ_query_param_auto_from_type (
&td->details.reserves_in.sender_account_h_payto),
GNUNET_PQ_query_param_string (
@@ -330,14 +340,13 @@ irbt_cb_table_reserves_in (struct PostgresClosure *pg,
"INSERT INTO reserves_in"
"(reserve_in_serial_id"
",wire_reference"
- ",credit_val"
- ",credit_frac"
+ ",credit"
",wire_source_h_payto"
",exchange_account_section"
",execution_date"
",reserve_pub"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8);");
+ "($1, $2, $3, $4, $5, $6, $7);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_reserves_in",
params);
@@ -362,6 +371,7 @@ irbt_cb_table_reserves_open_requests (struct PostgresClosure *pg,
GNUNET_PQ_query_param_auto_from_type (
&td->details.reserves_open_requests.reserve_sig),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.reserves_open_requests.reserve_payment),
GNUNET_PQ_query_param_uint32 (
&td->details.reserves_open_requests.requested_purse_limit),
@@ -376,11 +386,10 @@ irbt_cb_table_reserves_open_requests (struct PostgresClosure *pg,
",request_timestamp"
",expiration_date"
",reserve_sig"
- ",reserve_payment_val"
- ",reserve_payment_frac"
+ ",reserve_payment"
",requested_purse_limit"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8);");
+ "($1, $2, $3, $4, $5, $6, $7);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_reserves_open_requests",
params);
@@ -407,6 +416,7 @@ irbt_cb_table_reserves_open_deposits (
GNUNET_PQ_query_param_auto_from_type (
&td->details.reserves_open_deposits.reserve_sig),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.reserves_open_deposits.contribution),
GNUNET_PQ_query_param_end
};
@@ -419,10 +429,9 @@ irbt_cb_table_reserves_open_deposits (
",reserve_pub"
",coin_pub"
",coin_sig"
- ",contribution_val"
- ",contribution_frac"
+ ",contribution"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7);");
+ "($1, $2, $3, $4, $5, $6);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_reserves_open_deposits",
params);
@@ -447,8 +456,12 @@ irbt_cb_table_reserves_close (struct PostgresClosure *pg,
&td->details.reserves_close.wtid),
GNUNET_PQ_query_param_auto_from_type (
&td->details.reserves_close.sender_account_h_payto),
- TALER_PQ_query_param_amount (&td->details.reserves_close.amount),
- TALER_PQ_query_param_amount (&td->details.reserves_close.closing_fee),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.reserves_close.amount),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.reserves_close.closing_fee),
GNUNET_PQ_query_param_auto_from_type (
&td->details.reserves_close.reserve_pub),
GNUNET_PQ_query_param_end
@@ -461,13 +474,11 @@ irbt_cb_table_reserves_close (struct PostgresClosure *pg,
",execution_date"
",wtid"
",wire_target_h_payto"
- ",amount_val"
- ",amount_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
+ ",amount"
+ ",closing_fee"
",reserve_pub"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9);");
+ "($1, $2, $3, $4, $5, $6, $7);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_reserves_close",
params);
@@ -499,6 +510,7 @@ irbt_cb_table_reserves_out (struct PostgresClosure *pg,
GNUNET_PQ_query_param_timestamp (
&td->details.reserves_out.execution_date),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.reserves_out.amount_with_fee),
GNUNET_PQ_query_param_end
};
@@ -513,10 +525,9 @@ irbt_cb_table_reserves_out (struct PostgresClosure *pg,
",reserve_uuid"
",reserve_sig"
",execution_date"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9);");
+ "($1, $2, $3, $4, $5, $6, $7, $8);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_reserves_out",
params);
@@ -721,6 +732,7 @@ irbt_cb_table_refresh_commitments (struct PostgresClosure *pg,
GNUNET_PQ_query_param_auto_from_type (
&td->details.refresh_commitments.old_coin_sig),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.refresh_commitments.amount_with_fee),
GNUNET_PQ_query_param_uint32 (
&td->details.refresh_commitments.noreveal_index),
@@ -735,12 +747,11 @@ irbt_cb_table_refresh_commitments (struct PostgresClosure *pg,
"(melt_serial_id"
",rc"
",old_coin_sig"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
",noreveal_index"
",old_coin_pub"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7);");
+ "($1, $2, $3, $4, $5, $6);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_refresh_commitments",
params);
@@ -844,67 +855,106 @@ irbt_cb_table_refresh_transfer_keys (
/**
- * Function called with deposits records to insert into table.
+ * Function called with batch deposits records to insert into table.
*
* @param pg plugin context
* @param td record to insert
*/
static enum GNUNET_DB_QueryStatus
-irbt_cb_table_deposits (struct PostgresClosure *pg,
- const struct TALER_EXCHANGEDB_TableData *td)
+irbt_cb_table_batch_deposits (struct PostgresClosure *pg,
+ const struct TALER_EXCHANGEDB_TableData *td)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial),
- GNUNET_PQ_query_param_uint64 (&td->details.deposits.shard),
- GNUNET_PQ_query_param_uint64 (&td->details.deposits.known_coin_id),
+ GNUNET_PQ_query_param_uint64 (&td->details.batch_deposits.shard),
GNUNET_PQ_query_param_auto_from_type (
- &td->details.deposits.coin_pub),
- TALER_PQ_query_param_amount (&td->details.deposits.amount_with_fee),
- GNUNET_PQ_query_param_timestamp (&td->details.deposits.wallet_timestamp),
+ &td->details.batch_deposits.merchant_pub),
+ GNUNET_PQ_query_param_timestamp (
+ &td->details.batch_deposits.wallet_timestamp),
+ GNUNET_PQ_query_param_timestamp (
+ &td->details.batch_deposits.exchange_timestamp),
GNUNET_PQ_query_param_timestamp (
- &td->details.deposits.exchange_timestamp),
- GNUNET_PQ_query_param_timestamp (&td->details.deposits.refund_deadline),
- GNUNET_PQ_query_param_timestamp (&td->details.deposits.wire_deadline),
- GNUNET_PQ_query_param_auto_from_type (&td->details.deposits.merchant_pub),
- GNUNET_PQ_query_param_auto_from_type (
- &td->details.deposits.h_contract_terms),
- GNUNET_PQ_query_param_auto_from_type (&td->details.deposits.coin_sig),
- GNUNET_PQ_query_param_auto_from_type (&td->details.deposits.wire_salt),
- GNUNET_PQ_query_param_auto_from_type (
- &td->details.deposits.wire_target_h_payto),
- GNUNET_PQ_query_param_bool (td->details.deposits.policy_blocked),
- 0 == td->details.deposits.policy_details_serial_id
+ &td->details.batch_deposits.refund_deadline),
+ GNUNET_PQ_query_param_timestamp (&td->details.batch_deposits.wire_deadline),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.batch_deposits.h_contract_terms),
+ td->details.batch_deposits.no_wallet_data_hash
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_auto_from_type (
+ &td->details.batch_deposits.wallet_data_hash),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.batch_deposits.wire_salt),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.batch_deposits.wire_target_h_payto),
+ GNUNET_PQ_query_param_bool (td->details.batch_deposits.policy_blocked),
+ td->details.batch_deposits.no_policy_details
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_uint64 (
- &td->details.deposits.policy_details_serial_id),
+ &td->details.batch_deposits.policy_details_serial_id),
GNUNET_PQ_query_param_end
};
PREPARE (pg,
- "insert_into_table_deposits",
- "INSERT INTO deposits"
- "(deposit_serial_id"
+ "insert_into_table_batch_deposits",
+ "INSERT INTO batch_deposits"
+ "(batch_deposit_serial_id"
",shard"
- ",known_coin_id"
- ",coin_pub"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",merchant_pub"
",wallet_timestamp"
",exchange_timestamp"
",refund_deadline"
",wire_deadline"
- ",merchant_pub"
",h_contract_terms"
- ",coin_sig"
+ ",wallet_data_hash"
",wire_salt"
",wire_target_h_payto"
- ",policy_blocked"
",policy_details_serial_id"
+ ",policy_blocked"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
- " $11, $12, $13, $14, $15, $16, $17);");
+ " $11, $12, $13);");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_into_table_batch_deposits",
+ params);
+}
+
+
+/**
+ * Function called with deposits records to insert into table.
+ *
+ * @param pg plugin context
+ * @param td record to insert
+ */
+static enum GNUNET_DB_QueryStatus
+irbt_cb_table_coin_deposits (struct PostgresClosure *pg,
+ const struct TALER_EXCHANGEDB_TableData *td)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&td->serial),
+ GNUNET_PQ_query_param_uint64 (
+ &td->details.coin_deposits.batch_deposit_serial_id),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.coin_deposits.coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.coin_deposits.coin_sig),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.coin_deposits.amount_with_fee),
+ GNUNET_PQ_query_param_end
+ };
+
+ PREPARE (pg,
+ "insert_into_table_coin_deposits",
+ "INSERT INTO coin_deposits"
+ "(coin_deposit_serial_id"
+ ",batch_deposit_serial_id"
+ ",coin_pub"
+ ",coin_sig"
+ ",amount_with_fee"
+ ") VALUES "
+ "($1, $2, $3, $4, $5);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_into_table_deposits",
+ "insert_into_table_coin_deposits",
params);
}
@@ -924,8 +974,11 @@ irbt_cb_table_refunds (struct PostgresClosure *pg,
GNUNET_PQ_query_param_auto_from_type (&td->details.refunds.coin_pub),
GNUNET_PQ_query_param_auto_from_type (&td->details.refunds.merchant_sig),
GNUNET_PQ_query_param_uint64 (&td->details.refunds.rtransaction_id),
- TALER_PQ_query_param_amount (&td->details.refunds.amount_with_fee),
- GNUNET_PQ_query_param_uint64 (&td->details.refunds.deposit_serial_id),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.refunds.amount_with_fee),
+ GNUNET_PQ_query_param_uint64 (
+ &td->details.refunds.batch_deposit_serial_id),
GNUNET_PQ_query_param_end
};
@@ -936,11 +989,10 @@ irbt_cb_table_refunds (struct PostgresClosure *pg,
",coin_pub"
",merchant_sig"
",rtransaction_id"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",deposit_serial_id"
+ ",amount_with_fee"
+ ",batch_deposit_serial_id"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7);");
+ "($1, $2, $3, $4, $5, $6);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_refunds",
params);
@@ -965,7 +1017,9 @@ irbt_cb_table_wire_out (struct PostgresClosure *pg,
&td->details.wire_out.wire_target_h_payto),
GNUNET_PQ_query_param_string (
td->details.wire_out.exchange_account_section),
- TALER_PQ_query_param_amount (&td->details.wire_out.amount),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.wire_out.amount),
GNUNET_PQ_query_param_end
};
@@ -977,10 +1031,9 @@ irbt_cb_table_wire_out (struct PostgresClosure *pg,
",wtid_raw"
",wire_target_h_payto"
",exchange_account_section"
- ",amount_val"
- ",amount_frac"
+ ",amount"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7);");
+ "($1, $2, $3, $4, $5, $6);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_wire_out",
params);
@@ -1000,7 +1053,7 @@ irbt_cb_table_aggregation_tracking (struct PostgresClosure *pg,
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_uint64 (
- &td->details.aggregation_tracking.deposit_serial_id),
+ &td->details.aggregation_tracking.batch_deposit_serial_id),
GNUNET_PQ_query_param_auto_from_type (
&td->details.aggregation_tracking.wtid_raw),
GNUNET_PQ_query_param_end
@@ -1010,7 +1063,7 @@ irbt_cb_table_aggregation_tracking (struct PostgresClosure *pg,
"insert_into_table_aggregation_tracking",
"INSERT INTO aggregation_tracking"
"(aggregation_serial_id"
- ",deposit_serial_id"
+ ",batch_deposit_serial_id"
",wtid_raw"
") VALUES "
"($1, $2, $3);");
@@ -1035,8 +1088,12 @@ irbt_cb_table_wire_fee (struct PostgresClosure *pg,
GNUNET_PQ_query_param_string (td->details.wire_fee.wire_method),
GNUNET_PQ_query_param_timestamp (&td->details.wire_fee.start_date),
GNUNET_PQ_query_param_timestamp (&td->details.wire_fee.end_date),
- TALER_PQ_query_param_amount (&td->details.wire_fee.fees.wire),
- TALER_PQ_query_param_amount (&td->details.wire_fee.fees.closing),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.wire_fee.fees.wire),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.wire_fee.fees.closing),
GNUNET_PQ_query_param_auto_from_type (&td->details.wire_fee.master_sig),
GNUNET_PQ_query_param_end
};
@@ -1048,13 +1105,11 @@ irbt_cb_table_wire_fee (struct PostgresClosure *pg,
",wire_method"
",start_date"
",end_date"
- ",wire_fee_val"
- ",wire_fee_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
+ ",wire_fee"
+ ",closing_fee"
",master_sig"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9);");
+ "($1, $2, $3, $4, $5, $6, $7);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_wire_fee",
params);
@@ -1079,10 +1134,13 @@ irbt_cb_table_global_fee (struct PostgresClosure *pg,
GNUNET_PQ_query_param_timestamp (
&td->details.global_fee.end_date),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.global_fee.fees.history),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.global_fee.fees.account),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.global_fee.fees.purse),
GNUNET_PQ_query_param_relative_time (
&td->details.global_fee.purse_timeout),
@@ -1101,18 +1159,15 @@ irbt_cb_table_global_fee (struct PostgresClosure *pg,
"(global_fee_serial"
",start_date"
",end_date"
- ",history_fee_val"
- ",history_fee_frac"
- ",account_fee_val"
- ",account_fee_frac"
- ",purse_fee_val"
- ",purse_fee_frac"
+ ",history_fee"
+ ",account_fee"
+ ",purse_fee"
",purse_timeout"
",history_expiration"
",purse_account_limit"
",master_sig"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);");
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_global_fee",
params);
@@ -1133,7 +1188,9 @@ irbt_cb_table_recoup (struct PostgresClosure *pg,
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_auto_from_type (&td->details.recoup.coin_sig),
GNUNET_PQ_query_param_auto_from_type (&td->details.recoup.coin_blind),
- TALER_PQ_query_param_amount (&td->details.recoup.amount),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.recoup.amount),
GNUNET_PQ_query_param_timestamp (&td->details.recoup.timestamp),
GNUNET_PQ_query_param_auto_from_type (
&td->details.recoup.coin_pub),
@@ -1147,13 +1204,12 @@ irbt_cb_table_recoup (struct PostgresClosure *pg,
"(recoup_uuid"
",coin_sig"
",coin_blind"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",recoup_timestamp"
",coin_pub"
",reserve_out_serial_id"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8);");
+ "($1, $2, $3, $4, $5, $6, $7);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_recoup",
params);
@@ -1175,7 +1231,9 @@ irbt_cb_table_recoup_refresh (struct PostgresClosure *pg,
GNUNET_PQ_query_param_auto_from_type (&td->details.recoup_refresh.coin_sig),
GNUNET_PQ_query_param_auto_from_type (
&td->details.recoup_refresh.coin_blind),
- TALER_PQ_query_param_amount (&td->details.recoup_refresh.amount),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.recoup_refresh.amount),
GNUNET_PQ_query_param_timestamp (&td->details.recoup_refresh.timestamp),
GNUNET_PQ_query_param_uint64 (&td->details.recoup_refresh.known_coin_id),
GNUNET_PQ_query_param_auto_from_type (
@@ -1190,14 +1248,13 @@ irbt_cb_table_recoup_refresh (struct PostgresClosure *pg,
"(recoup_refresh_uuid"
",coin_sig"
",coin_blind"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",recoup_timestamp"
",known_coin_id"
",coin_pub"
",rrc_serial"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9);");
+ "($1, $2, $3, $4, $5, $6, $7, $8);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_recoup_refresh",
params);
@@ -1254,10 +1311,17 @@ irbt_cb_table_policy_details (struct PostgresClosure *pg,
(td->details.policy_details.no_policy_json)
? GNUNET_PQ_query_param_null ()
: TALER_PQ_query_param_json (td->details.policy_details.policy_json),
- TALER_PQ_query_param_amount (&td->details.policy_details.commitment),
- TALER_PQ_query_param_amount (&td->details.policy_details.accumulated_total),
- TALER_PQ_query_param_amount (&td->details.policy_details.fee),
- TALER_PQ_query_param_amount (&td->details.policy_details.transferable),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.policy_details.commitment),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.policy_details.accumulated_total),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.policy_details.fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ &td->details.policy_details.transferable),
GNUNET_PQ_query_param_timestamp (&td->details.policy_details.deadline),
GNUNET_PQ_query_param_uint16 (
&td->details.policy_details.fulfillment_state),
@@ -1275,18 +1339,14 @@ irbt_cb_table_policy_details (struct PostgresClosure *pg,
",policy_hash_code"
",policy_json"
",deadline"
- ",commitment_val"
- ",commitment_frac"
- ",accumulated_total_val"
- ",accumulated_total_frac"
- ",fee_val"
- ",fee_frac"
- ",transferable_val"
- ",transferable_frac"
+ ",commitment"
+ ",accumulated_total"
+ ",fee"
+ ",transferable"
",fulfillment_state"
",fulfillment_id"
") VALUES "
- "($1, $2);");
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_policy_details",
params);
@@ -1359,8 +1419,12 @@ irbt_cb_table_purse_requests (struct PostgresClosure *pg,
&td->details.purse_requests.h_contract_terms),
GNUNET_PQ_query_param_uint32 (&td->details.purse_requests.age_limit),
GNUNET_PQ_query_param_uint32 (&td->details.purse_requests.flags),
- TALER_PQ_query_param_amount (&td->details.purse_requests.amount_with_fee),
- TALER_PQ_query_param_amount (&td->details.purse_requests.purse_fee),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.purse_requests.amount_with_fee),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.purse_requests.purse_fee),
GNUNET_PQ_query_param_auto_from_type (
&td->details.purse_requests.purse_sig),
GNUNET_PQ_query_param_end
@@ -1377,13 +1441,11 @@ irbt_cb_table_purse_requests (struct PostgresClosure *pg,
",h_contract_terms"
",age_limit"
",flags"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",purse_fee_val"
- ",purse_fee_frac"
+ ",amount_with_fee"
+ ",purse_fee"
",purse_sig"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);");
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_purse_requests",
params);
@@ -1407,7 +1469,7 @@ irbt_cb_table_purse_decision (struct PostgresClosure *pg,
GNUNET_PQ_query_param_timestamp (
&td->details.purse_decision.action_timestamp),
GNUNET_PQ_query_param_bool (
- &td->details.purse_decision.refunded),
+ td->details.purse_decision.refunded),
GNUNET_PQ_query_param_end
};
@@ -1481,7 +1543,9 @@ irbt_cb_table_purse_deposits (struct PostgresClosure *pg,
GNUNET_PQ_query_param_auto_from_type (
&td->details.purse_deposits.purse_pub),
GNUNET_PQ_query_param_auto_from_type (&td->details.purse_deposits.coin_pub),
- TALER_PQ_query_param_amount (&td->details.purse_deposits.amount_with_fee),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.purse_deposits.amount_with_fee),
GNUNET_PQ_query_param_auto_from_type (&td->details.purse_deposits.coin_sig),
GNUNET_PQ_query_param_end
};
@@ -1493,11 +1557,10 @@ irbt_cb_table_purse_deposits (struct PostgresClosure *pg,
",partner_serial_id"
",purse_pub"
",coin_pub"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
",coin_sig"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7);");
+ "($1, $2, $3, $4, $5, $6);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_purse_deposits",
params);
@@ -1562,6 +1625,7 @@ irbt_cb_table_history_requests (struct PostgresClosure *pg,
GNUNET_PQ_query_param_auto_from_type (
&td->details.history_requests.reserve_sig),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.history_requests.history_fee),
GNUNET_PQ_query_param_end
};
@@ -1573,10 +1637,9 @@ irbt_cb_table_history_requests (struct PostgresClosure *pg,
",reserve_pub"
",request_timestamp"
",reserve_sig"
- ",history_fee_val"
- ",history_fee_frac"
+ ",history_fee"
") VALUES "
- "($1, $2, $3, $4, $5, $6);");
+ "($1, $2, $3, $4, $5);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_history_requests",
params);
@@ -1602,8 +1665,10 @@ irbt_cb_table_close_requests (struct PostgresClosure *pg,
GNUNET_PQ_query_param_auto_from_type (
&td->details.close_requests.reserve_sig),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.close_requests.close),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.close_requests.close_fee),
GNUNET_PQ_query_param_string (
td->details.close_requests.payto_uri),
@@ -1617,13 +1682,11 @@ irbt_cb_table_close_requests (struct PostgresClosure *pg,
",reserve_pub"
",close_timestamp"
",reserve_sig"
- ",close_val"
- ",close_frac"
- ",close_fee_val"
- ",close_fee_frac"
+ ",close"
+ ",close_fee"
",payto_uri"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9);");
+ "($1, $2, $3, $4, $5, $6, $7);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_close_requests",
params);
@@ -1644,7 +1707,9 @@ irbt_cb_table_wads_out (struct PostgresClosure *pg,
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_auto_from_type (&td->details.wads_out.wad_id),
GNUNET_PQ_query_param_uint64 (&td->details.wads_out.partner_serial_id),
- TALER_PQ_query_param_amount (&td->details.wads_out.amount),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.wads_out.amount),
GNUNET_PQ_query_param_timestamp (&td->details.wads_out.execution_time),
GNUNET_PQ_query_param_end
};
@@ -1655,11 +1720,10 @@ irbt_cb_table_wads_out (struct PostgresClosure *pg,
"(wad_out_serial_id"
",wad_id"
",partner_serial_id"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",execution_time"
") VALUES "
- "($1, $2, $3, $4, $5, $6);");
+ "($1, $2, $3, $4, $5);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_wads_out",
params);
@@ -1691,10 +1755,13 @@ irbt_cb_table_wads_out_entries (struct PostgresClosure *pg,
GNUNET_PQ_query_param_timestamp (
&td->details.wads_out_entries.merge_timestamp),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.wads_out_entries.amount_with_fee),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.wads_out_entries.wad_fee),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.wads_out_entries.deposit_fees),
GNUNET_PQ_query_param_auto_from_type (
&td->details.wads_out_entries.reserve_sig),
@@ -1713,16 +1780,13 @@ irbt_cb_table_wads_out_entries (struct PostgresClosure *pg,
",h_contract"
",purse_expiration"
",merge_timestamp"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",wad_fee_val"
- ",wad_fee_frac"
- ",deposit_fees_val"
- ",deposit_fees_frac"
+ ",amount_with_fee"
+ ",wad_fee"
+ ",deposit_fees"
",reserve_sig"
",purse_sig"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15);");
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_wads_out_entries",
params);
@@ -1743,7 +1807,9 @@ irbt_cb_table_wads_in (struct PostgresClosure *pg,
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_auto_from_type (&td->details.wads_in.wad_id),
GNUNET_PQ_query_param_string (td->details.wads_in.origin_exchange_url),
- TALER_PQ_query_param_amount (&td->details.wads_in.amount),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.wads_in.amount),
GNUNET_PQ_query_param_timestamp (&td->details.wads_in.arrival_time),
GNUNET_PQ_query_param_end
};
@@ -1754,11 +1820,10 @@ irbt_cb_table_wads_in (struct PostgresClosure *pg,
"(wad_in_serial_id"
",wad_id"
",origin_exchange_url"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",arrival_time"
") VALUES "
- "($1, $2, $3, $4, $5, $6);");
+ "($1, $2, $3, $4, $5);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_wads_in",
params);
@@ -1788,10 +1853,13 @@ irbt_cb_table_wads_in_entries (struct PostgresClosure *pg,
GNUNET_PQ_query_param_timestamp (
&td->details.wads_in_entries.merge_timestamp),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.wads_in_entries.amount_with_fee),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.wads_in_entries.wad_fee),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.wads_in_entries.deposit_fees),
GNUNET_PQ_query_param_auto_from_type (
&td->details.wads_in_entries.reserve_sig),
@@ -1810,16 +1878,13 @@ irbt_cb_table_wads_in_entries (struct PostgresClosure *pg,
",h_contract"
",purse_expiration"
",merge_timestamp"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",wad_fee_val"
- ",wad_fee_frac"
- ",deposit_fees_val"
- ",deposit_fees_frac"
+ ",amount_with_fee"
+ ",wad_fee"
+ ",deposit_fees"
",reserve_sig"
",purse_sig"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15);");
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_wads_in_entries",
params);
@@ -1847,6 +1912,7 @@ irbt_cb_table_profit_drains (struct PostgresClosure *pg,
GNUNET_PQ_query_param_timestamp (
&td->details.profit_drains.trigger_date),
TALER_PQ_query_param_amount (
+ pg->conn,
&td->details.profit_drains.amount),
GNUNET_PQ_query_param_auto_from_type (
&td->details.profit_drains.master_sig),
@@ -1861,23 +1927,252 @@ irbt_cb_table_profit_drains (struct PostgresClosure *pg,
",account_section"
",payto_uri"
",trigger_date"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",master_sig"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8);");
+ "($1, $2, $3, $4, $5, $6, $7);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_profit_drains",
params);
}
+/**
+ * Function called with aml_staff records to insert into table.
+ *
+ * @param pg plugin context
+ * @param td record to insert
+ */
+static enum GNUNET_DB_QueryStatus
+irbt_cb_table_aml_staff (struct PostgresClosure *pg,
+ const struct TALER_EXCHANGEDB_TableData *td)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&td->serial),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.aml_staff.decider_pub),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.aml_staff.master_sig),
+ GNUNET_PQ_query_param_string (
+ td->details.aml_staff.decider_name),
+ GNUNET_PQ_query_param_bool (
+ td->details.aml_staff.is_active),
+ GNUNET_PQ_query_param_bool (
+ td->details.aml_staff.read_only),
+ GNUNET_PQ_query_param_timestamp (
+ &td->details.aml_staff.last_change),
+ GNUNET_PQ_query_param_end
+ };
+
+ PREPARE (pg,
+ "insert_into_table_aml_staff",
+ "INSERT INTO aml_staff"
+ "(aml_staff_uuid"
+ ",decider_pub"
+ ",master_sig"
+ ",decider_name"
+ ",is_active"
+ ",read_only"
+ ",last_change"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7);");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_into_table_aml_staff",
+ params);
+}
+
+
+/**
+ * Function called with aml_history records to insert into table.
+ *
+ * @param pg plugin context
+ * @param td record to insert
+ */
+static enum GNUNET_DB_QueryStatus
+irbt_cb_table_aml_history (struct PostgresClosure *pg,
+ const struct TALER_EXCHANGEDB_TableData *td)
+{
+ uint32_t status32 = td->details.aml_history.new_status;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&td->serial),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.aml_history.h_payto),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.aml_history.new_threshold),
+ GNUNET_PQ_query_param_uint32 (
+ &status32),
+ GNUNET_PQ_query_param_timestamp (
+ &td->details.aml_history.decision_time),
+ GNUNET_PQ_query_param_string (
+ td->details.aml_history.justification),
+ (NULL == td->details.aml_history.kyc_requirements)
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (
+ td->details.aml_history.kyc_requirements),
+ GNUNET_PQ_query_param_uint64 (
+ &td->details.aml_history.kyc_req_row),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.aml_history.decider_pub),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.aml_history.decider_sig),
+ GNUNET_PQ_query_param_end
+ };
+
+ PREPARE (pg,
+ "insert_into_table_aml_history",
+ "INSERT INTO aml_history"
+ "(aml_history_serial_id"
+ ",h_payto"
+ ",new_threshold"
+ ",new_status"
+ ",decision_time"
+ ",justification"
+ ",kyc_requirements"
+ ",kyc_req_row"
+ ",decider_pub"
+ ",decider_sig"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_into_table_aml_history",
+ params);
+}
+
+
+/**
+ * Function called with kyc_attributes records to insert into table.
+ *
+ * @param pg plugin context
+ * @param td record to insert
+ */
+static enum GNUNET_DB_QueryStatus
+irbt_cb_table_kyc_attributes (struct PostgresClosure *pg,
+ const struct TALER_EXCHANGEDB_TableData *td)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&td->serial),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.kyc_attributes.h_payto),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.kyc_attributes.kyc_prox),
+ GNUNET_PQ_query_param_string (
+ td->details.kyc_attributes.provider),
+ GNUNET_PQ_query_param_timestamp (
+ &td->details.kyc_attributes.collection_time),
+ GNUNET_PQ_query_param_timestamp (
+ &td->details.kyc_attributes.expiration_time),
+ GNUNET_PQ_query_param_fixed_size (
+ &td->details.kyc_attributes.encrypted_attributes,
+ td->details.kyc_attributes.encrypted_attributes_size),
+ GNUNET_PQ_query_param_end
+ };
+
+ PREPARE (pg,
+ "insert_into_table_kyc_attributes",
+ "INSERT INTO kyc_attributes"
+ "(kyc_attributes_serial_id"
+ ",h_payto"
+ ",kyc_prox"
+ ",provider"
+ ",collection_time"
+ ",expiration_time"
+ ",encrypted_attributes"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7);");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_into_table_kyc_attributes",
+ params);
+}
+
+
+/**
+ * Function called with purse_deletion records to insert into table.
+ *
+ * @param pg plugin context
+ * @param td record to insert
+ */
+static enum GNUNET_DB_QueryStatus
+irbt_cb_table_purse_deletion (struct PostgresClosure *pg,
+ const struct TALER_EXCHANGEDB_TableData *td)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&td->serial),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.purse_deletion.purse_pub),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.purse_deletion.purse_sig),
+ GNUNET_PQ_query_param_end
+ };
+
+ PREPARE (pg,
+ "insert_into_table_purse_deletion",
+ "INSERT INTO purse_deletion"
+ "(purse_deletion_serial_id"
+ ",purse_pub"
+ ",purse_sig"
+ ") VALUES "
+ "($1, $2, $3);");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_into_table_purse_deletion",
+ params);
+}
+
+
+/**
+ * Function called with age_withdraw records to insert into table.
+ *
+ * @param pg plugin context
+ * @param td record to insert
+ */
+static enum GNUNET_DB_QueryStatus
+irbt_cb_table_age_withdraw (struct PostgresClosure *pg,
+ const struct
+ TALER_EXCHANGEDB_TableData *td)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&td->serial),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.age_withdraw.h_commitment),
+ TALER_PQ_query_param_amount (
+ pg->conn,
+ &td->details.age_withdraw.amount_with_fee),
+ GNUNET_PQ_query_param_uint16 (
+ &td->details.age_withdraw.max_age),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.age_withdraw.reserve_pub),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.age_withdraw.reserve_sig),
+ GNUNET_PQ_query_param_uint32 (
+ &td->details.age_withdraw.noreveal_index),
+ /* TODO: other fields, too! */
+ GNUNET_PQ_query_param_end
+ };
+
+ PREPARE (pg,
+ "insert_into_table_age_withdraw",
+ "INSERT INTO age_withdraw"
+ "(age_withdraw_commitment_id"
+ ",h_commitment"
+ ",amount_with_fee"
+ ",max_age"
+ ",reserve_pub"
+ ",reserve_sig"
+ ",noreveal_index"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7, $8);");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_into_table_age_withdraw",
+ params);
+}
+
+
enum GNUNET_DB_QueryStatus
TEH_PG_insert_records_by_table (void *cls,
const struct TALER_EXCHANGEDB_TableData *td)
{
struct PostgresClosure *pg = cls;
- InsertRecordCallback rh;
+ InsertRecordCallback rh = NULL;
switch (td->table)
{
@@ -1938,8 +2233,11 @@ TEH_PG_insert_records_by_table (void *cls,
case TALER_EXCHANGEDB_RT_REFRESH_TRANSFER_KEYS:
rh = &irbt_cb_table_refresh_transfer_keys;
break;
- case TALER_EXCHANGEDB_RT_DEPOSITS:
- rh = &irbt_cb_table_deposits;
+ case TALER_EXCHANGEDB_RT_BATCH_DEPOSITS:
+ rh = &irbt_cb_table_batch_deposits;
+ break;
+ case TALER_EXCHANGEDB_RT_COIN_DEPOSITS:
+ rh = &irbt_cb_table_coin_deposits;
break;
case TALER_EXCHANGEDB_RT_REFUNDS:
rh = &irbt_cb_table_refunds;
@@ -2007,7 +2305,24 @@ TEH_PG_insert_records_by_table (void *cls,
case TALER_EXCHANGEDB_RT_PROFIT_DRAINS:
rh = &irbt_cb_table_profit_drains;
break;
- default:
+ case TALER_EXCHANGEDB_RT_AML_STAFF:
+ rh = &irbt_cb_table_aml_staff;
+ break;
+ case TALER_EXCHANGEDB_RT_AML_HISTORY:
+ rh = &irbt_cb_table_aml_history;
+ break;
+ case TALER_EXCHANGEDB_RT_KYC_ATTRIBUTES:
+ rh = &irbt_cb_table_kyc_attributes;
+ break;
+ case TALER_EXCHANGEDB_RT_PURSE_DELETION:
+ rh = &irbt_cb_table_purse_deletion;
+ break;
+ case TALER_EXCHANGEDB_RT_AGE_WITHDRAW:
+ rh = &irbt_cb_table_age_withdraw;
+ break;
+ }
+ if (NULL == rh)
+ {
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
@@ -2016,4 +2331,4 @@ TEH_PG_insert_records_by_table (void *cls,
}
-/* end of irbt_callbacks.c */
+/* end of pg_insert_records_by_table.c */
diff --git a/src/exchangedb/pg_insert_refresh_reveal.c b/src/exchangedb/pg_insert_refresh_reveal.c
index 098a3aed8..bddca472b 100644
--- a/src/exchangedb/pg_insert_refresh_reveal.c
+++ b/src/exchangedb/pg_insert_refresh_reveal.c
@@ -43,6 +43,22 @@ TEH_PG_insert_refresh_reveal (
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
+ PREPARE (pg,
+ "insert_refresh_revealed_coin",
+ "INSERT INTO refresh_revealed_coins "
+ "(melt_serial_id "
+ ",freshcoin_index "
+ ",link_sig "
+ ",denominations_serial "
+ ",coin_ev"
+ ",ewv"
+ ",h_coin_ev"
+ ",ev_sig"
+ ") SELECT $1, $2, $3, "
+ " denominations_serial, $5, $6, $7, $8"
+ " FROM denominations"
+ " WHERE denom_pub_hash=$4"
+ " ON CONFLICT DO NOTHING;");
for (uint32_t i = 0; i<num_rrcs; i++)
{
const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
@@ -76,17 +92,16 @@ TEH_PG_insert_refresh_reveal (
GNUNET_PQ_query_param_end
};
- /* Used in #postgres_insert_refresh_reveal() to store the transfer
- keys we learned */
+ /* Used in #postgres_insert_refresh_reveal() to store the transfer
+ keys we learned */
PREPARE (pg,
- "insert_refresh_transfer_keys",
- "INSERT INTO refresh_transfer_keys "
- "(melt_serial_id"
- ",transfer_pub"
- ",transfer_privs"
- ") VALUES ($1, $2, $3)"
- " ON CONFLICT DO NOTHING;");
-
+ "insert_refresh_transfer_keys",
+ "INSERT INTO refresh_transfer_keys "
+ "(melt_serial_id"
+ ",transfer_pub"
+ ",transfer_privs"
+ ") VALUES ($1, $2, $3)"
+ " ON CONFLICT DO NOTHING;");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_refresh_transfer_keys",
params);
diff --git a/src/exchangedb/pg_insert_refund.c b/src/exchangedb/pg_insert_refund.c
index 047df0334..e989c91bc 100644
--- a/src/exchangedb/pg_insert_refund.c
+++ b/src/exchangedb/pg_insert_refund.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -28,7 +28,7 @@
enum GNUNET_DB_QueryStatus
TEH_PG_insert_refund (void *cls,
- const struct TALER_EXCHANGEDB_Refund *refund)
+ const struct TALER_EXCHANGEDB_Refund *refund)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -37,26 +37,25 @@ TEH_PG_insert_refund (void *cls,
GNUNET_PQ_query_param_auto_from_type (&refund->details.merchant_sig),
GNUNET_PQ_query_param_auto_from_type (&refund->details.h_contract_terms),
GNUNET_PQ_query_param_uint64 (&refund->details.rtransaction_id),
- TALER_PQ_query_param_amount (&refund->details.refund_amount),
+ TALER_PQ_query_param_amount (pg->conn,
+ &refund->details.refund_amount),
GNUNET_PQ_query_param_end
};
GNUNET_assert (GNUNET_YES ==
TALER_amount_cmp_currency (&refund->details.refund_amount,
&refund->details.refund_fee));
-
- /* Used in #postgres_insert_refund() to store refund information */
PREPARE (pg,
"insert_refund",
"INSERT INTO refunds "
- "(coin_pub "
- ",deposit_serial_id"
- ",merchant_sig "
- ",rtransaction_id "
- ",amount_with_fee_val "
- ",amount_with_fee_frac "
- ") SELECT $1, deposit_serial_id, $3, $5, $6, $7"
- " FROM deposits"
+ "(coin_pub"
+ ",batch_deposit_serial_id"
+ ",merchant_sig"
+ ",rtransaction_id"
+ ",amount_with_fee"
+ ") SELECT $1, cdep.batch_deposit_serial_id, $3, $5, $6"
+ " FROM coin_deposits cdep"
+ " JOIN batch_deposits bdep USING (batch_deposit_serial_id)"
" WHERE coin_pub=$1"
" AND h_contract_terms=$4"
" AND merchant_pub=$2");
diff --git a/src/exchangedb/pg_insert_reserve_closed.c b/src/exchangedb/pg_insert_reserve_closed.c
index d17c37edc..6644fb892 100644
--- a/src/exchangedb/pg_insert_reserve_closed.c
+++ b/src/exchangedb/pg_insert_reserve_closed.c
@@ -51,26 +51,25 @@ TEH_PG_insert_reserve_closed (
GNUNET_PQ_query_param_timestamp (&execution_date),
GNUNET_PQ_query_param_auto_from_type (wtid),
GNUNET_PQ_query_param_auto_from_type (&h_payto),
- TALER_PQ_query_param_amount (amount_with_fee),
- TALER_PQ_query_param_amount (closing_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ amount_with_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ closing_fee),
GNUNET_PQ_query_param_uint64 (&close_request_row),
GNUNET_PQ_query_param_end
};
- /* Used in #postgres_insert_reserve_closed() */
PREPARE (pg,
- "reserves_close_insert",
- "INSERT INTO reserves_close "
- "(reserve_pub"
- ",execution_date"
- ",wtid"
- ",wire_target_h_payto"
- ",amount_val"
- ",amount_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
- ",close_request_row"
- ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9);");
+ "reserves_close_insert",
+ "INSERT INTO reserves_close "
+ "(reserve_pub"
+ ",execution_date"
+ ",wtid"
+ ",wire_target_h_payto"
+ ",amount"
+ ",closing_fee"
+ ",close_request_row"
+ ") VALUES ($1, $2, $3, $4, $5, $6, $7);");
qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
"reserves_close_insert",
@@ -83,7 +82,7 @@ TEH_PG_insert_reserve_closed (
reserve.pub = *reserve_pub;
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
(qs = TEH_PG_reserves_get (cls,
- &reserve)))
+ &reserve)))
{
/* Existence should have been checked before we got here... */
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
@@ -110,5 +109,5 @@ TEH_PG_insert_reserve_closed (
GNUNET_break (TALER_AAR_RESULT_ZERO == ret);
}
return TEH_PG_reserves_update (cls,
- &reserve);
+ &reserve);
}
diff --git a/src/exchangedb/pg_insert_reserve_open_deposit.c b/src/exchangedb/pg_insert_reserve_open_deposit.c
index 8bf70e7b2..f9cedcbe7 100644
--- a/src/exchangedb/pg_insert_reserve_open_deposit.c
+++ b/src/exchangedb/pg_insert_reserve_open_deposit.c
@@ -44,7 +44,8 @@ TEH_PG_insert_reserve_open_deposit (
GNUNET_PQ_query_param_auto_from_type (coin_sig),
GNUNET_PQ_query_param_auto_from_type (reserve_sig),
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- TALER_PQ_query_param_amount (coin_total),
+ TALER_PQ_query_param_amount (pg->conn,
+ coin_total),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@@ -58,7 +59,7 @@ TEH_PG_insert_reserve_open_deposit (
"SELECT "
" out_insufficient_funds"
" FROM exchange_do_reserve_open_deposit"
- " ($1,$2,$3,$4,$5,$6,$7);");
+ " ($1,$2,$3,$4,$5,$6);");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"insert_reserve_open_deposit",
params,
diff --git a/src/exchangedb/pg_insert_wire.c b/src/exchangedb/pg_insert_wire.c
index 646bce94e..b1364cbb3 100644
--- a/src/exchangedb/pg_insert_wire.c
+++ b/src/exchangedb/pg_insert_wire.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022, 2023, 2024 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
@@ -28,28 +28,46 @@
enum GNUNET_DB_QueryStatus
TEH_PG_insert_wire (void *cls,
- const char *payto_uri,
- struct GNUNET_TIME_Timestamp start_date,
- const struct TALER_MasterSignatureP *master_sig)
+ const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
+ struct GNUNET_TIME_Timestamp start_date,
+ const struct TALER_MasterSignatureP *master_sig,
+ const char *bank_label,
+ int64_t priority)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (payto_uri),
+ NULL == conversion_url
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (conversion_url),
+ TALER_PQ_query_param_json (debit_restrictions),
+ TALER_PQ_query_param_json (credit_restrictions),
GNUNET_PQ_query_param_auto_from_type (master_sig),
GNUNET_PQ_query_param_timestamp (&start_date),
+ NULL == bank_label
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (bank_label),
+ GNUNET_PQ_query_param_int64 (&priority),
GNUNET_PQ_query_param_end
};
- /* used in #postgres_insert_wire() */
PREPARE (pg,
"insert_wire",
"INSERT INTO wire_accounts "
"(payto_uri"
+ ",conversion_url"
+ ",debit_restrictions"
+ ",credit_restrictions"
",master_sig"
",is_active"
",last_change"
+ ",bank_label"
+ ",priority"
") VALUES "
- "($1, $2, true, $3);");
+ "($1, $2, $3, $4, $5, true, $6, $7, $8);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_wire",
params);
diff --git a/src/exchangedb/pg_insert_wire.h b/src/exchangedb/pg_insert_wire.h
index 15ce08674..7a5e4caca 100644
--- a/src/exchangedb/pg_insert_wire.h
+++ b/src/exchangedb/pg_insert_wire.h
@@ -29,16 +29,27 @@
*
* @param cls closure
* @param payto_uri wire account of the exchange
+ * @param conversion_url URL of a conversion service, NULL if there is no conversion
+ * @param debit_restrictions JSON array with debit restrictions on the account
+ * @param credit_restrictions JSON array with credit restrictions on the account
* @param start_date date when the account was added by the offline system
* (only to be used for replay detection)
* @param master_sig public signature affirming the existence of the account,
* must be of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS
+ * @param bank_label label to show this entry under in the UI, can be NULL
+ * @param priority determines order in which entries are shown in the UI
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
TEH_PG_insert_wire (void *cls,
- const char *payto_uri,
- struct GNUNET_TIME_Timestamp start_date,
- const struct TALER_MasterSignatureP *master_sig);
+ const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
+ struct GNUNET_TIME_Timestamp start_date,
+ const struct TALER_MasterSignatureP *master_sig,
+ const char *bank_label,
+ int64_t priority);
+
#endif
diff --git a/src/exchangedb/pg_insert_wire_fee.c b/src/exchangedb/pg_insert_wire_fee.c
index 278ec2bcb..af818bcca 100644
--- a/src/exchangedb/pg_insert_wire_fee.c
+++ b/src/exchangedb/pg_insert_wire_fee.c
@@ -26,21 +26,24 @@
#include "pg_helper.h"
#include "pg_get_wire_fee.h"
+
enum GNUNET_DB_QueryStatus
TEH_PG_insert_wire_fee (void *cls,
- const char *type,
- struct GNUNET_TIME_Timestamp start_date,
- struct GNUNET_TIME_Timestamp end_date,
- const struct TALER_WireFeeSet *fees,
- const struct TALER_MasterSignatureP *master_sig)
+ const char *type,
+ struct GNUNET_TIME_Timestamp start_date,
+ struct GNUNET_TIME_Timestamp end_date,
+ const struct TALER_WireFeeSet *fees,
+ const struct TALER_MasterSignatureP *master_sig)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (type),
GNUNET_PQ_query_param_timestamp (&start_date),
GNUNET_PQ_query_param_timestamp (&end_date),
- TALER_PQ_query_param_amount (&fees->wire),
- TALER_PQ_query_param_amount (&fees->closing),
+ TALER_PQ_query_param_amount (pg->conn,
+ &fees->wire),
+ TALER_PQ_query_param_amount (pg->conn,
+ &fees->closing),
GNUNET_PQ_query_param_auto_from_type (master_sig),
GNUNET_PQ_query_param_end
};
@@ -51,12 +54,12 @@ TEH_PG_insert_wire_fee (void *cls,
enum GNUNET_DB_QueryStatus qs;
qs = TEH_PG_get_wire_fee (pg,
- type,
- start_date,
- &sd,
- &ed,
- &wx,
- &sig);
+ type,
+ start_date,
+ &sd,
+ &ed,
+ &wx,
+ &sig);
if (qs < 0)
return qs;
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
@@ -88,20 +91,17 @@ TEH_PG_insert_wire_fee (void *cls,
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
- /* Used in #postgres_insert_wire_fee */
PREPARE (pg,
"insert_wire_fee",
"INSERT INTO wire_fee "
"(wire_method"
",start_date"
",end_date"
- ",wire_fee_val"
- ",wire_fee_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
+ ",wire_fee"
+ ",closing_fee"
",master_sig"
") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8);");
+ "($1, $2, $3, $4, $5, $6);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_wire_fee",
params);
diff --git a/src/exchangedb/pg_insert_wire_fee.h b/src/exchangedb/pg_insert_wire_fee.h
index e53faf5a5..15c1a39f8 100644
--- a/src/exchangedb/pg_insert_wire_fee.h
+++ b/src/exchangedb/pg_insert_wire_fee.h
@@ -38,9 +38,9 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_insert_wire_fee (void *cls,
- const char *type,
- struct GNUNET_TIME_Timestamp start_date,
- struct GNUNET_TIME_Timestamp end_date,
- const struct TALER_WireFeeSet *fees,
+ const char *type,
+ struct GNUNET_TIME_Timestamp start_date,
+ struct GNUNET_TIME_Timestamp end_date,
+ const struct TALER_WireFeeSet *fees,
const struct TALER_MasterSignatureP *master_sig);
#endif
diff --git a/src/exchangedb/pg_iterate_active_auditors.h b/src/exchangedb/pg_iterate_active_auditors.h
index 1247d2d3d..f0e2808e9 100644
--- a/src/exchangedb/pg_iterate_active_auditors.h
+++ b/src/exchangedb/pg_iterate_active_auditors.h
@@ -36,6 +36,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_iterate_active_auditors (void *cls,
- TALER_EXCHANGEDB_AuditorsCallback cb,
+ TALER_EXCHANGEDB_AuditorsCallback cb,
void *cb_cls);
+
#endif
diff --git a/src/exchangedb/pg_iterate_active_signkeys.h b/src/exchangedb/pg_iterate_active_signkeys.h
index b99dfa8df..5ebba9f5a 100644
--- a/src/exchangedb/pg_iterate_active_signkeys.h
+++ b/src/exchangedb/pg_iterate_active_signkeys.h
@@ -37,7 +37,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_iterate_active_signkeys (void *cls,
- TALER_EXCHANGEDB_ActiveSignkeysCallback cb,
+ TALER_EXCHANGEDB_ActiveSignkeysCallback cb,
void *cb_cls);
#endif
diff --git a/src/exchangedb/pg_iterate_auditor_denominations.h b/src/exchangedb/pg_iterate_auditor_denominations.h
index da1f36701..1278e8a9f 100644
--- a/src/exchangedb/pg_iterate_auditor_denominations.h
+++ b/src/exchangedb/pg_iterate_auditor_denominations.h
@@ -41,4 +41,5 @@ TEH_PG_iterate_auditor_denominations (
void *cls,
TALER_EXCHANGEDB_AuditorDenominationsCallback cb,
void *cb_cls);
+
#endif
diff --git a/src/exchangedb/pg_iterate_denomination_info.c b/src/exchangedb/pg_iterate_denomination_info.c
index ba7026ebc..cab51d5ce 100644
--- a/src/exchangedb/pg_iterate_denomination_info.c
+++ b/src/exchangedb/pg_iterate_denomination_info.c
@@ -155,7 +155,6 @@ TEH_PG_iterate_denomination_info (void *cls,
.pg = pg
};
- /* Used in #postgres_iterate_denomination_info() */
PREPARE (pg,
"denomination_iterate",
"SELECT"
@@ -165,16 +164,11 @@ TEH_PG_iterate_denomination_info (void *cls,
",expire_withdraw"
",expire_deposit"
",expire_legal"
- ",coin_val" /* value of this denom */
- ",coin_frac" /* fractional value of this denom */
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refund_val"
- ",fee_refund_frac"
+ ",coin" /* value of this denom */
+ ",fee_withdraw"
+ ",fee_deposit"
+ ",fee_refresh"
+ ",fee_refund"
",denom_pub"
",age_mask"
" FROM denominations;");
diff --git a/src/exchangedb/pg_iterate_denomination_info.h b/src/exchangedb/pg_iterate_denomination_info.h
index 57847a515..27c08d0a9 100644
--- a/src/exchangedb/pg_iterate_denomination_info.h
+++ b/src/exchangedb/pg_iterate_denomination_info.h
@@ -35,7 +35,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_iterate_denomination_info (void *cls,
- TALER_EXCHANGEDB_DenominationCallback cb,
+ TALER_EXCHANGEDB_DenominationCallback cb,
void *cb_cls);
#endif
diff --git a/src/exchangedb/pg_iterate_denominations.c b/src/exchangedb/pg_iterate_denominations.c
index a38257689..684aa165a 100644
--- a/src/exchangedb/pg_iterate_denominations.c
+++ b/src/exchangedb/pg_iterate_denominations.c
@@ -72,6 +72,8 @@ dominations_cb_helper (void *cls,
struct TALER_DenominationHashP h_denom_pub = {0};
bool revoked;
struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("denominations_serial",
+ &meta.serial),
GNUNET_PQ_result_spec_auto_from_type ("master_sig",
&master_sig),
GNUNET_PQ_result_spec_bool ("revoked",
@@ -141,34 +143,27 @@ TEH_PG_iterate_denominations (void *cls,
.pg = pg
};
- /* Used in #postgres_iterate_denominations() */
PREPARE (pg,
"select_denominations",
"SELECT"
- " denominations.master_sig"
+ " denominations_serial"
+ ",denominations.master_sig"
",denom_revocations_serial_id IS NOT NULL AS revoked"
",valid_from"
",expire_withdraw"
",expire_deposit"
",expire_legal"
- ",coin_val" /* value of this denom */
- ",coin_frac" /* fractional value of this denom */
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refund_val"
- ",fee_refund_frac"
+ ",coin" /* value of this denom */
+ ",fee_withdraw"
+ ",fee_deposit"
+ ",fee_refresh"
+ ",fee_refund"
",denom_type"
",age_mask"
",denom_pub"
" FROM denominations"
" LEFT JOIN "
" denomination_revocations USING (denominations_serial);");
-
-
return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"select_denominations",
params,
diff --git a/src/exchangedb/pg_iterate_denominations.h b/src/exchangedb/pg_iterate_denominations.h
index a205fc6ba..9f59fc803 100644
--- a/src/exchangedb/pg_iterate_denominations.h
+++ b/src/exchangedb/pg_iterate_denominations.h
@@ -38,7 +38,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_iterate_denominations (void *cls,
- TALER_EXCHANGEDB_DenominationsCallback cb,
+ TALER_EXCHANGEDB_DenominationsCallback cb,
void *cb_cls);
#endif
diff --git a/src/exchangedb/pg_iterate_reserve_close_info.c b/src/exchangedb/pg_iterate_reserve_close_info.c
index f1b2d452b..ff0a813c3 100644
--- a/src/exchangedb/pg_iterate_reserve_close_info.c
+++ b/src/exchangedb/pg_iterate_reserve_close_info.c
@@ -113,8 +113,7 @@ TEH_PG_iterate_reserve_close_info (
PREPARE (pg,
"iterate_reserve_close_info",
"SELECT"
- " amount_val"
- ",amount_frac"
+ " amount"
",execution_date"
" FROM reserves_close"
" WHERE wire_target_h_payto=$1"
diff --git a/src/exchangedb/pg_kyc_provider_account_lookup.c b/src/exchangedb/pg_kyc_provider_account_lookup.c
index 32cd65f7d..f9db2cbc1 100644
--- a/src/exchangedb/pg_kyc_provider_account_lookup.c
+++ b/src/exchangedb/pg_kyc_provider_account_lookup.c
@@ -26,7 +26,6 @@
#include "pg_helper.h"
-
enum GNUNET_DB_QueryStatus
TEH_PG_kyc_provider_account_lookup (
void *cls,
@@ -37,8 +36,8 @@ TEH_PG_kyc_provider_account_lookup (
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (provider_section),
GNUNET_PQ_query_param_string (provider_legitimization_id),
+ GNUNET_PQ_query_param_string (provider_section),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@@ -48,7 +47,7 @@ TEH_PG_kyc_provider_account_lookup (
process_row),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_kyc_provider_account_lookup() */
+
PREPARE (pg,
"get_wire_target_by_legitimization_id",
"SELECT "
diff --git a/src/exchangedb/pg_kyc_provider_account_lookup.h b/src/exchangedb/pg_kyc_provider_account_lookup.h
index 41bcb86ae..74f90d88d 100644
--- a/src/exchangedb/pg_kyc_provider_account_lookup.h
+++ b/src/exchangedb/pg_kyc_provider_account_lookup.h
@@ -44,4 +44,5 @@ TEH_PG_kyc_provider_account_lookup (
const char *provider_legitimization_id,
struct TALER_PaytoHashP *h_payto,
uint64_t *process_row);
+
#endif
diff --git a/src/exchangedb/pg_lookup_aml_officer.c b/src/exchangedb/pg_lookup_aml_officer.c
new file mode 100644
index 000000000..c18e47eaf
--- /dev/null
+++ b/src/exchangedb/pg_lookup_aml_officer.c
@@ -0,0 +1,71 @@
+/*
+ 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 exchangedb/pg_lookup_aml_officer.c
+ * @brief Implementation of the lookup_aml_officer function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_lookup_aml_officer.h"
+#include "pg_helper.h"
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_lookup_aml_officer (
+ void *cls,
+ const struct TALER_AmlOfficerPublicKeyP *decider_pub,
+ struct TALER_MasterSignatureP *master_sig,
+ char **decider_name,
+ bool *is_active,
+ bool *read_only,
+ struct GNUNET_TIME_Absolute *last_change)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (decider_pub),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("master_sig",
+ master_sig),
+ GNUNET_PQ_result_spec_string ("decider_name",
+ decider_name),
+ GNUNET_PQ_result_spec_bool ("is_active",
+ is_active),
+ GNUNET_PQ_result_spec_bool ("read_only",
+ read_only),
+ GNUNET_PQ_result_spec_absolute_time ("last_change",
+ last_change),
+ GNUNET_PQ_result_spec_end
+ };
+
+ PREPARE (pg,
+ "lookup_aml_officer",
+ "SELECT "
+ " master_sig"
+ ",decider_name"
+ ",is_active"
+ ",read_only"
+ ",last_change"
+ " FROM aml_staff"
+ " WHERE decider_pub=$1;");
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "lookup_aml_officer",
+ params,
+ rs);
+}
diff --git a/src/exchangedb/pg_lookup_aml_officer.h b/src/exchangedb/pg_lookup_aml_officer.h
new file mode 100644
index 000000000..161d2e7e7
--- /dev/null
+++ b/src/exchangedb/pg_lookup_aml_officer.h
@@ -0,0 +1,51 @@
+/*
+ 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 exchangedb/pg_lookup_aml_officer.h
+ * @brief implementation of the lookup_aml_officer function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_LOOKUP_AML_OFFICER_H
+#define PG_LOOKUP_AML_OFFICER_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Fetch AML staff record.
+ *
+ * @param cls closure
+ * @param decider_pub public key of the staff member
+ * @param[out] master_sig offline signature affirming the AML officer
+ * @param[out] decider_name full name of the staff member
+ * @param[out] is_active true to enable, false to set as inactive
+ * @param[out] read_only true to set read-only access
+ * @param[out] last_change when was the change made effective
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_lookup_aml_officer (
+ void *cls,
+ const struct TALER_AmlOfficerPublicKeyP *decider_pub,
+ struct TALER_MasterSignatureP *master_sig,
+ char **decider_name,
+ bool *is_active,
+ bool *read_only,
+ struct GNUNET_TIME_Absolute *last_change);
+
+#endif
diff --git a/src/exchangedb/pg_lookup_auditor_status.c b/src/exchangedb/pg_lookup_auditor_status.c
index 5e62bfa9e..91afe6eaa 100644
--- a/src/exchangedb/pg_lookup_auditor_status.c
+++ b/src/exchangedb/pg_lookup_auditor_status.c
@@ -46,7 +46,7 @@ TEH_PG_lookup_auditor_status (
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_lookup_auditor_status() */
+ /* Used in #postgres_lookup_auditor_status() */
PREPARE (pg,
"lookup_auditor_status",
"SELECT"
diff --git a/src/exchangedb/pg_lookup_auditor_timestamp.c b/src/exchangedb/pg_lookup_auditor_timestamp.c
index 3a4bd6bed..eb85876fe 100644
--- a/src/exchangedb/pg_lookup_auditor_timestamp.c
+++ b/src/exchangedb/pg_lookup_auditor_timestamp.c
@@ -43,7 +43,7 @@ TEH_PG_lookup_auditor_timestamp (
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_lookup_auditor_timestamp() */
+ /* Used in #postgres_lookup_auditor_timestamp() */
PREPARE (pg,
"lookup_auditor_timestamp",
"SELECT"
diff --git a/src/exchangedb/pg_lookup_denomination_key.c b/src/exchangedb/pg_lookup_denomination_key.c
index 36ada96e4..a358528ad 100644
--- a/src/exchangedb/pg_lookup_denomination_key.c
+++ b/src/exchangedb/pg_lookup_denomination_key.c
@@ -60,7 +60,6 @@ TEH_PG_lookup_denomination_key (
GNUNET_PQ_result_spec_end
};
- /* used in #postgres_lookup_denomination_key() */
PREPARE (pg,
"lookup_denomination_key",
"SELECT"
@@ -68,16 +67,11 @@ TEH_PG_lookup_denomination_key (
",expire_withdraw"
",expire_deposit"
",expire_legal"
- ",coin_val"
- ",coin_frac"
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refund_val"
- ",fee_refund_frac"
+ ",coin"
+ ",fee_withdraw"
+ ",fee_deposit"
+ ",fee_refresh"
+ ",fee_refund"
",age_mask"
" FROM denominations"
" WHERE denom_pub_hash=$1;");
diff --git a/src/exchangedb/pg_lookup_global_fee_by_time.c b/src/exchangedb/pg_lookup_global_fee_by_time.c
index 0119c2cd1..c3a6ec8b7 100644
--- a/src/exchangedb/pg_lookup_global_fee_by_time.c
+++ b/src/exchangedb/pg_lookup_global_fee_by_time.c
@@ -165,12 +165,9 @@ TEH_PG_lookup_global_fee_by_time (
PREPARE (pg,
"lookup_global_fee_by_time",
"SELECT"
- " history_fee_val"
- ",history_fee_frac"
- ",account_fee_val"
- ",account_fee_frac"
- ",purse_fee_val"
- ",purse_fee_frac"
+ " history_fee"
+ ",account_fee"
+ ",purse_fee"
",purse_timeout"
",history_expiration"
",purse_account_limit"
diff --git a/src/exchangedb/pg_lookup_global_fee_by_time.h b/src/exchangedb/pg_lookup_global_fee_by_time.h
index 9ac7d7dcd..c5ff95fc6 100644
--- a/src/exchangedb/pg_lookup_global_fee_by_time.h
+++ b/src/exchangedb/pg_lookup_global_fee_by_time.h
@@ -48,4 +48,5 @@ TEH_PG_lookup_global_fee_by_time (
struct GNUNET_TIME_Relative *purse_timeout,
struct GNUNET_TIME_Relative *history_expiration,
uint32_t *purse_account_limit);
+
#endif
diff --git a/src/exchangedb/pg_lookup_kyc_process_by_account.c b/src/exchangedb/pg_lookup_kyc_process_by_account.c
index 6183ae7af..b077661c5 100644
--- a/src/exchangedb/pg_lookup_kyc_process_by_account.c
+++ b/src/exchangedb/pg_lookup_kyc_process_by_account.c
@@ -25,6 +25,7 @@
#include "pg_lookup_kyc_process_by_account.h"
#include "pg_helper.h"
+
enum GNUNET_DB_QueryStatus
TEH_PG_lookup_kyc_process_by_account (
void *cls,
@@ -59,7 +60,6 @@ TEH_PG_lookup_kyc_process_by_account (
*provider_account_id = NULL;
*provider_legitimization_id = NULL;
- /* Used in #postgres_lookup_kyc_process_by_account() */
PREPARE (pg,
"lookup_process_by_account",
"SELECT "
@@ -69,7 +69,12 @@ TEH_PG_lookup_kyc_process_by_account (
",provider_legitimization_id"
" FROM legitimization_processes"
" WHERE h_payto=$1"
- " AND provider_section=$2;");
+ " AND provider_section=$2"
+ " AND NOT finished"
+ /* Note: there *should* only be one unfinished
+ match, so this is just to be safe(r): */
+ " ORDER BY expiration_time DESC"
+ " LIMIT 1;");
return GNUNET_PQ_eval_prepared_singleton_select (
pg->conn,
"lookup_process_by_account",
diff --git a/src/exchangedb/pg_lookup_kyc_process_by_account.h b/src/exchangedb/pg_lookup_kyc_process_by_account.h
index 40af6a7f6..0300b498c 100644
--- a/src/exchangedb/pg_lookup_kyc_process_by_account.h
+++ b/src/exchangedb/pg_lookup_kyc_process_by_account.h
@@ -47,4 +47,5 @@ TEH_PG_lookup_kyc_process_by_account (
struct GNUNET_TIME_Absolute *expiration,
char **provider_account_id,
char **provider_legitimization_id);
+
#endif
diff --git a/src/exchangedb/pg_lookup_kyc_requirement_by_row.c b/src/exchangedb/pg_lookup_kyc_requirement_by_row.c
index 6542aa28f..6f9d76786 100644
--- a/src/exchangedb/pg_lookup_kyc_requirement_by_row.c
+++ b/src/exchangedb/pg_lookup_kyc_requirement_by_row.c
@@ -30,9 +30,11 @@ TEH_PG_lookup_kyc_requirement_by_row (
void *cls,
uint64_t requirement_row,
char **requirements,
+ enum TALER_AmlDecisionState *aml_status,
struct TALER_PaytoHashP *h_payto)
{
struct PostgresClosure *pg = cls;
+ uint32_t status = TALER_AML_NORMAL;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&requirement_row),
GNUNET_PQ_query_param_end
@@ -42,19 +44,28 @@ TEH_PG_lookup_kyc_requirement_by_row (
requirements),
GNUNET_PQ_result_spec_auto_from_type ("h_payto",
h_payto),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_uint32 ("status",
+ &status),
+ NULL),
GNUNET_PQ_result_spec_end
};
-/* Used in #postgres_lookup_kyc_requirement_by_row() */
+ enum GNUNET_DB_QueryStatus qs;
+
PREPARE (pg,
"lookup_legitimization_requirement_by_row",
"SELECT "
- " required_checks"
- ",h_payto"
- " FROM legitimization_requirements"
+ " lr.required_checks"
+ ",lr.h_payto"
+ ",aml.status"
+ " FROM legitimization_requirements lr"
+ " LEFT JOIN aml_status aml USING (h_payto)"
" WHERE legitimization_requirement_serial_id=$1;");
- return GNUNET_PQ_eval_prepared_singleton_select (
+ qs = GNUNET_PQ_eval_prepared_singleton_select (
pg->conn,
"lookup_legitimization_requirement_by_row",
params,
rs);
+ *aml_status = (enum TALER_AmlDecisionState) status;
+ return qs;
}
diff --git a/src/exchangedb/pg_lookup_kyc_requirement_by_row.h b/src/exchangedb/pg_lookup_kyc_requirement_by_row.h
index 12d726187..3d223c985 100644
--- a/src/exchangedb/pg_lookup_kyc_requirement_by_row.h
+++ b/src/exchangedb/pg_lookup_kyc_requirement_by_row.h
@@ -32,6 +32,7 @@
* @param cls closure
* @param requirement_row identifies requirement to look up
* @param[out] requirements provider that must be checked
+ * @param[out] aml_status set to the AML status of the account
* @param[out] h_payto account that must be KYC'ed
* @return database transaction status
*/
@@ -40,5 +41,7 @@ TEH_PG_lookup_kyc_requirement_by_row (
void *cls,
uint64_t requirement_row,
char **requirements,
+ enum TALER_AmlDecisionState *aml_status,
struct TALER_PaytoHashP *h_payto);
+
#endif
diff --git a/src/exchangedb/pg_lookup_records_by_table.c b/src/exchangedb/pg_lookup_records_by_table.c
index 806896e78..fc4af32a8 100644
--- a/src/exchangedb/pg_lookup_records_by_table.c
+++ b/src/exchangedb/pg_lookup_records_by_table.c
@@ -1,6 +1,6 @@
/*
This file is part of GNUnet
- Copyright (C) 2020, 2021, 2022 Taler Systems SA
+ Copyright (C) 2020-2023 Taler Systems SA
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
@@ -21,6 +21,7 @@
* @file exchangedb/pg_lookup_records_by_table.c
* @brief implementation of lookup_records_by_table
* @author Christian Grothoff
+ * @author Özgür Kesim
*/
#include "platform.h"
#include "taler_error_codes.h"
@@ -28,6 +29,7 @@
#include "taler_pq_lib.h"
#include "pg_lookup_records_by_table.h"
#include "pg_helper.h"
+#include <gnunet/gnunet_pq_lib.h>
/**
@@ -232,6 +234,114 @@ lrbt_cb_table_wire_targets (void *cls,
/**
+ * Function called with legitimization_processes table entries.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lrbt_cb_table_legitimization_processes (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LookupRecordsByTableContext *ctx = cls;
+ struct TALER_EXCHANGEDB_TableData td = {
+ .table = TALER_EXCHANGEDB_RT_LEGITIMIZATION_PROCESSES
+ };
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("serial",
+ &td.serial),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "h_payto",
+ &td.details.legitimization_processes.h_payto),
+ GNUNET_PQ_result_spec_timestamp (
+ "expiration_time",
+ &td.details.legitimization_processes.expiration_time),
+ GNUNET_PQ_result_spec_string (
+ "provider_section",
+ &td.details.legitimization_processes.provider_section),
+ GNUNET_PQ_result_spec_string (
+ "provider_user_id",
+ &td.details.legitimization_processes.provider_user_id),
+ GNUNET_PQ_result_spec_string (
+ "provider_legitimization_id",
+ &td.details.legitimization_processes.provider_legitimization_id),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->error = true;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ &td);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+/**
+ * Function called with legitimization_requirements table entries.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lrbt_cb_table_legitimization_requirements (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LookupRecordsByTableContext *ctx = cls;
+ struct TALER_EXCHANGEDB_TableData td = {
+ .table = TALER_EXCHANGEDB_RT_LEGITIMIZATION_REQUIREMENTS
+ };
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("serial",
+ &td.serial),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "h_payto",
+ &td.details.legitimization_requirements.h_payto),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type (
+ "reserve_pub",
+ &td.details.legitimization_requirements.reserve_pub),
+ &td.details.legitimization_requirements.no_reserve_pub),
+ GNUNET_PQ_result_spec_string (
+ "required_checks",
+ &td.details.legitimization_requirements.required_checks),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->error = true;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ &td);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+/**
* Function called with reserves table entries.
*
* @param cls closure
@@ -401,6 +511,123 @@ lrbt_cb_table_reserves_close (void *cls,
/**
+ * Function called with reserves_open_requests table entries.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lrbt_cb_table_reserves_open_requests (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LookupRecordsByTableContext *ctx = cls;
+ struct PostgresClosure *pg = ctx->pg;
+ struct TALER_EXCHANGEDB_TableData td = {
+ .table = TALER_EXCHANGEDB_RT_RESERVES_OPEN_REQUESTS
+ };
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("serial",
+ &td.serial),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "reserve_pub",
+ &td.details.reserves_open_requests.reserve_pub),
+ GNUNET_PQ_result_spec_timestamp (
+ "request_timestamp",
+ &td.details.reserves_open_requests.request_timestamp),
+ GNUNET_PQ_result_spec_timestamp (
+ "expiration_date",
+ &td.details.reserves_open_requests.expiration_date),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "reserve_sig",
+ &td.details.reserves_open_requests.reserve_sig),
+ TALER_PQ_RESULT_SPEC_AMOUNT (
+ "reserve_payment",
+ &td.details.reserves_open_requests.reserve_payment),
+ GNUNET_PQ_result_spec_uint32 (
+ "requested_purse_limit",
+ &td.details.reserves_open_requests.requested_purse_limit),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->error = true;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ &td);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+/**
+ * Function called with reserves_open_deposits table entries.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lrbt_cb_table_reserves_open_deposits (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LookupRecordsByTableContext *ctx = cls;
+ struct PostgresClosure *pg = ctx->pg;
+ struct TALER_EXCHANGEDB_TableData td = {
+ .table = TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS
+ };
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("serial",
+ &td.serial),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "reserve_sig",
+ &td.details.reserves_open_deposits.reserve_sig),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "reserve_pub",
+ &td.details.reserves_open_deposits.reserve_pub),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "coin_pub",
+ &td.details.reserves_open_deposits.coin_pub),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "coin_sig",
+ &td.details.reserves_open_deposits.coin_sig),
+ TALER_PQ_RESULT_SPEC_AMOUNT (
+ "contribution",
+ &td.details.reserves_open_deposits.contribution),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->error = true;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ &td);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+/**
* Function called with reserves_out table entries.
*
* @param cls closure
@@ -897,9 +1124,9 @@ lrbt_cb_table_refresh_transfer_keys (void *cls,
ctx->error = true;
return;
}
- memcpy (&td.details.refresh_transfer_keys.tprivs[0],
- tpriv,
- tpriv_size);
+ GNUNET_memcpy (&td.details.refresh_transfer_keys.tprivs[0],
+ tpriv,
+ tpriv_size);
ctx->cb (ctx->cb_cls,
&td);
GNUNET_PQ_cleanup_result (rs);
@@ -908,77 +1135,124 @@ lrbt_cb_table_refresh_transfer_keys (void *cls,
/**
- * Function called with deposits table entries.
+ * Function called with batch deposits table entries.
*
* @param cls closure
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
-lrbt_cb_table_deposits (void *cls,
- PGresult *result,
- unsigned int num_results)
+lrbt_cb_table_batch_deposits (void *cls,
+ PGresult *result,
+ unsigned int num_results)
{
struct LookupRecordsByTableContext *ctx = cls;
- struct PostgresClosure *pg = ctx->pg;
struct TALER_EXCHANGEDB_TableData td = {
- .table = TALER_EXCHANGEDB_RT_DEPOSITS
+ .table = TALER_EXCHANGEDB_RT_BATCH_DEPOSITS
};
for (unsigned int i = 0; i<num_results; i++)
{
- bool no_policy;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 (
"serial",
&td.serial),
GNUNET_PQ_result_spec_uint64 (
"shard",
- &td.details.deposits.shard),
- GNUNET_PQ_result_spec_uint64 (
- "known_coin_id",
- &td.details.deposits.known_coin_id),
+ &td.details.batch_deposits.shard),
GNUNET_PQ_result_spec_auto_from_type (
- "coin_pub",
- &td.details.deposits.coin_pub),
- TALER_PQ_RESULT_SPEC_AMOUNT (
- "amount_with_fee",
- &td.details.deposits.amount_with_fee),
+ "merchant_pub",
+ &td.details.batch_deposits.merchant_pub),
GNUNET_PQ_result_spec_timestamp (
"wallet_timestamp",
- &td.details.deposits.wallet_timestamp),
+ &td.details.batch_deposits.wallet_timestamp),
GNUNET_PQ_result_spec_timestamp (
"exchange_timestamp",
- &td.details.deposits.exchange_timestamp),
+ &td.details.batch_deposits.exchange_timestamp),
GNUNET_PQ_result_spec_timestamp (
"refund_deadline",
- &td.details.deposits.refund_deadline),
+ &td.details.batch_deposits.refund_deadline),
GNUNET_PQ_result_spec_timestamp (
"wire_deadline",
- &td.details.deposits.wire_deadline),
- GNUNET_PQ_result_spec_auto_from_type (
- "merchant_pub",
- &td.details.deposits.merchant_pub),
+ &td.details.batch_deposits.wire_deadline),
GNUNET_PQ_result_spec_auto_from_type (
"h_contract_terms",
- &td.details.deposits.h_contract_terms),
- GNUNET_PQ_result_spec_auto_from_type (
- "coin_sig",
- &td.details.deposits.coin_sig),
+ &td.details.batch_deposits.h_contract_terms),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type (
+ "wallet_data_hash",
+ &td.details.batch_deposits.wallet_data_hash),
+ &td.details.batch_deposits.no_wallet_data_hash),
GNUNET_PQ_result_spec_auto_from_type (
"wire_salt",
- &td.details.deposits.wire_salt),
+ &td.details.batch_deposits.wire_salt),
GNUNET_PQ_result_spec_auto_from_type (
"wire_target_h_payto",
- &td.details.deposits.wire_target_h_payto),
+ &td.details.batch_deposits.wire_target_h_payto),
GNUNET_PQ_result_spec_auto_from_type (
"policy_blocked",
- &td.details.deposits.policy_blocked),
+ &td.details.batch_deposits.policy_blocked),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_uint64 (
"policy_details_serial_id",
- &td.details.deposits.policy_details_serial_id),
- &no_policy),
+ &td.details.batch_deposits.policy_details_serial_id),
+ &td.details.batch_deposits.no_policy_details),
+ GNUNET_PQ_result_spec_end
+ };
+
+ td.details.batch_deposits.policy_details_serial_id = 0;
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->error = true;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ &td);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+/**
+ * Function called with coin deposits table entries.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lrbt_cb_table_coin_deposits (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LookupRecordsByTableContext *ctx = cls;
+ struct PostgresClosure *pg = ctx->pg;
+ struct TALER_EXCHANGEDB_TableData td = {
+ .table = TALER_EXCHANGEDB_RT_COIN_DEPOSITS
+ };
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 (
+ "serial",
+ &td.serial),
+ GNUNET_PQ_result_spec_uint64 (
+ "batch_deposit_serial_id",
+ &td.details.coin_deposits.batch_deposit_serial_id),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "coin_pub",
+ &td.details.coin_deposits.coin_pub),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "coin_sig",
+ &td.details.coin_deposits.coin_sig),
+ TALER_PQ_RESULT_SPEC_AMOUNT (
+ "amount_with_fee",
+ &td.details.coin_deposits.amount_with_fee),
GNUNET_PQ_result_spec_end
};
@@ -1035,8 +1309,8 @@ lrbt_cb_table_refunds (void *cls,
"amount_with_fee",
&td.details.refunds.amount_with_fee),
GNUNET_PQ_result_spec_uint64 (
- "deposit_serial_id",
- &td.details.refunds.deposit_serial_id),
+ "batch_deposit_serial_id",
+ &td.details.refunds.batch_deposit_serial_id),
GNUNET_PQ_result_spec_end
};
@@ -1137,8 +1411,8 @@ lrbt_cb_table_aggregation_tracking (void *cls,
"serial",
&td.serial),
GNUNET_PQ_result_spec_uint64 (
- "deposit_serial_id",
- &td.details.aggregation_tracking.deposit_serial_id),
+ "batch_deposit_serial_id",
+ &td.details.aggregation_tracking.batch_deposit_serial_id),
GNUNET_PQ_result_spec_auto_from_type (
"wtid_raw",
&td.details.aggregation_tracking.wtid_raw),
@@ -2296,6 +2570,313 @@ lrbt_cb_table_profit_drains (void *cls,
/**
+ * Function called with aml_staff table entries.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lrbt_cb_table_aml_staff (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LookupRecordsByTableContext *ctx = cls;
+ struct TALER_EXCHANGEDB_TableData td = {
+ .table = TALER_EXCHANGEDB_RT_AML_STAFF
+ };
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 (
+ "aml_staff_uuid",
+ &td.serial),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "decider_pub",
+ &td.details.aml_staff.decider_pub),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "master_sig",
+ &td.details.aml_staff.master_sig),
+ GNUNET_PQ_result_spec_string (
+ "decider_name",
+ &td.details.aml_staff.decider_name),
+ GNUNET_PQ_result_spec_bool (
+ "is_active",
+ &td.details.aml_staff.is_active),
+ GNUNET_PQ_result_spec_bool (
+ "read_only",
+ &td.details.aml_staff.read_only),
+ GNUNET_PQ_result_spec_timestamp (
+ "last_change",
+ &td.details.aml_staff.last_change),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->error = true;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ &td);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+/**
+ * Function called with aml_history table entries.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lrbt_cb_table_aml_history (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LookupRecordsByTableContext *ctx = cls;
+ struct PostgresClosure *pg = ctx->pg;
+ struct TALER_EXCHANGEDB_TableData td = {
+ .table = TALER_EXCHANGEDB_RT_AML_HISTORY
+ };
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ uint32_t status32;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 (
+ "aml_history_serial_id",
+ &td.serial),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "h_payto",
+ &td.details.aml_history.h_payto),
+ TALER_PQ_RESULT_SPEC_AMOUNT (
+ "new_threshold",
+ &td.details.aml_history.new_threshold),
+ GNUNET_PQ_result_spec_uint32 (
+ "new_status",
+ &status32),
+ GNUNET_PQ_result_spec_timestamp (
+ "decision_time",
+ &td.details.aml_history.decision_time),
+ GNUNET_PQ_result_spec_string (
+ "justification",
+ &td.details.aml_history.justification),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string (
+ "kyc_requirements",
+ &td.details.aml_history.kyc_requirements),
+ NULL),
+ GNUNET_PQ_result_spec_uint64 (
+ "kyc_req_row",
+ &td.details.aml_history.kyc_req_row),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "decider_pub",
+ &td.details.aml_history.decider_pub),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "decider_sig",
+ &td.details.aml_history.decider_sig),
+ GNUNET_PQ_result_spec_end
+ };
+
+ td.details.aml_history.kyc_requirements = NULL;
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->error = true;
+ return;
+ }
+ td.details.aml_history.new_status
+ = (enum TALER_AmlDecisionState) status32;
+ ctx->cb (ctx->cb_cls,
+ &td);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+/**
+ * Function called with kyc_attributes table entries.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lrbt_cb_table_kyc_attributes (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LookupRecordsByTableContext *ctx = cls;
+ struct TALER_EXCHANGEDB_TableData td = {
+ .table = TALER_EXCHANGEDB_RT_KYC_ATTRIBUTES
+ };
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 (
+ "kyc_attributes_serial_id",
+ &td.serial),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "h_payto",
+ &td.details.kyc_attributes.h_payto),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "kyc_prox",
+ &td.details.kyc_attributes.kyc_prox),
+ GNUNET_PQ_result_spec_string (
+ "provider",
+ &td.details.kyc_attributes.provider),
+ GNUNET_PQ_result_spec_timestamp (
+ "collection_time",
+ &td.details.kyc_attributes.collection_time),
+ GNUNET_PQ_result_spec_timestamp (
+ "expiration_time",
+ &td.details.kyc_attributes.expiration_time),
+ GNUNET_PQ_result_spec_variable_size (
+ "encrypted_attributes",
+ &td.details.kyc_attributes.encrypted_attributes,
+ &td.details.kyc_attributes.encrypted_attributes_size),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->error = true;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ &td);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+/**
+ * Function called with purse_deletion table entries.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lrbt_cb_table_purse_deletion (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LookupRecordsByTableContext *ctx = cls;
+ struct TALER_EXCHANGEDB_TableData td = {
+ .table = TALER_EXCHANGEDB_RT_PURSE_DELETION
+ };
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 (
+ "purse_deletion_serial_id",
+ &td.serial),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "purse_sig",
+ &td.details.purse_deletion.purse_sig),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "purse_pub",
+ &td.details.purse_deletion.purse_pub),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->error = true;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ &td);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+/**
+ * Function called with age_withdraw table entries.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lrbt_cb_table_age_withdraw (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LookupRecordsByTableContext *ctx = cls;
+ struct PostgresClosure *pg = ctx->pg;
+ struct TALER_EXCHANGEDB_TableData td = {
+ .table = TALER_EXCHANGEDB_RT_AGE_WITHDRAW
+ };
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 (
+ "age_withdraw_id",
+ &td.serial),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "h_commitment",
+ &td.details.age_withdraw.h_commitment),
+ GNUNET_PQ_result_spec_uint16 (
+ "max_age",
+ &td.details.age_withdraw.max_age),
+ TALER_PQ_RESULT_SPEC_AMOUNT (
+ "amount_with_fee",
+ &td.details.age_withdraw.amount_with_fee),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "reserve_pub",
+ &td.details.age_withdraw.reserve_pub),
+ GNUNET_PQ_result_spec_auto_from_type (
+ "reserve_sig",
+ &td.details.age_withdraw.reserve_sig),
+ GNUNET_PQ_result_spec_uint32 (
+ "noreveal_index",
+ &td.details.age_withdraw.noreveal_index),
+ /* TODO[oec]: more fields! */
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->error = true;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ &td);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+/**
* Assign statement to @a n and PREPARE
* @a sql under name @a n.
*/
@@ -2321,8 +2902,8 @@ TEH_PG_lookup_records_by_table (void *cls,
.cb = cb,
.cb_cls = cb_cls
};
- GNUNET_PQ_PostgresResultHandler rh;
- const char *statement;
+ GNUNET_PQ_PostgresResultHandler rh = NULL;
+ const char *statement = NULL;
enum GNUNET_DB_QueryStatus qs;
switch (table)
@@ -2338,16 +2919,11 @@ TEH_PG_lookup_records_by_table (void *cls,
",expire_withdraw"
",expire_deposit"
",expire_legal"
- ",coin_val"
- ",coin_frac"
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refund_val"
- ",fee_refund_frac"
+ ",coin"
+ ",fee_withdraw"
+ ",fee_deposit"
+ ",fee_refresh"
+ ",fee_refund"
",age_mask"
" FROM denominations"
" WHERE denominations_serial > $1"
@@ -2375,6 +2951,33 @@ TEH_PG_lookup_records_by_table (void *cls,
" ORDER BY wire_target_serial_id ASC;");
rh = &lrbt_cb_table_wire_targets;
break;
+ case TALER_EXCHANGEDB_RT_LEGITIMIZATION_PROCESSES:
+ XPREPARE ("select_above_serial_by_table_legitimization_processes",
+ "SELECT"
+ " legitimization_process_serial_id AS serial"
+ ",h_payto"
+ ",reserve_pub"
+ ",expiration_time"
+ ",provider_section"
+ ",provider_user_id"
+ ",provider_legitimization_id"
+ " FROM legitimization_processes"
+ " WHERE legitimization_process_serial_id > $1"
+ " ORDER BY legitimization_process_serial_id ASC;");
+ rh = &lrbt_cb_table_legitimization_processes;
+ break;
+ case TALER_EXCHANGEDB_RT_LEGITIMIZATION_REQUIREMENTS:
+ XPREPARE ("select_above_serial_by_table_legitimization_requirements",
+ "SELECT"
+ " legitimization_requirement_serial_id AS serial"
+ ",h_payto"
+ ",reserve_pub"
+ ",required_checks"
+ " FROM legitimization_requirements"
+ " WHERE legitimization_requirement_serial_id > $1"
+ " ORDER BY legitimization_requirement_serial_id ASC;");
+ rh = &lrbt_cb_table_legitimization_requirements;
+ break;
case TALER_EXCHANGEDB_RT_RESERVES:
XPREPARE ("select_above_serial_by_table_reserves",
"SELECT"
@@ -2393,8 +2996,7 @@ TEH_PG_lookup_records_by_table (void *cls,
" reserve_in_serial_id AS serial"
",reserve_pub"
",wire_reference"
- ",credit_val"
- ",credit_frac"
+ ",credit"
",wire_source_h_payto"
",exchange_account_section"
",execution_date"
@@ -2411,15 +3013,42 @@ TEH_PG_lookup_records_by_table (void *cls,
",execution_date"
",wtid"
",wire_target_h_payto"
- ",amount_val"
- ",amount_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
+ ",amount"
+ ",closing_fee"
" FROM reserves_close"
" WHERE close_uuid > $1"
" ORDER BY close_uuid ASC;");
rh = &lrbt_cb_table_reserves_close;
break;
+ case TALER_EXCHANGEDB_RT_RESERVES_OPEN_REQUESTS:
+ XPREPARE ("select_above_serial_by_table_reserves_open_requests",
+ "SELECT"
+ " open_request_uuid AS serial"
+ ",reserve_pub"
+ ",request_timestamp"
+ ",expiration_date"
+ ",reserve_sig"
+ ",reserve_payment"
+ ",requested_purse_limit"
+ " FROM reserves_open_requests"
+ " WHERE open_request_uuid > $1"
+ " ORDER BY open_request_uuid ASC;");
+ rh = &lrbt_cb_table_reserves_open_requests;
+ break;
+ case TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS:
+ XPREPARE ("select_above_serial_by_table_reserves_open_deposits",
+ "SELECT"
+ " reserves_open_deposit_uuid AS serial"
+ ",reserve_sig"
+ ",reserve_pub"
+ ",coin_pub"
+ ",coin_sig"
+ ",contribution"
+ " FROM reserves_open_deposits"
+ " WHERE reserves_open_deposit_uuid > $1"
+ " ORDER BY reserves_open_deposit_uuid ASC;");
+ rh = &lrbt_cb_table_reserves_open_deposits;
+ break;
case TALER_EXCHANGEDB_RT_RESERVES_OUT:
XPREPARE ("select_above_serial_by_table_reserves_out",
"SELECT"
@@ -2430,8 +3059,7 @@ TEH_PG_lookup_records_by_table (void *cls,
",reserve_uuid"
",reserve_sig"
",execution_date"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
" FROM reserves_out"
" JOIN reserves USING (reserve_uuid)"
" WHERE reserve_out_serial_id > $1"
@@ -2507,8 +3135,7 @@ TEH_PG_lookup_records_by_table (void *cls,
" melt_serial_id AS serial"
",rc"
",old_coin_sig"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
",noreveal_index"
",old_coin_pub"
" FROM refresh_commitments"
@@ -2544,31 +3171,40 @@ TEH_PG_lookup_records_by_table (void *cls,
" ORDER BY rtc_serial ASC;");
rh = &lrbt_cb_table_refresh_transfer_keys;
break;
- case TALER_EXCHANGEDB_RT_DEPOSITS:
- XPREPARE ("select_above_serial_by_table_deposits",
+ case TALER_EXCHANGEDB_RT_BATCH_DEPOSITS:
+ XPREPARE ("select_above_serial_by_table_batch_deposits",
"SELECT"
- " deposit_serial_id AS serial"
+ " batch_deposit_serial_id AS serial"
",shard"
- ",coin_pub"
- ",known_coin_id"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",merchant_pub"
",wallet_timestamp"
",exchange_timestamp"
",refund_deadline"
",wire_deadline"
- ",merchant_pub"
",h_contract_terms"
- ",coin_sig"
+ ",wallet_data_hash"
",wire_salt"
",wire_target_h_payto"
",done"
",policy_blocked"
",policy_details_serial_id"
- " FROM deposits"
- " WHERE deposit_serial_id > $1"
- " ORDER BY deposit_serial_id ASC;");
- rh = &lrbt_cb_table_deposits;
+ " FROM batch_deposits"
+ " WHERE batch_deposit_serial_id > $1"
+ " ORDER BY batch_deposit_serial_id ASC;");
+ rh = &lrbt_cb_table_batch_deposits;
+ break;
+ case TALER_EXCHANGEDB_RT_COIN_DEPOSITS:
+ XPREPARE ("select_above_serial_by_table_coin_deposits",
+ "SELECT"
+ " coin_deposit_serial_id AS serial"
+ ",batch_deposit_serial_id"
+ ",coin_pub"
+ ",coin_sig"
+ ",amount_with_fee"
+ " FROM coin_deposits"
+ " WHERE coin_deposit_serial_id > $1"
+ " ORDER BY coin_deposit_serial_id ASC;");
+ rh = &lrbt_cb_table_coin_deposits;
break;
case TALER_EXCHANGEDB_RT_REFUNDS:
XPREPARE ("select_above_serial_by_table_refunds",
@@ -2577,9 +3213,8 @@ TEH_PG_lookup_records_by_table (void *cls,
",coin_pub"
",merchant_sig"
",rtransaction_id"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",deposit_serial_id"
+ ",amount_with_fee"
+ ",batch_deposit_serial_id"
" FROM refunds"
" WHERE refund_serial_id > $1"
" ORDER BY refund_serial_id ASC;");
@@ -2593,8 +3228,7 @@ TEH_PG_lookup_records_by_table (void *cls,
",wtid_raw"
",wire_target_h_payto"
",exchange_account_section"
- ",amount_val"
- ",amount_frac"
+ ",amount"
" FROM wire_out"
" WHERE wireout_uuid > $1"
" ORDER BY wireout_uuid ASC;");
@@ -2604,7 +3238,7 @@ TEH_PG_lookup_records_by_table (void *cls,
XPREPARE ("select_above_serial_by_table_aggregation_tracking",
"SELECT"
" aggregation_serial_id AS serial"
- ",deposit_serial_id"
+ ",batch_deposit_serial_id"
",wtid_raw"
" FROM aggregation_tracking"
" WHERE aggregation_serial_id > $1"
@@ -2618,10 +3252,8 @@ TEH_PG_lookup_records_by_table (void *cls,
",wire_method"
",start_date"
",end_date"
- ",wire_fee_val"
- ",wire_fee_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
+ ",wire_fee"
+ ",closing_fee"
",master_sig"
" FROM wire_fee"
" WHERE wire_fee_serial > $1"
@@ -2634,12 +3266,9 @@ TEH_PG_lookup_records_by_table (void *cls,
" global_fee_serial AS serial"
",start_date"
",end_date"
- ",history_fee_val"
- ",history_fee_frac"
- ",account_fee_val"
- ",account_fee_frac"
- ",purse_fee_val"
- ",purse_fee_frac"
+ ",history_fee"
+ ",account_fee"
+ ",purse_fee"
",purse_timeout"
",history_expiration"
",purse_account_limit"
@@ -2655,8 +3284,7 @@ TEH_PG_lookup_records_by_table (void *cls,
" recoup_uuid AS serial"
",coin_sig"
",coin_blind"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",recoup_timestamp"
",coin_pub"
",reserve_out_serial_id"
@@ -2671,8 +3299,7 @@ TEH_PG_lookup_records_by_table (void *cls,
" recoup_refresh_uuid AS serial"
",coin_sig"
",coin_blind"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",recoup_timestamp"
",coin_pub"
",known_coin_id"
@@ -2705,10 +3332,8 @@ TEH_PG_lookup_records_by_table (void *cls,
",h_contract_terms"
",age_limit"
",flags"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",purse_fee_val"
- ",purse_fee_frac"
+ ",amount_with_fee"
+ ",purse_fee"
",purse_sig"
" FROM purse_requests"
" WHERE purse_requests_serial_id > $1"
@@ -2748,8 +3373,7 @@ TEH_PG_lookup_records_by_table (void *cls,
",partner_serial_id"
",purse_pub"
",coin_pub"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
",coin_sig"
" FROM purse_deposits"
" WHERE purse_deposit_serial_id > $1"
@@ -2776,8 +3400,7 @@ TEH_PG_lookup_records_by_table (void *cls,
",reserve_pub"
",request_timestamp"
",reserve_sig"
- ",history_fee_val"
- ",history_fee_frac"
+ ",history_fee"
" FROM history_requests"
" WHERE history_request_serial_id > $1"
" ORDER BY history_request_serial_id ASC;");
@@ -2790,8 +3413,7 @@ TEH_PG_lookup_records_by_table (void *cls,
",reserve_pub"
",close_timestamp"
",reserve_sig"
- ",close_val"
- ",close_frac"
+ ",close"
" FROM close_requests"
" WHERE close_request_serial_id > $1"
" ORDER BY close_request_serial_id ASC;");
@@ -2803,8 +3425,7 @@ TEH_PG_lookup_records_by_table (void *cls,
" wad_out_serial_id"
",wad_id"
",partner_serial_id"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",execution_time"
" FROM wads_out"
" WHERE wad_out_serial_id > $1"
@@ -2820,12 +3441,9 @@ TEH_PG_lookup_records_by_table (void *cls,
",h_contract"
",purse_expiration"
",merge_timestamp"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",wad_fee_val"
- ",wad_fee_frac"
- ",deposit_fees_val"
- ",deposit_fees_frac"
+ ",amount_with_fee"
+ ",wad_fee"
+ ",deposit_fees"
",reserve_sig"
",purse_sig"
" FROM wad_out_entries"
@@ -2839,8 +3457,7 @@ TEH_PG_lookup_records_by_table (void *cls,
" wad_in_serial_id"
",wad_id"
",origin_exchange_url"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",arrival_time"
" FROM wads_in"
" WHERE wad_in_serial_id > $1"
@@ -2856,12 +3473,9 @@ TEH_PG_lookup_records_by_table (void *cls,
",h_contract"
",purse_expiration"
",merge_timestamp"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",wad_fee_val"
- ",wad_fee_frac"
- ",deposit_fees_val"
- ",deposit_fees_frac"
+ ",amount_with_fee"
+ ",wad_fee"
+ ",deposit_fees"
",reserve_sig"
",purse_sig"
" FROM wad_in_entries"
@@ -2877,15 +3491,92 @@ TEH_PG_lookup_records_by_table (void *cls,
",account_section"
",payto_uri"
",trigger_date"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",master_sig"
" FROM profit_drains"
" WHERE profit_drain_serial_id > $1"
" ORDER BY profit_drain_serial_id ASC;");
rh = &lrbt_cb_table_profit_drains;
break;
- default:
+
+ case TALER_EXCHANGEDB_RT_AML_STAFF:
+ XPREPARE ("select_above_serial_by_table_aml_staff",
+ "SELECT"
+ " aml_staff_uuid"
+ ",decider_pub"
+ ",master_sig"
+ ",decider_name"
+ ",is_active"
+ ",read_only"
+ ",last_change"
+ " FROM aml_staff"
+ " WHERE aml_staff_uuid > $1"
+ " ORDER BY aml_staff_uuid ASC;");
+ rh = &lrbt_cb_table_aml_staff;
+ break;
+ case TALER_EXCHANGEDB_RT_AML_HISTORY:
+ XPREPARE ("select_above_serial_by_table_aml_history",
+ "SELECT"
+ " aml_history_serial_id"
+ ",h_payto"
+ ",new_threshold"
+ ",new_status"
+ ",decision_time"
+ ",justification"
+ ",kyc_requirements"
+ ",kyc_req_row"
+ ",decider_pub"
+ ",decider_sig"
+ " FROM aml_history"
+ " WHERE aml_history_serial_id > $1"
+ " ORDER BY aml_history_serial_id ASC;");
+ rh = &lrbt_cb_table_aml_history;
+ break;
+ case TALER_EXCHANGEDB_RT_KYC_ATTRIBUTES:
+ XPREPARE ("select_above_serial_by_table_kyc_attributes",
+ "SELECT"
+ " kyc_attributes_serial_id"
+ ",h_payto"
+ ",kyc_prox"
+ ",provider"
+ ",collection_time"
+ ",expiration_time"
+ ",encrypted_attributes"
+ " FROM kyc_attributes"
+ " WHERE kyc_attributes_serial_id > $1"
+ " ORDER BY kyc_attributes_serial_id ASC;");
+ rh = &lrbt_cb_table_kyc_attributes;
+ break;
+ case TALER_EXCHANGEDB_RT_PURSE_DELETION:
+ XPREPARE ("select_above_serial_by_table_purse_deletion",
+ "SELECT"
+ " purse_deletion_serial_id"
+ ",purse_pub"
+ ",purse_sig"
+ " FROM purse_deletion"
+ " WHERE purse_deletion_serial_id > $1"
+ " ORDER BY purse_deletion_serial_id ASC;");
+ rh = &lrbt_cb_table_purse_deletion;
+ break;
+ case TALER_EXCHANGEDB_RT_AGE_WITHDRAW:
+ XPREPARE ("select_above_serial_by_table_age_withdraw",
+ "SELECT"
+ " age_withdraw_id"
+ ",h_commitment"
+ ",amount_with_fee"
+ ",max_age"
+ ",reserve_pub"
+ ",reserve_sig"
+ ",noreveal_index"
+ " FROM age_withdraw"
+ " WHERE age_withdraw_id > $1"
+ " ORDER BY age_withdraw_id ASC;");
+ /* TODO[oec]: MORE FIELDS! */
+ rh = &lrbt_cb_table_age_withdraw;
+ break;
+ }
+ if (NULL == rh)
+ {
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
diff --git a/src/exchangedb/pg_lookup_serial_by_table.c b/src/exchangedb/pg_lookup_serial_by_table.c
index 7e150cd28..9fda7ddf8 100644
--- a/src/exchangedb/pg_lookup_serial_by_table.c
+++ b/src/exchangedb/pg_lookup_serial_by_table.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -205,12 +205,20 @@ TEH_PG_lookup_serial_by_table (void *cls,
" ORDER BY rtc_serial DESC"
" LIMIT 1;");
break;
- case TALER_EXCHANGEDB_RT_DEPOSITS:
- XPREPARE ("select_serial_by_table_deposits",
+ case TALER_EXCHANGEDB_RT_BATCH_DEPOSITS:
+ XPREPARE ("select_serial_by_table_batch_deposits",
"SELECT"
- " deposit_serial_id AS serial"
- " FROM deposits"
- " ORDER BY deposit_serial_id DESC"
+ " batch_deposit_serial_id AS serial"
+ " FROM batch_deposits"
+ " ORDER BY batch_deposit_serial_id DESC"
+ " LIMIT 1;");
+ break;
+ case TALER_EXCHANGEDB_RT_COIN_DEPOSITS:
+ XPREPARE ("select_serial_by_table_coin_deposits",
+ "SELECT"
+ " coin_deposit_serial_id AS serial"
+ " FROM coin_deposits"
+ " ORDER BY coin_deposit_serial_id DESC"
" LIMIT 1;");
break;
case TALER_EXCHANGEDB_RT_REFUNDS:
@@ -390,6 +398,51 @@ TEH_PG_lookup_serial_by_table (void *cls,
" LIMIT 1;");
statement = "select_serial_by_table_profit_drains";
break;
+ case TALER_EXCHANGEDB_RT_AML_STAFF:
+ XPREPARE ("select_serial_by_table_aml_staff",
+ "SELECT"
+ " aml_staff_uuid AS serial"
+ " FROM aml_staff"
+ " ORDER BY aml_staff_uuid DESC"
+ " LIMIT 1;");
+ statement = "select_serial_by_table_aml_staff";
+ break;
+ case TALER_EXCHANGEDB_RT_AML_HISTORY:
+ XPREPARE ("select_serial_by_table_aml_history",
+ "SELECT"
+ " aml_history_serial_id AS serial"
+ " FROM aml_history"
+ " ORDER BY aml_history_serial_id DESC"
+ " LIMIT 1;");
+ statement = "select_serial_by_table_aml_history";
+ break;
+ case TALER_EXCHANGEDB_RT_KYC_ATTRIBUTES:
+ XPREPARE ("select_serial_by_table_kyc_attributes",
+ "SELECT"
+ " kyc_attributes_serial_id AS serial"
+ " FROM kyc_attributes"
+ " ORDER BY kyc_attributes_serial_id DESC"
+ " LIMIT 1;");
+ statement = "select_serial_by_table_kyc_attributes";
+ break;
+ case TALER_EXCHANGEDB_RT_PURSE_DELETION:
+ XPREPARE ("select_serial_by_table_purse_deletion",
+ "SELECT"
+ " purse_deletion_serial_id AS serial"
+ " FROM purse_deletion"
+ " ORDER BY purse_deletion_serial_id DESC"
+ " LIMIT 1;");
+ statement = "select_serial_by_table_purse_deletion";
+ break;
+ case TALER_EXCHANGEDB_RT_AGE_WITHDRAW:
+ XPREPARE ("select_serial_by_table_age_withdraw",
+ "SELECT"
+ " age_withdraw_id AS serial"
+ " FROM age_withdraw"
+ " ORDER BY age_withdraw_id DESC"
+ " LIMIT 1;");
+ statement = "select_serial_by_table_age_withdraw";
+ break;
}
if (NULL == statement)
{
diff --git a/src/exchangedb/pg_lookup_signing_key.c b/src/exchangedb/pg_lookup_signing_key.c
index 3f31a6f49..3803d114f 100644
--- a/src/exchangedb/pg_lookup_signing_key.c
+++ b/src/exchangedb/pg_lookup_signing_key.c
@@ -62,4 +62,3 @@ TEH_PG_lookup_signing_key (
params,
rs);
}
-
diff --git a/src/exchangedb/pg_lookup_signkey_revocation.c b/src/exchangedb/pg_lookup_signkey_revocation.c
index 374aa7d5b..056ecddc4 100644
--- a/src/exchangedb/pg_lookup_signkey_revocation.c
+++ b/src/exchangedb/pg_lookup_signkey_revocation.c
@@ -26,7 +26,6 @@
#include "pg_helper.h"
-
enum GNUNET_DB_QueryStatus
TEH_PG_lookup_signkey_revocation (
void *cls,
@@ -44,7 +43,6 @@ TEH_PG_lookup_signkey_revocation (
GNUNET_PQ_result_spec_end
};
-
PREPARE (pg,
"lookup_signkey_revocation",
"SELECT "
diff --git a/src/exchangedb/pg_lookup_transfer_by_deposit.c b/src/exchangedb/pg_lookup_transfer_by_deposit.c
index 686b67cc4..192556130 100644
--- a/src/exchangedb/pg_lookup_transfer_by_deposit.c
+++ b/src/exchangedb/pg_lookup_transfer_by_deposit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -26,7 +26,6 @@
#include "pg_helper.h"
-
enum GNUNET_DB_QueryStatus
TEH_PG_lookup_transfer_by_deposit (
void *cls,
@@ -39,7 +38,8 @@ TEH_PG_lookup_transfer_by_deposit (
struct GNUNET_TIME_Timestamp *exec_time,
struct TALER_Amount *amount_with_fee,
struct TALER_Amount *deposit_fee,
- struct TALER_EXCHANGEDB_KycStatus *kyc)
+ struct TALER_EXCHANGEDB_KycStatus *kyc,
+ enum TALER_AmlDecisionState *aml_decision)
{
struct PostgresClosure *pg = cls;
enum GNUNET_DB_QueryStatus qs;
@@ -71,34 +71,33 @@ TEH_PG_lookup_transfer_by_deposit (
0,
sizeof (*kyc));
/* check if the aggregation record exists and get it */
-
- /* Used in #postgres_lookup_transfer_by_deposit */
PREPARE (pg,
"lookup_deposit_wtid",
"SELECT"
- " aggregation_tracking.wtid_raw"
+ " atr.wtid_raw"
",wire_out.execution_date"
- ",dep.amount_with_fee_val"
- ",dep.amount_with_fee_frac"
- ",dep.wire_salt"
+ ",cdep.amount_with_fee"
+ ",bdep.wire_salt"
",wt.payto_uri"
- ",denom.fee_deposit_val"
- ",denom.fee_deposit_frac"
- " FROM deposits dep"
+ ",denom.fee_deposit"
+ " FROM coin_deposits cdep"
+ " JOIN batch_deposits bdep"
+ " USING (batch_deposit_serial_id)"
" JOIN wire_targets wt"
" USING (wire_target_h_payto)"
- " JOIN aggregation_tracking"
- " USING (deposit_serial_id)"
+ " JOIN aggregation_tracking atr"
+ " ON (cdep.batch_deposit_serial_id = atr.batch_deposit_serial_id)"
" JOIN known_coins kc"
- " ON (kc.coin_pub = dep.coin_pub)"
+ " ON (kc.coin_pub = cdep.coin_pub)"
" JOIN denominations denom"
" USING (denominations_serial)"
" JOIN wire_out"
" USING (wtid_raw)"
- " WHERE dep.coin_pub=$1"
- " AND dep.merchant_pub=$3"
- " AND dep.h_contract_terms=$2");
-
+ " WHERE cdep.coin_pub=$1"
+ " AND bdep.merchant_pub=$3"
+ " AND bdep.h_contract_terms=$2");
+ /* NOTE: above query might be more efficient if we computed the shard
+ from the merchant_pub and included that in the query */
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_deposit_wtid",
params,
@@ -110,16 +109,18 @@ TEH_PG_lookup_transfer_by_deposit (
TALER_merchant_wire_signature_hash (payto_uri,
&wire_salt,
&wh);
- GNUNET_PQ_cleanup_result (rs);
if (0 ==
GNUNET_memcmp (&wh,
h_wire))
{
*pending = false;
kyc->ok = true;
+ *aml_decision = TALER_AML_NORMAL;
+ GNUNET_PQ_cleanup_result (rs);
return qs;
}
qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ GNUNET_PQ_cleanup_result (rs);
}
if (0 > qs)
return qs;
@@ -134,15 +135,29 @@ TEH_PG_lookup_transfer_by_deposit (
/* Check if transaction exists in deposits, so that we just
do not have a WTID yet. In that case, return without wtid
(by setting 'pending' true). */
+ uint32_t status32 = TALER_AML_NORMAL;
+ uint64_t aml_kyc_row = 0;
struct GNUNET_PQ_ResultSpec rs2[] = {
GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
&wire_salt),
GNUNET_PQ_result_spec_string ("payto_uri",
&payto_uri),
+ /* See Postgresql bug #18380 */
+#define BUG 1
+#if BUG
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_uint64 ("legitimization_requirement_serial_id",
&kyc->requirement_row),
NULL),
+#endif
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_uint64 ("kyc_requirement",
+ &aml_kyc_row),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_uint32 ("status",
+ &status32),
+ NULL),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
amount_with_fee),
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
@@ -152,33 +167,41 @@ TEH_PG_lookup_transfer_by_deposit (
GNUNET_PQ_result_spec_end
};
- /* Fetch an existing deposit request.
- Used in #postgres_lookup_transfer_by_deposit(). */
PREPARE (pg,
"get_deposit_without_wtid",
"SELECT"
- " agt.legitimization_requirement_serial_id"
- ",dep.wire_salt"
+ " bdep.wire_salt"
",wt.payto_uri"
- ",dep.amount_with_fee_val"
- ",dep.amount_with_fee_frac"
- ",denom.fee_deposit_val"
- ",denom.fee_deposit_frac"
- ",dep.wire_deadline"
- " FROM deposits dep"
+ ",cdep.amount_with_fee"
+ ",denom.fee_deposit"
+ ",bdep.wire_deadline"
+#if BUG
+ ",agt.legitimization_requirement_serial_id"
+#endif
+ ",aml.status"
+ ",aml.kyc_requirement"
+ " FROM coin_deposits cdep"
+ " JOIN batch_deposits bdep"
+ " USING (batch_deposit_serial_id)"
" JOIN wire_targets wt"
" USING (wire_target_h_payto)"
" JOIN known_coins kc"
- " ON (kc.coin_pub = dep.coin_pub)"
+ " ON (kc.coin_pub = cdep.coin_pub)"
" JOIN denominations denom"
" USING (denominations_serial)"
+#if BUG
" LEFT JOIN aggregation_transient agt "
- " ON ( (dep.wire_target_h_payto = agt.wire_target_h_payto) AND"
- " (dep.merchant_pub = agt.merchant_pub) )"
- " WHERE dep.coin_pub=$1"
- " AND dep.merchant_pub=$3"
- " AND dep.h_contract_terms=$2"
+ " ON ( (bdep.wire_target_h_payto = agt.wire_target_h_payto) AND"
+ " (bdep.merchant_pub = agt.merchant_pub) )"
+#endif
+ " LEFT JOIN aml_status aml"
+ " ON (wt.wire_target_h_payto = aml.h_payto)"
+ " WHERE cdep.coin_pub=$1"
+ " AND bdep.merchant_pub=$3"
+ " AND bdep.h_contract_terms=$2"
" LIMIT 1;");
+ /* NOTE: above query might be more efficient if we computed the shard
+ from the merchant_pub and included that in the query */
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_deposit_without_wtid",
params,
@@ -187,17 +210,30 @@ TEH_PG_lookup_transfer_by_deposit (
{
struct TALER_MerchantWireHashP wh;
+ *aml_decision = (enum TALER_AmlDecisionState) status32;
if (0 == kyc->requirement_row)
kyc->ok = true; /* technically: unknown */
+ if ( (kyc->ok) &&
+ (TALER_AML_FROZEN == *aml_decision) &&
+ (0 != aml_kyc_row) )
+ {
+ /* KYC required via AML */
+ kyc->ok = false;
+ kyc->requirement_row = aml_kyc_row;
+ }
TALER_merchant_wire_signature_hash (payto_uri,
&wire_salt,
&wh);
- GNUNET_PQ_cleanup_result (rs);
if (0 !=
GNUNET_memcmp (&wh,
h_wire))
+ {
+ GNUNET_PQ_cleanup_result (rs2);
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ }
+ GNUNET_PQ_cleanup_result (rs2);
}
+ *aml_decision = TALER_AML_NORMAL;
return qs;
}
}
diff --git a/src/exchangedb/pg_lookup_transfer_by_deposit.h b/src/exchangedb/pg_lookup_transfer_by_deposit.h
index ff5554dcc..83782d5a0 100644
--- a/src/exchangedb/pg_lookup_transfer_by_deposit.h
+++ b/src/exchangedb/pg_lookup_transfer_by_deposit.h
@@ -42,6 +42,7 @@
* @param[out] amount_with_fee set to the total deposited amount
* @param[out] deposit_fee set to how much the exchange did charge for the deposit
* @param[out] kyc set to the kyc status of the receiver (if @a pending)
+ * @param[out] aml_decision set to the AML status of the receiver
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
@@ -56,6 +57,7 @@ TEH_PG_lookup_transfer_by_deposit (
struct GNUNET_TIME_Timestamp *exec_time,
struct TALER_Amount *amount_with_fee,
struct TALER_Amount *deposit_fee,
- struct TALER_EXCHANGEDB_KycStatus *kyc);
+ struct TALER_EXCHANGEDB_KycStatus *kyc,
+ enum TALER_AmlDecisionState *aml_decision);
#endif
diff --git a/src/exchangedb/pg_lookup_wire_fee_by_time.c b/src/exchangedb/pg_lookup_wire_fee_by_time.c
index 89be4087a..775232a48 100644
--- a/src/exchangedb/pg_lookup_wire_fee_by_time.c
+++ b/src/exchangedb/pg_lookup_wire_fee_by_time.c
@@ -142,10 +142,8 @@ TEH_PG_lookup_wire_fee_by_time (
PREPARE (pg,
"lookup_wire_fee_by_time",
"SELECT"
- " wire_fee_val"
- ",wire_fee_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
+ " wire_fee"
+ ",closing_fee"
" FROM wire_fee"
" WHERE wire_method=$1"
" AND end_date > $2"
diff --git a/src/exchangedb/pg_lookup_wire_timestamp.c b/src/exchangedb/pg_lookup_wire_timestamp.c
index 19e915d48..17dffc706 100644
--- a/src/exchangedb/pg_lookup_wire_timestamp.c
+++ b/src/exchangedb/pg_lookup_wire_timestamp.c
@@ -28,8 +28,8 @@
enum GNUNET_DB_QueryStatus
TEH_PG_lookup_wire_timestamp (void *cls,
- const char *payto_uri,
- struct GNUNET_TIME_Timestamp *last_date)
+ const char *payto_uri,
+ struct GNUNET_TIME_Timestamp *last_date)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
diff --git a/src/exchangedb/pg_lookup_wire_timestamp.h b/src/exchangedb/pg_lookup_wire_timestamp.h
index 069dd940c..f2ee117de 100644
--- a/src/exchangedb/pg_lookup_wire_timestamp.h
+++ b/src/exchangedb/pg_lookup_wire_timestamp.h
@@ -34,7 +34,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_lookup_wire_timestamp (void *cls,
- const char *payto_uri,
+ const char *payto_uri,
struct GNUNET_TIME_Timestamp *last_date);
#endif
diff --git a/src/exchangedb/pg_lookup_wire_transfer.c b/src/exchangedb/pg_lookup_wire_transfer.c
index c5f63a070..7ab023fe7 100644
--- a/src/exchangedb/pg_lookup_wire_transfer.c
+++ b/src/exchangedb/pg_lookup_wire_transfer.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -48,14 +48,14 @@ struct WireTransferResultContext
/**
* Set to #GNUNET_SYSERR on serious errors.
*/
- int status;
+ enum GNUNET_GenericReturnValue status;
};
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results. Helper function
- * for #postgres_lookup_wire_transfer().
+ * for #TEH_PG_lookup_wire_transfer().
*
* @param cls closure of type `struct WireTransferResultContext *`
* @param result the postgres result
@@ -82,7 +82,8 @@ handle_wt_result (void *cls,
struct TALER_DenominationPublicKey denom_pub;
char *payto_uri;
struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id", &rowid),
+ GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id",
+ &rowid),
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
&h_contract_terms),
GNUNET_PQ_result_spec_string ("payto_uri",
@@ -141,33 +142,32 @@ TEH_PG_lookup_wire_transfer (
GNUNET_PQ_query_param_auto_from_type (wtid),
GNUNET_PQ_query_param_end
};
- struct WireTransferResultContext ctx;
+ struct WireTransferResultContext ctx = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .pg = pg,
+ .status = GNUNET_OK
+ };
enum GNUNET_DB_QueryStatus qs;
- ctx.cb = cb;
- ctx.cb_cls = cb_cls;
- ctx.pg = pg;
- ctx.status = GNUNET_OK;
- /* check if the melt record exists and get it */
- /* Used in #postgres_lookup_wire_transfer */
PREPARE (pg,
"lookup_transactions",
"SELECT"
" aggregation_serial_id"
- ",deposits.h_contract_terms"
+ ",bdep.h_contract_terms"
",payto_uri"
",wire_targets.wire_target_h_payto"
",kc.coin_pub"
- ",deposits.merchant_pub"
+ ",bdep.merchant_pub"
",wire_out.execution_date"
- ",deposits.amount_with_fee_val"
- ",deposits.amount_with_fee_frac"
- ",denom.fee_deposit_val"
- ",denom.fee_deposit_frac"
+ ",cdep.amount_with_fee"
+ ",denom.fee_deposit"
",denom.denom_pub"
" FROM aggregation_tracking"
- " JOIN deposits"
- " USING (deposit_serial_id)"
+ " JOIN batch_deposits bdep"
+ " USING (batch_deposit_serial_id)"
+ " JOIN coin_deposits cdep"
+ " USING (batch_deposit_serial_id)"
" JOIN wire_targets"
" USING (wire_target_h_payto)"
" JOIN known_coins kc"
diff --git a/src/exchangedb/pg_persist_policy_details.c b/src/exchangedb/pg_persist_policy_details.c
index 2b7787878..d97b92eac 100644
--- a/src/exchangedb/pg_persist_policy_details.c
+++ b/src/exchangedb/pg_persist_policy_details.c
@@ -39,10 +39,14 @@ TEH_PG_persist_policy_details (
GNUNET_PQ_query_param_auto_from_type (&details->hash_code),
TALER_PQ_query_param_json (details->policy_json),
GNUNET_PQ_query_param_timestamp (&details->deadline),
- TALER_PQ_query_param_amount (&details->commitment),
- TALER_PQ_query_param_amount (&details->accumulated_total),
- TALER_PQ_query_param_amount (&details->policy_fee),
- TALER_PQ_query_param_amount (&details->transferable_amount),
+ TALER_PQ_query_param_amount (pg->conn,
+ &details->commitment),
+ TALER_PQ_query_param_amount (pg->conn,
+ &details->accumulated_total),
+ TALER_PQ_query_param_amount (pg->conn,
+ &details->policy_fee),
+ TALER_PQ_query_param_amount (pg->conn,
+ &details->transferable_amount),
GNUNET_PQ_query_param_auto_from_type (&details->fulfillment_state),
(details->no_policy_fulfillment_id)
? GNUNET_PQ_query_param_null ()
@@ -59,7 +63,14 @@ TEH_PG_persist_policy_details (
GNUNET_PQ_result_spec_end
};
- // FIXME: prepare missing!!?!
+ PREPARE (pg,
+ "call_insert_or_update_policy_details",
+ "SELECT"
+ " out_policy_details_serial_id AS policy_details_serial_id"
+ ",out_accumulated_total AS accumulated_total"
+ ",out_fulfillment_state AS fulfillment_state"
+ " FROM exchange_do_insert_or_update_policy_details"
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9);");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"call_insert_or_update_policy_details",
params,
diff --git a/src/exchangedb/pg_persist_policy_details.h b/src/exchangedb/pg_persist_policy_details.h
index ed9fd95d8..4fe709d92 100644
--- a/src/exchangedb/pg_persist_policy_details.h
+++ b/src/exchangedb/pg_persist_policy_details.h
@@ -24,6 +24,8 @@
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+
+
/* Persist the details to a policy in the policy_details table. If there
* already exists a policy, update the fields accordingly.
*
diff --git a/src/exchangedb/pg_preflight.c b/src/exchangedb/pg_preflight.c
index cc4de6f76..4533c9a97 100644
--- a/src/exchangedb/pg_preflight.c
+++ b/src/exchangedb/pg_preflight.c
@@ -30,12 +30,10 @@
* Connect to the database if the connection does not exist yet.
*
* @param pg the plugin-specific state
- * @param skip_prepare true if we should skip prepared statement setup
* @return #GNUNET_OK on success
*/
enum GNUNET_GenericReturnValue
-TEH_PG_internal_setup (struct PostgresClosure *pg,
- bool skip_prepare);
+TEH_PG_internal_setup (struct PostgresClosure *pg);
enum GNUNET_GenericReturnValue
@@ -47,13 +45,9 @@ TEH_PG_preflight (void *cls)
GNUNET_PQ_EXECUTE_STATEMENT_END
};
- if (! pg->init)
- {
- if (GNUNET_OK !=
- TEH_PG_internal_setup (pg,
- false))
- return GNUNET_SYSERR;
- }
+ if (GNUNET_OK !=
+ TEH_PG_internal_setup (pg))
+ return GNUNET_SYSERR;
if (NULL == pg->transaction_name)
return GNUNET_OK; /* all good */
if (GNUNET_OK ==
diff --git a/src/exchangedb/pg_profit_drains_get_pending.c b/src/exchangedb/pg_profit_drains_get_pending.c
index a7044ebb8..c844a3f38 100644
--- a/src/exchangedb/pg_profit_drains_get_pending.c
+++ b/src/exchangedb/pg_profit_drains_get_pending.c
@@ -57,7 +57,7 @@ TEH_PG_profit_drains_get_pending (
master_sig),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_profit_drains_get_pending() */
+ /* Used in #postgres_profit_drains_get_pending() */
PREPARE (pg,
"get_ready_profit_drain",
"SELECT"
@@ -66,8 +66,7 @@ TEH_PG_profit_drains_get_pending (
",account_section"
",payto_uri"
",trigger_date"
- ",amount_val"
- ",amount_frac"
+ ",amount"
",master_sig"
" FROM profit_drains"
" WHERE NOT executed"
diff --git a/src/exchangedb/pg_profit_drains_set_finished.c b/src/exchangedb/pg_profit_drains_set_finished.c
index b70af31fe..f0de27945 100644
--- a/src/exchangedb/pg_profit_drains_set_finished.c
+++ b/src/exchangedb/pg_profit_drains_set_finished.c
@@ -38,18 +38,12 @@ TEH_PG_profit_drains_set_finished (
};
PREPARE (pg,
- "drain_profit_set_finished",
- "UPDATE profit_drains"
- " SET"
- " executed=TRUE"
- " WHERE profit_drain_serial_id=$1;");
+ "drain_profit_set_finished",
+ "UPDATE profit_drains"
+ " SET"
+ " executed=TRUE"
+ " WHERE profit_drain_serial_id=$1;");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"drain_profit_set_finished",
params);
}
-
-
-
-
-
-
diff --git a/src/exchangedb/pg_release_revolving_shard.c b/src/exchangedb/pg_release_revolving_shard.c
index f176972b6..43e45c4bc 100644
--- a/src/exchangedb/pg_release_revolving_shard.c
+++ b/src/exchangedb/pg_release_revolving_shard.c
@@ -27,9 +27,9 @@
enum GNUNET_DB_QueryStatus
TEH_PG_release_revolving_shard (void *cls,
- const char *job_name,
- uint32_t start_row,
- uint32_t end_row)
+ const char *job_name,
+ uint32_t start_row,
+ uint32_t end_row)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
diff --git a/src/exchangedb/pg_release_revolving_shard.h b/src/exchangedb/pg_release_revolving_shard.h
index f1712f538..ea65ab605 100644
--- a/src/exchangedb/pg_release_revolving_shard.h
+++ b/src/exchangedb/pg_release_revolving_shard.h
@@ -37,8 +37,8 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_release_revolving_shard (void *cls,
- const char *job_name,
- uint32_t start_row,
+ const char *job_name,
+ uint32_t start_row,
uint32_t end_row);
#endif
diff --git a/src/exchangedb/pg_reserves_get.c b/src/exchangedb/pg_reserves_get.c
index bea0022dd..cae4764a5 100644
--- a/src/exchangedb/pg_reserves_get.c
+++ b/src/exchangedb/pg_reserves_get.c
@@ -27,7 +27,7 @@
enum GNUNET_DB_QueryStatus
TEH_PG_reserves_get (void *cls,
- struct TALER_EXCHANGEDB_Reserve *reserve)
+ struct TALER_EXCHANGEDB_Reserve *reserve)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -35,7 +35,8 @@ TEH_PG_reserves_get (void *cls,
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
+ TALER_PQ_result_spec_amount ("current_balance",
+ pg->currency,
&reserve->balance),
GNUNET_PQ_result_spec_timestamp ("expiration_date",
&reserve->expiry),
@@ -43,12 +44,11 @@ TEH_PG_reserves_get (void *cls,
&reserve->gc),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_reserves_get() */
+ /* Used in #postgres_reserves_get() */
PREPARE (pg,
"reserves_get",
"SELECT"
- " current_balance_val"
- ",current_balance_frac"
+ " current_balance"
",expiration_date"
",gc_date"
" FROM reserves"
diff --git a/src/exchangedb/pg_reserves_get_origin.c b/src/exchangedb/pg_reserves_get_origin.c
index fd6c56586..55d3179d1 100644
--- a/src/exchangedb/pg_reserves_get_origin.c
+++ b/src/exchangedb/pg_reserves_get_origin.c
@@ -50,7 +50,7 @@ TEH_PG_reserves_get_origin (
" wire_source_h_payto"
" FROM reserves_in"
" WHERE reserve_pub=$1");
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_h_wire_source_of_reserve",
params,
rs);
diff --git a/src/exchangedb/pg_reserves_in_insert.c b/src/exchangedb/pg_reserves_in_insert.c
index 428e19231..21734942a 100644
--- a/src/exchangedb/pg_reserves_in_insert.c
+++ b/src/exchangedb/pg_reserves_in_insert.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -17,6 +17,7 @@
* @file exchangedb/pg_reserves_in_insert.c
* @brief Implementation of the reserves_in_insert function for Postgres
* @author Christian Grothoff
+ * @author Joseph Xu
*/
#include "platform.h"
#include "taler_error_codes.h"
@@ -27,6 +28,8 @@
#include "pg_start.h"
#include "pg_start_read_committed.h"
#include "pg_commit.h"
+#include "pg_preflight.h"
+#include "pg_rollback.h"
#include "pg_reserves_get.h"
#include "pg_reserves_update.h"
#include "pg_setup_wire_target.h"
@@ -34,15 +37,13 @@
/**
- * Generate event notification for the reserve
- * change.
+ * Generate event notification for the reserve change.
*
- * @param pg plugin state
* @param reserve_pub reserve to notfiy on
+ * @return string to pass to postgres for the notification
*/
-static void
-notify_on_reserve (struct PostgresClosure *pg,
- const struct TALER_ReservePublicKeyP *reserve_pub)
+static char *
+compute_notify_on_reserve (const struct TALER_ReservePublicKeyP *reserve_pub)
{
struct TALER_ReserveEventP rep = {
.header.size = htons (sizeof (rep)),
@@ -50,246 +51,323 @@ notify_on_reserve (struct PostgresClosure *pg,
.reserve_pub = *reserve_pub
};
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Notifying on reserve!\n");
- TEH_PG_event_notify (pg,
- &rep.header,
- NULL,
- 0);
+ return GNUNET_PQ_get_event_notify_channel (&rep.header);
}
-enum GNUNET_DB_QueryStatus
-TEH_PG_reserves_in_insert (void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_Amount *balance,
- struct GNUNET_TIME_Timestamp execution_time,
- const char *sender_account_details,
- const char *exchange_account_section,
- uint64_t wire_ref)
+/**
+ * Closure for our helper_cb()
+ */
+struct Context
{
- struct PostgresClosure *pg = cls;
- enum GNUNET_DB_QueryStatus qs1;
- struct TALER_EXCHANGEDB_Reserve reserve;
- struct GNUNET_TIME_Timestamp expiry;
- struct GNUNET_TIME_Timestamp gc;
- uint64_t reserve_uuid;
+ /**
+ * Array of reserve UUIDs to initialize.
+ */
+ uint64_t *reserve_uuids;
+
+ /**
+ * Array with entries set to 'true' for duplicate transactions.
+ */
+ bool *transaction_duplicates;
+
+ /**
+ * Array with entries set to 'true' for rows with conflicts.
+ */
+ bool *conflicts;
+
+ /**
+ * Set to #GNUNET_SYSERR on failures.
+ */
+ enum GNUNET_GenericReturnValue status;
+
+ /**
+ * Single value (no array) set to true if we need
+ * to follow-up with an update.
+ */
+ bool needs_update;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct Context *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct Context *ctx = cls;
- reserve.pub = *reserve_pub;
- expiry = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_add (execution_time.abs_time,
- pg->idle_reserve_expiration_time));
- gc = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
- pg->legal_reserve_expiration_time));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Creating reserve %s with expiration in %s\n",
- TALER_B2S (reserve_pub),
- GNUNET_STRINGS_relative_time_to_string (
- pg->idle_reserve_expiration_time,
- GNUNET_NO));
- /* Optimistically assume this is a new reserve, create balance for the first
- time; we do this before adding the actual transaction to "reserves_in",
- as for a new reserve it can't be a duplicate 'add' operation, and as
- the 'add' operation needs the reserve entry as a foreign key. */
+ for (unsigned int i = 0; i<num_results; i++)
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- TALER_PQ_query_param_amount (balance),
- GNUNET_PQ_query_param_timestamp (&expiry),
- GNUNET_PQ_query_param_timestamp (&gc),
- GNUNET_PQ_query_param_end
- };
struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
- &reserve_uuid),
+ GNUNET_PQ_result_spec_bool (
+ "transaction_duplicate",
+ &ctx->transaction_duplicates[i]),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_uint64 ("ruuid",
+ &ctx->reserve_uuids[i]),
+ &ctx->conflicts[i]),
GNUNET_PQ_result_spec_end
};
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Reserve does not exist; creating a new one\n");
- /* Note: query uses 'on conflict do nothing' */
- PREPARE (pg,
- "reserve_create",
- "INSERT INTO reserves "
- "(reserve_pub"
- ",current_balance_val"
- ",current_balance_frac"
- ",expiration_date"
- ",gc_date"
- ") VALUES "
- "($1, $2, $3, $4, $5)"
- " ON CONFLICT DO NOTHING"
- " RETURNING reserve_uuid;");
- qs1 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "reserve_create",
- params,
- rs);
- if (qs1 < 0)
- return qs1;
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->status = GNUNET_SYSERR;
+ return;
+ }
+ if (! ctx->transaction_duplicates[i])
+ ctx->needs_update |= ctx->conflicts[i];
}
+}
- /* Create new incoming transaction, "ON CONFLICT DO NOTHING"
- is again used to guard against duplicates. */
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_reserves_in_insert (
+ void *cls,
+ const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
+ unsigned int reserves_length,
+ enum GNUNET_DB_QueryStatus *results)
+{
+ struct PostgresClosure *pg = cls;
+ unsigned int dups = 0;
+
+ struct TALER_PaytoHashP h_paytos[GNUNET_NZL (reserves_length)];
+ char *notify_s[GNUNET_NZL (reserves_length)];
+ struct TALER_ReservePublicKeyP reserve_pubs[GNUNET_NZL (reserves_length)];
+ struct TALER_Amount balances[GNUNET_NZL (reserves_length)];
+ struct GNUNET_TIME_Timestamp execution_times[GNUNET_NZL (reserves_length)];
+ const char *sender_account_details[GNUNET_NZL (reserves_length)];
+ const char *exchange_account_names[GNUNET_NZL (reserves_length)];
+ uint64_t wire_references[GNUNET_NZL (reserves_length)];
+ uint64_t reserve_uuids[GNUNET_NZL (reserves_length)];
+ bool transaction_duplicates[GNUNET_NZL (reserves_length)];
+ bool conflicts[GNUNET_NZL (reserves_length)];
+ struct GNUNET_TIME_Timestamp reserve_expiration
+ = GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time);
+ struct GNUNET_TIME_Timestamp gc
+ = GNUNET_TIME_relative_to_timestamp (pg->legal_reserve_expiration_time);
+ enum GNUNET_DB_QueryStatus qs;
+ bool need_update;
+
+ for (unsigned int i = 0; i<reserves_length; i++)
{
- enum GNUNET_DB_QueryStatus qs2;
- enum GNUNET_DB_QueryStatus qs3;
- struct TALER_PaytoHashP h_payto;
+ const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i];
+
+ TALER_payto_hash (reserve->sender_account_details,
+ &h_paytos[i]);
+ notify_s[i] = compute_notify_on_reserve (reserve->reserve_pub);
+ reserve_pubs[i] = *reserve->reserve_pub;
+ balances[i] = *reserve->balance;
+ execution_times[i] = reserve->execution_time;
+ sender_account_details[i] = reserve->sender_account_details;
+ exchange_account_names[i] = reserve->exchange_account_name;
+ wire_references[i] = reserve->wire_reference;
+ }
- qs3 = TEH_PG_setup_wire_target (pg,
- sender_account_details,
- &h_payto);
- if (qs3 < 0)
- return qs3;
- /* We do not have the UUID, so insert by public key */
+ /* NOTE: kind-of pointless to explicitly start a transaction here... */
+ if (GNUNET_OK !=
+ TEH_PG_preflight (pg))
+ {
+ GNUNET_break (0);
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
+ goto finished;
+ }
+ if (GNUNET_OK !=
+ TEH_PG_start_read_committed (pg,
+ "READ_COMMITED"))
+ {
+ GNUNET_break (0);
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
+ goto finished;
+ }
+ PREPARE (pg,
+ "reserves_insert_with_array",
+ "SELECT"
+ " transaction_duplicate"
+ ",ruuid"
+ " FROM exchange_do_array_reserves_insert"
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);");
+ {
struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&reserve.pub),
- GNUNET_PQ_query_param_uint64 (&wire_ref),
- TALER_PQ_query_param_amount (balance),
- GNUNET_PQ_query_param_string (exchange_account_section),
- GNUNET_PQ_query_param_auto_from_type (&h_payto),
- GNUNET_PQ_query_param_timestamp (&execution_time),
+ GNUNET_PQ_query_param_timestamp (&gc),
+ GNUNET_PQ_query_param_timestamp (&reserve_expiration),
+ GNUNET_PQ_query_param_array_auto_from_type (reserves_length,
+ reserve_pubs,
+ pg->conn),
+ GNUNET_PQ_query_param_array_uint64 (reserves_length,
+ wire_references,
+ pg->conn),
+ TALER_PQ_query_param_array_amount (
+ reserves_length,
+ balances,
+ pg->conn),
+ GNUNET_PQ_query_param_array_ptrs_string (
+ reserves_length,
+ (const char **) exchange_account_names,
+ pg->conn),
+ GNUNET_PQ_query_param_array_timestamp (
+ reserves_length,
+ execution_times,
+ pg->conn),
+ GNUNET_PQ_query_param_array_auto_from_type (
+ reserves_length,
+ h_paytos,
+ pg->conn),
+ GNUNET_PQ_query_param_array_ptrs_string (
+ reserves_length,
+ (const char **) sender_account_details,
+ pg->conn),
+ GNUNET_PQ_query_param_array_ptrs_string (
+ reserves_length,
+ (const char **) notify_s,
+ pg->conn),
GNUNET_PQ_query_param_end
};
+ struct Context ctx = {
+ .reserve_uuids = reserve_uuids,
+ .transaction_duplicates = transaction_duplicates,
+ .conflicts = conflicts,
+ .needs_update = false,
+ .status = GNUNET_OK
+ };
- PREPARE (pg,
- "reserves_in_add_transaction",
- "INSERT INTO reserves_in "
- "(reserve_pub"
- ",wire_reference"
- ",credit_val"
- ",credit_frac"
- ",exchange_account_section"
- ",wire_source_h_payto"
- ",execution_date"
- ") VALUES ($1, $2, $3, $4, $5, $6, $7)"
- " ON CONFLICT DO NOTHING;");
- qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "reserves_in_add_transaction",
- params);
- /* qs2 could be 0 as statement used 'ON CONFLICT DO NOTHING' */
- if (0 >= qs2)
+ qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ "reserves_insert_with_array",
+ params,
+ &helper_cb,
+ &ctx);
+ GNUNET_PQ_cleanup_query_params_closures (params);
+ if ( (qs < 0) ||
+ (GNUNET_OK != ctx.status) )
{
- if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs2) &&
- (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs1) )
- {
- /* Conflict for the transaction, but the reserve was
- just now created, that should be impossible. */
- GNUNET_break (0); /* should be impossible: reserve was fresh,
- but transaction already known */
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- /* Transaction was already known or error. We are finished. */
- return qs2;
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to insert into reserves (%d)\n",
+ qs);
+ goto finished;
}
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs1)
- {
- /* New reserve, we are finished */
- notify_on_reserve (pg,
- reserve_pub);
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ need_update = ctx.needs_update;
}
- /* we were wrong with our optimistic assumption:
- reserve did already exist, need to do an update instead */
{
- /* We need to move away from 'read committed' to serializable.
- Also, we know that it should be safe to commit at this point.
- (We are only run in a larger transaction for performance.) */
enum GNUNET_DB_QueryStatus cs;
- cs = TEH_PG_commit(pg);
+ cs = TEH_PG_commit (pg);
if (cs < 0)
- return cs;
- if (GNUNET_OK !=
- TEH_PG_start (pg,
- "reserve-update-serializable"))
{
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to commit\n");
+ qs = cs;
+ goto finished;
}
}
+
+ for (unsigned int i = 0; i<reserves_length; i++)
{
- enum GNUNET_DB_QueryStatus reserve_exists;
+ if (transaction_duplicates[i])
+ dups++;
+ results[i] = transaction_duplicates[i]
+ ? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
+ : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ }
- reserve_exists = TEH_PG_reserves_get (pg,
- &reserve);
- switch (reserve_exists)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- return reserve_exists;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- return reserve_exists;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- /* First we got a conflict, but then we cannot select? Very strange. */
- GNUNET_break (0);
- return GNUNET_DB_STATUS_SOFT_ERROR;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- /* continued below */
- break;
- }
+ if (! need_update)
+ {
+ qs = reserves_length;
+ goto finished;
}
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Reserve update needed for some reserves in the batch\n");
+ PREPARE (pg,
+ "reserves_update",
+ "SELECT"
+ " out_duplicate AS duplicate "
+ "FROM exchange_do_batch_reserves_update"
+ " ($1,$2,$3,$4,$5,$6,$7);");
+ if (GNUNET_OK !=
+ TEH_PG_start (pg,
+ "reserve-insert-continued"))
{
- struct TALER_EXCHANGEDB_Reserve updated_reserve;
- enum GNUNET_DB_QueryStatus qs3;
+ GNUNET_break (0);
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
+ goto finished;
+ }
- /* If the reserve already existed, we need to still update the
- balance; we do this after checking for duplication, as
- otherwise we might have to actually pay the cost to roll this
- back for duplicate transactions; like this, we should virtually
- never actually have to rollback anything. */
- updated_reserve.pub = reserve.pub;
- if (0 >
- TALER_amount_add (&updated_reserve.balance,
- &reserve.balance,
- balance))
- {
- /* currency overflow or incompatible currency */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Attempt to deposit incompatible amount into reserve\n");
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- updated_reserve.expiry = GNUNET_TIME_timestamp_max (expiry,
- reserve.expiry);
- updated_reserve.gc = GNUNET_TIME_timestamp_max (gc,
- reserve.gc);
- qs3 = TEH_PG_reserves_update (pg,
- &updated_reserve);
- switch (qs3)
+ for (unsigned int i = 0; i<reserves_length; i++)
+ {
+ if (transaction_duplicates[i])
+ continue;
+ if (! conflicts[i])
+ continue;
{
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- return qs3;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- return qs3;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- /* How can the UPDATE not work here? Very strange. */
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- /* continued below */
- break;
+ bool duplicate;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&reserve_pubs[i]),
+ GNUNET_PQ_query_param_timestamp (&reserve_expiration),
+ GNUNET_PQ_query_param_uint64 (&wire_references[i]),
+ TALER_PQ_query_param_amount (pg->conn,
+ &balances[i]),
+ GNUNET_PQ_query_param_string (exchange_account_names[i]),
+ GNUNET_PQ_query_param_auto_from_type (&h_paytos[i]),
+ GNUNET_PQ_query_param_string (notify_s[i]),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("duplicate",
+ &duplicate),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "reserves_update",
+ params,
+ rs);
+ if (qs < 0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to update reserves (%d)\n",
+ qs);
+ results[i] = qs;
+ goto finished;
+ }
+ results[i] = duplicate
+ ? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
+ : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
}
- notify_on_reserve (pg,
- reserve_pub);
- /* Go back to original transaction mode */
{
enum GNUNET_DB_QueryStatus cs;
cs = TEH_PG_commit (pg);
if (cs < 0)
- return cs;
- if (GNUNET_OK !=
- TEH_PG_start_read_committed (pg,
- "reserve-insert-continued"))
{
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to commit\n");
+ qs = cs;
+ goto finished;
}
}
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+finished:
+ for (unsigned int i = 0; i<reserves_length; i++)
+ GNUNET_free (notify_s[i]);
+ if (qs < 0)
+ return qs;
+ GNUNET_PQ_event_do_poll (pg->conn);
+ if (0 != dups)
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "%u/%u duplicates among incoming transactions. Try increasing WIREWATCH_IDLE_SLEEP_INTERVAL in the [exchange] configuration section (if this happens a lot).\n",
+ dups,
+ reserves_length);
+ return qs;
}
diff --git a/src/exchangedb/pg_reserves_in_insert.h b/src/exchangedb/pg_reserves_in_insert.h
index 854019386..938df3adb 100644
--- a/src/exchangedb/pg_reserves_in_insert.h
+++ b/src/exchangedb/pg_reserves_in_insert.h
@@ -24,28 +24,24 @@
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+
+
/**
* Insert an incoming transaction into reserves. New reserves are also
- * created through this function. Started within the scope of an ongoing
- * transaction.
+ * created through this function. Runs its own transaction(s).
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param reserve_pub public key of the reserve
- * @param balance the amount that has to be added to the reserve
- * @param execution_time when was the amount added
- * @param sender_account_details account information for the sender (payto://-URL)
- * @param exchange_account_section name of the section in the configuration for the exchange's
- * account into which the deposit was made
- * @param wire_ref unique reference identifying the wire transfer
+ * @param reserves array of reserves to insert
+ * @param reserves_length length of the @a reserves array
+ * @param[out] results set to query status per reserve, must be of length @a reserves_length
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
-TEH_PG_reserves_in_insert (void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_Amount *balance,
- struct GNUNET_TIME_Timestamp execution_time,
- const char *sender_account_details,
- const char *exchange_account_section,
- uint64_t wire_ref);
+TEH_PG_reserves_in_insert (
+ void *cls,
+ const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
+ unsigned int reserves_length,
+ enum GNUNET_DB_QueryStatus *results);
+
#endif
diff --git a/src/exchangedb/pg_reserves_update.c b/src/exchangedb/pg_reserves_update.c
index 4a8861020..bfd32c6ce 100644
--- a/src/exchangedb/pg_reserves_update.c
+++ b/src/exchangedb/pg_reserves_update.c
@@ -27,13 +27,14 @@
enum GNUNET_DB_QueryStatus
TEH_PG_reserves_update (void *cls,
- const struct TALER_EXCHANGEDB_Reserve *reserve)
+ const struct TALER_EXCHANGEDB_Reserve *reserve)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_timestamp (&reserve->expiry),
GNUNET_PQ_query_param_timestamp (&reserve->gc),
- TALER_PQ_query_param_amount (&reserve->balance),
+ TALER_PQ_query_param_amount (pg->conn,
+ &reserve->balance),
GNUNET_PQ_query_param_auto_from_type (&reserve->pub),
GNUNET_PQ_query_param_end
};
@@ -44,9 +45,8 @@ TEH_PG_reserves_update (void *cls,
" SET"
" expiration_date=$1"
",gc_date=$2"
- ",current_balance_val=$3"
- ",current_balance_frac=$4"
- " WHERE reserve_pub=$5;");
+ ",current_balance=$3"
+ " WHERE reserve_pub=$4;");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"reserve_update",
params);
diff --git a/src/exchangedb/pg_rollback.c b/src/exchangedb/pg_rollback.c
index 6b200b55d..3610487f3 100644
--- a/src/exchangedb/pg_rollback.c
+++ b/src/exchangedb/pg_rollback.c
@@ -48,5 +48,3 @@ TEH_PG_rollback (void *cls)
es));
pg->transaction_name = NULL;
}
-
-
diff --git a/src/exchangedb/pg_select_account_merges_above_serial_id.c b/src/exchangedb/pg_select_account_merges_above_serial_id.c
index 95c2f93a1..6c3c81121 100644
--- a/src/exchangedb/pg_select_account_merges_above_serial_id.c
+++ b/src/exchangedb/pg_select_account_merges_above_serial_id.c
@@ -168,12 +168,10 @@ TEH_PG_select_account_merges_above_serial_id (
",am.purse_pub"
",pr.h_contract_terms"
",pr.purse_expiration"
- ",pr.amount_with_fee_val"
- ",pr.amount_with_fee_frac"
+ ",pr.amount_with_fee"
",pr.age_limit"
",pr.flags"
- ",pr.purse_fee_val"
- ",pr.purse_fee_frac"
+ ",pr.purse_fee"
",pm.merge_timestamp"
",am.reserve_sig"
" FROM account_merges am"
diff --git a/src/exchangedb/pg_select_aggregation_amounts_for_kyc_check.c b/src/exchangedb/pg_select_aggregation_amounts_for_kyc_check.c
index abddab52f..0d5d0ee25 100644
--- a/src/exchangedb/pg_select_aggregation_amounts_for_kyc_check.c
+++ b/src/exchangedb/pg_select_aggregation_amounts_for_kyc_check.c
@@ -26,8 +26,6 @@
#include "pg_helper.h"
-
-
/**
* Closure for #get_kyc_amounts_cb().
*/
@@ -112,7 +110,6 @@ get_kyc_amounts_cb (void *cls,
}
-
enum GNUNET_DB_QueryStatus
TEH_PG_select_aggregation_amounts_for_kyc_check (
void *cls,
@@ -138,8 +135,7 @@ TEH_PG_select_aggregation_amounts_for_kyc_check (
PREPARE (pg,
"select_kyc_relevant_aggregation_events",
"SELECT"
- " amount_val"
- ",amount_frac"
+ " amount"
",execution_date AS date"
" FROM wire_out"
" WHERE wire_target_h_payto=$1"
diff --git a/src/exchangedb/pg_select_aggregation_amounts_for_kyc_check.h b/src/exchangedb/pg_select_aggregation_amounts_for_kyc_check.h
index 87fc8677a..b91740581 100644
--- a/src/exchangedb/pg_select_aggregation_amounts_for_kyc_check.h
+++ b/src/exchangedb/pg_select_aggregation_amounts_for_kyc_check.h
@@ -44,4 +44,5 @@ TEH_PG_select_aggregation_amounts_for_kyc_check (
struct GNUNET_TIME_Absolute time_limit,
TALER_EXCHANGEDB_KycAmountCallback kac,
void *kac_cls);
+
#endif
diff --git a/src/exchangedb/pg_select_aggregation_transient.c b/src/exchangedb/pg_select_aggregation_transient.c
index f3e39c532..f9b6193ed 100644
--- a/src/exchangedb/pg_select_aggregation_transient.c
+++ b/src/exchangedb/pg_select_aggregation_transient.c
@@ -49,12 +49,11 @@ TEH_PG_select_aggregation_transient (
wtid),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_select_aggregation_transient() */
+ /* Used in #postgres_select_aggregation_transient() */
PREPARE (pg,
"select_aggregation_transient",
"SELECT"
- " amount_val"
- " ,amount_frac"
+ " amount"
" ,wtid_raw"
" FROM aggregation_transient"
" WHERE wire_target_h_payto=$1"
diff --git a/src/exchangedb/pg_select_aggregations_above_serial.c b/src/exchangedb/pg_select_aggregations_above_serial.c
new file mode 100644
index 000000000..52d202702
--- /dev/null
+++ b/src/exchangedb/pg_select_aggregations_above_serial.c
@@ -0,0 +1,137 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_select_aggregations_above_serial.c
+ * @brief Implementation of the select_aggregations_above_serial function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_select_aggregations_above_serial.h"
+#include "pg_helper.h"
+
+/**
+ * Closure for #aggregation_serial_helper_cb().
+ */
+struct AggregationSerialContext
+{
+
+ /**
+ * Callback to call.
+ */
+ TALER_EXCHANGEDB_AggregationCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on hard errors.
+ */
+ enum GNUNET_GenericReturnValue status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct AggregationSerialContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+aggregation_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct AggregationSerialContext *dsc = cls;
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ uint64_t tracking_rowid;
+ uint64_t batch_deposit_serial_id;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id",
+ &tracking_rowid),
+ GNUNET_PQ_result_spec_uint64 ("batch_deposit_serial_id",
+ &batch_deposit_serial_id),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ dsc->status = GNUNET_SYSERR;
+ return;
+ }
+ dsc->cb (dsc->cb_cls,
+ tracking_rowid,
+ batch_deposit_serial_id);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_aggregations_above_serial (
+ void *cls,
+ uint64_t min_tracking_serial_id,
+ TALER_EXCHANGEDB_AggregationCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&min_tracking_serial_id),
+ GNUNET_PQ_query_param_end
+ };
+ struct AggregationSerialContext asc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .pg = pg,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ /* Fetch aggregations with rowid '\geq' the given parameter */
+ PREPARE (pg,
+ "select_aggregations_above_serial",
+ "SELECT"
+ " aggregation_serial_id"
+ ",batch_deposit_serial_id"
+ " FROM aggregation_tracking"
+ " WHERE aggregation_serial_id>=$1"
+ " ORDER BY aggregation_serial_id ASC;");
+ qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ "select_aggregations_above_serial",
+ params,
+ &aggregation_serial_helper_cb,
+ &asc);
+ if (GNUNET_OK != asc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
diff --git a/src/exchangedb/pg_select_aggregations_above_serial.h b/src/exchangedb/pg_select_aggregations_above_serial.h
new file mode 100644
index 000000000..2883b19f2
--- /dev/null
+++ b/src/exchangedb/pg_select_aggregations_above_serial.h
@@ -0,0 +1,47 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_select_aggregations_above_serial.h
+ * @brief implementation of the select_aggregations_above_serial function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_SELECT_AGGREGATIONS_ABOVE_SERIAL_H
+#define PG_SELECT_AGGREGATIONS_ABOVE_SERIAL_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Select all aggregation tracking IDs in the database
+ * above a given @a min_tracking_serial_id.
+ *
+ * @param cls closure
+ * @param min_tracking_serial_id only return entries strictly above this row (and in order)
+ * @param cb function to call on all such aggregations
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_aggregations_above_serial (
+ void *cls,
+ uint64_t min_tracking_serial_id,
+ TALER_EXCHANGEDB_AggregationCallback cb,
+ void *cb_cls);
+
+
+#endif
diff --git a/src/exchangedb/pg_select_aml_history.c b/src/exchangedb/pg_select_aml_history.c
new file mode 100644
index 000000000..0461e0d9b
--- /dev/null
+++ b/src/exchangedb/pg_select_aml_history.c
@@ -0,0 +1,157 @@
+/*
+ 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 exchangedb/pg_select_aml_history.c
+ * @brief Implementation of the select_aml_history function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_select_aml_history.h"
+#include "pg_helper.h"
+
+
+/**
+ * Closure for #handle_aml_result.
+ */
+struct AmlHistoryResultContext
+{
+ /**
+ * Function to call on each result.
+ */
+ TALER_EXCHANGEDB_AmlHistoryCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Set to #GNUNET_SYSERR on serious errors.
+ */
+ enum GNUNET_GenericReturnValue status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results. Helper function
+ * for #TEH_PG_select_aml_history().
+ *
+ * @param cls closure of type `struct AmlHistoryResultContext *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+handle_aml_result (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct AmlHistoryResultContext *ctx = cls;
+ struct PostgresClosure *pg = ctx->pg;
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct TALER_Amount new_threshold;
+ uint32_t ns;
+ struct GNUNET_TIME_Timestamp decision_time;
+ char *justification;
+ struct TALER_AmlOfficerPublicKeyP decider_pub;
+ struct TALER_AmlOfficerSignatureP decider_sig;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC_AMOUNT ("new_threshold",
+ &new_threshold),
+ GNUNET_PQ_result_spec_uint32 ("new_status",
+ &ns),
+ GNUNET_PQ_result_spec_timestamp ("decision_time",
+ &decision_time),
+ GNUNET_PQ_result_spec_string ("justification",
+ &justification),
+ GNUNET_PQ_result_spec_auto_from_type ("decider_pub",
+ &decider_pub),
+ GNUNET_PQ_result_spec_auto_from_type ("decider_sig",
+ &decider_sig),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->status = GNUNET_SYSERR;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ &new_threshold,
+ (enum TALER_AmlDecisionState) ns,
+ decision_time,
+ justification,
+ &decider_pub,
+ &decider_sig);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_aml_history (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_AmlHistoryCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_end
+ };
+ struct AmlHistoryResultContext ctx = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .pg = pg,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (pg,
+ "lookup_aml_history",
+ "SELECT"
+ " new_threshold"
+ ",new_status"
+ ",decision_time"
+ ",justification"
+ ",decider_pub"
+ ",decider_sig"
+ " FROM aml_history"
+ " WHERE h_payto=$1;");
+ qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ "lookup_aml_history",
+ params,
+ &handle_aml_result,
+ &ctx);
+ if (GNUNET_OK != ctx.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
diff --git a/src/exchangedb/pg_select_history_requests_above_serial_id.h b/src/exchangedb/pg_select_aml_history.h
index b16efdce1..78569947f 100644
--- a/src/exchangedb/pg_select_history_requests_above_serial_id.h
+++ b/src/exchangedb/pg_select_aml_history.h
@@ -14,31 +14,33 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file exchangedb/pg_select_history_requests_above_serial_id.h
- * @brief implementation of the select_history_requests_above_serial_id function for Postgres
+ * @file exchangedb/pg_select_aml_history.h
+ * @brief implementation of the select_aml_history function for Postgres
* @author Christian Grothoff
*/
-#ifndef PG_SELECT_HISTORY_REQUESTS_ABOVE_SERIAL_ID_H
-#define PG_SELECT_HISTORY_REQUESTS_ABOVE_SERIAL_ID_H
+#ifndef PG_SELECT_AML_HISTORY_H
+#define PG_SELECT_AML_HISTORY_H
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+
+
/**
- * Select history requests above @a serial_id in monotonically increasing
- * order.
+ * Lookup AML decision history for a particular account.
*
* @param cls closure
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call on each result
+ * @param h_payto which account should we return the AML decision history for
+ * @param cb callback to invoke on each match
* @param cb_cls closure for @a cb
- * @return transaction status code
+ * @return database transaction status
*/
enum GNUNET_DB_QueryStatus
-TEH_PG_select_history_requests_above_serial_id (
+TEH_PG_select_aml_history (
void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_HistoryRequestCallback cb,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_AmlHistoryCallback cb,
void *cb_cls);
+
#endif
diff --git a/src/exchangedb/pg_select_aml_process.c b/src/exchangedb/pg_select_aml_process.c
new file mode 100644
index 000000000..c34cae4bb
--- /dev/null
+++ b/src/exchangedb/pg_select_aml_process.c
@@ -0,0 +1,170 @@
+/*
+ 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 exchangedb/pg_select_aml_process.c
+ * @brief Implementation of the select_aml_process function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_select_aml_process.h"
+#include "pg_helper.h"
+
+
+/**
+ * Closure for #handle_aml_result.
+ */
+struct AmlProcessResultContext
+{
+ /**
+ * Function to call on each result.
+ */
+ TALER_EXCHANGEDB_AmlStatusCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Set to #GNUNET_SYSERR on serious errors.
+ */
+ enum GNUNET_GenericReturnValue status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results. Helper function
+ * for #TEH_PG_select_aml_process().
+ *
+ * @param cls closure of type `struct AmlProcessResultContext *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+handle_aml_result (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct AmlProcessResultContext *ctx = cls;
+ struct PostgresClosure *pg = ctx->pg;
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct TALER_PaytoHashP h_payto;
+ struct TALER_Amount threshold;
+ uint64_t rowid;
+ uint32_t sv;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("aml_status_serial_id",
+ &rowid),
+ GNUNET_PQ_result_spec_auto_from_type ("h_payto",
+ &h_payto),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("threshold",
+ &threshold),
+ GNUNET_PQ_result_spec_uint32 ("status",
+ &sv),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->status = GNUNET_SYSERR;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ rowid,
+ &h_payto,
+ &threshold,
+ (enum TALER_AmlDecisionState) sv);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_aml_process (
+ void *cls,
+ enum TALER_AmlDecisionState decision,
+ uint64_t row_off,
+ uint64_t limit,
+ bool forward,
+ TALER_EXCHANGEDB_AmlStatusCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint32 (&decision),
+ GNUNET_PQ_query_param_uint64 (&row_off),
+ GNUNET_PQ_query_param_uint64 (&limit),
+ GNUNET_PQ_query_param_end
+ };
+ struct AmlProcessResultContext ctx = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .pg = pg,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+ const char *stmt = forward
+ ? "select_aml_process_inc"
+ : "select_aml_process_dec";
+
+ PREPARE (pg,
+ "select_aml_process_inc",
+ "SELECT"
+ " aml_status_serial_id"
+ ",h_payto"
+ ",threshold"
+ ",status"
+ " FROM aml_status"
+ " WHERE aml_status_serial_id > $2"
+ " AND status = $1"
+ " ORDER BY aml_status_serial_id ASC"
+ " LIMIT $3");
+ PREPARE (pg,
+ "select_aml_process_dec",
+ "SELECT"
+ " aml_status_serial_id"
+ ",h_payto"
+ ",threshold"
+ ",status"
+ " FROM aml_status"
+ " WHERE aml_status_serial_id < $2"
+ " AND status = $1"
+ " ORDER BY aml_status_serial_id DESC"
+ " LIMIT $3");
+ qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ stmt,
+ params,
+ &handle_aml_result,
+ &ctx);
+ if (GNUNET_OK != ctx.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
diff --git a/src/exchangedb/pg_select_aml_process.h b/src/exchangedb/pg_select_aml_process.h
new file mode 100644
index 000000000..648cace2e
--- /dev/null
+++ b/src/exchangedb/pg_select_aml_process.h
@@ -0,0 +1,52 @@
+/*
+ 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 exchangedb/pg_select_aml_process.h
+ * @brief implementation of the select_aml_process function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_SELECT_AML_PROCESS_H
+#define PG_SELECT_AML_PROCESS_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Lookup AML decisions that have a particular state.
+ *
+ * @param cls closure
+ * @param decision which decision states to filter by
+ * @param row_off offset to start from
+ * @param limit how many rows to return at most
+ * @param forward true to go forward in time, false to go backwards
+ * @param cb callback to invoke on each match
+ * @param cb_cls closure for @a cb
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_aml_process (
+ void *cls,
+ enum TALER_AmlDecisionState decision,
+ uint64_t row_off,
+ uint64_t limit,
+ bool forward,
+ TALER_EXCHANGEDB_AmlStatusCallback cb,
+ void *cb_cls);
+
+
+#endif
diff --git a/src/exchangedb/pg_select_aml_threshold.c b/src/exchangedb/pg_select_aml_threshold.c
new file mode 100644
index 000000000..23286f029
--- /dev/null
+++ b/src/exchangedb/pg_select_aml_threshold.c
@@ -0,0 +1,70 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_select_aml_threshold.c
+ * @brief Implementation of the select_aml_threshold function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_select_aml_threshold.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_aml_threshold (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ enum TALER_AmlDecisionState *decision,
+ struct TALER_EXCHANGEDB_KycStatus *kyc,
+ struct TALER_Amount *threshold)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_end
+ };
+ uint32_t status32 = TALER_AML_NORMAL;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC_AMOUNT ("threshold",
+ threshold),
+ GNUNET_PQ_result_spec_uint32 ("status",
+ &status32),
+ GNUNET_PQ_result_spec_uint64 ("kyc_requirement",
+ &kyc->requirement_row),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (pg,
+ "select_aml_threshold",
+ "SELECT"
+ " threshold"
+ ",status"
+ ",kyc_requirement"
+ " FROM aml_status"
+ " WHERE h_payto=$1;");
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "select_aml_threshold",
+ params,
+ rs);
+ *decision = (enum TALER_AmlDecisionState) status32;
+ kyc->ok = (TALER_AML_FROZEN != *decision)
+ || (0 != kyc->requirement_row);
+ return qs;
+}
diff --git a/src/exchangedb/pg_select_aml_threshold.h b/src/exchangedb/pg_select_aml_threshold.h
new file mode 100644
index 000000000..8f0e3bcfc
--- /dev/null
+++ b/src/exchangedb/pg_select_aml_threshold.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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 exchangedb/pg_select_aml_threshold.h
+ * @brief implementation of the select_aml_threshold function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_SELECT_AML_THRESHOLD_H
+#define PG_SELECT_AML_THRESHOLD_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Obtain the current AML threshold set for an account.
+ *
+ * @param cls closure
+ * @param h_payto account for which the AML threshold is stored
+ * @param[out] decision set to current AML decision
+ * @param[out] kyc set to KYC requirements imposed by AML, if any
+ * @param[out] threshold set to the existing threshold
+ * @return database transaction status, 0 if no threshold was set
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_aml_threshold (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ enum TALER_AmlDecisionState *decision,
+ struct TALER_EXCHANGEDB_KycStatus *kyc,
+ struct TALER_Amount *threshold);
+
+
+#endif
diff --git a/src/exchangedb/pg_select_auditor_denom_sig.c b/src/exchangedb/pg_select_auditor_denom_sig.c
index 90e0c5096..1dd6bd3d1 100644
--- a/src/exchangedb/pg_select_auditor_denom_sig.c
+++ b/src/exchangedb/pg_select_auditor_denom_sig.c
@@ -64,4 +64,3 @@ TEH_PG_select_auditor_denom_sig (
params,
rs);
}
-
diff --git a/src/exchangedb/pg_select_batch_deposits_missing_wire.c b/src/exchangedb/pg_select_batch_deposits_missing_wire.c
new file mode 100644
index 000000000..8f966326a
--- /dev/null
+++ b/src/exchangedb/pg_select_batch_deposits_missing_wire.c
@@ -0,0 +1,144 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-2023 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 exchangedb/pg_select_batch_deposits_missing_wire.c
+ * @brief Implementation of the select_batch_deposits_missing_wire function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_select_batch_deposits_missing_wire.h"
+#include "pg_helper.h"
+
+/**
+ * Closure for #missing_wire_cb().
+ */
+struct MissingWireContext
+{
+ /**
+ * Function to call per result.
+ */
+ TALER_EXCHANGEDB_WireMissingCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Set to #GNUNET_SYSERR on error.
+ */
+ enum GNUNET_GenericReturnValue status;
+};
+
+
+/**
+ * Invoke the callback for each result.
+ *
+ * @param cls a `struct MissingWireContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+missing_wire_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct MissingWireContext *mwc = cls;
+ struct PostgresClosure *pg = mwc->pg;
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ uint64_t batch_deposit_serial_id;
+ struct GNUNET_TIME_Timestamp deadline;
+ struct TALER_PaytoHashP wire_target_h_payto;
+ struct TALER_Amount total_amount;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("batch_deposit_serial_id",
+ &batch_deposit_serial_id),
+ GNUNET_PQ_result_spec_auto_from_type ("wire_target_h_payto",
+ &wire_target_h_payto),
+ GNUNET_PQ_result_spec_timestamp ("deadline",
+ &deadline),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("total_amount",
+ &total_amount),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ mwc->status = GNUNET_SYSERR;
+ return;
+ }
+ mwc->cb (mwc->cb_cls,
+ batch_deposit_serial_id,
+ &total_amount,
+ &wire_target_h_payto,
+ deadline);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_batch_deposits_missing_wire (
+ void *cls,
+ uint64_t min_batch_deposit_serial_id,
+ TALER_EXCHANGEDB_WireMissingCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&min_batch_deposit_serial_id),
+ GNUNET_PQ_query_param_end
+ };
+ struct MissingWireContext mwc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .pg = pg,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (pg,
+ "deposits_get_deposits_missing_wire",
+ "SELECT"
+ " batch_deposit_serial_id"
+ ",wire_target_h_payto"
+ ",deadline"
+ ",total_amount"
+ " FROM exchange_do_select_deposits_missing_wire"
+ " ($1);");
+ qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ "deposits_get_deposits_missing_wire",
+ params,
+ &missing_wire_cb,
+ &mwc);
+ if (GNUNET_OK != mwc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
diff --git a/src/exchangedb/pg_select_deposits_missing_wire.h b/src/exchangedb/pg_select_batch_deposits_missing_wire.h
index f702c2417..16f1d0cb3 100644
--- a/src/exchangedb/pg_select_deposits_missing_wire.h
+++ b/src/exchangedb/pg_select_batch_deposits_missing_wire.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -14,8 +14,8 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file exchangedb/pg_select_deposits_missing_wire.h
- * @brief implementation of the select_deposits_missing_wire function for Postgres
+ * @file exchangedb/pg_select_batch_deposits_missing_wire.h
+ * @brief implementation of the select_batch_deposits_missing_wire function for Postgres
* @author Christian Grothoff
*/
#ifndef PG_SELECT_DEPOSITS_MISSING_WIRE_H
@@ -25,22 +25,20 @@
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
/**
- * Select all of those deposits in the database for which we do
- * not have a wire transfer (or a refund) and which should have
- * been deposited between @a start_date and @a end_date.
+ * Select all of those batch deposits in the database
+ * above the given serial ID.
*
* @param cls closure
- * @param start_date lower bound on the requested wire execution date
- * @param end_date upper bound on the requested wire execution date
+ * @param min_batch_deposit_serial_id select all batch deposits above this ID
* @param cb function to call on all such deposits
* @param cb_cls closure for @a cb
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
-TEH_PG_select_deposits_missing_wire (void *cls,
- struct GNUNET_TIME_Timestamp start_date,
- struct GNUNET_TIME_Timestamp end_date,
- TALER_EXCHANGEDB_WireMissingCallback cb,
- void *cb_cls);
+TEH_PG_select_batch_deposits_missing_wire (
+ void *cls,
+ uint64_t min_batch_deposit_serial_id,
+ TALER_EXCHANGEDB_WireMissingCallback cb,
+ void *cb_cls);
#endif
diff --git a/src/exchangedb/pg_select_deposits_above_serial_id.c b/src/exchangedb/pg_select_coin_deposits_above_serial_id.c
index 52f96e5f4..000b908ed 100644
--- a/src/exchangedb/pg_select_deposits_above_serial_id.c
+++ b/src/exchangedb/pg_select_coin_deposits_above_serial_id.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -14,21 +14,21 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file exchangedb/pg_select_deposits_above_serial_id.c
- * @brief Implementation of the select_deposits_above_serial_id function for Postgres
+ * @file exchangedb/pg_select_coin_deposits_above_serial_id.c
+ * @brief Implementation of the select_coin_deposits_above_serial_id function for Postgres
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
-#include "pg_select_deposits_above_serial_id.h"
+#include "pg_select_coin_deposits_above_serial_id.h"
#include "pg_helper.h"
/**
* Closure for #deposit_serial_helper_cb().
*/
-struct DepositSerialContext
+struct CoinDepositSerialContext
{
/**
@@ -57,16 +57,16 @@ struct DepositSerialContext
* Helper function to be called with the results of a SELECT statement
* that has returned @a num_results results.
*
- * @param cls closure of type `struct DepositSerialContext`
+ * @param cls closure of type `struct CoinDepositSerialContext`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
-deposit_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
+coin_deposit_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
{
- struct DepositSerialContext *dsc = cls;
+ struct CoinDepositSerialContext *dsc = cls;
struct PostgresClosure *pg = dsc->pg;
for (unsigned int i = 0; i<num_results; i++)
@@ -93,6 +93,10 @@ deposit_serial_helper_cb (void *cls,
GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
&deposit.coin.h_age_commitment),
&deposit.coin.no_age_commitment),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("wallet_data_hash",
+ &deposit.wallet_data_hash),
+ &deposit.no_wallet_data_hash),
GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
&deposit.csig),
GNUNET_PQ_result_spec_timestamp ("refund_deadline",
@@ -107,7 +111,7 @@ deposit_serial_helper_cb (void *cls,
&deposit.receiver_wire_account),
GNUNET_PQ_result_spec_bool ("done",
&done),
- GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
+ GNUNET_PQ_result_spec_uint64 ("coin_deposit_serial_id",
&rowid),
GNUNET_PQ_result_spec_end
};
@@ -137,8 +141,9 @@ deposit_serial_helper_cb (void *cls,
}
}
+
enum GNUNET_DB_QueryStatus
-TEH_PG_select_deposits_above_serial_id (
+TEH_PG_select_coin_deposits_above_serial_id (
void *cls,
uint64_t serial_id,
TALER_EXCHANGEDB_DepositCallback cb,
@@ -149,7 +154,7 @@ TEH_PG_select_deposits_above_serial_id (
GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
- struct DepositSerialContext dsc = {
+ struct CoinDepositSerialContext dsc = {
.cb = cb,
.cb_cls = cb_cls,
.pg = pg,
@@ -157,39 +162,41 @@ TEH_PG_select_deposits_above_serial_id (
};
enum GNUNET_DB_QueryStatus qs;
- /* Fetch deposits with rowid '\geq' the given parameter */
+ /* Fetch deposits with rowid '\geq' the given parameter */
PREPARE (pg,
- "audit_get_deposits_incr",
+ "audit_get_coin_deposits_incr",
"SELECT"
- " amount_with_fee_val"
- ",amount_with_fee_frac"
- ",wallet_timestamp"
- ",exchange_timestamp"
- ",merchant_pub"
+ " cdep.amount_with_fee"
+ ",bdep.wallet_timestamp"
+ ",bdep.exchange_timestamp"
+ ",bdep.merchant_pub"
+ ",bdep.wallet_data_hash"
",denom.denom_pub"
",kc.coin_pub"
",kc.age_commitment_hash"
- ",coin_sig"
- ",refund_deadline"
- ",wire_deadline"
- ",h_contract_terms"
- ",wire_salt"
- ",payto_uri AS receiver_wire_account"
- ",done"
- ",deposit_serial_id"
- " FROM deposits"
- " JOIN wire_targets USING (wire_target_h_payto)"
- " JOIN known_coins kc USING (coin_pub)"
- " JOIN denominations denom USING (denominations_serial)"
- " WHERE ("
- " (deposit_serial_id>=$1)"
- " )"
- " ORDER BY deposit_serial_id ASC;");
-
+ ",cdep.coin_sig"
+ ",bdep.refund_deadline"
+ ",bdep.wire_deadline"
+ ",bdep.h_contract_terms"
+ ",bdep.wire_salt"
+ ",wt.payto_uri AS receiver_wire_account"
+ ",bdep.done"
+ ",cdep.coin_deposit_serial_id"
+ " FROM coin_deposits cdep"
+ " JOIN batch_deposits bdep"
+ " USING (batch_deposit_serial_id)"
+ " JOIN wire_targets wt"
+ " USING (wire_target_h_payto)"
+ " JOIN known_coins kc"
+ " USING (coin_pub)"
+ " JOIN denominations denom"
+ " USING (denominations_serial)"
+ " WHERE (coin_deposit_serial_id>=$1)"
+ " ORDER BY coin_deposit_serial_id ASC;");
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_deposits_incr",
+ "audit_get_coin_deposits_incr",
params,
- &deposit_serial_helper_cb,
+ &coin_deposit_serial_helper_cb,
&dsc);
if (GNUNET_OK != dsc.status)
return GNUNET_DB_STATUS_HARD_ERROR;
diff --git a/src/exchangedb/pg_select_deposits_above_serial_id.h b/src/exchangedb/pg_select_coin_deposits_above_serial_id.h
index e29937e08..5202336a4 100644
--- a/src/exchangedb/pg_select_deposits_above_serial_id.h
+++ b/src/exchangedb/pg_select_coin_deposits_above_serial_id.h
@@ -14,8 +14,8 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file exchangedb/pg_select_deposits_above_serial_id.h
- * @brief implementation of the select_deposits_above_serial_id function for Postgres
+ * @file exchangedb/pg_select_coin_deposits_above_serial_id.h
+ * @brief implementation of the select_coin_deposits_above_serial_id function for Postgres
* @author Christian Grothoff
*/
#ifndef PG_SELECT_DEPOSITS_ABOVE_SERIAL_ID_H
@@ -35,7 +35,7 @@
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
-TEH_PG_select_deposits_above_serial_id (
+TEH_PG_select_coin_deposits_above_serial_id (
void *cls,
uint64_t serial_id,
TALER_EXCHANGEDB_DepositCallback cb,
diff --git a/src/exchangedb/pg_select_contract.c b/src/exchangedb/pg_select_contract.c
index 4f4a525bd..3e6bf22bc 100644
--- a/src/exchangedb/pg_select_contract.c
+++ b/src/exchangedb/pg_select_contract.c
@@ -27,11 +27,11 @@
enum GNUNET_DB_QueryStatus
TEH_PG_select_contract (void *cls,
- const struct TALER_ContractDiffiePublicP *pub_ckey,
- struct TALER_PurseContractPublicKeyP *purse_pub,
- struct TALER_PurseContractSignatureP *econtract_sig,
- size_t *econtract_size,
- void **econtract)
+ const struct TALER_ContractDiffiePublicP *pub_ckey,
+ struct TALER_PurseContractPublicKeyP *purse_pub,
+ struct TALER_PurseContractSignatureP *econtract_sig,
+ size_t *econtract_size,
+ void **econtract)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -49,15 +49,15 @@ TEH_PG_select_contract (void *cls,
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_select_contract */
+ /* Used in #postgres_select_contract */
PREPARE (pg,
- "select_contract",
- "SELECT "
- " purse_pub"
- ",e_contract"
- ",contract_sig"
- " FROM contracts"
- " WHERE pub_ckey=$1;");
+ "select_contract",
+ "SELECT "
+ " purse_pub"
+ ",e_contract"
+ ",contract_sig"
+ " FROM contracts"
+ " WHERE pub_ckey=$1;");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"select_contract",
params,
diff --git a/src/exchangedb/pg_select_contract.h b/src/exchangedb/pg_select_contract.h
index a503c8da9..747a82753 100644
--- a/src/exchangedb/pg_select_contract.h
+++ b/src/exchangedb/pg_select_contract.h
@@ -38,10 +38,10 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_select_contract (void *cls,
- const struct TALER_ContractDiffiePublicP *pub_ckey,
- struct TALER_PurseContractPublicKeyP *purse_pub,
- struct TALER_PurseContractSignatureP *econtract_sig,
- size_t *econtract_size,
+ const struct TALER_ContractDiffiePublicP *pub_ckey,
+ struct TALER_PurseContractPublicKeyP *purse_pub,
+ struct TALER_PurseContractSignatureP *econtract_sig,
+ size_t *econtract_size,
void **econtract);
#endif
diff --git a/src/exchangedb/pg_select_contract_by_purse.c b/src/exchangedb/pg_select_contract_by_purse.c
index aeeb56d48..8d29b3954 100644
--- a/src/exchangedb/pg_select_contract_by_purse.c
+++ b/src/exchangedb/pg_select_contract_by_purse.c
@@ -46,7 +46,7 @@ TEH_PG_select_contract_by_purse (
&econtract->econtract_size),
GNUNET_PQ_result_spec_end
};
- /* Used in #postgres_select_contract_by_purse */
+ /* Used in #postgres_select_contract_by_purse */
PREPARE (pg,
"select_contract_by_purse",
"SELECT "
diff --git a/src/exchangedb/pg_select_deposits_missing_wire.c b/src/exchangedb/pg_select_deposits_missing_wire.c
deleted file mode 100644
index 2a260a369..000000000
--- a/src/exchangedb/pg_select_deposits_missing_wire.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- 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 exchangedb/pg_select_deposits_missing_wire.c
- * @brief Implementation of the select_deposits_missing_wire function for Postgres
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "taler_error_codes.h"
-#include "taler_dbevents.h"
-#include "taler_pq_lib.h"
-#include "pg_select_deposits_missing_wire.h"
-#include "pg_helper.h"
-
-/**
- * Closure for #missing_wire_cb().
- */
-struct MissingWireContext
-{
- /**
- * Function to call per result.
- */
- TALER_EXCHANGEDB_WireMissingCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Set to #GNUNET_SYSERR on error.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Invoke the callback for each result.
- *
- * @param cls a `struct MissingWireContext *`
- * @param result SQL result
- * @param num_results number of rows in @a result
- */
-static void
-missing_wire_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct MissingWireContext *mwc = cls;
- struct PostgresClosure *pg = mwc->pg;
-
- while (0 < num_results)
- {
- uint64_t rowid;
- struct TALER_CoinSpendPublicKeyP coin_pub;
- struct TALER_Amount amount;
- char *payto_uri;
- struct GNUNET_TIME_Timestamp deadline;
- bool done;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
- &rowid),
- GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &coin_pub),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &amount),
- GNUNET_PQ_result_spec_string ("payto_uri",
- &payto_uri),
- GNUNET_PQ_result_spec_timestamp ("wire_deadline",
- &deadline),
- GNUNET_PQ_result_spec_bool ("done",
- &done),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- --num_results))
- {
- GNUNET_break (0);
- mwc->status = GNUNET_SYSERR;
- return;
- }
- mwc->cb (mwc->cb_cls,
- rowid,
- &coin_pub,
- &amount,
- payto_uri,
- deadline,
- done);
- GNUNET_PQ_cleanup_result (rs);
- }
-}
-
-
-enum GNUNET_DB_QueryStatus
-TEH_PG_select_deposits_missing_wire (void *cls,
- struct GNUNET_TIME_Timestamp start_date,
- struct GNUNET_TIME_Timestamp end_date,
- TALER_EXCHANGEDB_WireMissingCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_timestamp (&start_date),
- GNUNET_PQ_query_param_timestamp (&end_date),
- GNUNET_PQ_query_param_end
- };
- struct MissingWireContext mwc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- /* Used in #postgres_select_deposits_missing_wire */
- // FIXME: used by the auditor; can probably be done
- // smarter by checking if 'done' or 'blocked'
- // are set correctly when going over deposits, instead
- // of JOINing with refunds.
- PREPARE (pg,
- "deposits_get_overdue",
- "SELECT"
- " deposit_serial_id"
- ",coin_pub"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",payto_uri"
- ",wire_deadline"
- ",done"
- " FROM deposits d"
- " JOIN known_coins"
- " USING (coin_pub)"
- " JOIN wire_targets"
- " USING (wire_target_h_payto)"
- " WHERE wire_deadline >= $1"
- " AND wire_deadline < $2"
- " AND NOT (EXISTS (SELECT 1"
- " FROM refunds r"
- " WHERE (r.coin_pub = d.coin_pub) AND (r.deposit_serial_id = d.deposit_serial_id))"
- " OR EXISTS (SELECT 1"
- " FROM aggregation_tracking"
- " WHERE (aggregation_tracking.deposit_serial_id = d.deposit_serial_id)))"
- " ORDER BY wire_deadline ASC");
-
-
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "deposits_get_overdue",
- params,
- &missing_wire_cb,
- &mwc);
- if (GNUNET_OK != mwc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
diff --git a/src/exchangedb/pg_select_history_requests_above_serial_id.c b/src/exchangedb/pg_select_history_requests_above_serial_id.c
deleted file mode 100644
index 81e038114..000000000
--- a/src/exchangedb/pg_select_history_requests_above_serial_id.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- 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 exchangedb/pg_select_history_requests_above_serial_id.c
- * @brief Implementation of the select_history_requests_above_serial_id function for Postgres
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "taler_error_codes.h"
-#include "taler_dbevents.h"
-#include "taler_pq_lib.h"
-#include "pg_select_history_requests_above_serial_id.h"
-#include "pg_helper.h"
-
-/**
- * Closure for #purse_deposit_serial_helper_cb().
- */
-struct HistoryRequestSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_HistoryRequestCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * Helper function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure of type `struct HistoryRequestSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-history_request_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct HistoryRequestSerialContext *dsc = cls;
- struct PostgresClosure *pg = dsc->pg;
-
- for (unsigned int i = 0; i<num_results; i++)
- {
- uint64_t rowid;
- struct TALER_Amount history_fee;
- struct GNUNET_TIME_Timestamp ts;
- struct TALER_ReservePublicKeyP reserve_pub;
- struct TALER_ReserveSignatureP reserve_sig;
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
- &history_fee),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- &reserve_pub),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
- &reserve_sig),
- GNUNET_PQ_result_spec_uint64 ("history_request_serial_id",
- &rowid),
- GNUNET_PQ_result_spec_timestamp ("request_timestamp",
- &ts),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_GenericReturnValue ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- dsc->status = GNUNET_SYSERR;
- return;
- }
- ret = dsc->cb (dsc->cb_cls,
- rowid,
- &history_fee,
- ts,
- &reserve_pub,
- &reserve_sig);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-
-
-enum GNUNET_DB_QueryStatus
-TEH_PG_select_history_requests_above_serial_id (
- void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_HistoryRequestCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- struct HistoryRequestSerialContext dsc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
- PREPARE (pg,
- "audit_get_history_requests_incr",
- "SELECT"
- " history_request_serial_id"
- ",history_fee_val"
- ",history_fee_frac"
- ",request_timestamp"
- ",reserve_pub"
- ",reserve_sig"
- " FROM history_requests"
- " WHERE ("
- " (history_request_serial_id>=$1)"
- " )"
- " ORDER BY history_request_serial_id ASC;");
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_history_requests_incr",
- params,
- &history_request_serial_helper_cb,
- &dsc);
- if (GNUNET_OK != dsc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
diff --git a/src/exchangedb/pg_select_justification_for_missing_wire.c b/src/exchangedb/pg_select_justification_for_missing_wire.c
new file mode 100644
index 000000000..77d5b4de7
--- /dev/null
+++ b/src/exchangedb/pg_select_justification_for_missing_wire.c
@@ -0,0 +1,89 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-2023 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 exchangedb/pg_select_batch_deposits_missing_wire.c
+ * @brief Implementation of the select_batch_deposits_missing_wire function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_select_batch_deposits_missing_wire.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_justification_for_missing_wire (
+ void *cls,
+ const struct TALER_PaytoHashP *wire_target_h_payto,
+ char **payto_uri,
+ char **kyc_pending,
+ enum TALER_AmlDecisionState *status,
+ struct TALER_Amount *aml_limit)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_TIME_Absolute now
+ = GNUNET_TIME_absolute_get ();
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (wire_target_h_payto),
+ GNUNET_PQ_query_param_absolute_time (&now),
+ GNUNET_PQ_query_param_end
+ };
+ uint32_t aml_status32;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("payto_uri",
+ payto_uri),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("kyc_pending",
+ kyc_pending),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_uint32 ("aml_status",
+ &aml_status32),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ TALER_PQ_RESULT_SPEC_AMOUNT ("aml_limit",
+ aml_limit),
+ NULL),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (pg,
+ "deposits_get_overdue",
+ "SELECT"
+ " out_payto_uri AS payto_uri"
+ ",out_kyc_pending AS kyc_pending"
+ ",out_deadline AS deadline"
+ ",out_aml_status AS aml_status"
+ ",out_aml_limit AS aml_limit"
+ " FROM exchange_do_select_justification_missing_wire"
+ " ($1, $2);");
+ memset (aml_limit,
+ 0,
+ sizeof (*aml_limit));
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "",
+ params,
+ rs);
+ if (qs <= 0)
+ return qs;
+ *status = (enum TALER_AmlDecisionState) aml_status32;
+ return qs;
+}
diff --git a/src/exchangedb/pg_select_justification_for_missing_wire.h b/src/exchangedb/pg_select_justification_for_missing_wire.h
new file mode 100644
index 000000000..7f73eb511
--- /dev/null
+++ b/src/exchangedb/pg_select_justification_for_missing_wire.h
@@ -0,0 +1,49 @@
+/*
+ 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 exchangedb/pg_select_justification_for_missing_wire.h
+ * @brief implementation of the select_justification_for_missing_wire function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_SELECT_JUSTIFICATION_FOR_MISSING_WIRE_H
+#define PG_SELECT_JUSTIFICATION_FOR_MISSING_WIRE_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+/**
+ * Select all of those justifications for why we might not have
+ * done a wire transfer from in the database for a particular target account.
+ *
+ * @param cls closure
+ * @param wire_target_h_payto effected target account
+ * @param[out] payto_uri target account URI, set to NULL if unknown
+ * @param[out] kyc_pending set to string describing missing KYC data
+ * @param[out] status set to AML status
+ * @param[out] aml_limit set to AML limit, or invalid amount for none
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_justification_for_missing_wire (
+ void *cls,
+ const struct TALER_PaytoHashP *wire_target_h_payto,
+ char **payto_uri,
+ char **kyc_pending,
+ enum TALER_AmlDecisionState *status,
+ struct TALER_Amount *aml_limit);
+
+#endif
diff --git a/src/exchangedb/pg_select_kyc_attributes.c b/src/exchangedb/pg_select_kyc_attributes.c
new file mode 100644
index 000000000..99ac43b3e
--- /dev/null
+++ b/src/exchangedb/pg_select_kyc_attributes.c
@@ -0,0 +1,156 @@
+/*
+ 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 exchangedb/pg_select_kyc_attributes.c
+ * @brief Implementation of the select_kyc_attributes function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_select_kyc_attributes.h"
+#include "pg_helper.h"
+
+
+/**
+ * Closure for #get_attributes_cb().
+ */
+struct GetAttributesContext
+{
+ /**
+ * Function to call per result.
+ */
+ TALER_EXCHANGEDB_AttributeCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Key of our query.
+ */
+ const struct TALER_PaytoHashP *h_payto;
+
+ /**
+ * Flag set to #GNUNET_OK as long as everything is fine.
+ */
+ enum GNUNET_GenericReturnValue status;
+
+};
+
+
+/**
+ * Invoke the callback for each result.
+ *
+ * @param cls a `struct GetAttributesContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+get_attributes_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct GetAttributesContext *ctx = cls;
+
+ for (unsigned int i = 0; i < num_results; i++)
+ {
+ struct GNUNET_TIME_Timestamp collection_time;
+ struct GNUNET_TIME_Timestamp expiration_time;
+ size_t enc_attributes_size;
+ void *enc_attributes;
+ char *provider;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_string ("provider",
+ &provider),
+ GNUNET_PQ_result_spec_timestamp ("collection_time",
+ &collection_time),
+ GNUNET_PQ_result_spec_timestamp ("expiration_time",
+ &expiration_time),
+ GNUNET_PQ_result_spec_variable_size ("encrypted_attributes",
+ &enc_attributes,
+ &enc_attributes_size),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->status = GNUNET_SYSERR;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ ctx->h_payto,
+ provider,
+ collection_time,
+ expiration_time,
+ enc_attributes_size,
+ enc_attributes);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_kyc_attributes (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_AttributeCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_end
+ };
+ struct GetAttributesContext ctx = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .pg = pg,
+ .h_payto = h_payto,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (pg,
+ "select_kyc_attributes",
+ "SELECT "
+ " provider"
+ ",collection_time"
+ ",expiration_time"
+ ",encrypted_attributes"
+ " FROM kyc_attributes"
+ " WHERE h_payto=$1");
+ qs = GNUNET_PQ_eval_prepared_multi_select (
+ pg->conn,
+ "select_kyc_attributes",
+ params,
+ &get_attributes_cb,
+ &ctx);
+ if (GNUNET_OK != ctx.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
diff --git a/src/exchangedb/pg_insert_aggregation_tracking.h b/src/exchangedb/pg_select_kyc_attributes.h
index 4f0ac1aae..7458aefe8 100644
--- a/src/exchangedb/pg_insert_aggregation_tracking.h
+++ b/src/exchangedb/pg_select_kyc_attributes.h
@@ -14,30 +14,32 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file exchangedb/pg_insert_aggregation_tracking.h
- * @brief implementation of the insert_aggregation_tracking function for Postgres
+ * @file exchangedb/pg_select_kyc_attributes.h
+ * @brief implementation of the select_kyc_attributes function for Postgres
* @author Christian Grothoff
*/
-#ifndef PG_INSERT_AGGREGATION_TRACKING_H
-#define PG_INSERT_AGGREGATION_TRACKING_H
+#ifndef PG_SELECT_KYC_ATTRIBUTES_H
+#define PG_SELECT_KYC_ATTRIBUTES_H
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+
/**
- * Function called to insert aggregation information into the DB.
+ * Lookup KYC attribute data for a specific account.
*
* @param cls closure
- * @param wtid the raw wire transfer identifier we used
- * @param deposit_serial_id row in the deposits table for which this is aggregation data
- * @return transaction status code
+ * @param h_payto account for which the attribute data is stored
+ * @param cb callback to invoke on each match
+ * @param cb_cls closure for @a cb
+ * @return database transaction status
*/
enum GNUNET_DB_QueryStatus
-TEH_PG_insert_aggregation_tracking (
+TEH_PG_select_kyc_attributes (
void *cls,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- unsigned long long deposit_serial_id);
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_AttributeCallback cb,
+ void *cb_cls);
#endif
-
diff --git a/src/exchangedb/pg_select_merge_amounts_for_kyc_check.c b/src/exchangedb/pg_select_merge_amounts_for_kyc_check.c
index 5cb665fa8..417d78ec7 100644
--- a/src/exchangedb/pg_select_merge_amounts_for_kyc_check.c
+++ b/src/exchangedb/pg_select_merge_amounts_for_kyc_check.c
@@ -26,7 +26,6 @@
#include "pg_helper.h"
-
/**
* Closure for #get_kyc_amounts_cb().
*/
@@ -132,12 +131,11 @@ TEH_PG_select_merge_amounts_for_kyc_check (
};
enum GNUNET_DB_QueryStatus qs;
-
+
PREPARE (pg,
"select_kyc_relevant_merge_events",
"SELECT"
- " amount_with_fee_val AS amount_val"
- ",amount_with_fee_frac AS amount_frac"
+ " amount_with_fee AS amount"
",merge_timestamp AS date"
" FROM account_merges"
" JOIN purse_merges USING (purse_pub)"
@@ -157,4 +155,3 @@ TEH_PG_select_merge_amounts_for_kyc_check (
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
-
diff --git a/src/exchangedb/pg_select_purse.c b/src/exchangedb/pg_select_purse.c
index e2a36c33e..ffccb905c 100644
--- a/src/exchangedb/pg_select_purse.c
+++ b/src/exchangedb/pg_select_purse.c
@@ -35,7 +35,9 @@ TEH_PG_select_purse (
struct TALER_Amount *amount,
struct TALER_Amount *deposited,
struct TALER_PrivateContractHashP *h_contract_terms,
- struct GNUNET_TIME_Timestamp *merge_timestamp)
+ struct GNUNET_TIME_Timestamp *merge_timestamp,
+ bool *purse_deleted,
+ bool *purse_refunded)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -57,25 +59,34 @@ TEH_PG_select_purse (
GNUNET_PQ_result_spec_timestamp ("merge_timestamp",
merge_timestamp),
NULL),
+ GNUNET_PQ_result_spec_bool ("purse_deleted",
+ purse_deleted),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_bool ("purse_refunded",
+ purse_refunded),
+ NULL),
GNUNET_PQ_result_spec_end
};
PREPARE (pg,
"select_purse",
"SELECT "
- " merge_pub"
- ",purse_creation"
- ",purse_expiration"
- ",h_contract_terms"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",balance_val"
- ",balance_frac"
- ",merge_timestamp"
- " FROM purse_requests"
- " LEFT JOIN purse_merges USING (purse_pub)"
- " WHERE purse_pub=$1;");
+ " pr.merge_pub"
+ ",pr.purse_creation"
+ ",pr.purse_expiration"
+ ",pr.h_contract_terms"
+ ",pr.amount_with_fee"
+ ",pr.balance"
+ ",pm.merge_timestamp"
+ ",pd.purse_sig IS NOT NULL AS purse_deleted"
+ ",pc.refunded AS purse_refunded"
+ " FROM purse_requests pr"
+ " LEFT JOIN purse_merges pm ON (pm.purse_pub = pr.purse_pub)"
+ " LEFT JOIN purse_decision pc ON (pc.purse_pub = pr.purse_pub)"
+ " LEFT JOIN purse_deletion pd ON (pd.purse_pub = pr.purse_pub)"
+ " WHERE pr.purse_pub=$1;");
*merge_timestamp = GNUNET_TIME_UNIT_FOREVER_TS;
+ *purse_refunded = false;
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"select_purse",
params,
diff --git a/src/exchangedb/pg_select_purse.h b/src/exchangedb/pg_select_purse.h
index f522256df..8f88c5cf7 100644
--- a/src/exchangedb/pg_select_purse.h
+++ b/src/exchangedb/pg_select_purse.h
@@ -37,6 +37,8 @@
* @param[out] deposited set to actual amount put into the purse so far
* @param[out] h_contract_terms set to hash of the contract for the purse
* @param[out] merge_timestamp set to time when the purse was merged, or NEVER if not
+ * @param[out] purse_deleted set to true if purse was deleted
+ * @param[out] purse_refunded set to true if purse was refunded
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
@@ -48,7 +50,8 @@ TEH_PG_select_purse (
struct TALER_Amount *amount,
struct TALER_Amount *deposited,
struct TALER_PrivateContractHashP *h_contract_terms,
- struct GNUNET_TIME_Timestamp *merge_timestamp);
-
+ struct GNUNET_TIME_Timestamp *merge_timestamp,
+ bool *purse_deleted,
+ bool *purse_refunded);
#endif
diff --git a/src/exchangedb/pg_select_purse_by_merge_pub.c b/src/exchangedb/pg_select_purse_by_merge_pub.c
index 965b27ba3..d035b3255 100644
--- a/src/exchangedb/pg_select_purse_by_merge_pub.c
+++ b/src/exchangedb/pg_select_purse_by_merge_pub.c
@@ -60,7 +60,6 @@ TEH_PG_select_purse_by_merge_pub (
GNUNET_PQ_result_spec_end
};
-
PREPARE (pg,
"select_purse_by_merge_pub",
"SELECT "
@@ -68,14 +67,11 @@ TEH_PG_select_purse_by_merge_pub (
",purse_expiration"
",h_contract_terms"
",age_limit"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",balance_val"
- ",balance_frac"
+ ",amount_with_fee"
+ ",balance"
",purse_sig"
" FROM purse_requests"
" WHERE merge_pub=$1;");
-
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"select_purse_by_merge_pub",
params,
diff --git a/src/exchangedb/pg_select_purse_decisions_above_serial_id.c b/src/exchangedb/pg_select_purse_decisions_above_serial_id.c
index 2368f2d3e..f301ea78a 100644
--- a/src/exchangedb/pg_select_purse_decisions_above_serial_id.c
+++ b/src/exchangedb/pg_select_purse_decisions_above_serial_id.c
@@ -112,7 +112,6 @@ purse_decision_serial_helper_cb (void *cls,
}
-
enum GNUNET_DB_QueryStatus
TEH_PG_select_purse_decisions_above_serial_id (
void *cls,
@@ -141,8 +140,7 @@ TEH_PG_select_purse_decisions_above_serial_id (
" pd.purse_pub"
",pm.reserve_pub"
",pd.purse_decision_serial_id"
- ",pr.amount_with_fee_val"
- ",pr.amount_with_fee_frac"
+ ",pr.amount_with_fee"
" FROM purse_decision pd"
" JOIN purse_requests pr ON (pd.purse_pub = pr.purse_pub)"
" LEFT JOIN purse_merges pm ON (pm.purse_pub = pd.purse_pub)"
diff --git a/src/exchangedb/pg_select_purse_decisions_above_serial_id.h b/src/exchangedb/pg_select_purse_decisions_above_serial_id.h
index 53ab31c80..83168d546 100644
--- a/src/exchangedb/pg_select_purse_decisions_above_serial_id.h
+++ b/src/exchangedb/pg_select_purse_decisions_above_serial_id.h
@@ -43,4 +43,5 @@ TEH_PG_select_purse_decisions_above_serial_id (
bool refunded,
TALER_EXCHANGEDB_PurseDecisionCallback cb,
void *cb_cls);
+
#endif
diff --git a/src/exchangedb/pg_select_purse_deposits_above_serial_id.c b/src/exchangedb/pg_select_purse_deposits_above_serial_id.c
index 72fdcd99b..bb4320663 100644
--- a/src/exchangedb/pg_select_purse_deposits_above_serial_id.c
+++ b/src/exchangedb/pg_select_purse_deposits_above_serial_id.c
@@ -168,12 +168,9 @@ TEH_PG_select_purse_deposits_above_serial_id (
PREPARE (pg,
"audit_get_purse_deposits_incr",
"SELECT"
- " pd.amount_with_fee_val"
- ",pd.amount_with_fee_frac"
- ",pr.amount_with_fee_val AS total_val"
- ",pr.amount_with_fee_frac AS total_frac"
- ",pr.balance_val"
- ",pr.balance_frac"
+ " pd.amount_with_fee"
+ ",pr.amount_with_fee AS total"
+ ",pr.balance"
",pr.flags"
",pd.purse_pub"
",pd.coin_sig"
diff --git a/src/exchangedb/pg_select_purse_deposits_by_purse.c b/src/exchangedb/pg_select_purse_deposits_by_purse.c
index 5fe7e014b..94b935cb4 100644
--- a/src/exchangedb/pg_select_purse_deposits_by_purse.c
+++ b/src/exchangedb/pg_select_purse_deposits_by_purse.c
@@ -133,15 +133,15 @@ TEH_PG_select_purse_deposits_by_purse (
"audit_get_purse_deposits_by_purse",
"SELECT"
" pd.purse_deposit_serial_id"
- ",pd.amount_with_fee_val"
- ",pd.amount_with_fee_frac"
+ ",pd.amount_with_fee"
",pd.coin_pub"
",denom.denom_pub"
" FROM purse_deposits pd"
- " JOIN known_coins kc USING (coin_pub)"
- " JOIN denominations denom USING (denominations_serial)"
+ " JOIN known_coins kc"
+ " USING (coin_pub)"
+ " JOIN denominations denom"
+ " USING (denominations_serial)"
" WHERE purse_pub=$1;");
-
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"audit_get_purse_deposits_by_purse",
params,
diff --git a/src/exchangedb/pg_select_purse_merge.c b/src/exchangedb/pg_select_purse_merge.c
index d1f6a5396..ecc047cc5 100644
--- a/src/exchangedb/pg_select_purse_merge.c
+++ b/src/exchangedb/pg_select_purse_merge.c
@@ -33,14 +33,14 @@ TEH_PG_select_purse_merge (
struct TALER_PurseMergeSignatureP *merge_sig,
struct GNUNET_TIME_Timestamp *merge_timestamp,
char **partner_url,
- struct TALER_ReservePublicKeyP *reserve_pub)
+ struct TALER_ReservePublicKeyP *reserve_pub,
+ bool *refunded)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (purse_pub),
GNUNET_PQ_query_param_end
};
- bool is_null;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("merge_sig",
merge_sig),
@@ -51,22 +51,28 @@ TEH_PG_select_purse_merge (
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("partner_base_url",
partner_url),
- &is_null),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_bool ("refunded",
+ refunded),
+ NULL),
GNUNET_PQ_result_spec_end
};
*partner_url = NULL;
- /* Used in #postgres_select_purse_merge */
+ *refunded = false;
PREPARE (pg,
"select_purse_merge",
"SELECT "
- " reserve_pub"
- ",merge_sig"
- ",merge_timestamp"
- ",partner_base_url"
- " FROM purse_merges"
- " LEFT JOIN partners USING (partner_serial_id)"
- " WHERE purse_pub=$1;");
+ " pm.reserve_pub"
+ ",pm.merge_sig"
+ ",pm.merge_timestamp"
+ ",pr.partner_base_url"
+ ",pd.refunded"
+ " FROM purse_merges pm"
+ " LEFT JOIN purse_decision pd USING (purse_pub)"
+ " LEFT JOIN partners pr USING (partner_serial_id)"
+ " WHERE pm.purse_pub=$1;");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"select_purse_merge",
params,
diff --git a/src/exchangedb/pg_select_purse_merge.h b/src/exchangedb/pg_select_purse_merge.h
index 982225123..8054974aa 100644
--- a/src/exchangedb/pg_select_purse_merge.h
+++ b/src/exchangedb/pg_select_purse_merge.h
@@ -35,6 +35,7 @@
* @param[out] merge_timestamp set to the time of the merge
* @param[out] partner_url set to the URL of the target exchange, or NULL if the target exchange is us. To be freed by the caller.
* @param[out] reserve_pub set to the public key of the reserve/account being credited
+ * @param[out] refunded set to true if purse was refunded
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
@@ -44,6 +45,7 @@ TEH_PG_select_purse_merge (
struct TALER_PurseMergeSignatureP *merge_sig,
struct GNUNET_TIME_Timestamp *merge_timestamp,
char **partner_url,
- struct TALER_ReservePublicKeyP *reserve_pub);
+ struct TALER_ReservePublicKeyP *reserve_pub,
+ bool *refunded);
#endif
diff --git a/src/exchangedb/pg_select_purse_merges_above_serial_id.c b/src/exchangedb/pg_select_purse_merges_above_serial_id.c
index 748a92b73..cd06d65e9 100644
--- a/src/exchangedb/pg_select_purse_merges_above_serial_id.c
+++ b/src/exchangedb/pg_select_purse_merges_above_serial_id.c
@@ -163,10 +163,8 @@ TEH_PG_select_purse_merges_above_serial_id (
"SELECT"
" pm.purse_merge_request_serial_id"
",partner_base_url"
- ",pr.amount_with_fee_val"
- ",pr.amount_with_fee_frac"
- ",pr.balance_val"
- ",pr.balance_frac"
+ ",pr.amount_with_fee"
+ ",pr.balance"
",pr.flags"
",pr.merge_pub"
",pm.reserve_pub"
diff --git a/src/exchangedb/pg_select_purse_requests_above_serial_id.c b/src/exchangedb/pg_select_purse_requests_above_serial_id.c
index 51f5de82c..61a4f2041 100644
--- a/src/exchangedb/pg_select_purse_requests_above_serial_id.c
+++ b/src/exchangedb/pg_select_purse_requests_above_serial_id.c
@@ -155,8 +155,7 @@ TEH_PG_select_purse_requests_above_serial_id (
"SELECT"
" purse_requests_serial_id"
",purse_pub"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
",age_limit"
",h_contract_terms"
",purse_creation"
diff --git a/src/exchangedb/pg_select_recoup_above_serial_id.c b/src/exchangedb/pg_select_recoup_above_serial_id.c
index 9047a86f8..5791ee500 100644
--- a/src/exchangedb/pg_select_recoup_above_serial_id.c
+++ b/src/exchangedb/pg_select_recoup_above_serial_id.c
@@ -26,7 +26,6 @@
#include "pg_helper.h"
-
/**
* Closure for #recoup_serial_helper_cb().
*/
@@ -77,7 +76,7 @@ recoup_serial_helper_cb (void *cls,
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_CoinPublicInfo coin;
struct TALER_CoinSpendSignatureP coin_sig;
- union TALER_DenominationBlindingKeyP coin_blind;
+ union GNUNET_CRYPTO_BlindingSecretP coin_blind;
struct TALER_Amount amount;
struct TALER_DenominationPublicKey denom_pub;
struct TALER_BlindedCoinHashP h_blind_ev;
@@ -137,6 +136,7 @@ recoup_serial_helper_cb (void *cls,
}
}
+
enum GNUNET_DB_QueryStatus
TEH_PG_select_recoup_above_serial_id (
void *cls,
@@ -157,7 +157,6 @@ TEH_PG_select_recoup_above_serial_id (
};
enum GNUNET_DB_QueryStatus qs;
- /* Used in #postgres_select_recoup_above_serial_id() to obtain recoup transactions */
PREPARE (pg,
"recoup_get_incr",
"SELECT"
@@ -172,8 +171,7 @@ TEH_PG_select_recoup_above_serial_id (
",coins.denom_sig"
",coins.age_commitment_hash"
",denoms.denom_pub"
- ",amount_val"
- ",amount_frac"
+ ",amount"
" FROM recoup"
" JOIN known_coins coins"
" USING (coin_pub)"
diff --git a/src/exchangedb/pg_select_recoup_refresh_above_serial_id.c b/src/exchangedb/pg_select_recoup_refresh_above_serial_id.c
index a3f6bc93d..22f906738 100644
--- a/src/exchangedb/pg_select_recoup_refresh_above_serial_id.c
+++ b/src/exchangedb/pg_select_recoup_refresh_above_serial_id.c
@@ -76,7 +76,7 @@ recoup_refresh_serial_helper_cb (void *cls,
struct TALER_CoinSpendPublicKeyP old_coin_pub;
struct TALER_CoinPublicInfo coin;
struct TALER_CoinSpendSignatureP coin_sig;
- union TALER_DenominationBlindingKeyP coin_blind;
+ union GNUNET_CRYPTO_BlindingSecretP coin_blind;
struct TALER_DenominationPublicKey denom_pub;
struct TALER_DenominationHashP old_denom_pub_hash;
struct TALER_Amount amount;
@@ -141,7 +141,6 @@ recoup_refresh_serial_helper_cb (void *cls,
}
-
enum GNUNET_DB_QueryStatus
TEH_PG_select_recoup_refresh_above_serial_id (
void *cls,
@@ -162,8 +161,6 @@ TEH_PG_select_recoup_refresh_above_serial_id (
};
enum GNUNET_DB_QueryStatus qs;
- /* Used in #postgres_select_recoup_refresh_above_serial_id() to obtain
- recoup-refresh transactions */
PREPARE (pg,
"recoup_refresh_get_incr",
"SELECT"
@@ -179,8 +176,7 @@ TEH_PG_select_recoup_refresh_above_serial_id (
",rrc.h_coin_ev AS h_blind_ev"
",new_denoms.denom_pub_hash"
",new_coins.denom_sig AS denom_sig"
- ",amount_val"
- ",amount_frac"
+ ",amount"
" FROM recoup_refresh"
" INNER JOIN refresh_revealed_coins rrc"
" USING (rrc_serial)"
diff --git a/src/exchangedb/pg_select_refreshes_above_serial_id.c b/src/exchangedb/pg_select_refreshes_above_serial_id.c
index d2b4a7fa6..db432269c 100644
--- a/src/exchangedb/pg_select_refreshes_above_serial_id.c
+++ b/src/exchangedb/pg_select_refreshes_above_serial_id.c
@@ -130,10 +130,6 @@ refreshs_serial_helper_cb (void *cls,
}
-
-
-
-
enum GNUNET_DB_QueryStatus
TEH_PG_select_refreshes_above_serial_id (
void *cls,
@@ -153,8 +149,7 @@ TEH_PG_select_refreshes_above_serial_id (
.status = GNUNET_OK
};
enum GNUNET_DB_QueryStatus qs;
- /* Used in #postgres_select_refreshes_above_serial_id() to fetch
- refresh session with id '\geq' the given parameter */
+
PREPARE (pg,
"audit_get_refresh_commitments_incr",
"SELECT"
@@ -162,8 +157,7 @@ TEH_PG_select_refreshes_above_serial_id (
",kc.coin_pub AS old_coin_pub"
",kc.age_commitment_hash"
",old_coin_sig"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
",noreveal_index"
",melt_serial_id"
",rc"
diff --git a/src/exchangedb/pg_select_refunds_above_serial_id.c b/src/exchangedb/pg_select_refunds_above_serial_id.c
index a5f7d3df6..396e8d3d5 100644
--- a/src/exchangedb/pg_select_refunds_above_serial_id.c
+++ b/src/exchangedb/pg_select_refunds_above_serial_id.c
@@ -178,28 +178,40 @@ TEH_PG_select_refunds_above_serial_id (
};
enum GNUNET_DB_QueryStatus qs;
- /* Fetch refunds with rowid '\geq' the given parameter */
+ /* Fetch refunds with rowid '\geq' the given parameter */
PREPARE (pg,
"audit_get_refunds_incr",
"SELECT"
- " dep.merchant_pub"
+ " bdep.merchant_pub"
",ref.merchant_sig"
- ",dep.h_contract_terms"
+ ",bdep.h_contract_terms"
",ref.rtransaction_id"
",denom.denom_pub"
",kc.coin_pub"
- ",ref.amount_with_fee_val"
- ",ref.amount_with_fee_frac"
+ ",ref.amount_with_fee"
",ref.refund_serial_id"
" FROM refunds ref"
- " JOIN deposits dep"
- " ON (ref.coin_pub=dep.coin_pub AND ref.deposit_serial_id=dep.deposit_serial_id)"
+ " JOIN batch_deposits bdep"
+ " ON (ref.batch_deposit_serial_id=bdep.batch_deposit_serial_id)"
+ " JOIN coin_deposits cdep"
+ " ON (ref.coin_pub=cdep.coin_pub AND ref.batch_deposit_serial_id=cdep.batch_deposit_serial_id)"
" JOIN known_coins kc"
- " ON (dep.coin_pub=kc.coin_pub)"
+ " ON (cdep.coin_pub=kc.coin_pub)"
" JOIN denominations denom"
" ON (kc.denominations_serial=denom.denominations_serial)"
" WHERE ref.refund_serial_id>=$1"
" ORDER BY ref.refund_serial_id ASC;");
+ PREPARE (pg,
+ "test_refund_full",
+ "SELECT"
+ " CAST(SUM(CAST((ref.amount_with_fee).frac AS INT8)) AS INT8) AS s_f"
+ ",CAST(SUM((ref.amount_with_fee).val) AS INT8) AS s_v"
+ ",cdep.amount_with_fee"
+ " FROM refunds ref"
+ " JOIN coin_deposits cdep"
+ " ON (ref.coin_pub=cdep.coin_pub AND ref.batch_deposit_serial_id=cdep.batch_deposit_serial_id)"
+ " WHERE ref.refund_serial_id=$1"
+ " GROUP BY (cdep.amount_with_fee);");
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"audit_get_refunds_incr",
params,
diff --git a/src/exchangedb/pg_select_refunds_by_coin.c b/src/exchangedb/pg_select_refunds_by_coin.c
index 84b63a719..d9cd6dd3c 100644
--- a/src/exchangedb/pg_select_refunds_by_coin.c
+++ b/src/exchangedb/pg_select_refunds_by_coin.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -49,7 +49,7 @@ struct SelectRefundContext
/**
* Set to #GNUNET_SYSERR on error.
*/
- int status;
+ enum GNUNET_GenericReturnValue status;
};
@@ -118,42 +118,22 @@ TEH_PG_select_refunds_by_coin (
.pg = pg,
.status = GNUNET_OK
};
+ const char *query = "get_refunds_by_coin_and_contract";
- if (NULL == getenv ("NEW_LOGIC"))
- {
- PREPARE (pg,
- "get_refunds_by_coin_and_contract",
- "SELECT"
- " ref.amount_with_fee_val"
- ",ref.amount_with_fee_frac"
- " FROM refunds ref"
- " JOIN deposits dep"
- " USING (coin_pub,deposit_serial_id)"
- " WHERE ref.coin_pub=$1"
- " AND dep.merchant_pub=$2"
- " AND dep.h_contract_terms=$3;");
- }
- else
- {
- // FIXME-Joseph
- PREPARE (pg,
- "get_refunds_by_coin_and_contract",
- "WITH rc AS MATERIALIZED("
- "SELECT"
- " * FROM refunds ref"
- "WHERE ref.coin_pub=$1"
- "AND dep.merchant_pub=$2"
- "AND dep.h_contract_terms=$3"
- ")"
- "SELECT"
- " ref.amount_with_fee_val"
- " ,ref.amount_with_fee_frac"
- "FROM deposits dep"
- "JOIN rc"
- " USING (coin_pub,deposit_serial_id)");
- }
+ PREPARE (pg,
+ query,
+ "SELECT"
+ " ref.amount_with_fee"
+ " FROM refunds ref"
+ " JOIN coin_deposits cdep"
+ " USING (coin_pub,batch_deposit_serial_id)"
+ " JOIN batch_deposits bdep"
+ " ON (ref.batch_deposit_serial_id = bdep.batch_deposit_serial_id)"
+ " WHERE ref.coin_pub=$1"
+ " AND bdep.merchant_pub=$2"
+ " AND bdep.h_contract_terms=$3;");
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "get_refunds_by_coin_and_contract",
+ query,
params,
&get_refunds_cb,
&srctx);
diff --git a/src/exchangedb/pg_select_refunds_by_coin.h b/src/exchangedb/pg_select_refunds_by_coin.h
index e1838b235..72df13fda 100644
--- a/src/exchangedb/pg_select_refunds_by_coin.h
+++ b/src/exchangedb/pg_select_refunds_by_coin.h
@@ -44,4 +44,5 @@ TEH_PG_select_refunds_by_coin (
const struct TALER_PrivateContractHashP *h_contract,
TALER_EXCHANGEDB_RefundCoinCallback cb,
void *cb_cls);
+
#endif
diff --git a/src/exchangedb/pg_select_reserve_close_info.c b/src/exchangedb/pg_select_reserve_close_info.c
index 973f5fa51..eccba8e4c 100644
--- a/src/exchangedb/pg_select_reserve_close_info.c
+++ b/src/exchangedb/pg_select_reserve_close_info.c
@@ -39,7 +39,8 @@ TEH_PG_select_reserve_close_info (
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
+ TALER_PQ_result_spec_amount ("current_balance",
+ pg->currency,
balance),
GNUNET_PQ_result_spec_string ("payto_uri",
payto_uri),
@@ -49,8 +50,7 @@ TEH_PG_select_reserve_close_info (
PREPARE (pg,
"select_reserve_close_info",
"SELECT "
- " r.current_balance_val"
- ",r.current_balance_frac"
+ " r.current_balance"
",wt.payto_uri"
" FROM reserves r"
" LEFT JOIN reserves_in ri USING (reserve_pub)"
diff --git a/src/exchangedb/pg_select_reserve_closed_above_serial_id.c b/src/exchangedb/pg_select_reserve_closed_above_serial_id.c
index 985c6792c..d24d6a600 100644
--- a/src/exchangedb/pg_select_reserve_closed_above_serial_id.c
+++ b/src/exchangedb/pg_select_reserve_closed_above_serial_id.c
@@ -157,10 +157,8 @@ TEH_PG_select_reserve_closed_above_serial_id (
",execution_date"
",wtid"
",payto_uri AS receiver_account"
- ",amount_val"
- ",amount_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
+ ",amount"
+ ",closing_fee"
",close_request_row"
" FROM reserves_close"
" JOIN wire_targets"
diff --git a/src/exchangedb/pg_select_reserve_open_above_serial_id.c b/src/exchangedb/pg_select_reserve_open_above_serial_id.c
index cc33bc48c..1675e71a7 100644
--- a/src/exchangedb/pg_select_reserve_open_above_serial_id.c
+++ b/src/exchangedb/pg_select_reserve_open_above_serial_id.c
@@ -152,8 +152,7 @@ TEH_PG_select_reserve_open_above_serial_id (
",request_timestamp"
",expiration_date"
",reserve_sig"
- ",reserve_payment_val"
- ",reserve_payment_frac"
+ ",reserve_payment"
",requested_purse_limit"
" FROM reserves_open_requests"
" WHERE open_request_uuid>=$1"
diff --git a/src/exchangedb/pg_select_reserves_in_above_serial_id.c b/src/exchangedb/pg_select_reserves_in_above_serial_id.c
index 1a6efc66b..21033e80d 100644
--- a/src/exchangedb/pg_select_reserves_in_above_serial_id.c
+++ b/src/exchangedb/pg_select_reserves_in_above_serial_id.c
@@ -137,15 +137,12 @@ TEH_PG_select_reserves_in_above_serial_id (
};
enum GNUNET_DB_QueryStatus qs;
- /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
- transactions for reserves with serial id '\geq' the given parameter */
PREPARE (pg,
"audit_reserves_in_get_transactions_incr",
"SELECT"
" reserves.reserve_pub"
",wire_reference"
- ",credit_val"
- ",credit_frac"
+ ",credit"
",execution_date"
",payto_uri AS sender_account_details"
",reserve_in_serial_id"
diff --git a/src/exchangedb/pg_select_reserves_in_above_serial_id_by_account.c b/src/exchangedb/pg_select_reserves_in_above_serial_id_by_account.c
index ba73994f0..1c7bc15a0 100644
--- a/src/exchangedb/pg_select_reserves_in_above_serial_id_by_account.c
+++ b/src/exchangedb/pg_select_reserves_in_above_serial_id_by_account.c
@@ -24,6 +24,8 @@
#include "taler_pq_lib.h"
#include "pg_select_reserves_in_above_serial_id_by_account.h"
#include "pg_helper.h"
+
+
/**
* Closure for #reserves_in_serial_helper_cb().
*/
@@ -138,15 +140,12 @@ TEH_PG_select_reserves_in_above_serial_id_by_account (
};
enum GNUNET_DB_QueryStatus qs;
- /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
- transactions for reserves with serial id '\geq' the given parameter */
PREPARE (pg,
"audit_reserves_in_get_transactions_incr_by_account",
"SELECT"
" reserves.reserve_pub"
",wire_reference"
- ",credit_val"
- ",credit_frac"
+ ",credit"
",execution_date"
",payto_uri AS sender_account_details"
",reserve_in_serial_id"
@@ -155,9 +154,9 @@ TEH_PG_select_reserves_in_above_serial_id_by_account (
" USING (reserve_pub)"
" JOIN wire_targets"
" ON (wire_source_h_payto = wire_target_h_payto)"
- " WHERE reserve_in_serial_id>=$1 AND exchange_account_section=$2"
- " ORDER BY reserve_in_serial_id;");
-
+ " WHERE reserve_in_serial_id>=$1"
+ " AND exchange_account_section=$2"
+ " ORDER BY reserve_in_serial_id ASC;");
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"audit_reserves_in_get_transactions_incr_by_account",
params,
diff --git a/src/exchangedb/pg_select_satisfied_kyc_processes.c b/src/exchangedb/pg_select_satisfied_kyc_processes.c
index f21915813..e0d884ef1 100644
--- a/src/exchangedb/pg_select_satisfied_kyc_processes.c
+++ b/src/exchangedb/pg_select_satisfied_kyc_processes.c
@@ -26,17 +26,8 @@
#include "pg_helper.h"
-
-
-
-
-
-
-
-
-
/**
- * Closure for #get_wire_fees_cb().
+ * Closure for #get_legitimizations_cb().
*/
struct GetLegitimizationsContext
{
@@ -105,7 +96,6 @@ get_legitimizations_cb (void *cls,
}
-
enum GNUNET_DB_QueryStatus
TEH_PG_select_satisfied_kyc_processes (
void *cls,
@@ -128,7 +118,7 @@ TEH_PG_select_satisfied_kyc_processes (
.status = GNUNET_OK
};
enum GNUNET_DB_QueryStatus qs;
- /* Used in #postgres_select_satisfied_kyc_processes() */
+
PREPARE (pg,
"get_satisfied_legitimizations",
"SELECT "
@@ -136,7 +126,6 @@ TEH_PG_select_satisfied_kyc_processes (
" FROM legitimization_processes"
" WHERE h_payto=$1"
" AND expiration_time>=$2;");
-
qs = GNUNET_PQ_eval_prepared_multi_select (
pg->conn,
"get_satisfied_legitimizations",
diff --git a/src/exchangedb/pg_select_similar_kyc_attributes.c b/src/exchangedb/pg_select_similar_kyc_attributes.c
new file mode 100644
index 000000000..342f9ef33
--- /dev/null
+++ b/src/exchangedb/pg_select_similar_kyc_attributes.c
@@ -0,0 +1,154 @@
+/*
+ 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 exchangedb/pg_select_similar_kyc_attributes.c
+ * @brief Implementation of the select_similar_kyc_attributes function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_select_similar_kyc_attributes.h"
+#include "pg_helper.h"
+
+
+/**
+ * Closure for #get_similar_attributes_cb().
+ */
+struct GetAttributesContext
+{
+ /**
+ * Function to call per result.
+ */
+ TALER_EXCHANGEDB_AttributeCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Flag set to #GNUNET_OK as long as everything is fine.
+ */
+ enum GNUNET_GenericReturnValue status;
+
+};
+
+
+/**
+ * Invoke the callback for each result.
+ *
+ * @param cls a `struct GetAttributesContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+get_attributes_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct GetAttributesContext *ctx = cls;
+
+ for (unsigned int i = 0; i < num_results; i++)
+ {
+ struct TALER_PaytoHashP h_payto;
+ struct GNUNET_TIME_Timestamp collection_time;
+ struct GNUNET_TIME_Timestamp expiration_time;
+ size_t enc_attributes_size;
+ void *enc_attributes;
+ char *provider;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("h_payto",
+ &h_payto),
+ GNUNET_PQ_result_spec_string ("provider",
+ &provider),
+ GNUNET_PQ_result_spec_timestamp ("collection_time",
+ &collection_time),
+ GNUNET_PQ_result_spec_timestamp ("expiration_time",
+ &expiration_time),
+ GNUNET_PQ_result_spec_variable_size ("encrypted_attributes",
+ &enc_attributes,
+ &enc_attributes_size),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->status = GNUNET_SYSERR;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ &h_payto,
+ provider,
+ collection_time,
+ expiration_time,
+ enc_attributes_size,
+ enc_attributes);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_similar_kyc_attributes (
+ void *cls,
+ const struct GNUNET_ShortHashCode *kyc_prox,
+ TALER_EXCHANGEDB_AttributeCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (kyc_prox),
+ GNUNET_PQ_query_param_end
+ };
+ struct GetAttributesContext ctx = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .pg = pg,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (pg,
+ "select_similar_kyc_attributes",
+ "SELECT "
+ " h_payto"
+ ",provider"
+ ",collection_time"
+ ",expiration_time"
+ ",encrypted_attributes"
+ " FROM kyc_attributes"
+ " WHERE kyc_prox=$1");
+ qs = GNUNET_PQ_eval_prepared_multi_select (
+ pg->conn,
+ "select_similar_kyc_attributes",
+ params,
+ &get_attributes_cb,
+ &ctx);
+ if (GNUNET_OK != ctx.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
diff --git a/src/exchangedb/pg_select_similar_kyc_attributes.h b/src/exchangedb/pg_select_similar_kyc_attributes.h
new file mode 100644
index 000000000..72f2407e8
--- /dev/null
+++ b/src/exchangedb/pg_select_similar_kyc_attributes.h
@@ -0,0 +1,45 @@
+/*
+ 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 exchangedb/pg_select_similar_kyc_attributes.h
+ * @brief implementation of the select_similar_kyc_attributes function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_SELECT_SIMILAR_KYC_ATTRIBUTES_H
+#define PG_SELECT_SIMILAR_KYC_ATTRIBUTES_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Lookup similar KYC attribute data.
+ *
+ * @param cls closure
+ * @param kyc_prox key for similarity search
+ * @param cb callback to invoke on each match
+ * @param cb_cls closure for @a cb
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_similar_kyc_attributes (
+ void *cls,
+ const struct GNUNET_ShortHashCode *kyc_prox,
+ TALER_EXCHANGEDB_AttributeCallback cb,
+ void *cb_cls);
+
+#endif
diff --git a/src/exchangedb/pg_select_wire_out_above_serial_id.c b/src/exchangedb/pg_select_wire_out_above_serial_id.c
index e219f5d4b..8668c429d 100644
--- a/src/exchangedb/pg_select_wire_out_above_serial_id.c
+++ b/src/exchangedb/pg_select_wire_out_above_serial_id.c
@@ -113,7 +113,6 @@ wire_out_serial_helper_cb (void *cls,
}
-
enum GNUNET_DB_QueryStatus
TEH_PG_select_wire_out_above_serial_id (
void *cls,
@@ -133,7 +132,7 @@ TEH_PG_select_wire_out_above_serial_id (
.status = GNUNET_OK
};
enum GNUNET_DB_QueryStatus qs;
- /* Used in #postgres_select_wire_out_above_serial_id() */
+ /* Used in #postgres_select_wire_out_above_serial_id() */
PREPARE (pg,
"audit_get_wire_incr",
"SELECT"
@@ -141,8 +140,7 @@ TEH_PG_select_wire_out_above_serial_id (
",execution_date"
",wtid_raw"
",payto_uri"
- ",amount_val"
- ",amount_frac"
+ ",amount"
" FROM wire_out"
" JOIN wire_targets"
" USING (wire_target_h_payto)"
diff --git a/src/exchangedb/pg_select_wire_out_above_serial_id_by_account.c b/src/exchangedb/pg_select_wire_out_above_serial_id_by_account.c
index 08883c9a4..3448c5a49 100644
--- a/src/exchangedb/pg_select_wire_out_above_serial_id_by_account.c
+++ b/src/exchangedb/pg_select_wire_out_above_serial_id_by_account.c
@@ -112,6 +112,7 @@ wire_out_serial_helper_cb (void *cls,
}
}
+
enum GNUNET_DB_QueryStatus
TEH_PG_select_wire_out_above_serial_id_by_account (
void *cls,
@@ -134,7 +135,6 @@ TEH_PG_select_wire_out_above_serial_id_by_account (
};
enum GNUNET_DB_QueryStatus qs;
- /* Used in #postgres_select_wire_out_above_serial_id_by_account() */
PREPARE (pg,
"audit_get_wire_incr_by_account",
"SELECT"
@@ -142,8 +142,7 @@ TEH_PG_select_wire_out_above_serial_id_by_account (
",execution_date"
",wtid_raw"
",payto_uri"
- ",amount_val"
- ",amount_frac"
+ ",amount"
" FROM wire_out"
" JOIN wire_targets"
" USING (wire_target_h_payto)"
diff --git a/src/exchangedb/pg_select_wire_out_above_serial_id_by_account.h b/src/exchangedb/pg_select_wire_out_above_serial_id_by_account.h
index 98315cace..04c6a62b2 100644
--- a/src/exchangedb/pg_select_wire_out_above_serial_id_by_account.h
+++ b/src/exchangedb/pg_select_wire_out_above_serial_id_by_account.h
@@ -43,4 +43,5 @@ TEH_PG_select_wire_out_above_serial_id_by_account (
uint64_t serial_id,
TALER_EXCHANGEDB_WireTransferOutCallback cb,
void *cb_cls);
+
#endif
diff --git a/src/exchangedb/pg_select_withdraw_amounts_for_kyc_check.c b/src/exchangedb/pg_select_withdraw_amounts_for_kyc_check.c
index e200da8de..71ed81833 100644
--- a/src/exchangedb/pg_select_withdraw_amounts_for_kyc_check.c
+++ b/src/exchangedb/pg_select_withdraw_amounts_for_kyc_check.c
@@ -131,21 +131,21 @@ TEH_PG_select_withdraw_amounts_for_kyc_check (
.status = GNUNET_OK
};
enum GNUNET_DB_QueryStatus qs;
- /* Used in #postgres_select_withdraw_amounts_for_kyc_check (
-() */
+
PREPARE (pg,
- "select_kyc_relevant_withdraw_events",
- "SELECT"
- " ro.amount_with_fee_val AS amount_val"
- ",ro.amount_with_fee_frac AS amount_frac"
- ",ro.execution_date AS date"
- " FROM reserves_out ro"
- " JOIN reserves_out_by_reserve USING (h_blind_ev)"
- " JOIN reserves res ON (ro.reserve_uuid = res.reserve_uuid)"
- " JOIN reserves_in ri ON (res.reserve_pub = ri.reserve_pub)"
- " WHERE wire_source_h_payto=$1"
- " AND ro.execution_date >= $2"
- " ORDER BY ro.execution_date DESC");
+ "select_kyc_relevant_withdraw_events",
+ "SELECT"
+ " ro.amount_with_fee AS amount"
+ ",ro.execution_date AS date"
+ " FROM reserves_in ri"
+ " JOIN reserve_history rh"
+ " ON (rh.reserve_pub = ri.reserve_pub)"
+ " JOIN reserves_out ro"
+ " ON (ro.reserve_out_serial_id = rh.serial_id)"
+ " WHERE ri.wire_source_h_payto=$1"
+ " AND rh.table_name='reserves_out'"
+ " AND ro.execution_date >= $2"
+ " ORDER BY rh.reserve_history_serial_id DESC");
qs = GNUNET_PQ_eval_prepared_multi_select (
pg->conn,
"select_kyc_relevant_withdraw_events",
diff --git a/src/exchangedb/pg_select_withdraw_amounts_for_kyc_check.h b/src/exchangedb/pg_select_withdraw_amounts_for_kyc_check.h
index 4c1283744..9a780adbe 100644
--- a/src/exchangedb/pg_select_withdraw_amounts_for_kyc_check.h
+++ b/src/exchangedb/pg_select_withdraw_amounts_for_kyc_check.h
@@ -44,4 +44,5 @@ TEH_PG_select_withdraw_amounts_for_kyc_check (
struct GNUNET_TIME_Absolute time_limit,
TALER_EXCHANGEDB_KycAmountCallback kac,
void *kac_cls);
+
#endif
diff --git a/src/exchangedb/pg_select_withdrawals_above_serial_id.c b/src/exchangedb/pg_select_withdrawals_above_serial_id.c
index 4718a62ae..9beb0f936 100644
--- a/src/exchangedb/pg_select_withdrawals_above_serial_id.c
+++ b/src/exchangedb/pg_select_withdrawals_above_serial_id.c
@@ -121,7 +121,6 @@ reserves_out_serial_helper_cb (void *cls,
}
-
enum GNUNET_DB_QueryStatus
TEH_PG_select_withdrawals_above_serial_id (
void *cls,
@@ -142,8 +141,7 @@ TEH_PG_select_withdrawals_above_serial_id (
};
enum GNUNET_DB_QueryStatus qs;
- /* Fetch deposits with rowid '\geq' the given parameter */
-
+ /* Fetch deposits with rowid '\geq' the given parameter */
PREPARE (pg,
"audit_get_reserves_out_incr",
"SELECT"
@@ -152,8 +150,7 @@ TEH_PG_select_withdrawals_above_serial_id (
",reserve_sig"
",reserves.reserve_pub"
",execution_date"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
",reserve_out_serial_id"
" FROM reserves_out"
" JOIN reserves"
@@ -162,8 +159,6 @@ TEH_PG_select_withdrawals_above_serial_id (
" USING (denominations_serial)"
" WHERE reserve_out_serial_id>=$1"
" ORDER BY reserve_out_serial_id ASC;");
-
-
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"audit_get_reserves_out_incr",
params,
diff --git a/src/exchangedb/pg_select_withdrawals_above_serial_id.h b/src/exchangedb/pg_select_withdrawals_above_serial_id.h
index adc23fb30..2b741a3b4 100644
--- a/src/exchangedb/pg_select_withdrawals_above_serial_id.h
+++ b/src/exchangedb/pg_select_withdrawals_above_serial_id.h
@@ -41,4 +41,5 @@ TEH_PG_select_withdrawals_above_serial_id (
uint64_t serial_id,
TALER_EXCHANGEDB_WithdrawCallback cb,
void *cb_cls);
+
#endif
diff --git a/src/exchangedb/pg_set_extension_manifest.c b/src/exchangedb/pg_set_extension_manifest.c
index 86e9d3f08..c7db04312 100644
--- a/src/exchangedb/pg_set_extension_manifest.c
+++ b/src/exchangedb/pg_set_extension_manifest.c
@@ -28,8 +28,8 @@
enum GNUNET_DB_QueryStatus
TEH_PG_set_extension_manifest (void *cls,
- const char *extension_name,
- const char *manifest)
+ const char *extension_name,
+ const char *manifest)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam pcfg =
diff --git a/src/exchangedb/pg_set_extension_manifest.h b/src/exchangedb/pg_set_extension_manifest.h
index ead3abd28..0befeedd8 100644
--- a/src/exchangedb/pg_set_extension_manifest.h
+++ b/src/exchangedb/pg_set_extension_manifest.h
@@ -37,7 +37,7 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_set_extension_manifest (void *cls,
- const char *extension_name,
+ const char *extension_name,
const char *manifest);
#endif
diff --git a/src/exchangedb/pg_set_purse_balance.c b/src/exchangedb/pg_set_purse_balance.c
index a996104bc..1e34ea6ed 100644
--- a/src/exchangedb/pg_set_purse_balance.c
+++ b/src/exchangedb/pg_set_purse_balance.c
@@ -35,21 +35,18 @@ TEH_PG_set_purse_balance (
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (purse_pub),
- TALER_PQ_query_param_amount (balance),
+ TALER_PQ_query_param_amount (pg->conn,
+ balance),
GNUNET_PQ_query_param_end
};
PREPARE (pg,
"set_purse_balance",
"UPDATE purse_requests"
- " SET balance_val=$2"
- " ,balance_frac=$3"
+ " SET balance=$2"
" WHERE purse_pub=$1;");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"set_purse_balance",
params);
}
-
-
-
diff --git a/src/exchangedb/pg_setup_wire_target.c b/src/exchangedb/pg_setup_wire_target.c
index a2d890c50..ed6fbe338 100644
--- a/src/exchangedb/pg_setup_wire_target.c
+++ b/src/exchangedb/pg_setup_wire_target.c
@@ -26,7 +26,7 @@
enum GNUNET_DB_QueryStatus
-TEH_PG_setup_wire_target(
+TEH_PG_setup_wire_target (
struct PostgresClosure *pg,
const char *payto_uri,
struct TALER_PaytoHashP *h_payto)
diff --git a/src/exchangedb/pg_setup_wire_target.h b/src/exchangedb/pg_setup_wire_target.h
index 12c0e59b0..77512a600 100644
--- a/src/exchangedb/pg_setup_wire_target.h
+++ b/src/exchangedb/pg_setup_wire_target.h
@@ -35,7 +35,7 @@
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
-TEH_PG_setup_wire_target(
+TEH_PG_setup_wire_target (
struct PostgresClosure *pg,
const char *payto_uri,
struct TALER_PaytoHashP *h_payto);
diff --git a/src/exchangedb/pg_start.c b/src/exchangedb/pg_start.c
index 395b87733..de5d698f6 100644
--- a/src/exchangedb/pg_start.c
+++ b/src/exchangedb/pg_start.c
@@ -28,7 +28,7 @@
enum GNUNET_GenericReturnValue
TEH_PG_start (void *cls,
- const char *name)
+ const char *name)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_ExecuteStatement es[] = {
@@ -54,4 +54,3 @@ TEH_PG_start (void *cls,
pg->transaction_name = name;
return GNUNET_OK;
}
-
diff --git a/src/exchangedb/pg_store_wire_transfer_out.c b/src/exchangedb/pg_store_wire_transfer_out.c
index 8bc0147dd..337dc5855 100644
--- a/src/exchangedb/pg_store_wire_transfer_out.c
+++ b/src/exchangedb/pg_store_wire_transfer_out.c
@@ -40,11 +40,11 @@ TEH_PG_store_wire_transfer_out (
GNUNET_PQ_query_param_auto_from_type (wtid),
GNUNET_PQ_query_param_auto_from_type (h_payto),
GNUNET_PQ_query_param_string (exchange_account_section),
- TALER_PQ_query_param_amount (amount),
+ TALER_PQ_query_param_amount (pg->conn,
+ amount),
GNUNET_PQ_query_param_end
};
- /* Used in #postgres_store_wire_transfer_out */
PREPARE (pg,
"insert_wire_out",
"INSERT INTO wire_out "
@@ -52,11 +52,9 @@ TEH_PG_store_wire_transfer_out (
",wtid_raw"
",wire_target_h_payto"
",exchange_account_section"
- ",amount_val"
- ",amount_frac"
+ ",amount"
") VALUES "
- "($1, $2, $3, $4, $5, $6);");
-
+ "($1, $2, $3, $4, $5);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_wire_out",
params);
diff --git a/src/exchangedb/pg_template.c b/src/exchangedb/pg_template.c
index 095d89615..69cd45035 100644
--- a/src/exchangedb/pg_template.c
+++ b/src/exchangedb/pg_template.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2024 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
diff --git a/src/exchangedb/pg_template.h b/src/exchangedb/pg_template.h
index 88bb930d3..d858689fb 100644
--- a/src/exchangedb/pg_template.h
+++ b/src/exchangedb/pg_template.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2024 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
diff --git a/src/exchangedb/pg_insert_aggregation_tracking.c b/src/exchangedb/pg_test_aml_officer.c
index 01c5928ba..b00828244 100644
--- a/src/exchangedb/pg_insert_aggregation_tracking.c
+++ b/src/exchangedb/pg_test_aml_officer.c
@@ -14,41 +14,35 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file exchangedb/pg_insert_aggregation_tracking.c
- * @brief Implementation of the insert_aggregation_tracking function for Postgres
+ * @file exchangedb/pg_test_aml_officer.c
+ * @brief Implementation of the test_aml_officer function for Postgres
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
-#include "pg_insert_aggregation_tracking.h"
+#include "pg_test_aml_officer.h"
#include "pg_helper.h"
enum GNUNET_DB_QueryStatus
-TEH_PG_insert_aggregation_tracking (
+TEH_PG_test_aml_officer (
void *cls,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- unsigned long long deposit_serial_id)
+ const struct TALER_AmlOfficerPublicKeyP *decider_pub)
{
struct PostgresClosure *pg = cls;
- uint64_t rid = deposit_serial_id;
struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&rid),
- GNUNET_PQ_query_param_auto_from_type (wtid),
+ GNUNET_PQ_query_param_auto_from_type (decider_pub),
GNUNET_PQ_query_param_end
};
PREPARE (pg,
- "insert_aggregation_tracking",
- "INSERT INTO aggregation_tracking "
- "(deposit_serial_id"
- ",wtid_raw"
- ") VALUES "
- "($1, $2);");
+ "test_aml_staff",
+ "SELECT 1 FROM aml_staff"
+ " WHERE decider_pub=$1"
+ " AND is_active;");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_aggregation_tracking",
+ "test_aml_staff",
params);
}
-
diff --git a/src/exchangedb/pg_batch2_reserves_in_insert.h b/src/exchangedb/pg_test_aml_officer.h
index bb6be3f6b..e034007bd 100644
--- a/src/exchangedb/pg_batch2_reserves_in_insert.h
+++ b/src/exchangedb/pg_test_aml_officer.h
@@ -14,20 +14,30 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file exchangedb/pg_batch2_reserves_in_insert.h
- * @brief implementation of the batch2_reserves_in_insert function for Postgres
+ * @file exchangedb/pg_test_aml_officer.h
+ * @brief implementation of the test_aml_officer function for Postgres
* @author Christian Grothoff
*/
-#ifndef PG_BATCH2_RESERVES_IN_INSERT_H
-#define PG_BATCH2_RESERVES_IN_INSERT_H
+#ifndef PG_TEST_AML_OFFICER_H
+#define PG_TEST_AML_OFFICER_H
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Test if the given AML staff member is active
+ * (at least read-only).
+ *
+ * @param cls closure
+ * @param decider_pub public key of the staff member
+ * @return database transaction status, if member is unknown or not active, 1 if member is active
+ */
enum GNUNET_DB_QueryStatus
-TEH_PG_batch2_reserves_in_insert (void *cls,
- const struct TALER_EXCHANGEDB_ReserveInInfo *reserves,
- unsigned int reserves_length,
- enum GNUNET_DB_QueryStatus *results);
+TEH_PG_test_aml_officer (
+ void *cls,
+ const struct TALER_AmlOfficerPublicKeyP *decider_pub);
+
#endif
diff --git a/src/exchangedb/pg_trigger_aml_process.c b/src/exchangedb/pg_trigger_aml_process.c
new file mode 100644
index 000000000..7534fe3df
--- /dev/null
+++ b/src/exchangedb/pg_trigger_aml_process.c
@@ -0,0 +1,58 @@
+/*
+ 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 exchangedb/pg_trigger_aml_process.c
+ * @brief Implementation of the trigger_aml_process function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_trigger_aml_process.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_trigger_aml_process (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ const struct TALER_Amount *threshold_crossed)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ TALER_PQ_query_param_amount (pg->conn,
+ threshold_crossed),
+ GNUNET_PQ_query_param_end
+ };
+
+ PREPARE (pg,
+ "trigger_aml_process",
+ "INSERT INTO aml_status"
+ "(h_payto"
+ ",threshold"
+ ",status)"
+ " VALUES"
+ " ($1, $2, 1)" // 1: decision needed
+ " ON CONFLICT (h_payto) DO"
+ " UPDATE SET"
+ " threshold=$2"
+ " ,status=aml_status.status | 1;"); // do not clear 'frozen' status
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "trigger_aml_process",
+ params);
+}
diff --git a/src/exchangedb/pg_trigger_aml_process.h b/src/exchangedb/pg_trigger_aml_process.h
new file mode 100644
index 000000000..2283571af
--- /dev/null
+++ b/src/exchangedb/pg_trigger_aml_process.h
@@ -0,0 +1,45 @@
+/*
+ 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 exchangedb/pg_trigger_aml_process.h
+ * @brief implementation of the trigger_aml_process function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_TRIGGER_AML_PROCESS_H
+#define PG_TRIGGER_AML_PROCESS_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Trigger AML process, an account has crossed the threshold. Inserts or
+ * updates the AML status.
+ *
+ * @param cls closure
+ * @param h_payto account for which the attribute data is stored
+ * @param threshold_crossed existing threshold that was crossed
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_trigger_aml_process (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ const struct TALER_Amount *threshold_crossed);
+
+
+#endif
diff --git a/src/exchangedb/pg_update_aggregation_transient.c b/src/exchangedb/pg_update_aggregation_transient.c
index 1d91301c3..38b65316e 100644
--- a/src/exchangedb/pg_update_aggregation_transient.c
+++ b/src/exchangedb/pg_update_aggregation_transient.c
@@ -26,7 +26,6 @@
#include "pg_helper.h"
-
enum GNUNET_DB_QueryStatus
TEH_PG_update_aggregation_transient (
void *cls,
@@ -37,23 +36,21 @@ TEH_PG_update_aggregation_transient (
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
- TALER_PQ_query_param_amount (total),
+ TALER_PQ_query_param_amount (pg->conn,
+ total),
GNUNET_PQ_query_param_auto_from_type (h_payto),
GNUNET_PQ_query_param_auto_from_type (wtid),
GNUNET_PQ_query_param_uint64 (&kyc_requirement_row),
GNUNET_PQ_query_param_end
};
-
- /* Used in #postgres_update_aggregation_transient() */
PREPARE (pg,
"update_aggregation_transient",
"UPDATE aggregation_transient"
- " SET amount_val=$1"
- " ,amount_frac=$2"
- " ,legitimization_requirement_serial_id=$5"
- " WHERE wire_target_h_payto=$3"
- " AND wtid_raw=$4");
+ " SET amount=$1"
+ " ,legitimization_requirement_serial_id=$4"
+ " WHERE wire_target_h_payto=$2"
+ " AND wtid_raw=$3");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"update_aggregation_transient",
params);
diff --git a/src/exchangedb/pg_update_auditor.c b/src/exchangedb/pg_update_auditor.c
index 9d82f25db..167a270b9 100644
--- a/src/exchangedb/pg_update_auditor.c
+++ b/src/exchangedb/pg_update_auditor.c
@@ -28,11 +28,11 @@
enum GNUNET_DB_QueryStatus
TEH_PG_update_auditor (void *cls,
- const struct TALER_AuditorPublicKeyP *auditor_pub,
- const char *auditor_url,
- const char *auditor_name,
- struct GNUNET_TIME_Timestamp change_date,
- bool enabled)
+ const struct TALER_AuditorPublicKeyP *auditor_pub,
+ const char *auditor_url,
+ const char *auditor_name,
+ struct GNUNET_TIME_Timestamp change_date,
+ bool enabled)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -43,16 +43,16 @@ TEH_PG_update_auditor (void *cls,
GNUNET_PQ_query_param_timestamp (&change_date),
GNUNET_PQ_query_param_end
};
- /* used in #postgres_update_auditor() */
- PREPARE(pg,
- "update_auditor",
- "UPDATE auditors"
- " SET"
- " auditor_url=$2"
- " ,auditor_name=$3"
- " ,is_active=$4"
- " ,last_change=$5"
- " WHERE auditor_pub=$1");
+ /* used in #postgres_update_auditor() */
+ PREPARE (pg,
+ "update_auditor",
+ "UPDATE auditors"
+ " SET"
+ " auditor_url=$2"
+ " ,auditor_name=$3"
+ " ,is_active=$4"
+ " ,last_change=$5"
+ " WHERE auditor_pub=$1");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"update_auditor",
params);
diff --git a/src/exchangedb/pg_update_auditor.h b/src/exchangedb/pg_update_auditor.h
index af8d06062..ee869f8b7 100644
--- a/src/exchangedb/pg_update_auditor.h
+++ b/src/exchangedb/pg_update_auditor.h
@@ -38,10 +38,10 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_update_auditor (void *cls,
- const struct TALER_AuditorPublicKeyP *auditor_pub,
- const char *auditor_url,
- const char *auditor_name,
- struct GNUNET_TIME_Timestamp change_date,
+ const struct TALER_AuditorPublicKeyP *auditor_pub,
+ const char *auditor_url,
+ const char *auditor_name,
+ struct GNUNET_TIME_Timestamp change_date,
bool enabled);
#endif
diff --git a/src/exchangedb/pg_update_kyc_process_by_row.c b/src/exchangedb/pg_update_kyc_process_by_row.c
index b15472839..c339436a8 100644
--- a/src/exchangedb/pg_update_kyc_process_by_row.c
+++ b/src/exchangedb/pg_update_kyc_process_by_row.c
@@ -25,6 +25,7 @@
#include "pg_update_kyc_process_by_row.h"
#include "pg_helper.h"
+
enum GNUNET_DB_QueryStatus
TEH_PG_update_kyc_process_by_row (
void *cls,
@@ -33,6 +34,7 @@ TEH_PG_update_kyc_process_by_row (
const struct TALER_PaytoHashP *h_payto,
const char *provider_account_id,
const char *provider_legitimization_id,
+ const char *redirect_url,
struct GNUNET_TIME_Absolute expiration)
{
struct PostgresClosure *pg = cls;
@@ -46,11 +48,29 @@ TEH_PG_update_kyc_process_by_row (
(NULL != provider_legitimization_id)
? GNUNET_PQ_query_param_string (provider_legitimization_id)
: GNUNET_PQ_query_param_null (),
+ (NULL != redirect_url)
+ ? GNUNET_PQ_query_param_string (redirect_url)
+ : GNUNET_PQ_query_param_null (),
GNUNET_PQ_query_param_absolute_time (&expiration),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Updating KYC data for %llu (%s)\n",
+ (unsigned long long) process_row,
+ provider_section);
+ PREPARE (pg,
+ "update_legitimization_process",
+ "UPDATE legitimization_processes"
+ " SET provider_user_id=$4"
+ " ,provider_legitimization_id=$5"
+ " ,redirect_url=$6"
+ " ,expiration_time=GREATEST(expiration_time,$7)"
+ " WHERE"
+ " h_payto=$3"
+ " AND legitimization_process_serial_id=$1"
+ " AND provider_section=$2;");
qs = GNUNET_PQ_eval_prepared_non_select (
pg->conn,
"update_legitimization_process",
@@ -58,7 +78,8 @@ TEH_PG_update_kyc_process_by_row (
if (qs <= 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to update legitimization process: %d\n",
+ "Failed to update legitimization process %llu: %d\n",
+ (unsigned long long) process_row,
qs);
return qs;
}
diff --git a/src/exchangedb/pg_update_kyc_process_by_row.h b/src/exchangedb/pg_update_kyc_process_by_row.h
index 07e896dbc..7ef5285e9 100644
--- a/src/exchangedb/pg_update_kyc_process_by_row.h
+++ b/src/exchangedb/pg_update_kyc_process_by_row.h
@@ -35,6 +35,7 @@
* @param h_payto account that must be KYC'ed (helps access by shard, otherwise also redundant)
* @param provider_account_id provider account ID
* @param provider_legitimization_id provider legitimization ID
+ * @param redirect_url where the user should be redirected to start the KYC process
* @param expiration how long is this KYC check set to be valid (in the past if invalid)
* @return database transaction status
*/
@@ -46,6 +47,7 @@ TEH_PG_update_kyc_process_by_row (
const struct TALER_PaytoHashP *h_payto,
const char *provider_account_id,
const char *provider_legitimization_id,
+ const char *redirect_url,
struct GNUNET_TIME_Absolute expiration);
#endif
diff --git a/src/exchangedb/pg_update_wire.c b/src/exchangedb/pg_update_wire.c
index f5f5672cb..5c4bb9045 100644
--- a/src/exchangedb/pg_update_wire.c
+++ b/src/exchangedb/pg_update_wire.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022, 2023, 2024 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
@@ -28,25 +28,52 @@
enum GNUNET_DB_QueryStatus
TEH_PG_update_wire (void *cls,
- const char *payto_uri,
- struct GNUNET_TIME_Timestamp change_date,
- bool enabled)
+ const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
+ struct GNUNET_TIME_Timestamp change_date,
+ const struct TALER_MasterSignatureP *master_sig,
+ const char *bank_label,
+ int64_t priority,
+ bool enabled)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (payto_uri),
GNUNET_PQ_query_param_bool (enabled),
+ NULL == conversion_url
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (conversion_url),
+ enabled
+ ? TALER_PQ_query_param_json (debit_restrictions)
+ : GNUNET_PQ_query_param_null (),
+ enabled
+ ? TALER_PQ_query_param_json (credit_restrictions)
+ : GNUNET_PQ_query_param_null (),
GNUNET_PQ_query_param_timestamp (&change_date),
+ NULL == master_sig
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_auto_from_type (master_sig),
+ NULL == bank_label
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (bank_label),
+ GNUNET_PQ_query_param_int64 (&priority),
GNUNET_PQ_query_param_end
};
- /* used in #postgres_update_wire() */
PREPARE (pg,
"update_wire",
"UPDATE wire_accounts"
" SET"
" is_active=$2"
- " ,last_change=$3"
+ " ,conversion_url=$3"
+ " ,debit_restrictions=$4"
+ " ,credit_restrictions=$5"
+ " ,last_change=$6"
+ " ,master_sig=$7"
+ " ,bank_label=$8"
+ " ,priority=$9"
" WHERE payto_uri=$1");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"update_wire",
diff --git a/src/exchangedb/pg_update_wire.h b/src/exchangedb/pg_update_wire.h
index c01d68a46..a596a0802 100644
--- a/src/exchangedb/pg_update_wire.h
+++ b/src/exchangedb/pg_update_wire.h
@@ -24,20 +24,34 @@
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+
+
/**
* Update information about a wire account of the exchange.
*
* @param cls closure
* @param payto_uri account the update is about
+ * @param conversion_url URL of a conversion service, NULL if there is no conversion
+ * @param debit_restrictions JSON array with debit restrictions on the account; NULL allowed if not @a enabled
+ * @param credit_restrictions JSON array with credit restrictions on the account; NULL allowed if not @a enabled
* @param change_date date when the account status was last changed
* (only to be used for replay detection)
+ * @param master_sig master signature to store, can be NULL (if @a enabled is false)
+ * @param bank_label label to show this entry under in the UI, can be NULL
+ * @param priority determines order in which entries are shown in the UI
* @param enabled true to enable, false to disable (the actual change)
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
TEH_PG_update_wire (void *cls,
- const char *payto_uri,
- struct GNUNET_TIME_Timestamp change_date,
+ const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
+ struct GNUNET_TIME_Timestamp change_date,
+ const struct TALER_MasterSignatureP *master_sig,
+ const char *bank_label,
+ int64_t priority,
bool enabled);
#endif
diff --git a/src/exchangedb/pg_wire_prepare_data_get.c b/src/exchangedb/pg_wire_prepare_data_get.c
index d45413010..0cc57e41f 100644
--- a/src/exchangedb/pg_wire_prepare_data_get.c
+++ b/src/exchangedb/pg_wire_prepare_data_get.c
@@ -99,10 +99,10 @@ prewire_cb (void *cls,
enum GNUNET_DB_QueryStatus
TEH_PG_wire_prepare_data_get (void *cls,
- uint64_t start_row,
- uint64_t limit,
- TALER_EXCHANGEDB_WirePreparationIterator cb,
- void *cb_cls)
+ uint64_t start_row,
+ uint64_t limit,
+ TALER_EXCHANGEDB_WirePreparationIterator cb,
+ void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -117,8 +117,6 @@ TEH_PG_wire_prepare_data_get (void *cls,
};
enum GNUNET_DB_QueryStatus qs;
-
- /* Used in #postgres_wire_prepare_data_get() */
PREPARE (pg,
"wire_prepare_data_get",
"SELECT"
diff --git a/src/exchangedb/pg_wire_prepare_data_get.h b/src/exchangedb/pg_wire_prepare_data_get.h
index 91e21d27e..815c14bdf 100644
--- a/src/exchangedb/pg_wire_prepare_data_get.h
+++ b/src/exchangedb/pg_wire_prepare_data_get.h
@@ -38,8 +38,9 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_wire_prepare_data_get (void *cls,
- uint64_t start_row,
- uint64_t limit,
- TALER_EXCHANGEDB_WirePreparationIterator cb,
+ uint64_t start_row,
+ uint64_t limit,
+ TALER_EXCHANGEDB_WirePreparationIterator cb,
void *cb_cls);
+
#endif
diff --git a/src/exchangedb/pg_wire_prepare_data_insert.c b/src/exchangedb/pg_wire_prepare_data_insert.c
index 903f22a5b..919fccdaf 100644
--- a/src/exchangedb/pg_wire_prepare_data_insert.c
+++ b/src/exchangedb/pg_wire_prepare_data_insert.c
@@ -27,9 +27,9 @@
enum GNUNET_DB_QueryStatus
TEH_PG_wire_prepare_data_insert (void *cls,
- const char *type,
- const char *buf,
- size_t buf_size)
+ const char *type,
+ const char *buf,
+ size_t buf_size)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -39,15 +39,15 @@ TEH_PG_wire_prepare_data_insert (void *cls,
};
- /* Used in #postgres_wire_prepare_data_insert() to store
- wire transfer information before actually committing it with the bank */
+ /* Used in #postgres_wire_prepare_data_insert() to store
+ wire transfer information before actually committing it with the bank */
PREPARE (pg,
- "wire_prepare_data_insert",
- "INSERT INTO prewire "
- "(wire_method"
- ",buf"
- ") VALUES "
- "($1, $2);");
+ "wire_prepare_data_insert",
+ "INSERT INTO prewire "
+ "(wire_method"
+ ",buf"
+ ") VALUES "
+ "($1, $2);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"wire_prepare_data_insert",
params);
diff --git a/src/exchangedb/pg_wire_prepare_data_insert.h b/src/exchangedb/pg_wire_prepare_data_insert.h
index 2b6050d05..e73ee152d 100644
--- a/src/exchangedb/pg_wire_prepare_data_insert.h
+++ b/src/exchangedb/pg_wire_prepare_data_insert.h
@@ -36,7 +36,8 @@
*/
enum GNUNET_DB_QueryStatus
TEH_PG_wire_prepare_data_insert (void *cls,
- const char *type,
- const char *buf,
+ const char *type,
+ const char *buf,
size_t buf_size);
+
#endif
diff --git a/src/exchangedb/pg_wire_prepare_data_mark_failed.c b/src/exchangedb/pg_wire_prepare_data_mark_failed.c
index fe2236b82..1d46c84d4 100644
--- a/src/exchangedb/pg_wire_prepare_data_mark_failed.c
+++ b/src/exchangedb/pg_wire_prepare_data_mark_failed.c
@@ -37,8 +37,6 @@ TEH_PG_wire_prepare_data_mark_failed (
GNUNET_PQ_query_param_end
};
- /* Used in #postgres_wire_prepare_data_mark_failed() */
-
PREPARE (pg,
"wire_prepare_data_mark_failed",
"UPDATE prewire"
diff --git a/src/exchangedb/pg_wire_prepare_data_mark_failed.h b/src/exchangedb/pg_wire_prepare_data_mark_failed.h
index cae1523d1..98846b284 100644
--- a/src/exchangedb/pg_wire_prepare_data_mark_failed.h
+++ b/src/exchangedb/pg_wire_prepare_data_mark_failed.h
@@ -36,4 +36,5 @@ enum GNUNET_DB_QueryStatus
TEH_PG_wire_prepare_data_mark_failed (
void *cls,
uint64_t rowid);
+
#endif
diff --git a/src/exchangedb/pg_wire_prepare_data_mark_finished.c b/src/exchangedb/pg_wire_prepare_data_mark_finished.c
index de8738e40..998b9d731 100644
--- a/src/exchangedb/pg_wire_prepare_data_mark_finished.c
+++ b/src/exchangedb/pg_wire_prepare_data_mark_finished.c
@@ -36,13 +36,11 @@ TEH_PG_wire_prepare_data_mark_finished (
GNUNET_PQ_query_param_end
};
- /* Used in #postgres_wire_prepare_data_mark_finished() */
PREPARE (pg,
- "wire_prepare_data_mark_done",
- "UPDATE prewire"
- " SET finished=TRUE"
- " WHERE prewire_uuid=$1;");
-
+ "wire_prepare_data_mark_done",
+ "UPDATE prewire"
+ " SET finished=TRUE"
+ " WHERE prewire_uuid=$1;");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"wire_prepare_data_mark_done",
params);
diff --git a/src/exchangedb/pg_wire_prepare_data_mark_finished.h b/src/exchangedb/pg_wire_prepare_data_mark_finished.h
index 19db2ca99..ba2e384cd 100644
--- a/src/exchangedb/pg_wire_prepare_data_mark_finished.h
+++ b/src/exchangedb/pg_wire_prepare_data_mark_finished.h
@@ -36,4 +36,5 @@ enum GNUNET_DB_QueryStatus
TEH_PG_wire_prepare_data_mark_finished (
void *cls,
uint64_t rowid);
+
#endif
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index c805810b1..108b55219 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014--2022 Taler Systems SA
+ Copyright (C) 2014--2023 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
@@ -21,8 +21,12 @@
* @author Christian Grothoff
* @author Sree Harsha Totakura
* @author Marcello Stanisci
+ * @author Özgür Kesim
*/
#include "platform.h"
+#include <poll.h>
+#include <pthread.h>
+#include <libpq-fe.h>
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
@@ -34,7 +38,6 @@
#include "pg_get_link_data.h"
#include "pg_helper.h"
#include "pg_do_reserve_open.h"
-#include "pg_do_withdraw.h"
#include "pg_get_coin_transactions.h"
#include "pg_get_expired_reserves.h"
#include "pg_get_purse_request.h"
@@ -43,11 +46,13 @@
#include "pg_insert_close_request.h"
#include "pg_insert_records_by_table.h"
#include "pg_insert_reserve_open_deposit.h"
+#include "pg_get_pending_kyc_requirement_process.h"
#include "pg_iterate_kyc_reference.h"
#include "pg_iterate_reserve_close_info.h"
#include "pg_lookup_records_by_table.h"
#include "pg_lookup_serial_by_table.h"
#include "pg_select_account_merges_above_serial_id.h"
+#include "pg_select_aml_threshold.h"
#include "pg_select_all_purse_decisions_above_serial_id.h"
#include "pg_select_purse.h"
#include "pg_select_purse_deposits_above_serial_id.h"
@@ -56,16 +61,10 @@
#include "pg_select_reserve_close_info.h"
#include "pg_select_reserve_closed_above_serial_id.h"
#include "pg_select_reserve_open_above_serial_id.h"
-#include <poll.h>
-#include <pthread.h>
-#include <libpq-fe.h>
-
-/**NEW INCLUDES**/
#include "pg_insert_purse_request.h"
#include "pg_iterate_active_signkeys.h"
#include "pg_preflight.h"
#include "pg_commit.h"
-#include "pg_insert_aggregation_tracking.h"
#include "pg_drop_tables.h"
#include "pg_select_satisfied_kyc_processes.h"
#include "pg_select_aggregation_amounts_for_kyc_check.h"
@@ -82,6 +81,7 @@
#include "pg_get_drain_profit.h"
#include "pg_get_purse_deposit.h"
#include "pg_insert_contract.h"
+#include "pg_insert_kyc_failure.h"
#include "pg_select_contract.h"
#include "pg_select_purse_merge.h"
#include "pg_select_contract_by_purse.h"
@@ -93,7 +93,7 @@
#include "pg_update_auditor.h"
#include "pg_begin_revolving_shard.h"
#include "pg_get_extension_manifest.h"
-#include "pg_insert_history_request.h"
+#include "pg_do_purse_delete.h"
#include "pg_do_purse_merge.h"
#include "pg_start_read_committed.h"
#include "pg_start_read_only.h"
@@ -116,10 +116,13 @@
#include "pg_drain_kyc_alert.h"
#include "pg_reserves_in_insert.h"
#include "pg_get_withdraw_info.h"
+#include "pg_get_age_withdraw.h"
#include "pg_do_batch_withdraw.h"
+#include "pg_do_age_withdraw.h"
#include "pg_get_policy_details.h"
#include "pg_persist_policy_details.h"
#include "pg_do_deposit.h"
+#include "pg_get_wire_hash_for_contract.h"
#include "pg_add_policy_fulfillment_proof.h"
#include "pg_do_melt.h"
#include "pg_do_refund.h"
@@ -129,6 +132,7 @@
#include "pg_count_known_coins.h"
#include "pg_ensure_coin_known.h"
#include "pg_get_known_coin.h"
+#include "pg_get_signature_for_known_coin.h"
#include "pg_get_coin_denomination.h"
#include "pg_have_deposit2.h"
#include "pg_aggregate.h"
@@ -137,7 +141,6 @@
#include "pg_find_aggregation_transient.h"
#include "pg_update_aggregation_transient.h"
#include "pg_get_ready_deposit.h"
-#include "pg_insert_deposit.h"
#include "pg_insert_refund.h"
#include "pg_select_refunds_by_coin.h"
#include "pg_get_melt.h"
@@ -158,8 +161,8 @@
#include "pg_start_deferred_wire_out.h"
#include "pg_store_wire_transfer_out.h"
#include "pg_gc.h"
-#include "pg_select_deposits_above_serial_id.h"
-#include "pg_select_history_requests_above_serial_id.h"
+#include "pg_inject_auditor_triggers.h"
+#include "pg_select_coin_deposits_above_serial_id.h"
#include "pg_select_purse_decisions_above_serial_id.h"
#include "pg_select_purse_deposits_by_purse.h"
#include "pg_select_refreshes_above_serial_id.h"
@@ -175,7 +178,9 @@
#include "pg_get_old_coin_by_h_blind.h"
#include "pg_insert_denomination_revocation.h"
#include "pg_get_denomination_revocation.h"
-#include "pg_select_deposits_missing_wire.h"
+#include "pg_select_batch_deposits_missing_wire.h"
+#include "pg_select_justification_for_missing_wire.h"
+#include "pg_select_aggregations_above_serial.h"
#include "pg_lookup_auditor_timestamp.h"
#include "pg_lookup_auditor_status.h"
#include "pg_insert_auditor.h"
@@ -204,14 +209,24 @@
#include "pg_reserves_update.h"
#include "pg_setup_wire_target.h"
#include "pg_compute_shard.h"
-#include "pg_batch_reserves_in_insert.h"
-#include "pg_batch2_reserves_in_insert.h"
+#include "pg_insert_kyc_attributes.h"
+#include "pg_select_similar_kyc_attributes.h"
+#include "pg_select_kyc_attributes.h"
+#include "pg_insert_aml_officer.h"
+#include "pg_test_aml_officer.h"
+#include "pg_lookup_aml_officer.h"
+#include "pg_trigger_aml_process.h"
+#include "pg_select_aml_process.h"
+#include "pg_select_aml_history.h"
+#include "pg_insert_aml_decision.h"
+#include "pg_batch_ensure_coin_known.h"
+
/**
* Set to 1 to enable Postgres auto_explain module. This will
* slow down things a _lot_, but also provide extensive logging
* in the Postgres database logger for performance analysis.
*/
-#define AUTO_EXPLAIN 1
+#define AUTO_EXPLAIN 0
/**
@@ -233,94 +248,13 @@
/**
- * Initialize prepared statements for @a pg.
- *
- * @param[in,out] pg connection to initialize
- * @return #GNUNET_OK on success
- */
-enum GNUNET_GenericReturnValue
-prepare_statements (struct PostgresClosure *pg)
-{
- enum GNUNET_GenericReturnValue ret;
- struct GNUNET_PQ_PreparedStatement ps[] = {
- GNUNET_PQ_make_prepare (
- "get_kyc_h_payto",
- "SELECT"
- " wire_target_h_payto"
- " FROM wire_targets"
- " WHERE wire_target_h_payto=$1"
- " LIMIT 1;"),
- /* Used in #postgres_ensure_coin_known() */
- GNUNET_PQ_make_prepare (
- "get_known_coin_dh",
- "SELECT"
- " denominations.denom_pub_hash"
- " FROM known_coins"
- " JOIN denominations USING (denominations_serial)"
- " WHERE coin_pub=$1;"),
- /* Store information about the desired denominations for a
- refresh operation, used in #postgres_insert_refresh_reveal() */
- GNUNET_PQ_make_prepare (
- "insert_refresh_revealed_coin",
- "INSERT INTO refresh_revealed_coins "
- "(melt_serial_id "
- ",freshcoin_index "
- ",link_sig "
- ",denominations_serial "
- ",coin_ev"
- ",ewv"
- ",h_coin_ev"
- ",ev_sig"
- ") SELECT $1, $2, $3, "
- " denominations_serial, $5, $6, $7, $8"
- " FROM denominations"
- " WHERE denom_pub_hash=$4"
- " ON CONFLICT DO NOTHING;"),
- GNUNET_PQ_make_prepare (
- "test_refund_full",
- "SELECT"
- " CAST(SUM(CAST(ref.amount_with_fee_frac AS INT8)) AS INT8) AS s_f"
- ",CAST(SUM(ref.amount_with_fee_val) AS INT8) AS s_v"
- ",dep.amount_with_fee_val"
- ",dep.amount_with_fee_frac"
- " FROM refunds ref"
- " JOIN deposits dep"
- " ON (ref.coin_pub=dep.coin_pub AND ref.deposit_serial_id=dep.deposit_serial_id)"
- " WHERE ref.refund_serial_id=$1"
- " GROUP BY (dep.amount_with_fee_val, dep.amount_with_fee_frac);"),
- /* Used in #postgres_update_kyc_requirement_by_row() */
- GNUNET_PQ_make_prepare (
- "update_legitimization_process",
- "UPDATE legitimization_processes"
- " SET provider_user_id=$4"
- " ,provider_legitimization_id=$5"
- " ,expiration_time=GREATEST(expiration_time,$6)"
- " WHERE"
- " h_payto=$3"
- " AND legitimization_process_serial_id=$1"
- " AND provider_section=$2;"),
- GNUNET_PQ_PREPARED_STATEMENT_END
- };
-
- ret = GNUNET_PQ_prepare_statements (pg->conn,
- ps);
- if (GNUNET_OK != ret)
- return ret;
- pg->init = true;
- return GNUNET_OK;
-}
-
-
-/**
* Connect to the database if the connection does not exist yet.
*
* @param pg the plugin-specific state
- * @param skip_prepare true if we should skip prepared statement setup
* @return #GNUNET_OK on success
*/
enum GNUNET_GenericReturnValue
-TEH_PG_internal_setup (struct PostgresClosure *pg,
- bool skip_prepare)
+TEH_PG_internal_setup (struct PostgresClosure *pg)
{
if (NULL == pg->conn)
{
@@ -339,6 +273,8 @@ TEH_PG_internal_setup (struct PostgresClosure *pg,
GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
+ /* Mergejoin causes issues, see Postgres #18380 */
+ GNUNET_PQ_make_try_execute ("SET enable_mergejoin=OFF;"),
GNUNET_PQ_EXECUTE_STATEMENT_END
};
#else
@@ -347,7 +283,8 @@ TEH_PG_internal_setup (struct PostgresClosure *pg,
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
- GNUNET_PQ_make_try_execute ("SET autocommit=OFF;"),
+ /* Mergejoin causes issues, see Postgres #18380 */
+ GNUNET_PQ_make_try_execute ("SET enable_mergejoin=OFF;"),
GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
GNUNET_PQ_EXECUTE_STATEMENT_END
};
@@ -361,16 +298,13 @@ TEH_PG_internal_setup (struct PostgresClosure *pg,
NULL);
if (NULL == db_conn)
return GNUNET_SYSERR;
+
pg->prep_gen++;
pg->conn = db_conn;
}
if (NULL == pg->transaction_name)
GNUNET_PQ_reconnect_if_down (pg->conn);
- if (pg->init)
- return GNUNET_OK;
- if (skip_prepare)
- return GNUNET_OK;
- return prepare_statements (pg);
+ return GNUNET_OK;
}
@@ -399,7 +333,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchangedb-postgres",
- "CONFIG");
+ "SQL_DIR");
GNUNET_free (pg);
return NULL;
}
@@ -416,21 +350,29 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
GNUNET_free (pg);
return NULL;
}
- if ( (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (cfg,
- "exchangedb",
- "IDLE_RESERVE_EXPIRATION_TIME",
- &pg->idle_reserve_expiration_time))
- ||
- (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (cfg,
- "exchangedb",
- "LEGAL_RESERVE_EXPIRATION_TIME",
- &pg->legal_reserve_expiration_time)) )
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (cfg,
+ "exchangedb",
+ "IDLE_RESERVE_EXPIRATION_TIME",
+ &pg->idle_reserve_expiration_time))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchangedb",
- "LEGAL/IDLE_RESERVE_EXPIRATION_TIME");
+ "IDLE_RESERVE_EXPIRATION_TIME");
+ GNUNET_free (pg->exchange_url);
+ GNUNET_free (pg->sql_dir);
+ GNUNET_free (pg);
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (cfg,
+ "exchangedb",
+ "LEGAL_RESERVE_EXPIRATION_TIME",
+ &pg->legal_reserve_expiration_time))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchangedb",
+ "LEGAL_RESERVE_EXPIRATION_TIME");
GNUNET_free (pg->exchange_url);
GNUNET_free (pg->sql_dir);
GNUNET_free (pg);
@@ -472,8 +414,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
return NULL;
}
if (GNUNET_OK !=
- TEH_PG_internal_setup (pg,
- true))
+ TEH_PG_internal_setup (pg))
{
GNUNET_free (pg->exchange_url);
GNUNET_free (pg->currency);
@@ -483,13 +424,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
}
plugin = GNUNET_new (struct TALER_EXCHANGEDB_Plugin);
plugin->cls = pg;
- /* New style, sort alphabetically! */
plugin->do_reserve_open
= &TEH_PG_do_reserve_open;
plugin->drop_tables
= &TEH_PG_drop_tables;
- plugin->do_withdraw
- = &TEH_PG_do_withdraw;
plugin->free_coin_transaction_list
= &TEH_COMMON_free_coin_transaction_list;
plugin->free_reserve_history
@@ -502,8 +440,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_get_purse_request;
plugin->get_reserve_history
= &TEH_PG_get_reserve_history;
- plugin->get_reserve_status
- = &TEH_PG_get_reserve_status;
plugin->get_unfinished_close_requests
= &TEH_PG_get_unfinished_close_requests;
plugin->insert_records_by_table
@@ -528,6 +464,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_select_account_merges_above_serial_id;
plugin->select_all_purse_decisions_above_serial_id
= &TEH_PG_select_all_purse_decisions_above_serial_id;
+ plugin->select_aml_threshold
+ = &TEH_PG_select_aml_threshold;
plugin->select_purse
= &TEH_PG_select_purse;
plugin->select_purse_deposits_above_serial_id
@@ -542,7 +480,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_select_reserve_closed_above_serial_id;
plugin->select_reserve_open_above_serial_id
= &TEH_PG_select_reserve_open_above_serial_id;
- /*need to sort*/
plugin->insert_purse_request
= &TEH_PG_insert_purse_request;
plugin->iterate_active_signkeys
@@ -551,11 +488,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_commit;
plugin->preflight
= &TEH_PG_preflight;
- plugin->insert_aggregation_tracking
- = &TEH_PG_insert_aggregation_tracking;
plugin->select_aggregation_amounts_for_kyc_check
= &TEH_PG_select_aggregation_amounts_for_kyc_check;
-
plugin->select_satisfied_kyc_processes
= &TEH_PG_select_satisfied_kyc_processes;
plugin->kyc_provider_account_lookup
@@ -606,10 +540,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_begin_revolving_shard;
plugin->get_extension_manifest
= &TEH_PG_get_extension_manifest;
- plugin->insert_history_request
- = &TEH_PG_insert_history_request;
plugin->do_purse_merge
= &TEH_PG_do_purse_merge;
+ plugin->do_purse_delete
+ = &TEH_PG_do_purse_delete;
plugin->start_read_committed
= &TEH_PG_start_read_committed;
plugin->start_read_only
@@ -654,12 +588,18 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_get_withdraw_info;
plugin->do_batch_withdraw
= &TEH_PG_do_batch_withdraw;
+ plugin->do_age_withdraw
+ = &TEH_PG_do_age_withdraw;
+ plugin->get_age_withdraw
+ = &TEH_PG_get_age_withdraw;
plugin->get_policy_details
= &TEH_PG_get_policy_details;
plugin->persist_policy_details
= &TEH_PG_persist_policy_details;
plugin->do_deposit
= &TEH_PG_do_deposit;
+ plugin->get_wire_hash_for_contract
+ = &TEH_PG_get_wire_hash_for_contract;
plugin->add_policy_fulfillment_proof
= &TEH_PG_add_policy_fulfillment_proof;
plugin->do_melt
@@ -678,6 +618,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_ensure_coin_known;
plugin->get_known_coin
= &TEH_PG_get_known_coin;
+ plugin->get_signature_for_known_coin
+ = &TEH_PG_get_signature_for_known_coin;
plugin->get_coin_denomination
= &TEH_PG_get_coin_denomination;
plugin->have_deposit2
@@ -694,8 +636,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_update_aggregation_transient;
plugin->get_ready_deposit
= &TEH_PG_get_ready_deposit;
- plugin->insert_deposit
- = &TEH_PG_insert_deposit;
plugin->insert_refund
= &TEH_PG_insert_refund;
plugin->select_refunds_by_coin
@@ -736,10 +676,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_store_wire_transfer_out;
plugin->gc
= &TEH_PG_gc;
- plugin->select_deposits_above_serial_id
- = &TEH_PG_select_deposits_above_serial_id;
- plugin->select_history_requests_above_serial_id
- = &TEH_PG_select_history_requests_above_serial_id;
+ plugin->select_coin_deposits_above_serial_id
+ = &TEH_PG_select_coin_deposits_above_serial_id;
plugin->select_purse_decisions_above_serial_id
= &TEH_PG_select_purse_decisions_above_serial_id;
plugin->select_purse_deposits_by_purse
@@ -770,8 +708,12 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_insert_denomination_revocation;
plugin->get_denomination_revocation
= &TEH_PG_get_denomination_revocation;
- plugin->select_deposits_missing_wire
- = &TEH_PG_select_deposits_missing_wire;
+ plugin->select_batch_deposits_missing_wire
+ = &TEH_PG_select_batch_deposits_missing_wire;
+ plugin->select_justification_for_missing_wire
+ = &TEH_PG_select_justification_for_missing_wire;
+ plugin->select_aggregations_above_serial
+ = &TEH_PG_select_aggregations_above_serial;
plugin->lookup_auditor_timestamp
= &TEH_PG_lookup_auditor_timestamp;
plugin->lookup_auditor_status
@@ -806,6 +748,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_begin_shard;
plugin->abort_shard
= &TEH_PG_abort_shard;
+ plugin->insert_kyc_failure
+ = &TEH_PG_insert_kyc_failure;
plugin->complete_shard
= &TEH_PG_complete_shard;
plugin->release_revolving_shard
@@ -822,10 +766,33 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_select_purse_by_merge_pub;
plugin->set_purse_balance
= &TEH_PG_set_purse_balance;
- plugin->batch_reserves_in_insert
- = &TEH_PG_batch_reserves_in_insert;
- plugin->batch2_reserves_in_insert
- = &TEH_PG_batch2_reserves_in_insert;
+ plugin->get_pending_kyc_requirement_process
+ = &TEH_PG_get_pending_kyc_requirement_process;
+ plugin->insert_kyc_attributes
+ = &TEH_PG_insert_kyc_attributes;
+ plugin->select_similar_kyc_attributes
+ = &TEH_PG_select_similar_kyc_attributes;
+ plugin->select_kyc_attributes
+ = &TEH_PG_select_kyc_attributes;
+ plugin->insert_aml_officer
+ = &TEH_PG_insert_aml_officer;
+ plugin->test_aml_officer
+ = &TEH_PG_test_aml_officer;
+ plugin->lookup_aml_officer
+ = &TEH_PG_lookup_aml_officer;
+ plugin->trigger_aml_process
+ = &TEH_PG_trigger_aml_process;
+ plugin->select_aml_process
+ = &TEH_PG_select_aml_process;
+ plugin->select_aml_history
+ = &TEH_PG_select_aml_history;
+ plugin->insert_aml_decision
+ = &TEH_PG_insert_aml_decision;
+
+ plugin->batch_ensure_coin_known
+ = &TEH_PG_batch_ensure_coin_known;
+ plugin->inject_auditor_triggers
+ = &TEH_PG_inject_auditor_triggers;
return plugin;
}
diff --git a/src/exchangedb/procedures.sql.in b/src/exchangedb/procedures.sql.in
index b412b66d7..7afb01f0b 100644
--- a/src/exchangedb/procedures.sql.in
+++ b/src/exchangedb/procedures.sql.in
@@ -18,24 +18,32 @@ BEGIN;
SET search_path TO exchange;
-#include "exchange_do_withdraw.sql"
+#include "exchange_do_amount_specific.sql"
#include "exchange_do_batch_withdraw.sql"
#include "exchange_do_batch_withdraw_insert.sql"
-#include "exchange_do_recoup_by_reserve.sql"
+#include "exchange_do_age_withdraw.sql"
#include "exchange_do_deposit.sql"
#include "exchange_do_melt.sql"
+#include "exchange_do_select_deposits_missing_wire.sql"
+#include "exchange_do_select_justification_for_missing_wire.sql"
#include "exchange_do_refund.sql"
#include "exchange_do_recoup_to_reserve.sql"
#include "exchange_do_recoup_to_coin.sql"
#include "exchange_do_gc.sql"
+#include "exchange_do_purse_delete.sql"
#include "exchange_do_purse_deposit.sql"
+#include "exchange_do_purse_merge.sql"
#include "exchange_do_reserve_purse.sql"
#include "exchange_do_expire_purse.sql"
-#include "exchange_do_history_request.sql"
#include "exchange_do_reserve_open_deposit.sql"
#include "exchange_do_reserve_open.sql"
#include "exchange_do_insert_or_update_policy_details.sql"
-#include "exchange_do_batch_reserves_in.sql"
-
+#include "exchange_do_insert_aml_decision.sql"
+#include "exchange_do_insert_aml_officer.sql"
+#include "exchange_do_insert_kyc_attributes.sql"
+#include "exchange_do_reserves_in_insert.sql"
+#include "exchange_do_batch_reserves_update.sql"
+#include "exchange_do_get_link_data.sql"
+#include "exchange_do_batch_coin_known.sql"
COMMIT;
diff --git a/src/exchangedb/spi/Makefile b/src/exchangedb/spi/Makefile
new file mode 100644
index 000000000..d654d91e9
--- /dev/null
+++ b/src/exchangedb/spi/Makefile
@@ -0,0 +1,9 @@
+EXTENSION = own_test
+MODULES = own_test
+DATA = own_test.sql
+PG_CPPFLAGS = -I /usr/include/postgresql
+
+# postgresql build stuff
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
diff --git a/src/exchangedb/spi/README.md b/src/exchangedb/spi/README.md
new file mode 100644
index 000000000..47eb37b94
--- /dev/null
+++ b/src/exchangedb/spi/README.md
@@ -0,0 +1,37 @@
+ Server Programming Interface (SPI)
+
+
+Overview
+========
+
+This folder contains results from an experiment by Joseph Xu
+to use the Postgres SPI. They are not currently used at all
+by the GNU Taler exchange.
+
+
+Dependencies
+============
+
+These are the direct dependencies for compiling the code:
+
+# apt-get install libpq-dev postgresql-server-dev-13
+# apt-get install libkrb5-dev
+# apt-get install libssl-dev
+
+
+Compilation
+===========
+
+$ make
+
+Loading functions
+=================
+
+# make install
+$ psql "$DB_NAME" < own_test.sql
+
+
+Calling functions
+==================
+
+$ psql -c "SELECT $FUNCTION_NAME($ARGS);" "$DB_NAME"
diff --git a/src/exchangedb/spi/own_test.c b/src/exchangedb/spi/own_test.c
new file mode 100644
index 000000000..ac72fad7b
--- /dev/null
+++ b/src/exchangedb/spi/own_test.c
@@ -0,0 +1,873 @@
+#include "postgres.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <postgresql/libpq-fe.h>
+#include <libpq-int.h>
+#include <catalog/pg_type.h>
+#include <executor/spi.h>
+#include <funcapi.h>
+#include <fmgr.h>
+#include <utils/builtins.h>
+#include <utils/array.h>
+#include <sys/time.h>
+#include <utils/numeric.h>
+#include <utils/timestamp.h>
+#include <utils/bytea.h>
+
+#ifdef PG_MODULE_MAGIC
+PG_MODULE_MAGIC;
+#endif
+
+typedef struct
+{
+ Datum col1;
+ Datum col2;
+} valuest;
+
+void _PG_init (void);
+
+void _PG_fini (void);
+
+void
+_PG_init (void)
+{
+}
+
+
+PG_FUNCTION_INFO_V1 (pg_spi_insert_int);
+PG_FUNCTION_INFO_V1 (pg_spi_select_from_x);
+PG_FUNCTION_INFO_V1 (pg_spi_select_pair_from_y);
+// PG_FUNCTION_INFO_V1(pg_spi_select_with_cond);
+PG_FUNCTION_INFO_V1 (pg_spi_update_y);
+PG_FUNCTION_INFO_V1 (pg_spi_prepare_example);
+PG_FUNCTION_INFO_V1 (pg_spi_prepare_example_without_saveplan);
+PG_FUNCTION_INFO_V1 (pg_spi_prepare_insert);
+PG_FUNCTION_INFO_V1 (pg_spi_prepare_insert_without_saveplan);
+// PG_FUNCTION_INFO_V1(pg_spi_prepare_select_with_cond);
+PG_FUNCTION_INFO_V1 (pg_spi_prepare_select_with_cond_without_saveplan);
+PG_FUNCTION_INFO_V1 (pg_spi_prepare_update);
+PG_FUNCTION_INFO_V1 (pg_spi_get_dep_ref_fees);
+// SIMPLE SELECT
+Datum
+pg_spi_prepare_example (PG_FUNCTION_ARGS)
+{
+ static SPIPlanPtr prepared_plan;
+ int ret;
+ int64 result;
+ char *value;
+ SPIPlanPtr new_plan;
+
+ ret = SPI_connect ();
+ if (ret != SPI_OK_CONNECT)
+ {
+ elog (ERROR, "DB connection failed ! \n");
+ }
+ {
+ if (prepared_plan == NULL)
+ {
+ new_plan = SPI_prepare ("SELECT 1 FROM X", 0, NULL);
+ prepared_plan = SPI_saveplan (new_plan);
+
+ if (prepared_plan == NULL)
+ {
+ elog (ERROR, "FAIL TO SAVE !\n");
+ }
+ }
+
+ ret = SPI_execute_plan (prepared_plan, NULL, 0,false, 0);
+ if (ret != SPI_OK_SELECT)
+ {
+ elog (ERROR, "SELECT FAILED %d !\n", ret);
+ }
+
+ if (SPI_tuptable != NULL && SPI_tuptable->vals != NULL &&
+ SPI_tuptable->tupdesc != NULL)
+ {
+ value = SPI_getvalue (SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1);
+ result = atoi (value);
+ }
+ else
+ {
+ elog (ERROR, "EMPTY TABLE !\n");
+ }
+ }
+ SPI_finish ();
+ PG_RETURN_INT64 (result);
+}
+
+
+Datum
+pg_spi_prepare_example_without_saveplan (PG_FUNCTION_ARGS)
+{
+ int ret;
+ int64 result;
+ char *value;
+ SPIPlanPtr new_plan;
+
+ ret = SPI_connect ();
+ if (ret != SPI_OK_CONNECT)
+ {
+ elog (ERROR, "DB connection failed ! \n");
+ }
+
+ {
+ new_plan = SPI_prepare ("SELECT 1 FROM X", 0, NULL);
+ ret = SPI_execute_plan (new_plan, NULL, 0,false, 0);
+ if (ret != SPI_OK_SELECT)
+ {
+ elog (ERROR, "SELECT FAILED %d !\n", ret);
+ }
+
+ if (SPI_tuptable != NULL
+ && SPI_tuptable->vals != NULL
+ && SPI_tuptable->tupdesc != NULL)
+ {
+ value = SPI_getvalue (SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1);
+ result = atoi (value);
+ }
+ else
+ {
+ elog (ERROR, "EMPTY TABLE !\n");
+ }
+ }
+ SPI_finish ();
+ PG_RETURN_INT64 (result);// PG_RETURN_INT64(result);
+}
+
+
+// SELECT 1 FROM X
+// V1
+Datum
+pg_spi_select_from_x (PG_FUNCTION_ARGS)
+{
+ int ret;
+ char *query = "SELECT 1 FROM X";
+ uint64 proc;
+ ret = SPI_connect ();
+
+ if (ret != SPI_OK_CONNECT)
+ {
+ elog (ERROR, "SPI_connect failed");
+ }
+
+ ret = SPI_exec (query, 10);
+ proc = SPI_processed;
+ if (ret != SPI_OK_SELECT)
+ {
+ elog (ERROR, "SPI_exec failed");
+ }
+
+ SPI_finish ();
+
+ PG_RETURN_INT64 (proc);
+}
+
+
+// INSERT INTO X VALUES (1)
+Datum
+pg_spi_insert_int (PG_FUNCTION_ARGS)
+{
+ int ret;
+ int nargs;
+ Oid argtypes[1];
+ Datum values[1];
+ char *query = "INSERT INTO X (a) VALUES ($1)";
+
+ ret = SPI_connect ();
+ if (ret != SPI_OK_CONNECT)
+ {
+ elog (ERROR, "SPI_connect failed");
+ }
+
+ nargs = 1;
+ argtypes[0] = INT4OID;
+ values[0] = Int32GetDatum (3);
+
+ ret = SPI_execute_with_args (query, nargs, argtypes, values, NULL, false, 0);
+ if (ret != SPI_OK_INSERT)
+ {
+ elog (ERROR, "SPI_execute_with_args failed");
+ }
+
+ SPI_finish ();
+
+ PG_RETURN_VOID ();
+}
+
+
+Datum
+pg_spi_prepare_insert (PG_FUNCTION_ARGS)
+{
+ static SPIPlanPtr prepared_plan = NULL;
+ int ret;
+ int nargs;
+ Oid argtypes[1];
+ Datum values[1];
+ const char *query = "INSERT INTO X (a) VALUES ($1)";
+ SPIPlanPtr new_plan;
+ ret = SPI_connect ();
+ if (ret != SPI_OK_CONNECT)
+ {
+ elog (ERROR, "SPI_connect failed ! \n");
+ }
+ if (prepared_plan == NULL)
+ {
+
+ argtypes[0] = INT4OID;
+ nargs = 1;
+ values[0] = Int32GetDatum (3);
+ new_plan = SPI_prepare (query, nargs, argtypes);
+ if (new_plan== NULL)
+ {
+ elog (ERROR, "SPI_prepare failed ! \n");
+ }
+ prepared_plan = SPI_saveplan (new_plan);
+ if (prepared_plan == NULL)
+ {
+ elog (ERROR, "SPI_saveplan failed ! \n");
+ }
+ }
+
+ ret = SPI_execute_plan (prepared_plan, values, NULL, false, 0);
+ if (ret != SPI_OK_INSERT)
+ {
+ elog (ERROR, "SPI_execute_plan failed ! \n");
+ }
+
+ SPI_finish ();
+
+ PG_RETURN_VOID ();
+}
+
+
+/*
+Datum
+pg_spi_prepare_insert_bytea(PG_FUNCTION_ARGS)
+{
+ static SPIPlanPtr prepared_plan = NULL;
+ int ret;
+ int nargs;
+ Oid argtypes[1];
+ Datum values[1];
+ Oid argtypes2[1];
+ Datum val[1];
+ char *query = "INSERT INTO X (a) VALUES ($1)";
+ SPIPlanPtr new_plan;
+ ret = SPI_connect();
+ if (ret != SPI_OK_CONNECT)
+ {
+ elog(ERROR, "SPI_connect failed ! \n");
+ }
+ if (prepared_plan == NULL) {
+ argtypes2[0] = BOOLOID;
+ val[0] = BoolGetDatum();
+ argtypes[0] = BYTEAOID;
+ nargs = 1;
+ values[0] = Int32GetDatum(3);
+ new_plan = SPI_prepare(query, nargs, argtypes);
+ if (new_plan== NULL)
+ {
+ elog(ERROR, "SPI_prepare failed ! \n");
+ }
+ prepared_plan = SPI_saveplan(new_plan);
+ if (prepared_plan == NULL)
+ {
+ elog(ERROR, "SPI_saveplan failed ! \n");
+ }
+ }
+
+ ret = SPI_execute_plan(prepared_plan, values, NULL, false, 0);
+ if (ret != SPI_OK_INSERT)
+ {
+ elog(ERROR, "SPI_execute_plan failed ! \n");
+ }
+
+ SPI_finish();
+
+ PG_RETURN_VOID();
+}
+*/
+
+Datum
+pg_spi_prepare_insert_without_saveplan (PG_FUNCTION_ARGS)
+{
+ int ret;
+ int nargs;
+ Oid argtypes[1];
+ Datum values[1];
+ const char *query = "INSERT INTO X (a) VALUES ($1)";
+ SPIPlanPtr new_plan;
+ ret = SPI_connect ();
+ if (ret != SPI_OK_CONNECT)
+ {
+ elog (ERROR, "SPI_connect failed");
+ }
+ {
+ argtypes[0] = INT4OID;
+ nargs = 1;
+ values[0] = Int32GetDatum (3);
+ new_plan = SPI_prepare (query, nargs, argtypes);
+ if (new_plan== NULL)
+ {
+ elog (ERROR, "SPI_prepare failed");
+ }
+ }
+
+ ret = SPI_execute_plan (new_plan, values, NULL, false, 0);
+ if (ret != SPI_OK_INSERT)
+ {
+ elog (ERROR, "SPI_execute_plan failed");
+ }
+
+ SPI_finish ();
+
+ PG_RETURN_VOID ();
+}
+
+
+/*
+Datum
+pg_spi_select_pair_from_y(PG_FUNCTION_ARGS)
+{
+ int ret;
+ valuest result;
+ bool isnull;
+ char *query = "SELECT 1,1 FROM Y";
+ result.col1 = 0;
+ result.col2 = 0;
+
+ if ((ret = SPI_connect()) < 0) {
+ fprintf(stderr, "SPI_connect returned %d\n", ret);
+ exit(1);
+ }
+ ret = SPI_exec(query, 0);
+ if (ret == SPI_OK_SELECT && SPI_processed > 0) {
+ int i;
+ SPITupleTable *tuptable = SPI_tuptable;
+ TupleDesc tupdesc = tuptable->tupdesc;
+ for (i = 0; i < SPI_processed; i++) {
+ HeapTuple tuple = tuptable->vals[i];
+ result.col1 = SPI_getbinval(tuple, tupdesc, 1, &isnull);
+ result.col2 = SPI_getbinval(tuple, tupdesc, 2, &isnull);
+ }
+ }
+ SPI_finish();
+ PG_RETURN_TEXT_P(result);
+}
+*/
+
+// SELECT X FROM Y WHERE Z=$1
+/*
+Datum
+pg_spi_select_with_cond(PG_FUNCTION_ARGS)
+{
+ int ret;
+ char *query;
+ int nargs;
+ Oid argtypes[1];
+ Datum values[1];
+ uint64 proc;
+ query = "SELECT col1 FROM Y WHERE col2 = $1";
+
+ ret = SPI_connect();
+ if (ret != SPI_OK_CONNECT) {
+ elog(ERROR, "SPI_connect failed: %d", ret);
+ }
+ nargs = 1;
+ argtypes[0] = INT4OID;
+ values[0] = Int32GetDatum(2);
+
+ ret = SPI_execute_with_args(query, nargs, argtypes, values, NULL, false, 0);
+ proc = SPI_processed;
+ if (ret != SPI_OK_SELECT)
+ {
+ elog(ERROR, "SPI_execute_with_args failed");
+ }
+
+ SPI_finish();
+
+
+ PG_RETURN_INT64(proc);
+ }*/
+
+////////SELECT WITH COND
+/*
+Datum pg_spi_prepare_select_with_cond(PG_FUNCTION_ARGS) {
+ static SPIPlanPtr prepared_plan = NULL;
+ SPIPlanPtr new_plan;
+ int ret;
+ Datum values[1];
+ uint64 proc;
+ int nargs;
+ Oid argtypes[1];
+ char *query = "SELECT col1 FROM Y WHERE col1 = $1";
+ int result = 0;
+
+ ret = SPI_connect();
+ if (ret != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect failed ! \n");
+
+ if (prepared_plan == NULL) {
+
+ argtypes[0] = INT4OID;
+ nargs = 1;
+ values[0] = DatumGetByteaP(SPI_getbinval(tuptable->vals[0], tupdesc, 1, &isnull)); //Value col2
+
+ new_plan = SPI_prepare(query, nargs, argtypes);
+ if (new_plan == NULL)
+ elog(ERROR, "SPI_prepare failed ! \n");
+
+ prepared_plan = SPI_saveplan(new_plan);
+ if (prepared_plan == NULL)
+ elog(ERROR, "SPI_saveplan failed ! \n");
+ }
+
+
+ ret = SPI_execute_plan(prepared_plan, values, NULL, false, 0);
+
+ if (ret != SPI_OK_SELECT) {
+ elog(ERROR, "SPI_execute_plan failed: %d \n", ret);
+ }
+
+ proc = SPI_processed;
+
+ if (proc > 0) {
+ SPITupleTable *tuptable = SPI_tuptable;
+ TupleDesc tupdesc = tuptable->tupdesc;
+ HeapTuple tuple;
+ int i;
+
+ for (i = 0; i < proc; i++) {
+ tuple = tuptable->vals[i];
+ for (int j = 1; j <= tupdesc->natts; j++) {
+ char * value = SPI_getvalue(tuple, tupdesc, j);
+ result += atoi(value);
+ }
+ }
+ }
+ SPI_finish();
+ PG_RETURN_INT64(result);
+}
+*/
+
+Datum
+pg_spi_prepare_select_with_cond_without_saveplan (PG_FUNCTION_ARGS)
+{
+
+ SPIPlanPtr new_plan;
+ int ret;
+ Datum values[1];
+ uint64 proc;
+ int nargs;
+ Oid argtypes[1];
+ char *query = "SELECT col1 FROM Y WHERE col2 = $1";
+ int result = 0;
+
+ ret = SPI_connect ();
+ if (ret != SPI_OK_CONNECT)
+ elog (ERROR, "SPI_connect failed ! \n");
+
+ {
+
+ argtypes[0] = INT4OID;
+ nargs = 1;
+ values[0] = Int32GetDatum (2); // Value col2
+
+ new_plan = SPI_prepare (query, nargs, argtypes);
+ if (new_plan == NULL)
+ elog (ERROR, "SPI_prepare failed ! \n");
+
+ }
+
+
+ ret = SPI_execute_plan (new_plan, values, NULL, false, 0);
+
+ if (ret != SPI_OK_SELECT)
+ {
+ elog (ERROR, "SPI_execute_plan failed: %d \n", ret);
+ }
+
+ proc = SPI_processed;
+
+ if (proc > 0)
+ {
+ SPITupleTable *tuptable = SPI_tuptable;
+ TupleDesc tupdesc = tuptable->tupdesc;
+ HeapTuple tuple;
+ int i;
+
+ for (i = 0; i < proc; i++)
+ {
+ tuple = tuptable->vals[i];
+ for (int j = 1; j <= tupdesc->natts; j++)
+ {
+ char *value = SPI_getvalue (tuple, tupdesc, j);
+ result += atoi (value);
+ }
+ }
+ }
+ SPI_finish ();
+ PG_RETURN_INT64 (result);
+}
+
+
+Datum
+pg_spi_update_y (PG_FUNCTION_ARGS)
+{
+ int ret;
+ int nargs;
+ Oid argtypes[1];
+ Datum values[1];
+ const char *query = "UPDATE Y SET col1 = 4 WHERE (col2 = $1)";
+
+ ret = SPI_connect ();
+ if (ret != SPI_OK_CONNECT)
+ {
+ elog (ERROR, "SPI_connect failed ! \n");
+ }
+
+ nargs = 1;
+ argtypes[0] = INT4OID;
+ values[0] = Int32GetDatum (0);
+
+ ret = SPI_execute_with_args (query, nargs, argtypes, values, NULL, false, 0);
+ if (ret != SPI_OK_UPDATE)
+ {
+ elog (ERROR, "SPI_execute_with_args failed ! \n");
+ }
+
+ SPI_finish ();
+
+ PG_RETURN_VOID ();
+}
+
+
+Datum
+pg_spi_prepare_update (PG_FUNCTION_ARGS)
+{
+ static SPIPlanPtr prepared_plan = NULL;
+ SPIPlanPtr new_plan;
+ int ret;
+ int nargs;
+ Oid argtypes[1];
+ Datum values[1];
+ const char *query = "UPDATE Y SET col1 = 4 WHERE (col2 = $1)";
+
+ ret = SPI_connect ();
+ if (ret != SPI_OK_CONNECT)
+ {
+ elog (ERROR, "SPI_connect failed ! \n");
+ }
+
+ if (prepared_plan == NULL)
+ {
+ argtypes[0] = INT4OID;
+ nargs = 1;
+ values[0] = Int32GetDatum (3);
+ // PREPARE
+ new_plan = SPI_prepare (query, nargs, argtypes);
+ if (new_plan == NULL)
+ elog (ERROR, "SPI_prepare failed ! \n");
+ // SAVEPLAN
+ prepared_plan = SPI_saveplan (new_plan);
+ if (prepared_plan == NULL)
+ elog (ERROR, "SPI_saveplan failed ! \n");
+ }
+ ret = SPI_execute_plan (prepared_plan, values, NULL, false, 0);
+ if (ret != SPI_OK_UPDATE)
+ elog (ERROR, "SPI_execute_plan failed ! \n");
+
+ SPI_finish ();
+ PG_RETURN_VOID ();
+}
+
+
+/*
+Datum
+pg_spi_prepare_update_without_saveplan(PG_FUNCTION_ARGS)
+{}*/
+void
+_PG_fini (void)
+{
+}
+
+
+/*
+
+*/
+
+
+Datum
+pg_spi_get_dep_ref_fees (PG_FUNCTION_ARGS)
+{
+ /* Define plan to save */
+ static SPIPlanPtr deposit_plan;
+ static SPIPlanPtr ref_plan;
+ static SPIPlanPtr fees_plan;
+ static SPIPlanPtr dummy_plan;
+ /* Define variables to update */
+ Timestamp refund_deadline = PG_GETARG_TIMESTAMP (0);
+ bytea *merchant_pub = PG_GETARG_BYTEA_P (1);
+ bytea *wire_target_h_payto = PG_GETARG_BYTEA_P (2);
+ bytea *wtid_raw = PG_GETARG_BYTEA_P (3);
+ bool is_null;
+ /* Define variables to store the results of each SPI query */
+ uint64_t sum_deposit_val = 0;
+ uint32_t sum_deposit_frac = 0;
+ uint64_t s_refund_val = 0;
+ uint32_t s_refund_frac = 0;
+ uint64_t sum_dep_fee_val = 0;
+ uint32_t sum_dep_fee_frac = 0;
+ uint64_t norm_refund_val = 0;
+ uint32_t norm_refund_frac = 0;
+ uint64_t sum_refund_val = 0;
+ uint32_t sum_refund_frac = 0;
+ /* Define variables to store the Tuptable */
+ SPITupleTable *dep_res;
+ SPITupleTable *ref_res;
+ SPITupleTable *ref_by_coin_res;
+ SPITupleTable *norm_ref_by_coin_res;
+ SPITupleTable *fully_refunded_coins_res;
+ SPITupleTable *fees_res;
+ SPITupleTable *dummys_res;
+ /* Define variable to update */
+ Datum values_refund[2];
+ Datum values_deposit[3];
+ Datum values_fees[2];
+ Datum values_dummys[2];
+ TupleDesc tupdesc;
+ /* Define variables to replace some tables */
+ bytea *ref_by_coin_coin_pub;
+ int64 ref_by_coin_deposit_serial_id = 0;
+ bytea *norm_ref_by_coin_coin_pub;
+ int64_t norm_ref_by_coin_deposit_serial_id = 0;
+ bytea *new_dep_coin_pub = NULL;
+ int res = SPI_connect ();
+
+ /* Connect to SPI */
+ if (res < 0)
+ {
+ elog (ERROR, "Could not connect to SPI manager");
+ }
+ if (deposit_plan == NULL)
+ {
+ const char *dep_sql;
+ SPIPlanPtr new_plan;
+
+ // Execute first query and store results in variables
+ dep_sql =
+ "UPDATE deposits SET done=TRUE "
+ "WHERE NOT (done OR policy_blocked) "
+ "AND refund_deadline=$1 "
+ "AND merchant_pub=$2 "
+ "AND wire_target_h_payto=$3 "
+ "RETURNING "
+ "deposit_serial_id,"
+ "coin_pub,"
+ "amount_with_fee_val,"
+ "amount_with_fee_frac;";
+ fprintf (stderr, "dep sql %d\n", 1);
+ new_plan =
+ SPI_prepare (dep_sql, 4,(Oid[]){INT8OID, BYTEAOID, BYTEAOID});
+ fprintf (stderr, "dep sql %d\n", 2);
+ if (new_plan == NULL)
+ elog (ERROR, "SPI_prepare failed for dep \n");
+ deposit_plan = SPI_saveplan (new_plan);
+ if (deposit_plan == NULL)
+ elog (ERROR, "SPI_saveplan failed for dep \n");
+ }
+ fprintf (stdout, "dep sql %d\n", 3);
+
+ values_deposit[0] = Int64GetDatum (refund_deadline);
+ values_deposit[1] = PointerGetDatum (merchant_pub);
+ values_deposit[2] = PointerGetDatum (wire_target_h_payto);
+
+ res = SPI_execute_plan (deposit_plan,
+ values_deposit,
+ NULL,
+ true,
+ 0);
+ fprintf (stdout, "dep sql %d\n", 4);
+ if (res != SPI_OK_UPDATE)
+ {
+ elog (ERROR, "Failed to execute subquery 1 \n");
+ }
+ // STORE TUPTABLE deposit
+ dep_res = SPI_tuptable;
+
+ for (unsigned int i = 0; i < SPI_processed; i++)
+ {
+ int64 dep_deposit_serial_ids = DatumGetInt64 (SPI_getbinval (
+ SPI_tuptable->vals[i],
+ SPI_tuptable->tupdesc, 1,
+ &is_null));
+ bytea *dep_coin_pub = DatumGetByteaP (SPI_getbinval (SPI_tuptable->vals[i],
+ SPI_tuptable->tupdesc,
+ 2, &is_null));
+ int64 dep_amount_val = DatumGetInt64 (SPI_getbinval (SPI_tuptable->vals[i],
+ SPI_tuptable->tupdesc,
+ 3, &is_null));
+ int32 dep_amount_frac = DatumGetInt32 (SPI_getbinval (SPI_tuptable->vals[i],
+ SPI_tuptable->tupdesc,
+ 4, &is_null));
+
+ if (is_null)
+ elog (ERROR, "Failed to retrieve data from deposit \n");
+ if (ref_plan == NULL)
+ {
+ // Execute second query with parameters from first query and store results in variables
+ const char *ref_sql =
+ "SELECT amount_with_fee_val, amount_with_fee_frac, coin_pub, deposit_serial_id "
+ "FROM refunds "
+ "WHERE coin_pub=$1 "
+ "AND deposit_serial_id=$2;";
+ SPIPlanPtr new_plan = SPI_prepare (ref_sql, 3, (Oid[]){BYTEAOID,
+ INT8OID});
+ if (new_plan == NULL)
+ elog (ERROR, "SPI_prepare failed for refund\n");
+ ref_plan = SPI_saveplan (new_plan);
+ if (ref_plan == NULL)
+ elog (ERROR, "SPI_saveplan failed for refund\n");
+ }
+ values_refund[0] = PointerGetDatum (dep_coin_pub);
+ values_refund[1] = Int64GetDatum (dep_deposit_serial_ids);
+ res = SPI_execute_plan (ref_plan,
+ values_refund,
+ NULL,
+ false,
+ 0);
+ if (res != SPI_OK_SELECT)
+ elog (ERROR, "Failed to execute subquery 2\n");
+ // STORE TUPTABLE refund
+ ref_res = SPI_tuptable;
+ for (unsigned int j = 0; j < SPI_processed; j++)
+ {
+ int64 ref_refund_val = DatumGetInt64 (SPI_getbinval (
+ SPI_tuptable->vals[j],
+ SPI_tuptable->tupdesc, 1,
+ &is_null));
+ int32 ref_refund_frac = DatumGetInt32 (SPI_getbinval (
+ SPI_tuptable->vals[j],
+ SPI_tuptable->tupdesc, 2,
+ &is_null));
+ bytea *ref_coin_pub = DatumGetByteaP (SPI_getbinval (
+ SPI_tuptable->vals[j],
+ SPI_tuptable->tupdesc, 3,
+ &is_null));
+ int64 ref_deposit_serial_id = DatumGetInt64 (SPI_getbinval (
+ SPI_tuptable->vals[j],
+ SPI_tuptable->tupdesc, 4,
+ &is_null));
+ // Execute third query with parameters from second query and store results in variables
+ ref_by_coin_coin_pub = ref_coin_pub;
+ ref_by_coin_deposit_serial_id = ref_deposit_serial_id;
+ // LOOP TO GET THE SUM FROM REFUND BY COIN
+ for (unsigned int i = 0; i<SPI_processed; i++)
+ {
+ if ((ref_by_coin_coin_pub ==
+ DatumGetByteaP (SPI_getbinval (SPI_tuptable->vals[i],
+ SPI_tuptable->tupdesc, 1,
+ &is_null)))
+ &&
+ (ref_by_coin_deposit_serial_id ==
+ DatumGetUInt64 (SPI_getbinval (SPI_tuptable->vals[i],
+ SPI_tuptable->tupdesc, 2,
+ &is_null)))
+ )
+ {
+ sum_refund_val += ref_refund_val;
+ sum_refund_frac += ref_refund_frac;
+ norm_ref_by_coin_coin_pub = ref_by_coin_coin_pub;
+ norm_ref_by_coin_deposit_serial_id = ref_by_coin_deposit_serial_id;
+ }
+ }// END SUM CALCULATION
+ // NORMALIZE REFUND VAL FRAC
+ norm_refund_val =
+ (sum_refund_val + sum_refund_frac) / 100000000;
+ norm_refund_frac =
+ sum_refund_frac % 100000000;
+ // Get refund values
+ s_refund_val += sum_refund_val;
+ s_refund_frac = sum_refund_frac;
+ }// END REFUND
+ if (norm_ref_by_coin_coin_pub == dep_coin_pub
+ && ref_by_coin_deposit_serial_id == dep_deposit_serial_ids
+ && norm_refund_val == dep_amount_val
+ && norm_refund_frac == dep_amount_frac)
+ {
+ new_dep_coin_pub = dep_coin_pub;
+ }
+ // Ensure we get the fee for each coin and not only once per denomination
+ if (fees_plan == NULL)
+ {
+ const char *fees_sql =
+ "SELECT "
+ " denom.fee_deposit_val AS fee_val, "
+ " denom.fee_deposit_frac AS fee_frac, "
+ "FROM known_coins kc"
+ "JOIN denominations denom USING (denominations_serial) "
+ "WHERE kc.coin_pub = $1 AND kc.coin_pub != $2;";
+ SPIPlanPtr new_plan = SPI_prepare (fees_sql, 3, (Oid[]){BYTEAOID,
+ BYTEAOID});
+ if (new_plan == NULL)
+ {
+ elog (ERROR, "SPI_prepare for fees failed ! \n");
+ }
+ fees_plan = SPI_saveplan (new_plan);
+ if (fees_plan == NULL)
+ {
+ elog (ERROR, "SPI_saveplan for fees failed ! \n");
+ }
+ }
+ values_fees[0] = PointerGetDatum (dep_coin_pub);
+ values_fees[1] = PointerGetDatum (new_dep_coin_pub);
+ res = SPI_execute_plan (fees_plan, values_fees, NULL, false, 0);
+ if (res != SPI_OK_SELECT)
+ elog (ERROR, "SPI_execute_plan failed for fees \n");
+ fees_res = SPI_tuptable;
+ tupdesc = fees_res->tupdesc;
+ for (unsigned int i = 0; i<SPI_processed; i++)
+ {
+ HeapTuple tuple = fees_res->vals[i];
+ bool is_null;
+ uint64_t fee_val = DatumGetUInt64 (SPI_getbinval (tuple, tupdesc, 1,
+ &is_null));
+ uint32_t fee_frac = DatumGetUInt32 (SPI_getbinval (tuple, tupdesc, 2,
+ &is_null));
+ uint64_t fees_deposit_serial_id = DatumGetUInt64 (SPI_getbinval (tuple,
+ tupdesc,
+ 3,
+ &is_null));
+ if (dummy_plan == NULL)
+ {
+ const char *insert_dummy_sql =
+ "INSERT INTO "
+ "aggregation_tracking(deposit_serial_id, wtid_raw)"
+ " VALUES ($1, $2)";
+
+ SPIPlanPtr new_plan = SPI_prepare (insert_dummy_sql, 2, (Oid[]){INT8OID,
+ BYTEAOID});
+ if (new_plan == NULL)
+ elog (ERROR, "FAILED to prepare aggregation tracking \n");
+ dummy_plan = SPI_saveplan (new_plan);
+ if (dummy_plan == NULL)
+ elog (ERROR, "FAILED to saveplan aggregation tracking\n");
+ }
+ values_dummys[0] = Int64GetDatum (dep_deposit_serial_ids);
+ values_dummys[1] = PointerGetDatum (wtid_raw);
+ res = SPI_execute_plan (dummy_plan, values_dummys, NULL, false, 0);
+ if (res != SPI_OK_INSERT)
+ elog (ERROR, "Failed to insert dummy\n");
+ dummys_res = SPI_tuptable;
+ // Calculation of deposit fees for not fully refunded deposits
+ sum_dep_fee_val += fee_val;
+ sum_dep_fee_frac += fee_frac;
+ }
+ // Get deposit values
+ sum_deposit_val += dep_amount_val;
+ sum_deposit_frac += dep_amount_frac;
+ }// END DEPOSIT
+ SPI_finish ();
+ PG_RETURN_VOID ();
+}
diff --git a/src/exchangedb/spi/own_test.control b/src/exchangedb/spi/own_test.control
new file mode 100644
index 000000000..4e73e207f
--- /dev/null
+++ b/src/exchangedb/spi/own_test.control
@@ -0,0 +1,4 @@
+comment = 'Example extension for testing purposes'
+default_version = '1.0'
+module_pathname = '$libdir/own_test'
+relocatable = true
diff --git a/src/exchangedb/spi/own_test.sql b/src/exchangedb/spi/own_test.sql
new file mode 100644
index 000000000..12729d068
--- /dev/null
+++ b/src/exchangedb/spi/own_test.sql
@@ -0,0 +1,201 @@
+DROP TABLE IF EXISTS X;
+CREATE TABLE X (
+ a integer
+);
+
+INSERT INTO X (a)
+ VALUES (1), (2), (3), (4), (5), (6), (7);
+
+DROP TABLE IF EXISTS Y;
+CREATE TABLE Y (col1 INT, col2 INT);
+INSERT INTO Y (col1,col2)
+ VALUES (1,2), (2,0), (0,4), (4,0), (0,6), (6,7), (7,8);
+
+DROP TABLE IF EXISTS Z;
+CREATE TABLE Z (col1 BYTEA);
+
+DROP TABLE IF EXISTS deposits;
+CREATE TABLE deposits(
+ deposit_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
+ ,shard INT8 NOT NULL
+ ,coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)
+ ,known_coin_id INT8 NOT NULL
+ ,amount_with_fee_val INT8 NOT NULL
+ ,amount_with_fee_frac INT4 NOT NULL
+ ,wallet_timestamp INT8 NOT NULL
+ ,exchange_timestamp INT8 NOT NULL
+ ,refund_deadline INT8 NOT NULL
+ ,wire_deadline INT8 NOT NULL
+ ,merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)
+ ,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
+ ,coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)
+ ,wire_salt BYTEA NOT NULL CHECK (LENGTH(wire_salt)=16)
+ ,wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)
+ ,done BOOLEAN NOT NULL DEFAULT FALSE
+ ,policy_blocked BOOLEAN NOT NULL DEFAULT FALSE
+ ,policy_details_serial_id INT8);
+
+
+DROP FUNCTION IF EXISTS pg_spi_insert_int;
+CREATE FUNCTION pg_spi_insert_int()
+ RETURNS VOID
+ LANGUAGE c VOLATILE COST 100
+AS '$libdir/own_test', 'pg_spi_insert_int';
+
+DROP FUNCTION IF EXISTS pg_spi_select_from_x;
+CREATE FUNCTION pg_spi_select_from_x()
+ RETURNS INT8
+ LANGUAGE c COST 100
+AS '$libdir/own_test', 'pg_spi_select_from_x';
+
+/*
+CREATE FUNCTION pg_spi_select_pair_from_y()
+ RETURNS valuest
+ LANGUAGE c COST 100
+AS '$libdir/own_test', 'pg_spi_select_pair_from_y';
+*/
+/*CREATE FUNCTION pg_spi_select_with_cond()
+ RETURNS INT8
+ LANGUAGE c COST 100
+AS '$libdir/own_test', 'pg_spi_select_with_cond';
+*/
+
+DROP FUNCTION IF EXISTS pg_spi_update_y;
+CREATE FUNCTION pg_spi_update_y()
+ RETURNS VOID
+ LANGUAGE c VOLATILE COST 100
+AS '$libdir/own_test', 'pg_spi_update_y';
+
+DROP FUNCTION IF EXISTS pg_spi_prepare_example;
+CREATE FUNCTION pg_spi_prepare_example()
+ RETURNS INT8
+ LANGUAGE c COST 100
+AS '$libdir/own_test', 'pg_spi_prepare_example';
+
+DROP FUNCTION IF EXISTS pg_spi_prepare_example_without_saveplan;
+CREATE FUNCTION pg_spi_prepare_example_without_saveplan()
+ RETURNS INT8
+ LANGUAGE c COST 100
+AS '$libdir/own_test', 'pg_spi_prepare_example_without_saveplan';
+
+DROP FUNCTION IF EXISTS pg_spi_prepare_insert;
+CREATE FUNCTION pg_spi_prepare_insert()
+ RETURNS VOID
+ LANGUAGE c VOLATILE COST 100
+AS '$libdir/own_test', 'pg_spi_prepare_insert';
+
+DROP FUNCTION IF EXISTS pg_spi_prepare_insert_without_saveplan;
+CREATE FUNCTION pg_spi_prepare_insert_without_saveplan()
+ RETURNS VOID
+ LANGUAGE c VOLATILE COST 100
+AS '$libdir/own_test', 'pg_spi_prepare_insert_without_saveplan';
+
+/*
+CREATE FUNCTION pg_spi_prepare_select_with_cond()
+ RETURNS INT8
+ LANGUAGE c COST 100
+AS '$libdir/own_test', 'pg_spi_prepare_select_with_cond';
+*/
+
+DROP FUNCTION IF EXISTS pg_spi_prepare_select_with_cond_without_saveplan;
+CREATE FUNCTION pg_spi_prepare_select_with_cond_without_saveplan()
+ RETURNS INT8
+ LANGUAGE c COST 100
+AS '$libdir/own_test', 'pg_spi_prepare_select_with_cond_without_saveplan';
+
+DROP FUNCTION IF EXISTS pg_spi_prepare_update;
+CREATE FUNCTION pg_spi_prepare_update()
+ RETURNS VOID
+ LANGUAGE c VOLATILE COST 100
+AS '$libdir/own_test', 'pg_spi_prepare_update';
+
+DROP FUNCTION IF EXISTS pg_spi_get_dep_ref_fees;
+CREATE FUNCTION pg_spi_get_dep_ref_fees(
+ IN in_timestamp INT8
+ ,IN merchant_pub BYTEA
+ ,IN wire_target_h_payto BYTEA
+ ,IN wtid BYTEA
+)
+ RETURNS VOID
+ LANGUAGE c VOLATILE COST 100
+AS '$libdir/own_test', 'pg_spi_get_dep_ref_fees';
+
+DROP FUNCTION IF EXISTS update_pg_spi_get_dep_ref_fees;
+CREATE FUNCTION update_pg_spi_get_dep_ref_fees(
+ IN in_refund_deadline INT8,
+ IN in_merchant_pub BYTEA,
+ IN in_wire_target_h_payto BYTEA
+)
+RETURNS SETOF record
+LANGUAGE plpgsql VOLATILE
+AS $$
+DECLARE
+
+BEGIN
+RETURN QUERY
+ UPDATE deposits
+ SET done = TRUE
+ WHERE NOT (done OR policy_blocked)
+ AND refund_deadline < in_refund_deadline
+ AND merchant_pub = in_merchant_pub
+ AND wire_target_h_payto = in_wire_target_h_payto
+ RETURNING
+ deposit_serial_id,
+ coin_pub,
+ amount_with_fee_val,
+ amount_with_fee_frac;
+END $$;
+
+DROP FUNCTION IF EXISTS stored_procedure_update;
+CREATE FUNCTION stored_procedure_update(
+IN in_number INT8
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ UPDATE Y
+ SET col1 = 4
+ WHERE col2 = in_number;
+END $$;
+
+DROP FUNCTION IF EXISTS stored_procedure_select;
+CREATE FUNCTION stored_procedure_select(OUT out_value INT8)
+RETURNS INT8
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ SELECT 1
+ INTO out_value
+ FROM X;
+ RETURN;
+END $$;
+
+
+DROP FUNCTION IF EXISTS stored_procedure_insert;
+CREATE FUNCTION stored_procedure_insert(
+IN in_number INT8,
+OUT out_number INT8)
+RETURNS INT8
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ INSERT INTO X (a)
+ VALUES (in_number)
+ RETURNING a INTO out_number;
+END $$;
+
+DROP FUNCTION IF EXISTS stored_procedure_select_with_cond;
+CREATE FUNCTION stored_procedure_select_with_cond(
+IN in_number INT8,
+OUT out_number INT8
+)
+RETURNS INT8
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ SELECT col1 INTO out_number
+ FROM Y
+ WHERE col2 = in_number;
+ RETURN;
+END $$;
diff --git a/src/exchangedb/spi/perf_own_test.c b/src/exchangedb/spi/perf_own_test.c
new file mode 100644
index 000000000..92be2235e
--- /dev/null
+++ b/src/exchangedb/spi/perf_own_test.c
@@ -0,0 +1,25 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2023 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 exchangedb/spi/perf_own_test.c
+ * @brief benchmark for 'own_test'
+ * @author Joseph Xu
+ */
+#include "exchangedb/platform.h"
+#include "exchangedb/taler_exchangedb_lib.h"
+#include "exchangedb/taler_json_lib.h"
+#include "exchangedb/taler_exchangedb_plugin.h"
+#include "own_test.sql"
diff --git a/src/exchangedb/spi/pg_aggregate.c b/src/exchangedb/spi/pg_aggregate.c
new file mode 100644
index 000000000..721f247c7
--- /dev/null
+++ b/src/exchangedb/spi/pg_aggregate.c
@@ -0,0 +1,411 @@
+#include "postgres.h"
+#include "fmgr.h"
+#include "utils/numeric.h"
+#include "utils/builtins.h"
+#include "executor/spi.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1 (get_deposit_summary);
+
+Datum
+get_deposit_summary (PG_FUNCTION_ARGS)
+{
+
+ static SPIPlanPtr deposit_plan;
+ static SPIPlanPtr refund_plan;
+ static SPIPlanPtr refund_by_coin_plan;
+ static SPIPlanPtr norm_refund_by_coin_plan;
+ static SPIPlanPtr fully_refunded_by_coins_plan;
+ static SPIPlanPtr fees_plan;
+
+ int shard = PG_GETARG_INT32 (0);
+ char *sql;
+ char *merchant_pub = text_to_cstring (PG_GETARG_TEXT_P (1));
+ char *wire_target_h_payto = text_to_cstring (PG_GETARG_TEXT_P (2));
+ char *wtid_raw = text_to_cstring (PG_GETARG_TEXT_P (3));
+ int refund_deadline = PG_GETARG_INT32 (4);
+ int conn = SPI_connect ();
+ if (conn != SPI_OK_CONNECT)
+ {
+ elog (ERROR, "DB connection failed ! \n");
+ }
+
+ if (deposit_plan == NULL
+ || refund_plan == NULL
+ || refund_by_coin_plan == NULL
+ || norm_refund_by_coin_plan = NULL
+ || fully_refunded_coins_plan = NULL
+ || fees_plan
+ == NULL)
+ {
+ if (deposit_plan == NULL)
+ {
+ int nargs = 3;
+ Oid argtypes[3];
+ argtypes[0] = INT8OID;
+ argtypes[1] = BYTEAOID;
+ argtypes[2] = BYTEAOID;
+ const char *dep_sql =
+ " UPDATE deposits"
+ " SET done=TRUE"
+ " WHERE NOT (done OR policy_blocked)"
+ " AND refund_deadline < $1"
+ " AND merchant_pub = $2"
+ " AND wire_target_h_payto = $3"
+ " RETURNING"
+ " deposit_serial_id"
+ " ,coin_pub"
+ " ,amount_with_fee_val AS amount_val"
+ " ,amount_with_fee_frac AS amount_frac";
+ SPIPlanPtr new_plan =
+ SPI_prepare (dep_sql, 4, argtypes);
+ if (new_plan == NULL)
+ {
+ elog (ERROR, "SPI_prepare for deposit failed ! \n");
+ }
+ deposit_plan = SPI_saveplan (new_plan);
+ if (deposit_plan == NULL)
+ {
+ elog (ERROR, "SPI_saveplan for deposit failed ! \n");
+ }
+ }
+
+ Datum values[4];
+ values[0] = Int64GetDatum (refund_deadline);
+ values[1] = CStringGetDatum (merchant_pub);
+ values[2] = CStringGetDatum (wire_target_h_payto);
+ int ret = SPI_execute_plan (deposit_plan,
+ values,
+ NULL,
+ true,
+ 0);
+ if (ret != SPI_OK_UPDATE)
+ {
+ elog (ERROR, "Failed to execute subquery 1\n");
+ }
+ uint64_t *dep_deposit_serial_ids = palloc (sizeof(uint64_t)
+ * SPI_processed);
+ BYTEA **dep_coin_pubs = palloc (sizeof(BYTEA *) * SPI_processed);
+ uint64_t *dep_amount_vals = palloc (sizeof(uint64_t) * SPI_processed);
+ uint32_t *dep_amount_fracs = palloc (sizeof(uint32_t) * SPI_processed);
+ for (unsigned int i = 0; i < SPI_processed; i++)
+ {
+ HeapTuple tuple = SPI_tuptable->vals[i];
+ dep_deposit_serial_ids[i] =
+ DatumGetInt64 (SPI_getbinval (tuple, SPI_tuptable->tupdesc, 1, &ret));
+ dep_coin_pubs[i] =
+ DatumGetByteaP (SPI_getbinval (tuple, SPI_tuptable->tupdesc, 2, &ret));
+ dep_amount_vals[i] =
+ DatumGetInt64 (SPI_getbinval (tuple, SPI_tuptable->tupdesc, 3, &ret));
+ dep_amount_fracs[i] =
+ DatumGetInt32 (SPI_getbinval (tuple, SPI_tuptable->tupdesc, 4, &ret));
+ }
+
+
+ if (refund_plan == NULL)
+ {
+ const char *ref_sql =
+ "ref AS ("
+ " SELECT"
+ " amount_with_fee_val AS refund_val"
+ " ,amount_with_fee_frac AS refund_frac"
+ " ,coin_pub"
+ " ,deposit_serial_id"
+ " FROM refunds"
+ " WHERE coin_pub IN (SELECT coin_pub FROM dep)"
+ " AND deposit_serial_id IN (SELECT deposit_serial_id FROM dep)) ";
+ SPIPlanPtr new_plan = SPI_prepare (ref_sql, 0, NULL);
+ if (new_plan == NULL)
+ elog (ERROR, "SPI_prepare for refund failed ! \n");
+ refund_plan = SPI_saveplan (new_plan);
+ if (refund_plan == NULL)
+ {
+ elog (ERROR, "SPI_saveplan for refund failed ! \n");
+ }
+ }
+
+ int64t_t *ref_deposit_serial_ids = palloc (sizeof(int64_t) * SPI_processed);
+
+ int res = SPI_execute_plan (refund_plan, NULL, NULL, false, 0);
+ if (res != SPI_OK_SELECT)
+ {
+ elog (ERROR, "Failed to execute subquery 2\n");
+ }
+ SPITupleTable *tuptable = SPI_tuptable;
+ TupleDesc tupdesc = tuptable->tupdesc;
+ for (unsigned int i = 0; i < SPI_processed; i++)
+ {
+ HeapTuple tuple = tuptable->vals[i];
+ Datum refund_val = SPI_getbinval (tuple, tupdesc, 1, &refund_val_isnull);
+ Datum refund_frac = SPI_getbinval (tuple, tupdesc, 2,
+ &refund_frac_isnull);
+ Datum coin_pub = SPI_getbinval (tuple, tupdesc, 3, &coin_pub_isnull);
+ Datum deposit_serial_id = SPI_getbinval (tuple, tupdesc, 4,
+ &deposit_serial_id_isnull);
+ if (refund_val_isnull
+ || refund_frac_isnull
+ || coin_pub_isnull
+ || deposit_serial_id_isnull)
+ {
+ elog (ERROR, "Failed to retrieve data from subquery 2");
+ }
+ uint64_t refund_val_int = DatumGetUInt64 (refund_val);
+ uint32_t refund_frac_int = DatumGetUInt32 (refund_frac);
+ BYTEA coin_pub = DatumGetByteaP (coin_pub);
+ ref_deposit_serial_ids = DatumGetInt64 (deposit_serial_id);
+
+ refund *new_refund = (refund*) palloc (sizeof(refund));
+ new_refund->coin_pub = coin_pub_str;
+ new_refund->deposit_serial_id = deposit_serial_id_int;
+ new_refund->amount_with_fee_val = refund_val_int;
+ new_refund->amount_with_fee_frac = refund_frac_int;
+ }
+
+
+ if (refund_by_coin_plan == NULL)
+ {
+ const char *ref_by_coin_sql =
+ "ref_by_coin AS ("
+ " SELECT"
+ " SUM(refund_val) AS sum_refund_val"
+ " ,SUM(refund_frac) AS sum_refund_frac"
+ " ,coin_pub"
+ " ,deposit_serial_id"
+ " FROM ref"
+ " GROUP BY coin_pub, deposit_serial_id) ";
+ SPIPlanPtr new_plan = SPI_prepare (ref_by_coin_sql, 0, NULL);
+ if (new_plan == NULL)
+ elog (ERROR, "SPI_prepare for refund by coin failed ! \n");
+ refund_by_coin_plan = SPI_saveplan (new_plan);
+ if (refund_by_coin_plan == NULL)
+ elog (ERROR, "SPI_saveplan for refund failed");
+ }
+
+
+ int res = SPI_execute_plan (refund_by_coin_plan, NULL, NULL, false, 0);
+ if (res != SPI_OK_SELECT)
+ {
+ elog (ERROR, "Failed to execute subquery 2\n");
+ }
+
+ SPITupleTable *tuptable = SPI_tuptable;
+ TupleDesc tupdesc = tuptable->tupdesc;
+ for (unsigned int i = 0; i < SPI_processed; i++)
+ {
+ HeapTuple tuple = tuptable->vals[i];
+ Datum sum_refund_val = SPI_getbinval (tuple, tupdesc, 1,
+ &refund_val_isnull);
+ Datum sum_refund_frac = SPI_getbinval (tuple, tupdesc, 2,
+ &refund_frac_isnull);
+ Datum coin_pub = SPI_getbinval (tuple, tupdesc, 3, &coin_pub_isnull);
+ Datum deposit_serial_id_int = SPI_getbinval (tuple, tupdesc, 4,
+ &deposit_serial_id_isnull);
+ if (refund_val_isnull
+ || refund_frac_isnull
+ || coin_pub_isnull
+ || deposit_serial_id_isnull)
+ {
+ elog (ERROR, "Failed to retrieve data from subquery 2");
+ }
+ uint64_t s_refund_val_int = DatumGetUInt64 (sum_refund_val);
+ uint32_t s_refund_frac_int = DatumGetUInt32 (sum_refund_frac);
+ BYTEA coin_pub = DatumGetByteaP (coin_pub);
+ uint64_t deposit_serial_id_int = DatumGetInt64 (deposit_serial_id_int);
+ refund *new_refund_by_coin = (refund*) palloc (sizeof(refund));
+ new_refund_by_coin->coin_pub = coin_pub;
+ new_refund_by_coin->deposit_serial_id = deposit_serial_id_int;
+ new_refund_by_coin->refund_amount_with_fee_val = s_refund_val_int;
+ new_refund_by_coin->refund_amount_with_fee_frac = s_refund_frac_int;
+ }
+
+
+ if (norm_refund_by_coin_plan == NULL)
+ {
+ const char *norm_ref_by_coin_sql =
+ "norm_ref_by_coin AS ("
+ " SELECT"
+ " coin_pub"
+ " ,deposit_serial_id"
+ " FROM ref_by_coin) ";
+ SPIPlanPtr new_plan = SPI_prepare (norm_ref_by_coin_sql, 0, NULL);
+ if (new_plan == NULL)
+ elog (ERROR, "SPI_prepare for norm refund by coin failed ! \n");
+ norm_refund_by_coin_plan = SPI_saveplan (new_plan);
+ if (norm_refund_by_coin_plan == NULL)
+ elog (ERROR, "SPI_saveplan for norm refund by coin failed ! \n");
+ }
+
+ double norm_refund_val =
+ ((double) new_refund_by_coin->refund_amount_with_fee_val
+ + (double) new_refund_by_coin->refund_amount_with_fee_frac) / 100000000;
+ double norm_refund_frac =
+ (double) new_refund_by_coin->refund_amount_with_fee_frac % 100000000;
+
+ if (fully_refunded_coins_plan == NULL)
+ {
+ const char *fully_refunded_coins_sql =
+ "fully_refunded_coins AS ("
+ " SELECT"
+ " dep.coin_pub"
+ " FROM norm_ref_by_coin norm"
+ " JOIN dep"
+ " ON (norm.coin_pub = dep.coin_pub"
+ " AND norm.deposit_serial_id = dep.deposit_serial_id"
+ " AND norm.norm_refund_val = dep.amount_val"
+ " AND norm.norm_refund_frac = dep.amount_frac)) ";
+ SPIPlanPtr new_plan =
+ SPI_prepare (fully_refunded_coins_sql, 0, NULL);
+ if (new_plan == NULL)
+ elog (ERROR, "SPI_prepare for fully refunded coins failed ! \n");
+ fully_refunded_coins_plan = SPI_saveplan (new_plan);
+ if (fully_refunded_coins_plan == NULL)
+ elog (ERROR, "SPI_saveplan for fully refunded coins failed ! \n");
+ }
+
+ int res = SPI_execute_plan (fully_refunded_coins_sql);
+ if (res != SPI_OK_SELECT)
+ elog (ERROR, "Failed to execute subquery 4\n");
+ SPITupleTable *tuptable = SPI_tuptable;
+ TupleDesc tupdesc = tuptable->tupdesc;
+
+ BYTEA coin_pub = SPI_getbinval (tuple, tupdesc, 1, &coin_pub_isnull);
+ if (fees_plan == NULL)
+ {
+ const char *fees_sql =
+ "SELECT "
+ " denom.fee_deposit_val AS fee_val, "
+ " denom.fee_deposit_frac AS fee_frac, "
+ " cs.deposit_serial_id "
+ "FROM dep cs "
+ "JOIN known_coins kc USING (coin_pub) "
+ "JOIN denominations denom USING (denominations_serial) "
+ "WHERE coin_pub NOT IN (SELECT coin_pub FROM fully_refunded_coins)";
+ SPIPlanPtr new_plan =
+ SPI_prepare (fees_sql, 0, NULL);
+ if (new_plan == NULL)
+ {
+ elog (ERROR, "SPI_prepare for fees failed ! \n");
+ }
+ fees_plan = SPI_saveplan (new_plan);
+ if (fees_plan == NULL)
+ {
+ elog (ERROR, "SPI_saveplan for fees failed ! \n");
+ }
+ }
+ }
+ int fees_ntuples;
+ SPI_execute (fees_sql, true, 0);
+ if (SPI_result_code () != SPI_OK_SELECT)
+ {
+ ereport (
+ ERROR,
+ (errcode (ERRCODE_INTERNAL_ERROR),
+ errmsg ("deposit fee query failed: error code %d \n",
+ SPI_result_code ())));
+ }
+ fees_ntuples = SPI_processed;
+
+ if (fees_ntuples > 0)
+ {
+ for (i = 0; i < fees_ntuples; i++)
+ {
+ Datum fee_val_datum =
+ SPI_getbinval (SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1,
+ &fee_null);
+ Datum fee_frac_datum =
+ SPI_getbinval (SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2,
+ &fee_null);
+ Datum deposit_id_datum =
+ SPI_getbinval (SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 3,
+ &deposit_null);
+ if (! fee_null && ! deposit_null)
+ {
+ int64 fee_val = DatumGetInt64 (fee_val_datum);
+ int32 fee_frac = DatumGetInt32 (fee_frac_datum);
+ int64 deposit_id = DatumGetInt64 (deposit_id_datum);
+ sum_fee_value += fee_val;
+ sum_fee_fraction += fee_frac;
+ char *insert_agg_sql =
+ psprintf (
+ "INSERT INTO "
+ "aggregation_tracking(deposit_serial_id, wtid_raw)"
+ " VALUES (%lld, '%s')",
+ deposit_id, wtid_raw);
+ SPI_execute (insert_agg_sql, false, 0);
+ }
+ }
+ }
+
+ TupleDesc tupdesc;
+ SPITupleTable *tuptable = SPI_tuptable;
+ HeapTuple tuple;
+ Datum result;
+
+ if (tuptable == NULL || SPI_processed != 1)
+ {
+ ereport (
+ ERROR,
+ (errcode (ERRCODE_INTERNAL_ERROR),
+ errmsg ("Unexpected result \n")));
+ }
+ tupdesc = SPI_tuptable->tupdesc;
+ tuple = SPI_tuptable->vals[0];
+ result = HeapTupleGetDatum (tuple);
+
+ TupleDesc result_desc = CreateTemplateTupleDesc (6, false);
+ TupleDescInitEntry (result_desc, (AttrNumber) 1, "sum_deposit_value", INT8OID,
+ -1, 0);
+ TupleDescInitEntry (result_desc, (AttrNumber) 2, "sum_deposit_fraction",
+ INT4OID, -1, 0);
+ TupleDescInitEntry (result_desc, (AttrNumber) 3, "sum_refund_value", INT8OID,
+ -1, 0);
+ TupleDescInitEntry (result_desc, (AttrNumber) 4, "sum_refund_fraction",
+ INT4OID, -1, 0);
+ TupleDescInitEntry (result_desc, (AttrNumber) 5, "sum_fee_value", INT8OID, -1,
+ 0);
+ TupleDescInitEntry (result_desc, (AttrNumber) 6, "sum_fee_fraction", INT4OID,
+ -1, 0);
+
+ int ret = SPI_prepare (sql, 4, argtypes);
+ if (ret != SPI_OK_PREPARE)
+ {
+ elog (ERROR, "Failed to prepare statement: %s \n", sql);
+ }
+
+ ret = SPI_execute_plan (plan, args, nulls, true, 0);
+ if (ret != SPI_OK_SELECT)
+ {
+ elog (ERROR, "Failed to execute statement: %s \n", sql);
+ }
+
+ if (SPI_processed > 0)
+ {
+ HeapTuple tuple;
+ Datum values[6];
+ bool nulls[6] = {false};
+ values[0] =
+ SPI_getbinval (SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1,
+ &nulls[0]);
+ values[1] =
+ SPI_getbinval (SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2,
+ &nulls[1]);
+ values[2] =
+ SPI_getbinval (SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 3,
+ &nulls[2]);
+ values[3] =
+ SPI_getbinval (SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 4,
+ &nulls[3]);
+ values[4] =
+ SPI_getbinval (SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 5,
+ &nulls[4]);
+ values[5] =
+ SPI_getbinval (SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 6,
+ &nulls[5]);
+ tuple = heap_form_tuple (result_desc, values, nulls);
+ PG_RETURN_DATUM (HeapTupleGetDatum (tuple));
+ }
+ SPI_finish ();
+
+ PG_RETURN_NULL ();
+}
diff --git a/src/exchangedb/test-exchangedb-batch-reserves-in-insert-postgres b/src/exchangedb/test-exchangedb-batch-reserves-in-insert-postgres
new file mode 100755
index 000000000..bc044232a
--- /dev/null
+++ b/src/exchangedb/test-exchangedb-batch-reserves-in-insert-postgres
@@ -0,0 +1,210 @@
+#! /bin/bash
+
+# test-exchangedb-batch-reserves-in-insert-postgres - temporary wrapper script for .libs/test-exchangedb-batch-reserves-in-insert-postgres
+# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15
+#
+# The test-exchangedb-batch-reserves-in-insert-postgres program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=""
+
+# This environment variable determines our operation mode.
+if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='2.4.6'
+ notinst_deplibs=' libtalerexchangedb.la ../../src/json/libtalerjson.la ../../src/util/libtalerutil.la ../../src/pq/libtalerpq.la'
+else
+ # When we are sourced in execute mode, $file and $ECHO are already set.
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ file="$0"
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+ ECHO="printf %s\\n"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string --lt-
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's ../../libtool value, followed by no.
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=$0
+ shift
+ for lt_opt
+ do
+ case "$lt_opt" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
+ test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
+ lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'`
+ cat "$lt_dump_D/$lt_dump_F"
+ exit 0
+ ;;
+ --lt-*)
+ $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n "$lt_option_debug"; then
+ echo "test-exchangedb-batch-reserves-in-insert-postgres:test-exchangedb-batch-reserves-in-insert-postgres:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-15" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ $ECHO "test-exchangedb-batch-reserves-in-insert-postgres:test-exchangedb-batch-reserves-in-insert-postgres:$LINENO: newargv[$lt_dump_args_N]: $lt_arg"
+ lt_dump_args_N=`expr $lt_dump_args_N + 1`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+
+ if test -n "$lt_option_debug"; then
+ $ECHO "test-exchangedb-batch-reserves-in-insert-postgres:test-exchangedb-batch-reserves-in-insert-postgres:$LINENO: newargv[0]: $progdir/$program" 1>&2
+ func_lt_dump_args ${1+"$@"} 1>&2
+ fi
+ exec "$progdir/$program" ${1+"$@"}
+
+ $ECHO "$0: cannot exec $program $*" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from $@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case " $* " in
+ *\ --lt-*)
+ for lt_wr_arg
+ do
+ case $lt_wr_arg in
+ --lt-*) ;;
+ *) set x "$@" "$lt_wr_arg"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core ${1+"$@"}
+}
+
+ # Parse options
+ func_parse_lt_options "$0" ${1+"$@"}
+
+ # Find the directory that this script lives in.
+ thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+ test "x$thisdir" = "x$file" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
+ while test -n "$file"; do
+ destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+
+ # If there was a directory component, then change thisdir.
+ if test "x$destdir" != "x$file"; then
+ case "$destdir" in
+ [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
+ *) thisdir="$thisdir/$destdir" ;;
+ esac
+ fi
+
+ file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'`
+ file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
+ if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
+ # special case for '.'
+ if test "$thisdir" = "."; then
+ thisdir=`pwd`
+ fi
+ # remove .libs from thisdir
+ case "$thisdir" in
+ *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;;
+ .libs ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=`cd "$thisdir" && pwd`
+ test -n "$absdir" && thisdir="$absdir"
+
+ program='test-exchangedb-batch-reserves-in-insert-postgres'
+ progdir="$thisdir/.libs"
+
+
+ if test -f "$progdir/$program"; then
+ # Add our own library path to LD_LIBRARY_PATH
+ LD_LIBRARY_PATH="/home/priscilla/exchange/src/exchangedb/.libs:/home/priscilla/exchange/src/json/.libs:/home/priscilla/exchange/src/util/.libs:/home/priscilla/exchange/src/pq/.libs:$LD_LIBRARY_PATH"
+
+ # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /usr/bin/sed 's/::*$//'`
+
+ export LD_LIBRARY_PATH
+
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ # Run the actual program with our arguments.
+ func_exec_program ${1+"$@"}
+ fi
+ else
+ # The program doesn't exist.
+ $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2
+ $ECHO "This script is just a wrapper for $program." 1>&2
+ $ECHO "See the libtool documentation for more information." 1>&2
+ exit 1
+ fi
+fi
diff --git a/src/exchangedb/test-exchangedb-by-j-postgres b/src/exchangedb/test-exchangedb-by-j-postgres
new file mode 100755
index 000000000..11d295cc2
--- /dev/null
+++ b/src/exchangedb/test-exchangedb-by-j-postgres
@@ -0,0 +1,210 @@
+#! /bin/bash
+
+# test-exchangedb-by-j-postgres - temporary wrapper script for .libs/test-exchangedb-by-j-postgres
+# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15
+#
+# The test-exchangedb-by-j-postgres program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=""
+
+# This environment variable determines our operation mode.
+if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='2.4.6'
+ notinst_deplibs=' libtalerexchangedb.la ../../src/json/libtalerjson.la ../../src/util/libtalerutil.la ../../src/pq/libtalerpq.la'
+else
+ # When we are sourced in execute mode, $file and $ECHO are already set.
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ file="$0"
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+ ECHO="printf %s\\n"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string --lt-
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's ../../libtool value, followed by no.
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=$0
+ shift
+ for lt_opt
+ do
+ case "$lt_opt" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
+ test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
+ lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'`
+ cat "$lt_dump_D/$lt_dump_F"
+ exit 0
+ ;;
+ --lt-*)
+ $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n "$lt_option_debug"; then
+ echo "test-exchangedb-by-j-postgres:test-exchangedb-by-j-postgres:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-15" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ $ECHO "test-exchangedb-by-j-postgres:test-exchangedb-by-j-postgres:$LINENO: newargv[$lt_dump_args_N]: $lt_arg"
+ lt_dump_args_N=`expr $lt_dump_args_N + 1`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+
+ if test -n "$lt_option_debug"; then
+ $ECHO "test-exchangedb-by-j-postgres:test-exchangedb-by-j-postgres:$LINENO: newargv[0]: $progdir/$program" 1>&2
+ func_lt_dump_args ${1+"$@"} 1>&2
+ fi
+ exec "$progdir/$program" ${1+"$@"}
+
+ $ECHO "$0: cannot exec $program $*" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from $@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case " $* " in
+ *\ --lt-*)
+ for lt_wr_arg
+ do
+ case $lt_wr_arg in
+ --lt-*) ;;
+ *) set x "$@" "$lt_wr_arg"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core ${1+"$@"}
+}
+
+ # Parse options
+ func_parse_lt_options "$0" ${1+"$@"}
+
+ # Find the directory that this script lives in.
+ thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+ test "x$thisdir" = "x$file" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
+ while test -n "$file"; do
+ destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+
+ # If there was a directory component, then change thisdir.
+ if test "x$destdir" != "x$file"; then
+ case "$destdir" in
+ [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
+ *) thisdir="$thisdir/$destdir" ;;
+ esac
+ fi
+
+ file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'`
+ file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
+ if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
+ # special case for '.'
+ if test "$thisdir" = "."; then
+ thisdir=`pwd`
+ fi
+ # remove .libs from thisdir
+ case "$thisdir" in
+ *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;;
+ .libs ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=`cd "$thisdir" && pwd`
+ test -n "$absdir" && thisdir="$absdir"
+
+ program='test-exchangedb-by-j-postgres'
+ progdir="$thisdir/.libs"
+
+
+ if test -f "$progdir/$program"; then
+ # Add our own library path to LD_LIBRARY_PATH
+ LD_LIBRARY_PATH="/home/priscilla/exchange/src/exchangedb/.libs:/home/priscilla/exchange/src/json/.libs:/home/priscilla/exchange/src/util/.libs:/home/priscilla/exchange/src/pq/.libs:$LD_LIBRARY_PATH"
+
+ # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /usr/bin/sed 's/::*$//'`
+
+ export LD_LIBRARY_PATH
+
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ # Run the actual program with our arguments.
+ func_exec_program ${1+"$@"}
+ fi
+ else
+ # The program doesn't exist.
+ $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2
+ $ECHO "This script is just a wrapper for $program." 1>&2
+ $ECHO "See the libtool documentation for more information." 1>&2
+ exit 1
+ fi
+fi
diff --git a/src/exchangedb/test-exchangedb-populate-link-data-postgres b/src/exchangedb/test-exchangedb-populate-link-data-postgres
new file mode 100755
index 000000000..f3d673519
--- /dev/null
+++ b/src/exchangedb/test-exchangedb-populate-link-data-postgres
@@ -0,0 +1,210 @@
+#! /bin/bash
+
+# test-exchangedb-populate-link-data-postgres - temporary wrapper script for .libs/test-exchangedb-populate-link-data-postgres
+# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15
+#
+# The test-exchangedb-populate-link-data-postgres program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=""
+
+# This environment variable determines our operation mode.
+if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='2.4.6'
+ notinst_deplibs=' libtalerexchangedb.la ../../src/json/libtalerjson.la ../../src/util/libtalerutil.la ../../src/pq/libtalerpq.la'
+else
+ # When we are sourced in execute mode, $file and $ECHO are already set.
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ file="$0"
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+ ECHO="printf %s\\n"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string --lt-
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's ../../libtool value, followed by no.
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=$0
+ shift
+ for lt_opt
+ do
+ case "$lt_opt" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
+ test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
+ lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'`
+ cat "$lt_dump_D/$lt_dump_F"
+ exit 0
+ ;;
+ --lt-*)
+ $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n "$lt_option_debug"; then
+ echo "test-exchangedb-populate-link-data-postgres:test-exchangedb-populate-link-data-postgres:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-15" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ $ECHO "test-exchangedb-populate-link-data-postgres:test-exchangedb-populate-link-data-postgres:$LINENO: newargv[$lt_dump_args_N]: $lt_arg"
+ lt_dump_args_N=`expr $lt_dump_args_N + 1`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+
+ if test -n "$lt_option_debug"; then
+ $ECHO "test-exchangedb-populate-link-data-postgres:test-exchangedb-populate-link-data-postgres:$LINENO: newargv[0]: $progdir/$program" 1>&2
+ func_lt_dump_args ${1+"$@"} 1>&2
+ fi
+ exec "$progdir/$program" ${1+"$@"}
+
+ $ECHO "$0: cannot exec $program $*" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from $@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case " $* " in
+ *\ --lt-*)
+ for lt_wr_arg
+ do
+ case $lt_wr_arg in
+ --lt-*) ;;
+ *) set x "$@" "$lt_wr_arg"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core ${1+"$@"}
+}
+
+ # Parse options
+ func_parse_lt_options "$0" ${1+"$@"}
+
+ # Find the directory that this script lives in.
+ thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+ test "x$thisdir" = "x$file" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
+ while test -n "$file"; do
+ destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+
+ # If there was a directory component, then change thisdir.
+ if test "x$destdir" != "x$file"; then
+ case "$destdir" in
+ [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
+ *) thisdir="$thisdir/$destdir" ;;
+ esac
+ fi
+
+ file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'`
+ file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
+ if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
+ # special case for '.'
+ if test "$thisdir" = "."; then
+ thisdir=`pwd`
+ fi
+ # remove .libs from thisdir
+ case "$thisdir" in
+ *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;;
+ .libs ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=`cd "$thisdir" && pwd`
+ test -n "$absdir" && thisdir="$absdir"
+
+ program='test-exchangedb-populate-link-data-postgres'
+ progdir="$thisdir/.libs"
+
+
+ if test -f "$progdir/$program"; then
+ # Add our own library path to LD_LIBRARY_PATH
+ LD_LIBRARY_PATH="/home/priscilla/exchange/src/exchangedb/.libs:/home/priscilla/exchange/src/json/.libs:/home/priscilla/exchange/src/util/.libs:/home/priscilla/exchange/src/pq/.libs:$LD_LIBRARY_PATH"
+
+ # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /usr/bin/sed 's/::*$//'`
+
+ export LD_LIBRARY_PATH
+
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ # Run the actual program with our arguments.
+ func_exec_program ${1+"$@"}
+ fi
+ else
+ # The program doesn't exist.
+ $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2
+ $ECHO "This script is just a wrapper for $program." 1>&2
+ $ECHO "See the libtool documentation for more information." 1>&2
+ exit 1
+ fi
+fi
diff --git a/src/exchangedb/test-exchangedb-populate-ready-deposit-postgres b/src/exchangedb/test-exchangedb-populate-ready-deposit-postgres
new file mode 100755
index 000000000..7747f381c
--- /dev/null
+++ b/src/exchangedb/test-exchangedb-populate-ready-deposit-postgres
@@ -0,0 +1,210 @@
+#! /bin/bash
+
+# test-exchangedb-populate-ready-deposit-postgres - temporary wrapper script for .libs/test-exchangedb-populate-ready-deposit-postgres
+# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15
+#
+# The test-exchangedb-populate-ready-deposit-postgres program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=""
+
+# This environment variable determines our operation mode.
+if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='2.4.6'
+ notinst_deplibs=' libtalerexchangedb.la ../../src/json/libtalerjson.la ../../src/util/libtalerutil.la ../../src/pq/libtalerpq.la'
+else
+ # When we are sourced in execute mode, $file and $ECHO are already set.
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ file="$0"
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+ ECHO="printf %s\\n"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string --lt-
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's ../../libtool value, followed by no.
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=$0
+ shift
+ for lt_opt
+ do
+ case "$lt_opt" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
+ test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
+ lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'`
+ cat "$lt_dump_D/$lt_dump_F"
+ exit 0
+ ;;
+ --lt-*)
+ $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n "$lt_option_debug"; then
+ echo "test-exchangedb-populate-ready-deposit-postgres:test-exchangedb-populate-ready-deposit-postgres:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-15" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ $ECHO "test-exchangedb-populate-ready-deposit-postgres:test-exchangedb-populate-ready-deposit-postgres:$LINENO: newargv[$lt_dump_args_N]: $lt_arg"
+ lt_dump_args_N=`expr $lt_dump_args_N + 1`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+
+ if test -n "$lt_option_debug"; then
+ $ECHO "test-exchangedb-populate-ready-deposit-postgres:test-exchangedb-populate-ready-deposit-postgres:$LINENO: newargv[0]: $progdir/$program" 1>&2
+ func_lt_dump_args ${1+"$@"} 1>&2
+ fi
+ exec "$progdir/$program" ${1+"$@"}
+
+ $ECHO "$0: cannot exec $program $*" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from $@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case " $* " in
+ *\ --lt-*)
+ for lt_wr_arg
+ do
+ case $lt_wr_arg in
+ --lt-*) ;;
+ *) set x "$@" "$lt_wr_arg"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core ${1+"$@"}
+}
+
+ # Parse options
+ func_parse_lt_options "$0" ${1+"$@"}
+
+ # Find the directory that this script lives in.
+ thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+ test "x$thisdir" = "x$file" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
+ while test -n "$file"; do
+ destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+
+ # If there was a directory component, then change thisdir.
+ if test "x$destdir" != "x$file"; then
+ case "$destdir" in
+ [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
+ *) thisdir="$thisdir/$destdir" ;;
+ esac
+ fi
+
+ file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'`
+ file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
+ if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
+ # special case for '.'
+ if test "$thisdir" = "."; then
+ thisdir=`pwd`
+ fi
+ # remove .libs from thisdir
+ case "$thisdir" in
+ *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;;
+ .libs ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=`cd "$thisdir" && pwd`
+ test -n "$absdir" && thisdir="$absdir"
+
+ program='test-exchangedb-populate-ready-deposit-postgres'
+ progdir="$thisdir/.libs"
+
+
+ if test -f "$progdir/$program"; then
+ # Add our own library path to LD_LIBRARY_PATH
+ LD_LIBRARY_PATH="/home/priscilla/exchange/src/exchangedb/.libs:/home/priscilla/exchange/src/json/.libs:/home/priscilla/exchange/src/util/.libs:/home/priscilla/exchange/src/pq/.libs:$LD_LIBRARY_PATH"
+
+ # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /usr/bin/sed 's/::*$//'`
+
+ export LD_LIBRARY_PATH
+
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ # Run the actual program with our arguments.
+ func_exec_program ${1+"$@"}
+ fi
+ else
+ # The program doesn't exist.
+ $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2
+ $ECHO "This script is just a wrapper for $program." 1>&2
+ $ECHO "See the libtool documentation for more information." 1>&2
+ exit 1
+ fi
+fi
diff --git a/src/exchangedb/test-exchangedb-populate-select-refunds-by-coin-postgres b/src/exchangedb/test-exchangedb-populate-select-refunds-by-coin-postgres
new file mode 100755
index 000000000..ce7ebb712
--- /dev/null
+++ b/src/exchangedb/test-exchangedb-populate-select-refunds-by-coin-postgres
@@ -0,0 +1,210 @@
+#! /bin/bash
+
+# test-exchangedb-populate-select-refunds-by-coin-postgres - temporary wrapper script for .libs/test-exchangedb-populate-select-refunds-by-coin-postgres
+# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15
+#
+# The test-exchangedb-populate-select-refunds-by-coin-postgres program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=""
+
+# This environment variable determines our operation mode.
+if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='2.4.6'
+ notinst_deplibs=' libtalerexchangedb.la ../../src/json/libtalerjson.la ../../src/util/libtalerutil.la ../../src/pq/libtalerpq.la'
+else
+ # When we are sourced in execute mode, $file and $ECHO are already set.
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ file="$0"
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+ ECHO="printf %s\\n"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string --lt-
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's ../../libtool value, followed by no.
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=$0
+ shift
+ for lt_opt
+ do
+ case "$lt_opt" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
+ test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
+ lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'`
+ cat "$lt_dump_D/$lt_dump_F"
+ exit 0
+ ;;
+ --lt-*)
+ $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n "$lt_option_debug"; then
+ echo "test-exchangedb-populate-select-refunds-by-coin-postgres:test-exchangedb-populate-select-refunds-by-coin-postgres:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-15" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ $ECHO "test-exchangedb-populate-select-refunds-by-coin-postgres:test-exchangedb-populate-select-refunds-by-coin-postgres:$LINENO: newargv[$lt_dump_args_N]: $lt_arg"
+ lt_dump_args_N=`expr $lt_dump_args_N + 1`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+
+ if test -n "$lt_option_debug"; then
+ $ECHO "test-exchangedb-populate-select-refunds-by-coin-postgres:test-exchangedb-populate-select-refunds-by-coin-postgres:$LINENO: newargv[0]: $progdir/$program" 1>&2
+ func_lt_dump_args ${1+"$@"} 1>&2
+ fi
+ exec "$progdir/$program" ${1+"$@"}
+
+ $ECHO "$0: cannot exec $program $*" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from $@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case " $* " in
+ *\ --lt-*)
+ for lt_wr_arg
+ do
+ case $lt_wr_arg in
+ --lt-*) ;;
+ *) set x "$@" "$lt_wr_arg"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core ${1+"$@"}
+}
+
+ # Parse options
+ func_parse_lt_options "$0" ${1+"$@"}
+
+ # Find the directory that this script lives in.
+ thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+ test "x$thisdir" = "x$file" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
+ while test -n "$file"; do
+ destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+
+ # If there was a directory component, then change thisdir.
+ if test "x$destdir" != "x$file"; then
+ case "$destdir" in
+ [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
+ *) thisdir="$thisdir/$destdir" ;;
+ esac
+ fi
+
+ file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'`
+ file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
+ if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
+ # special case for '.'
+ if test "$thisdir" = "."; then
+ thisdir=`pwd`
+ fi
+ # remove .libs from thisdir
+ case "$thisdir" in
+ *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;;
+ .libs ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=`cd "$thisdir" && pwd`
+ test -n "$absdir" && thisdir="$absdir"
+
+ program='test-exchangedb-populate-select-refunds-by-coin-postgres'
+ progdir="$thisdir/.libs"
+
+
+ if test -f "$progdir/$program"; then
+ # Add our own library path to LD_LIBRARY_PATH
+ LD_LIBRARY_PATH="/home/priscilla/exchange/src/exchangedb/.libs:/home/priscilla/exchange/src/json/.libs:/home/priscilla/exchange/src/util/.libs:/home/priscilla/exchange/src/pq/.libs:$LD_LIBRARY_PATH"
+
+ # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /usr/bin/sed 's/::*$//'`
+
+ export LD_LIBRARY_PATH
+
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ # Run the actual program with our arguments.
+ func_exec_program ${1+"$@"}
+ fi
+ else
+ # The program doesn't exist.
+ $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2
+ $ECHO "This script is just a wrapper for $program." 1>&2
+ $ECHO "See the libtool documentation for more information." 1>&2
+ exit 1
+ fi
+fi
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index eb258f002..22788a562 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2022 Taler Systems SA
+ Copyright (C) 2014-2023 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
@@ -33,9 +33,9 @@ static int result;
/**
* Report line of error if @a cond is true, and jump to label "drop".
*/
-#define FAILIF(cond) \
+#define FAILIF(cond) \
do { \
- if (! (cond)) { break;} \
+ if (! (cond)) { break;} \
GNUNET_break (0); \
goto drop; \
} while (0)
@@ -45,7 +45,8 @@ static int result;
* Initializes @a ptr with random data.
*/
#define RND_BLK(ptr) \
- GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr))
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, \
+ sizeof (*ptr))
/**
* Initializes @a ptr with zeros.
@@ -278,7 +279,7 @@ create_denom_key_pair (unsigned int size,
GNUNET_assert (GNUNET_OK ==
TALER_denom_priv_create (&dkp->priv,
&dkp->pub,
- TALER_DENOMINATION_RSA,
+ GNUNET_CRYPTO_BSA_RSA,
size));
/* Using memset() as fields like master key and signature
are not properly initialized for this test. */
@@ -603,7 +604,7 @@ static struct TALER_PaytoHashP wire_target_h_payto;
/**
- * Callback for #select_deposits_above_serial_id ()
+ * Callback for #select_coin_deposits_above_serial_id ()
*
* @param cls closure
* @param rowid unique serial ID for the deposit in our DB
@@ -930,14 +931,17 @@ audit_wire_cb (void *cls,
/**
* Test API relating to wire_out handling.
*
+ * @param bd batch deposit to test
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-test_wire_out (const struct TALER_EXCHANGEDB_Deposit *deposit)
+test_wire_out (const struct TALER_EXCHANGEDB_BatchDeposit *bd)
{
+ const struct TALER_EXCHANGEDB_CoinDepositInformation *deposit = &bd->cdis[0];
struct TALER_PaytoHashP h_payto;
- TALER_payto_hash (deposit->receiver_wire_account,
+ GNUNET_assert (0 < bd->num_cdis);
+ TALER_payto_hash (bd->receiver_wire_account,
&h_payto);
auditor_row_cnt = 0;
memset (&wire_out_wtid,
@@ -955,8 +959,8 @@ test_wire_out (const struct TALER_EXCHANGEDB_Deposit *deposit)
plugin->start_deferred_wire_out (plugin->cls));
/* setup values for wire transfer aggregation data */
- merchant_pub_wt = deposit->merchant_pub;
- h_contract_terms_wt = deposit->h_contract_terms;
+ merchant_pub_wt = bd->merchant_pub;
+ h_contract_terms_wt = bd->h_contract_terms;
coin_pub_wt = deposit->coin.coin_pub;
coin_value_wt = deposit->amount_with_fee;
@@ -980,6 +984,7 @@ test_wire_out (const struct TALER_EXCHANGEDB_Deposit *deposit)
struct TALER_Amount coin_fee2;
struct GNUNET_TIME_Timestamp execution_time2;
struct TALER_EXCHANGEDB_KycStatus kyc;
+ enum TALER_AmlDecisionState aml;
h_contract_terms_wt2.hash.bits[0]++;
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
@@ -993,7 +998,8 @@ test_wire_out (const struct TALER_EXCHANGEDB_Deposit *deposit)
&execution_time2,
&coin_contribution2,
&coin_fee2,
- &kyc));
+ &kyc,
+ &aml));
}
{
struct TALER_ReservePublicKeyP rpub;
@@ -1025,6 +1031,7 @@ test_wire_out (const struct TALER_EXCHANGEDB_Deposit *deposit)
struct TALER_Amount coin_fee2;
struct GNUNET_TIME_Timestamp execution_time2;
struct TALER_EXCHANGEDB_KycStatus kyc;
+ enum TALER_AmlDecisionState aml = TALER_AML_FROZEN;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->lookup_transfer_by_deposit (plugin->cls,
@@ -1037,7 +1044,9 @@ test_wire_out (const struct TALER_EXCHANGEDB_Deposit *deposit)
&execution_time2,
&coin_contribution2,
&coin_fee2,
- &kyc));
+ &kyc,
+ &aml));
+ FAILIF (TALER_AML_NORMAL != aml);
GNUNET_assert (0 == GNUNET_memcmp (&wtid2,
&wire_out_wtid));
GNUNET_assert (GNUNET_TIME_timestamp_cmp (execution_time2,
@@ -1084,9 +1093,9 @@ recoup_cb (void *cls,
const struct TALER_CoinPublicInfo *coin,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_CoinSpendSignatureP *coin_sig,
- const union TALER_DenominationBlindingKeyP *coin_blind)
+ const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
{
- const union TALER_DenominationBlindingKeyP *cb = cls;
+ const union GNUNET_CRYPTO_BlindingSecretP *cb = cls;
(void) rowid;
(void) timestamp;
@@ -1105,48 +1114,32 @@ drop:
/**
- * Function called on deposits that are past their due date
- * and have not yet seen a wire transfer.
+ * Function called on batch deposits that may require a
+ * wire transfer.
*
* @param cls closure a `struct TALER_EXCHANGEDB_Deposit *`
- * @param rowid deposit table row of the coin's deposit
- * @param coin_pub public key of the coin
- * @param amount value of the deposit, including fee
- * @param payto_uri where should the funds be wired
- * @param deadline what was the requested wire transfer deadline
- * @param done did the exchange claim that it made a transfer?
+ * @param batch_deposit_serial_id where in the table are we
+ * @param total_amount value of all missing deposits, including fees
+ * @param wire_target_h_payto hash of the recipient account's payto URI
+ * @param deadline what was the earliest requested wire transfer deadline
*/
static void
-wire_missing_cb (void *cls,
- uint64_t rowid,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_Amount *amount,
- const char *payto_uri,
- struct GNUNET_TIME_Timestamp deadline,
- bool done)
+wire_missing_cb (
+ void *cls,
+ uint64_t batch_deposit_serial_id,
+ const struct TALER_Amount *total_amount,
+ const struct TALER_PaytoHashP *wire_target_h_payto,
+ struct GNUNET_TIME_Timestamp deadline)
{
- const struct TALER_EXCHANGEDB_Deposit *deposit = cls;
+ const struct TALER_EXCHANGEDB_CoinDepositInformation *deposit = cls;
- (void) payto_uri;
+ (void) batch_deposit_serial_id;
(void) deadline;
- (void) rowid;
- if (done)
- {
- GNUNET_break (0);
- result = 66;
- }
- if (0 != TALER_amount_cmp (amount,
- &deposit->amount_with_fee))
- {
- GNUNET_break (0);
- result = 66;
- }
- if (0 != GNUNET_memcmp (coin_pub,
- &deposit->coin.coin_pub))
- {
- GNUNET_break (0);
- result = 66;
- }
+ (void) wire_target_h_payto;
+ if (0 ==
+ TALER_amount_cmp (total_amount,
+ &deposit->amount_with_fee))
+ result = 8;
}
@@ -1185,7 +1178,7 @@ run (void *cls)
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct TALER_CoinSpendSignatureP coin_sig;
struct GNUNET_TIME_Timestamp deadline;
- union TALER_DenominationBlindingKeyP coin_blind;
+ union GNUNET_CRYPTO_BlindingSecretP coin_blind;
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_ReservePublicKeyP reserve_pub2;
struct TALER_ReservePublicKeyP reserve_pub3;
@@ -1197,8 +1190,10 @@ run (void *cls)
struct TALER_EXCHANGEDB_ReserveHistory *rh_head;
struct TALER_EXCHANGEDB_BankTransfer *bt;
struct TALER_EXCHANGEDB_CollectableBlindcoin *withdraw;
- struct TALER_EXCHANGEDB_Deposit deposit;
- struct TALER_EXCHANGEDB_Deposit deposit2;
+ struct TALER_EXCHANGEDB_CoinDepositInformation deposit;
+ struct TALER_EXCHANGEDB_BatchDeposit bd;
+ struct TALER_CoinSpendPublicKeyP cpub2;
+ struct TALER_MerchantPublicKeyP mpub2;
struct TALER_EXCHANGEDB_Refund refund;
struct TALER_EXCHANGEDB_TransactionList *tl;
struct TALER_EXCHANGEDB_TransactionList *tlp;
@@ -1218,17 +1213,19 @@ run (void *cls)
uint64_t reserve_out_serial_id;
uint64_t melt_serial_id;
struct TALER_PlanchetMasterSecretP ps;
- union TALER_DenominationBlindingKeyP bks;
- struct TALER_ExchangeWithdrawValues alg_values = {
- /* RSA is simpler, and for the DB there is no real difference between
- CS and RSA, just one should be used, so we use RSA */
- .cipher = TALER_DENOMINATION_RSA
- };
+ union GNUNET_CRYPTO_BlindingSecretP bks;
+ const struct TALER_ExchangeWithdrawValues *alg_values
+ = TALER_denom_ewv_rsa_singleton ();
memset (&deposit,
0,
sizeof (deposit));
- deposit.receiver_wire_account = (char *) rcvr;
+ memset (&bd,
+ 0,
+ sizeof (bd));
+ bd.receiver_wire_account = (char *) rcvr;
+ bd.cdis = &deposit;
+ bd.num_cdis = 1;
memset (&salt,
45,
sizeof (salt));
@@ -1277,7 +1274,6 @@ run (void *cls)
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":0.000010",
&fees.deposit));
- deposit.deposit_fee = fees.deposit;
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":0.000010",
&fees.refresh));
@@ -1288,16 +1284,28 @@ run (void *cls)
TALER_string_to_amount (CURRENCY ":1.000010",
&amount_with_fee));
result = 4;
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+ plugin->commit (plugin->cls));
now = GNUNET_TIME_timestamp_get ();
- FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->reserves_in_insert (plugin->cls,
- &reserve_pub,
- &value,
- now,
- sndr,
- "exchange-account-1",
- 4));
+ {
+ struct TALER_EXCHANGEDB_ReserveInInfo reserve = {
+ .reserve_pub = &reserve_pub,
+ .balance = &value,
+ .execution_time = now,
+ .sender_account_details = sndr,
+ .exchange_account_name = "exchange-account-1",
+ .wire_reference = 4
+ };
+ enum GNUNET_DB_QueryStatus qsr;
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->reserves_in_insert (plugin->cls,
+ &reserve,
+ 1,
+ &qsr));
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ qsr);
+ }
FAILIF (GNUNET_OK !=
check_reserve (&reserve_pub,
value.value,
@@ -1305,14 +1313,28 @@ run (void *cls)
value.currency));
now = GNUNET_TIME_timestamp_get ();
RND_BLK (&reserve_pub2);
- FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->reserves_in_insert (plugin->cls,
- &reserve_pub2,
- &value,
- now,
- sndr,
- "exchange-account-1",
- 5));
+ {
+ struct TALER_EXCHANGEDB_ReserveInInfo reserve = {
+ .reserve_pub = &reserve_pub2,
+ .balance = &value,
+ .execution_time = now,
+ .sender_account_details = sndr,
+ .exchange_account_name = "exchange-account-1",
+ .wire_reference = 5
+ };
+ enum GNUNET_DB_QueryStatus qsr;
+
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->reserves_in_insert (plugin->cls,
+ &reserve,
+ 1,
+ &qsr));
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ qsr);
+ }
+ FAILIF (GNUNET_OK !=
+ plugin->start (plugin->cls,
+ "test-2"));
FAILIF (GNUNET_OK !=
check_reserve (&reserve_pub,
value.value,
@@ -1335,7 +1357,7 @@ run (void *cls)
RND_BLK (&cbc.reserve_sig);
RND_BLK (&ps);
TALER_planchet_blinding_secret_create (&ps,
- &alg_values,
+ alg_values,
&bks);
{
struct TALER_PlanchetDetail pd;
@@ -1351,19 +1373,20 @@ run (void *cls)
RND_BLK (&age_hash);
for (size_t i = 0; i < sizeof(p_ah) / sizeof(p_ah[0]); i++)
{
+
RND_BLK (&coin_pub);
GNUNET_assert (GNUNET_OK ==
TALER_denom_blind (&dkp->pub,
&bks,
+ NULL,
p_ah[i],
&coin_pub,
- &alg_values,
+ alg_values,
&c_hash,
&pd.blinded_planchet));
- GNUNET_assert (GNUNET_OK ==
- TALER_coin_ev_hash (&pd.blinded_planchet,
- &cbc.denom_pub_hash,
- &cbc.h_coin_envelope));
+ TALER_coin_ev_hash (&pd.blinded_planchet,
+ &cbc.denom_pub_hash,
+ &cbc.h_coin_envelope);
if (i != 0)
TALER_blinded_denom_sig_free (&cbc.sig);
GNUNET_assert (
@@ -1385,21 +1408,39 @@ run (void *cls)
{
bool found;
- bool nonce_ok;
+ bool nonce_reuse;
bool balance_ok;
+ bool age_ok;
+ bool conflict;
+ bool denom_unknown;
+ uint16_t maximum_age;
uint64_t ruuid;
+ struct TALER_Amount reserve_balance;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->do_withdraw (plugin->cls,
- NULL,
- &cbc,
- now,
- &found,
- &balance_ok,
- &nonce_ok,
- &ruuid));
+ plugin->do_batch_withdraw (plugin->cls,
+ now,
+ &reserve_pub,
+ &value,
+ true,
+ &found,
+ &balance_ok,
+ &reserve_balance,
+ &age_ok,
+ &maximum_age,
+ &ruuid));
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->do_batch_withdraw_insert (plugin->cls,
+ NULL,
+ &cbc,
+ now,
+ ruuid,
+ &denom_unknown,
+ &conflict,
+ &nonce_reuse));
GNUNET_assert (found);
- GNUNET_assert (nonce_ok);
+ GNUNET_assert (! nonce_reuse);
+ GNUNET_assert (! denom_unknown);
GNUNET_assert (balance_ok);
}
@@ -1440,7 +1481,7 @@ run (void *cls)
&cbc2.sig,
&bks,
&c_hash,
- &alg_values,
+ alg_values,
&dkp->pub));
FAILIF (GNUNET_OK !=
TALER_denom_pub_verify (&dkp->pub,
@@ -1459,7 +1500,7 @@ run (void *cls)
&cbc.sig,
&bks,
&c_hash,
- &alg_values,
+ alg_values,
&dkp->pub));
deadline = GNUNET_TIME_timestamp_get ();
{
@@ -1479,23 +1520,22 @@ run (void *cls)
struct GNUNET_TIME_Timestamp deposit_timestamp
= GNUNET_TIME_timestamp_get ();
bool balance_ok;
+ uint32_t bad_balance_idx;
bool in_conflict;
struct TALER_PaytoHashP h_payto;
RND_BLK (&h_payto);
- deposit.refund_deadline
+ bd.refund_deadline
= GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MONTHS);
- deposit.wire_deadline
+ bd.wire_deadline
= GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MONTHS);
deposit.amount_with_fee = value;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->do_deposit (plugin->cls,
- &deposit,
- known_coin_id,
- &h_payto,
- false,
+ &bd,
&deposit_timestamp,
&balance_ok,
+ &bad_balance_idx,
&in_conflict));
FAILIF (! balance_ok);
FAILIF (in_conflict);
@@ -1508,9 +1548,9 @@ run (void *cls)
bool conflict;
refund.coin = deposit.coin;
- refund.details.merchant_pub = deposit.merchant_pub;
+ refund.details.merchant_pub = bd.merchant_pub;
RND_BLK (&refund.details.merchant_sig);
- refund.details.h_contract_terms = deposit.h_contract_terms;
+ refund.details.h_contract_terms = bd.h_contract_terms;
refund.details.rtransaction_id = 1;
refund.details.refund_amount = value;
refund.details.refund_fee = fees.refund;
@@ -1613,7 +1653,8 @@ run (void *cls)
{
struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin;
struct GNUNET_TIME_Timestamp now;
- struct TALER_BlindedRsaPlanchet *rp;
+ struct GNUNET_CRYPTO_BlindedMessage *rp;
+ struct GNUNET_CRYPTO_RsaBlindedMessage *rsa;
struct TALER_BlindedPlanchet *bp;
now = GNUNET_TIME_timestamp_get ();
@@ -1625,18 +1666,22 @@ run (void *cls)
new_denom_pubs[cnt] = new_dkp[cnt]->pub;
ccoin = &revealed_coins[cnt];
bp = &ccoin->blinded_planchet;
- bp->cipher = TALER_DENOMINATION_RSA;
- rp = &bp->details.rsa_blinded_planchet;
- rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 (
+ rp = GNUNET_new (struct GNUNET_CRYPTO_BlindedMessage);
+ bp->blinded_message = rp;
+ rp->cipher = GNUNET_CRYPTO_BSA_RSA;
+ rp->rc = 1;
+ rsa = &rp->details.rsa_blinded_message;
+ rsa->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 (
GNUNET_CRYPTO_QUALITY_WEAK,
(RSA_KEY_SIZE / 8) - 1);
- rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size);
+ rsa->blinded_msg = GNUNET_malloc (rsa->blinded_msg_size);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
- rp->blinded_msg,
- rp->blinded_msg_size);
+ rsa->blinded_msg,
+ rsa->blinded_msg_size);
TALER_denom_pub_hash (&new_dkp[cnt]->pub,
&ccoin->h_denom_pub);
- ccoin->exchange_vals = alg_values;
+ TALER_denom_ewv_copy (&ccoin->exchange_vals,
+ alg_values);
TALER_coin_ev_hash (bp,
&ccoin->h_denom_pub,
&ccoin->coin_envelope_hash);
@@ -1694,11 +1739,20 @@ run (void *cls)
/* Just to test fetching a coin with melt history */
struct TALER_EXCHANGEDB_TransactionList *tl;
enum GNUNET_DB_QueryStatus qs;
+ uint64_t etag;
+ struct TALER_Amount balance;
+ struct TALER_DenominationHashP h_denom_pub;
qs = plugin->get_coin_transactions (plugin->cls,
&refresh.coin.coin_pub,
+ 0,
+ 0,
+ &etag,
+ &balance,
+ &h_denom_pub,
&tl);
- FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
+ FAILIF (0 >= qs);
+ FAILIF (NULL == tl);
plugin->free_coin_transaction_list (plugin->cls,
tl);
}
@@ -1708,7 +1762,7 @@ run (void *cls)
{
struct GNUNET_TIME_Timestamp recoup_timestamp
= GNUNET_TIME_timestamp_get ();
- union TALER_DenominationBlindingKeyP coin_bks;
+ union GNUNET_CRYPTO_BlindingSecretP coin_bks;
uint64_t new_known_coin_id;
struct TALER_CoinPublicInfo new_coin;
struct TALER_DenominationHashP dph;
@@ -1825,13 +1879,19 @@ run (void *cls)
0,
value.currency));
result = 7;
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+ plugin->commit (plugin->cls));
/* check reserve history */
{
struct TALER_Amount balance;
+ uint64_t etag_out;
qs = plugin->get_reserve_history (plugin->cls,
&reserve_pub,
+ 0,
+ 0,
+ &etag_out,
&balance,
&rh);
}
@@ -1943,9 +2003,20 @@ run (void *cls)
&audit_refund_cb,
NULL));
FAILIF (1 != auditor_row_cnt);
- qs = plugin->get_coin_transactions (plugin->cls,
- &refund.coin.coin_pub,
- &tl);
+ {
+ uint64_t etag = 0;
+ struct TALER_Amount balance;
+ struct TALER_DenominationHashP h_denom_pub;
+
+ qs = plugin->get_coin_transactions (plugin->cls,
+ &refund.coin.coin_pub,
+ 0,
+ 0,
+ &etag,
+ &balance,
+ &h_denom_pub,
+ &tl);
+ }
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
GNUNET_assert (NULL != tl);
matched = 0;
@@ -1965,26 +2036,24 @@ run (void *cls)
&deposit.csig));
FAILIF (0 !=
GNUNET_memcmp (&have->merchant_pub,
- &deposit.merchant_pub));
+ &bd.merchant_pub));
FAILIF (0 !=
GNUNET_memcmp (&have->h_contract_terms,
- &deposit.h_contract_terms));
+ &bd.h_contract_terms));
FAILIF (0 !=
GNUNET_memcmp (&have->wire_salt,
- &deposit.wire_salt));
+ &bd.wire_salt));
FAILIF (GNUNET_TIME_timestamp_cmp (have->timestamp,
!=,
- deposit.timestamp));
+ bd.wallet_timestamp));
FAILIF (GNUNET_TIME_timestamp_cmp (have->refund_deadline,
!=,
- deposit.refund_deadline));
+ bd.refund_deadline));
FAILIF (GNUNET_TIME_timestamp_cmp (have->wire_deadline,
!=,
- deposit.wire_deadline));
+ bd.wire_deadline));
FAILIF (0 != TALER_amount_cmp (&have->amount_with_fee,
&deposit.amount_with_fee));
- FAILIF (0 != TALER_amount_cmp (&have->deposit_fee,
- &deposit.deposit_fee));
matched |= 1;
break;
}
@@ -2055,7 +2124,6 @@ run (void *cls)
memset (&deposit,
0,
sizeof (deposit));
- deposit.deposit_fee = fees.deposit;
RND_BLK (&deposit.coin.coin_pub);
TALER_denom_pub_hash (&dkp->pub,
&deposit.coin.denom_pub_hash);
@@ -2064,24 +2132,25 @@ run (void *cls)
&cbc.sig,
&bks,
&c_hash,
- &alg_values,
+ alg_values,
&dkp->pub));
RND_BLK (&deposit.csig);
- RND_BLK (&deposit.merchant_pub);
- RND_BLK (&deposit.h_contract_terms);
- RND_BLK (&deposit.wire_salt);
- deposit.receiver_wire_account =
+ RND_BLK (&bd.merchant_pub);
+ RND_BLK (&bd.h_contract_terms);
+ RND_BLK (&bd.wire_salt);
+ bd.receiver_wire_account =
"payto://iban/DE67830654080004822650?receiver-name=Test";
TALER_merchant_wire_signature_hash (
"payto://iban/DE67830654080004822650?receiver-name=Test",
- &deposit.wire_salt,
+ &bd.wire_salt,
&h_wire_wt);
deposit.amount_with_fee = value;
- deposit.deposit_fee = fees.deposit;
-
- deposit.refund_deadline = deadline;
- deposit.wire_deadline = deadline;
+ bd.refund_deadline = deadline;
+ bd.wire_deadline = deadline;
result = 8;
+ FAILIF (GNUNET_OK !=
+ plugin->start (plugin->cls,
+ "test-3"));
{
uint64_t known_coin_id;
struct TALER_DenominationHashP dph;
@@ -2099,22 +2168,30 @@ run (void *cls)
struct GNUNET_TIME_Timestamp r;
struct TALER_Amount deposit_fee;
struct TALER_MerchantWireHashP h_wire;
+ bool balance_ok;
+ uint32_t bad_idx;
+ bool ctr_conflict;
now = GNUNET_TIME_timestamp_get ();
+ TALER_payto_hash (bd.receiver_wire_account,
+ &bd.wire_target_h_payto);
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->insert_deposit (plugin->cls,
- now,
- &deposit));
- TALER_merchant_wire_signature_hash (deposit.receiver_wire_account,
- &deposit.wire_salt,
+ plugin->do_deposit (plugin->cls,
+ &bd,
+ &now,
+ &balance_ok,
+ &bad_idx,
+ &ctr_conflict));
+ TALER_merchant_wire_signature_hash (bd.receiver_wire_account,
+ &bd.wire_salt,
&h_wire);
FAILIF (1 !=
plugin->have_deposit2 (plugin->cls,
- &deposit.h_contract_terms,
+ &bd.h_contract_terms,
&h_wire,
&deposit.coin.coin_pub,
- &deposit.merchant_pub,
- deposit.refund_deadline,
+ &bd.merchant_pub,
+ bd.refund_deadline,
&deposit_fee,
&r));
FAILIF (GNUNET_TIME_timestamp_cmp (now,
@@ -2122,29 +2199,20 @@ run (void *cls)
r));
}
{
- struct GNUNET_TIME_Timestamp start_range;
- struct GNUNET_TIME_Timestamp end_range;
-
- start_range = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_subtract (deadline.abs_time,
- GNUNET_TIME_UNIT_SECONDS));
- end_range = GNUNET_TIME_absolute_to_timestamp (
- GNUNET_TIME_absolute_add (deadline.abs_time,
- GNUNET_TIME_UNIT_SECONDS));
- FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->select_deposits_missing_wire (plugin->cls,
- start_range,
- end_range,
- &wire_missing_cb,
- &deposit));
+ result = 66;
+ FAILIF (0 >=
+ plugin->select_batch_deposits_missing_wire (plugin->cls,
+ 0,
+ &wire_missing_cb,
+ &deposit));
FAILIF (8 != result);
}
auditor_row_cnt = 0;
FAILIF (0 >=
- plugin->select_deposits_above_serial_id (plugin->cls,
- 0,
- &audit_deposit_cb,
- NULL));
+ plugin->select_coin_deposits_above_serial_id (plugin->cls,
+ 0,
+ &audit_deposit_cb,
+ NULL));
FAILIF (0 == auditor_row_cnt);
result = 8;
sleep (2); /* give deposit time to be ready */
@@ -2159,9 +2227,9 @@ run (void *cls)
&merchant_pub2,
&payto_uri2));
FAILIF (0 != GNUNET_memcmp (&merchant_pub2,
- &deposit.merchant_pub));
+ &bd.merchant_pub));
FAILIF (0 != strcmp (payto_uri2,
- deposit.receiver_wire_account));
+ bd.receiver_wire_account));
TALER_payto_hash (payto_uri2,
&wire_target_h_payto);
GNUNET_free (payto_uri2);
@@ -2177,7 +2245,7 @@ run (void *cls)
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->aggregate (plugin->cls,
&wire_target_h_payto,
- &deposit.merchant_pub,
+ &bd.merchant_pub,
&wtid,
&total));
}
@@ -2201,7 +2269,7 @@ run (void *cls)
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->select_aggregation_transient (plugin->cls,
&wire_target_h_payto,
- &deposit.merchant_pub,
+ &bd.merchant_pub,
"x-bank",
&wtid2,
&total2));
@@ -2209,14 +2277,14 @@ run (void *cls)
plugin->create_aggregation_transient (plugin->cls,
&wire_target_h_payto,
"x-bank",
- &deposit.merchant_pub,
+ &bd.merchant_pub,
&wtid,
0,
&total));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->select_aggregation_transient (plugin->cls,
&wire_target_h_payto,
- &deposit.merchant_pub,
+ &bd.merchant_pub,
"x-bank",
&wtid2,
&total2));
@@ -2238,7 +2306,7 @@ run (void *cls)
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->select_aggregation_transient (plugin->cls,
&wire_target_h_payto,
- &deposit.merchant_pub,
+ &bd.merchant_pub,
"x-bank",
&wtid2,
&total2));
@@ -2255,7 +2323,7 @@ run (void *cls)
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->select_aggregation_transient (plugin->cls,
&wire_target_h_payto,
- &deposit.merchant_pub,
+ &bd.merchant_pub,
"x-bank",
&wtid2,
&total2));
@@ -2264,37 +2332,35 @@ run (void *cls)
plugin->commit (plugin->cls));
result = 10;
- deposit2 = deposit;
FAILIF (GNUNET_OK !=
plugin->start (plugin->cls,
"test-2"));
- RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */
+ RND_BLK (&mpub2); /* should fail if merchant is different */
{
struct TALER_MerchantWireHashP h_wire;
struct GNUNET_TIME_Timestamp r;
struct TALER_Amount deposit_fee;
- TALER_merchant_wire_signature_hash (deposit2.receiver_wire_account,
- &deposit2.wire_salt,
+ TALER_merchant_wire_signature_hash (bd.receiver_wire_account,
+ &bd.wire_salt,
&h_wire);
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->have_deposit2 (plugin->cls,
- &deposit2.h_contract_terms,
+ &bd.h_contract_terms,
&h_wire,
- &deposit2.coin.coin_pub,
- &deposit2.merchant_pub,
- deposit2.refund_deadline,
+ &deposit.coin.coin_pub,
+ &mpub2,
+ bd.refund_deadline,
&deposit_fee,
&r));
- deposit2.merchant_pub = deposit.merchant_pub;
- RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */
+ RND_BLK (&cpub2); /* should fail if coin is different */
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->have_deposit2 (plugin->cls,
- &deposit2.h_contract_terms,
+ &bd.h_contract_terms,
&h_wire,
- &deposit2.coin.coin_pub,
- &deposit2.merchant_pub,
- deposit2.refund_deadline,
+ &cpub2,
+ &bd.merchant_pub,
+ bd.refund_deadline,
&deposit_fee,
&r));
}
@@ -2344,7 +2410,7 @@ run (void *cls)
FAILIF (GNUNET_OK !=
test_wire_prepare ());
FAILIF (GNUNET_OK !=
- test_wire_out (&deposit));
+ test_wire_out (&bd));
FAILIF (GNUNET_OK !=
test_gc ());
FAILIF (GNUNET_OK !=
@@ -2408,8 +2474,9 @@ main (int argc,
return -1;
}
GNUNET_log_setup (argv[0],
- "WARNING",
+ "INFO",
NULL);
+ TALER_OS_init ();
plugin_name++;
(void) GNUNET_asprintf (&testname,
"test-exchange-db-%s",
diff --git a/src/exchangedb/test_exchangedb_batch_reserves_in_insert.c b/src/exchangedb/test_exchangedb_batch_reserves_in_insert.c
deleted file mode 100644
index 460778b88..000000000
--- a/src/exchangedb/test_exchangedb_batch_reserves_in_insert.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-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 exchangedb/test_exchangedb_by_j.c
- * @brief test cases for DB interaction functions
- * @author Joseph Xu
- */
-#include "platform.h"
-#include "taler_exchangedb_lib.h"
-#include "taler_json_lib.h"
-#include "taler_exchangedb_plugin.h"
-
-/**o
- * Global result from the testcase.
- */
-static int result;
-
-/**
- * Report line of error if @a cond is true, and jump to label "drop".
- */
-#define FAILIF(cond) \
- do { \
- if (! (cond)) {break;} \
- GNUNET_break (0); \
- goto drop; \
- } while (0)
-
-
-/**
- * Initializes @a ptr with random data.
- */
-#define RND_BLK(ptr) \
- GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr))
-
-/**
- * Initializes @a ptr with zeros.
- */
-#define ZR_BLK(ptr) \
- memset (ptr, 0, sizeof (*ptr))
-
-
-/**
- * Currency we use. Must match test-exchange-db-*.conf.
- */
-#define CURRENCY "EUR"
-
-/**
- * Database plugin under test.
- */
-static struct TALER_EXCHANGEDB_Plugin *plugin;
-
-
-/**
- * Main function that will be run by the scheduler.
- *
- * @param cls closure with config
- */
-static void
-run (void *cls)
-{
- struct GNUNET_CONFIGURATION_Handle *cfg = cls;
- const uint32_t num_partitions = 10;
-
- if (NULL ==
- (plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
- {
- GNUNET_break (0);
- result = 77;
- return;
- }
- (void) plugin->drop_tables (plugin->cls);
- if (GNUNET_OK !=
- plugin->create_tables (plugin->cls))
- {
- GNUNET_break (0);
- result = 77;
- goto cleanup;
- }
- if (GNUNET_OK !=
- plugin->setup_partitions (plugin->cls,
- num_partitions))
- {
- GNUNET_break (0);
- result = 77;
- goto cleanup;
- }
-
- for (unsigned int i = 0; i< 8; i++)
- {
- static unsigned int batches[] = {1, 1,0, 2, 4, 16, 64, 256};
- const char *sndr = "payto://x-taler-bank/localhost:8080/1";
- struct TALER_Amount value;
- unsigned int batch_size = batches[i];
- struct GNUNET_TIME_Absolute now;
- struct GNUNET_TIME_Timestamp ts;
- struct GNUNET_TIME_Relative duration;
- struct TALER_EXCHANGEDB_ReserveInInfo reserves[batch_size];
- enum GNUNET_DB_QueryStatus results[batch_size];
- GNUNET_assert (GNUNET_OK ==
- TALER_string_to_amount (CURRENCY ":1.000010",
- &value));
- now = GNUNET_TIME_absolute_get ();
- ts = GNUNET_TIME_timestamp_get ();
- for (unsigned int r=0;r<10;r++)
- {
-
- for (unsigned int k = 0; k<batch_size; k++)
- {
- RND_BLK (&reserves[k].reserve_pub);
- reserves[k].balance = value;
- reserves[k].execution_time = ts;
- reserves[k].sender_account_details = sndr;
- reserves[k].exchange_account_name = "name";
- reserves[k].wire_reference = k;
- }
- FAILIF (batch_size !=
- plugin->batch_reserves_in_insert (plugin->cls,
- reserves,
- batch_size,
- results));
- }
-
- duration = GNUNET_TIME_absolute_get_duration (now);
- fprintf (stdout,
- "for a batchsize equal to %d it took %s\n",
- batch_size,
- GNUNET_STRINGS_relative_time_to_string (duration,
- GNUNET_NO) );
-
- }
- result = 0;
-drop:
- GNUNET_break (GNUNET_OK ==
- plugin->drop_tables (plugin->cls));
-cleanup:
- TALER_EXCHANGEDB_plugin_unload (plugin);
- plugin = NULL;
-}
-
-
-int
-main (int argc,
- char *const argv[])
-{
- const char *plugin_name;
- char *config_filename;
- char *testname;
- struct GNUNET_CONFIGURATION_Handle *cfg;
- (void) argc;
- result = -1;
- if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
- {
- GNUNET_break (0);
- return -1;
- }
-
- GNUNET_log_setup (argv[0],
- "WARNING",
- NULL);
- plugin_name++;
- (void) GNUNET_asprintf (&testname,
- "test-exchange-db-%s",
- plugin_name);
- (void) GNUNET_asprintf (&config_filename,
- "%s.conf",
- testname);
- fprintf (stdout,
- "Using config: %s\n",
- config_filename);
- cfg = GNUNET_CONFIGURATION_create ();
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_parse (cfg,
- config_filename))
- {
- GNUNET_break (0);
- GNUNET_free (config_filename);
- GNUNET_free (testname);
- return 2;
- }
- GNUNET_SCHEDULER_run (&run,
- cfg);
- GNUNET_CONFIGURATION_destroy (cfg);
- GNUNET_free (config_filename);
- GNUNET_free (testname);
- return result;
-}
-
-/* end of test_exchangedb_by_j.c */
diff --git a/src/exchangedb/test_exchangedb_by_j.c b/src/exchangedb/test_exchangedb_by_j.c
index 834373b55..24b24d5b0 100644
--- a/src/exchangedb/test_exchangedb_by_j.c
+++ b/src/exchangedb/test_exchangedb_by_j.c
@@ -22,6 +22,8 @@
#include "taler_exchangedb_lib.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+#include "math.h"
+#define ROUNDS 10
/**
* Global result from the testcase.
@@ -71,6 +73,9 @@ static struct TALER_EXCHANGEDB_Plugin *plugin;
static void
run (void *cls)
{
+ static const unsigned int batches[] = {1, 2, 3, 4, 8, 16 };
+ struct GNUNET_TIME_Relative times[sizeof (batches) / sizeof(*batches)];
+ unsigned long long sqrs[sizeof (batches) / sizeof(*batches)];
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
const uint32_t num_partitions = 10;
@@ -81,7 +86,8 @@ run (void *cls)
result = 77;
return;
}
- (void) plugin->drop_tables (plugin->cls);
+
+
if (GNUNET_OK !=
plugin->create_tables (plugin->cls,
true,
@@ -92,62 +98,79 @@ run (void *cls)
goto cleanup;
}
- for (unsigned int i = 0; i< 7; i++)
-
- if (GNUNET_OK !=
- plugin->setup_partitions (plugin->cls,
- num_partitions))
+ memset (times, 0, sizeof (times));
+ memset (sqrs, 0, sizeof (sqrs));
+ for (unsigned int r = 0; r < ROUNDS; r++)
{
- GNUNET_break (0);
- result = 77;
- goto cleanup;
- }
-
- for (unsigned int i = 0; i< 8; i++)
-
- {
- static unsigned int batches[] = {1, 1,0, 2, 4, 16, 64, 256};
- const char *sndr = "payto://x-taler-bank/localhost:8080/1";
- struct TALER_Amount value;
- unsigned int batch_size = batches[i];
- struct GNUNET_TIME_Absolute now;
- struct GNUNET_TIME_Timestamp ts;
- struct GNUNET_TIME_Relative duration;
- struct TALER_EXCHANGEDB_ReserveInInfo reserves[batch_size];
- /* struct TALER_EXCHANGEDB_ReserveInInfo reserves2[batch_size];*/
- enum GNUNET_DB_QueryStatus results[batch_size];
- GNUNET_assert (GNUNET_OK ==
- TALER_string_to_amount (CURRENCY ":1.000010",
- &value));
- now = GNUNET_TIME_absolute_get ();
- ts = GNUNET_TIME_timestamp_get ();
- for (unsigned int r = 0; r<10; r++)
+ for (unsigned int i = 0; i< 6; i++)
{
-
- for (unsigned int k = 0; k<batch_size; k++)
+ const char *sndr = "payto://x-taler-bank/localhost:8080/1";
+ struct TALER_Amount value;
+ unsigned int batch_size = batches[i];
+ unsigned int iterations = 16; // 1024*10;
+ struct TALER_ReservePublicKeyP reserve_pubs[iterations];
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_TIME_Timestamp ts;
+ struct GNUNET_TIME_Relative duration;
+ struct TALER_EXCHANGEDB_ReserveInInfo reserves[iterations];
+ enum GNUNET_DB_QueryStatus results[iterations];
+ unsigned long long duration_sq;
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":1.000010",
+ &value));
+ now = GNUNET_TIME_absolute_get ();
+ ts = GNUNET_TIME_timestamp_get ();
+ for (unsigned int r = 0; r<iterations; r++)
{
- RND_BLK (&reserves[k].reserve_pub);
- reserves[k].balance = value;
- reserves[k].execution_time = ts;
- reserves[k].sender_account_details = sndr;
- reserves[k].exchange_account_name = "name";
- reserves[k].wire_reference = k;
+ RND_BLK (&reserve_pubs[r]);
+ reserves[r].reserve_pub = &reserve_pubs[r];
+ reserves[r].balance = &value;
+ reserves[r].execution_time = ts;
+ reserves[r].sender_account_details = sndr;
+ reserves[r].exchange_account_name = "name";
+ reserves[r].wire_reference = r;
}
- FAILIF (batch_size !=
- plugin->batch_reserves_in_insert (plugin->cls,
- reserves,
- batch_size,
- results));
+ FAILIF (iterations !=
+ plugin->batch2_reserves_in_insert (plugin->cls,
+ reserves,
+ iterations,
+ batch_size,
+ results));
+ duration = GNUNET_TIME_absolute_get_duration (now);
+ times[i] = GNUNET_TIME_relative_add (times[i],
+ duration);
+ duration_sq = duration.rel_value_us * duration.rel_value_us;
+ GNUNET_assert (duration_sq / duration.rel_value_us ==
+ duration.rel_value_us);
+ GNUNET_assert (sqrs[i] + duration_sq >= sqrs[i]);
+ sqrs[i] += duration_sq;
+ fprintf (stdout,
+ "for a batchsize equal to %d it took %s\n",
+ batch_size,
+ GNUNET_STRINGS_relative_time_to_string (duration,
+ GNUNET_NO) );
+
+ system ("./test.sh"); // DELETE AFTER TIMER
}
-
- duration = GNUNET_TIME_absolute_get_duration (now);
+ }
+ for (unsigned int i = 0; i< 6; i++)
+ {
+ struct GNUNET_TIME_Relative avg;
+ double avg_dbl;
+ double variance;
+
+ avg = GNUNET_TIME_relative_divide (times[i],
+ ROUNDS);
+ avg_dbl = avg.rel_value_us;
+ variance = sqrs[i] - (avg_dbl * avg_dbl * ROUNDS);
fprintf (stdout,
- "for a batchsize equal to %d it took %s\n",
- batch_size,
- GNUNET_STRINGS_relative_time_to_string (duration,
- GNUNET_NO) );
-
+ "Batch[%2u]: %8llu ± %6.0f\n",
+ batches[i],
+ (unsigned long long) avg.rel_value_us,
+ sqrt (variance / (ROUNDS - 1)));
}
+
result = 0;
drop:
GNUNET_break (GNUNET_OK ==
diff --git a/src/exchangedb/test_idempotency.sh b/src/exchangedb/test_idempotency.sh
new file mode 100755
index 000000000..7314b8c3f
--- /dev/null
+++ b/src/exchangedb/test_idempotency.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+# This file is in the public domain.
+set -eu
+psql talercheck < /dev/null || exit 77
+echo "Initializing DB"
+taler-exchange-dbinit -r -c test-exchange-db-postgres.conf
+echo "Re-initializing DB"
+taler-exchange-dbinit -c test-exchange-db-postgres.conf
+echo "Re-loading procedures"
+psql talercheck < procedures.sql
+echo "Test PASSED"
+exit 0
diff --git a/src/exchangedb/versioning.sql b/src/exchangedb/versioning.sql
index 116f409b7..444cf95ed 100644
--- a/src/exchangedb/versioning.sql
+++ b/src/exchangedb/versioning.sql
@@ -146,12 +146,13 @@
BEGIN;
+
-- This file adds versioning support to database it will be loaded to.
-- It requires that PL/pgSQL is already loaded - will raise exception otherwise.
-- All versioning "stuff" (tables, functions) is in "_v" schema.
-- All functions are defined as 'RETURNS SETOF INT4' to be able to make them to RETURN literally nothing (0 rows).
--- >> RETURNS VOID<< IS similar, but it still outputs "empty line" in psql when calling.
+-- >> RETURNS VOID<< IS similar, but it still outputs "empty line" in psql when calling
CREATE SCHEMA IF NOT EXISTS _v;
COMMENT ON SCHEMA _v IS 'Schema for versioning data and functionality.';