Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2018, 2024 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it
6 : under the terms of the GNU General Public License as published
7 : by the Free Software Foundation; either version 3, or (at your
8 : option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not,
17 : see <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing/testing_api_cmd_insert_deposit.c
21 : * @brief deposit a coin directly into the database.
22 : * @author Marcello Stanisci
23 : * @author Christian Grothoff
24 : */
25 : #include "taler/platform.h" /* UNNECESSARY? */
26 : #include "taler/taler_util.h"
27 : #include "taler/taler_json_lib.h"
28 : #include <gnunet/gnunet_curl_lib.h>
29 : #include "taler/taler_signatures.h"
30 : #include "taler/taler_testing_lib.h"
31 : #include "taler/taler_exchangedb_lib.h"
32 : #include "taler/taler_exchangedb_lib.h"
33 : #include "exchange-database/rollback.h"
34 : #include "exchange-database/start.h" /* UNNECESSARY? */
35 : #include "exchange-database/commit.h"
36 : #include "exchange-database/preflight.h"
37 : #include "exchange-database/do_deposit.h"
38 : #include "exchange-database/insert_denomination_info.h"
39 : #include "exchange-database/ensure_coin_known.h"
40 :
41 : /**
42 : * State for a "insert-deposit" CMD.
43 : */
44 : struct InsertDepositState
45 : {
46 : /**
47 : * Database connection we use.
48 : */
49 : struct TALER_EXCHANGEDB_PostgresContext *plugin;
50 :
51 : /**
52 : * Human-readable name of the shop.
53 : */
54 : const char *merchant_name;
55 :
56 : /**
57 : * Merchant account name (NOT a payto-URI).
58 : */
59 : const char *merchant_account;
60 :
61 : /**
62 : * Deadline before which the aggregator should
63 : * send the payment to the merchant.
64 : */
65 : struct GNUNET_TIME_Relative wire_deadline;
66 :
67 : /**
68 : * When did the exchange receive the deposit?
69 : */
70 : struct GNUNET_TIME_Timestamp exchange_timestamp;
71 :
72 : /**
73 : * Amount to deposit, inclusive of deposit fee.
74 : */
75 : const char *amount_with_fee;
76 :
77 : /**
78 : * Deposit fee.
79 : */
80 : const char *deposit_fee;
81 :
82 : /**
83 : * Do we used a cached @e plugin?
84 : */
85 : bool cached;
86 : };
87 :
88 : /**
89 : * Setup (fake) information about a coin used in deposit.
90 : *
91 : * @param[out] issue information to initialize with "valid" data
92 : */
93 : static void
94 24 : fake_issue (struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
95 : {
96 : struct GNUNET_TIME_Timestamp now;
97 :
98 24 : memset (issue,
99 : 0,
100 : sizeof (*issue));
101 24 : now = GNUNET_TIME_timestamp_get ();
102 : issue->start
103 24 : = now;
104 : issue->expire_withdraw
105 24 : = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MINUTES);
106 : issue->expire_deposit
107 24 : = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_HOURS);
108 : issue->expire_legal
109 24 : = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_DAYS);
110 24 : GNUNET_assert (GNUNET_OK ==
111 : TALER_string_to_amount ("EUR:1",
112 : &issue->value));
113 24 : GNUNET_assert (GNUNET_OK ==
114 : TALER_string_to_amount ("EUR:0.1",
115 : &issue->fees.withdraw));
116 24 : GNUNET_assert (GNUNET_OK ==
117 : TALER_string_to_amount ("EUR:0.1",
118 : &issue->fees.deposit));
119 24 : GNUNET_assert (GNUNET_OK ==
120 : TALER_string_to_amount ("EUR:0.1",
121 : &issue->fees.refresh));
122 24 : GNUNET_assert (GNUNET_OK ==
123 : TALER_string_to_amount ("EUR:0.1",
124 : &issue->fees.refund));
125 24 : }
126 :
127 :
128 : /**
129 : * Run the command.
130 : *
131 : * @param cls closure.
132 : * @param cmd the commaind being run.
133 : * @param is interpreter state.
134 : */
135 : static void
136 24 : insert_deposit_run (void *cls,
137 : const struct TALER_TESTING_Command *cmd,
138 : struct TALER_TESTING_Interpreter *is)
139 : {
140 24 : struct InsertDepositState *ids = cls;
141 : struct TALER_EXCHANGEDB_CoinDepositInformation deposit;
142 : struct TALER_EXCHANGEDB_BatchDeposit bd;
143 : struct TALER_MerchantPrivateKeyP merchant_priv;
144 : struct TALER_EXCHANGEDB_DenominationKeyInformation issue;
145 : struct TALER_DenominationPublicKey dpk;
146 : struct TALER_DenominationPrivateKey denom_priv;
147 : struct TALER_FullPayto receiver_wire_account;
148 :
149 : (void) cmd;
150 24 : if (NULL == ids->plugin)
151 : {
152 0 : GNUNET_break (0);
153 0 : TALER_TESTING_interpreter_fail (is);
154 0 : return;
155 : }
156 24 : if (GNUNET_OK !=
157 24 : TALER_EXCHANGEDB_preflight (ids->plugin))
158 : {
159 0 : GNUNET_break (0);
160 0 : TALER_TESTING_interpreter_fail (is);
161 0 : return;
162 : }
163 24 : fake_issue (&issue);
164 24 : GNUNET_assert (GNUNET_OK ==
165 : TALER_denom_priv_create (&denom_priv,
166 : &dpk,
167 : GNUNET_CRYPTO_BSA_RSA,
168 : 1024));
169 24 : TALER_denom_pub_hash (&dpk,
170 : &issue.denom_hash);
171 :
172 24 : if ( (GNUNET_OK !=
173 24 : TALER_EXCHANGEDB_start (ids->plugin,
174 24 : "talertestinglib: denomination insertion")) ||
175 : (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
176 24 : TALER_EXCHANGEDB_insert_denomination_info (ids->plugin,
177 : &dpk,
178 24 : &issue)) ||
179 : (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
180 24 : TALER_EXCHANGEDB_commit (ids->plugin)) )
181 : {
182 0 : TALER_TESTING_interpreter_fail (is);
183 0 : TALER_denom_pub_free (&dpk);
184 0 : TALER_denom_priv_free (&denom_priv);
185 0 : return;
186 : }
187 :
188 : /* prepare and store deposit now. */
189 24 : memset (&deposit,
190 : 0,
191 : sizeof (deposit));
192 24 : memset (&bd,
193 : 0,
194 : sizeof (bd));
195 24 : bd.cdis = &deposit;
196 24 : bd.num_cdis = 1;
197 :
198 24 : GNUNET_assert (
199 : GNUNET_YES ==
200 : GNUNET_CRYPTO_hkdf_gnunet (
201 : &merchant_priv,
202 : sizeof (struct TALER_MerchantPrivateKeyP),
203 : "merchant-priv",
204 : strlen ("merchant-priv"),
205 : ids->merchant_name,
206 : strlen (ids->merchant_name)));
207 24 : GNUNET_assert (
208 : GNUNET_YES ==
209 : GNUNET_CRYPTO_hkdf_gnunet (
210 : &bd.merchant_sig,
211 : sizeof (struct TALER_MerchantSignatureP),
212 : "merchant-sig",
213 : strlen ("merchant-sig"),
214 : ids->merchant_name,
215 : strlen (ids->merchant_name)));
216 24 : GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv.eddsa_priv,
217 : &bd.merchant_pub.eddsa_pub);
218 24 : GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
219 : &bd.h_contract_terms.hash);
220 24 : if (GNUNET_OK !=
221 24 : TALER_string_to_amount (ids->amount_with_fee,
222 : &deposit.amount_with_fee))
223 : {
224 0 : TALER_TESTING_interpreter_fail (is);
225 0 : TALER_denom_pub_free (&dpk);
226 0 : TALER_denom_priv_free (&denom_priv);
227 0 : return;
228 : }
229 :
230 24 : TALER_denom_pub_hash (&dpk,
231 : &deposit.coin.denom_pub_hash);
232 24 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
233 : &deposit.coin.coin_pub,
234 : sizeof (deposit.coin.coin_pub));
235 : {
236 : struct TALER_CoinPubHashP c_hash;
237 : struct TALER_PlanchetDetail pd;
238 : struct TALER_BlindedDenominationSignature bds;
239 : struct TALER_PlanchetMasterSecretP ps;
240 : union GNUNET_CRYPTO_BlindingSecretP bks;
241 : const struct TALER_ExchangeBlindingValues *alg_values;
242 :
243 24 : alg_values = TALER_denom_ewv_rsa_singleton ();
244 24 : TALER_planchet_blinding_secret_create (&ps,
245 : alg_values,
246 : &bks);
247 24 : GNUNET_assert (GNUNET_OK ==
248 : TALER_denom_blind (&dpk,
249 : &bks,
250 : NULL, /* no age restriction active */
251 : NULL, /* no nonce needed */
252 : &deposit.coin.coin_pub,
253 : alg_values,
254 : &c_hash,
255 : &pd.blinded_planchet));
256 24 : GNUNET_assert (GNUNET_OK ==
257 : TALER_denom_sign_blinded (&bds,
258 : &denom_priv,
259 : false,
260 : &pd.blinded_planchet));
261 24 : TALER_blinded_planchet_free (&pd.blinded_planchet);
262 24 : GNUNET_assert (GNUNET_OK ==
263 : TALER_denom_sig_unblind (&deposit.coin.denom_sig,
264 : &bds,
265 : &bks,
266 : &c_hash,
267 : alg_values,
268 : &dpk));
269 24 : TALER_blinded_denom_sig_free (&bds);
270 : }
271 24 : GNUNET_asprintf (&receiver_wire_account.full_payto,
272 : "payto://x-taler-bank/localhost/%s?receiver-name=%s",
273 : ids->merchant_account,
274 : ids->merchant_account);
275 24 : bd.receiver_wire_account = receiver_wire_account;
276 24 : TALER_full_payto_hash (bd.receiver_wire_account,
277 : &bd.wire_target_h_payto);
278 24 : memset (&bd.wire_salt,
279 : 46,
280 : sizeof (bd.wire_salt));
281 24 : bd.wallet_timestamp = GNUNET_TIME_timestamp_get ();
282 24 : bd.wire_deadline = GNUNET_TIME_relative_to_timestamp (
283 : ids->wire_deadline);
284 : /* finally, actually perform the DB operation */
285 : {
286 : uint64_t known_coin_id;
287 : struct TALER_Amount total;
288 : struct TALER_DenominationHashP dph;
289 : struct TALER_AgeCommitmentHashP agh;
290 : bool balance_ok;
291 : uint32_t bad_index;
292 : bool ctr_conflict;
293 :
294 24 : if ( (GNUNET_OK !=
295 24 : TALER_EXCHANGEDB_start (ids->plugin,
296 24 : "libtalertesting: insert deposit")) ||
297 : (0 >
298 24 : TALER_EXCHANGEDB_ensure_coin_known (ids->plugin,
299 : &deposit.coin,
300 : &known_coin_id,
301 : &dph,
302 24 : &agh)) ||
303 : (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
304 24 : TALER_EXCHANGEDB_do_deposit (ids->plugin,
305 : &bd,
306 : &issue.fees.deposit,
307 : &ids->exchange_timestamp,
308 : &total,
309 : &balance_ok,
310 : &bad_index,
311 24 : &ctr_conflict)) ||
312 : (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
313 24 : TALER_EXCHANGEDB_commit (ids->plugin)) )
314 : {
315 0 : GNUNET_break (0);
316 0 : TALER_EXCHANGEDB_rollback (ids->plugin);
317 0 : GNUNET_free (receiver_wire_account.full_payto);
318 0 : TALER_denom_pub_free (&dpk);
319 0 : TALER_denom_priv_free (&denom_priv);
320 0 : TALER_TESTING_interpreter_fail (is);
321 0 : return;
322 : }
323 : }
324 :
325 24 : TALER_denom_sig_free (&deposit.coin.denom_sig);
326 24 : TALER_denom_pub_free (&dpk);
327 24 : TALER_denom_priv_free (&denom_priv);
328 24 : GNUNET_free (receiver_wire_account.full_payto);
329 24 : TALER_TESTING_interpreter_next (is);
330 : }
331 :
332 :
333 : /**
334 : * Free the state of a "auditor-dbinit" CMD, and possibly kills its
335 : * process if it did not terminate correctly.
336 : *
337 : * @param cls closure.
338 : * @param cmd the command being freed.
339 : */
340 : static void
341 24 : insert_deposit_cleanup (void *cls,
342 : const struct TALER_TESTING_Command *cmd)
343 : {
344 24 : struct InsertDepositState *ids = cls;
345 :
346 : (void) cmd;
347 24 : if ( (NULL != ids->plugin) &&
348 24 : (! ids->cached) )
349 : {
350 : // FIXME: historically, we also did:
351 : // TALER_EXCHANGEDB_drop_tables (ids->plugin);
352 1 : TALER_EXCHANGEDB_disconnect (ids->plugin);
353 1 : ids->plugin = NULL;
354 : }
355 24 : GNUNET_free (ids);
356 24 : }
357 :
358 :
359 : struct TALER_TESTING_Command
360 24 : TALER_TESTING_cmd_insert_deposit (
361 : const char *label,
362 : const struct GNUNET_CONFIGURATION_Handle *db_cfg,
363 : const char *merchant_name,
364 : const char *merchant_account,
365 : struct GNUNET_TIME_Timestamp exchange_timestamp,
366 : struct GNUNET_TIME_Relative wire_deadline,
367 : const char *amount_with_fee,
368 : const char *deposit_fee)
369 : {
370 : static struct TALER_EXCHANGEDB_PostgresContext *pluginc;
371 : static const struct GNUNET_CONFIGURATION_Handle *db_cfgc;
372 : struct InsertDepositState *ids;
373 :
374 24 : ids = GNUNET_new (struct InsertDepositState);
375 24 : if (db_cfgc == db_cfg)
376 : {
377 23 : ids->plugin = pluginc;
378 23 : ids->cached = true;
379 : }
380 : else
381 : {
382 1 : ids->plugin = TALER_EXCHANGEDB_connect (db_cfg,
383 : false);
384 1 : pluginc = ids->plugin;
385 1 : db_cfgc = db_cfg;
386 : }
387 24 : ids->merchant_name = merchant_name;
388 24 : ids->merchant_account = merchant_account;
389 24 : ids->exchange_timestamp = exchange_timestamp;
390 24 : ids->wire_deadline = wire_deadline;
391 24 : ids->amount_with_fee = amount_with_fee;
392 24 : ids->deposit_fee = deposit_fee;
393 :
394 : {
395 24 : struct TALER_TESTING_Command cmd = {
396 : .cls = ids,
397 : .label = label,
398 : .run = &insert_deposit_run,
399 : .cleanup = &insert_deposit_cleanup
400 : };
401 :
402 24 : return cmd;
403 : }
404 : }
405 :
406 :
407 : /* end of testing_api_cmd_insert_deposit.c */
|