diff options
author | Marcello Stanisci <marcello.stanisci@inria.fr> | 2015-09-15 18:02:09 +0200 |
---|---|---|
committer | Marcello Stanisci <marcello.stanisci@inria.fr> | 2015-09-15 18:02:09 +0200 |
commit | f9e4086dffd7874abda25c6b6548c8fb39475100 (patch) | |
tree | a9a097afa71876ef0df1cf8b13cbd1ca1ab99fa0 /src/backend-lib | |
parent | a25a80150f36d94f28075666932677571b3076d1 (diff) | |
download | merchant-f9e4086dffd7874abda25c6b6548c8fb39475100.tar.gz merchant-f9e4086dffd7874abda25c6b6548c8fb39475100.tar.bz2 merchant-f9e4086dffd7874abda25c6b6548c8fb39475100.zip |
adding merchant library +
testcases for contract mgmt + minor tree adjustments
Diffstat (limited to 'src/backend-lib')
-rw-r--r-- | src/backend-lib/Makefile.am | 24 | ||||
-rw-r--r-- | src/backend-lib/merchant_db.c | 354 | ||||
-rw-r--r-- | src/backend-lib/merchant_db.h | 101 | ||||
-rw-r--r-- | src/backend-lib/taler-merchant-httpd_contract.c | 144 | ||||
-rw-r--r-- | src/backend-lib/taler_merchant_contract_lib.h | 62 | ||||
-rw-r--r-- | src/backend-lib/taler_merchant_lib.h | 1 |
6 files changed, 686 insertions, 0 deletions
diff --git a/src/backend-lib/Makefile.am b/src/backend-lib/Makefile.am new file mode 100644 index 00000000..61867e41 --- /dev/null +++ b/src/backend-lib/Makefile.am @@ -0,0 +1,24 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +lib_LTLIBRARIES = \ + libtalermerchant.la + +include_HEADERS = \ + taler_merchant_lib.h \ + taler_merchant_contract_lib.h + +libtalermerchant_la_SOURCES = \ + taler-merchant-httpd_contract.c \ + taler_merchant_contract_lib.h \ + merchant_db.c merchant_db.h \ + merchant.h + +libtalermerchant_la_LIBADD = \ + $(LIBGCRYPT_LIBS) \ + -ltalerutil \ + -lgnunetutil \ + -ltalerpq \ + -lgnunetpostgres \ + -lpq \ + -lpthread diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c new file mode 100644 index 00000000..274de25a --- /dev/null +++ b/src/backend-lib/merchant_db.c @@ -0,0 +1,354 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + 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, If not, see <http://www.gnu.org/licenses/> +*/ + +/** + * @file merchant/merchant_db.c + * @brief database helper functions used by the merchant + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include <taler/taler_util.h> +#include <taler/taler_pq_lib.h> +#include "merchant_db.h" + + +#define PQSQL_strerror(kind, cmd, res) \ + GNUNET_log_from (kind, "merchant-db", \ + "SQL %s failed at %s:%u with error: %s", \ + cmd, __FILE__, __LINE__, PQresultErrorMessage (res)); + +/** + * Shorthand for exit jumps. + */ +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + + +/** + * Connect to postgresql database + * + * @param cfg the configuration handle + * @return connection to the postgresql database; NULL upon error + */ +PGconn * +MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + return GNUNET_POSTGRES_connect (cfg, "merchant-db"); +} + + +/** + * Disconnect from the database + * + * @param conn database handle to close + */ +void +MERCHANT_DB_disconnect (PGconn *conn) +{ + PQfinish (conn); +} + + +/** + * Initialize merchant tables + * + * @param conn the connection handle to postgres db. + * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their + * contents are dropped when the @a conn is closed + * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure + */ +int +MERCHANT_DB_initialize (PGconn *conn, int tmp) +{ + const char *tmp_str = (1 == tmp) ? "TEMPORARY" : ""; + char *sql; + PGresult *res; + ExecStatusType status; + int ret; + + res = NULL; + (void) GNUNET_asprintf (&sql, + "BEGIN TRANSACTION;" + "CREATE %1$s TABLE IF NOT EXISTS contracts (" + "contract_id INT8 PRIMARY KEY," + "amount INT8 NOT NULL," + "amount_fraction INT4 NOT NULL," + "amount_currency VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL," + "description TEXT NOT NULL," + "nounce INT8 NOT NULL," + "expiry INT8 NOT NULL," + "product INT8 NOT NULL);" + "CREATE %1$s TABLE IF NOT EXISTS checkouts (" + "coin_pub BYTEA PRIMARY KEY," + "contract_id INT8 REFERENCES contracts(contract_id)," + "amount INT4 NOT NULL," + "amount_fraction INT4 NOT NULL," + "coin_sig BYTEA NOT NULL);", + tmp_str); + ret = GNUNET_POSTGRES_exec (conn, sql); + (void) GNUNET_POSTGRES_exec (conn, + (GNUNET_OK == ret) ? "COMMIT;" : "ROLLBACK;"); + GNUNET_free (sql); + if (GNUNET_OK != ret) + return ret; + + while (NULL != (res = PQgetResult (conn))) + { + status = PQresultStatus (res); + PQclear (res); + } + + EXITIF (NULL == (res = PQprepare + (conn, + "contract_create", + "INSERT INTO contracts" + "(contract_id, amount, amount_fraction, amount_currency," + "description, nounce, expiry, product) VALUES" + "($1, $2, $3, $4, $5, $6, $7, $8)", + 8, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); + + EXITIF (NULL == (res = PQprepare + (conn, + "get_contract_product", + "SELECT (" + "product" + ") FROM contracts " + "WHERE (" + "contract_id=$1" + ")", + 1, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); + + EXITIF (NULL == (res = PQprepare + (conn, + "checkout_create", + "INSERT INTO checkouts (" + "coin_pub," + "contract_id," + "amount," + "amount_fraction," + "coin_sig" + ") VALUES (" + "$1, $2, $3, $4, $5" + ")", + 5, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res))); + PQclear (res); + + EXITIF (NULL == (res = PQprepare + (conn, + "get_checkout_product", + "SELECT (" + "product" + ") FROM contracts " + "WHERE " + "contract_id IN (" + "SELECT (contract_id) FROM checkouts " + "WHERE coin_pub=$1" + ")", + 1, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res))); + PQclear (res); + + return GNUNET_OK; + + EXITIF_exit: + if (NULL != res) + { + PQSQL_strerror (GNUNET_ERROR_TYPE_ERROR, "PQprepare", res); + PQclear (res); + } + return GNUNET_SYSERR; +} + + +/** + * Inserts a contract record into the database and if successfull returns the + * serial number of the inserted row. + * + * @param conn the database connection + * @param expiry the time when the contract will expire + * @param amount the taler amount corresponding to the contract + * @param c_id contract's id + * @param desc descripition of the contract + * @param nounce a random 64-bit nounce + * @param product description to identify a product + * @return GNUNET_OK on success, GNUNET_SYSERR upon error + */ + +uint32_t +MERCHANT_DB_contract_create (PGconn *conn, + const struct GNUNET_TIME_Absolute *expiry, + const struct TALER_Amount *amount, + uint64_t c_id, + const char *desc, + uint64_t nounce, + uint64_t product) +{ + PGresult *res; + #if 0 + uint64_t expiry_ms_nbo; + uint64_t value_nbo; + uint32_t fraction_nbo; + uint64_t nounce_nbo; + #endif + ExecStatusType status; + + #if 0 + /* + NOTE: the conversion to nl(l) happens *inside* the query param helpers; since + the policy imposes this format for storing values. */ + value_nbo = GNUNET_htonll (amount->value); + fraction_nbo = GNUNET_htonll (amount->fraction); + nounce_nbo = GNUNET_htonll (nounce); + expiry_ms_nbo = GNUNET_htonll (expiry.abs_value_us); + product = GNUNET_htonll (product); + #endif + + /* ported. To be tested/compiled */ + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_uint64 (&c_id), + TALER_PQ_query_param_amount (amount), + /* a *string* is being put in the following statement, + though the API talks about a *blob*. Will this be liked by + the DB ? */ + // the following inserts a string as a blob. Will Taler provide a param-from-string helper? + TALER_PQ_query_param_fixed_size (desc, strlen(desc)), + TALER_PQ_query_param_uint64 (&nounce), + TALER_PQ_query_param_absolute_time (expiry), + TALER_PQ_query_param_uint64 (&product), + TALER_PQ_query_param_end + }; + + /* NOTE: the statement is prepared by MERCHANT_DB_initialize function */ + res = TALER_PQ_exec_prepared (conn, "contract_create", params); + status = PQresultStatus (res); + EXITIF (PGRES_COMMAND_OK != status); + PQclear (res); + return GNUNET_OK; + + EXITIF_exit: + PQclear (res); + return GNUNET_SYSERR; +} + +long long +MERCHANT_DB_get_contract_product (PGconn *conn, + uint64_t contract_id) +{ + PGresult *res; + uint64_t product; + ExecStatusType status; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_uint64 (&contract_id), + TALER_PQ_query_param_end + }; + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_uint64 ("product", &product), + TALER_PQ_result_spec_end + }; + + contract_id = GNUNET_htonll (contract_id); + res = TALER_PQ_exec_prepared (conn, "get_contract_product", params); + status = PQresultStatus (res); + EXITIF (PGRES_TUPLES_OK != status); + EXITIF (1 != PQntuples (res)); + EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); + PQclear (res); + return GNUNET_ntohll ((uint64_t) product); + + EXITIF_exit: + PQclear (res); + return -1; +} + +unsigned int +MERCHANT_DB_checkout_create (PGconn *conn, + struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub, + uint64_t transaction_id, + struct TALER_Amount *amount, + struct GNUNET_CRYPTO_rsa_Signature *coin_sig) +{ + PGresult *res; + ExecStatusType status; + uint32_t value_nbo; + uint32_t fraction_nbo; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_rsa_public_key (coin_pub), + TALER_PQ_query_param_uint64 (&transaction_id), + TALER_PQ_query_param_uint32 (&value_nbo), + TALER_PQ_query_param_uint32 (&fraction_nbo), + TALER_PQ_query_param_rsa_signature (coin_sig), + TALER_PQ_query_param_end + }; + + transaction_id = GNUNET_htonll (transaction_id); + value_nbo = htonl (amount->value); + fraction_nbo = htonl (amount->fraction); + res = TALER_PQ_exec_prepared (conn, "checkout_create", params); + status = PQresultStatus (res); + EXITIF (PGRES_COMMAND_OK != status); + PQclear (res); + return GNUNET_OK; + + EXITIF_exit: + PQclear (res); + return GNUNET_SYSERR; +} + + +long long +MERCHANT_DB_get_checkout_product (PGconn *conn, + struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub) +{ + PGresult *res; + ExecStatusType status; + uint64_t product; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_rsa_public_key (coin_pub), + TALER_PQ_query_param_end + }; + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_uint64 ("product", &product), + TALER_PQ_result_spec_end + }; + + product = -1; + res = TALER_PQ_exec_prepared (conn, "get_checkout_product", params); + status = PQresultStatus (res); + EXITIF (PGRES_TUPLES_OK != status); + if (0 == PQntuples (res)) + { + TALER_LOG_DEBUG ("Checkout not found for given coin"); + goto EXITIF_exit; + } + EXITIF (1 != PQntuples (res)); + EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); + PQclear (res); + return GNUNET_ntohll ((uint64_t) product); + + EXITIF_exit: + PQclear (res); + return -1; +} +/* end of merchant-db.c */ diff --git a/src/backend-lib/merchant_db.h b/src/backend-lib/merchant_db.h new file mode 100644 index 00000000..a723b229 --- /dev/null +++ b/src/backend-lib/merchant_db.h @@ -0,0 +1,101 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + 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, If not, see <http://www.gnu.org/licenses/> +*/ + +/** + * @file merchant/merchant_db.h + * @brief database helper functions used by the merchant + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#ifndef MERCHANT_DB_H +#define MERCHANT_DB_H + +#include <gnunet/gnunet_postgres_lib.h> +#include <taler/taler_util.h> + +/** + * Connect to postgresql database + * + * @param cfg the configuration handle + * @return connection to the postgresql database; NULL upon error + */ +PGconn * +MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg); + + +/** + * Disconnect from the database + * + * @param conn database handle to close + */ +void +MERCHANT_DB_disconnect (PGconn *conn); + + +/** + * Initialize merchant tables + * + * @param conn the connection handle to postgres db. + * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their + * contents are dropped when the @a conn is closed + * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure + */ +int +MERCHANT_DB_initialize (PGconn *conn, int tmp); + + +/** + * Inserts a contract record into the database and if successfull returns the + * serial number of the inserted row. + * + * @param conn the database connection + * @param expiry the time when the contract will expire + * @param amount the taler amount corresponding to the contract + * @param c_id this contract's identification number + * @param desc descripition of the contract + * @param nounce a random 64-bit nounce + * @param product description to identify a product + * @return GNUNET_OK on success, GNUNET_SYSERR upon error + */ + +uint32_t +MERCHANT_DB_contract_create (PGconn *conn, + const struct GNUNET_TIME_Absolute *expiry, + const struct TALER_Amount *amount, + uint64_t c_id, + const char *desc, + uint64_t nounce, + uint64_t product); + +long long +MERCHANT_DB_get_contract_product (PGconn *conn, + uint64_t contract_id); + +unsigned int +MERCHANT_DB_checkout_create (PGconn *conn, + struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub, + uint64_t transaction_id, + struct TALER_Amount *amount, + struct GNUNET_CRYPTO_rsa_Signature *coin_sig); + + +long long +MERCHANT_DB_get_checkout_product (PGconn *conn, + struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub); + +#endif /* MERCHANT_DB_H */ + +/* end of merchant-db.h */ diff --git a/src/backend-lib/taler-merchant-httpd_contract.c b/src/backend-lib/taler-merchant-httpd_contract.c new file mode 100644 index 00000000..55c56e0a --- /dev/null +++ b/src/backend-lib/taler-merchant-httpd_contract.c @@ -0,0 +1,144 @@ +#include "platform.h" +#include <jansson.h> +#include <taler/taler_signatures.h> +#include <gnunet/gnunet_util_lib.h> +#include "merchant.h" +#include "merchant_db.h" +#include "taler_merchant_contract_lib.h" + + +/* TODO: make this file a library, and programmatically call the following + * functions */ + +/** + * Macro to round microseconds to seconds in GNUNET_TIME_* structs. + */ +#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000) + +/** + * Shorthand for exit jumps. + */ +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + +/** +* Generate the hash containing the information (= a nounce + merchant's IBAN) to +* redeem money from mint in a subsequent /deposit operation +* @param nounce the nounce +* @param wire the merchant's wire details +* @return the hash to be included in the contract's blob +* +*/ + +static struct GNUNET_HashCode +hash_wireformat (uint64_t nounce, const struct MERCHANT_WIREFORMAT_Sepa *wire) +{ + struct GNUNET_HashContext *hc; + struct GNUNET_HashCode hash; + + hc = GNUNET_CRYPTO_hash_context_start (); + GNUNET_CRYPTO_hash_context_read (hc, wire->iban, strlen (wire->iban)); + GNUNET_CRYPTO_hash_context_read (hc, wire->name, strlen (wire->name)); + GNUNET_CRYPTO_hash_context_read (hc, wire->bic, strlen (wire->bic)); + nounce = GNUNET_htonll (nounce); + GNUNET_CRYPTO_hash_context_read (hc, &nounce, sizeof (nounce)); + GNUNET_CRYPTO_hash_context_finish (hc, &hash); + return hash; +} + +/** +* Take from the frontend the (partly) generated contract and fill +* the missing values in it; for example, the SEPA-aware values. +* Moreover, it stores the contract in the DB and does the signature of it. +* @param contract parsed contract, originated by the frontend +* @param db_conn the handle to the local DB +* @param kpriv merchant's private key +* @param wire merchant's bank's details +* @param sig where to store the signature +* @return pointer to the complete JSON; NULL upon errors +*/ + +/* TODO: this handles a simplified version (for debugging purposes) + of the contract. To expand to the full fledged version */ + +json_t * +MERCHANT_handle_contract (json_t *contract, + PGconn *db_conn, + struct GNUNET_CRYPTO_EddsaPrivateKey *kpriv, + const struct MERCHANT_WIREFORMAT_Sepa *wire, + struct GNUNET_CRYPTO_EddsaSignature *sig) +{ + json_t *root; + json_t *j_timestamp; + json_t *jh_wire; + json_t *j_amount; + json_int_t j_trans_id; + + uint64_t nounce; + uint64_t product_id; + char *a; + char *h_wire_enc; + char *str; + struct GNUNET_HashCode h_wire; + struct GNUNET_TIME_Absolute timestamp; + struct TALER_Amount amount; + struct TALER_AmountNBO amount_nbo; + struct ContractNBO contract_nbo; + + nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); + // timing mgmt + timestamp = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), + GNUNET_TIME_UNIT_DAYS); + ROUND_TO_SECS (timestamp, abs_value_us); + j_timestamp = TALER_json_from_abs (timestamp); + + // wireformat mgmt + h_wire = hash_wireformat (nounce, wire); + h_wire_enc = GNUNET_STRINGS_data_to_string_alloc (&h_wire, + sizeof (struct GNUNET_HashCode)); + + jh_wire = json_string (h_wire_enc); + + str = json_dumps (contract, JSON_INDENT(2) | JSON_PRESERVE_ORDER); + + + json_unpack (contract, "{s:o, s:I}", + "amount", &j_amount, + "trans_id", &j_trans_id); + + /* needed for DB stuff */ + TALER_json_to_amount (j_amount, &amount); + /* temporary way of getting this value. To be adapted to the expanded contract + format. See 'TODO' above. */ + product_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); + /* adding the generated values in this JSON */ + root = json_pack ("{s:o, s:I, s:s, s:o}", + "amount", j_amount, + "trans_id", j_trans_id, + "h_wire", jh_wire, + "timestamp", j_timestamp); + a = json_dumps (root, JSON_COMPACT | JSON_PRESERVE_ORDER); + + // DB mgmt + if (GNUNET_SYSERR == MERCHANT_DB_contract_create (db_conn, + ×tamp, + &amount, + (uint64_t) j_trans_id, // safe? + a, + nounce, + product_id)); + contract_nbo.h_wire = h_wire; + TALER_amount_hton (&amount_nbo, &amount); + contract_nbo.amount = amount_nbo; + contract_nbo.t = GNUNET_TIME_absolute_hton (timestamp); + contract_nbo.m = GNUNET_htonll ((uint64_t) j_trans_id); // safe? + GNUNET_CRYPTO_hash (a, strlen (a) + 1, &contract_nbo.h_contract_details); + free (a); + contract_nbo.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); + contract_nbo.purpose.size = htonl (sizeof (struct ContractNBO)); + GNUNET_CRYPTO_eddsa_sign (kpriv, &contract_nbo.purpose, sig); + + return root; +} diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h new file mode 100644 index 00000000..4e345703 --- /dev/null +++ b/src/backend-lib/taler_merchant_contract_lib.h @@ -0,0 +1,62 @@ + +GNUNET_NETWORK_STRUCT_BEGIN + +struct ContractNBO +{ + /** + * Purpose header for the signature over contract + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * The transaction identifier. NOTE: it was m[13]. TODO: + * change the API accordingly! + */ + uint64_t m; + + /** + * Expiry time + */ + struct GNUNET_TIME_AbsoluteNBO t; + + /** + * The invoice amount + */ + struct TALER_AmountNBO amount; + + /** + * The hash of the merchant's wire details (bank account information), with a nounce + */ + struct GNUNET_HashCode h_wire; + + /** + * Hash of the JSON contract in UTF-8 including 0-termination, + * using JSON_COMPACT encoding with sorted fields. + */ + struct GNUNET_HashCode h_contract_details; + +}; + +GNUNET_NETWORK_STRUCT_END + +/** +* Take from the frontend the (partly) generated contract and fill +* the missing values in it; for example, the SEPA-aware values. +* Moreover, it stores the contract in the DB and does the signature of it. +* @param contract parsed contract, originated by the frontend +* @param db_conn the handle to the local DB +* @param kpriv merchant's private key +* @param wire merchant's bank's details +* @param sig where to store the signature +* @return pointer to the complete JSON; NULL upon errors +*/ + +/* TODO: this handles a simplified version (for debugging purposes) + of the contract. To expand to the full fledged version */ + +json_t * +MERCHANT_handle_contract (json_t *contract, + PGconn *db_conn, + struct GNUNET_CRYPTO_EddsaPrivateKey *kpriv, + const struct MERCHANT_WIREFORMAT_Sepa *wire, + struct GNUNET_CRYPTO_EddsaSignature *sig); diff --git a/src/backend-lib/taler_merchant_lib.h b/src/backend-lib/taler_merchant_lib.h new file mode 100644 index 00000000..41891783 --- /dev/null +++ b/src/backend-lib/taler_merchant_lib.h @@ -0,0 +1 @@ +#include "taler_merchant_contract_lib.h" |