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 "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 "exchangedb_lib.h"
32 : #include "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/do_insert_known_coin.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_random_block (&bd.h_contract_terms.hash,
219 : sizeof 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 (&deposit.coin.coin_pub,
233 : sizeof (deposit.coin.coin_pub));
234 : {
235 : struct TALER_CoinPubHashP c_hash;
236 : struct TALER_PlanchetDetail pd;
237 : struct TALER_BlindedDenominationSignature bds;
238 : struct TALER_PlanchetMasterSecretP ps;
239 : union GNUNET_CRYPTO_BlindingSecretP bks;
240 : const struct TALER_ExchangeBlindingValues *alg_values;
241 :
242 24 : alg_values = TALER_denom_ewv_rsa_singleton ();
243 24 : TALER_planchet_blinding_secret_create (&ps,
244 : alg_values,
245 : &bks);
246 24 : GNUNET_assert (GNUNET_OK ==
247 : TALER_denom_blind (&dpk,
248 : &bks,
249 : NULL, /* no age restriction active */
250 : NULL, /* no nonce needed */
251 : &deposit.coin.coin_pub,
252 : alg_values,
253 : &c_hash,
254 : &pd.blinded_planchet));
255 24 : GNUNET_assert (GNUNET_OK ==
256 : TALER_denom_sign_blinded (&bds,
257 : &denom_priv,
258 : false,
259 : &pd.blinded_planchet));
260 24 : TALER_blinded_planchet_free (&pd.blinded_planchet);
261 24 : GNUNET_assert (GNUNET_OK ==
262 : TALER_denom_sig_unblind (&deposit.coin.denom_sig,
263 : &bds,
264 : &bks,
265 : &c_hash,
266 : alg_values,
267 : &dpk));
268 24 : TALER_blinded_denom_sig_free (&bds);
269 : }
270 24 : GNUNET_asprintf (&receiver_wire_account.full_payto,
271 : "payto://x-taler-bank/localhost/%s?receiver-name=%s",
272 : ids->merchant_account,
273 : ids->merchant_account);
274 24 : bd.receiver_wire_account = receiver_wire_account;
275 24 : TALER_full_payto_hash (bd.receiver_wire_account,
276 : &bd.wire_target_h_payto);
277 24 : memset (&bd.wire_salt,
278 : 46,
279 : sizeof (bd.wire_salt));
280 24 : bd.wallet_timestamp = GNUNET_TIME_timestamp_get ();
281 24 : bd.wire_deadline = GNUNET_TIME_relative_to_timestamp (
282 : ids->wire_deadline);
283 : /* finally, actually perform the DB operation */
284 : {
285 : uint64_t known_coin_id;
286 : struct TALER_Amount total;
287 : struct TALER_DenominationHashP dph;
288 : struct TALER_AgeCommitmentHashP agh;
289 : bool balance_ok;
290 : uint32_t bad_index;
291 : bool ctr_conflict;
292 :
293 24 : if ( (GNUNET_OK !=
294 24 : TALER_EXCHANGEDB_start (ids->plugin,
295 24 : "libtalertesting: insert deposit")) ||
296 : (0 >
297 24 : TALER_EXCHANGEDB_do_insert_known_coin (ids->plugin,
298 : &deposit.coin,
299 : &known_coin_id,
300 : &dph,
301 24 : &agh)) ||
302 : (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
303 24 : TALER_EXCHANGEDB_do_deposit (ids->plugin,
304 : &bd,
305 : &issue.fees.deposit,
306 : &ids->exchange_timestamp,
307 : &total,
308 : &balance_ok,
309 : &bad_index,
310 24 : &ctr_conflict)) ||
311 : (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
312 24 : TALER_EXCHANGEDB_commit (ids->plugin)) )
313 : {
314 0 : GNUNET_break (0);
315 0 : TALER_EXCHANGEDB_rollback (ids->plugin);
316 0 : GNUNET_free (receiver_wire_account.full_payto);
317 0 : TALER_denom_pub_free (&dpk);
318 0 : TALER_denom_priv_free (&denom_priv);
319 0 : TALER_TESTING_interpreter_fail (is);
320 0 : return;
321 : }
322 : }
323 :
324 24 : TALER_denom_sig_free (&deposit.coin.denom_sig);
325 24 : TALER_denom_pub_free (&dpk);
326 24 : TALER_denom_priv_free (&denom_priv);
327 24 : GNUNET_free (receiver_wire_account.full_payto);
328 24 : TALER_TESTING_interpreter_next (is);
329 : }
330 :
331 :
332 : /**
333 : * Free the state of a "auditor-dbinit" CMD, and possibly kills its
334 : * process if it did not terminate correctly.
335 : *
336 : * @param cls closure.
337 : * @param cmd the command being freed.
338 : */
339 : static void
340 24 : insert_deposit_cleanup (void *cls,
341 : const struct TALER_TESTING_Command *cmd)
342 : {
343 24 : struct InsertDepositState *ids = cls;
344 :
345 : (void) cmd;
346 24 : if ( (NULL != ids->plugin) &&
347 24 : (! ids->cached) )
348 : {
349 : // FIXME: historically, we also did:
350 : // TALER_EXCHANGEDB_drop_tables (ids->plugin);
351 1 : TALER_EXCHANGEDB_disconnect (ids->plugin);
352 1 : ids->plugin = NULL;
353 : }
354 24 : GNUNET_free (ids);
355 24 : }
356 :
357 :
358 : struct TALER_TESTING_Command
359 24 : TALER_TESTING_cmd_insert_deposit (
360 : const char *label,
361 : const struct GNUNET_CONFIGURATION_Handle *db_cfg,
362 : const char *merchant_name,
363 : const char *merchant_account,
364 : struct GNUNET_TIME_Timestamp exchange_timestamp,
365 : struct GNUNET_TIME_Relative wire_deadline,
366 : const char *amount_with_fee,
367 : const char *deposit_fee)
368 : {
369 : static struct TALER_EXCHANGEDB_PostgresContext *pluginc;
370 : static const struct GNUNET_CONFIGURATION_Handle *db_cfgc;
371 : struct InsertDepositState *ids;
372 :
373 24 : ids = GNUNET_new (struct InsertDepositState);
374 24 : if (db_cfgc == db_cfg)
375 : {
376 23 : ids->plugin = pluginc;
377 23 : ids->cached = true;
378 : }
379 : else
380 : {
381 1 : ids->plugin = TALER_EXCHANGEDB_connect (db_cfg);
382 1 : pluginc = ids->plugin;
383 1 : db_cfgc = db_cfg;
384 : }
385 24 : ids->merchant_name = merchant_name;
386 24 : ids->merchant_account = merchant_account;
387 24 : ids->exchange_timestamp = exchange_timestamp;
388 24 : ids->wire_deadline = wire_deadline;
389 24 : ids->amount_with_fee = amount_with_fee;
390 24 : ids->deposit_fee = deposit_fee;
391 :
392 : {
393 24 : struct TALER_TESTING_Command cmd = {
394 : .cls = ids,
395 : .label = label,
396 : .run = &insert_deposit_run,
397 : .cleanup = &insert_deposit_cleanup
398 : };
399 :
400 24 : return cmd;
401 : }
402 : }
403 :
404 :
405 : /* end of testing_api_cmd_insert_deposit.c */
|