Changing configuration structure to enable multiple accounts.

This change enables using multiple wire plugins at the same time.
Also, we now distinguish between the wire plugin (i.e. EBICS or
taler_bank) and the wire method (i.e. SEPA or x-taler-bank) that
the wire plugin is implementing.  The "taler-bank" wire method
was renamed from "test" to "x-taler-bank".

This also changes the format of the /wire response of the exchange,
as we now need to return multiple accounts.  Note that wire fees
are specified per wire method, not per wire account.

taler-exchange-keyup now automatically signs all of the /wire
responses in the location specified by the configuration.

Account identification in wire plugins was changed to use
payto://-URLs instead of method-specific JSON fields.  Signing
and validation of /wire responses was moved from each wire
plugin to a generic validation method in libtalerutil (crypto)
or libtalerjson (for JSON-formatted inputs).
Convenience methods were added to generate JSON for wire accounts
(salting, signing).

Various section and option names were adjusted to streamline the
configuration and make it more consistent overall.  Documentation
was updated as well.
This commit is contained in:
Christian Grothoff 2018-04-02 14:24:45 +02:00
parent 7a20062baf
commit cb55c1a3af
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
110 changed files with 4485 additions and 3476 deletions

7
.gitignore vendored
View File

@ -59,10 +59,11 @@ src/exchange-tools/taler-exchange-wire
src/exchangedb/perf-exchangedb
src/benchmark/taler-exchange-benchmark
src/benchmark/test_benchmark_home/.local/share/taler/exchange/live-keys/
src/benchmark/test_benchmark_home/.local/share/taler/exchange/wirefees/
src/json/test_json
src/wire/test_sepa_wireformat
src/wire/test_wire_plugin
src/wire/test_wire_plugin_transactions_test
src/wire-plugins/test_ebics_wireformat
src/wire-plugins/test_wire_plugin
src/wire-plugins/test_wire_plugin_transactions_taler_bank
src/pq/test_pq
src/util/test_amount
src/util/test_crypto

View File

@ -536,5 +536,6 @@ AC_CONFIG_FILES([Makefile
src/util/Makefile
src/util/taler-config
src/wire/Makefile
src/wire-plugins/Makefile
])
AC_OUTPUT

View File

@ -17,27 +17,98 @@ TESTRUN = YES
[exchangedb-postgres]
DB_CONN_STR = "postgres:///talercheck"
[auditordb-postgres]
DB_CONN_STR = "postgres:///talercheck"
[exchange-wire-sepa]
# Change here to enable SEPA wire transfers.
ENABLE = NO
# Provide account information
[account-1]
# Accounts must have a payto:// URL
URL = payto://METHOD/DETAILS
# To be included in /wire, accounts must have a signed wire file
# Must match URL.
WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json
# For access to the account, we need to know the plugin.
PLUGIN = "ebics"
# TBD: authentication data.
# Accounts need to be enabled for the aggregator to debit them.
ENABLE_DEBIT = NO
# Accounts need to be enabled for the wirewatcher to look for credits into them.
ENABLE_CREDIT = YES
[account-2]
URL = payto://x-taler-wire/bank/2
# Response for /wire
SEPA_RESPONSE_FILE = "sepa.json"
# Must match URL.
WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-2.json
[exchange-wire-outgoing-sepa]
# TBD
PLUGIN = taler_bank
# We also may need authentication data.
TALER_BANK_AUTH_METHOD = "none" # or basic
# TALER_BANK_USERNAME = user
# TALER_BANK_PASSWORD = pass
# Accounts need to be enabled for the aggregator to debit them.
ENABLE_DEBIT = YES
# Accounts need to be enabled for the wirewatcher to look for credits into them.
ENABLE_CREDIT = YES
[exchange-wire-test]
# Change here to disable TEST wire transfers.
ENABLE = YES
# Wire fees are specified by wire method, NOT by wire plugin.
[fees-x-taler-bank]
# Fees for the forseeable future...
# If you see this after 2018, update to match the next 10 years...
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
WIRE-FEE-2020 = EUR:0.01
WIRE-FEE-2021 = EUR:0.01
WIRE-FEE-2022 = EUR:0.01
WIRE-FEE-2023 = EUR:0.01
WIRE-FEE-2024 = EUR:0.01
WIRE-FEE-2025 = EUR:0.01
WIRE-FEE-2026 = EUR:0.01
WIRE-FEE-2027 = EUR:0.01
# Response for /wire
TEST_RESPONSE_FILE = "test.json"
CLOSING-FEE-2018 = EUR:0.01
CLOSING-FEE-2019 = EUR:0.01
CLOSING-FEE-2020 = EUR:0.01
CLOSING-FEE-2021 = EUR:0.01
CLOSING-FEE-2022 = EUR:0.01
CLOSING-FEE-2023 = EUR:0.01
CLOSING-FEE-2024 = EUR:0.01
CLOSING-FEE-2025 = EUR:0.01
CLOSING-FEE-2026 = EUR:0.01
CLOSING-FEE-2027 = EUR:0.01
# What is the main website of the bank?
BANK_URL = "https://bank/"
# Into which account at the 'bank' should incoming
# wire transfers be made?
BANK_ACCOUNT_NUMBER = 2
[fees-sepa]
# Fees for the forseeable future...
# If you see this after 2018, update to match the next 10 years...
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
WIRE-FEE-2020 = EUR:0.01
WIRE-FEE-2021 = EUR:0.01
WIRE-FEE-2022 = EUR:0.01
WIRE-FEE-2023 = EUR:0.01
WIRE-FEE-2024 = EUR:0.01
WIRE-FEE-2025 = EUR:0.01
WIRE-FEE-2026 = EUR:0.01
WIRE-FEE-2027 = EUR:0.01
CLOSING-FEE-2018 = EUR:0.01
CLOSING-FEE-2019 = EUR:0.01
CLOSING-FEE-2020 = EUR:0.01
CLOSING-FEE-2021 = EUR:0.01
CLOSING-FEE-2022 = EUR:0.01
CLOSING-FEE-2023 = EUR:0.01
CLOSING-FEE-2024 = EUR:0.01
CLOSING-FEE-2025 = EUR:0.01
CLOSING-FEE-2026 = EUR:0.01
CLOSING-FEE-2027 = EUR:0.01

View File

@ -1,4 +1,4 @@
.TH TALER\-EXCHANGE\-WIRE 1 "Apr 2, 2016" "GNU Taler"
.TH TALER\-EXCHANGE\-WIRE 1 "Mar 18, 2018" "GNU Taler"
.SH NAME
taler\-exchange\-wire \- Create the master-key signed responses to /wire.
@ -13,18 +13,8 @@ taler\-exchange\-wire \- Create the master-key signed responses to /wire.
.SH OPTIONS
.B
.IP "\-j JSON, \-\-json=JSON"
Gives JSON with all of the relevant account details in a method-specific format.
.B
.IP "\-t METHOD, \-\-type=METHOD"
Specifies the wire transfer method to use. Common are 'test' and 'sepa'.
.B
.IP "\-m MASTERKEYFILE, \-\-master=MASTERKEYFILE"
Specifies the name of the file containing the exchange's master key.
.B
.IP "\-o FILENAME, \-\-output=FILENAME"
Where to write the SEPA_RESPONSE_FILE.
.B
.IP "\-h, \-\-help"
Print short help on options.
.B

View File

@ -13,7 +13,7 @@ This manual is for the GNU Taler Exchange
(version @value{VERSION}, @value{UPDATED}),
a payment service provider for GNU Taler.
Copyright @copyright{} 2014-2017 GNUnet e.V. and INRIA
Copyright @copyright{} 2014-2018 Taler Systems SA
@quotation
Permission is granted to copy, distribute and/or modify this document
@ -260,7 +260,7 @@ GNU libunistring @geq{} 0.9.3
libcurl @geq{} 7.26 (or libgnurl @geq{} 7.26)
@item
GNU libmicrohttpd @geq{} 0.9.52
GNU libmicrohttpd @geq{} 0.9.59
@item
GNU libgcrypt @geq{} 1.6
@ -354,6 +354,8 @@ description of some of the options.
The exchange works with three types of keys:
@c FIXME: explain better!
@itemize
@item
@ -366,34 +368,18 @@ The exchange works with three types of keys:
@cite{denomination keys} (see section @cite{Coins})
@end itemize
@cite{master key}: in section @cite{[exchange]}, edit the two following values:
@c FIXME: text here.
@itemize
@item
@cite{MASTER_PRIV_FILE}: Path to the exchange's master private file.
@item
@cite{master_priv_file}: Path to the exchange's master private file.
@item
@cite{master_public_key}: Must specify the exchange's master public key.
@cite{MASTER_PUBLIC_KEY}: Must specify the exchange's master public key.
@end itemize
@cite{sign keys}: the following two options under
@cite{[exchange_keys]} section control @cite{sign keys}:
@itemize
@item
@cite{signkey_duration}: How long should one signing key be used?
@item
@cite{lookahead_sign}: How much time we want to cover with our
@cite{signkeys}? Note that if @cite{signkey_duration} is bigger than
@cite{lookahead_sign}, @cite{taler-exchange-keyup} will generate a
quantity of @cite{signkeys} which is sufficient to cover all the
gap. See keys-duration.
@end itemize
@node Serving
@ -436,99 +422,148 @@ option @cite{currency} in section @cite{[taler]}.
@node Bank account
@section Bank account
To configure a bank account in Taler, we need to furnish four
pieces of information:
@itemize
@item
The @code{payto://} URL of the bank account, which uniquely idenfies
the account. Examples for such URLs include
@code{payto://sepa/CH9300762011623852957} for a bank account
in the single European payment area (SEPA) or
@code{payto://x-taler-bank/localhost:8080/2} for the 2nd bank
account a the Taler bank demonstrator running at @code{localhost}
on port 8080. The first part of the URL following @code{payto://}
(``sepa'' or ``x-taler-bank'') is called the wire method.
@item
A matching wire plugin that implements a protocol to interact
with the banking system. For example, the EBICS plugin can
be used for SEPA transfers, or the ``taler-bank'' plugin can
interact with the Taler bank demonstrator. A wire plugin
only supports one particular wire method. Thus, you must make
sure to pick a plugin that supports the wire method used in the
URL.
@item
A file containing the signed JSON-encoded bank account details
for the /wire API. This is necessary as Taler supports offline
signing for bank accounts for additional security.
@item
Finally, the plugin needs to be provided resources for
authentication to the respective banking service. The format
in which the authentication information must be provided
depends on the wire plugin.
@end itemize
You can configure multiple accounts for an exchange by creating
sections starting with ``account-'' for the section name. You can
ENABLE for each account whether it should be used, and for what
(incoming or outgoing wire transfers):
@setsyntax ini
@example
[account-1]
URL = "payto://sepa/CH9300762011623852957"
WIRE_RESPONSE = $@{TALER_CONFIG_HOME@}/account-1.json
PLUGIN = ebics
# Use for exchange-aggregator (outgoing transfers)
ENABLE_DEBIT = YES
# Use for exchange-wirewatch (and listed in /wire)
ENABLE_CREDIT = YES
# ... add authentication options here
@end example
The command line tool @cite{taler-exchange-wire} is used to create
the @code{account-1.json} file.
For example, the utility may be invoked as follows to create
all of the WIRE_RESPONSE files (in the locations specified by the configuration):
@example
$ taler-exchange-wire
@end example
The generated file will be echoed by the exchange when serving
/wire@footnote{https://api.taler.net/api-exchange.html#wire-req}
requests.
@menu
* Wiremethod-test::
* Wiremethod-sepa::
* Wire plugin ``taler_bank''::
* Wire plugin ``ebics''::
* Wire fee structure::
@end menu
@node Wiremethod-test
@subsection Wiremethod-test
@node Wire plugin ``taler_bank''
@subsection Wire plugin ``taler_bank''
@cindex x-taler-bank
@cindex taler_bank plugin
To enable the wire transfer method ``test'', you need to set
``ENABLE=YES'' in section @cite{[exchange-wire-test]}.
The @code{taler_bank} plugin implements the wire method ``x-taler-bank''.
The bank account where the exchange gets money from customers is
configured under the section @cite{[exchange-wire-test]}. It must
contain the options ``EXCHANGE_ACCOUNT_NO'' and ``BANK_URL''.
For basic authentication, it must additionally contain the
``USERNAME'' and ``PASSWORD'' of the exchange's account at the bank.
The format of the @code{payto://} URL is @code{payto://x-taler-bank/HOSTNAME:PORT},
possibly followed by other parameters like the amount and wire transfer subject
as per the @code{payto://} standard.
For incoming transfers, the section must additional contain the
option: @cite{test_response_file}, which takes
the path to a text file containing the exchange's bank account details
in JSON format.
The command line tool @cite{taler-exchange-wire} is used to create such a file.
For example, the utility may be invoked as follows:
For basic authentication, the @code{taler_bank} plugin only supports
simple password-based authentication. For this, the configuration
must contain the ``USERNAME'' and ``PASSWORD'' of the respective
account at the bank.
@setsyntax ini
@example
$ taler-exchange-wire -j '@{"name": "The Exchange", "account_number":
10, "bank_url": "https://bank.demo.taler.net", "type": "test"@}' -t
test -o exchange.json
[account-2]
URL = "payto://test/localhost:8080"
USERNAME = exchange
PASSWORD = super-secure
@end example
Note that the value given to option @cite{-t} must match the value in
the JSON's field @code{"type"}.
The generated file will be echoed by the exchange when serving
/wire@footnote{https://api.taler.net/api-exchange.html#wire-req}
requests.
@node Wire plugin ``ebics''
@subsection Wire plugin ``ebics''
The ``ebics'' wire plugin is not fully implemented and today
does not support actual wire transfers.
@node Wiremethod-sepa
@subsection Wiremethod-sepa
To enable the wire transfer method ``sepa'', you need to set
``ENABLE=YES'' in section @cite{[exchange-wire-sepa]}.
This section contains only one option: @cite{sepa_response_file},
which takes the path to a text file containing the exchange's bank
account details in JSON format.
The command line tool @cite{taler-exchange-wire} is used to create such a file.
For example, the utility may be invoked as follows:
@example
$ taler-exchange-wire -j '@{"name": "The Exchange", "account_number":
10, "bank_url": "https://bank.demo.taler.net", "type": "sepa"@}' -t
sepa -o exchange.json
@end example
Note that the value given to option @cite{-t} must match the value in the JSON's field @code{"type"}.
The generated file will be echoed by the exchange when serving
/wire@footnote{https://api.taler.net/api-exchange.html#wire-req}
requests.
This exchange's outgoing bank account is used to give money to merchants, after
successful
deposits@footnote{https://api.taler.net/api-exchange.html#deposit-par}
operations. The outgoing bank account is configured by the following
options under @cite{[exchange-wire-outgoing-sepa]}: (NOT YET SUPPORTED).
@quotation
@itemize
@item
@cite{exchange_account_numer}: which bank account number has the exchange
@item
@cite{bank_url}: base URL of the bank hosting the exchange bank account
@end itemize
@end quotation
@cartouche
@quotation Note
The rationale behind having two bank accounts is that the exchange operator, as a security
The rationale behind having multiple bank accounts is that the exchange operator, as a security
measure, may want to instruct the bank that the incoming bank account is only supposed to
@emph{receive} money.
@end quotation
@end cartouche
@node Wire fee structure
@subsection Wire fee structure
@cindex wire fee
@cindex fee
For each wire method (``sepa'' or ``x-taler-wire'', but not per plugin!) the
exchange configuration must specify applicable wire fees. This is done
in configuration sections of the format @code{fee-METHOD}. There are two
types of fees, simple wire fees and closing fees. Wire fees apply whenever
the aggregator transfers funds to a merchant. Closing fees apply whenever
the exchange closes a reserve (sending back funds to the customer). The
fees must be constant for a full year, which is specified as part of the name
of the option.
@setsyntax ini
@example
[fee-iban]
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
CLOSING-FEE-2018 = EUR:0.01
CLOSING-FEE-2019 = EUR:0.01
[fee-x-taler-bank]
WIRE-FEE-2018 = KUDOS:0.01
WIRE-FEE-2019 = KUDOS:0.01
CLOSING-FEE-2018 = KUDOS:0.01
CLOSING-FEE-2019 = KUDOS:0.01
@end example
@node Database
@section Database
@ -546,18 +581,18 @@ possible in two ways:
via an environment variable: @cite{TALER_EXCHANGEDB_POSTGRES_CONFIG}.
@item
via configuration option @cite{db_conn_str}, under section @cite{[exchangedb-BACKEND]}. For example, the demo exchange is configured as follows:
via configuration option @cite{CONFIG}, under section @cite{[exchangedb-BACKEND]}. For example, the demo exchange is configured as follows:
@end itemize
@setsyntax ini
@example
[exchange]
...
db = postgres
DB = postgres
...
[exchangedb-postgres]
db_conn_str = postgres:///talerdemo
CONFIG = postgres:///talerdemo
@end example
@node Coins denomination keys
@ -639,6 +674,25 @@ key will get a starting time of @cite{t}, and the @cite{j}-th key will
get a starting time of @cite{x + duration_withdraw}, where @cite{x} is
the starting time of the @cite{(j-1)}-th key.
To change these settings, edit the following
values in section @cite{[exchange]}:
@itemize
@item
@cite{SIGNKEY_DURATION}: How long should one signing key be used?
@item
@cite{LOOKAHEAD_SIGN}: How much time we want to cover with our
signing keys? Note that if @cite{SIGNKEY_DURATION} is bigger than
@cite{LOOKAHEAD_SIGN}, @code{taler-exchange-keyup} will generate a
quantity of signing keys which is sufficient to cover all the
gap.
@end itemize
@c FIXME: LEGAL_DURATION not covered!
@c FIXME: LOOKAHEAD_PROVIDE not covered!
@node Deployment
@chapter Deployment

View File

@ -1,4 +1,4 @@
.TH TALER.CONF 5 "Apr 11, 2016" "GNU Taler"
.TH TALER.CONF 5 "Mar 20, 2018" "GNU Taler"
.SH NAME
taler.conf \- Taler configuration file.
@ -10,46 +10,174 @@ taler.conf \- Taler configuration file.
The basic structure of the configuration file is the following. The file is split into sections. Every section begins with "[SECTIONNAME]" and contains a number of options of the form "OPTION=VALUE". Empty lines and lines beginning with a "#" are treated as comments. Files containing default values for many of the options described below are installed under $TALER\_PREFIX/share/taler/config.d/. The configuration file given with \-c to Taler binaries overrides these defaults.
.SH GLOBAL OPTIONS
The following options are from the "[taler]" section and used by virtually all Taler components.
.IP CURRENCY
Name of the currency, i.e. "EUR" for Euro.
The "[PATHS]" section is special in that it contains paths that can be referenced using "$" in other configuration values that specify filenames. For Taler, it commonly contains the following paths:
.IP TALER_HOME
Home directory of the user, usually "${HOME}". Can be overwritten by testcases by setting ${TALER_TEST_HOME}.
.IP TALER_DATA_HOME
Where should Taler store its long-term data. Usually "${TALER_HOME}/.local/share/taler/"
.IP TALER_CONFIG_HOME
Where is the Taler configuration kept. Usually "${TALER_HOME}/.config/taler/"
.IP TALER_CACHE_HOME
Where should Taler store cached data. Usually "${TALER_HOME}/.cache/taler/"
.IP TALER_RUNTIME_DIR
Where should Taler store system runtime data (like UNIX domain sockets). Usually "${TMP}/taler-system-runtime".
.SH EXCHANGE OPTIONS
The following options are from the "[exchange]" section and used by most exchange tools:
.IP CURRENCY
Name of the currency, i.e. "EUR" for Euro.
.IP DB
Plugin to use for the database, i.e. "postgres"
.IP PORT
Port on which the HTTP server listens, i.e. 8080.
.IP MASTER_PUBLIC_KEY
Crockford Base32-encoded master public key, public version of the exchange\'s long\-time offline signing key.
.IP MASTER_PRIV_FILE
Location of the master private key on disk. Only used by tools that can be run offline (as the master key is for offline signing).
.IP BASE_URL
Specifies the base URL under which the exchange can be reached. Added to wire transfers to enable tracking by merchants.
.IP SIGNKEY_DURATION
For how long is a signing key valid?
.IP LEGAL_DURATION
For how long are signatures with signing keys legally valid?
.IP LOOKAHEAD_SIGN
How long do we generate denomination and signing keys ahead of time?
.IP LOOKAHEAD_PROVIDE
How long into the future do we provide signing and denomination keys to clients?
.SH WIRE transfer details
To enable the "test" wire transfer method, you must set "ENABLE = YES" in "[exchange\-wire\-test]".
Then, the following options must be in section "[exchange\-wire\-incoming\-test]" and "[exchange\-wire\-outgoing\-test]":
.SH EXCHANGE POSTGRES BACKEND DATABASE OPTIONS
.IP BANK_URI
URL of the Taler bank.
The following options must be in section "[exchangedb\-postgres]" if the "postgres" plugin was selected for the database:
.IP BANK_ACCOUNT_NUMBER
Number of the bank account of the exchange.
To enable the "sepa" wire transfer method, you must set "ENABLE = YES" in "[exchange\-wire\-sepa]".
Then, the following options must be in section "[exchange\-wire\-incoming\-sepa]" and "[exchange\-wire\-outgoing\-sepa]":
.IP SEPA_RESPONSE_FILE
Filename with the JSON body for the /wire response, signed using the exchange\'s long-term offline master key. Use taler\-exchange\-sepa to create the SEPA_RESPONSE_FILE.
.SH Postgres database options
The following options must be in section "[exchangedb\-postgres]":
.IP DB_CONN_STR
.IP CONFIG
How to access the database, i.e. "postgres:///taler" to use the "taler" database. Testcases use "talercheck".
.SH COIN OPTIONS
.SH MERCHANT OPTIONS
The following options are from the "[merchant]" section and used by the merchant backend:
.IP DB
Plugin to use for the database, i.e. "postgres"
.IP PORT
Port on which the HTTP server listens, i.e. 8080.
.IP WIRE_TRANSFER_DELAY
How quickly do we want the exchange to send us money? Note that wire transfer fees will be higher if we ask for money to be wired often. Given as a relative time, i.e. "5 s"
.IP DEFAULT_MAX_WIRE_FEE
Maximum wire fee we are willing to accept from exchanges. Given as a Taler amount, i.e. "EUR:0.1"
.IP DEFAULT_MAX_DEPOSIT_FEE
Maximum deposit fee we are willing to cover. Given as a Taler amount, i.e. "EUR:0.1"
.SH MERCHANT POSTGRES BACKEND DATABASE OPTIONS
The following options must be in section "[merchantdb\-postgres]" if the "postgres" plugin was selected for the database:
.IP CONFIG
How to access the database, i.e. "postgres:///taler" to use the "taler" database. Testcases use "talercheck".
.SH MERCHANT INSTANCES
The merchant configuration must specify a set of instances, containing at least the "default" instance. The following options must be given in each "[instance-NAME]" section:
.IP KEYFILE
Name of the file where the instance\'s private key is to be stored, i.e. "${TALER_CONFIG_HOME}/merchant/instance/name.priv"
.IP NAME
Human-readable name of the instance, i.e. "Kudos Inc."
Additionally, for instances that support tipping, the following options are required:
.IP TIP_EXCHANGE
Base-URL of the exchange that holds the reserve for tipping, i.e. "https://exchange.demo.taler.net/"
.IP TIP_EXCHANGE_PRIV_FILENAME
Filename with the private key granting access to the reserve, i.e. "${TALER_CONFIG_HOME}/merchant/reserve/tip.priv"
.SH KNOWN EXCHANGES (for merchants and wallets)
The merchant configuration can include a list of known exchanges if the merchant wants to specify that certain exchanges are explicitly trusted. For each trusted exchange, a section [exchange-NAME] must exist, where NAME is a merchant-given name for the exchange. The following options must be given in each "[exchange-NAME]" section:
.IP BASE_URL
Base URL of the exchange, i.e. "https://exchange.demo.taler.net/"
.IP MASTER_KEY
Crockford Base32 encoded master public key, public version of the exchange\'s long\-time offline signing key
.IP CURRENCY
Name of the currency for which this exchange is trusted, i.e. "KUDOS"
.SH KNOWN AUDITORS (for merchants and wallets)
The merchant configuration can include a list of known exchanges if the merchant wants to specify that certain auditors are explicitly trusted. For each trusted exchange, a section [auditor-NAME] must exist, where NAME is a merchant-given name for the exchange. The following options must be given in each "[auditor-NAME]" section:
.IP BASE_URL
Base URL of the auditor, i.e. "https://auditor.demo.taler.net/"
.IP AUDITOR_KEY
Crockford Base32 encoded auditor public key.
.IP CURRENCY
Name of the currency for which this auditor is trusted, i.e. "KUDOS"
.SH ACCOUNT OPTIONS (for exchanges and merchants)
An exchange (or merchant) can have multiple bank accounts. The following options are for sections named "[account-SOMETHING]". The SOMETHING is arbitrary and should be chosen to uniquely identify the bank account for the operator. Additional authentication options may need to be specified in the account section depending on the PLUGIN used.
.IP URL
Specifies the payto://-URL of the account. The general format is payto://METHOD/DETAILS. This option is used for exchanges and merchants.
.IP WIRE_RESPONSE
Specifies the name of the file in which the /wire response for this account should be located. Used by the Taler exchange service and the taler\-exchange\-wire tool and the taler\-merchant\-httpd (to generate the files).
.IP PLUGIN
Name of the plugin can be used to access the account (i.e. "taler-bank" or "ebics"). Used by the merchant backend for back office operations (i.e. to identify incoming wire transfers) and by the exchange.
.IP ENABLE_DEBIT
Must be set to YES for the accounts that the taler\-exchange\-aggregator should debit. Not used by merchants.
.IP ENABLE_CREDIT
Must be set to YES for the accounts that the taler\-exchange\-wirewatch should check for credits. (It is yet uncertain if the merchant implementation may check this flag as well.)
.IP HONOR_instance
Must be set to YES for the instances (where "instance" is the section name of the instance) of the merchant backend that should allow incoming wire transfers for this bank account.
.IP ACTIVE_instance
Must be set to YES for the instances (where "instance" is the section name of the instance) of the merchant backend that should use this bank account in new offers/contracts. Setting ACTIVE_instance to YES requires also setting ENABLE_instance to YES.
.SH TALER-BANK AUTHENTICATION OPTIONS (for accounts)
The following authentication options are supported by the "taler-bank" wire plugin. They must be specified in the "[account-]" section that uses the "taler-bank" plugin.
.IP TALER_BANK_AUTH_METHOD
Authentication method to use. "none" or "basic" are currently supported.
.IP USERNAME
Username to use for authentication. Used with the "basic" authentication method.
.IP PASSWORD
Password to use for authentication. Used with the "basic" authentication method.
.SH EBICS AUTHENTICATION OPTIONS
The following authentication options are supported by the "ebics" wire plugin. They must be specified in the "[account-]" section that uses the "ebics" plugin.
.IP NONE
Currently the "ebics" implementation is incomplete and does not support authentication.
.SH EXCHANGE WIRE FEE OPTIONS
For each supported wire method (i.e. "x-taler-bank" or "sepa"), sections named "[fees-METHOD]" state the (aggregate) wire transfer fee and the reserve closing fees charged by the exchange. Note that fees are specified using the name of the wire method, not by the plugin name. You need to replace "YEAR" in the option name by the calendar year for which the fee should apply. Usually, fees should be given for serveral years in advance.
.IP WIRE-FEE-YEAR
Aggregate wire transfer fee merchants are charged in YEAR. Specified as a Taler amount using the usual amount syntax (CURRENCY:VALUE.FRACTION).
.IP CLOSING-FEE-YEAR
Reserve closing fee customers are charged in YEAR. Specified as a Taler amount using the usual amount syntax (CURRENCY:VALUE.FRACTION).
.SH EXCHANGE COIN OPTIONS
The following options must be in sections starting with "[coin_]" and are used by taler\-exchange\-keyup to create denomination keys:
@ -72,22 +200,27 @@ The following options must be in sections starting with "[coin_]" and are used b
.IP RSA_KEYSIZE
What is the RSA keysize modulos (in bits)?
.SH KEY OPTIONS
The following options are from the "[exchange_keys]" section and used by most taler\-exchange\-keyup to create keys:
.SH AUDITOR OPTIONS
.IP SIGNKEY_DURATION
For how long is a signing key valid?
.IP LEGAL_DURATION
For how long are signatures with signing keys legally valid?
.IP LOOKAHEAD_SIGN
How long do we generate denomination and signing keys ahead of time?
.IP LOOKAHEAD_PROVIDE
How long into the future do we provide signing and denomination keys to clients?
The following options must be in section "[auditor]" for the Taler auditor:
.IP DB
Plugin to use for the database, i.e. "postgres"
.IP AUDITOR_PRIV_FILE
Name of the file containing the auditor's private key
.SH AUDITOR POSTGRES BACKEND DATABASE OPTIONS
The following options must be in section "[auditordb\-postgres]" if the "postgres" plugin was selected for the database:
.IP CONFIG
How to access the database, i.e. "postgres:///taler" to use the "taler" database. Testcases use "talercheck".
.SH BUGS
Report bugs by using Mantis <https://gnunet.org/bugs/> or by sending electronic mail to <taler@gnu.org>
.SH "SEE ALSO"
\fBtaler\-exchange\-httpd\fP(1), \fBtaler\-exchange\-keyup\fP(1), \fBtaler\-exchange\-reservemod\fP(1), \fBtaler\-exchange\-dbinit\fP(1), \fBtaler\-exchange\-sepa(1)
\fBtaler\-exchange\-httpd\fP(1), \fBtaler\-exchange\-keyup\fP(1), \fBtaler\-exchange\-dbinit\fP(1), \fBtaler\-exchange\-wire(1)

View File

@ -22,7 +22,7 @@ pkgcfg_DATA = \
EXTRA_DIST = \
taler.conf
SUBDIRS = include util json $(PQ_DIR) $(BANK_LIB) wire exchangedb exchange exchange-tools auditordb auditor
SUBDIRS = include util wire json $(PQ_DIR) $(BANK_LIB) wire-plugins exchangedb exchange exchange-tools auditordb auditor
if HAVE_LIBCURL
SUBDIRS += exchange-lib benchmark
else

View File

@ -761,7 +761,7 @@ handle_reserve_in (void *cls,
uint64_t rowid,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *credit,
const json_t *sender_account_details,
const char *sender_account_details,
const void *wire_reference,
size_t wire_reference_size,
struct GNUNET_TIME_Absolute execution_date)
@ -1193,7 +1193,7 @@ handle_reserve_closed (void *cls,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *closing_fee,
const struct TALER_ReservePublicKeyP *reserve_pub,
const json_t *receiver_account,
const char *receiver_account,
const struct TALER_WireTransferIdentifierRawP *transfer_details)
{
struct ReserveContext *rc = cls;
@ -1773,11 +1773,6 @@ struct WireCheckContext
*/
struct GNUNET_TIME_Absolute date;
/**
* Wire method used for the transfer.
*/
const char *method;
/**
* Database transaction status.
*/
@ -2069,8 +2064,8 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
* @param cls a `struct WireCheckContext`
* @param rowid which row in the table is the information from (for diagnostics)
* @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
* @param wire_method which wire plugin was used for the transfer?
* @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
* @param account_details where did we transfer the funds?
* @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
* @param h_contract_terms which proposal was this payment about
* @param coin_pub which public key was this payment about
@ -2082,8 +2077,8 @@ static void
wire_transfer_information_cb (void *cls,
uint64_t rowid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const char *wire_method,
const struct GNUNET_HashCode *h_wire,
const json_t *account_details,
struct GNUNET_TIME_Absolute exec_time,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -2097,6 +2092,29 @@ wire_transfer_information_cb (void *cls,
struct TALER_EXCHANGEDB_TransactionList *tl;
const struct TALER_CoinPublicInfo *coin;
enum GNUNET_DB_QueryStatus qs;
struct GNUNET_HashCode hw;
if (GNUNET_OK !=
TALER_JSON_wire_signature_hash (account_details,
&hw))
{
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation",
rowid,
"failed to compute hash of given wire data");
return;
}
if (0 !=
memcmp (&hw,
h_wire,
sizeof (struct GNUNET_HashCode)))
{
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation",
rowid,
"database contains wrong hash code for wire details");
return;
}
/* Obtain coin's transaction history */
qs = edb->get_coin_transactions (edb->cls,
@ -2183,8 +2201,9 @@ wire_transfer_information_cb (void *cls,
tl);
/* Check other details of wire transfer match */
if (0 != strcmp (wire_method,
wcc->method))
if (0 != memcmp (h_wire,
&wcc->h_wire,
sizeof (struct GNUNET_HashCode)))
{
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation",
@ -2360,12 +2379,12 @@ check_wire_out_cb (void *cls,
{
struct AggregationContext *ac = cls;
struct WireCheckContext wcc;
json_t *method;
struct TALER_WIRE_Plugin *plugin;
const struct TALER_Amount *wire_fee;
struct TALER_Amount final_amount;
struct TALER_Amount exchange_gain;
enum GNUNET_DB_QueryStatus qs;
char *method;
/* should be monotonically increasing */
GNUNET_assert (rowid >= pp.last_wire_out_serial_id);
@ -2376,25 +2395,22 @@ check_wire_out_cb (void *cls,
TALER_B2S (wtid),
TALER_amount2s (amount),
GNUNET_STRINGS_absolute_time_to_string (date));
wcc.ac = ac;
method = json_object_get (wire,
"type");
if ( (NULL == method) ||
(! json_is_string (method)) )
if (NULL == (method = TALER_JSON_wire_to_method (wire)))
{
report_row_inconsistency ("wire_out",
rowid,
"specified wire address lacks type");
"specified wire address lacks method");
return GNUNET_OK;
}
wcc.method = json_string_value (method);
wcc.ac = ac;
wcc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
wcc.date = date;
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (amount->currency,
&wcc.total_deposits));
if (GNUNET_OK !=
TALER_JSON_hash (wire,
TALER_JSON_wire_signature_hash (wire,
&wcc.h_wire))
{
GNUNET_break (0);
@ -2409,6 +2425,7 @@ check_wire_out_cb (void *cls,
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
ac->qs = qs;
GNUNET_free (method);
return GNUNET_SYSERR;
}
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs)
@ -2417,17 +2434,19 @@ check_wire_out_cb (void *cls,
report_row_inconsistency ("wire_out",
rowid,
"audit of associated transactions failed");
GNUNET_free (method);
return GNUNET_OK;
}
/* Subtract aggregation fee from total */
wire_fee = get_wire_fee (ac,
wcc.method,
method,
date);
if (NULL == wire_fee)
{
GNUNET_break (0);
ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
GNUNET_free (method);
return GNUNET_SYSERR;
}
if (GNUNET_SYSERR ==
@ -2440,18 +2459,20 @@ check_wire_out_cb (void *cls,
&wcc.total_deposits,
wire_fee,
-1);
GNUNET_free (method);
return GNUNET_OK;
}
/* Round down to amount supported by wire method */
plugin = get_wire_plugin (ac,
wcc.method);
method);
if (NULL == plugin)
{
GNUNET_break (0);
GNUNET_free (method);
return GNUNET_SYSERR;
}
GNUNET_free (method);
GNUNET_break (GNUNET_SYSERR !=
plugin->amount_round (plugin->cls,
&final_amount));
@ -3408,7 +3429,7 @@ deposit_cb (void *cls,
dr.purpose.size = htonl (sizeof (dr));
dr.h_contract_terms = *h_contract_terms;
if (GNUNET_OK !=
TALER_JSON_hash (receiver_wire_account,
TALER_JSON_wire_signature_hash (receiver_wire_account,
&dr.h_wire))
{
GNUNET_break (0);

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2017 Taler Systems SA
Copyright (C) 2017-2018 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
@ -40,6 +40,45 @@
*/
#define GRACE_PERIOD GNUNET_TIME_UNIT_HOURS
/**
* Information we keep for each supported account.
*/
struct WireAccount
{
/**
* Accounts are kept in a DLL.
*/
struct WireAccount *next;
/**
* Plugins are kept in a DLL.
*/
struct WireAccount *prev;
/**
* Handle to the plugin.
*/
struct TALER_WIRE_Plugin *wire_plugin;
/**
* Name of the section that configures this account.
*/
char *section_name;
/**
* We should check for inbound transactions to this account.
*/
int watch_credit;
/**
* We should check for outbound transactions from this account.
*/
int watch_debit;
};
/**
* Return value from main().
*/
@ -50,11 +89,6 @@ static int global_ret;
*/
static int restart;
/**
* Name of the wire plugin to load to access the exchange's bank account.
*/
static char *wire_plugin;
/**
* Handle to access the exchange's database.
*/
@ -103,11 +137,27 @@ static struct TALER_AUDITORDB_Session *asession;
*/
static struct TALER_MasterPublicKeyP master_pub;
/**
* Head of list of wire accounts we still need to look at.
*/
static struct WireAccount *wa_head;
/**
* Tail of list of wire accounts we still need to look at.
*/
static struct WireAccount *wa_tail;
/**
* Handle to the wire plugin for wire operations.
*/
static struct TALER_WIRE_Plugin *wp;
/**
* Name of the section that configures the account
* we are currently processing (matches #wp).
*/
static char *wp_section_name;
/**
* Active wire request for the transaction history.
*/
@ -289,7 +339,8 @@ free_rii (void *cls,
GNUNET_CONTAINER_multihashmap_remove (in_map,
key,
rii));
json_decref (rii->details.account_details);
GNUNET_free (rii->details.account_url);
GNUNET_free_non_null (rii->details.wtid_s); /* field not used (yet) */
GNUNET_free (rii);
return GNUNET_OK;
}
@ -314,7 +365,8 @@ free_roi (void *cls,
GNUNET_CONTAINER_multihashmap_remove (out_map,
key,
roi));
json_decref (roi->details.account_details);
GNUNET_free (roi->details.account_url);
GNUNET_free_non_null (roi->details.wtid_s); /* field not used (yet) */
GNUNET_free (roi);
return GNUNET_OK;
}
@ -328,6 +380,8 @@ free_roi (void *cls,
static void
do_shutdown (void *cls)
{
struct WireAccount *wa;
if (NULL != report_row_inconsistencies)
{
json_t *report;
@ -407,6 +461,20 @@ do_shutdown (void *cls)
TALER_WIRE_plugin_unload (wp);
wp = NULL;
}
if (NULL != wp_section_name)
{
GNUNET_free (wp_section_name);
wp_section_name = NULL;
}
while (NULL != (wa = wa_head))
{
GNUNET_CONTAINER_DLL_remove (wa_head,
wa_tail,
wa);
TALER_WIRE_plugin_unload (wa->wire_plugin);
GNUNET_free (wa->section_name);
GNUNET_free (wa);
}
if (NULL != adb)
{
TALER_AUDITORDB_plugin_unload (adb);
@ -470,6 +538,7 @@ commit (enum GNUNET_DB_QueryStatus qs)
qs = adb->update_wire_auditor_progress (adb->cls,
asession,
&master_pub,
wp_section_name,
&pp,
in_wire_off,
out_wire_off,
@ -478,6 +547,7 @@ commit (enum GNUNET_DB_QueryStatus qs)
qs = adb->insert_wire_auditor_progress (adb->cls,
asession,
&master_pub,
wp_section_name,
&pp,
in_wire_off,
out_wire_off,
@ -583,8 +653,12 @@ wire_out_cb (void *cls,
amount));
return GNUNET_OK;
}
if (! json_equal ((json_t *) wire,
roi->details.account_details))
{
char *payto_url;
payto_url = TALER_JSON_wire_to_payto (wire);
if (0 != strcasecmp (payto_url,
roi->details.account_url))
{
/* Destination bank account is wrong in actual wire transfer, so
we should count the wire transfer as entirely spurious, and
@ -613,8 +687,11 @@ wire_out_cb (void *cls,
TALER_amount_add (&total_bad_amount_out_minus,
&total_bad_amount_out_minus,
amount));
GNUNET_free (payto_url);
goto cleanup;
}
GNUNET_free (payto_url);
}
if (0 != TALER_amount_cmp (&roi->details.amount,
amount))
{
@ -764,6 +841,16 @@ wire_missing_cb (void *cls,
}
/**
* Start processing the next wire account.
* Shuts down if we are done.
*
* @param cls NULL
*/
static void
process_next_account (void *cls);
/**
* Go over the "wire_out" table of the exchange and
* verify that all wire outs are in that table.
@ -818,9 +905,8 @@ check_exchange_wire_out ()
}
pp.last_timestamp = next_timestamp;
/* conclude with: */
commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
GNUNET_SCHEDULER_shutdown ();
/* continue with next account: */
process_next_account (NULL);
}
@ -892,7 +978,7 @@ history_debit_cb (void *cls,
roi->details.amount = details->amount;
roi->details.execution_date = details->execution_date;
roi->details.wtid = details->wtid;
roi->details.account_details = json_incref ((json_t *) details->account_details);
roi->details.account_url = GNUNET_strdup (details->account_url);
if (GNUNET_OK !=
GNUNET_CONTAINER_multihashmap_put (out_map,
&roi->subject_hash,
@ -936,6 +1022,7 @@ process_debits ()
out_map = GNUNET_CONTAINER_multihashmap_create (1024,
GNUNET_YES);
hh = wp->get_history (wp->cls,
wp_section_name,
TALER_BANK_DIRECTION_DEBIT,
out_wire_off,
wire_off_size,
@ -965,7 +1052,7 @@ process_debits ()
* @param rowid unique serial ID for the refresh session in our DB
* @param reserve_pub public key of the reserve (also the WTID)
* @param credit amount that was received
* @param sender_account_details information about the sender's bank account
* @param sender_url payto://-URL of the sender's bank account
* @param wire_reference unique identifier for the wire transfer (plugin-specific format)
* @param wire_reference_size number of bytes in @a wire_reference
* @param execution_date when did we receive the funds
@ -976,7 +1063,7 @@ reserve_in_cb (void *cls,
uint64_t rowid,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *credit,
const json_t *sender_account_details,
const char *sender_url,
const void *wire_reference,
size_t wire_reference_size,
struct GNUNET_TIME_Absolute execution_date)
@ -997,7 +1084,7 @@ reserve_in_cb (void *cls,
memcpy (&rii->details.wtid,
reserve_pub,
sizeof (*reserve_pub));
rii->details.account_details = json_incref ((json_t *) sender_account_details);
rii->details.account_url = GNUNET_strdup (sender_url);
rii->rowid = rowid;
if (GNUNET_OK !=
GNUNET_CONTAINER_multihashmap_put (in_map,
@ -1011,7 +1098,8 @@ reserve_in_cb (void *cls,
"row", (json_int_t) rowid,
"wire_offset_hash", GNUNET_JSON_from_data_auto (&rii->row_off_hash),
"diagnostic", "duplicate wire offset"));
json_decref (rii->details.account_details);
GNUNET_free (rii->details.account_url);
GNUNET_free_non_null (rii->details.wtid_s); /* field not used (yet) */
GNUNET_free (rii);
return GNUNET_OK;
}
@ -1228,8 +1316,8 @@ history_credit_cb (void *cls,
}
goto cleanup;
}
if (! json_equal (details->account_details,
rii->details.account_details))
if (0 != strcasecmp (details->account_url,
rii->details.account_url))
{
report (report_missattribution_in_inconsistencies,
json_pack ("{s:s, s:o, s:o}",
@ -1266,6 +1354,167 @@ history_credit_cb (void *cls,
/* ***************************** Setup logic ************************ */
/**
* Start processing the next wire account.
* Shuts down if we are done.
*
* @param cls NULL
*/
static void
process_next_account (void *cls)
{
struct WireAccount *wa;
enum GNUNET_DB_QueryStatus qs;
int ret;
(void) cls;
if (NULL == (wa = wa_head))
{
commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
GNUNET_SCHEDULER_shutdown ();
return;
}
GNUNET_CONTAINER_DLL_remove (wa_head,
wa_tail,
wa);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Starting audit of account `%s'\n",
wa->section_name);
/* setup globals */
if (NULL != wp)
TALER_WIRE_plugin_unload (wp);
wp = wa->wire_plugin;
GNUNET_free_non_null (wp_section_name);
wp_section_name = wa->section_name;
GNUNET_free (wa);
ret = adb->start (adb->cls,
asession);
if (GNUNET_OK != ret)
{
GNUNET_break (0);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
edb->preflight (edb->cls,
esession);
ret = edb->start (edb->cls,
esession,
"wire auditor");
if (GNUNET_OK != ret)
{
GNUNET_break (0);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
qsx = adb->get_wire_auditor_progress (adb->cls,
asession,
&master_pub,
wp_section_name,
&pp,
&in_wire_off,
&out_wire_off,
&wire_off_size);
if (0 > qsx)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
{
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
_("First analysis using this auditor, starting audit from scratch\n"));
}
else
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Resuming audit at %llu/%llu\n"),
(unsigned long long) pp.last_reserve_in_serial_id,
(unsigned long long) pp.last_wire_out_serial_id);
}
in_map = GNUNET_CONTAINER_multihashmap_create (1024,
GNUNET_YES);
qs = edb->select_reserves_in_above_serial_id (edb->cls,
esession,
pp.last_reserve_in_serial_id,
&reserve_in_cb,
NULL);
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"No new incoming transactions available, skipping CREDIT phase\n");
process_debits ();
return;
}
hh = wp->get_history (wp->cls,
wp_section_name,
TALER_BANK_DIRECTION_CREDIT,
in_wire_off,
wire_off_size,
INT64_MAX,
&history_credit_cb,
NULL);
if (NULL == hh)
{
fprintf (stderr,
"Failed to obtain bank transaction history\n");
commit (GNUNET_DB_STATUS_HARD_ERROR);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
}
/**
* Function called with information about a wire account. Adds the
* account to our list for processing (if it is enabled and we can
* load the plugin).
*
* @param cls closure, NULL
* @param ai account information
*/
static void
process_account_cb (void *cls,
const struct TALER_EXCHANGEDB_AccountInfo *ai)
{
struct WireAccount *wa;
struct TALER_WIRE_Plugin *wp;
wp = TALER_WIRE_plugin_load (cfg,
ai->plugin_name);
if (NULL == wp)
{
fprintf (stderr,
"Failed to load wire plugin `%s'\n",
ai->plugin_name);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
wa = GNUNET_new (struct WireAccount);
wa->wire_plugin = wp;
wa->section_name = GNUNET_strdup (ai->section_name);
wa->watch_debit = ai->debit_enabled;
wa->watch_credit = ai->credit_enabled;
GNUNET_CONTAINER_DLL_insert (wa_head,
wa_tail,
wa);
}
/**
* Main function that will be run.
*
@ -1281,8 +1530,6 @@ run (void *cls,
const struct GNUNET_CONFIGURATION_Handle *c)
{
static const struct TALER_MasterPublicKeyP zeromp;
enum GNUNET_DB_QueryStatus qs;
int ret;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Launching auditor\n");
@ -1390,40 +1637,6 @@ run (void *cls,
GNUNET_SCHEDULER_shutdown ();
return;
}
wp = TALER_WIRE_plugin_load (cfg,
wire_plugin);
if (NULL == wp)
{
fprintf (stderr,
"Failed to load wire plugin `%s'\n",
wire_plugin);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Starting audit\n");
ret = adb->start (adb->cls,
asession);
if (GNUNET_OK != ret)
{
GNUNET_break (0);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
edb->preflight (edb->cls,
esession);
ret = edb->start (edb->cls,
esession,
"wire auditor");
if (GNUNET_OK != ret)
{
GNUNET_break (0);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
GNUNET_assert (NULL !=
(report_wire_out_inconsistencies = json_array ()));
GNUNET_assert (NULL !=
@ -1462,71 +1675,9 @@ run (void *cls,
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency,
&zero));
qsx = adb->get_wire_auditor_progress (adb->cls,
asession,
&master_pub,
&pp,
&in_wire_off,
&out_wire_off,
&wire_off_size);
if (0 > qsx)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
{
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
_("First analysis using this auditor, starting audit from scratch\n"));
}
else
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Resuming audit at %llu/%llu\n"),
(unsigned long long) pp.last_reserve_in_serial_id,
(unsigned long long) pp.last_wire_out_serial_id);
}
in_map = GNUNET_CONTAINER_multihashmap_create (1024,
GNUNET_YES);
qs = edb->select_reserves_in_above_serial_id (edb->cls,
esession,
pp.last_reserve_in_serial_id,
&reserve_in_cb,
TALER_EXCHANGEDB_find_accounts (cfg,
&process_account_cb,
NULL);
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"No new incoming transactions available, skipping CREDIT phase\n");
process_debits ();
return;
}
hh = wp->get_history (wp->cls,
TALER_BANK_DIRECTION_CREDIT,
in_wire_off,
wire_off_size,
INT64_MAX,
&history_credit_cb,
NULL);
if (NULL == hh)
{
fprintf (stderr,
"Failed to obtain bank transaction history\n");
commit (GNUNET_DB_STATUS_HARD_ERROR);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
}
@ -1552,12 +1703,6 @@ main (int argc,
"restart",
"restart audit from the beginning (required on first run)",
&restart),
GNUNET_GETOPT_option_mandatory
(GNUNET_GETOPT_option_string ('w',
"wire",
"PLUGINNAME",
"name of the wire plugin to use",
&wire_plugin)),
GNUNET_GETOPT_OPTION_END
};

View File

@ -1,3 +1,3 @@
[auditordb-postgres]
# Argument for Postgres for how to connect to the database.
DB_CONN_STR = "postgres:///taler"
CONFIG = "postgres:///taler"

View File

@ -231,6 +231,7 @@ postgres_create_tables (void *cls)
")"),
GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS wire_auditor_progress"
"(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)"
",account_name TEXT NOT NULL"
",last_wire_reserve_in_serial_id INT8 NOT NULL DEFAULT 0"
",last_wire_wire_out_serial_id INT8 NOT NULL DEFAULT 0"
",last_timestamp INT8 NOT NULL"
@ -521,13 +522,14 @@ postgres_prepare (PGconn *db_conn)
GNUNET_PQ_make_prepare ("wire_auditor_progress_insert",
"INSERT INTO wire_auditor_progress "
"(master_pub"
",account_name"
",last_wire_reserve_in_serial_id"
",last_wire_wire_out_serial_id"
",last_timestamp"
",wire_in_off"
",wire_out_off"
") VALUES ($1,$2,$3,$4,$5,$6);",
6),
") VALUES ($1,$2,$3,$4,$5,$6,$7);",
7),
/* Used in #postgres_update_wire_auditor_progress() */
GNUNET_PQ_make_prepare ("wire_auditor_progress_update",
"UPDATE wire_auditor_progress SET "
@ -536,8 +538,8 @@ postgres_prepare (PGconn *db_conn)
",last_timestamp=$3"
",wire_in_off=$4"
",wire_out_off=$5"
" WHERE master_pub=$6",
6),
" WHERE master_pub=$6 AND account_name=$7",
7),
/* Used in #postgres_get_wire_auditor_progress() */
GNUNET_PQ_make_prepare ("wire_auditor_progress_select",
"SELECT"
@ -547,8 +549,8 @@ postgres_prepare (PGconn *db_conn)
",wire_in_off"
",wire_out_off"
" FROM wire_auditor_progress"
" WHERE master_pub=$1;",
1),
" WHERE master_pub=$1 AND account_name=$2;",
2),
/* Used in #postgres_insert_reserve_info() */
GNUNET_PQ_make_prepare ("auditor_reserves_insert",
"INSERT INTO auditor_reserves "
@ -1342,6 +1344,7 @@ postgres_get_auditor_progress (void *cls,
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param master_pub master key of the exchange
* @param account_name name of the wire account we are auditing
* @param pp where is the auditor in processing
* @return transaction status code
*/
@ -1349,6 +1352,7 @@ static enum GNUNET_DB_QueryStatus
postgres_insert_wire_auditor_progress (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
const char *account_name,
const struct TALER_AUDITORDB_WireProgressPoint *pp,
const void *in_wire_off,
const void *out_wire_off,
@ -1356,6 +1360,7 @@ postgres_insert_wire_auditor_progress (void *cls,
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_string (account_name),
GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id),
GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id),
TALER_PQ_query_param_absolute_time (&pp->last_timestamp),
@ -1379,6 +1384,7 @@ postgres_insert_wire_auditor_progress (void *cls,
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param master_pub master key of the exchange
* @param account_name name of the wire account we are auditing
* @param pp where is the auditor in processing
* @return transaction status code
*/
@ -1386,6 +1392,7 @@ static enum GNUNET_DB_QueryStatus
postgres_update_wire_auditor_progress (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
const char *account_name,
const struct TALER_AUDITORDB_WireProgressPoint *pp,
const void *in_wire_off,
const void *out_wire_off,
@ -1400,6 +1407,7 @@ postgres_update_wire_auditor_progress (void *cls,
GNUNET_PQ_query_param_fixed_size (out_wire_off,
wire_off_size),
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_string (account_name),
GNUNET_PQ_query_param_end
};
@ -1415,6 +1423,7 @@ postgres_update_wire_auditor_progress (void *cls,
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param master_pub master key of the exchange
* @param account_name name of the wire account we are auditing
* @param[out] pp set to where the auditor is in processing
* @return transaction status code
*/
@ -1422,6 +1431,7 @@ static enum GNUNET_DB_QueryStatus
postgres_get_wire_auditor_progress (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
const char *account_name,
struct TALER_AUDITORDB_WireProgressPoint *pp,
void **in_wire_off,
void **out_wire_off,
@ -1431,6 +1441,7 @@ postgres_get_wire_auditor_progress (void *cls,
enum GNUNET_DB_QueryStatus qs;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_string (account_name),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@ -2594,12 +2605,12 @@ libtaler_plugin_auditordb_postgres_init (void *cls)
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"auditordb-postgres",
"db_conn_str",
"CONFIG",
&pg->connection_cfg_str))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"auditordb-postgres",
"db_conn_str");
"CONFIG");
GNUNET_free (pg);
return NULL;
}

View File

@ -4,4 +4,4 @@ DB = postgres
[auditordb-postgres]
# Argument for Postgres for how to connect to the database.
DB_CONN_STR = "postgres:///talercheck"
CONFIG = "postgres:///talercheck"

View File

@ -32,7 +32,8 @@ libtalerbank_la_SOURCES = \
bank_api_admin.c \
bank_api_common.c bank_api_common.h \
bank_api_history.c \
bank_api_reject.c
bank_api_reject.c \
bank_api_parse.c
libtalerbank_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
@ -148,6 +149,7 @@ test_bank_api_new_SOURCES = \
test_bank_api_new_LDADD = \
$(top_builddir)/src/exchange-lib/libtalertesting.la \
$(top_builddir)/src/json/libtalerjson.la \
libtalerbanktesting.la \
-ltalerexchange \
-lgnunetutil \

View File

@ -1,8 +1,11 @@
[taler]
currency = KUDOS
[account-1]
URL = payto://x-taler-bank/localhost:8081/1
[bank]
http_port = 8081
HTTP_PORT = 8081
[exchange-wire-test]
bank_url = http://localhost:8081/

View File

@ -136,10 +136,12 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh,
GNUNET_JSON_parse_free (hist_spec);
return GNUNET_SYSERR;
}
td.account_details = json_pack ("{s:s, s:s, s:I}",
"type", "test",
"bank_url", hh->bank_base_url,
"account_number", (json_int_t) other_account);
GNUNET_asprintf (&td.account_url,
('/' == hh->bank_base_url[strlen(hh->bank_base_url)-1])
? "payto://x-taler-bank/%s%llu"
: "payto://x-taler-bank/%s/%llu",
hh->bank_base_url,
(unsigned long long) other_account);
hh->hcb (hh->hcb_cls,
MHD_HTTP_OK,
TALER_EC_NONE,
@ -147,7 +149,7 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh,
row_id,
&td,
transaction);
json_decref (td.account_details);
GNUNET_free (td.account_url);
GNUNET_JSON_parse_free (hist_spec);
}
return GNUNET_OK;

View File

@ -0,0 +1,133 @@
/*
This file is part of TALER
Copyright (C) 2018 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 bank-lib/bank_api_parse.c
* @brief Convenience function to parse authentication configuration
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_bank_service.h"
/**
* Parse configuration section with bank authentication data.
*
* @param cfg configuration to parse
* @param section the section with the configuration data
* @param auth[out] set to the configuration data found
* @return #GNUNET_OK on success
*/
int
TALER_BANK_auth_parse_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *section,
struct TALER_BANK_AuthenticationData *auth)
{
const struct {
const char *m;
enum TALER_BANK_AuthenticationMethod e;
} methods[] = {
{ "NONE", TALER_BANK_AUTH_NONE },
{ "BASIC", TALER_BANK_AUTH_BASIC },
{ NULL, TALER_BANK_AUTH_NONE }
};
char *method;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
section,
"TALER_BANK_AUTH_METHOD",
&method))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
section,
"TALER_BANK_AUTH_METHOD");
return GNUNET_SYSERR;
}
for (unsigned int i=0; NULL != methods[i].m;i++)
{
if (0 == strcasecmp (method,
methods[i].m))
{
switch (methods[i].e)
{
case TALER_BANK_AUTH_NONE:
auth->method = TALER_BANK_AUTH_NONE;
return GNUNET_OK;
case TALER_BANK_AUTH_BASIC:
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
section,
"USERNAME",
&auth->details.basic.username))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
section,
"USERNAME");
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
section,
"PASSWORD",
&auth->details.basic.password))
{
GNUNET_free (auth->details.basic.username);
auth->details.basic.username = NULL;
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
section,
"USERNAME");
return GNUNET_SYSERR;
}
auth->method = TALER_BANK_AUTH_BASIC;
return GNUNET_OK;
}
}
}
return GNUNET_SYSERR;
}
/**
* Free memory inside of @a auth (but not auth itself).
* Dual to #TALER_BANK_auth_parse_cfg().
*
* @param auth authentication data to free
*/
void
TALER_BANK_auth_free (struct TALER_BANK_AuthenticationData *auth)
{
switch (auth->method)
{
case TALER_BANK_AUTH_NONE:
break;
case TALER_BANK_AUTH_BASIC:
if (NULL != auth->details.basic.username)
{
GNUNET_free (auth->details.basic.username);
auth->details.basic.username = NULL;
}
if (NULL != auth->details.basic.password)
{
GNUNET_free (auth->details.basic.password);
auth->details.basic.password = NULL;
}
break;
}
}
/* end of bank_api_parse.c */

View File

@ -210,7 +210,8 @@ main (int argc,
"DEBUG",
NULL);
if (NULL ==
(fakebank_url = TALER_TESTING_prepare_fakebank (CONFIG_FILE)))
(fakebank_url = TALER_TESTING_prepare_fakebank (CONFIG_FILE,
"account-1")))
return 77;
return (GNUNET_OK == TALER_TESTING_setup (&run,

View File

@ -326,15 +326,10 @@ build_history (struct InterpreterState *is,
h[total].direction = TALER_BANK_DIRECTION_CREDIT;
if (GNUNET_YES == cancelled)
h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
h[total].details.account_details
= json_pack ("{s:s, s:s, s:I}",
"type",
"test",
"bank_url",
GNUNET_asprintf (&h[total].details.account_url,
"payto://x-taler-bank/%s/%llu",
"http://localhost:8080",
"account_number",
(json_int_t) pos->details.admin_add_incoming.debit_account_no);
GNUNET_assert (NULL != h[total].details.account_details);
(unsigned long long) pos->details.admin_add_incoming.debit_account_no);
}
if ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_DEBIT)) &&
(cmd->details.history.account_number ==
@ -343,15 +338,10 @@ build_history (struct InterpreterState *is,
h[total].direction = TALER_BANK_DIRECTION_DEBIT;
if (GNUNET_YES == cancelled)
h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
h[total].details.account_details
= json_pack ("{s:s, s:s, s:I}",
"type",
"test",
"bank_url",
GNUNET_asprintf (&h[total].details.account_url,
"payto://x-taler-bank/%s/%llu",
"http://localhost:8080",
"account_number",
(json_int_t) pos->details.admin_add_incoming.credit_account_no);
GNUNET_assert (NULL != h[total].details.account_details);
(unsigned long long) pos->details.admin_add_incoming.credit_account_no);
}
if ( ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_CREDIT)) &&
(cmd->details.history.account_number ==
@ -398,10 +388,6 @@ print_expected (struct History *h,
"Expected history:\n");
for (uint64_t i=0;i<h_len;i++)
{
char *acc;
acc = json_dumps (h[i].details.account_details,
JSON_COMPACT);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"H(%llu): %s%s (serial: %llu, subject: %s, to: %s)\n",
(unsigned long long) i,
@ -409,9 +395,7 @@ print_expected (struct History *h,
TALER_amount2s (&h[i].details.amount),
(unsigned long long) h[i].row_id,
h[i].details.wire_transfer_subject,
acc);
if (NULL != acc)
free (acc);
h[i].details.account_url);
}
}
@ -429,7 +413,7 @@ free_history (struct History *h,
for (uint64_t off = 0;off<h_len;off++)
{
GNUNET_free (h[off].details.wire_transfer_subject);
json_decref (h[off].details.account_details);
GNUNET_free (h[off].details.account_url);
}
GNUNET_free_non_null (h);
}
@ -500,8 +484,8 @@ check_result (struct InterpreterState *is,
details->wire_transfer_subject)) ||
(0 != TALER_amount_cmp (&h[off].details.amount,
&details->amount)) ||
(1 != json_equal (h[off].details.account_details,
details->account_details)) )
(0 != strcasecmp (h[off].details.account_url,
details->account_url)) )
{
GNUNET_break (0);
print_expected (h, total, off);

View File

@ -99,6 +99,7 @@ history_traits (void *cls,
return GNUNET_SYSERR;
}
/**
* Test if the /admin/add/incoming transaction at offset @a off
* has been /rejected.
@ -115,7 +116,8 @@ test_cancelled (struct TALER_TESTING_Interpreter *is,
const struct TALER_TESTING_Command *current_cmd;
current_cmd = &is->commands[off];
TALER_LOG_INFO ("Is `%s' rejected?\n", current_cmd->label);
TALER_LOG_INFO ("Is `%s' rejected?\n",
current_cmd->label);
for (unsigned int i=0;i<is->ip;i++)
{
const struct TALER_TESTING_Command *c = &is->commands[i];
@ -140,6 +142,7 @@ test_cancelled (struct TALER_TESTING_Interpreter *is,
return GNUNET_NO;
}
/**
* Free history @a h of length @a h_len.
*
@ -153,11 +156,12 @@ free_history (struct History *h,
for (uint64_t off = 0;off<h_len;off++)
{
GNUNET_free (h[off].details.wire_transfer_subject);
json_decref (h[off].details.account_details);
GNUNET_free (h[off].details.account_url);
}
GNUNET_free_non_null (h);
}
/**
* Log which history we expected.
*
@ -178,10 +182,6 @@ print_expected (struct History *h,
"Expected history:\n");
for (uint64_t i=0;i<h_len;i++)
{
char *acc;
acc = json_dumps (h[i].details.account_details,
JSON_COMPACT);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"H(%llu): %s%s (serial: %llu, subject: %s, to: %s)\n",
(unsigned long long) i,
@ -189,12 +189,11 @@ print_expected (struct History *h,
TALER_amount2s (&h[i].details.amount),
(unsigned long long) h[i].row_id,
h[i].details.wire_transfer_subject,
acc);
if (NULL != acc)
free (acc);
h[i].details.account_url);
}
}
/**
* Build history of transactions matching the current
* command in @a is.
@ -409,15 +408,10 @@ build_history (struct TALER_TESTING_Interpreter *is,
h[total].direction = TALER_BANK_DIRECTION_CREDIT;
if (GNUNET_YES == cancelled)
h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
h[total].details.account_details
= json_pack ("{s:s, s:s, s:I}",
"type",
"test",
"bank_url",
GNUNET_asprintf (&h[total].details.account_url,
"payto://x-taler-bank/%s/%llu",
hs->bank_url,
"account_number",
(json_int_t) *debit_account_no);
GNUNET_assert (NULL != h[total].details.account_details);
(unsigned long long) *debit_account_no);
}
if ( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) &&
(hs->account_no == *debit_account_no))
@ -425,15 +419,10 @@ build_history (struct TALER_TESTING_Interpreter *is,
h[total].direction = TALER_BANK_DIRECTION_DEBIT;
if (GNUNET_YES == cancelled)
h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
h[total].details.account_details
= json_pack ("{s:s, s:s, s:I}",
"type",
"test",
"bank_url",
GNUNET_asprintf (&h[total].details.account_url,
"payto://x-taler-bank/%s/%llu",
hs->bank_url,
"account_number",
(json_int_t) *credit_account_no);
GNUNET_assert (NULL != h[total].details.account_details);
(unsigned long long) *credit_account_no);
}
if ( ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) &&
(hs->account_no == *credit_account_no)) ||
@ -533,8 +522,8 @@ check_result (struct TALER_TESTING_Interpreter *is,
details->wire_transfer_subject)) ||
(0 != TALER_amount_cmp (&h[off].details.amount,
&details->amount)) ||
(1 != json_equal (h[off].details.account_details,
details->account_details)) )
(0 != strcasecmp (h[off].details.account_url,
details->account_url)) )
{
GNUNET_break (0);
print_expected (h, total, off);

View File

@ -127,7 +127,7 @@ reject_run (void *cls,
(GNUNET_OK == TALER_TESTING_GET_TRAIT_ROW_ID
(deposit_cmd, &row_id));
TALER_LOG_INFO ("Account %llu rejects deposit\n",
*credit_account);
(unsigned long long) *credit_account);
rs->rh = TALER_BANK_reject (is->ctx,
rs->bank_url,
&AUTHS[*credit_account -1],

View File

@ -895,7 +895,7 @@ spend_coin (struct Coin *coin,
dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
dr.h_contract_terms = h_contract_terms;
GNUNET_assert (GNUNET_OK ==
TALER_JSON_hash (merchant_details,
TALER_JSON_wire_signature_hash (merchant_details,
&dr.h_wire));
dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);

View File

@ -20,22 +20,20 @@ DB = postgres
MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
[exchangedb-postgres]
DB_CONN_STR = "postgres:///talercheck"
CONFIG = "postgres:///talercheck"
[exchange-wire-test]
# Enable 'test' for testing of the actual coin operations.
ENABLE = YES
[account-exchange]
# What is the main website of the bank?
# (Not used unless the aggregator is run.)
BANK_URL = "http://localhost:8082/"
# From which account at the 'bank' should outgoing wire transfers be made?
BANK_ACCOUNT_NUMBER = 2
URL = "payto://x-taler-bank/localhost:8082/2"
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json
WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account.json
[fees-x-taler-bank]
WIRE-FEE-2017 = KUDOS:0.01
WIRE-FEE-2018 = KUDOS:0.01
WIRE-FEE-2019 = KUDOS:0.01

View File

@ -61,6 +61,7 @@ libtalertesting_la_SOURCES = \
testing_api_trait_coin_priv.c \
testing_api_trait_denom_pub.c \
testing_api_trait_denom_sig.c \
testing_api_trait_json.c \
testing_api_trait_process.c \
testing_api_trait_reserve_priv.c \
testing_api_trait_number.c \
@ -69,8 +70,8 @@ libtalertesting_la_SOURCES = \
testing_api_trait_key_peer.c \
testing_api_trait_wtid.c \
testing_api_trait_amount.c
libtalertesting_la_LIBADD = \
$(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/bank-lib/libtalerfakebank.la \

View File

@ -416,7 +416,7 @@ TALER_EXCHANGE_deposit (struct TALER_EXCHANGE_Handle *exchange,
MAH_handle_is_ready (exchange));
/* initialize h_wire */
if (GNUNET_OK !=
TALER_JSON_hash (wire_details,
TALER_JSON_wire_signature_hash (wire_details,
&h_wire))
{
GNUNET_break (0);

View File

@ -139,7 +139,7 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
if (0 == strcasecmp (type,
"DEPOSIT"))
{
json_t *wire_account;
const char *wire_url;
void *wire_reference;
size_t wire_reference_size;
struct GNUNET_TIME_Absolute timestamp;
@ -150,8 +150,8 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
&wire_reference_size),
GNUNET_JSON_spec_absolute_time ("timestamp",
&timestamp),
GNUNET_JSON_spec_json ("sender_account_details",
&wire_account),
GNUNET_JSON_spec_string ("sender_account_url",
&wire_url),
GNUNET_JSON_spec_end()
};
@ -173,7 +173,7 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
rhistory[off].details.in_details.sender_account_details = wire_account;
rhistory[off].details.in_details.sender_url = GNUNET_strdup (wire_url);
rhistory[off].details.in_details.wire_reference = wire_reference;
rhistory[off].details.in_details.wire_reference_size = wire_reference_size;
rhistory[off].details.in_details.timestamp = timestamp;
@ -361,7 +361,7 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
TALER_amount_hton (&rcc.closing_amount,
&amount);
if (GNUNET_OK !=
TALER_JSON_hash (rhistory[off].details.close_details.receiver_account_details,
TALER_JSON_wire_signature_hash (rhistory[off].details.close_details.receiver_account_details,
&rcc.h_wire))
{
GNUNET_break (0);
@ -440,8 +440,7 @@ free_rhistory (struct TALER_EXCHANGE_ReserveHistory *rhistory,
{
case TALER_EXCHANGE_RTT_DEPOSIT:
GNUNET_free_non_null (rhistory[i].details.in_details.wire_reference);
if (NULL != rhistory[i].details.in_details.sender_account_details)
json_decref (rhistory[i].details.in_details.sender_account_details);
GNUNET_free_non_null (rhistory[i].details.in_details.sender_url);
break;
case TALER_EXCHANGE_RTT_WITHDRAWAL:
break;

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
Copyright (C) 2014-2018 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,6 +27,7 @@
#include <gnunet/gnunet_curl_lib.h>
#include "taler_exchange_service.h"
#include "taler_json_lib.h"
#include "taler_wire_lib.h"
#include "taler_signatures.h"
#include "taler_wire_plugin.h"
#include "exchange_api_handle.h"
@ -63,66 +64,133 @@ struct TALER_EXCHANGE_WireHandle
*/
void *cb_cls;
/**
* Set to the "methods" JSON array returned by the
* /wire request.
*/
json_t *methods;
/**
* Current iteration offset in the @e methods array.
*/
unsigned int methods_off;
};
/**
* Verify that the signature on the "200 OK" response
* for /wire/METHOD from the exchange is valid.
*
* @param wh wire handle with key material
* @param method method to verify the reply for
* @param json json reply with the signature
* @return #GNUNET_SYSERR if @a json is invalid,
* #GNUNET_NO if the method is unknown,
* #GNUNET_OK if the json is valid
* List of wire fees by method.
*/
static int
verify_wire_method_signature_ok (const struct TALER_EXCHANGE_WireHandle *wh,
const char *method,
const json_t *json)
struct FeeMap
{
const struct TALER_EXCHANGE_Keys *key_state;
struct TALER_WIRE_Plugin *plugin;
char *lib_name;
char *emsg;
enum TALER_ErrorCode ec;
/**
* Next entry in list.
*/
struct FeeMap *next;
key_state = TALER_EXCHANGE_get_keys (wh->exchange);
(void) GNUNET_asprintf (&lib_name,
"libtaler_plugin_wire_%s",
method);
plugin = GNUNET_PLUGIN_load (lib_name,
NULL);
if (NULL == plugin)
/**
* Wire method this fee structure is for.
*/
char *method;
/**
* Array of wire fees, also linked list, but allocated
* only once.
*/
struct TALER_EXCHANGE_WireAggregateFees *fee_list;
};
/**
* Frees @a fm.
*
* @param fm memory to release
*/
static void
free_fees (struct FeeMap *fm)
{
GNUNET_free (lib_name);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Wire transfer method `%s' not supported\n",
method);
return GNUNET_NO;
while (NULL != fm)
{
struct FeeMap *fe = fm->next;
GNUNET_free (fm->fee_list);
GNUNET_free (fm->method);
GNUNET_free (fm);
fm = fe;
}
plugin->library_name = lib_name;
ec = plugin->wire_validate (plugin->cls,
json,
&key_state->master_pub,
&emsg);
GNUNET_free_non_null (emsg);
GNUNET_PLUGIN_unload (lib_name,
plugin);
GNUNET_free (lib_name);
return (TALER_EC_NONE == ec) ? GNUNET_OK : GNUNET_SYSERR;
}
/**
* Parse wire @a fees and return map.
*
* @param fees json AggregateTransferFee to parse
* @return NULL on error
*/
static struct FeeMap *
parse_fees (json_t *fees)
{
struct FeeMap *fm = NULL;
const char *key;
json_t *fee_array;
json_object_foreach (fees, key, fee_array) {
struct FeeMap *fe = GNUNET_new (struct FeeMap);
int len;
unsigned int idx;
json_t *fee;
if (0 == (len = json_array_size (fee_array)))
{
GNUNET_break_op (0);
GNUNET_free (fe);
continue; /* skip */
}
fe->method = GNUNET_strdup (key);
fe->next = fm;
fe->fee_list = GNUNET_new_array (len,
struct TALER_EXCHANGE_WireAggregateFees);
fm = fe;
json_array_foreach (fee_array, idx, fee)
{
struct TALER_EXCHANGE_WireAggregateFees *wa = &fe->fee_list[idx];
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("sig",
&wa->master_sig),
TALER_JSON_spec_amount ("wire_fee",
&wa->wire_fee),
TALER_JSON_spec_amount ("closing_fee",
&wa->closing_fee),
GNUNET_JSON_spec_absolute_time ("start_date",
&wa->start_date),
GNUNET_JSON_spec_absolute_time ("end_date",
&wa->end_date),
GNUNET_JSON_spec_end()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (fee,
spec,
NULL,
NULL))
{
GNUNET_break_op (0);
free_fees (fm);
return NULL;
}
if (idx < len)
wa->next = &fe->fee_list[idx + 1];
}
}
return fm;
}
/**
* Find fee by @a method.
*
* @param fm map to look in
* @param method key to look for
* @return NULL if fee is not specified in @a fm
*/
static const struct TALER_EXCHANGE_WireAggregateFees *
lookup_fee (const struct FeeMap *fm,
const char *method)
{
for (;NULL != fm; fm = fm->next)
if (0 == strcasecmp (fm->method,
method))
return fm->fee_list;
return NULL;
}
@ -140,70 +208,131 @@ handle_wire_finished (void *cls,
const json_t *json)
{
struct TALER_EXCHANGE_WireHandle *wh = cls;
json_t *keep = NULL;
enum TALER_ErrorCode ec;
wh->job = NULL;
ec = TALER_EC_NONE;
switch (response_code)
{
case 0:
break;
case MHD_HTTP_OK:
{
const struct TALER_EXCHANGE_Keys *keys;
const struct TALER_MasterPublicKeyP *master_pub;
const char *key;
json_t *method;
int ret;
json_t *accounts;
json_t *fees;
int num_accounts;
struct FeeMap *fm;
const struct TALER_EXCHANGE_Keys *key_state;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("accounts", &accounts),
GNUNET_JSON_spec_json ("fees", &fees),
GNUNET_JSON_spec_end()
};
/* We 'keep' methods that we support and that are well-formed;
we fail (by setting response_code=0) if any method that we do
support fails to verify. */
keep = json_object ();
json_object_foreach ((json_t *) json, key, method) {
ret = verify_wire_method_signature_ok (wh,
key,
method);
if (GNUNET_SYSERR == ret)
{
/* bogus reply */
GNUNET_break_op (0);
response_code = 0;
}
/* GNUNET_NO: not understood by us, simply skip! */
if (GNUNET_OK == ret)
{
/* supported and valid, keep! */
json_object_set (keep,
key,
method);
}
}
/* check fees */
keys = TALER_EXCHANGE_get_keys (wh->exchange);
if (NULL == keys)
master_pub = NULL;
else
master_pub = &keys->master_pub;
if (GNUNET_OK !=
TALER_EXCHANGE_wire_get_fees (master_pub,
keep,
NULL,
NULL))
GNUNET_JSON_parse (json,
spec,
NULL, NULL))
{
/* bogus reply */
GNUNET_break_op (0);
response_code = 0;
}
if (0 != response_code)
{
/* all supported methods were valid, use 'keep' for 'json' */
ec = TALER_EC_SERVER_JSON_INVALID;
break;
}
/* some supported methods were invalid, release 'keep', preserve
full 'json' for application-level error handling. */
json_decref (keep);
keep = NULL;
if (0 == (num_accounts = json_array_size (accounts)))
{
/* bogus reply */
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
response_code = 0;
ec = TALER_EC_SERVER_JSON_INVALID;
break;
}
if (NULL == (fm = parse_fees (fees)))
{
/* bogus reply */
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
response_code = 0;
ec = TALER_EC_SERVER_JSON_INVALID;
break;
}
key_state = TALER_EXCHANGE_get_keys (wh->exchange);
/* parse accounts */
{
struct TALER_EXCHANGE_WireAccount was[num_accounts];
for (unsigned int i=0;i<num_accounts;i++)
{
struct TALER_EXCHANGE_WireAccount *wa = &was[i];
json_t *account;
struct GNUNET_JSON_Specification spec_account[] = {
GNUNET_JSON_spec_string ("url", &wa->url),
GNUNET_JSON_spec_string ("salt", &wa->salt),
GNUNET_JSON_spec_fixed_auto ("master_sig", &wa->master_sig),
GNUNET_JSON_spec_end()
};
char *method;
account = json_array_get (accounts,
i);
if (GNUNET_OK !=
TALER_JSON_wire_signature_check (account,
&key_state->master_pub))
{
/* bogus reply */
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_SERVER_SIGNATURE_INVALID;
break;
}
if (GNUNET_OK !=
GNUNET_JSON_parse (account,
spec_account,
NULL, NULL))
{
/* bogus reply */
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_SERVER_JSON_INVALID;
break;
}
if (NULL == (method = TALER_WIRE_payto_get_method (wa->url)))
{
/* bogus reply */
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_SERVER_JSON_INVALID;
break;
}
if (NULL == (wa->fees = lookup_fee (fm,
method)))
{
/* bogus reply */
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_SERVER_JSON_INVALID;
GNUNET_free (method);
break;
}
GNUNET_free (method);
} /* end 'for all accounts */
if ( (0 != response_code) &&
(NULL != wh->cb) )
{
wh->cb (wh->cb_cls,
response_code,
TALER_EC_NONE,
num_accounts,
was);
wh->cb = NULL;
}
} /* end of 'parse accounts */
free_fees (fm);
GNUNET_JSON_parse_free (spec);
} /* end of MHD_HTTP_OK */
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
@ -226,12 +355,12 @@ handle_wire_finished (void *cls,
response_code = 0;
break;
}
if (NULL != wh->cb)
wh->cb (wh->cb_cls,
response_code,
TALER_JSON_get_error_code (json),
(NULL != keep) ? keep : json);
if (NULL != keep)
json_decref (keep);
(0 == response_code) ? ec : TALER_JSON_get_error_code (json),
0,
NULL);
TALER_EXCHANGE_wire_cancel (wh);
}
@ -306,157 +435,9 @@ TALER_EXCHANGE_wire_cancel (struct TALER_EXCHANGE_WireHandle *wh)
GNUNET_CURL_job_cancel (wh->job);
wh->job = NULL;
}
if (NULL != wh->methods)
{
json_decref (wh->methods);
wh->methods = NULL;
}
GNUNET_free (wh->url);
GNUNET_free (wh);
}
/**
* Parse wire @a fee and store the result in @a af.
*
* @param[out] af where to write the result
* @param fee json AggregateTransferFee to parse
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
static int
parse_json_fees (struct TALER_EXCHANGE_WireAggregateFees *af,
json_t *fee)
{
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("sig",
&af->master_sig),
TALER_JSON_spec_amount ("wire_fee",
&af->wire_fee),
TALER_JSON_spec_amount ("closing_fee",
&af->closing_fee),
GNUNET_JSON_spec_absolute_time ("start_date",
&af->start_date),
GNUNET_JSON_spec_absolute_time ("end_date",
&af->end_date),
GNUNET_JSON_spec_end()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (fee,
spec,
NULL,
NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/**
* Check the #TALER_SIGNATURE_MASTER_WIRE_FEES signature.
*
* @param af record to check
* @param wire_method wire method to check against
* @param master_pub expected signing key
* @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
*/
static int
check_sig (const struct TALER_EXCHANGE_WireAggregateFees *af,
const char *wire_method,
const struct TALER_MasterPublicKeyP *master_pub)
{
struct TALER_MasterWireFeePS wp;
wp.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES);
wp.purpose.size = htonl (sizeof (wp));
GNUNET_CRYPTO_hash (wire_method,
strlen (wire_method) + 1,
&wp.h_wire_method);
wp.start_date = GNUNET_TIME_absolute_hton (af->start_date);
wp.end_date = GNUNET_TIME_absolute_hton (af->end_date);
TALER_amount_hton (&wp.wire_fee,
&af->wire_fee);
TALER_amount_hton (&wp.closing_fee,
&af->closing_fee);
return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES,
&wp.purpose,
&af->master_sig.eddsa_signature,
&master_pub->eddsa_pub);
}
/**
* Obtain information about wire fees encoded in @a obj
* by wire method.
*
* @param master_pub public key to use to verify signatures, NULL to not verify
* @param obj wire information as encoded in the #TALER_EXCHANGE_WireResultCallback
* @param cb callback to invoke for the fees
* @param cb_cls closure for @a cb
* @return #GNUNET_OK in success, #GNUNET_SYSERR if @a obj is ill-formed
*/
int
TALER_EXCHANGE_wire_get_fees (const struct TALER_MasterPublicKeyP *master_pub,
const json_t *obj,
TALER_EXCHANGE_WireFeeCallback cb,
void *cb_cls)
{
const char *wire_method;
json_t *value;
json_object_foreach (((json_t *) obj), wire_method, value)
{
json_t *fees;
size_t num_fees;
fees = json_object_get (value, "fees");
if ( (NULL == fees) ||
(! json_is_array (fees)) )
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
num_fees = json_array_size (fees);
if (num_fees > 1024)
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
{
struct TALER_EXCHANGE_WireAggregateFees af[num_fees + 1];
for (size_t i=0;i<num_fees;i++)
{
af[i].next = &af[i+1];
if (GNUNET_OK !=
parse_json_fees (&af[i],
json_array_get (fees,
i)))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if ( (NULL != master_pub) &&
(GNUNET_OK !=
check_sig (&af[i],
wire_method,
master_pub)) )
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
af[num_fees - 1].next = NULL;
if (NULL != cb)
cb (cb_cls,
wire_method,
&af[0]);
}
}
return GNUNET_OK;
}
/* end of exchange_api_wire.c */

View File

@ -36,9 +36,12 @@
#define WIRE_TEST 1
/**
* Is the configuration file is set to include wire format 'sepa'?
* Is the configuration file is set to include wire format 'ebics'?
* Requires that EBICS /history function is implemented, which it
* is currently not. Once it is, set ENABLE_CREDIT to YES in the
* configuration and then set this option to 1.
*/
#define WIRE_SEPA 1
#define WIRE_EBICS 0
/**
* Account number of the exchange at the bank.
@ -1539,12 +1542,15 @@ find_pk (const struct TALER_EXCHANGE_Keys *keys,
}
#if LEGACY
/* Tests the *old* /wire API, the _modern_ testcase was adapted,
but little point in right now adapting the old testcase */
/**
* Function called with information about the wire fees
* for each wire method.
*
* @param cls closure
* @param wire_method name of the wire method (i.e. "sepa")
* @param wire_method name of the wire method (i.e. "ebics")
* @param fees fee structure for this method
*/
static void
@ -1556,9 +1562,9 @@ check_fee_cb (void *cls,
struct Command *cmd = &is->commands[is->ip];
struct TALER_Amount expected_amount;
GNUNET_break ( (0 == strcasecmp ("test",
GNUNET_break ( (0 == strcasecmp ("x-taler-bank",
wire_method)) ||
(0 == strcasecmp ("sepa",
(0 == strcasecmp ("ebics",
wire_method)) );
if (GNUNET_OK !=
TALER_string_to_amount (cmd->details.wire.expected_fee,
@ -1582,6 +1588,7 @@ check_fee_cb (void *cls,
fees = fees->next;
}
}
#endif
/**
@ -1592,14 +1599,15 @@ check_fee_cb (void *cls,
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful request;
* 0 if the exchange's reply is bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param obj the received JSON reply, if successful this should be the wire
* format details as provided by /wire.
* @param accounts_len length of the @a accounts array
* @param accounts list of wire accounts of the exchange, NULL on error
*/
static void
wire_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const json_t *obj)
unsigned int accounts_len,
const struct TALER_EXCHANGE_WireAccount *accounts)
{
struct InterpreterState *is = cls;
struct Command *cmd = &is->commands[is->ip];
@ -1611,7 +1619,9 @@ wire_cb (void *cls,
"Unexpected response code %u to command %s\n",
http_status,
cmd->label);
#if LEGACY
json_dumpf (obj, stderr, 0);
#endif
fail (is);
return;
}
@ -1619,6 +1629,7 @@ wire_cb (void *cls,
{
case MHD_HTTP_OK:
{
#if LEGACY
json_t *method;
method = json_object_get (obj,
@ -1646,6 +1657,7 @@ wire_cb (void *cls,
fail (is);
return;
}
#endif
}
break;
default:
@ -1760,7 +1772,7 @@ wire_deposits_cb (void *cls,
JSON_REJECT_DUPLICATES,
NULL);
GNUNET_assert (GNUNET_OK ==
TALER_JSON_hash (wire,
TALER_JSON_wire_signature_hash (wire,
&hw));
json_decref (wire);
if (0 != memcmp (&hw,
@ -2331,12 +2343,14 @@ interpreter_run (void *cls)
{
struct TALER_DepositRequestPS dr;
memset (&dr, 0, sizeof (dr));
memset (&dr,
0,
sizeof (dr));
dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
dr.h_contract_terms = h_contract_terms;
GNUNET_assert (GNUNET_OK ==
TALER_JSON_hash (wire,
TALER_JSON_wire_signature_hash (wire,
&dr.h_wire));
dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
@ -2581,7 +2595,7 @@ interpreter_run (void *cls)
NULL);
GNUNET_assert (NULL != wire);
GNUNET_assert (GNUNET_OK ==
TALER_JSON_hash (wire,
TALER_JSON_wire_signature_hash (wire,
&h_wire));
json_decref (wire);
contract_terms = json_loads (ref->details.deposit.contract_terms,
@ -2640,7 +2654,6 @@ interpreter_run (void *cls)
"taler-exchange-wirewatch",
"taler-exchange-wirewatch",
"-c", "test_exchange_api.conf",
"-t", "test", /* use Taler's bank/fakebank */
"-T", /* exit when done */
NULL);
if (NULL == cmd->details.run_wirewatch.wirewatch_proc)
@ -3204,15 +3217,15 @@ run (void *cls)
.label = "wire-test",
/* expecting 'test' method in response */
.expected_response_code = MHD_HTTP_OK,
.details.wire.format = "test",
.details.wire.format = "x-taler-bank",
.details.wire.expected_fee = "EUR:0.01" },
#endif
#if WIRE_SEPA
#if WIRE_EBICS
{ .oc = OC_WIRE,
.label = "wire-sepa",
/* expecting 'sepa' method in response */
/* expecting 'ebics' method in response */
.expected_response_code = MHD_HTTP_OK,
.details.wire.format = "sepa",
.details.wire.format = "ebics",
.details.wire.expected_fee = "EUR:0.01" },
#endif
/* *************** end of /wire testing ************** */
@ -3252,7 +3265,7 @@ run (void *cls)
.expected_response_code = MHD_HTTP_OK,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }" },
/* Try to overdraw funds ... */
@ -3268,7 +3281,7 @@ run (void *cls)
.expected_response_code = MHD_HTTP_FORBIDDEN,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":43 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/43\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }" },
/* Try to double-spend the 5 EUR coin at the same merchant (but different
transaction ID) */
@ -3277,7 +3290,7 @@ run (void *cls)
.expected_response_code = MHD_HTTP_FORBIDDEN,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }" },
/* Try to double-spend the 5 EUR coin at the same merchant (but different
proposal) */
@ -3286,7 +3299,7 @@ run (void *cls)
.expected_response_code = MHD_HTTP_FORBIDDEN,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\":[{ \"name\":\"ice cream\", \"value\":2 } ] }" },
/* ***************** /refresh testing ******************** */
@ -3316,7 +3329,7 @@ run (void *cls)
.expected_response_code = MHD_HTTP_OK,
.details.deposit.amount = "EUR:1",
.details.deposit.coin_ref = "refresh-withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:1\" } ] }" },
/* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
@ -3357,7 +3370,7 @@ run (void *cls)
.details.deposit.amount = "EUR:1",
.details.deposit.coin_ref = "refresh-reveal-1-idempotency",
.details.deposit.coin_idx = 0,
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }" },
/* Test successfully spending coins from the refresh operation:
@ -3368,7 +3381,7 @@ run (void *cls)
.details.deposit.amount = "EUR:0.1",
.details.deposit.coin_ref = "refresh-reveal-1",
.details.deposit.coin_idx = 4,
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":43 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/43\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }" },
/* Test running a failing melt operation (same operation again must fail) */
@ -3507,7 +3520,7 @@ run (void *cls)
.expected_response_code = MHD_HTTP_OK,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-r1",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:5\" } ] }",
.details.deposit.refund_deadline = { 60LL * 1000 * 1000 } /* 60 s */,
},
@ -3539,7 +3552,7 @@ run (void *cls)
.expected_response_code = MHD_HTTP_OK,
.details.deposit.amount = "EUR:4.99",
.details.deposit.coin_ref = "withdraw-coin-r1",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"more ice cream\", \"value\":\"EUR:5\" } ] }",
},
/* Run transfers. This will do the transfer as refund deadline was 0 */
@ -3591,9 +3604,9 @@ run (void *cls)
.expected_response_code = MHD_HTTP_OK,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-rb",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:5\" } ] }",
.details.deposit.refund_deadline = { 0 },
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }"
},
{ .oc = OC_CHECK_BANK_TRANSFER,
.label = "check_bank_transfer-aai-3b",
@ -3695,7 +3708,7 @@ run (void *cls)
.expected_response_code = MHD_HTTP_OK,
.details.deposit.amount = "EUR:0.5",
.details.deposit.coin_ref = "payback-withdraw-coin-2a",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"more ice cream\", \"value\":1 } ] }" },
{ .oc = OC_REVOKE,
.label = "revoke-2",
@ -3716,7 +3729,7 @@ run (void *cls)
.expected_response_code = MHD_HTTP_NOT_FOUND,
.details.deposit.amount = "EUR:1",
.details.deposit.coin_ref = "payback-withdraw-coin-2b",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"more ice cream\", \"value\":1 } ] }" },
/* Test deposit fails after payback, with proof in payback */
@ -3727,7 +3740,7 @@ run (void *cls)
.expected_response_code = MHD_HTTP_NOT_FOUND,
.details.deposit.amount = "EUR:0.5",
.details.deposit.coin_ref = "payback-withdraw-coin-2a",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }",
.details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"extra ice cream\", \"value\":1 } ] }" },
@ -3999,6 +4012,7 @@ main (int argc,
}
while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o /dev/null -O /dev/null"));
fprintf (stderr, "\n");
result = GNUNET_NO;
sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO);
GNUNET_assert (NULL != sigpipe);

View File

@ -9,7 +9,6 @@ TALER_TEST_HOME = test_exchange_api_home/
CURRENCY = EUR
[exchange]
# HTTP port the exchange listens to
PORT = 8081
@ -23,55 +22,58 @@ DB = postgres
# exchange (or the twister) is actually listening.
BASE_URL = "http://localhost:8081/"
[exchangedb-postgres]
DB_CONN_STR = "postgres:///talercheck"
[auditordb-postgres]
DB_CONN_STR = "postgres:///talercheck"
[exchange-wire-sepa]
# Enable 'sepa' to test SEPA-specific routines.
ENABLE = YES
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
SEPA_RESPONSE_FILE = ${TALER_CONFIG_HOME}/sepa.json
# Fees for the forseeable future...
# If you see this after 2017, update to match the next 10 years...
WIRE-FEE-2017 = EUR:0.01
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
WIRE-FEE-2020 = EUR:0.01
WIRE-FEE-2021 = EUR:0.01
WIRE-FEE-2022 = EUR:0.01
WIRE-FEE-2023 = EUR:0.01
WIRE-FEE-2024 = EUR:0.01
WIRE-FEE-2025 = EUR:0.01
WIRE-FEE-2026 = EUR:0.01
CLOSING-FEE-2017 = EUR:0.01
CLOSING-FEE-2018 = EUR:0.01
CLOSING-FEE-2019 = EUR:0.01
CLOSING-FEE-2020 = EUR:0.01
CLOSING-FEE-2021 = EUR:0.01
CLOSING-FEE-2022 = EUR:0.01
CLOSING-FEE-2023 = EUR:0.01
CLOSING-FEE-2024 = EUR:0.01
CLOSING-FEE-2025 = EUR:0.01
CLOSING-FEE-2026 = EUR:0.01
[exchange_keys]
# Keep it short so the test runs fast.
LOOKAHEAD_SIGN = 12 h
[exchange-wire-test]
# Enable 'test' for testing of the actual coin operations.
ENABLE = YES
[exchangedb-postgres]
CONFIG = "postgres:///talercheck"
[auditordb-postgres]
CONFIG = "postgres:///talercheck"
# Sections starting with "account-" configure the bank accounts
# of the exchange. The "URL" specifies the account in
# payto://-format, while the WIRE_JSON specifies the
# (possibly offline) signed version to be returned in /wire.
# WIRE_JSON is optional, as not all accounts must be
# advertised in /wire.
[account-1]
# What is the URL of our account?
URL = "payto://sepa/CH9300762011623852957"
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json
# Which wire plugin should we used to access the account?
PLUGIN = ebics
# ENABLE_CREDIT = YES
[account-2]
# What is the bank account (with the "Taler Bank" demo system)?
URL = "payto://x-taler-bank/localhost:8082/2"
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-2.json
# Which wire plugin should we used to access the account?
PLUGIN = taler_bank
# Authentication information for basic authentication
TALER_BANK_AUTH_METHOD = "basic"
USERNAME = user
PASSWORD = pass
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
# Sections starting with "fee-" configure the wire fee for the
# respective wire method.
[fees-sepa]
# Fees for the forseeable future...
# If you see this after 2017, update to match the next 10 years...
WIRE-FEE-2017 = EUR:0.01
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
WIRE-FEE-2020 = EUR:0.01
@ -81,8 +83,8 @@ WIRE-FEE-2023 = EUR:0.01
WIRE-FEE-2024 = EUR:0.01
WIRE-FEE-2025 = EUR:0.01
WIRE-FEE-2026 = EUR:0.01
WIRE-FEE-2027 = EUR:0.01
CLOSING-FEE-2017 = EUR:0.01
CLOSING-FEE-2018 = EUR:0.01
CLOSING-FEE-2019 = EUR:0.01
CLOSING-FEE-2020 = EUR:0.01
@ -92,17 +94,35 @@ CLOSING-FEE-2023 = EUR:0.01
CLOSING-FEE-2024 = EUR:0.01
CLOSING-FEE-2025 = EUR:0.01
CLOSING-FEE-2026 = EUR:0.01
CLOSING-FEE-2027 = EUR:0.01
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json
# What is the main website of the bank?
BANK_URL = "http://localhost:8082/"
# From which account at the 'bank' should outgoing wire transfers be made?
BANK_ACCOUNT_NUMBER = 2
[fees-x-taler-bank]
# Fees for the forseeable future...
# If you see this after 2017, update to match the next 10 years...
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
WIRE-FEE-2020 = EUR:0.01
WIRE-FEE-2021 = EUR:0.01
WIRE-FEE-2022 = EUR:0.01
WIRE-FEE-2023 = EUR:0.01
WIRE-FEE-2024 = EUR:0.01
WIRE-FEE-2025 = EUR:0.01
WIRE-FEE-2026 = EUR:0.01
WIRE-FEE-2027 = EUR:0.01
CLOSING-FEE-2018 = EUR:0.01
CLOSING-FEE-2019 = EUR:0.01
CLOSING-FEE-2020 = EUR:0.01
CLOSING-FEE-2021 = EUR:0.01
CLOSING-FEE-2022 = EUR:0.01
CLOSING-FEE-2023 = EUR:0.01
CLOSING-FEE-2024 = EUR:0.01
CLOSING-FEE-2025 = EUR:0.01
CLOSING-FEE-2026 = EUR:0.01
CLOSING-FEE-2027 = EUR:0.01
# Sections starting with "coin_" specify which denominations
# the exchange should support (and their respective fee structure)
[coin_eur_ct_1]
value = EUR:0.01
duration_overlap = 5 minutes

View File

@ -0,0 +1,5 @@
{
"url": "payto://sepa/CH9300762011623852957",
"salt": "N83T9J9202WCC8TQFDMJDWEGZNBEKA33C1ZM241VNYH88RZNTHPW509Y1M2YF7Y098R8VRESWQ05H03BK1SPAZCWE54KARDCKT5N8AG",
"master_sig": "D4V5GJ998YK7D6N0N56AD0J6MZNFEW6MRZT2CFPVQ5ME3NMQ59AA2007CXYESSFGRN70CNCFM06858QSSENCWTZM8VHEJ93YQ20ZJ1R"
}

View File

@ -0,0 +1,5 @@
{
"url": "payto://x-taler-bank/localhost:8082/2",
"salt": "TMXB995ZZVKA02AG4074X3C6XX0BFTHY8XK76EF4BSG5XVDF069FEBN4TCKW9GS7NKZH409GKAVHMQPA3T361MC6VM7J268V3GBH42R",
"master_sig": "CK7BGHKYVAT7DMVCN00DQ0761NCTJVESZT69049BCF3SKNJKVHXXEQ5X6FH2HFGHCJ18YA1MGHBD8RRG4W3G4KJWQJDY2CGPGTHDJ2G"
}

View File

@ -0,0 +1,5 @@
{
"url": "payto://x-taler-bank/http://localhost:8082/2",
"salt": "WGRD0W7YKD8ZAN960B0JBRARRY0K5FQ4920Q3DJBTYH4GY7W0XNAX1F04R5B1E0RWH1NFG08TM8K1517WNCXTJM9KMH4913Q5XPK0N8",
"master_sig": "J4N0KP64MGNEQX9HST9TDWK67152MSHHM9CTZH8GSMKD607BXSAF209AQYDKYT6QJP0NQXYXC1JMM9Z405DJHGV75JFMWP4G6WB6A00"
}

View File

@ -23,52 +23,53 @@ DB = postgres
# exchange (or the twister) is actually listening.
BASE_URL = "http://localhost:8081/"
[exchangedb-postgres]
DB_CONN_STR = "postgres:///talercheck"
[auditordb-postgres]
DB_CONN_STR = "postgres:///talercheck"
[exchange-wire-sepa]
# Enable 'sepa' to test SEPA-specific routines.
ENABLE = YES
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
SEPA_RESPONSE_FILE = ${TALER_CONFIG_HOME}/sepa.json
# Fees for the forseeable future...
# If you see this after 2017, update to match the next 10 years...
WIRE-FEE-2017 = EUR:0.01
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
WIRE-FEE-2020 = EUR:0.01
WIRE-FEE-2021 = EUR:0.01
WIRE-FEE-2022 = EUR:0.01
WIRE-FEE-2023 = EUR:0.01
WIRE-FEE-2024 = EUR:0.01
WIRE-FEE-2025 = EUR:0.01
WIRE-FEE-2026 = EUR:0.01
CLOSING-FEE-2017 = EUR:0.01
CLOSING-FEE-2018 = EUR:0.01
CLOSING-FEE-2019 = EUR:0.01
CLOSING-FEE-2020 = EUR:0.01
CLOSING-FEE-2021 = EUR:0.01
CLOSING-FEE-2022 = EUR:0.01
CLOSING-FEE-2023 = EUR:0.01
CLOSING-FEE-2024 = EUR:0.01
CLOSING-FEE-2025 = EUR:0.01
CLOSING-FEE-2026 = EUR:0.01
[exchange_keys]
# Keep it short so we can prolong later!
LOOKAHEAD_SIGN = 60 s
[exchange-wire-test]
# Enable 'test' for testing of the actual coin operations.
ENABLE = YES
[exchangedb-postgres]
CONFIG = "postgres:///talercheck"
[auditordb-postgres]
CONFIG = "postgres:///talercheck"
[account-1]
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
WIRE_RESPONSE = ${TALER_CONFIG_HOME}/iban.json
# What is the URL of our bank account? Must match WIRE_RESPONSE above!
URL = payto://sepa/FIXME
# Which plugin implements access for this account?
PLUGIN = "ebics"
[account-2]
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
WIRE_RESPONSE = ${TALER_CONFIG_HOME}/x-taler-bank.json
# What is the URL of our bank account? Must match WIRE_RESPONSE above!
URL = payto://x-taler-bank/http://localhost:8082/2
# Which plugin implements access for this account?
PLUGIN = "taler_bank"
# Authentication information for basic authentication
TALER_BANK_AUTH_METHOD = "basic"
USERNAME = user
PASSWORD = pass
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
[fees-x-taler-bank]
# Fees for the forseeable future...
# If you see this after 2017, update to match the next 10 years...
WIRE-FEE-2017 = EUR:0.01
@ -93,15 +94,30 @@ CLOSING-FEE-2024 = EUR:0.01
CLOSING-FEE-2025 = EUR:0.01
CLOSING-FEE-2026 = EUR:0.01
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json
# What is the main website of the bank?
BANK_URL = "http://localhost:8082/"
# From which account at the 'bank' should outgoing wire transfers be made?
BANK_ACCOUNT_NUMBER = 2
[fees-sepa]
# Fees for the forseeable future...
# If you see this after 2017, update to match the next 10 years...
WIRE-FEE-2017 = EUR:0.01
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
WIRE-FEE-2020 = EUR:0.01
WIRE-FEE-2021 = EUR:0.01
WIRE-FEE-2022 = EUR:0.01
WIRE-FEE-2023 = EUR:0.01
WIRE-FEE-2024 = EUR:0.01
WIRE-FEE-2025 = EUR:0.01
WIRE-FEE-2026 = EUR:0.01
CLOSING-FEE-2017 = EUR:0.01
CLOSING-FEE-2018 = EUR:0.01
CLOSING-FEE-2019 = EUR:0.01
CLOSING-FEE-2020 = EUR:0.01
CLOSING-FEE-2021 = EUR:0.01
CLOSING-FEE-2022 = EUR:0.01
CLOSING-FEE-2023 = EUR:0.01
CLOSING-FEE-2024 = EUR:0.01
CLOSING-FEE-2025 = EUR:0.01
CLOSING-FEE-2026 = EUR:0.01
[coin_eur_ct_1]
value = EUR:0.01

View File

@ -1,5 +1,5 @@
@INLINE@ test_exchange_api_keys_cherry_picking.conf
[exchange_keys]
[exchange]
# Lengthen over original value (60 s)
LOOKAHEAD_SIGN = 100 s

View File

@ -42,6 +42,14 @@
*/
#define CONFIG_FILE "test_exchange_api.conf"
/**
* Is the configuration file is set to include wire format 'ebics'?
* Requires that EBICS /history function is implemented, which it
* is currently not. Once it is, set ENABLE_CREDIT to YES in the
* configuration and then set this option to 1.
*/
#define WIRE_EBICS 0
/**
* URL of the fakebank. Obtained from CONFIG_FILE's
* "exchange-wire-test:BANK_URI" option.
@ -145,23 +153,23 @@ run (void *cls,
CMD_EXEC_WIREWATCH ("wirewatch-1"),
/**
* Check if 'test' wire method is offered by the exchange.
* Check if 'x-taler-bank' wire method is offered by the exchange.
*/
TALER_TESTING_cmd_wire ("wire-test-1",
TALER_TESTING_cmd_wire ("wire-taler-bank-1",
is->exchange,
"test",
"x-taler-bank",
NULL,
MHD_HTTP_OK),
#if WIRE_EBICS
/**
* Check if 'sepa' wire method is offered by the exchange.
* Check if 'ebics' wire method is offered by the exchange.
*/
TALER_TESTING_cmd_wire ("wire-sepa-1",
is->exchange,
"sepa",
"ebics",
NULL,
MHD_HTTP_OK),
#endif
/****** End of "wire" testing ******/
/****** Start of withdraw and spend testing ******/
@ -188,8 +196,7 @@ run (void *cls,
*/
TALER_TESTING_cmd_deposit
("deposit-simple", is->exchange, "withdraw-coin-1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
TALER_TESTING_make_wire_details (42,
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK),
@ -208,8 +215,7 @@ run (void *cls,
*/
TALER_TESTING_cmd_deposit
("deposit-double-1", is->exchange, "withdraw-coin-1", 0,
TALER_TESTING_make_wire_details
("{\"type\":\"test\",\"account_number\":43}",
TALER_TESTING_make_wire_details (43,
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
@ -225,8 +231,7 @@ run (void *cls,
*/
TALER_TESTING_cmd_deposit
("deposit-double-1", is->exchange, "withdraw-coin-1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\", \"account_number\":43}",
TALER_TESTING_make_wire_details (43,
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
@ -236,8 +241,7 @@ run (void *cls,
*/
TALER_TESTING_cmd_deposit
("deposit-double-2", is->exchange, "withdraw-coin-1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\", \"account_number\":43}",
TALER_TESTING_make_wire_details (43,
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\"value\":2}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
@ -277,8 +281,7 @@ run (void *cls,
TALER_TESTING_cmd_deposit
("refresh-deposit-partial", is->exchange,
"refresh-withdraw-coin-1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
TALER_TESTING_make_wire_details (42,
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\
\"value\":\"EUR:1\"}]}",
@ -318,8 +321,7 @@ run (void *cls,
TALER_TESTING_cmd_deposit
("refresh-deposit-refreshed-1a", is->exchange,
"refresh-reveal-1-idempotency", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
TALER_TESTING_make_wire_details (42,
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\
\"value\":3}]}",
@ -331,8 +333,7 @@ run (void *cls,
TALER_TESTING_cmd_deposit
("refresh-deposit-refreshed-1b", is->exchange,
"refresh-reveal-1", 4,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":43}",
TALER_TESTING_make_wire_details (43,
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\",\
\"value\":3}]}",
@ -463,8 +464,7 @@ run (void *cls,
*/
TALER_TESTING_cmd_deposit
("deposit-refund-1", is->exchange, "withdraw-coin-r1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\", \"account_number\":42}",
TALER_TESTING_make_wire_details (42,
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\","
"\"value\":\"EUR:5\"}]}",
@ -502,8 +502,7 @@ run (void *cls,
* 1 ct deposit fee) */
TALER_TESTING_cmd_deposit
("deposit-refund-2", is->exchange, "withdraw-coin-r1", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\", \"account_number\":42}",
TALER_TESTING_make_wire_details (42,
fakebank_url),
"{\"items\":[{\"name\":\"more ice cream\","
"\"value\":\"EUR:5\"}]}",
@ -555,8 +554,7 @@ run (void *cls,
TALER_TESTING_cmd_deposit
("deposit-refund-1b", is->exchange, "withdraw-coin-rb", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\", \"account_number\":42}",
TALER_TESTING_make_wire_details (42,
fakebank_url),
"{\"items\":[{\"name\":\"ice cream\","
"\"value\":\"EUR:5\"}]}",
@ -648,8 +646,7 @@ run (void *cls,
TALER_TESTING_cmd_deposit
("payback-deposit-partial", is->exchange,
"payback-withdraw-coin-2a", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
TALER_TESTING_make_wire_details (42,
fakebank_url),
"{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_OK),
@ -670,8 +667,7 @@ run (void *cls,
TALER_TESTING_cmd_deposit
("payback-deposit-revoked", is->exchange,
"payback-withdraw-coin-2b", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
TALER_TESTING_make_wire_details (42,
fakebank_url),
"{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_NOT_FOUND),
@ -685,8 +681,7 @@ run (void *cls,
TALER_TESTING_cmd_deposit
("payback-deposit-partial-after-payback", is->exchange,
"payback-withdraw-coin-2a", 0,
TALER_TESTING_make_wire_details
("{ \"type\":\"test\",\"account_number\":42}",
TALER_TESTING_make_wire_details (42,
fakebank_url),
"{\"items\":[{\"name\":\"extra ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_NOT_FOUND),
@ -740,6 +735,7 @@ run (void *cls,
fakebank_url);
}
int
main (int argc,
char * const *argv)
@ -753,7 +749,8 @@ main (int argc,
if (NULL == (fakebank_url
/* Check fakebank port is available and config cares
* about bank url. */
= TALER_TESTING_prepare_fakebank (CONFIG_FILE)))
= TALER_TESTING_prepare_fakebank (CONFIG_FILE,
"account-2")))
return 77;
TALER_TESTING_cleanup_files (CONFIG_FILE);
/* @helpers. Run keyup, create tables, ... Note: it

View File

@ -50,9 +50,9 @@ struct DepositState
unsigned int coin_index;
/**
* JSON string describing the merchant's "wire details".
* payto://-URL of the merchant's bank account.
*/
char *wire_details;
json_t *wire_details;
/**
* JSON string describing what a proposal is about.
@ -126,6 +126,7 @@ deposit_cb (void *cls,
TALER_TESTING_interpreter_next (ds->is);
}
/**
* Run the command.
*
@ -133,7 +134,7 @@ deposit_cb (void *cls,
* @param cmd the command to execute, a /wire one.
* @param i the interpreter state.
*/
void
static void
deposit_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
@ -153,7 +154,6 @@ deposit_run (void *cls,
struct TALER_MerchantPublicKeyP merchant_pub;
struct GNUNET_HashCode h_contract_terms;
json_t *contract_terms;
json_t *wire;
struct TALER_Amount amount;
ds->is = is;
@ -213,21 +213,6 @@ deposit_run (void *cls,
TALER_JSON_hash (contract_terms,
&h_contract_terms));
json_decref (contract_terms);
wire = json_loads (ds->wire_details,
JSON_REJECT_DUPLICATES,
NULL);
if (NULL == wire)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse wire details `%s' at %u/%s\n",
ds->wire_details,
is->ip,
this_cmd->label);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
&coin_pub.eddsa_pub);
@ -267,8 +252,9 @@ deposit_run (void *cls,
dr.purpose.purpose = htonl
(TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
dr.h_contract_terms = h_contract_terms;
GNUNET_assert (GNUNET_OK == TALER_JSON_hash
(wire, &dr.h_wire));
GNUNET_assert (GNUNET_OK ==
TALER_JSON_wire_signature_hash (ds->wire_details,
&dr.h_wire));
dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
dr.refund_deadline = GNUNET_TIME_absolute_hton
(refund_deadline);
@ -286,7 +272,7 @@ deposit_run (void *cls,
(ds->exchange,
&amount,
wire_deadline,
wire,
ds->wire_details,
&h_contract_terms,
&coin_pub,
denom_pub_sig,
@ -301,21 +287,20 @@ deposit_run (void *cls,
if (NULL == ds->dh)
{
GNUNET_break (0);
json_decref (wire);
TALER_TESTING_interpreter_fail (is);
return;
}
json_decref (wire);
return;
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
static void
deposit_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
@ -331,10 +316,11 @@ deposit_cleanup (void *cls,
ds->dh = NULL;
}
GNUNET_free (ds->wire_details);
json_decref (ds->wire_details);
GNUNET_free (ds);
}
/**
* Extract information from a command that is useful for other
* commands.
@ -402,8 +388,8 @@ deposit_traits (void *cls,
* coins, this parameter selects which one in that array.
* This value is currently ignored, as only one-coin
* withdrawals are implemented.
* @param wire_details bank details of the merchant performing the
* deposit
* @param wire_details JSON details of the wire account of the merchant performing the
* deposit, reference is captured by this command
* @param contract_terms contract terms to be signed over by the
* coin
* @param refund_deadline refund deadline, zero means 'no refunds'
@ -419,7 +405,7 @@ TALER_TESTING_cmd_deposit
struct TALER_EXCHANGE_Handle *exchange,
const char *coin_reference,
unsigned int coin_index,
char *wire_details,
json_t *wire_details,
const char *contract_terms,
struct GNUNET_TIME_Relative refund_deadline,
const char *amount,

View File

@ -68,7 +68,6 @@ wirewatch_run (void *cls,
"taler-exchange-wirewatch",
"taler-exchange-wirewatch",
"-c", ws->config_filename,
"-t", "test", /* use Taler's bank/fakebank */
"-T", /* exit when done */
NULL);
if (NULL == ws->wirewatch_proc)

View File

@ -201,6 +201,7 @@ payback_cb (void *cls,
TALER_TESTING_interpreter_next (is);
}
/**
* Run the command.
*
@ -208,7 +209,7 @@ payback_cb (void *cls,
* @param cmd the command to execute, a /wire one.
* @param is the interpreter state.
*/
void
static void
payback_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
@ -280,13 +281,14 @@ payback_run (void *cls,
GNUNET_assert (NULL != ps->ph);
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
static void
revoke_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
@ -302,17 +304,18 @@ revoke_cleanup (void *cls,
rs->revoke_proc = NULL;
}
GNUNET_free (rs->dhks);
GNUNET_free_non_null (rs->dhks);
GNUNET_free (rs);
}
/**
* Cleanup the state.
*
* @param cls closure, typically a #struct WireState.
* @param cmd the command which is being cleaned up.
*/
void
static void
payback_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
@ -325,6 +328,7 @@ payback_cleanup (void *cls,
GNUNET_free (ps);
}
/**
* Extract information from a command that is useful for other
* commands.
@ -366,7 +370,7 @@ revoke_traits (void *cls,
* @param cmd the command to execute, a /wire one.
* @param is the interpreter state.
*/
void
static void
revoke_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
@ -455,6 +459,7 @@ TALER_TESTING_cmd_payback (const char *label,
return cmd;
}
/**
* Make a /revoke command.
*

View File

@ -201,8 +201,9 @@ refund_run (void *cls,
/* Very unlikely to fail */
GNUNET_assert (NULL != j_contract_terms);
GNUNET_assert (GNUNET_OK == TALER_JSON_hash
(j_contract_terms, &h_contract_terms));
GNUNET_assert (GNUNET_OK ==
TALER_JSON_hash (j_contract_terms,
&h_contract_terms));
json_decref (j_contract_terms);

View File

@ -248,9 +248,8 @@ track_transaction_run (void *cls,
const struct TALER_TESTING_Command *transaction_cmd;
struct TALER_CoinSpendPrivateKeyP *coin_priv;
struct TALER_CoinSpendPublicKeyP coin_pub;
const char *wire_details;
const char *contract_terms;
json_t *j_wire_details;
const json_t *wire_details;
json_t *j_contract_terms;
struct GNUNET_HashCode h_wire_details;
struct GNUNET_HashCode h_contract_terms;
@ -296,12 +295,10 @@ track_transaction_run (void *cls,
}
/* Parse them.. */
j_wire_details = json_loads
(wire_details, JSON_REJECT_DUPLICATES, NULL);
j_contract_terms = json_loads
(contract_terms, JSON_REJECT_DUPLICATES, NULL);
if ((NULL == j_wire_details) || (NULL == j_contract_terms))
if ((NULL == wire_details) || (NULL == j_contract_terms))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (tts->is);
@ -310,9 +307,11 @@ track_transaction_run (void *cls,
/* Should not fail here, json has been parsed already */
GNUNET_assert
( (GNUNET_OK == TALER_JSON_hash (j_wire_details,
( (GNUNET_OK ==
TALER_JSON_wire_signature_hash (wire_details,
&h_wire_details)) &&
(GNUNET_OK == TALER_JSON_hash (j_contract_terms,
(GNUNET_OK ==
TALER_JSON_hash (j_contract_terms,
&h_contract_terms)) );
if (GNUNET_OK != TALER_TESTING_get_trait_peer_key
@ -594,8 +593,7 @@ track_transfer_cb
if (NULL != tts->wire_details_reference)
{
const struct TALER_TESTING_Command *wire_details_cmd;
const char *wire_details;
json_t *j_wire_details;
const json_t *wire_details;
struct GNUNET_HashCode h_wire_details;
if (NULL == (wire_details_cmd
@ -615,13 +613,9 @@ track_transfer_cb
return;
}
j_wire_details = json_loads
(wire_details, JSON_REJECT_DUPLICATES, NULL);
GNUNET_assert (NULL != j_wire_details);
GNUNET_assert (GNUNET_OK == TALER_JSON_hash
(j_wire_details, &h_wire_details));
GNUNET_assert (GNUNET_OK ==
TALER_JSON_wire_signature_hash (wire_details,
&h_wire_details));
if (0 != memcmp (&h_wire_details,
h_wire,

View File

@ -27,6 +27,7 @@
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_wire_lib.h"
#include "taler_testing_lib.h"
struct WireState
@ -70,19 +71,6 @@ struct WireState
};
/**
* Check all the expected values have been returned by /wire.
*
* @param cls closure
* @param wire_method name of the wire method (i.e. "sepa")
* @param fees fee structure for this method
*/
static void
check_method_and_fee_cb
(void *cls,
const char *wire_method,
const struct TALER_EXCHANGE_WireAggregateFees *fees);
/**
* Callbacks called with the result(s) of a wire format inquiry
* request to the exchange.
@ -92,23 +80,20 @@ check_method_and_fee_cb
* for successful request; 0 if the exchange's
* reply is bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param obj the received JSON reply, if successful this should
* be the wire format details as provided by /wire.
* @param accounts_len length of the @a accounts array
* @param accounts list of wire accounts of the exchange, NULL on error
*/
static void
wire_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const json_t *obj)
unsigned int accounts_len,
const struct TALER_EXCHANGE_WireAccount *accounts)
{
struct WireState *ws = cls;
struct TALER_TESTING_Command *cmd
= &ws->is->commands[ws->is->ip];
struct TALER_TESTING_Command *cmd = &ws->is->commands[ws->is->ip];
struct TALER_Amount expected_fee;
/**
* The handle has been free'd by GNUnet curl-lib. FIXME:
* shouldn't GNUnet nullify it once it frees it?
*/
ws->wh = NULL;
if (ws->expected_response_code != http_status)
{
@ -117,22 +102,42 @@ wire_cb (void *cls,
return;
}
if (GNUNET_OK != TALER_EXCHANGE_wire_get_fees
(&TALER_EXCHANGE_get_keys (ws->exchange)->master_pub,
obj,
// will check synchronously.
&check_method_and_fee_cb,
ws))
if (MHD_HTTP_OK == http_status)
{
for (unsigned int i=0;i<accounts_len;i++)
{
char *method;
method = TALER_WIRE_payto_get_method (accounts[i].url);
if (0 == strcmp (ws->expected_method,
method))
{
ws->method_found = GNUNET_OK;
if (NULL != ws->expected_fee)
{
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (ws->expected_fee,
&expected_fee));
for (const struct TALER_EXCHANGE_WireAggregateFees *waf = accounts[i].fees;
NULL != waf;
waf = waf->next)
{
if (0 != TALER_amount_cmp (&waf->wire_fee,
&expected_fee))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire fee extraction in command %s failed\n",
"Wire fee missmatch to command %s\n",
cmd->label);
json_dumpf (obj, stderr, 0);
TALER_TESTING_interpreter_fail (ws->is);
GNUNET_free (method);
return;
}
if (ws->method_found != GNUNET_OK)
}
}
}
GNUNET_free (method);
}
if (GNUNET_OK != ws->method_found)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"/wire does not offer method '%s'\n",
@ -140,50 +145,10 @@ wire_cb (void *cls,
TALER_TESTING_interpreter_fail (ws->is);
return;
}
}
TALER_TESTING_interpreter_next (ws->is);
}
/**
* Check all the expected values have been returned by /wire.
*
* @param cls closure
* @param wire_method name of the wire method (i.e. "sepa")
* @param fees fee structure for this method
*/
static void
check_method_and_fee_cb
(void *cls,
const char *wire_method,
const struct TALER_EXCHANGE_WireAggregateFees *fees)
{
struct WireState *ws = cls;
struct TALER_TESTING_Command *cmd
= &ws->is->commands[ws->is->ip]; // ugly?
struct TALER_Amount expected_fee;
if (0 == strcmp (ws->expected_method, wire_method))
ws->method_found = GNUNET_OK;
if ( ws->expected_fee && (ws->method_found == GNUNET_OK) )
{
GNUNET_assert (GNUNET_OK == TALER_string_to_amount
(ws->expected_fee,
&expected_fee));
while (NULL != fees)
{
if (0 != TALER_amount_cmp (&fees->wire_fee,
&expected_fee))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire fee missmatch to command %s\n",
cmd->label);
TALER_TESTING_interpreter_fail (ws->is);
return;
}
fees = fees->next;
}
}
}
/**
* Run the command.

View File

@ -416,42 +416,54 @@ TALER_TESTING_url_port_free (const char *url)
return GNUNET_OK;
}
/**
* Allocate and return a piece of wire-details. Mostly, it adds
* the bank_url to the JSON.
* Allocate and return a piece of wire-details. Combines
* the @a account_no and the @a bank_url to a
* @a payto://-URL and adds some salt to create the JSON.
*
* @param template the wire-details template.
* @param account_no account number
* @param bank_url the bank_url
*
* @return the filled out and stringified wire-details. To
* be manually free'd.
* @return JSON describing the account, including the
* payto://-URL of the account, must be manually decref'd
*/
char *
TALER_TESTING_make_wire_details (const char *template,
json_t *
TALER_TESTING_make_wire_details (unsigned long long account_no,
const char *bank_url)
{
json_t *jtemplate;
char *payto;
json_t *ret;
GNUNET_assert (NULL != (jtemplate = json_loads
(template, JSON_REJECT_DUPLICATES, NULL)));
GNUNET_assert (0 == json_object_set
(jtemplate, "bank_url", json_string (bank_url)));
return json_dumps (jtemplate, JSON_COMPACT);
GNUNET_asprintf (&payto,
"payto://x-taler-bank/%s/%llu",
bank_url,
account_no);
ret = json_pack ("{s:s, s:s}",
"url", payto,
"salt", "test-salt (must be constant for aggregation tests)");
GNUNET_free (payto);
return ret;
}
/**
* Prepare launching a fakebank. Check that the configuration
* file has the right option, and that the port is available.
* If everything is OK, return the configured URL of the fakebank.
*
* @param config_filename configuration file to use
* @param config_section which account to use (must match x-taler-bank)
* @return NULL on error, fakebank URL otherwise
*/
char *
TALER_TESTING_prepare_fakebank (const char *config_filename)
TALER_TESTING_prepare_fakebank (const char *config_filename,
const char *config_section)
{
struct GNUNET_CONFIGURATION_Handle *cfg;
char *payto_url;
char *fakebank_url;
const char *start;
const char *end;
cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg,
@ -459,17 +471,37 @@ TALER_TESTING_prepare_fakebank (const char *config_filename)
return NULL;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"exchange-wire-test",
"BANK_URL",
&fakebank_url))
config_section,
"URL",
&payto_url))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
"exchange-wire-test",
"BANK_URL");
config_section,
"URL");
GNUNET_CONFIGURATION_destroy (cfg);
return NULL;
}
GNUNET_CONFIGURATION_destroy (cfg);
if (0 != strncasecmp (payto_url,
"payto://x-taler-bank/",
strlen ("payto://x-taler-bank/")))
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
config_section,
"URL",
"expected `x-taler-bank' payto://-URL");
GNUNET_CONFIGURATION_destroy (cfg);
GNUNET_free (payto_url);
return NULL;
}
start = &payto_url [strlen ("payto://x-taler-bank/")];
end = strchr (start,
(unsigned char) '/');
if (NULL == end)
end = &start[strlen (start)];
fakebank_url = GNUNET_strndup (start,
end - start);
GNUNET_free (payto_url);
if (GNUNET_OK !=
TALER_TESTING_url_port_free (fakebank_url))
{

View File

@ -0,0 +1,76 @@
/*
This file is part of TALER
Copyright (C) 2018 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 exchange-lib/testing_api_trait_json.c
* @brief offers JSON traits.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_WIRE_DETAILS "wire-details"
/**
* Obtain wire details from @a cmd.
*
* @param cmd command to extract trait from
* @param index always (?) zero, as one command sticks
* to one bank account
* @param wire_details[out] where to write the wire details.
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_wire_details
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
const json_t **wire_details)
{
return cmd->traits (cmd->cls,
(void **) wire_details,
TALER_TESTING_TRAIT_WIRE_DETAILS,
index);
}
/**
* Offer wire details in a trait.
*
* @param index always (?) zero, as one command sticks
* to one bank account
* @param wire_details wire details to offer
* @return the trait, to be put in the traits array of the command
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_wire_details
(unsigned int index,
const json_t *wire_details)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_WIRE_DETAILS,
.ptr = (const json_t *) wire_details
};
return ret;
}
/* end of testing_api_trait_json.c */

View File

@ -30,7 +30,6 @@
#include "taler_signatures.h"
#include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_WIRE_DETAILS "wire-details"
#define TALER_TESTING_TRAIT_CONTRACT_TERMS "contract-terms"
#define TALER_TESTING_TRAIT_TRANSFER_SUBJECT "transfer-subject"
#define TALER_TESTING_TRAIT_AMOUNT "amount"
@ -80,49 +79,6 @@ TALER_TESTING_make_trait_contract_terms
}
/**
* Obtain wire details from @a cmd.
*
* @param cmd command to extract trait from
* @param index always (?) zero, as one command sticks
* to one bank account
* @param wire_details[out] where to write the wire details.
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_wire_details
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
const char **wire_details)
{
return cmd->traits (cmd->cls,
(void **) wire_details,
TALER_TESTING_TRAIT_WIRE_DETAILS,
index);
}
/**
* Offer wire details in a trait.
*
* @param index always (?) zero, as one command sticks
* to one bank account
* @param wire_details wire details to offer
* @return the trait, to be put in the traits array of the command
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_wire_details
(unsigned int index,
const char *wire_details)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_WIRE_DETAILS,
.ptr = (const void *) wire_details
};
return ret;
}
/**
* Obtain a transfer subject from @a cmd.
*

View File

@ -4,7 +4,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/include
pkgcfgdir = $(prefix)/share/taler/config.d/
pkgcfg_DATA = \
exchange-signkeys.conf \
coins.conf
if USE_COVERAGE
@ -34,6 +33,8 @@ taler_exchange_wire_SOURCES = \
taler-exchange-wire.c
taler_exchange_wire_LDADD = \
$(LIBGCRYPT_LIBS) \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \
$(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetjson \

View File

@ -1,16 +0,0 @@
# General data for signing keys.
[exchange_keys]
# how long is one signkey valid?
signkey_duration = 4 weeks
# how long are the signatures with the signkey valid?
legal_duration = 2 years
# how long do we generate denomination and signing keys
# ahead of time?
lookahead_sign = 32 weeks 1 day
# how long do we provide to clients denomination and signing keys
# ahead of time?
lookahead_provide = 4 weeks 1 day

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014-2017 GNUnet e.V.
Copyright (C) 2014-2018 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
@ -499,23 +499,23 @@ exchange_keys_update_signkeys ()
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (kcfg,
"exchange_keys",
"exchange",
"signkey_duration",
&signkey_duration))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange_keys",
"exchange",
"signkey_duration");
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (kcfg,
"exchange_keys",
"exchange",
"legal_duration",
&legal_duration))
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
"exchange_keys",
"exchange",
"legal_duration",
"fails to specify valid timeframe");
return GNUNET_SYSERR;
@ -523,7 +523,7 @@ exchange_keys_update_signkeys ()
if (signkey_duration.rel_value_us > legal_duration.rel_value_us)
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
"exchange_keys",
"exchange",
"legal_duration",
"must be longer than signkey_duration");
return GNUNET_SYSERR;
@ -926,10 +926,13 @@ create_wire_fee_for_method (void *cls,
if (GNUNET_OK != *ret)
return;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Setting up wire fees for `%s'\n",
wiremethod);
last_date = GNUNET_TIME_absolute_add (lookahead_sign_stamp,
max_duration_spend);
GNUNET_asprintf (&section,
"exchange-wire-%s",
"fees-%s",
wiremethod);
GNUNET_asprintf (&fn,
"%s%s.fee",
@ -1045,6 +1048,43 @@ create_wire_fee_for_method (void *cls,
}
/**
* Output the wire fee structure. Must be run after #max_duration_spend
* was initialized.
*
* @param cls pointer to `int`, set to #GNUNET_SYSERR on error
* @param ai information about enabled accounts
*/
static void
create_wire_fee_by_account (void *cls,
const struct TALER_EXCHANGEDB_AccountInfo *ai)
{
int *ret = cls;
struct TALER_WIRE_Plugin *plugin;
if (GNUNET_NO == ai->credit_enabled)
return;
plugin = TALER_WIRE_plugin_load (kcfg,
ai->plugin_name);
if (NULL == plugin)
{
fprintf (stderr,
"Failed to load wire plugin `%s' configured for account `%s'\n",
ai->plugin_name,
ai->section_name);
*ret = GNUNET_SYSERR;
return;
}
/* We may call this function repeatedly for the same method
if there are multiple accounts with plugins using the
same method, but except for some minor performance loss,
this is harmless. */
create_wire_fee_for_method (ret,
plugin->method);
TALER_WIRE_plugin_unload (plugin);
}
/**
* Output the wire fee structure. Must be run after #max_duration_spend
* was initialized.
@ -1057,8 +1097,8 @@ create_wire_fees ()
int ret;
ret = GNUNET_OK;
TALER_WIRE_find_enabled (kcfg,
&create_wire_fee_for_method,
TALER_EXCHANGEDB_find_accounts (kcfg,
&create_wire_fee_by_account,
&ret);
return ret;
}
@ -1305,12 +1345,12 @@ run (void *cls,
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (kcfg,
"exchange_keys",
"exchange",
"lookahead_sign",
&lookahead_sign))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange_keys",
"exchange",
"lookahead_sign");
global_ret = 1;
return;
@ -1318,7 +1358,7 @@ run (void *cls,
if (0 == lookahead_sign.rel_value_us)
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
"exchange_keys",
"exchange",
"lookahead_sign",
_("must not be zero"));
global_ret = 1;

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2015, 2016, 2017 Inria
Copyright (C) 2015-2018 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
@ -24,6 +24,8 @@
#include "taler_crypto_lib.h"
#include "taler_util.h"
#include "taler_wire_lib.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_lib.h"
#include "taler_signatures.h"
@ -33,19 +35,9 @@
static char *masterkeyfile;
/**
* Account holder information in JSON format.
* Private key for signing.
*/
static json_t *account_holder;
/**
* Which wire method is this for?
*/
static char *method;
/**
* Where to write the result.
*/
static char *output_filename;
static struct TALER_MasterPrivateKeyP master_priv;
/**
* Return value from main().
@ -53,6 +45,56 @@ static char *output_filename;
static int global_ret;
/**
* Function called with information about a wire account. Signs
* the account's wire details and writes out the JSON file to disk.
*
* @param cls closure
* @param ai account information
*/
static void
sign_account_data (void *cls,
const struct TALER_EXCHANGEDB_AccountInfo *ai)
{
json_t *wire;
char *json_out;
FILE *out;
if (GNUNET_NO == ai->credit_enabled)
return;
if (NULL == ai->wire_response_filename)
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
ai->section_name,
"WIRE_RESPONSE");
global_ret = 1;
return;
}
wire = TALER_JSON_wire_signature_make (ai->payto_url,
&master_priv);
json_out = json_dumps (wire,
JSON_INDENT(2));
json_decref (wire);
GNUNET_assert (NULL != json_out);
out = fopen (ai->wire_response_filename,
"w+");
if (NULL == out)
{
GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
"fopen",
ai->wire_response_filename);
global_ret = 1;
return;
}
fprintf (out,
"%s",
json_out);
fclose (out);
free (json_out);
}
/**
* Main function that will be run.
*
@ -68,11 +110,6 @@ run (void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv;
struct TALER_MasterPrivateKeyP key;
struct TALER_MasterSignatureP sig;
char *json_out;
struct GNUNET_HashCode salt;
struct TALER_WIRE_Plugin *plugin;
if ( (NULL == masterkeyfile) &&
(GNUNET_OK !=
@ -86,7 +123,8 @@ run (void *cls,
global_ret = 1;
return;
}
if (GNUNET_YES != GNUNET_DISK_file_test (masterkeyfile))
if (GNUNET_YES !=
GNUNET_DISK_file_test (masterkeyfile))
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Exchange master private key `%s' does not exist yet, creating it!\n",
masterkeyfile);
@ -99,97 +137,11 @@ run (void *cls,
global_ret = 1;
return;
}
if (NULL == method)
{
json_t *test;
const char *m;
test = json_object_get(account_holder,
"type");
if ( (NULL == test) ||
(NULL == (m = json_string_value (test))))
{
fprintf (stderr,
"Required -t argument missing\n");
global_ret = 1;
return;
}
method = GNUNET_strdup (m);
}
else
{
json_object_set_new (account_holder,
"type",
json_string (method));
}
key.eddsa_priv = *eddsa_priv;
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&salt,
sizeof (salt));
plugin = TALER_WIRE_plugin_load (cfg,
method);
if (NULL == plugin)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Wire transfer method `%s' not supported\n",
method);
GNUNET_free (method);
global_ret = 1;
return;
}
GNUNET_free (method);
if (GNUNET_OK !=
plugin->sign_wire_details (plugin->cls,
account_holder,
&key,
&salt,
&sig))
{
/* sign function should have logged applicable errors */
json_decref (account_holder);
TALER_WIRE_plugin_unload (plugin);
global_ret = 1;
return;
}
TALER_WIRE_plugin_unload (plugin);
master_priv.eddsa_priv = *eddsa_priv;
TALER_EXCHANGEDB_find_accounts (cfg,
&sign_account_data,
NULL);
GNUNET_free (eddsa_priv);
/* add signature and salt to JSON message */
json_object_set_new (account_holder,
"salt",
GNUNET_JSON_from_data (&salt,
sizeof (salt)));
json_object_set_new (account_holder,
"sig",
GNUNET_JSON_from_data (&sig,
sizeof (sig)));
/* dump result to stdout */
json_out = json_dumps (account_holder,
JSON_INDENT(2));
json_decref (account_holder);
GNUNET_assert (NULL != json_out);
if (NULL != output_filename)
{
if (NULL != stdout)
fclose (stdout);
stdout = fopen (output_filename,
"w+");
if (NULL == stdout)
{
fprintf (stderr,
"Failed to open `%s': %s\n",
output_filename,
STRERROR (errno));
return;
}
}
fprintf (stdout,
"%s",
json_out);
fflush (stdout);
free (json_out);
}
@ -206,27 +158,11 @@ main (int argc,
char *const *argv)
{
const struct GNUNET_GETOPT_CommandLineOption options[] = {
GNUNET_GETOPT_option_mandatory
(GNUNET_JSON_getopt ('j',
"json",
"JSON",
"account information in JSON format",
&account_holder)),
GNUNET_GETOPT_option_filename ('m',
"master-key",
"FILENAME",
"master key file (private key)",
&masterkeyfile),
GNUNET_GETOPT_option_string ('t',
"type",
"METHOD",
"which wire transfer method (i.e. 'test' or 'sepa') is this for?",
&method),
GNUNET_GETOPT_option_filename ('o',
"output",
"FILENAME",
"where to write the result",
&output_filename),
GNUNET_GETOPT_OPTION_END
};

View File

@ -6,3 +6,4 @@ taler-exchange-reservemod
taler-exchange-httpd
taler-exchange-wirewatch
test_taler_exchange_wirewatch-postgres
test_taler_exchange_httpd_home/.config/taler/account-1.json

View File

@ -36,3 +36,18 @@ PORT = 8081
# Required for wire transfers as we need to include it in the wire
# transfers to enable tracking.
BASE_URL = http://localhost:8081/
# how long is one signkey valid?
SIGNKEY_DURATION = 4 weeks
# how long are the signatures with the signkey valid?
LEGAL_DURATION = 2 years
# how long do we generate denomination and signing keys
# ahead of time?
LOOKAHEAD_SIGN = 32 weeks 1 day
# how long do we provide to clients denomination and signing keys
# ahead of time?
LOOKAHEAD_PROVIDE = 4 weeks 1 day

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2016, 2017 GNUnet e.V.
Copyright (C) 2016-2018 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -30,35 +30,35 @@
/**
* Information we keep for each loaded wire plugin.
* Information we keep for each supported account.
*/
struct WirePlugin
struct WireAccount
{
/**
* Plugins are kept in a DLL.
* Accounts are kept in a DLL.
*/
struct WirePlugin *next;
struct WireAccount *next;
/**
* Plugins are kept in a DLL.
*/
struct WirePlugin *prev;
struct WireAccount *prev;
/**
* Handle to the plugin.
*/
struct TALER_WIRE_Plugin *wire_plugin;
/**
* Name of the plugin.
*/
char *type;
/**
* Wire transfer fee structure.
*/
struct TALER_EXCHANGEDB_AggregateFees *af;
/**
* Name of the section that configures this account.
*/
char *section_name;
};
@ -82,7 +82,7 @@ struct WirePrepareData
/**
* Wire plugin used for this preparation.
*/
struct WirePlugin *wp;
struct WireAccount *wa;
/**
* Row ID of the transfer.
@ -149,9 +149,9 @@ struct AggregationUnit
json_t *wire;
/**
* Wire plugin to be used for the preparation.
* Wire account to be used for the preparation.
*/
struct WirePlugin *wp;
struct WireAccount *wa;
/**
* Database session for all of our transactions.
@ -200,12 +200,12 @@ struct CloseTransferContext
/**
* Wire transfer method.
*/
char *type;
char *method;
/**
* Wire plugin used for closing the reserve.
* Wire account used for closing the reserve.
*/
struct WirePlugin *wp;
struct WireAccount *wa;
};
@ -238,12 +238,12 @@ static struct TALER_EXCHANGEDB_Plugin *db_plugin;
/**
* Head of list of loaded wire plugins.
*/
static struct WirePlugin *wp_head;
static struct WireAccount *wa_head;
/**
* Tail of list of loaded wire plugins.
*/
static struct WirePlugin *wp_tail;
static struct WireAccount *wa_tail;
/**
* Next task to run, if any.
@ -290,49 +290,22 @@ static int reserves_idle;
static unsigned int aggregation_limit = TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT;
/**
* Extract wire plugin type from @a wire address
*
* @param wire a wire address
* @return NULL if @a wire is ill-formed
*/
const char *
extract_type (const json_t *wire)
{
const char *type;
json_t *t;
t = json_object_get (wire, "type");
if (NULL == t)
{
GNUNET_break (0);
return NULL;
}
type = json_string_value (t);
if (NULL == type)
{
GNUNET_break (0);
return NULL;
}
return type;
}
/**
* Advance the "af" pointer in @a wp to point to the
* currently valid record.
*
* @param wp wire transfer fee data structure to update
* @param wa wire transfer fee data structure to update
* @param now timestamp to update fees to
*/
static void
advance_fees (struct WirePlugin *wp,
advance_fees (struct WireAccount *wa,
struct GNUNET_TIME_Absolute now)
{
struct TALER_EXCHANGEDB_AggregateFees *af;
/* First, try to see if we have current fee information in memory */
af = wp->af;
af = wa->af;
while ( (NULL != af) &&
(af->end_date.abs_value_us < now.abs_value_us) )
{
@ -341,7 +314,7 @@ advance_fees (struct WirePlugin *wp,
GNUNET_free (af);
af = n;
}
wp->af = af;
wa->af = af;
}
@ -354,28 +327,28 @@ advance_fees (struct WirePlugin *wp,
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
update_fees (struct WirePlugin *wp,
update_fees (struct WireAccount *wa,
struct GNUNET_TIME_Absolute now,
struct TALER_EXCHANGEDB_Session *session)
{
enum GNUNET_DB_QueryStatus qs;
advance_fees (wp,
advance_fees (wa,
now);
if (NULL != wp->af)
if (NULL != wa->af)
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
/* Let's try to load it from disk... */
wp->af = TALER_EXCHANGEDB_fees_read (cfg,
wp->type);
advance_fees (wp,
wa->af = TALER_EXCHANGEDB_fees_read (cfg,
wa->wire_plugin->method);
advance_fees (wa,
now);
for (struct TALER_EXCHANGEDB_AggregateFees *p = wp->af;
for (struct TALER_EXCHANGEDB_AggregateFees *p = wa->af;
NULL != p;
p = p->next)
{
qs = db_plugin->insert_wire_fee (db_plugin->cls,
session,
wp->type,
wa->wire_plugin->method,
p->start_date,
p->end_date,
&p->wire_fee,
@ -383,53 +356,94 @@ update_fees (struct WirePlugin *wp,
&p->master_sig);
if (qs < 0)
{
TALER_EXCHANGEDB_fees_free (wp->af);
wp->af = NULL;
TALER_EXCHANGEDB_fees_free (wa->af);
wa->af = NULL;
return qs;
}
}
if (NULL != wp->af)
if (NULL != wa->af)
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to find current wire transfer fees for `%s'\n",
wp->type);
wa->wire_plugin->method);
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
/**
* Find the wire plugin for the given wire address.
* Find the wire plugin for the given payto:// URL
*
* @param type wire plugin type we need a plugin for
* @param method wire method we need an account for
* @return NULL on error
*/
static struct WirePlugin *
find_plugin (const char *type)
static struct WireAccount *
find_account_by_method (const char *method)
{
struct WirePlugin *wp;
if (NULL == type)
for (struct WireAccount *wa = wa_head; NULL != wa; wa = wa->next)
if (0 == strcmp (method,
wa->wire_plugin->method))
return wa;
return NULL;
for (wp = wp_head; NULL != wp; wp = wp->next)
if (0 == strcmp (type,
wp->type))
return wp;
wp = GNUNET_new (struct WirePlugin);
wp->wire_plugin = TALER_WIRE_plugin_load (cfg,
type);
if (NULL == wp->wire_plugin)
}
/**
* Find the wire plugin for the given payto:// URL
*
* @param url wire address we need an account for
* @return NULL on error
*/
static struct WireAccount *
find_account_by_url (const char *url)
{
char *method;
struct WireAccount *wa;
method = TALER_WIRE_payto_get_method (url);
if (NULL == method)
{
fprintf (stderr,
"Invalid payto:// URL `%s'\n",
url);
return NULL;
}
wa = find_account_by_method (method);
GNUNET_free (method);
return wa;
}
/**
* Function called with information about a wire account. Adds the
* account to our list (if it is enabled and we can load the plugin).
*
* @param cls closure, NULL
* @param ai account information
*/
static void
add_account_cb (void *cls,
const struct TALER_EXCHANGEDB_AccountInfo *ai)
{
struct WireAccount *wa;
(void) cls;
if (GNUNET_YES != ai->debit_enabled)
return; /* not enabled for us, skip */
wa = GNUNET_new (struct WireAccount);
wa->wire_plugin = TALER_WIRE_plugin_load (cfg,
ai->plugin_name);
if (NULL == wa->wire_plugin)
{
fprintf (stderr,
"Failed to load wire plugin for `%s'\n",
type);
GNUNET_free (wp);
return NULL;
ai->plugin_name);
GNUNET_free (wa);
return;
}
wp->type = GNUNET_strdup (type);
GNUNET_CONTAINER_DLL_insert (wp_head,
wp_tail,
wp);
return wp;
wa->section_name = GNUNET_strdup (ai->section_name);
GNUNET_CONTAINER_DLL_insert (wa_head,
wa_tail,
wa);
}
@ -471,7 +485,7 @@ shutdown_task (void *cls)
{
if (NULL != wpd->eh)
{
wpd->wp->wire_plugin->execute_wire_transfer_cancel (wpd->wp->wire_plugin->cls,
wpd->wa->wire_plugin->execute_wire_transfer_cancel (wpd->wa->wire_plugin->cls,
wpd->eh);
wpd->eh = NULL;
}
@ -484,7 +498,7 @@ shutdown_task (void *cls)
{
if (NULL != au->ph)
{
au->wp->wire_plugin->prepare_wire_transfer_cancel (au->wp->wire_plugin->cls,
au->wa->wire_plugin->prepare_wire_transfer_cancel (au->wa->wire_plugin->cls,
au->ph);
au->ph = NULL;
}
@ -494,29 +508,29 @@ shutdown_task (void *cls)
}
if (NULL != ctc)
{
ctc->wp->wire_plugin->prepare_wire_transfer_cancel (ctc->wp->wire_plugin->cls,
ctc->wa->wire_plugin->prepare_wire_transfer_cancel (ctc->wa->wire_plugin->cls,
ctc->ph);
ctc->ph = NULL;
db_plugin->rollback (db_plugin->cls,
ctc->session);
GNUNET_free (ctc->type);
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
}
TALER_EXCHANGEDB_plugin_unload (db_plugin);
{
struct WirePlugin *wp;
struct WireAccount *wa;
while (NULL != (wp = wp_head))
while (NULL != (wa = wa_head))
{
GNUNET_CONTAINER_DLL_remove (wp_head,
wp_tail,
wp);
TALER_WIRE_plugin_unload (wp->wire_plugin);
TALER_EXCHANGEDB_fees_free (wp->af);
GNUNET_free (wp->type);
GNUNET_free (wp);
GNUNET_CONTAINER_DLL_remove (wa_head,
wa_tail,
wa);
TALER_WIRE_plugin_unload (wa->wire_plugin);
TALER_EXCHANGEDB_fees_free (wa->af);
GNUNET_free (wa->section_name);
GNUNET_free (wa);
}
}
GNUNET_CONFIGURATION_destroy (cfg);
@ -568,7 +582,16 @@ exchange_serve_process_config ()
TALER_EXCHANGEDB_plugin_unload (db_plugin);
return GNUNET_SYSERR;
}
TALER_EXCHANGEDB_find_accounts (cfg,
&add_account_cb,
NULL);
if (NULL == wa_head)
{
fprintf (stderr,
"No wire accounts configured for debit!\n");
TALER_EXCHANGEDB_plugin_unload (db_plugin);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
@ -677,16 +700,13 @@ deposit_cb (void *cls,
}
GNUNET_assert (NULL == au->wire);
au->wire = json_incref ((json_t *) wire);
if (GNUNET_OK !=
TALER_JSON_hash (au->wire,
&au->h_wire))
if (NULL == (au->wire = json_incref ((json_t *) wire)))
{
GNUNET_break (0);
json_decref (au->wire);
au->wire = NULL;
return GNUNET_DB_STATUS_HARD_ERROR;
}
TALER_JSON_wire_signature_hash (wire,
&au->h_wire);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&au->wtid,
sizeof (au->wtid));
@ -695,15 +715,20 @@ deposit_cb (void *cls,
TALER_B2S (&au->wtid),
TALER_amount2s (amount_with_fee),
(unsigned long long) row_id);
{
char *url;
au->wp = find_plugin (extract_type (au->wire));
if (NULL == au->wp)
url = TALER_JSON_wire_to_payto (au->wire);
au->wa = find_account_by_url (url);
GNUNET_free (url);
}
if (NULL == au->wa)
return GNUNET_DB_STATUS_HARD_ERROR;
/* make sure we have current fees */
au->execution_time = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&au->execution_time);
qs = update_fees (au->wp,
qs = update_fees (au->wa,
au->execution_time,
au->session);
if (qs <= 0)
@ -713,7 +738,7 @@ deposit_cb (void *cls,
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return qs;
}
au->wire_fee = au->wp->af->wire_fee;
au->wire_fee = au->wa->af->wire_fee;
qs = db_plugin->insert_aggregation_tracking (db_plugin->cls,
au->session,
@ -942,7 +967,7 @@ prepare_close_cb (void *cls,
db_plugin->rollback (db_plugin->cls,
ctc->session);
/* start again */
GNUNET_free (ctc->type);
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
@ -953,7 +978,7 @@ prepare_close_cb (void *cls,
/* Commit our intention to execute the wire transfer! */
qs = db_plugin->wire_prepare_data_insert (db_plugin->cls,
ctc->session,
ctc->type,
ctc->method,
buf,
buf_size);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
@ -963,7 +988,7 @@ prepare_close_cb (void *cls,
ctc->session);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
GNUNET_free (ctc->type);
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
return;
@ -975,7 +1000,7 @@ prepare_close_cb (void *cls,
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
GNUNET_free (ctc->type);
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
return;
@ -983,7 +1008,7 @@ prepare_close_cb (void *cls,
/* finally commit */
(void) commit_or_warn (ctc->session);
GNUNET_free (ctc->type);
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -1029,7 +1054,7 @@ static enum GNUNET_DB_QueryStatus
expired_reserve_cb (void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *left,
const json_t *account_details,
const char *account_details,
struct GNUNET_TIME_Absolute expiration_date)
{
struct ExpiredReserveContext *erc = cls;
@ -1040,24 +1065,15 @@ expired_reserve_cb (void *cls,
const struct TALER_Amount *closing_fee;
int ret;
enum GNUNET_DB_QueryStatus qs;
const char *type;
struct WirePlugin *wp;
struct WireAccount *wa;
GNUNET_assert (NULL == ctc);
now = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&now);
/* lookup wire plugin */
type = extract_type (account_details);
if (NULL == type)
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
return GNUNET_DB_STATUS_HARD_ERROR;
}
wp = find_plugin (type);
if (NULL == wp)
wa = find_account_by_url (account_details);
if (NULL == wa)
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
@ -1066,7 +1082,7 @@ expired_reserve_cb (void *cls,
}
/* lookup `closing_fee` */
qs = update_fees (wp,
qs = update_fees (wa,
now,
session);
if (qs <= 0)
@ -1079,7 +1095,7 @@ expired_reserve_cb (void *cls,
GNUNET_SCHEDULER_shutdown ();
return qs;
}
closing_fee = &wp->af->closing_fee;
closing_fee = &wa->af->closing_fee;
/* calculate transfer amount */
ret = TALER_amount_subtract (&amount_without_fee,
@ -1124,7 +1140,7 @@ expired_reserve_cb (void *cls,
{
/* success, perform wire transfer */
if (GNUNET_SYSERR ==
wp->wire_plugin->amount_round (wp->wire_plugin->cls,
wa->wire_plugin->amount_round (wa->wire_plugin->cls,
&amount_without_fee))
{
GNUNET_break (0);
@ -1133,11 +1149,12 @@ expired_reserve_cb (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR;
}
ctc = GNUNET_new (struct CloseTransferContext);
ctc->wp = wp;
ctc->wa = wa;
ctc->session = session;
ctc->type = GNUNET_strdup (type);
ctc->method = TALER_WIRE_payto_get_method (account_details);
ctc->ph
= wp->wire_plugin->prepare_wire_transfer (wp->wire_plugin->cls,
= wa->wire_plugin->prepare_wire_transfer (wa->wire_plugin->cls,
wa->section_name,
account_details,
&amount_without_fee,
exchange_base_url,
@ -1149,7 +1166,7 @@ expired_reserve_cb (void *cls,
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
GNUNET_free (ctc->type);
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
return GNUNET_DB_STATUS_HARD_ERROR;
@ -1398,7 +1415,7 @@ run_aggregation (void *cls)
&au->total_amount,
&au->wire_fee)) ||
(GNUNET_SYSERR ==
au->wp->wire_plugin->amount_round (au->wp->wire_plugin->cls,
au->wa->wire_plugin->amount_round (au->wa->wire_plugin->cls,
&au->final_amount)) ||
( (0 == au->final_amount.value) &&
(0 == au->final_amount.fraction) ) )
@ -1479,22 +1496,28 @@ run_aggregation (void *cls)
TALER_B2S (&au->merchant_pub));
GNUNET_free (amount_s);
}
au->ph = au->wp->wire_plugin->prepare_wire_transfer (au->wp->wire_plugin->cls,
au->wire,
{
char *url;
url = TALER_JSON_wire_to_payto (au->wire);
au->ph = au->wa->wire_plugin->prepare_wire_transfer (au->wa->wire_plugin->cls,
au->wa->section_name,
url,
&au->final_amount,
exchange_base_url,
&au->wtid,
&prepare_cb,
au);
GNUNET_free (url);
}
if (NULL == au->ph)
{
GNUNET_break (0); /* why? how to best recover? */
/* something went very wrong, likely bad configuration,
abort */
db_plugin->rollback (db_plugin->cls,
session);
cleanup_au ();
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
GNUNET_SCHEDULER_shutdown ();
return;
}
/* otherwise we continue with #prepare_cb(), see below */
@ -1536,7 +1559,7 @@ prepare_cb (void *cls,
/* Commit our intention to execute the wire transfer! */
qs = db_plugin->wire_prepare_data_insert (db_plugin->cls,
session,
au->wp->type,
au->wa->wire_plugin->method,
buf,
buf_size);
/* Commit the WTID data to 'wire_out' to finally satisfy aggregation
@ -1711,8 +1734,8 @@ wire_prepare_cb (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Starting wire transfer %llu\n",
(unsigned long long) rowid);
wpd->wp = find_plugin (wire_method);
if (NULL == wpd->wp)
wpd->wa = find_account_by_method (wire_method);
if (NULL == wpd->wa)
{
/* Should really never happen here, as when we get
here the plugin should be in the cache. */
@ -1725,7 +1748,7 @@ wire_prepare_cb (void *cls,
wpd = NULL;
return;
}
wpd->eh = wpd->wp->wire_plugin->execute_wire_transfer (wpd->wp->wire_plugin->cls,
wpd->eh = wpd->wa->wire_plugin->execute_wire_transfer (wpd->wa->wire_plugin->cls,
buf,
buf_size,
&wire_confirm_cb,

View File

@ -402,9 +402,12 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
&json);
if (GNUNET_SYSERR == res)
return MHD_NO;
if ( (GNUNET_NO == res) || (NULL == json) )
if ( (GNUNET_NO == res) ||
(NULL == json) )
return MHD_YES;
memset (&deposit, 0, sizeof (deposit));
memset (&deposit,
0,
sizeof (deposit));
res = TEH_PARSE_json_data (connection,
json,
spec);
@ -426,7 +429,6 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
if (TALER_EC_NONE !=
(ec = TEH_json_validate_wireformat (wire,
GNUNET_NO,
&emsg)))
{
GNUNET_JSON_parse_free (spec);
@ -446,7 +448,7 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
"timestamp");
}
if (GNUNET_OK !=
TALER_JSON_hash (wire,
TALER_JSON_wire_signature_hash (wire,
&my_h_wire))
{
TALER_LOG_WARNING ("Failed to parse JSON wire format specification for /deposit request\n");

View File

@ -148,7 +148,7 @@ TEH_RESPONSE_reply_json (struct MHD_Connection *connection,
JSON_INDENT(2));
if (NULL == json_str)
{
GNUNET_break (0);
GNUNET_assert (0);
return MHD_NO;
}
json_len = strlen (json_str);
@ -733,10 +733,10 @@ TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHisto
ret |= 1;
GNUNET_assert (0 ==
json_array_append_new (json_history,
json_pack ("{s:s, s:o, s:O, s:o, s:o}",
json_pack ("{s:s, s:o, s:s, s:o, s:o}",
"type", "DEPOSIT",
"timestamp", GNUNET_JSON_from_time_abs (pos->details.bank->execution_date),
"sender_account_details", pos->details.bank->sender_account_details,
"sender_account_url", pos->details.bank->sender_account_details,
"wire_reference", GNUNET_JSON_from_data (pos->details.bank->wire_reference,
pos->details.bank->wire_reference_size),
"amount", TALER_JSON_from_amount (&pos->details.bank->amount))));
@ -858,14 +858,9 @@ TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHisto
TALER_amount_hton (&rcc.closing_fee,
&pos->details.closing->closing_fee);
rcc.reserve_pub = pos->details.closing->reserve_pub;
if (GNUNET_OK !=
TALER_JSON_hash (pos->details.closing->receiver_account_details,
&rcc.h_wire))
{
GNUNET_break (0);
json_decref (json_history);
return NULL;
}
GNUNET_CRYPTO_hash (pos->details.closing->receiver_account_details,
strlen (pos->details.closing->receiver_account_details) + 1,
&rcc.h_wire);
rcc.wtid = pos->details.closing->wtid;
if (GNUNET_OK !=
TEH_KS_sign (&rcc.purpose,

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014-2017 GNUnet e.V.
Copyright (C) 2014-2018 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -28,6 +28,7 @@
#include "taler-exchange-httpd_keystate.h"
#include "taler-exchange-httpd_track_transfer.h"
#include "taler-exchange-httpd_responses.h"
#include "taler_wire_lib.h"
/**
@ -236,8 +237,8 @@ struct WtidTransactionContext
* @param cls our context for transmission
* @param rowid which row in the DB is the information from (for diagnostics)
* @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
* @param wire_method which wire plugin was used
* @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
* @param wire where the funds were sent
* @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
* @param h_contract_terms which proposal was this payment about
* @param coin_pub which public key was this payment about
@ -248,8 +249,8 @@ static void
handle_transaction_data (void *cls,
uint64_t rowid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const char *wire_method,
const struct GNUNET_HashCode *h_wire,
const json_t *wire,
struct GNUNET_TIME_Absolute exec_time,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -259,15 +260,22 @@ handle_transaction_data (void *cls,
struct WtidTransactionContext *ctx = cls;
struct TALER_Amount delta;
struct TEH_TrackTransferDetail *wdd;
char *wire_method;
if (GNUNET_SYSERR == ctx->is_valid)
return;
if (NULL == (wire_method = TALER_JSON_wire_to_method (wire)))
{
GNUNET_break (0);
ctx->is_valid = GNUNET_SYSERR;
return;
}
if (GNUNET_NO == ctx->is_valid)
{
ctx->merchant_pub = *merchant_pub;
ctx->h_wire = *h_wire;
ctx->exec_time = exec_time;
ctx->wire_method = GNUNET_strdup (wire_method);
ctx->wire_method = wire_method;
ctx->is_valid = GNUNET_YES;
if (GNUNET_OK !=
TALER_amount_subtract (&ctx->total,
@ -292,8 +300,10 @@ handle_transaction_data (void *cls,
{
GNUNET_break (0);
ctx->is_valid = GNUNET_SYSERR;
GNUNET_free (wire_method);
return;
}
GNUNET_free (wire_method);
if (GNUNET_OK !=
TALER_amount_subtract (&delta,
deposit_value,

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2016, 2017 GNUnet e.V.
Copyright (C) 2016, 2017, 2018 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -24,6 +24,8 @@
#include "taler-exchange-httpd.h"
#include "taler-exchange-httpd_validation.h"
#include "taler-exchange-httpd_wire.h"
#include "taler_exchangedb_lib.h"
#include "taler_json_lib.h"
#include "taler_wire_lib.h"
@ -43,11 +45,6 @@ struct Plugin
*/
struct Plugin *prev;
/**
* Type of the wireformat.
*/
char *type;
/**
* Pointer to the plugin.
*/
@ -55,6 +52,7 @@ struct Plugin
};
/**
* Head of DLL of wire plugins.
*/
@ -65,51 +63,163 @@ static struct Plugin *wire_head;
*/
static struct Plugin *wire_tail;
/**
* Array of wire methods supported by this exchange.
*/
static json_t *wire_accounts_array;
/**
* Load plugin @a name.
* Object mapping wire methods to the respective fee structure.
*/
static json_t *wire_fee_object;
/**
* Load wire fees for @a method.
*
* @param method wire method to load fee structure for
* @return #GNUNET_OK on success
*/
static int
load_fee (const char *method)
{
json_t *fees;
if (NULL != json_object_get (wire_fee_object,
method))
return GNUNET_OK; /* already have them */
fees = TEH_WIRE_get_fees (method);
if (NULL == fees)
return GNUNET_SYSERR;
/* Add fees to #wire_fee_object */
GNUNET_assert (-1 !=
json_object_set_new (wire_fee_object,
method,
fees));
return GNUNET_OK;
}
/**
* Initialize account; checks if @ai has /wire information, and if so,
* adds the /wire information (if included) to our responses. Also, if
* the account is debitable, we try to load the plugin.
*
* @param cls pointer to `int` to set to #GNUNET_SYSERR on errors
* @param name name of the plugin to load
*/
static void
load_plugin (void *cls,
const char *name)
load_account (void *cls,
const struct TALER_EXCHANGEDB_AccountInfo *ai)
{
int *ret = cls;
if ( (NULL != ai->wire_response_filename) &&
(GNUNET_YES == ai->credit_enabled) )
{
json_t *wire_s;
json_error_t error;
char *url;
char *method;
if (NULL == (wire_s = json_load_file (ai->wire_response_filename,
JSON_REJECT_DUPLICATES,
&error)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse `%s': %s at %d:%d (%d)\n",
ai->wire_response_filename,
error.text,
error.line,
error.column,
error.position);
*ret = GNUNET_SYSERR;
return;
}
if (NULL == (url = TALER_JSON_wire_to_payto (wire_s)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire response file `%s' lacks `url' entry\n",
ai->wire_response_filename);
json_decref (wire_s);
*ret = GNUNET_SYSERR;
return;
}
if (0 != strcasecmp (url,
ai->payto_url))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"URL in Wire response file `%s' does not match URL in configuration!\n",
ai->wire_response_filename);
json_decref (wire_s);
GNUNET_free (url);
*ret = GNUNET_SYSERR;
return;
}
GNUNET_free (url);
if (GNUNET_OK !=
TALER_JSON_wire_signature_check (wire_s,
&TEH_master_public_key))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid signature in `%s'\n",
ai->wire_response_filename);
json_decref (wire_s);
*ret = GNUNET_SYSERR;
return;
}
method = TALER_WIRE_payto_get_method (ai->payto_url);
if (GNUNET_OK ==
load_fee (method))
{
GNUNET_assert (-1 !=
json_array_append_new (wire_accounts_array,
wire_s));
}
else
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire fees not specified for `%s', ignoring plugin %s\n",
method,
ai->plugin_name);
*ret = GNUNET_SYSERR;
}
GNUNET_free (method);
}
if (GNUNET_YES == ai->debit_enabled)
{
struct Plugin *p;
json_t *fees;
p = GNUNET_new (struct Plugin);
p->type = GNUNET_strdup (name);
p->plugin = TALER_WIRE_plugin_load (cfg,
name);
ai->plugin_name);
if (NULL == p->plugin)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to load plugin %s\n",
name);
GNUNET_free (p->type);
ai->plugin_name);
GNUNET_free (p);
*ret = GNUNET_SYSERR;
return;
}
fees = TEH_WIRE_get_fees (name);
if (NULL == fees)
if (GNUNET_OK !=
load_fee (p->plugin->method))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Disabling method `%s' as wire transfer fees are not given correctly\n",
name);
GNUNET_free (p->type);
"Disabling plugin `%s' as wire transfer fees for `%s' are not given correctly\n",
ai->plugin_name,
p->plugin->method);
TALER_WIRE_plugin_unload (p->plugin);
GNUNET_free (p);
*ret = GNUNET_SYSERR;
return;
}
json_decref (fees);
GNUNET_CONTAINER_DLL_insert (wire_head,
wire_tail,
p);
}
}
/**
@ -124,8 +234,10 @@ TEH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
int ret;
ret = GNUNET_OK;
TALER_WIRE_find_enabled (cfg,
&load_plugin,
wire_accounts_array = json_array ();
wire_fee_object = json_object ();
TALER_EXCHANGEDB_find_accounts (cfg,
&load_account,
&ret);
if (NULL == wire_head)
{
@ -153,9 +265,12 @@ TEH_VALIDATION_done ()
wire_tail,
p);
TALER_WIRE_plugin_unload (p->plugin);
GNUNET_free (p->type);
GNUNET_free (p);
}
json_decref (wire_fee_object);
wire_fee_object = NULL;
json_decref (wire_accounts_array);
wire_accounts_array = NULL;
}
@ -164,109 +279,74 @@ TEH_VALIDATION_done ()
* a wire address.
*
* @param wire the JSON wire format object
* @param ours #GNUNET_YES if the signature should match our master key
* @param[out] emsg set to error message if we return an error code
* @return #TALER_EC_NONE if correctly formatted; otherwise error code
*/
enum TALER_ErrorCode
TEH_json_validate_wireformat (const json_t *wire,
int ours,
char **emsg)
{
const char *stype;
const char *payto_url;
json_error_t error;
struct Plugin *p;
char *method;
*emsg = NULL;
if (0 != json_unpack_ex ((json_t *) wire,
&error, 0,
"{s:s}",
"type", &stype))
"url", &payto_url))
{
GNUNET_asprintf (emsg,
"No `type' specified in the wire details\n");
"No `url' specified in the wire details\n");
return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE_MISSING;
}
for (p=wire_head; NULL != p; p = p->next)
if (0 == strcasecmp (p->type,
stype))
return p->plugin->wire_validate (p->plugin->cls,
wire,
(GNUNET_YES == ours)
? &TEH_master_public_key
: NULL,
emsg);
method = TALER_WIRE_payto_get_method (payto_url);
if (NULL == method)
{
GNUNET_asprintf (emsg,
"Malformed payto URL `%s'\n",
payto_url);
return TALER_EC_PAYTO_MALFORMED;
}
for (struct Plugin *p=wire_head; NULL != p; p = p->next)
{
if (0 == strcasecmp (p->plugin->method,
method))
{
enum TALER_ErrorCode ec;
GNUNET_free (method);
ec = p->plugin->wire_validate (p->plugin->cls,
payto_url);
if (TALER_EC_NONE != ec)
GNUNET_asprintf (emsg,
"Payto URL `%s' rejected by plugin\n",
payto_url);
return ec;
}
}
GNUNET_asprintf (emsg,
"Wire format type `%s' is not supported by this exchange\n",
stype);
method);
GNUNET_free (method);
return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE_UNSUPPORTED;
}
/**
* Obtain JSON of the supported wire methods for a given
* account name prefix.
* Obtain JSON response for /wire
*
* @return JSON array with the supported validation methods
* @return JSON array with the supported validation methods, NULL on error
*/
json_t *
TEH_VALIDATION_get_wire_methods ()
TEH_VALIDATION_get_wire_response ()
{
json_t *methods;
char *account_name;
char *emsg;
enum TALER_ErrorCode ec;
methods = json_object ();
for (struct Plugin *p=wire_head;NULL != p;p = p->next)
{
struct TALER_WIRE_Plugin *plugin = p->plugin;
json_t *method;
json_t *fees;
GNUNET_asprintf (&account_name,
"exchange-wire-%s",
p->type);
method = plugin->get_wire_details (plugin->cls,
cfg,
account_name);
if (TALER_EC_NONE !=
(ec = TEH_json_validate_wireformat (method,
GNUNET_YES,
&emsg)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Disabling method `%s' as details are ill-formed: %s (%d)\n",
p->type,
emsg,
ec);
GNUNET_free (emsg);
json_decref (method);
method = NULL;
}
fees = TEH_WIRE_get_fees (p->type);
if (NULL == fees)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Disabling method `%s' as wire transfer fees are not given correctly\n",
p->type);
json_decref (method);
method = NULL;
}
else
{
json_object_set_new (method,
"fees",
fees);
}
if (NULL != method)
json_object_set_new (methods,
p->type,
method);
GNUNET_free (account_name);
}
return methods;
if ( (0 == json_array_size (wire_accounts_array)) ||
(0 == json_object_size (wire_fee_object)) )
return NULL;
return json_pack ("{s:O, s:O}",
"accounts", wire_accounts_array,
"fees", wire_fee_object);
}

View File

@ -47,24 +47,21 @@ TEH_VALIDATION_done (void);
* a wire address.
*
* @param wire the JSON wire format object
* @param ours #GNUNET_YES if the signature should match our master key
* @param[out] emsg set to error message if we return an error code
* @return #TALER_EC_NONE if correctly formatted; otherwise error code
*/
enum TALER_ErrorCode
TEH_json_validate_wireformat (const json_t *wire,
int ours,
char **emsg);
/**
* Obtain JSON of the supported wire methods for a given
* account name prefix.
* Obtain JSON response for /wire
*
* @return JSON array with the supported validation methods
* @return JSON object with the supported wire transfer options, NULL on error
*/
json_t *
TEH_VALIDATION_get_wire_methods (void);
TEH_VALIDATION_get_wire_response (void);
#endif

View File

@ -74,20 +74,20 @@ fees_to_json (struct TALER_EXCHANGEDB_AggregateFees *af)
/**
* Obtain fee structure for @a wire_plugin_name wire transfers.
* Obtain fee structure for @a method wire transfers.
*
* @param wire_plugin_name name of the plugin to load fees for
* @param method method to load fees for
* @return JSON object (to be freed by caller) with fee structure
*/
json_t *
TEH_WIRE_get_fees (const char *wire_plugin_name)
TEH_WIRE_get_fees (const char *method)
{
struct TALER_EXCHANGEDB_AggregateFees *af;
json_t *j;
struct GNUNET_TIME_Absolute now;
af = TALER_EXCHANGEDB_fees_read (cfg,
wire_plugin_name);
method);
now = GNUNET_TIME_absolute_get ();
while ( (NULL != af) &&
(af->end_date.abs_value_us < now.abs_value_us) )
@ -101,7 +101,7 @@ TEH_WIRE_get_fees (const char *wire_plugin_name)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to find current wire transfer fees for `%s'\n",
wire_plugin_name);
method);
return NULL;
}
j = fees_to_json (af);
@ -143,9 +143,8 @@ TEH_WIRE_handler_wire (struct TEH_RequestHandler *rh,
int
TEH_WIRE_init ()
{
wire_methods = TEH_VALIDATION_get_wire_methods ();
if ( (NULL == wire_methods) ||
(0 == json_object_size (wire_methods)) )
wire_methods = TEH_VALIDATION_get_wire_response ();
if (NULL == wire_methods)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to find properly configured wire transfer method\n");
@ -156,7 +155,7 @@ TEH_WIRE_init ()
/**
* Initialize libgcrypt.
* Clean up wire subsystem.
*/
void __attribute__ ((destructor))
TEH_wire_cleanup ()

View File

@ -39,11 +39,11 @@ TEH_WIRE_init (void);
/**
* Obtain fee structure for @a wire_plugin_name wire transfers.
*
* @param wire_plugin_name name of the plugin to load fees for
* @param method method to load fees for
* @return JSON object (to be freed by caller) with fee structure
*/
json_t *
TEH_WIRE_get_fees (const char *wire_plugin_name);
TEH_WIRE_get_fees (const char *method);
/**

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2016, 2017 GNUnet e.V.
Copyright (C) 2016, 2017, 2018 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -53,10 +53,59 @@ struct RejectContext
};
/**
* Information we keep for each supported account.
*/
struct WireAccount
{
/**
* Accounts are kept in a DLL.
*/
struct WireAccount *next;
/**
* Plugins are kept in a DLL.
*/
struct WireAccount *prev;
/**
* Handle to the plugin.
*/
static struct TALER_WIRE_Plugin *wire_plugin;
struct TALER_WIRE_Plugin *wire_plugin;
/**
* Name of the section that configures this account.
*/
char *section_name;
/**
* Are we running from scratch and should re-process all transactions
* for this account?
*/
int reset_mode;
/**
* Until when is processing this wire plugin delayed?
*/
struct GNUNET_TIME_Absolute delayed_until;
};
/**
* Head of list of loaded wire plugins.
*/
static struct WireAccount *wa_head;
/**
* Tail of list of loaded wire plugins.
*/
static struct WireAccount *wa_tail;
/**
* Wire plugin we are currently using.
*/
static struct WireAccount *wa_pos;
/**
* Which currency is used by this exchange?
@ -90,11 +139,6 @@ static void *last_row_off;
*/
static size_t last_row_off_size;
/**
* Which wire plugin are we watching?
*/
static char *type;
/**
* Should we delay the next request to the wire plugin a bit?
*/
@ -134,6 +178,8 @@ static struct TALER_WIRE_RejectHandle *rt;
static void
shutdown_task (void *cls)
{
struct WireAccount *wa;
if (NULL != task)
{
GNUNET_SCHEDULER_cancel (task);
@ -141,7 +187,7 @@ shutdown_task (void *cls)
}
if (NULL != hh)
{
wire_plugin->get_history_cancel (wire_plugin->cls,
wa_pos->wire_plugin->get_history_cancel (wa_pos->wire_plugin->cls,
hh);
hh = NULL;
}
@ -149,21 +195,64 @@ shutdown_task (void *cls)
{
char *wtid_s;
wtid_s = wire_plugin->reject_transfer_cancel (wire_plugin->cls,
wtid_s = wa_pos->wire_plugin->reject_transfer_cancel (wa_pos->wire_plugin->cls,
rt);
rt = NULL;
GNUNET_free (wtid_s);
}
TALER_EXCHANGEDB_plugin_unload (db_plugin);
db_plugin = NULL;
TALER_WIRE_plugin_unload (wire_plugin);
wire_plugin = NULL;
while (NULL != (wa = wa_head))
{
GNUNET_CONTAINER_DLL_remove (wa_head,
wa_tail,
wa);
TALER_WIRE_plugin_unload (wa->wire_plugin);
GNUNET_free (wa->section_name);
GNUNET_free (wa);
}
wa_pos = NULL;
GNUNET_free_non_null (last_row_off);
last_row_off = NULL;
last_row_off_size = 0;
}
/**
* Function called with information about a wire account. Adds the
* account to our list (if it is enabled and we can load the plugin).
*
* @param cls closure, NULL
* @param ai account information
*/
static void
add_account_cb (void *cls,
const struct TALER_EXCHANGEDB_AccountInfo *ai)
{
struct WireAccount *wa;
(void) cls;
if (GNUNET_YES != ai->credit_enabled)
return; /* not enabled for us, skip */
wa = GNUNET_new (struct WireAccount);
wa->reset_mode = reset_mode;
wa->wire_plugin = TALER_WIRE_plugin_load (cfg,
ai->plugin_name);
if (NULL == wa->wire_plugin)
{
fprintf (stderr,
"Failed to load wire plugin for `%s'\n",
ai->plugin_name);
GNUNET_free (wa);
return;
}
wa->section_name = GNUNET_strdup (ai->section_name);
GNUNET_CONTAINER_DLL_insert (wa_head,
wa_tail,
wa);
}
/**
* Parse configuration parameters for the exchange server into the
* corresponding global variables.
@ -173,12 +262,6 @@ shutdown_task (void *cls)
static int
exchange_serve_process_config ()
{
if (NULL == type)
{
fprintf (stderr,
"Option `-t' to specify wire plugin is mandatory.\n");
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"taler",
@ -206,17 +289,16 @@ exchange_serve_process_config ()
"Failed to initialize DB subsystem\n");
return GNUNET_SYSERR;
}
if (NULL ==
(wire_plugin = TALER_WIRE_plugin_load (cfg,
type)))
TALER_EXCHANGEDB_find_accounts (cfg,
&add_account_cb,
NULL);
if (NULL == wa_head)
{
fprintf (stderr,
"Failed to load wire plugin for `%s'\n",
type);
"No wire accounts configured for credit!\n");
TALER_EXCHANGEDB_plugin_unload (db_plugin);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
@ -292,12 +374,11 @@ history_cb (void *cls,
struct TALER_ReservePublicKeyP reserve_pub;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Got history callback, direction %u!\n", (unsigned int) dir);
"Got history callback, direction %u!\n",
(unsigned int) dir);
if (TALER_BANK_DIRECTION_NONE == dir)
{
hh = NULL;
if (TALER_EC_NONE != ec)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -309,18 +390,27 @@ history_cb (void *cls,
qs = db_plugin->commit (db_plugin->cls,
session);
if ( (GNUNET_YES == delay) &&
(test_mode) )
(test_mode) &&
(NULL == wa_pos->next) )
{
GNUNET_SCHEDULER_shutdown ();
return GNUNET_OK;
}
if (GNUNET_YES == delay)
task = GNUNET_SCHEDULER_add_delayed (DELAY,
{
wa_pos->delayed_until
= GNUNET_TIME_relative_to_absolute (DELAY);
GNUNET_free_non_null (last_row_off);
last_row_off = NULL;
last_row_off_size = 0;
wa_pos = wa_pos->next;
if (NULL == wa_pos)
wa_pos = wa_head;
GNUNET_assert (NULL != wa_pos);
}
task = GNUNET_SCHEDULER_add_at (wa_pos->delayed_until,
&find_transfers,
NULL);
else
task = GNUNET_SCHEDULER_add_now (&find_transfers,
NULL);
return GNUNET_OK; /* will be ignored anyway */
}
if (NULL != details->wtid_s)
@ -344,7 +434,8 @@ history_cb (void *cls,
rtc = GNUNET_new (struct RejectContext);
rtc->session = session;
rtc->wtid_s = GNUNET_strdup (details->wtid_s);
rt = wire_plugin->reject_transfer (wire_plugin->cls,
rt = wa_pos->wire_plugin->reject_transfer (wa_pos->wire_plugin->cls,
wa_pos->section_name,
row_off,
row_off_size,
&reject_cb,
@ -366,7 +457,8 @@ history_cb (void *cls,
&reserve_pub,
&details->amount,
details->execution_date,
details->account_details,
details->account_url,
wa_pos->section_name,
row_off,
row_off_size);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
@ -434,10 +526,11 @@ find_transfers (void *cls)
GNUNET_SCHEDULER_shutdown ();
return;
}
if (! reset_mode)
if (! wa_pos->reset_mode)
{
qs = db_plugin->get_latest_reserve_in_reference (db_plugin->cls,
session,
wa_pos->section_name,
&last_row_off,
&last_row_off_size);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
@ -456,11 +549,13 @@ find_transfers (void *cls)
return;
}
}
wa_pos->reset_mode = GNUNET_NO;
GNUNET_assert ( (NULL == last_row_off) ||
( (NULL != last_row_off) &&
(0 != last_row_off_size) ) );
delay = GNUNET_YES;
hh = wire_plugin->get_history (wire_plugin->cls,
hh = wa_pos->wire_plugin->get_history (wa_pos->wire_plugin->cls,
wa_pos->section_name,
TALER_BANK_DIRECTION_CREDIT,
last_row_off,
last_row_off_size,
@ -501,7 +596,8 @@ run (void *cls,
global_ret = 1;
return;
}
wa_pos = wa_head;
GNUNET_assert (NULL != wa_pos);
task = GNUNET_SCHEDULER_add_now (&find_transfers,
NULL);
GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
@ -521,11 +617,6 @@ main (int argc,
char *const *argv)
{
struct GNUNET_GETOPT_CommandLineOption options[] = {
GNUNET_GETOPT_option_string ('t',
"type",
"PLUGINNAME",
"which wire plugin to use",
&type),
GNUNET_GETOPT_option_flag ('T',
"test",
"run in test mode and exit when idle",

View File

@ -16,7 +16,8 @@ PORT = 8081
# Master public key used to sign the exchange's various keys
MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
# Expected base URL of the exchange.
# Expected base URL of the exchange. Used in wire transfers for
# the tracking API.
BASE_URL = "https://exchange.taler.net/"
[exchangedb]
@ -31,16 +32,24 @@ IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
[exchangedb-postgres]
#The connection string the plugin has to use for connecting to the database
DB_CONN_STR = postgres:///talercheck
CONFIG = postgres:///talercheck
[exchange-wire-test]
# Enable 'test' for testing of the actual coin operations.
ENABLE = YES
[account-1]
# What is the account URL?
URL = "payto://x-taler-bank/localhost:8082/3"
WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json
PLUGIN = "taler_bank"
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
TALER_BANK_AUTH_METHOD = NONE
[fees-x-taler-bank]
# Fees for the forseeable future...
# If you see this after 2017, update to match the next 10 years...
WIRE-FEE-2017 = EUR:0.01
# If you see this after 2018, update to match the next 10 years...
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
WIRE-FEE-2020 = EUR:0.01
@ -50,8 +59,8 @@ WIRE-FEE-2023 = EUR:0.01
WIRE-FEE-2024 = EUR:0.01
WIRE-FEE-2025 = EUR:0.01
WIRE-FEE-2026 = EUR:0.01
WIRE-FEE-2027 = EUR:0.01
CLOSING-FEE-2017 = EUR:0.01
CLOSING-FEE-2018 = EUR:0.01
CLOSING-FEE-2019 = EUR:0.01
CLOSING-FEE-2020 = EUR:0.01
@ -61,11 +70,4 @@ CLOSING-FEE-2023 = EUR:0.01
CLOSING-FEE-2024 = EUR:0.01
CLOSING-FEE-2025 = EUR:0.01
CLOSING-FEE-2026 = EUR:0.01
# What is the main website of the bank?
BANK_URL = "http://localhost:8082/"
# From which account at the 'bank' should outgoing
# wire transfers be made?
EXCHANGE_ACCOUNT_NUMBER = 3
CLOSING-FEE-2027 = EUR:0.01

View File

@ -33,16 +33,23 @@ IDLE_RESERVE_EXPIRATION_TIME = 5 s
[exchangedb-postgres]
#The connection string the plugin has to use for connecting to the database
DB_CONN_STR = postgres:///talercheck
CONFIG = postgres:///talercheck
[account-1]
[exchange-wire-test]
# Enable 'test' for testing of the actual coin operations.
ENABLE = YES
# What is the account URL?
URL = "payto://x-taler-bank/localhost:8082/3"
WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json
PLUGIN = "taler_bank"
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
TALER_BANK_AUTH_METHOD = NONE
[fees-x-taler-bank]
# Fees for the forseeable future...
# If you see this after 2017, update to match the next 10 years...
WIRE-FEE-2017 = EUR:0.01
# If you see this after 2018, update to match the next 10 years...
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
WIRE-FEE-2020 = EUR:0.01
@ -52,8 +59,8 @@ WIRE-FEE-2023 = EUR:0.01
WIRE-FEE-2024 = EUR:0.01
WIRE-FEE-2025 = EUR:0.01
WIRE-FEE-2026 = EUR:0.01
WIRE-FEE-2027 = EUR:0.01
CLOSING-FEE-2017 = EUR:0.01
CLOSING-FEE-2018 = EUR:0.01
CLOSING-FEE-2019 = EUR:0.01
CLOSING-FEE-2020 = EUR:0.01
@ -63,11 +70,4 @@ CLOSING-FEE-2023 = EUR:0.01
CLOSING-FEE-2024 = EUR:0.01
CLOSING-FEE-2025 = EUR:0.01
CLOSING-FEE-2026 = EUR:0.01
# What is the main website of the bank?
BANK_URL = "http://localhost:8082/"
# From which account at the 'bank' should outgoing
# wire transfers be made?
EXCHANGE_ACCOUNT_NUMBER = 3
CLOSING-FEE-2027 = EUR:0.01

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
(C) 2016, 2017 Inria and GNUnet e.V.
(C) 2016, 2017, 2018 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
@ -403,7 +403,9 @@ do_deposit (struct Command *cmd)
struct TALER_MerchantPrivateKeyP merchant_priv;
int ret;
memset (&deposit, 0, sizeof (deposit));
memset (&deposit,
0,
sizeof (deposit));
/* we derive the merchant's private key from the
name, to ensure that the same name always
results in the same key pair. */
@ -432,12 +434,20 @@ do_deposit (struct Command *cmd)
}
fake_coin (&deposit.coin);
/* Build JSON for wire details */
deposit.receiver_wire_account = json_pack ("{s:s, s:s, s:I}",
"type", "test",
"bank_url", "http://localhost:8082/",
"account_number", (json_int_t) cmd->details.deposit.merchant_account);
{
char *str;
GNUNET_asprintf (&str,
"payto://x-taler-bank/localhost:8082/%llu",
(unsigned long long) cmd->details.deposit.merchant_account);
deposit.receiver_wire_account
= json_pack ("{s:s, s:s}",
"salt", "this-is-a-salt-value",
"url", str);
GNUNET_free (str);
}
GNUNET_assert (GNUNET_OK ==
TALER_JSON_hash (deposit.receiver_wire_account,
TALER_JSON_wire_signature_hash (deposit.receiver_wire_account,
&deposit.h_wire));
deposit.timestamp = GNUNET_TIME_absolute_get ();
GNUNET_TIME_round_abs (&deposit.timestamp);

View File

@ -30,18 +30,49 @@ IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
[exchangedb-postgres]
DB_CONN_STR = "postgres:///talercheck"
CONFIG = "postgres:///talercheck"
[exchange-wire-test]
# Enable 'test' for testing of the actual coin operations.
ENABLE = YES
[account-1]
# What is the main website of the bank?
BANK_URL = "http://localhost:8082/"
URL = "payto://x-taler-bank/localhost:8082/3"
# From which account at the 'bank' should outgoing
# wire transfers be made?
EXCHANGE_ACCOUNT_NUMBER = 3
WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json
PLUGIN = "taler_bank"
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
TALER_BANK_AUTH_METHOD = NONE
# Wire fees are specified by wire method, NOT by wire plugin.
[fees-x-taler-bank]
# Fees for the forseeable future...
# If you see this after 2018, update to match the next 10 years...
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
WIRE-FEE-2020 = EUR:0.01
WIRE-FEE-2021 = EUR:0.01
WIRE-FEE-2022 = EUR:0.01
WIRE-FEE-2023 = EUR:0.01
WIRE-FEE-2024 = EUR:0.01
WIRE-FEE-2025 = EUR:0.01
WIRE-FEE-2026 = EUR:0.01
WIRE-FEE-2027 = EUR:0.01
CLOSING-FEE-2018 = EUR:0.01
CLOSING-FEE-2019 = EUR:0.01
CLOSING-FEE-2020 = EUR:0.01
CLOSING-FEE-2021 = EUR:0.01
CLOSING-FEE-2022 = EUR:0.01
CLOSING-FEE-2023 = EUR:0.01
CLOSING-FEE-2024 = EUR:0.01
CLOSING-FEE-2025 = EUR:0.01
CLOSING-FEE-2026 = EUR:0.01
CLOSING-FEE-2027 = EUR:0.01
# Coins for the tests.

View File

@ -1,5 +1,5 @@
# This file is part of TALER
# Copyright (C) 2015 GNUnet e.V.
# Copyright (C) 2015, 2018 Taler Systems SA
#
# TALER is free software; you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free Software
@ -23,21 +23,6 @@
# Note that neither element may contain any spaces!
#
#
# Bad amount:
/admin/add/incoming {"reserve_pub":"7RZBZ86677QMASD2KAYGEPD246C7B7RC6P101FNTG6ZK8X61A620","amount":"1","execution_date":"\/Date(1435934428788)\/","wire":{"empty":"empty"}}
#
# Bad wire format:
/admin/add/incoming {"reserve_pub":"6VRFYZRVHJ434BV3J018MS6H7Q1V5Q6YECNMEF9G4WKB8QJQCAX0","amount":{"currency":"EUR","value":10,"fraction":3},"execution_date":"\/Date(1436258333286)\/","wire":{"empty":"empty"}}
#
# Malformed JSON (ill-balanced quotes around 'amount')
/admin/add/incoming {"reserve_pub":"BSEFVVNZ4C3724BPVKTJMQMD73HQREA5FWSS1C1BZ36ZFF2WBTK0",amount":{"currency":"EUR","value":5,"fraction":3},"execution_date":"\/Date(1436271156447)\/","wire":{"type":"test","IBAN":0,"name":"Jack","BIC":999,"edate":"\/Date(1436271156447)\/","r":50}}
#
# Bad amount (value not a string)
/admin/add/incoming {"reserve_pub":"BSEFVVNZ4C3724BPVKTJMQMD73HQREA5FWSS1C1BZ36ZFF2WBTK0","amount":{"currency":"EUR","value":"5","fraction":3},"execution_date":"\/Date(1436271156447)\/","wire":{"type":"test","IBAN":0,"name":"Jack","BIC":999,"edate":"\/Date(1436271156447)\/","r":50}}
#
# Bad amount (overall amount is a string)
/admin/add/incoming {"reserve_pub":"BSEFVVNZ4C3724BPVKTJMQMD73HQREA5FWSS1C1BZ36ZFF2WBTK0","amount":"{\"currency\":\"EUR\",\"value\":5,\"fraction\":3}","execution_date":"\/Date(1436271156447)\/","wire":{"type":"test"}}
#
# Bogus denomination key
/deposit {"f":{"currency":"EUR","value":5,"fraction":0},"h_contract_terms":"NRT9E07FYT147V4VCDG0102P0YX0FZ11ZRG90F4X1HDV95M0J64ZVE4XQGNN9MJ3B5K3JX6TJ181KNGRYSZSTYZ5PQHBM1F9QKQ5B50","wire":{"bank":"dest bank","type":"TEST","account":42},"timestamp":"/Date(1436823947)/","coin_pub":"2KCPBGZ77VGJT4DG99EZAY0GQ5TJ89DF53FWYR5RFRTK0CCXRMFG","denom_pub":"51B7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30E9S6GVK2DHM8S234C236CR32C9N8RW44E9M712KAH1R60VM2CJ16RT3GGA18RR36CA575144DJ58CTK0E9M8D2M2E9S8GTKGH1Q8S0KACT174S3AD2670R4ADJ664W32C1N8N23CHA58MSK6DJ26WSMAD1P8H132CHP8GWKAG9K8RS46GJ6890M6GT28GSK4GJ66X2KCCA168RM4GA67113GDA28RR4AGA36RVK6GA460VKJDT58CVK6HA488R48E9R6D2KEH258N246HHJ850K4H9R8N0KEC9N68SM2EA48RR3JEA284SM6C9M6D130D228MSK6H1J6MSKCH1K8CR38CJ48MV36GJ38513CE9P60TM6CA56D1K8HHQ75244DA26WW4CG9M8MW3JE9M7133JGH354520818CMG26C1H60R30C935452081918G2J2G0","coin_sig":"W1TDFCSW5XQX9ZF4QPVP3JAJFYA7G4X6SY2B49KRNTDMA685M9YNFETV4610RFKZMSQ3RBRCYBJQH1ZQSMTDMW9W8X6C9SGPCA5ST0R","H_wire":"YQED9FDYPKK2QQYB3FS19Y15ZMKBAXJP2C73CXASAF1KM6ZYY723TEJ3HBR6D864A7X5W58G92QJ0A9PFMZNB81ZP9NJAQQCCABM4RG","ub_sig":"51SPJSSDESGPR80A40M74WV140520818ECG26DSQ8913AC1S7513EC9G6914AE228H236HHR68VKCCSK650MAGSM6GV3GCHP6D330H1R8GVKJD9P68W46H9Q8GVK8HA6752M2CSN8N248DHJ8H346E9J8RS4CDA28D33ECJ38S33JC9R6MRM8D9G74WM8HA668T44H1N6RT44GHS8CSK8H1G6D346C9J6CS3EC1N8H2M4HA38CSK2D246CW4CD1P70VMAD1Q891K8H1M64TK6C258MRM6G9R88RM6E2488WK2CSQ6GW3GH9N64RKGH2375136GA66533GCSJ65344CHH84W38HHP75330DA58RSKJCJ364SK0C1R8GVK6DSP61134HA48GT4CE1J6MW36C9G891K2GT68GTMCCSQ890M8E1P88R44DA174VK4E135452081918G2J2G0","merchant_pub":"4032W2ZXFW000KRJQDH3CZR00000000004000030P6YG1NR50000","refund_deadline":"/Date(0)/"}
#

View File

@ -25,7 +25,9 @@ unset XDG_DATA_HOME
unset XDG_CONFIG_HOME
#
# Setup keys.
taler-exchange-keyup -c test_taler_exchange_httpd.conf
taler-exchange-keyup -c test_taler_exchange_httpd.conf || exit 1
# Setup wire accounts.
taler-exchange-wire -c test_taler_exchange_httpd.conf || exit 1
# Run Exchange HTTPD (in background)
taler-exchange-httpd -c test_taler_exchange_httpd.conf -i &
# Give HTTP time to start

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
(C) 2016, 2017 Inria and GNUnet e.V.
(C) 2016, 2017, 2018 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
@ -499,7 +499,6 @@ interpreter (void *cls)
"taler-exchange-wirewatch",
"taler-exchange-wirewatch",
"-c", config_filename,
"-t", "test",
"-T", /* run in test mode, exit instead of looping */
NULL);
if (NULL == cmd->details.wirewatch.wirewatch_proc)

View File

@ -42,6 +42,7 @@ lib_LTLIBRARIES = \
libtalerexchangedb.la
libtalerexchangedb_la_SOURCES = \
exchangedb_accounts.c \
exchangedb_auditorkeys.c \
exchangedb_denomkeys.c \
exchangedb_fees.c \
@ -111,7 +112,8 @@ test_exchangedb_postgres_LDADD = \
$(top_builddir)/src/json/libtalerjson.la \
$(top_srcdir)/src/util/libtalerutil.la \
$(top_srcdir)/src/pq/libtalerpq.la \
-lgnunetutil -ljansson
-ljansson \
-lgnunetutil
test_perf_taler_exchangedb_SOURCES = \
test_perf_taler_exchangedb.c \
@ -121,8 +123,8 @@ test_perf_taler_exchangedb_LDADD = \
libtalerexchangedb.la \
$(top_srcdir)/src/util/libtalerutil.la \
$(top_srcdir)/src/pq/libtalerpq.la \
-ljansson \
-lgnunetutil
-lgnunetutil \
-ljansson
perf_exchangedb_SOURCES = \
perf_taler_exchangedb.c \
@ -132,8 +134,8 @@ perf_exchangedb_LDADD = \
libtalerexchangedb.la \
$(top_srcdir)/src/util/libtalerutil.la \
$(top_srcdir)/src/pq/libtalerpq.la \
-ljansson \
-lgnunetutil
-lgnunetutil \
-ljansson
EXTRA_test_exchangedb_postgres_DEPENDENCIES = \

View File

@ -1,2 +1,2 @@
[exchangedb-postgres]
DB_CONN_STR = "postgres:///taler"
CONFIG = "postgres:///taler"

View File

@ -0,0 +1,141 @@
/*
This file is part of TALER
Copyright (C) 2018 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/exchangedb_accounts.c
* @brief Logic to parse account information from the configuration
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_exchangedb_lib.h"
/**
* Closure of #check_for_account.
*/
struct FindAccountContext
{
/**
* Configuration we are usign.
*/
const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Callback to invoke.
*/
TALER_EXCHANGEDB_AccountCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
};
/**
* Check if @a section begins with "exchange-wire-", and if
* so if the "ENABLE" option is set to "YES". If both are
* true, call the callback from the context with the
* rest of the section name.
*
* @param cls our `struct FindEnabledWireContext`
* @param section name of a section in the configuration
*/
static void
check_for_account (void *cls,
const char *section)
{
struct FindAccountContext *ctx = cls;
char *plugin_name;
char *payto_url;
char *wire_response_filename;
struct TALER_EXCHANGEDB_AccountInfo ai;
if (0 != strncasecmp (section,
"account-",
strlen ("account-")))
return;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (ctx->cfg,
section,
"URL",
&payto_url))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
section,
"URL");
return;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (ctx->cfg,
section,
"PLUGIN",
&plugin_name))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
section,
"PLUGIN");
GNUNET_free (payto_url);
return;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_filename (ctx->cfg,
section,
"WIRE_RESPONSE",
&wire_response_filename))
wire_response_filename = NULL;
ai.section_name = section;
ai.plugin_name = plugin_name;
ai.payto_url = payto_url;
ai.wire_response_filename = wire_response_filename;
ai.debit_enabled = (GNUNET_YES ==
GNUNET_CONFIGURATION_get_value_yesno (ctx->cfg,
section,
"ENABLE_DEBIT"));
ai.credit_enabled = (GNUNET_YES ==
GNUNET_CONFIGURATION_get_value_yesno (ctx->cfg,
section,
"ENABLE_CREDIT"));
ctx->cb (ctx->cb_cls,
&ai);
GNUNET_free (payto_url);
GNUNET_free (plugin_name);
GNUNET_free_non_null (wire_response_filename);
}
/**
* Parse the configuration to find account information.
*
* @param cfg configuration to use
* @param cb callback to invoke
* @param cb_cls closure for @a cb
*/
void
TALER_EXCHANGEDB_find_accounts (const struct GNUNET_CONFIGURATION_Handle *cfg,
TALER_EXCHANGEDB_AccountCallback cb,
void *cb_cls)
{
struct FindAccountContext ctx;
ctx.cfg = cfg;
ctx.cb = cb;
ctx.cb_cls = cb_cls;
GNUNET_CONFIGURATION_iterate_sections (cfg,
&check_for_account,
&ctx);
}
/* end of exchangedb_accounts.c */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
Copyright (C) 2014-2018 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

View File

@ -204,6 +204,7 @@ PERF_TALER_EXCHANGEDB_reserve_free (struct PERF_TALER_EXCHANGEDB_Reserve *reserv
/**
* Generate a dummy deposit for testing purposes
*
* @param dki the denomination key used to sign the key
*/
struct TALER_EXCHANGEDB_Deposit *
@ -214,19 +215,12 @@ PERF_TALER_EXCHANGEDB_deposit_init (const struct PERF_TALER_EXCHANGEDB_Coin *coi
struct TALER_MerchantPublicKeyP merchant_pub;
struct GNUNET_HashCode h_contract_terms;
struct GNUNET_HashCode h_wire;
const char wire[] = "{"
"\"type\":\"SEPA\","
"\"IBAN\":\"DE67830654080004822650\","
"\"NAME\":\"GNUNET E.\","
"\"BIC\":\"GENODEF1SRL\""
"}";
struct GNUNET_TIME_Absolute timestamp;
struct GNUNET_TIME_Absolute refund_deadline;
struct TALER_Amount amount_with_fee;
struct TALER_Amount deposit_fee;
GNUNET_assert (NULL !=
(deposit = GNUNET_malloc (sizeof (struct TALER_EXCHANGEDB_Deposit) + sizeof (wire))));
deposit = GNUNET_new (struct TALER_EXCHANGEDB_Deposit);
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
&h_contract_terms);
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
@ -253,8 +247,7 @@ PERF_TALER_EXCHANGEDB_deposit_init (const struct PERF_TALER_EXCHANGEDB_Coin *coi
eddsa_prv = GNUNET_CRYPTO_eddsa_key_create ();
GNUNET_assert(NULL != eddsa_prv);
GNUNET_CRYPTO_eddsa_key_get_public (
eddsa_prv,
GNUNET_CRYPTO_eddsa_key_get_public (eddsa_prv,
&merchant_pub.eddsa_pub);
GNUNET_free (eddsa_prv);
}
@ -280,7 +273,10 @@ PERF_TALER_EXCHANGEDB_deposit_init (const struct PERF_TALER_EXCHANGEDB_Coin *coi
deposit->csig = csig;
deposit->h_contract_terms = h_contract_terms;
deposit->h_wire = h_wire;
deposit->receiver_wire_account = json_loads (wire, 0, NULL);
deposit->receiver_wire_account
= json_pack ("{s:s, s:s}",
"url", "payto://sepa/DE67830654080004822650",
"salt", "this-is-a-salt-value");
deposit->timestamp = timestamp;
deposit->refund_deadline = refund_deadline;
deposit->amount_with_fee = amount_with_fee;
@ -301,7 +297,7 @@ PERF_TALER_EXCHANGEDB_deposit_copy (const struct TALER_EXCHANGEDB_Deposit *depos
copy = GNUNET_new (struct TALER_EXCHANGEDB_Deposit);
*copy = *deposit;
json_incref (copy->receiver_wire_account);
copy->receiver_wire_account = json_incref (deposit->receiver_wire_account);
copy->coin.denom_pub.rsa_public_key =
GNUNET_CRYPTO_rsa_public_key_dup (deposit->coin.denom_pub.rsa_public_key);
copy->coin.denom_sig.rsa_signature =

View File

@ -1243,19 +1243,18 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state)
unsigned int reserve_index;
int ret;
struct PERF_TALER_EXCHANGEDB_Reserve *reserve;
json_t *sndr;
char *sndr;
uint32_t uid;
struct GNUNET_TIME_Absolute now;
reserve_index = state->cmd[state->i].details.insert_reserve.index_reserve;
reserve = state->cmd[reserve_index].exposed.data.reserve;
sndr = json_pack ("{s:i}",
"account",
(int) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
GNUNET_asprintf (&sndr,
"payto://x-taler-test/localhost:8080/%u",
(unsigned int) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
UINT32_MAX));
uid = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
UINT32_MAX);
GNUNET_assert (NULL != sndr);
now = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&now);
ret = state->plugin->reserves_in_insert (state->plugin->cls,
@ -1264,10 +1263,11 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state)
&reserve->reserve.balance,
now,
sndr,
"account-1",
&uid,
sizeof (uid));
GNUNET_assert (GNUNET_SYSERR != ret);
json_decref (sndr);
GNUNET_free (sndr);
}
break;

View File

@ -42,8 +42,7 @@ common_free_reserve_history (void *cls,
{
case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
bt = rh->details.bank;
if (NULL != bt->sender_account_details)
json_decref (bt->sender_account_details);
GNUNET_free_non_null (bt->sender_account_details);
GNUNET_free_non_null (bt->wire_reference);
GNUNET_free (bt);
break;
@ -61,8 +60,7 @@ common_free_reserve_history (void *cls,
break;
case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
closing = rh->details.closing;
if (NULL != closing->receiver_account_details)
json_decref (closing->receiver_account_details);
GNUNET_free_non_null (closing->receiver_account_details);
GNUNET_free (closing);
break;
}
@ -92,7 +90,7 @@ common_free_coin_transaction_list (void *cls,
switch (list->type)
{
case TALER_EXCHANGEDB_TT_DEPOSIT:
json_decref (list->details.deposit->receiver_wire_account);
GNUNET_free_non_null (list->details.deposit->receiver_wire_account);
if (NULL != list->details.deposit->coin.denom_pub.rsa_public_key)
GNUNET_CRYPTO_rsa_public_key_free (list->details.deposit->coin.denom_pub.rsa_public_key);
if (NULL != list->details.deposit->coin.denom_sig.rsa_signature)

View File

@ -127,6 +127,7 @@ postgres_drop_tables (void *cls)
PGconn *conn;
int ret;
/* FIXME: use GNUNET_PQ_connect_with_cfg instead? */
conn = GNUNET_PQ_connect (pc->connection_cfg_str);
if (NULL == conn)
return GNUNET_SYSERR;
@ -218,12 +219,13 @@ postgres_create_tables (void *cls)
",credit_frac INT4 NOT NULL"
",credit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
",sender_account_details TEXT NOT NULL"
",exchange_account_section TEXT NOT NULL"
",execution_date INT8 NOT NULL"
",PRIMARY KEY (reserve_pub, wire_reference)"
");"),
/* Create indices on reserves_in */
GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_in_execution_index"
" ON reserves_in (execution_date);"),
" ON reserves_in (exchange_account_section,execution_date);"),
/* This table contains the data for wire transfers the exchange has
executed to close a reserve. */
GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS reserves_close "
@ -433,6 +435,7 @@ postgres_create_tables (void *cls)
PGconn *conn;
int ret;
/* FIXME: use GNUNET_PQ_connect_with_cfg instead? */
conn = GNUNET_PQ_connect (pc->connection_cfg_str);
if (NULL == conn)
return GNUNET_SYSERR;
@ -585,21 +588,23 @@ postgres_prepare (PGconn *db_conn)
",credit_val"
",credit_frac"
",credit_curr"
",exchange_account_section"
",sender_account_details"
",execution_date"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7) "
"($1, $2, $3, $4, $5, $6, $7, $8) "
"ON CONFLICT DO NOTHING;",
7),
8),
/* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
transactions for reserves with serial id '\geq' the given parameter */
GNUNET_PQ_make_prepare ("reserves_in_get_latest_wire_reference",
"SELECT"
" wire_reference"
" FROM reserves_in"
" WHERE exchange_account_section=$1"
" ORDER BY reserve_in_serial_id DESC"
" LIMIT 1;",
0),
1),
/* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
transactions for reserves with serial id '\geq' the given parameter */
GNUNET_PQ_make_prepare ("audit_reserves_in_get_transactions_incr",
@ -1509,6 +1514,7 @@ postgres_get_session (void *cls)
return session;
}
}
/* FIXME: use GNUNET_PQ_connect_with_cfg instead? */
db_conn = GNUNET_PQ_connect (pc->connection_cfg_str);
if (NULL == db_conn)
return NULL;
@ -1836,7 +1842,9 @@ reserves_update (void *cls,
* @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
* @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_reference unique reference identifying the wire transfer (binary blob)
* @param wire_reference_size number of bytes in @a wire_reference
* @return transaction status code
@ -1847,7 +1855,8 @@ postgres_reserves_in_insert (void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *balance,
struct GNUNET_TIME_Absolute execution_time,
const json_t *sender_account_details,
const char *sender_account_details,
const char *exchange_account_section,
const void *wire_reference,
size_t wire_reference_size)
{
@ -1900,7 +1909,7 @@ postgres_reserves_in_insert (void *cls,
as a foreign key. */
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
TALER_PQ_query_param_json (sender_account_details),
GNUNET_PQ_query_param_string (sender_account_details),
TALER_PQ_query_param_amount (balance),
TALER_PQ_query_param_absolute_time (&expiry),
GNUNET_PQ_query_param_end
@ -1929,7 +1938,8 @@ postgres_reserves_in_insert (void *cls,
GNUNET_PQ_query_param_fixed_size (wire_reference,
wire_reference_size),
TALER_PQ_query_param_amount (balance),
TALER_PQ_query_param_json (sender_account_details),
GNUNET_PQ_query_param_string (exchange_account_section),
GNUNET_PQ_query_param_string (sender_account_details),
TALER_PQ_query_param_absolute_time (&execution_time),
GNUNET_PQ_query_param_end
};
@ -1976,6 +1986,8 @@ postgres_reserves_in_insert (void *cls,
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session the database session handle
* @param exchange_account_name name of the section in the exchange's configuration
* for the account that we are tracking here
* @param[out] wire_reference set to unique reference identifying the wire transfer (binary blob)
* @param[out] wire_reference_size set to number of bytes in @a wire_reference
* @return transaction status code
@ -1983,10 +1995,12 @@ postgres_reserves_in_insert (void *cls,
static enum GNUNET_DB_QueryStatus
postgres_get_latest_reserve_in_reference (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const char *exchange_account_name,
void **wire_reference,
size_t *wire_reference_size)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (exchange_account_name),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@ -2222,7 +2236,7 @@ add_bank_to_exchange (void *cls,
&bt->amount),
TALER_PQ_result_spec_absolute_time ("execution_date",
&bt->execution_date),
TALER_PQ_result_spec_json ("sender_account_details",
GNUNET_PQ_result_spec_string ("sender_account_details",
&bt->sender_account_details),
GNUNET_PQ_result_spec_end
};
@ -2389,7 +2403,7 @@ add_exchange_to_bank (void *cls,
&closing->closing_fee),
TALER_PQ_result_spec_absolute_time ("execution_date",
&closing->execution_date),
TALER_PQ_result_spec_json ("receiver_account",
GNUNET_PQ_result_spec_string ("receiver_account",
&closing->receiver_account_details),
GNUNET_PQ_result_spec_auto_from_type ("wtid",
&closing->wtid),
@ -4178,8 +4192,6 @@ handle_wt_result (void *cls,
struct TALER_Amount amount_with_fee;
struct TALER_Amount deposit_fee;
json_t *wire;
json_t *t;
const char *wire_method;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id", &rowid),
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", &h_contract_terms),
@ -4202,25 +4214,11 @@ handle_wt_result (void *cls,
ctx->status = GNUNET_SYSERR;
return;
}
t = json_object_get (wire, "type");
if (NULL == t)
{
GNUNET_break (0);
ctx->status = GNUNET_SYSERR;
return;
}
wire_method = json_string_value (t);
if (NULL == wire_method)
{
GNUNET_break (0);
ctx->status = GNUNET_SYSERR;
return;
}
ctx->cb (ctx->cb_cls,
rowid,
&merchant_pub,
wire_method,
&h_wire,
wire,
exec_time,
&h_contract_terms,
&coin_pub,
@ -4580,13 +4578,13 @@ reserve_expired_cb (void *cls,
for (unsigned int i=0;i<num_results;i++)
{
struct GNUNET_TIME_Absolute exp_date;
json_t *account_details;
char *account_details;
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_Amount remaining_balance;
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_absolute_time ("expiration_date",
&exp_date),
TALER_PQ_result_spec_json ("account_details",
GNUNET_PQ_result_spec_string ("account_details",
&account_details),
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
&reserve_pub),
@ -4674,7 +4672,7 @@ postgres_insert_reserve_closed (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_ReservePublicKeyP *reserve_pub,
struct GNUNET_TIME_Absolute execution_date,
const json_t *receiver_account,
const char *receiver_account,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *closing_fee)
@ -4684,7 +4682,7 @@ postgres_insert_reserve_closed (void *cls,
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
TALER_PQ_query_param_absolute_time (&execution_date),
GNUNET_PQ_query_param_auto_from_type (wtid),
TALER_PQ_query_param_json (receiver_account),
GNUNET_PQ_query_param_string (receiver_account),
TALER_PQ_query_param_amount (amount_with_fee),
TALER_PQ_query_param_amount (closing_fee),
GNUNET_PQ_query_param_end
@ -4946,6 +4944,7 @@ postgres_gc (void *cls)
long_ago = GNUNET_TIME_absolute_subtract (now,
GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
10));
/* FIXME: use GNUNET_PQ_connect_with_cfg instead? */
conn = GNUNET_PQ_connect (pc->connection_cfg_str);
if (NULL == conn)
return GNUNET_SYSERR;
@ -5421,7 +5420,7 @@ reserves_in_serial_helper_cb (void *cls,
{
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_Amount credit;
json_t *sender_account_details;
char *sender_account_details;
struct GNUNET_TIME_Absolute execution_date;
uint64_t rowid;
void *wire_reference;
@ -5436,7 +5435,7 @@ reserves_in_serial_helper_cb (void *cls,
&credit),
TALER_PQ_result_spec_absolute_time("execution_date",
&execution_date),
TALER_PQ_result_spec_json ("sender_account_details",
GNUNET_PQ_result_spec_string ("sender_account_details",
&sender_account_details),
GNUNET_PQ_result_spec_uint64 ("reserve_in_serial_id",
&rowid),
@ -5943,7 +5942,7 @@ reserve_closed_serial_helper_cb (void *cls,
{
uint64_t rowid;
struct TALER_ReservePublicKeyP reserve_pub;
json_t *receiver_account;
char *receiver_account;
struct TALER_WireTransferIdentifierRawP wtid;
struct TALER_Amount amount_with_fee;
struct TALER_Amount closing_fee;
@ -5957,7 +5956,7 @@ reserve_closed_serial_helper_cb (void *cls,
&execution_date),
GNUNET_PQ_result_spec_auto_from_type ("wtid",
&wtid),
TALER_PQ_result_spec_json ("receiver_account",
GNUNET_PQ_result_spec_string ("receiver_account",
&receiver_account),
TALER_PQ_result_spec_amount ("amount",
&amount_with_fee),
@ -6380,12 +6379,12 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"exchangedb-postgres",
"db_conn_str",
"CONFIG",
&pg->connection_cfg_str))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchangedb-postgres",
"db_conn_str");
"CONFIG");
GNUNET_free (pg);
return NULL;
}

View File

@ -5,7 +5,7 @@ DB = postgres
[exchangedb-postgres]
#The connection string the plugin has to use for connecting to the database
DB_CONN_STR = postgres:///talercheck
CONFIG = postgres:///talercheck
[exchangedb]

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015, 2016, 2017 GNUnet e.V.
Copyright (C) 2014-2018 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
@ -703,8 +703,8 @@ static void
cb_wt_never (void *cls,
uint64_t serial_id,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const char *wire_method,
const struct GNUNET_HashCode *h_wire,
const json_t *wire,
struct GNUNET_TIME_Absolute exec_time,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -747,8 +747,8 @@ static void
cb_wt_check (void *cls,
uint64_t rowid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const char *wire_method,
const struct GNUNET_HashCode *h_wire,
const json_t *wire,
struct GNUNET_TIME_Absolute exec_time,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -759,8 +759,9 @@ cb_wt_check (void *cls,
GNUNET_assert (0 == memcmp (merchant_pub,
&merchant_pub_wt,
sizeof (struct TALER_MerchantPublicKeyP)));
GNUNET_assert (0 == strcmp (wire_method,
"SEPA"));
GNUNET_assert (0 == strcmp (json_string_value (json_object_get (wire,
"url")),
"payto://sepa/DE67830654080004822650"));
GNUNET_assert (0 == memcmp (h_wire,
&h_wire_wt,
sizeof (struct GNUNET_HashCode)));
@ -842,7 +843,7 @@ deposit_cb (void *cls,
deposit_rowid = rowid;
if (NULL != wire)
GNUNET_assert (GNUNET_OK ==
TALER_JSON_hash (wire,
TALER_JSON_wire_signature_hash (wire,
&h_wire));
if ( (0 != memcmp (merchant_pub,
&deposit->merchant_pub,
@ -960,7 +961,7 @@ audit_reserve_in_cb (void *cls,
uint64_t rowid,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *credit,
const json_t *sender_account_details,
const char *sender_account_details,
const void *wire_reference,
size_t wire_reference_size,
struct GNUNET_TIME_Absolute execution_date)
@ -1156,8 +1157,6 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
}
static json_t *wire_out_account;
static struct TALER_Amount wire_out_amount;
@ -1204,11 +1203,11 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit)
{
auditor_row_cnt = 0;
memset (&wire_out_wtid, 42, sizeof (wire_out_wtid));
memset (&wire_out_wtid,
42,
sizeof (wire_out_wtid));
wire_out_date = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&wire_out_date);
wire_out_account = json_loads ("{ \"account\":\"1\" }", 0, NULL);
GNUNET_assert (NULL != wire_out_account);
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1",
&wire_out_amount));
@ -1262,13 +1261,25 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
/* Now let's fix the transient constraint violation by
putting in the WTID into the wire_out table */
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
{
json_t *wire_out_account;
wire_out_account = json_pack ("{s:s,s:s}",
"url", "payto://x-taler-bank/localhost:8080/1",
"salt", "this-is-my-salt");
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->store_wire_transfer_out (plugin->cls,
session,
wire_out_date,
&wire_out_wtid,
wire_out_account,
&wire_out_amount));
&wire_out_amount))
{
json_decref (wire_out_account);
FAILIF (1);
}
json_decref (wire_out_account);
}
/* And now the commit should still succeed! */
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->commit (plugin->cls,
@ -1365,7 +1376,7 @@ wire_missing_cb (void *cls,
if (NULL != wire)
GNUNET_assert (GNUNET_OK ==
TALER_JSON_hash (wire,
TALER_JSON_wire_signature_hash (wire,
&h_wire));
else
memset (&h_wire,
@ -1500,16 +1511,8 @@ run (void *cls)
struct TALER_EXCHANGEDB_TransactionList *tl;
struct TALER_EXCHANGEDB_TransactionList *tlp;
json_t *wire;
json_t *sndr;
const char *sndr = "payto://x-taler-bank/localhost:8080/1";
unsigned int matched;
const char * const json_wire_str =
"{ \"type\":\"SEPA\", \
\"IBAN\":\"DE67830654080004822650\", \
\"name\":\"GNUnet e.V.\", \
\"bic\":\"GENODEF1SLR\", \
\"wire_transfer_deadline\":\"1449930207000\", \
\"r\":123456789, \
\"address\": \"foobar\"}";
unsigned int cnt;
void *rr;
size_t rr_size;
@ -1518,9 +1521,11 @@ run (void *cls)
dkp = NULL;
rh = NULL;
wire = NULL;
session = NULL;
deposit.coin.denom_sig.rsa_signature = NULL;
wire = json_pack ("{s:s, s:s}",
"url", "payto://sepa/DE67830654080004822650",
"salt", "this-is-a-salt-value");
ZR_BLK (&cbc);
ZR_BLK (&cbc2);
if (NULL ==
@ -1576,11 +1581,10 @@ run (void *cls)
&amount_with_fee));
result = 4;
sndr = json_loads ("{ \"account\":\"1\" }", 0, NULL);
GNUNET_assert (NULL != sndr);
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->get_latest_reserve_in_reference (plugin->cls,
session,
"account-1",
&rr,
&rr_size));
now = GNUNET_TIME_absolute_get ();
@ -1592,11 +1596,13 @@ run (void *cls)
&value,
now,
sndr,
"account-1",
"TEST",
4));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_latest_reserve_in_reference (plugin->cls,
session,
"account-1",
&rr,
&rr_size));
FAILIF (4 != rr_size);
@ -1617,23 +1623,25 @@ run (void *cls)
&value,
now,
sndr,
"account-1",
"TEST2",
5));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_latest_reserve_in_reference (plugin->cls,
session,
"account-1",
&rr,
&rr_size));
GNUNET_free (rr);
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_latest_reserve_in_reference (plugin->cls,
session,
"account-1",
&rr,
&rr_size));
FAILIF (5 != rr_size);
FAILIF (0 != memcmp ("TEST2", rr, 5));
GNUNET_free (rr);
json_decref (sndr);
FAILIF (GNUNET_OK !=
check_reserve (session,
&reserve_pub,
@ -1731,7 +1739,6 @@ run (void *cls)
TALER_amount_add (&amount_with_fee,
&value,
&value));
sndr = json_loads ("{ \"account\":\"1\" }", 0, NULL);
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":0.000010",
&fee_closing));
@ -1753,7 +1760,6 @@ run (void *cls)
0,
value.currency));
json_decref (sndr);
result = 7;
qs = plugin->get_reserve_history (plugin->cls,
session,
@ -1840,16 +1846,17 @@ run (void *cls)
FAILIF (3 != auditor_row_cnt);
/* Tests for deposits */
memset (&deposit, 0, sizeof (deposit));
memset (&deposit,
0,
sizeof (deposit));
RND_BLK (&deposit.coin.coin_pub);
deposit.coin.denom_pub = dkp->pub;
deposit.coin.denom_sig = cbc.sig;
RND_BLK (&deposit.csig);
RND_BLK (&deposit.merchant_pub);
RND_BLK (&deposit.h_contract_terms);
wire = json_loads (json_wire_str, 0, NULL);
GNUNET_assert (GNUNET_OK ==
TALER_JSON_hash (wire,
TALER_JSON_wire_signature_hash (wire,
&deposit.h_wire));
deposit.receiver_wire_account = wire;
deposit.amount_with_fee = value;
@ -1897,7 +1904,8 @@ run (void *cls)
session,
&deposit.h_wire,
&deposit.merchant_pub,
&deposit_cb, &deposit,
&deposit_cb,
&deposit,
2));
sleep (2); /* giv deposit time to be ready */
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
@ -2179,8 +2187,9 @@ run (void *cls)
result = 0;
drop:
if (NULL != wire)
json_decref (wire);
if (0 != result)
plugin->rollback (plugin->cls,
session);
if (NULL != rh)
plugin->free_reserve_history (plugin->cls,
rh);
@ -2196,6 +2205,7 @@ run (void *cls)
if (NULL != cbc2.sig.rsa_signature)
GNUNET_CRYPTO_rsa_signature_free (cbc2.sig.rsa_signature);
dkp = NULL;
json_decref (wire);
TALER_EXCHANGEDB_plugin_unload (plugin);
plugin = NULL;
}

View File

@ -382,6 +382,7 @@ struct TALER_AUDITORDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param master_pub master key of the exchange
* @param account_name name of the wire account we are auditing
* @param pp where is the auditor in processing
* @param in_wire_off how far are we in the incoming wire transaction history
* @param out_wire_off how far are we in the outgoing wire transaction history
@ -392,6 +393,7 @@ struct TALER_AUDITORDB_Plugin
(*insert_wire_auditor_progress)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
const char *account_name,
const struct TALER_AUDITORDB_WireProgressPoint *pp,
const void *in_wire_off,
const void *out_wire_off,
@ -405,6 +407,7 @@ struct TALER_AUDITORDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param master_pub master key of the exchange
* @param account_name name of the wire account we are auditing
* @param pp where is the auditor in processing
* @param in_wire_off how far are we in the incoming wire transaction history
* @param out_wire_off how far are we in the outgoing wire transaction history
@ -415,6 +418,7 @@ struct TALER_AUDITORDB_Plugin
(*update_wire_auditor_progress)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
const char *account_name,
const struct TALER_AUDITORDB_WireProgressPoint *pp,
const void *in_wire_off,
const void *out_wire_off,
@ -428,6 +432,7 @@ struct TALER_AUDITORDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param master_pub master key of the exchange
* @param account_name name of the wire account we are auditing
* @param[out] pp set to where the auditor is in processing
* @param[out] in_wire_off how far are we in the incoming wire transaction history
* @param[out] out_wire_off how far are we in the outgoing wire transaction history
@ -438,6 +443,7 @@ struct TALER_AUDITORDB_Plugin
(*get_wire_auditor_progress)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
const char *account_name,
struct TALER_AUDITORDB_WireProgressPoint *pp,
void **in_wire_off,
void **out_wire_off,

View File

@ -217,9 +217,9 @@ struct TALER_BANK_TransferDetails
char *wire_transfer_subject;
/**
* The other account that was involved
* payto://-URL of the other account that was involved
*/
json_t *account_details;
char *account_url;
};
@ -347,4 +347,29 @@ void
TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh);
/**
* Convenience method for parsing configuration section with bank
* authentication data. The section must contain an option
* "METHOD", plus other options that depend on the METHOD specified.
*
* @param cfg configuration to parse
* @param section the section with the configuration data
* @param auth[out] set to the configuration data found
* @return #GNUNET_OK on success
*/
int
TALER_BANK_auth_parse_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *section,
struct TALER_BANK_AuthenticationData *auth);
/**
* Free memory inside of @a auth (but not auth itself).
* Dual to #TALER_BANK_auth_parse_cfg().
*
* @param auth authentication data to free
*/
void
TALER_BANK_auth_free (struct TALER_BANK_AuthenticationData *auth);
#endif /* _TALER_BANK_SERVICE_H */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015, 2016, 2017 Taler Systems SA
Copyright (C) 2014-2018 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
@ -730,4 +730,51 @@ TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc,
const struct TALER_Amount *amount_with_fee);
/* **************** /wire account offline signing **************** */
/**
* Compute the hash of the given wire details. The resulting
* hash is what is put into the contract.
*
* @param payto_url bank account
* @param salt salt used to eliminate brute-force inversion
* @param hc[out] set to the hash
*/
void
TALER_wire_signature_hash (const char *payto_url,
const char *salt,
struct GNUNET_HashCode *hc);
/**
* Check the signature in @a wire_s.
*
* @param payto_url URL that is signed
* @param salt the salt used to salt the @a payto_url when hashing
* @param master_pub master public key of the exchange
* @param master_sig signature of the exchange
* @return #GNUNET_OK if signature is valid
*/
int
TALER_wire_signature_check (const char *payto_url,
const char *salt,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig);
/**
* Create a signed wire statement for the given account.
*
* @param payto_url account specification
* @param salt the salt used to salt the @a payto_url when hashing
* @param master_priv private key to sign with
* @param master_sig[out] where to write the signature
*/
void
TALER_wire_signature_make (const char *payto_url,
const char *salt,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig);
#endif

View File

@ -165,7 +165,15 @@ enum TALER_ErrorCode
*/
TALER_EC_INTERNAL_LOGIC_ERROR = 1011,
/**
* The method specified in a payto:// URL is not one we expected.
*/
TALER_EC_PAYTO_WRONG_METHOD = 1012,
/**
* The PAYTO URL is malformed.
*/
TALER_EC_PAYTO_MALFORMED = 1013,
/* ********** request-specific error codes ************* */
@ -1535,6 +1543,20 @@ enum TALER_ErrorCode
*/
TALER_EC_TEST_RSA_SIGN_ERROR = 4005,
/**
* The JSON in the server's response was malformed. This response
* is provided with HTTP status code of 0.
*/
TALER_EC_SERVER_JSON_INVALID = 5000,
/**
* A signature in the server's response was malformed. This response
* is provided with HTTP status code of 0.
*/
TALER_EC_SERVER_SIGNATURE_INVALID = 5001,
/* *************** Taler BANK/FAKEBANK error codes *************** */
/**

View File

@ -472,40 +472,32 @@ struct TALER_EXCHANGE_WireAggregateFees
/**
* Function called with information about the wire fees
* for each wire method.
*
* @param cls closure
* @param wire_method name of the wire method (i.e. "sepa")
* @param fees fee structure for this method
* Information about a wire account of the exchange.
*/
typedef void
(*TALER_EXCHANGE_WireFeeCallback)(void *cls,
const char *wire_method,
const struct TALER_EXCHANGE_WireAggregateFees *fees);
struct TALER_EXCHANGE_WireAccount
{
/**
* payto://-URL of the exchange.
*/
const char *url;
/**
* Obtain information about wire fees encoded in @a obj
* by wire method.
*
* @param master_pub public key to use to verify signatures, NULL to not verify
* @param obj wire information as encoded in the #TALER_EXCHANGE_WireResultCallback
* @param cb callback to invoke for the fees
* @param cb_cls closure for @a cb
* @return #GNUNET_OK in success, #GNUNET_SYSERR if @a obj is ill-formed
* Salt used to generate @e master_sig.
*/
int
TALER_EXCHANGE_wire_get_fees (const struct TALER_MasterPublicKeyP *master_pub,
const json_t *obj,
TALER_EXCHANGE_WireFeeCallback cb,
void *cb_cls);
const char *salt;
/**
* @brief A Wire format inquiry handle
* Signature of the exchange over the account (was checked by the API).
*/
struct TALER_EXCHANGE_WireHandle;
struct TALER_MasterSignatureP master_sig;
/**
* Linked list of wire fees the exchange charges for
* accounts of the wire method matching @e url.
*/
const struct TALER_EXCHANGE_WireAggregateFees *fees;
};
/**
@ -519,15 +511,21 @@ struct TALER_EXCHANGE_WireHandle;
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful request;
* 0 if the exchange's reply is bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param obj the received JSON reply, if successful this should be the wire
* format details as provided by /wire, or NULL if the
* reply was not in JSON format.
* @param accounts_len length of the @a accounts array
* @param accounts list of wire accounts of the exchange, NULL on error
*/
typedef void
(*TALER_EXCHANGE_WireResultCallback) (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const json_t *obj);
unsigned int accounts_len,
const struct TALER_EXCHANGE_WireAccount *accounts);
/**
* @brief A Wire format inquiry handle
*/
struct TALER_EXCHANGE_WireHandle;
/**
@ -843,9 +841,9 @@ struct TALER_EXCHANGE_ReserveHistory
*/
struct {
/**
* Sender account information for the incoming transfer.
* Sender account payto://-URL of the incoming transfer.
*/
json_t *sender_account_details;
char *sender_url;
/**
* Information that uniquely identifies the wire transfer.

View File

@ -406,4 +406,72 @@ TALER_EXCHANGEDB_fees_write (const char *filename,
void
TALER_EXCHANGEDB_fees_free (struct TALER_EXCHANGEDB_AggregateFees *af);
/**
* Information about an account from the configuration.
*/
struct TALER_EXCHANGEDB_AccountInfo
{
/**
* Section in the configuration file that specifies the
* account. Must start with "account-".
*/
const char *section_name;
/**
* Name of the wire plugin that should be used to access
* the account.
*/
const char *plugin_name;
/**
* payto://-URL of the account.
*/
const char *payto_url;
/**
* Filename containing the signed /wire response, or NULL
* if not given.
*/
const char *wire_response_filename;
/**
* #GNUNET_YES if this account is enabed to be debited
* by the taler-exchange-aggregator.
*/
int debit_enabled;
/**
* #GNUNET_YES if this account is enabed to be credited by wallets
* and needs to be watched by the taler-exchange-wirewatch.
* Also, the account will only be included in /wire if credit
* is enabled.
*/
int credit_enabled;
};
/**
* Function called with information about a wire account.
*
* @param cls closure
* @param ai account information
*/
typedef void
(*TALER_EXCHANGEDB_AccountCallback)(void *cls,
const struct TALER_EXCHANGEDB_AccountInfo *ai);
/**
* Parse the configuration to find account information.
*
* @param cfg configuration to use
* @param cb callback to invoke
* @param cb_cls closure for @a cb
*/
void
TALER_EXCHANGEDB_find_accounts (const struct GNUNET_CONFIGURATION_Handle *cfg,
TALER_EXCHANGEDB_AccountCallback cb,
void *cb_cls);
#endif

View File

@ -21,7 +21,6 @@
*/
#ifndef TALER_EXCHANGEDB_PLUGIN_H
#define TALER_EXCHANGEDB_PLUGIN_H
#include <jansson.h>
#include <gnunet/gnunet_util_lib.h>
#include <gnunet/gnunet_db_lib.h>
@ -52,9 +51,10 @@ struct TALER_EXCHANGEDB_BankTransfer
struct GNUNET_TIME_Absolute execution_date;
/**
* Detailed wire information about the sending account.
* Detailed wire information about the sending account
* in "payto://" format.
*/
json_t *sender_account_details;
char *sender_account_details;
/**
* Data uniquely identifying the wire transfer (wire transfer-type specific)
@ -97,9 +97,10 @@ struct TALER_EXCHANGEDB_ClosingTransfer
struct GNUNET_TIME_Absolute execution_date;
/**
* Detailed wire information about the receiving account.
* Detailed wire information about the receiving account
* in payto://-format.
*/
json_t *receiver_account_details;
char *receiver_account_details;
/**
* Detailed wire transfer information that uniquely identifies the
@ -361,6 +362,7 @@ struct TALER_EXCHANGEDB_Deposit
/**
* Detailed information about the receiver for executing the transaction.
* Includes URL in payto://-format and salt.
*/
json_t *receiver_wire_account;
@ -638,7 +640,8 @@ struct TALER_EXCHANGEDB_Session;
* @param h_contract_terms hash of the proposal data known to merchant and customer
* @param wire_deadline by which the merchant adviced that he would like the
* wire transfer to be executed
* @param receiver_wire_account wire details for the merchant, NULL from iterate_matching_deposits()
* @param receiver_wire_account wire details for the merchant, includes
* 'url' in payto://-format; NULL from iterate_matching_deposits()
* @return transaction status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT to continue to iterate
*/
typedef enum GNUNET_DB_QueryStatus
@ -687,7 +690,8 @@ typedef void
* to get a refund
* @param wire_deadline by which the merchant adviced that he would like the
* wire transfer to be executed
* @param receiver_wire_account wire details for the merchant, NULL from iterate_matching_deposits()
* @param receiver_wire_account wire details for the merchant including 'url' in payto://-format;
* NULL from iterate_matching_deposits()
* @param done flag set if the deposit was already executed (or not)
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
@ -837,7 +841,7 @@ typedef int
* @param rowid unique serial ID for the refresh session in our DB
* @param reserve_pub public key of the reserve (also the WTID)
* @param credit amount that was received
* @param sender_account_details information about the sender's bank account
* @param sender_account_details information about the sender's bank account, in payto://-format
* @param wire_reference unique identifier for the wire transfer (plugin-specific format)
* @param wire_reference_size number of bytes in @a wire_reference
* @param execution_date when did we receive the funds
@ -848,7 +852,7 @@ typedef int
uint64_t rowid,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *credit,
const json_t *sender_account_details,
const char *sender_account_details,
const void *wire_reference,
size_t wire_reference_size,
struct GNUNET_TIME_Absolute execution_date);
@ -923,8 +927,8 @@ typedef void
* @param cls closure
* @param rowid which row in the table is the information from (for diagnostics)
* @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
* @param wire_method which wire plugin was used for the transfer?
* @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
* @param account_details which account did the transfer go to?
* @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
* @param h_contract_terms which proposal was this payment about
* @param coin_pub which public key was this payment about
@ -935,8 +939,8 @@ typedef void
(*TALER_EXCHANGEDB_WireTransferDataCallback)(void *cls,
uint64_t rowid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const char *wire_method,
const struct GNUNET_HashCode *h_wire,
const json_t *account_details,
struct GNUNET_TIME_Absolute exec_time,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -952,7 +956,7 @@ typedef void
* @param rowid identifier of the respective row in the database
* @param date timestamp of the wire transfer (roughly)
* @param wtid wire transfer subject
* @param wire wire transfer details of the receiver
* @param wire wire transfer details of the receiver, including "url" in payto://-format
* @param amount amount that was wired
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop iteration
*/
@ -1019,7 +1023,7 @@ typedef int
* @param amount_with_fee how much did we debit the reserve
* @param closing_fee how much did we charge for closing the reserve
* @param reserve_pub public key of the reserve
* @param receiver_account where did we send the funds
* @param receiver_account where did we send the funds, in payto://-format
* @param wtid identifier used for the wire transfer
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
@ -1030,7 +1034,7 @@ typedef int
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *closing_fee,
const struct TALER_ReservePublicKeyP *reserve_pub,
const json_t *receiver_account,
const char *receiver_account,
const struct TALER_WireTransferIdentifierRawP *wtid);
@ -1040,7 +1044,7 @@ typedef int
* @param cls closure
* @param reserve_pub public key of the reserve
* @param left amount left in the reserve
* @param account_details information about the reserve's bank account
* @param account_details information about the reserve's bank account, in payto://-format
* @param expiration_date when did the reserve expire
* @return transaction status code to pass on
*/
@ -1048,7 +1052,7 @@ typedef enum GNUNET_DB_QueryStatus
(*TALER_EXCHANGEDB_ReserveExpiredCallback)(void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *left,
const json_t *account_details,
const char *account_details,
struct GNUNET_TIME_Absolute expiration_date);
@ -1082,7 +1086,7 @@ typedef void
* @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 wire where should the funds be wired
* @param wire where should the funds be wired, including 'url' in payto://-format
* @param deadline what was the requested wire transfer deadline
* @param tiny did the exchange defer this transfer because it is too small?
* @param done did the exchange claim that it made a transfer?
@ -1259,7 +1263,7 @@ struct TALER_EXCHANGEDB_Plugin
* @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 information about the sender's bank account
* @param sender_account_details information about the sender's bank account, in payto://-format
* @param wire_reference unique reference identifying the wire transfer (binary blob)
* @param wire_reference_size number of bytes in @a wire_reference
* @return transaction status code
@ -1270,7 +1274,8 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *balance,
struct GNUNET_TIME_Absolute execution_time,
const json_t *sender_account_details,
const char *sender_account_details,
const char *exchange_account_name,
const void *wire_reference,
size_t wire_reference_size);
@ -1287,6 +1292,7 @@ struct TALER_EXCHANGEDB_Plugin
enum GNUNET_DB_QueryStatus
(*get_latest_reserve_in_reference)(void *cls,
struct TALER_EXCHANGEDB_Session *db,
const char *exchange_account_name,
void **wire_reference,
size_t *wire_reference_size);
@ -1781,7 +1787,7 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database connection
* @param reserve_pub which reserve is this about?
* @param execution_date when did we perform the transfer?
* @param receiver_account to which account do we transfer?
* @param receiver_account to which account do we transfer, in payto://-format
* @param wtid identifier for the wire transfer
* @param amount_with_fee amount we charged to the reserve
* @param closing_fee how high is the closing fee
@ -1792,7 +1798,7 @@ struct TALER_EXCHANGEDB_Plugin
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_ReservePublicKeyP *reserve_pub,
struct GNUNET_TIME_Absolute execution_date,
const json_t *receiver_account,
const char *receiver_account,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *closing_fee);
@ -1869,7 +1875,8 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database connection
* @param date time of the wire transfer
* @param wtid subject of the wire transfer
* @param wire_account details about the receiver account of the wire transfer
* @param wire_account details about the receiver account of the wire transfer,
* including 'url' in payto://-format
* @param amount amount that was transmitted
* @return transaction status code
*/

View File

@ -126,6 +126,65 @@ enum TALER_ErrorCode
TALER_JSON_get_error_code (const json_t *json);
/* **************** /wire account offline signing **************** */
/**
* Compute the hash of the given wire details. The resulting
* hash is what is put into the contract.
*
* @param wire_s wire details to hash
* @param hc[out] set to the hash
* @return #GNUNET_OK on success, #GNUNET_SYSERR if @a wire_s is malformed
*/
int
TALER_JSON_wire_signature_hash (const json_t *wire_s,
struct GNUNET_HashCode *hc);
/**
* Check the signature in @a wire_s.
*
* @param wire_s signed wire information of an exchange
* @param master_pub master public key of the exchange
* @return #GNUNET_OK if signature is valid
*/
int
TALER_JSON_wire_signature_check (const json_t *wire_s,
const struct TALER_MasterPublicKeyP *master_pub);
/**
* Create a signed wire statement for the given account.
*
* @param payto_url account specification
* @param master_priv private key to sign with, NULL to not sign
*/
json_t *
TALER_JSON_wire_signature_make (const char *payto_url,
const struct TALER_MasterPrivateKeyP *master_priv);
/**
* Obtain the wire method associated with the given
* wire account details. @a wire_s must contain a payto://-URL
* under 'url'.
*
* @return NULL on error
*/
char *
TALER_JSON_wire_to_method (const json_t *wire_s);
/**
* Obtain the payto://-URL associated with the given
* wire account details. @a wire_s must contain a payto://-URL
* under 'url'.
*
* @return NULL on error
*/
char *
TALER_JSON_wire_to_payto (const json_t *wire_s);
#endif /* TALER_JSON_LIB_H_ */
/* End of taler_json_lib.h */

View File

@ -61,18 +61,6 @@
*/
#define TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY 1025
/**
* Signature where the Exchange confirms its SEPA details in
* the /wire response.
*/
#define TALER_SIGNATURE_MASTER_SEPA_DETAILS 1026
/**
* Signature where the Exchange confirms its TEST details in
* the /wire response.
*/
#define TALER_SIGNATURE_MASTER_TEST_DETAILS 1027
/**
* Fees charged per (aggregate) wire transfer to the merchant.
*/
@ -83,6 +71,12 @@
*/
#define TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED 1029
/**
* Signature where the Exchange confirms its SEPA details in
* the /wire response.
*/
#define TALER_SIGNATURE_MASTER_WIRE_DETAILS 1030
/*********************************************/
/* Exchange online signatures (with signing key) */
/*********************************************/
@ -891,16 +885,15 @@ struct TALER_MasterWireDetailsPS
{
/**
* Purpose is #TALER_SIGNATURE_MASTER_SEPA_DETAILS or
* #TALER_SIGNATURE_MASTER_TEST_DETAILS.
* Purpose is #TALER_SIGNATURE_MASTER_WIRE_DETAILS.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Hash over the account holder's name, IBAN and BIC
* code (all as 0-terminated strings).
* Hash over the account holder's payto:// URL and
* the salt, as done by #TALER_wire_signature_hash().
*/
struct GNUNET_HashCode h_sepa_details GNUNET_PACKED;
struct GNUNET_HashCode h_wire_details GNUNET_PACKED;
};

View File

@ -66,20 +66,22 @@
#define TALER_TESTING_MAKE_TRAIT_ROW_ID(data) \
TALER_TESTING_make_trait_uint64 (3, data)
/**
* Allocate and return a piece of wire-details. Mostly, it adds
* the bank_url to the JSON.
* Allocate and return a piece of wire-details. Combines
* the @a account_no and the @a bank_url to a
* @a payto://-URL and adds some salt to create the JSON.
*
* @param template the wire-details template.
* @param account_no account number
* @param bank_url the bank_url
*
* @return the filled out and stringified wire-details. To
* be manually free'd.
* @return JSON describing the account, including the
* payto://-URL of the account, must be manually decref'd
*/
char *
TALER_TESTING_make_wire_details (const char *template,
json_t *
TALER_TESTING_make_wire_details (unsigned long long account_no,
const char *bank_url);
/**
* Find denomination key matching the given amount.
*
@ -130,10 +132,12 @@ TALER_TESTING_url_port_free (const char *url);
* If everything is OK, return the configured URL of the fakebank.
*
* @param config_filename configuration file to use
* @param config_section which account to use (must match x-taler-bank)
* @return NULL on error, fakebank URL otherwise
*/
char *
TALER_TESTING_prepare_fakebank (const char *config_filename);
TALER_TESTING_prepare_fakebank (const char *config_filename,
const char *config_section);
/* ******************* Generic interpreter logic ************ */
@ -613,8 +617,8 @@ TALER_TESTING_cmd_status (const char *label,
* coins, this parameter selects which one in that array
* This value is currently ignored, as only one-coin
* withdrawals are implemented.
* @param wire_details bank details of the merchant performing the
* deposit
* @param wire_details JSON including payto://-URL of the merchant performing the
* deposit, reference is captured by this command
* @param contract_terms contract terms to be signed over by the
* coin
* @param refund_deadline refund deadline
@ -630,7 +634,7 @@ TALER_TESTING_cmd_deposit
struct TALER_EXCHANGE_Handle *exchange,
const char *coin_reference,
unsigned int coin_index,
char *wire_details,
json_t *wire_details,
const char *contract_terms,
struct GNUNET_TIME_Relative refund_deadline,
const char *amount,
@ -1209,7 +1213,7 @@ int
TALER_TESTING_get_trait_wire_details
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
const char **wire_details);
const json_t **wire_details);
/**
@ -1223,7 +1227,7 @@ TALER_TESTING_get_trait_wire_details
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_wire_details
(unsigned int index,
const char *wire_details);
const json_t *wire_details);
/**
* Obtain a private key from a "peer". Used e.g. to obtain
@ -1300,11 +1304,11 @@ TALER_TESTING_get_trait_transfer_subject
/**
* Offer wire details in a trait.
* Offer wire wire transfer subject in a trait.
*
* @param index always (?) zero, as one command sticks
* to one bank account
* @param wire_details wire details to offer
* @param transfer_subject wire transfer subject to offer
* @return the trait, to be put in the traits array of the command
*/
struct TALER_TESTING_Trait

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2016 GNUnet e.V.
Copyright (C) 2016,2018 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,6 +25,16 @@
#include "taler_wire_plugin.h"
/**
* Obtain the payment method from a @a payto_url
*
* @param payto_url the URL to parse
* @return NULL on error (malformed @a payto_url)
*/
char *
TALER_WIRE_payto_get_method (const char *payto_url);
/**
* Load a WIRE plugin.
*
@ -45,28 +55,4 @@ void
TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin);
/**
* Signature of a function to be called on each enabled
* wire plugin.
*
* @param cls closure
* @param name name of the enabled plugin
*/
typedef void
(*TALER_WIRE_EnabledCallback)(void *cls,
const char *name);
/**
* Check which wire plugins are enabled in @a cfg and call @a cb for each one.
*
* @param cfg configuration to use
* @param cb callback to invoke
* @param cb_cls closure for @a cb
*/
void
TALER_WIRE_find_enabled (const struct GNUNET_CONFIGURATION_Handle *cfg,
TALER_WIRE_EnabledCallback cb,
void *cb_cls);
#endif

View File

@ -22,7 +22,6 @@
#define TALER_WIRE_PLUGIN_H
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include "taler_util.h"
#include "taler_error_codes.h"
#include "taler_bank_service.h" /* for `enum TALER_BANK_Direction` and `struct TALER_BANK_TransferDetails` */
@ -72,9 +71,9 @@ struct TALER_WIRE_TransferDetails
char *wtid_s;
/**
* The other account that was involved
* payto://-URL of the other account that was involved
*/
json_t *account_details;
char *account_url;
};
@ -160,6 +159,13 @@ struct TALER_WIRE_Plugin
*/
char *library_name;
/**
* Which wire method (payto://METHOD/") is supported by this plugin?
* For example, "sepa" or "x-taler-bank".
*/
const char *method;
/**
* Round amount DOWN to the amount that can be transferred via the wire
* method. For example, Taler may support 0.000001 EUR as a unit of
@ -177,62 +183,27 @@ struct TALER_WIRE_Plugin
/**
* Obtain wire transfer details in the plugin-specific format
* from the configuration.
*
* @param cls closure
* @param cfg configuration with details about wire accounts
* @param account_name which section in the configuration should we parse
* @return NULL if @a cfg fails to have valid wire details for @a account_name
*/
json_t *
(*get_wire_details)(void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *account_name);
/**
* Sign wire transfer details in the plugin-specific format.
*
* @param cls closure
* @param in wire transfer details in JSON format
* @param key private signing key to use
* @param salt salt to add
* @param[out] sig where to write the signature
* @return #GNUNET_OK on success
*/
int
(*sign_wire_details)(void *cls,
const json_t *in,
const struct TALER_MasterPrivateKeyP *key,
const struct GNUNET_HashCode *salt,
struct TALER_MasterSignatureP *sig);
/**
* Check if the given wire format JSON object is correctly formatted
* Check if the given payto:// URL is correctly formatted for this plugin
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param wire the JSON wire format object
* @param master_pub public key of the exchange to verify against
* @param[out] emsg set to an error message, unless we return #TALER_EC_NONE;
* error message must be freed by the caller using GNUNET_free()
* @param account_url the payto:// URL
* @return #TALER_EC_NONE if correctly formatted
*/
enum TALER_ErrorCode
(*wire_validate) (void *cls,
const json_t *wire,
const struct TALER_MasterPublicKeyP *master_pub,
char **emsg);
const char *account_url);
/**
* Prepare for exeuction of a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param wire valid wire account information
* @param origin_account_section configuration section specifying the origin
* account of the exchange to use
* @param destination_account_url payto:// URL identifying where to send the money
* @param amount amount to transfer, already rounded
* @param exchange_base_url base URL of this exchange
* @param exchange_base_url base URL of this exchange (included in subject
* to facilitate use of tracking API by merchant backend)
* @param wtid wire transfer identifier to use
* @param ptc function to call with the prepared data to persist
* @param ptc_cls closure for @a ptc
@ -240,13 +211,15 @@ struct TALER_WIRE_Plugin
*/
struct TALER_WIRE_PrepareHandle *
(*prepare_wire_transfer) (void *cls,
const json_t *wire,
const char *origin_account_section,
const char *destination_account_url,
const struct TALER_Amount *amount,
const char *exchange_base_url,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_WIRE_PrepareTransactionCallback ptc,
void *ptc_cls);
/**
* Abort preparation of a wire transfer. For example,
* because we are shutting down.
@ -305,6 +278,8 @@ struct TALER_WIRE_Plugin
* (with negative @a num_results).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param account_section specifies the configuration section which
* identifies the account for which we should get the history
* @param direction what kinds of wire transfers should be returned
* @param start_off from which row on do we want to get results, use NULL for the latest; exclusive
* @param start_off_len number of bytes in @a start_off
@ -316,6 +291,7 @@ struct TALER_WIRE_Plugin
*/
struct TALER_WIRE_HistoryHandle *
(*get_history) (void *cls,
const char *account_section,
enum TALER_BANK_Direction direction,
const void *start_off,
size_t start_off_len,
@ -323,6 +299,7 @@ struct TALER_WIRE_Plugin
TALER_WIRE_HistoryResultCallback hres_cb,
void *hres_cb_cls);
/**
* Cancel going over the account's history.
*
@ -345,6 +322,8 @@ struct TALER_WIRE_Plugin
* results returned by @e get_history.
*
* @param cls plugin's closure
* @param account_section specifies the configuration section which
* identifies the account to use to reject the transfer
* @param start_off offset of the wire transfer in plugin-specific format
* @param start_off_len number of bytes in @a start_off
* @param rej_cb function to call with the result of the operation
@ -353,11 +332,13 @@ struct TALER_WIRE_Plugin
*/
struct TALER_WIRE_RejectHandle *
(*reject_transfer)(void *cls,
const char *account_section,
const void *start_off,
size_t start_off_len,
TALER_WIRE_RejectTransferCallback rej_cb,
void *rej_cb_cls);
/**
* Cancel ongoing reject operation. Note that the rejection may still
* proceed. Basically, if this function is called, the rejection may

View File

@ -11,12 +11,14 @@ lib_LTLIBRARIES = \
libtalerjson_la_SOURCES = \
json.c \
json_helper.c
json_helper.c \
json_wire.c
libtalerjson_la_LDFLAGS = \
-version-info 1:0:1 \
-export-dynamic -no-undefined
libtalerjson_la_LIBADD = \
-lgnunetjson \
$(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetutil \
-ljansson \

193
src/json/json_wire.c Normal file
View File

@ -0,0 +1,193 @@
/*
This file is part of TALER
Copyright (C) 2018 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 json/json_wire.c
* @brief helper functions to generate or check /wire replies
* @author Christian Grothoff
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_wire_lib.h"
/**
* Compute the hash of the given wire details. The resulting
* hash is what is put into the contract.
*
* @param wire_s wire details to hash
* @param hc[out] set to the hash
* @return #GNUNET_OK on success, #GNUNET_SYSERR if @a wire_s is malformed
*/
int
TALER_JSON_wire_signature_hash (const json_t *wire_s,
struct GNUNET_HashCode *hc)
{
const char *payto_url;
const char *salt;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("url", &payto_url),
GNUNET_JSON_spec_string ("salt", &salt),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (wire_s,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
TALER_wire_signature_hash (payto_url,
salt,
hc);
return GNUNET_OK;
}
/**
* Check the signature in @a wire_s.
*
* @param wire_s signed wire information of an exchange
* @param master_pub master public key of the exchange
* @return #GNUNET_OK if signature is valid
*/
int
TALER_JSON_wire_signature_check (const json_t *wire_s,
const struct TALER_MasterPublicKeyP *master_pub)
{
const char *payto_url;
const char *salt;
struct TALER_MasterSignatureP master_sig;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("url", &payto_url),
GNUNET_JSON_spec_string ("salt", &salt),
GNUNET_JSON_spec_fixed_auto ("master_sig", &master_sig),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (wire_s,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
return TALER_wire_signature_check (payto_url,
salt,
master_pub,
&master_sig);
}
/**
* Create a signed wire statement for the given account.
*
* @param payto_url account specification
* @param master_priv private key to sign with, NULL to not sign
*/
json_t *
TALER_JSON_wire_signature_make (const char *payto_url,
const struct TALER_MasterPrivateKeyP *master_priv)
{
struct TALER_MasterSignatureP master_sig;
struct GNUNET_HashCode salt;
char *salt_str;
json_t *ret;
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&salt,
sizeof (salt));
salt_str = GNUNET_STRINGS_data_to_string_alloc (&salt,
sizeof (salt));
if (NULL != master_priv)
{
TALER_wire_signature_make (payto_url,
salt_str,
master_priv,
&master_sig);
ret = json_pack ("{s:s, s:s, s:o}",
"url", payto_url,
"salt", salt_str,
"master_sig", GNUNET_JSON_from_data_auto (&master_sig));
}
else
{
ret = json_pack ("{s:s, s:s}",
"url", payto_url,
"salt", salt_str);
}
GNUNET_free (salt_str);
return ret;
}
/**
* Obtain the wire method associated with the given
* wire account details. @a wire_s must contain a payto://-URL
* under 'url'.
*
* @return NULL on error
*/
char *
TALER_JSON_wire_to_payto (const json_t *wire_s)
{
json_t *payto_o;
const char *payto_str;
payto_o = json_object_get (wire_s,
"url");
if ( (NULL == payto_o) ||
(NULL == (payto_str = json_string_value (payto_o))) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Fatally malformed wire record encountered: lacks payto://-url\n");
return NULL;
}
return GNUNET_strdup (payto_str);
}
/**
* Obtain the wire method associated with the given
* wire account details. @a wire_s must contain a payto://-URL
* under 'url'.
*
* @return NULL on error
*/
char *
TALER_JSON_wire_to_method (const json_t *wire_s)
{
json_t *payto_o;
const char *payto_str;
payto_o = json_object_get (wire_s,
"url");
if ( (NULL == payto_o) ||
(NULL == (payto_str = json_string_value (payto_o))) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Fatally malformed wire record encountered: lacks payto://-url\n");
return NULL;
}
return TALER_WIRE_payto_get_method (payto_str);
}
/* end of json_wire.c */

View File

@ -43,6 +43,7 @@ libtalerutil_wallet_la_LDFLAGS = \
libtalerutil_la_SOURCES = \
amount.c \
crypto.c \
crypto_wire.c \
util.c \
os_installation.c

108
src/util/crypto_wire.c Normal file
View File

@ -0,0 +1,108 @@
/*
This file is part of TALER
Copyright (C) 2018 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 util/crypto_wire.c
* @brief functions for making and verifying /wire account signatures
* @author Christian Grothoff <christian@grothoff.org>
*/
#include "platform.h"
#include "taler_crypto_lib.h"
#include "taler_signatures.h"
/**
* Compute the hash of the given wire details. The resulting
* hash is what is put into the contract.
*
* @param payto_url bank account
* @param salt salt used to eliminate brute-force inversion
* @param hc[out] set to the hash
*/
void
TALER_wire_signature_hash (const char *payto_url,
const char *salt,
struct GNUNET_HashCode *hc)
{
GNUNET_assert (GNUNET_YES ==
GNUNET_CRYPTO_kdf (hc,
sizeof (*hc),
salt,
strlen (salt) + 1,
payto_url,
strlen (payto_url) + 1,
"wire-signature",
strlen ("wire-signature"),
NULL, 0));
}
/**
* Check the signature in @a wire_s.
*
* @param payto_url URL that is signed
* @param salt the salt used to salt the @a payto_url when hashing
* @param master_pub master public key of the exchange
* @param master_sig signature of the exchange
* @return #GNUNET_OK if signature is valid
*/
int
TALER_wire_signature_check (const char *payto_url,
const char *salt,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig)
{
struct TALER_MasterWireDetailsPS wd;
wd.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_DETAILS);
wd.purpose.size = htonl (sizeof (wd));
TALER_wire_signature_hash (payto_url,
salt,
&wd.h_wire_details);
return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_DETAILS,
&wd.purpose,
&master_sig->eddsa_signature,
&master_pub->eddsa_pub);
}
/**
* Create a signed wire statement for the given account.
*
* @param payto_url account specification
* @param salt the salt used to salt the @a payto_url when hashing
* @param master_priv private key to sign with
* @param master_sig[out] where to write the signature
*/
void
TALER_wire_signature_make (const char *payto_url,
const char *salt,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig)
{
struct TALER_MasterWireDetailsPS wd;
wd.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_DETAILS);
wd.purpose.size = htonl (sizeof (wd));
TALER_wire_signature_hash (payto_url,
salt,
&wd.h_wire_details);
GNUNET_assert (GNUNET_OK ==
GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
&wd.purpose,
&master_sig->eddsa_signature));
}
/* end of crypto_wire.c */

View File

@ -0,0 +1,101 @@
# This Makefile.am is in the public domain
AM_CPPFLAGS = -I$(top_srcdir)/src/include
if USE_COVERAGE
AM_CFLAGS = --coverage -O0
XLIB = -lgcov
endif
pkgcfgdir = $(prefix)/share/taler/config.d/
EXTRA_DIST = \
test_wire_plugin.conf \
test_wire_plugin_transactions_taler-bank.conf \
test_wire_plugin_key.priv \
test_wire_plugin_test.json \
test_wire_plugin_sepa.json
plugindir = $(libdir)/taler
plugin_LTLIBRARIES = \
libtaler_plugin_wire_ebics.la \
libtaler_plugin_wire_taler_bank.la
noinst_LTLIBRARIES = \
libtaler_plugin_wire_template.la
libtaler_plugin_wire_taler_bank_la_SOURCES = \
plugin_wire_taler-bank.c
libtaler_plugin_wire_taler_bank_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_wire_taler_bank_la_LDFLAGS = \
$(TALER_PLUGIN_LDFLAGS) \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetcurl \
-lgnunetutil $(XLIB)
libtaler_plugin_wire_ebics_la_SOURCES = \
plugin_wire_ebics.c
libtaler_plugin_wire_ebics_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_wire_ebics_la_LDFLAGS = \
$(TALER_PLUGIN_LDFLAGS) \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetjson \
-lgnunetutil $(XLIB)
libtaler_plugin_wire_template_la_SOURCES = \
plugin_wire_template.c
libtaler_plugin_wire_template_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_wire_template_la_LDFLAGS = \
$(TALER_PLUGIN_LDFLAGS) \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetutil $(XLIB)
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
TESTS = \
test_ebics_wireformat \
test_wire_plugin \
test_wire_plugin_transactions_taler_bank
check_PROGRAMS= \
test_ebics_wireformat \
test_wire_plugin \
test_wire_plugin_transactions_taler_bank
test_ebics_wireformat_SOURCES = \
test_ebics_wireformat.c
test_ebics_wireformat_LDADD = \
-lgnunetutil \
$(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/util/libtalerutil.la
test_wire_plugin_SOURCES = \
test_wire_plugin.c
test_wire_plugin_LDADD = \
-lgnunetutil \
$(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/util/libtalerutil.la
test_wire_plugin_transactions_taler_bank_SOURCES = \
test_wire_plugin_transactions_taler-bank.c
test_wire_plugin_transactions_taler_bank_LDADD = \
-lgnunetjson \
-lgnunetutil \
-ljansson \
$(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/bank-lib/libtalerfakebank.la \
$(top_builddir)/src/util/libtalerutil.la

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2016, 2017 GNUnet e.V. & Inria
Copyright (C) 2016, 2017, 2018 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 plugin_wire_sepa.c
* @file plugin_wire_ebics.c
* @brief wire plugin for transfers using SEPA/EBICS
* @author Florian Dold
* @author Christian Grothoff
@ -31,7 +31,7 @@
* Type of the "cls" argument given to each of the functions in
* our API.
*/
struct SepaClosure
struct EbicsClosure
{
/**
@ -39,6 +39,11 @@ struct SepaClosure
*/
char *currency;
/**
* Configuration we use to lookup account information.
*/
struct GNUNET_CONFIGURATION_Handle *cfg;
};
@ -54,10 +59,10 @@ struct SepaClosure
* #GNUNET_SYSERR if the amount or currency was invalid
*/
static int
sepa_amount_round (void *cls,
ebics_amount_round (void *cls,
struct TALER_Amount *amount)
{
struct SepaClosure *sc = cls;
struct EbicsClosure *sc = cls;
uint32_t delta;
if (NULL == sc->currency)
@ -81,7 +86,7 @@ sepa_amount_round (void *cls,
/**
* Entry in the country table.
*/
struct table_entry
struct CountryTableEntry
{
/**
* 2-Character international country code.
@ -100,7 +105,7 @@ struct table_entry
/**
* List of country codes.
*/
static const struct table_entry country_table[] =
static const struct CountryTableEntry country_table[] =
{
{ "AE", "U.A.E." },
{ "AF", "Afghanistan" },
@ -259,8 +264,8 @@ static int
cmp_country_code (const void *ptr1,
const void *ptr2)
{
const struct table_entry *cc1 = ptr1;
const struct table_entry *cc2 = ptr2;
const struct CountryTableEntry *cc1 = ptr1;
const struct CountryTableEntry *cc2 = ptr2;
return strncmp (cc1->code,
cc2->code,
@ -280,20 +285,21 @@ validate_iban (const char *iban)
{
char cc[2];
char ibancpy[35];
struct table_entry cc_entry;
struct CountryTableEntry cc_entry;
unsigned int len;
char *nbuf;
unsigned int i;
unsigned int j;
unsigned long long dividend;
unsigned long long remainder;
int nread;
int ret;
unsigned int i;
unsigned int j;
len = strlen (iban);
if (len > 34)
{
GNUNET_break_op (0);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"IBAN number too long to be valid\n");
return GNUNET_NO;
}
strncpy (cc, iban, 2);
@ -305,11 +311,14 @@ validate_iban (const char *iban)
if (NULL ==
bsearch (&cc_entry,
country_table,
sizeof (country_table) / sizeof (struct table_entry),
sizeof (struct table_entry),
sizeof (country_table) / sizeof (struct CountryTableEntry),
sizeof (struct CountryTableEntry),
&cmp_country_code))
{
GNUNET_break_op (0);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Country code `%c%c' not supported\n",
cc[0],
cc[1]);
return GNUNET_NO;
}
nbuf = GNUNET_malloc ((len * 2) + 1);
@ -354,309 +363,89 @@ validate_iban (const char *iban)
GNUNET_free (nbuf);
if (1 == remainder)
return GNUNET_YES;
GNUNET_break_op (0); /* checksum wrong */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"IBAN checksum wrong\n");
return GNUNET_NO;
}
/**
* Compute purpose for signing.
*
* @param sepa_name name of the account holder
* @param iban bank account number in IBAN format
* @param bic bank identifier
* @param[out] wsd purpose to be signed
* Information about an account extracted from a payto://-URL.
*/
static void
compute_purpose (const char *sepa_name,
const char *iban,
const char *bic,
struct TALER_MasterWireDetailsPS *wsd)
struct Account
{
struct GNUNET_HashContext *hc;
wsd->purpose.size = htonl (sizeof (struct TALER_MasterWireDetailsPS));
wsd->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SEPA_DETAILS);
hc = GNUNET_CRYPTO_hash_context_start ();
GNUNET_CRYPTO_hash_context_read (hc,
"sepa",
strlen ("sepa") + 1);
GNUNET_CRYPTO_hash_context_read (hc,
sepa_name,
strlen (sepa_name) + 1);
GNUNET_CRYPTO_hash_context_read (hc,
iban,
strlen (iban) + 1);
GNUNET_CRYPTO_hash_context_read (hc,
bic,
strlen (bic) + 1);
GNUNET_CRYPTO_hash_context_finish (hc,
&wsd->h_sepa_details);
}
/**
* Verify that the signature in the @a json for /wire/sepa is valid.
*
* @param json json reply with the signature
* @param master_pub public key of the exchange to verify against
* @return #GNUNET_SYSERR if @a json is invalid,
* #GNUNET_NO if the method is unknown,
* #GNUNET_OK if the json is valid
* The IBAN number.
*/
static int
verify_wire_sepa_signature_ok (const json_t *json,
const struct TALER_MasterPublicKeyP *master_pub)
{
struct TALER_MasterSignatureP exchange_sig;
struct TALER_MasterWireDetailsPS mp;
const char *name;
const char *iban;
const char *bic;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("sig", &exchange_sig),
GNUNET_JSON_spec_string ("name", &name),
GNUNET_JSON_spec_string ("iban", &iban),
GNUNET_JSON_spec_string ("bic", &bic),
GNUNET_JSON_spec_end()
char *iban;
};
if (NULL == master_pub)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Skipping signature check as master public key not given\n");
return GNUNET_OK;
}
if (GNUNET_OK !=
GNUNET_JSON_parse (json, spec,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
compute_purpose (name,
iban,
bic,
&mp);
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SEPA_DETAILS,
&mp.purpose,
&exchange_sig.eddsa_signature,
&master_pub->eddsa_pub))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
GNUNET_JSON_parse_free (spec);
return GNUNET_OK;
}
/**
* Check if the given wire format JSON object is correctly formatted
* Parse payto:// account URL (only account information,
* wire subject and amount are ignored).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param wire the JSON wire format object
* @param master_pub public key of the exchange to verify against
* @param[out] emsg set to an error message, unless we return #TALER_EC_NONE;
* error message must be freed by the caller using GNUNET_free()
* @return #TALER_EC_NONE if correctly formatted
* @param account_url URL to parse
* @param account[out] set to information, can be NULL
* @return #TALER_EC_NONE if @a account_url is well-formed
*/
static enum TALER_ErrorCode
sepa_wire_validate (void *cls,
const json_t *wire,
const struct TALER_MasterPublicKeyP *master_pub,
char **emsg)
parse_payto (const char *account_url,
struct Account *account)
{
json_error_t error;
const char *type;
const char *iban;
const char *name;
const char *bic;
const char *q;
char *result;
*emsg = NULL;
if (0 != json_unpack_ex
((json_t *) wire,
&error, 0,
"{"
"s:s," /* type: sepa */
"s:s," /* iban: IBAN */
"s:s," /* name: beneficiary name */
"s:s" /* bic: beneficiary bank's BIC */
"}",
"type", &type,
"iban", &iban,
"name", &name,
"bic", &bic))
#define PREFIX "payto://sepa/"
if (0 != strncasecmp (account_url,
PREFIX,
strlen (PREFIX)))
return TALER_EC_PAYTO_WRONG_METHOD;
iban = &account_url[strlen (PREFIX)];
q = strchr (iban,
'?');
if (NULL != q)
{
char *dump;
dump = json_dumps (wire, 0);
GNUNET_asprintf (emsg,
"JSON parsing failed at %s:%u: %s (%s): %s\n",
__FILE__, __LINE__,
error.text,
error.source,
dump);
free (dump);
return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_JSON;
result = GNUNET_strndup (iban,
q - iban);
}
if (0 != strcasecmp (type,
"sepa"))
else
{
GNUNET_asprintf (emsg,
"Transfer type `%s' invalid for SEPA wire plugin\n",
type);
return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE;
result = GNUNET_strdup (iban);
}
if (1 != validate_iban (iban))
{
GNUNET_asprintf (emsg,
"IBAN `%s' invalid\n",
iban);
return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_ACCOUNT_NUMBER;
}
/* FIXME: don't parse again, integrate properly... */
if (GNUNET_OK !=
verify_wire_sepa_signature_ok (wire,
master_pub))
validate_iban (result))
{
GNUNET_asprintf (emsg,
"Signature using public key `%s' invalid\n",
TALER_B2S (master_pub));
return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE;
GNUNET_free (result);
return TALER_EC_PAYTO_MALFORMED;
}
if (NULL != account)
{
account->iban = result;
}
else
{
GNUNET_free (result);
}
return TALER_EC_NONE;
}
/**
* Obtain wire transfer details in the plugin-specific format
* from the configuration.
* Check if the given payto:// URL is correctly formatted for this plugin
*
* @param cls closure
* @param cfg configuration with details about wire accounts
* @param account_name which section in the configuration should we parse
* @return NULL if @a cfg fails to have valid wire details for @a account_name
* @param cls the @e cls of this struct with the plugin-specific state
* @param account_url the payto:// URL
* @return #TALER_EC_NONE if correctly formatted
*/
static json_t *
sepa_get_wire_details (void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *account_name)
static enum TALER_ErrorCode
ebics_wire_validate (void *cls,
const char *account_url)
{
char *sepa_wire_file;
json_error_t err;
json_t *ret;
char *emsg;
/* Fetch reply */
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_filename (cfg,
account_name,
"SEPA_RESPONSE_FILE",
&sepa_wire_file))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
account_name,
"SEPA_RESPONSE_FILE");
return NULL;
}
ret = json_load_file (sepa_wire_file,
JSON_REJECT_DUPLICATES,
&err);
if (NULL == ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse JSON in %s: %s (%s:%u)\n",
sepa_wire_file,
err.text,
err.source,
err.line);
GNUNET_free (sepa_wire_file);
return NULL;
}
if (TALER_EC_NONE !=
sepa_wire_validate (cls,
ret,
NULL,
&emsg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to validate SEPA data in %s: %s\n",
sepa_wire_file,
emsg);
GNUNET_free (emsg);
GNUNET_free (sepa_wire_file);
json_decref (ret);
return NULL;
}
GNUNET_free (sepa_wire_file);
return ret;
}
/**
* Sign wire transfer details in the plugin-specific format.
*
* @param cls closure
* @param in wire transfer details in JSON format
* @param key private signing key to use
* @param salt salt to add
* @param[out] sig where to write the signature
* @return #GNUNET_OK on success
*/
static int
sepa_sign_wire_details (void *cls,
const json_t *in,
const struct TALER_MasterPrivateKeyP *key,
const struct GNUNET_HashCode *salt,
struct TALER_MasterSignatureP *sig)
{
struct TALER_MasterWireDetailsPS wsd;
const char *sepa_name;
const char *iban;
const char *bic;
const char *type;
json_error_t err;
if (0 !=
json_unpack_ex ((json_t *) in,
&err,
0 /* flags */,
"{s:s, s:s, s:s, s:s}",
"type", &type,
"name", &sepa_name,
"iban", &iban,
"bic", &bic))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to unpack JSON: %s (at %u)\n",
err.text,
err.position);
return GNUNET_SYSERR;
}
if (0 != strcmp (type,
"sepa"))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"`type' must be `sepa' for SEPA wire details\n");
return GNUNET_SYSERR;
}
if (1 != validate_iban (iban))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"IBAN invalid in SEPA wire details\n");
return GNUNET_SYSERR;
}
compute_purpose (sepa_name,
iban,
bic,
&wsd);
GNUNET_CRYPTO_eddsa_sign (&key->eddsa_priv,
&wsd.purpose,
&sig->eddsa_signature);
return GNUNET_OK;
return parse_payto (account_url,
NULL);
}
@ -673,8 +462,9 @@ sepa_sign_wire_details (void *cls,
* @return NULL on failure
*/
static struct TALER_WIRE_PrepareHandle *
sepa_prepare_wire_transfer (void *cls,
const json_t *wire,
ebics_prepare_wire_transfer (void *cls,
const char *origin_account_section,
const char *destination_account_url,
const struct TALER_Amount *amount,
const char *exchange_base_url,
const struct TALER_WireTransferIdentifierRawP *wtid,
@ -694,7 +484,7 @@ sepa_prepare_wire_transfer (void *cls,
* @param pth preparation to cancel
*/
static void
sepa_prepare_wire_transfer_cancel (void *cls,
ebics_prepare_wire_transfer_cancel (void *cls,
struct TALER_WIRE_PrepareHandle *pth)
{
GNUNET_break (0); // FIXME: not implemented
@ -712,7 +502,7 @@ sepa_prepare_wire_transfer_cancel (void *cls,
* @return NULL on error
*/
static struct TALER_WIRE_ExecuteHandle *
sepa_execute_wire_transfer (void *cls,
ebics_execute_wire_transfer (void *cls,
const char *buf,
size_t buf_size,
TALER_WIRE_ConfirmationCallback cc,
@ -736,7 +526,7 @@ sepa_execute_wire_transfer (void *cls,
* @param eh execution to cancel
*/
static void
sepa_execute_wire_transfer_cancel (void *cls,
ebics_execute_wire_transfer_cancel (void *cls,
struct TALER_WIRE_ExecuteHandle *eh)
{
GNUNET_break (0); // FIXME: not implemented
@ -754,6 +544,8 @@ sepa_execute_wire_transfer_cancel (void *cls,
* (with negative @a num_results).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param account_section specifies the configuration section which
* identifies the account for which we should get the history
* @param direction what kinds of wire transfers should be returned
* @param start_off from which row on do we want to get results, use NULL for the latest; exclusive
* @param start_off_len number of bytes in @a start_off; must be `sizeof(uint64_t)`.
@ -764,7 +556,8 @@ sepa_execute_wire_transfer_cancel (void *cls,
* @param hres_cb_cls closure for the above callback
*/
static struct TALER_WIRE_HistoryHandle *
sepa_get_history (void *cls,
ebics_get_history (void *cls,
const char *account_section,
enum TALER_BANK_Direction direction,
const void *start_off,
size_t start_off_len,
@ -784,7 +577,7 @@ sepa_get_history (void *cls,
* @param whh operation to cancel
*/
static void
sepa_get_history_cancel (void *cls,
ebics_get_history_cancel (void *cls,
struct TALER_WIRE_HistoryHandle *whh)
{
GNUNET_break (0);
@ -843,6 +636,8 @@ timeout_reject (void *cls)
* results returned by @e get_history.
*
* @param cls plugin's closure
* @param account_section specifies the configuration section which
* identifies the account to use to reject the transfer
* @param start_off offset of the wire transfer in plugin-specific format
* @param start_off_len number of bytes in @a start_off
* @param rej_cb function to call with the result of the operation
@ -850,7 +645,8 @@ timeout_reject (void *cls)
* @return handle to cancel the operation
*/
static struct TALER_WIRE_RejectHandle *
sepa_reject_transfer (void *cls,
ebics_reject_transfer (void *cls,
const char *account_section,
const void *start_off,
size_t start_off_len,
TALER_WIRE_RejectTransferCallback rej_cb,
@ -881,7 +677,7 @@ sepa_reject_transfer (void *cls,
* @return closure of the callback of the operation
*/
static void *
sepa_reject_transfer_cancel (void *cls,
ebics_reject_transfer_cancel (void *cls,
struct TALER_WIRE_RejectHandle *rh)
{
void *ret = rh->rej_cb_cls;
@ -893,21 +689,20 @@ sepa_reject_transfer_cancel (void *cls,
/**
* Initialize sepa-wire subsystem.
* Initialize ebics-wire subsystem.
*
* @param cls a configuration instance
* @return NULL on error, otherwise a `struct TALER_WIRE_Plugin`
*/
void *
libtaler_plugin_wire_sepa_init (void *cls)
libtaler_plugin_wire_ebics_init (void *cls)
{
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct SepaClosure *sc;
struct EbicsClosure *sc;
struct TALER_WIRE_Plugin *plugin;
sc = GNUNET_new (struct SepaClosure);
if (NULL != cfg)
{
sc = GNUNET_new (struct EbicsClosure);
sc->cfg = cfg;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"taler",
@ -920,36 +715,34 @@ libtaler_plugin_wire_sepa_init (void *cls)
GNUNET_free (sc);
return NULL;
}
}
plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = sc;
plugin->amount_round = &sepa_amount_round;
plugin->get_wire_details = &sepa_get_wire_details;
plugin->sign_wire_details = &sepa_sign_wire_details;
plugin->wire_validate = &sepa_wire_validate;
plugin->prepare_wire_transfer = &sepa_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &sepa_prepare_wire_transfer_cancel;
plugin->execute_wire_transfer = &sepa_execute_wire_transfer;
plugin->execute_wire_transfer_cancel = &sepa_execute_wire_transfer_cancel;
plugin->get_history = &sepa_get_history;
plugin->get_history_cancel = &sepa_get_history_cancel;
plugin->reject_transfer = &sepa_reject_transfer;
plugin->reject_transfer_cancel = &sepa_reject_transfer_cancel;
plugin->method = "sepa";
plugin->amount_round = &ebics_amount_round;
plugin->wire_validate = &ebics_wire_validate;
plugin->prepare_wire_transfer = &ebics_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &ebics_prepare_wire_transfer_cancel;
plugin->execute_wire_transfer = &ebics_execute_wire_transfer;
plugin->execute_wire_transfer_cancel = &ebics_execute_wire_transfer_cancel;
plugin->get_history = &ebics_get_history;
plugin->get_history_cancel = &ebics_get_history_cancel;
plugin->reject_transfer = &ebics_reject_transfer;
plugin->reject_transfer_cancel = &ebics_reject_transfer_cancel;
return plugin;
}
/**
* Shutdown Sepa wire subsystem.
* Shutdown Ebics wire subsystem.
*
* @param cls a `struct TALER_WIRE_Plugin`
* @return NULL (always)
*/
void *
libtaler_plugin_wire_sepa_done (void *cls)
libtaler_plugin_wire_ebics_done (void *cls)
{
struct TALER_WIRE_Plugin *plugin = cls;
struct SepaClosure *sc = plugin->cls;
struct EbicsClosure *sc = plugin->cls;
GNUNET_free_non_null (sc->currency);
GNUNET_free (sc);
@ -957,4 +750,4 @@ libtaler_plugin_wire_sepa_done (void *cls)
return NULL;
}
/* end of plugin_wire_sepa.c */
/* end of plugin_wire_ebics.c */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2016 GNUnet e.V.
Copyright (C) 2016, 2018 GNUnet e.V.
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
@ -32,16 +32,16 @@
struct TemplateClosure
{
/**
* URL of the bank for sending funds to the bank.
*/
char *bank_url;
/**
* Which currency do we support?
*/
char *currency;
/**
* Which configuration do we use to lookup accounts?
*/
struct GNUNET_CONFIGURATION_Handle *cfg;
};
@ -74,42 +74,17 @@ template_amount_round (void *cls,
/**
* Obtain wire transfer details in the plugin-specific format
* from the configuration.
*
* @param cls closure
* @param cfg configuration with details about wire accounts
* @param account_name which section in the configuration should we parse
* @return NULL if @a cfg fails to have valid wire details for @a account_name
*/
static json_t *
template_get_wire_details (void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *account_name)
{
GNUNET_break (0);
return NULL;
}
/**
* Check if the given wire format JSON object is correctly formatted
* Check if the given payto:// URL is correctly formatted for this plugin
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param wire the JSON wire format object
* @param master_pub public key of the exchange to verify against
* @param[out] emsg set to an error message, unless we return #TALER_EC_NONE;
* error message must be freed by the caller using GNUNET_free()
* @param account_url the payto:// URL
* @return #TALER_EC_NONE if correctly formatted
*/
static enum TALER_ErrorCode
template_wire_validate (void *cls,
const json_t *wire,
const struct TALER_MasterPublicKeyP *master_pub,
char **emsg)
const char *account_url)
{
GNUNET_asprintf (emsg,
"Not implemented");
GNUNET_break (0);
return TALER_EC_NOT_IMPLEMENTED;
}
@ -118,7 +93,9 @@ template_wire_validate (void *cls,
* Prepare for exeuction of a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param wire valid wire account information
* @param origin_account_section configuration section specifying the origin
* account of the exchange to use
* @param destination_account_url payto:// URL identifying where to send the money
* @param amount amount to transfer, already rounded
* @param exchange_base_url base URL of the exchange (for tracking)
* @param wtid wire transfer identifier to use
@ -128,7 +105,8 @@ template_wire_validate (void *cls,
*/
static struct TALER_WIRE_PrepareHandle *
template_prepare_wire_transfer (void *cls,
const json_t *wire,
const char *origin_account_section,
const char *destination_account_url,
const struct TALER_Amount *amount,
const char *exchange_base_url,
const struct TALER_WireTransferIdentifierRawP *wtid,
@ -177,28 +155,6 @@ template_execute_wire_transfer (void *cls,
}
/**
* Sign wire transfer details in the plugin-specific format.
*
* @param cls closure
* @param in wire transfer details in JSON format
* @param key private signing key to use
* @param salt salt to add
* @param[out] sig where to write the signature
* @return #GNUNET_OK on success
*/
static int
template_sign_wire_details (void *cls,
const json_t *in,
const struct TALER_MasterPrivateKeyP *key,
const struct GNUNET_HashCode *salt,
struct TALER_MasterSignatureP *sig)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
/**
* Abort execution of a wire transfer. For example, because we are
* shutting down. Note that if an execution is aborted, it may or
@ -230,6 +186,8 @@ template_execute_wire_transfer_cancel (void *cls,
* (with negative @a num_results).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param account_section specifies the configuration section which
* identifies the account for which we should get the history
* @param direction what kinds of wire transfers should be returned
* @param start_off from which row on do we want to get results, use NULL for the latest; exclusive
* @param start_off_len number of bytes in @a start_off; must be `sizeof(uint64_t)`.
@ -241,6 +199,7 @@ template_execute_wire_transfer_cancel (void *cls,
*/
static struct TALER_WIRE_HistoryHandle *
template_get_history (void *cls,
const char *account_section,
enum TALER_BANK_Direction direction,
const void *start_off,
size_t start_off_len,
@ -267,6 +226,59 @@ template_get_history_cancel (void *cls,
}
/**
* Reject an incoming wire transfer that was obtained from the
* history. This function can be used to transfer funds back to
* the sender if the WTID was malformed (i.e. due to a typo).
*
* Calling `reject_transfer` twice on the same wire transfer should
* be idempotent, i.e. not cause the funds to be wired back twice.
* Furthermore, the transfer should henceforth be removed from the
* results returned by @e get_history.
*
* @param cls plugin's closure
* @param account_section specifies the configuration section which
* identifies the account to use to reject the transfer
* @param start_off offset of the wire transfer in plugin-specific format
* @param start_off_len number of bytes in @a start_off
* @param rej_cb function to call with the result of the operation
* @param rej_cb_cls closure for @a rej_cb
* @return handle to cancel the operation
*/
static struct TALER_WIRE_RejectHandle *
template_reject_transfer (void *cls,
const char *account_section,
const void *start_off,
size_t start_off_len,
TALER_WIRE_RejectTransferCallback rej_cb,
void *rej_cb_cls)
{
GNUNET_break (0);
return NULL;
}
/**
* Cancel ongoing reject operation. Note that the rejection may still
* proceed. Basically, if this function is called, the rejection may
* have happened or not. This function is usually used during shutdown
* or system upgrades. At a later point, the application must call
* @e reject_transfer again for this wire transfer, unless the
* @e get_history shows that the wire transfer no longer exists.
*
* @param cls plugins' closure
* @param rh operation to cancel
* @return closure of the callback of the operation
*/
static void *
template_reject_transfer_cancel (void *cls,
struct TALER_WIRE_RejectHandle *rh)
{
GNUNET_break (0);
return NULL;
}
/**
* Initialize template-wire subsystem.
*
@ -281,19 +293,7 @@ libtaler_plugin_wire_template_init (void *cls)
struct TALER_WIRE_Plugin *plugin;
tc = GNUNET_new (struct TemplateClosure);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"exchange-wire-template",
"bank_url",
&tc->bank_url))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange-wire-template",
"bank_url");
GNUNET_free (tc);
return NULL;
}
tc->cfg = cfg;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"taler",
@ -303,16 +303,14 @@ libtaler_plugin_wire_template_init (void *cls)
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"taler",
"CURRENCY");
GNUNET_free (tc->bank_url);
GNUNET_free (tc);
return NULL;
}
plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = tc;
plugin->method = "FIXME-REPLACE-BY-METHOD";
plugin->amount_round = &template_amount_round;
plugin->get_wire_details = &template_get_wire_details;
plugin->sign_wire_details = &template_sign_wire_details;
plugin->wire_validate = &template_wire_validate;
plugin->prepare_wire_transfer = &template_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &template_prepare_wire_transfer_cancel;
@ -320,6 +318,8 @@ libtaler_plugin_wire_template_init (void *cls)
plugin->execute_wire_transfer_cancel = &template_execute_wire_transfer_cancel;
plugin->get_history = &template_get_history;
plugin->get_history_cancel = &template_get_history_cancel;
plugin->reject_transfer = &template_reject_transfer;
plugin->reject_transfer_cancel = &template_reject_transfer_cancel;
return plugin;
}
@ -336,7 +336,6 @@ libtaler_plugin_wire_template_done (void *cls)
struct TALER_WIRE_Plugin *plugin = cls;
struct TemplateClosure *tc = plugin->cls;
GNUNET_free (tc->bank_url);
GNUNET_free (tc->currency);
GNUNET_free (tc);
GNUNET_free (plugin);

View File

@ -0,0 +1,80 @@
/*
This file is part of TALER
(C) 2015, 2016, 2018 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 wire/test_ebics_wireformat.c
* @brief Tests for SEPA format validation by the EBICS plugin
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_wire_lib.h"
/**
* Valid SEPA data
*/
static const char *valid_wire_str = "payto://sepa/DE67830654080004822650";
/**
* IBAN has wrong country code
*/
static const char *invalid_wire_str = "payto://sepa/XX67830654080004822650";
/**
* IBAN has wrong checksum
*/
static const char *invalid_wire_str2 = "payto://sepa/DE67830654080004822651";
/**
* Unsupported wireformat type
*/
static const char *unsupported_wire_str = "payto://sega/DE67830654080004822650";
int
main (int argc,
const char *const argv[])
{
struct GNUNET_CONFIGURATION_Handle *cfg;
struct TALER_WIRE_Plugin *plugin;
GNUNET_log_setup ("test-sepa-wireformats",
"WARNING",
NULL);
cfg = GNUNET_CONFIGURATION_create ();
GNUNET_CONFIGURATION_set_value_string (cfg,
"taler",
"currency",
"EUR");
plugin = TALER_WIRE_plugin_load (cfg,
"ebics");
GNUNET_assert (NULL != plugin);
GNUNET_assert (TALER_EC_NONE !=
plugin->wire_validate (plugin->cls,
unsupported_wire_str));
GNUNET_assert (TALER_EC_NONE !=
plugin->wire_validate (plugin->cls,
invalid_wire_str));
GNUNET_assert (TALER_EC_NONE !=
plugin->wire_validate (plugin->cls,
invalid_wire_str2));
GNUNET_assert (TALER_EC_NONE ==
plugin->wire_validate (plugin->cls,
valid_wire_str));
TALER_WIRE_plugin_unload (plugin);
GNUNET_CONFIGURATION_destroy (cfg);
return 0;
}

View File

@ -24,7 +24,6 @@
#include "taler_wire_lib.h"
#include "taler_wire_plugin.h"
#include <gnunet/gnunet_json_lib.h>
#include <jansson.h>
/**
@ -37,11 +36,6 @@ struct TestBlock {
*/
const char *plugin_name;
/**
* JSON template expected by the plugin for an account definition.
*/
const char *json_proto;
/**
* Amount to give to the rounding function.
*/
@ -65,35 +59,23 @@ struct TestBlock {
*/
static struct TestBlock tests[] = {
{
.plugin_name = "sepa",
.json_proto = "{ \"type\":\"sepa\", \"iban\":\"DE67830654080004822650\", \"name\":\"GNUnet e.V.\", \"bic\":\"GENODEF1SLR\" }",
.plugin_name = "ebics",
.round_in = "EUR:0.123456",
.round_out = "EUR:0.12",
.currency = "EUR"
},
{
.plugin_name = "test",
.json_proto = "{ \"type\":\"test\", \"bank_url\":\"http://localhost/\", \"account_number\":42 }",
.plugin_name = "taler_bank",
.round_in = "KUDOS:0.123456",
.round_out = "KUDOS:0.12",
.currency = "KUDOS"
},
{
NULL, NULL, NULL, NULL, NULL
NULL, NULL, NULL, NULL
}
};
/**
* Private key used to sign wire details.
*/
static struct TALER_MasterPrivateKeyP priv_key;
/**
* Public key matching #priv_key.
*/
static struct TALER_MasterPublicKeyP pub_key;
/**
* Our configuration.
*/
@ -105,73 +87,19 @@ static struct GNUNET_CONFIGURATION_Handle *cfg;
*
* @param test details of the test
* @param plugin plugin to test
* @param wire wire details for testing
* @return #GNUNET_OK on success
*/
static int
run_test (const struct TestBlock *test,
struct TALER_WIRE_Plugin *plugin,
json_t *wire)
struct TALER_WIRE_Plugin *plugin)
{
struct GNUNET_HashCode salt;
struct TALER_MasterSignatureP sig;
json_t *lwire;
struct TALER_Amount in;
struct TALER_Amount expect;
char *emsg;
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&salt,
sizeof (salt));
if (GNUNET_OK !=
plugin->sign_wire_details (plugin->cls,
wire,
&priv_key,
&salt,
&sig))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
json_object_set_new (wire,
"salt",
GNUNET_JSON_from_data (&salt,
sizeof (salt)));
json_object_set_new (wire,
"sig",
GNUNET_JSON_from_data (&sig,
sizeof (sig)));
if (TALER_EC_NONE !=
plugin->wire_validate (plugin->cls,
wire,
&pub_key,
&emsg))
{
GNUNET_break (0);
GNUNET_free (emsg);
return GNUNET_SYSERR;
}
/* load wire details from file */
lwire = plugin->get_wire_details (plugin->cls,
cfg,
test->plugin_name);
if (NULL == lwire)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (TALER_EC_NONE !=
plugin->wire_validate (plugin->cls,
lwire,
&pub_key,
&emsg))
{
GNUNET_break (0);
GNUNET_free (emsg);
json_decref (lwire);
return GNUNET_SYSERR;
}
json_decref (lwire);
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (test->round_in,
&in));
@ -214,12 +142,9 @@ int
main (int argc,
const char *const argv[])
{
json_t *wire;
int ret;
struct TALER_WIRE_Plugin *plugin;
const struct TestBlock *test;
unsigned int i;
struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
GNUNET_log_setup ("test-wire-plugin",
"WARNING",
@ -228,13 +153,8 @@ main (int argc,
GNUNET_assert (GNUNET_OK ==
GNUNET_CONFIGURATION_load (cfg,
"test_wire_plugin.conf"));
pk = GNUNET_CRYPTO_eddsa_key_create_from_file ("test_wire_plugin_key.priv");
priv_key.eddsa_priv = *pk;
GNUNET_free (pk);
GNUNET_CRYPTO_eddsa_key_get_public (&priv_key.eddsa_priv,
&pub_key.eddsa_pub);
ret = GNUNET_OK;
for (i=0;NULL != (test = &tests[i])->plugin_name;i++)
for (unsigned int i=0;NULL != (test = &tests[i])->plugin_name;i++)
{
GNUNET_CONFIGURATION_set_value_string (cfg,
"taler",
@ -243,10 +163,7 @@ main (int argc,
plugin = TALER_WIRE_plugin_load (cfg,
test->plugin_name);
GNUNET_assert (NULL != plugin);
wire = json_loads (test->json_proto, 0, NULL);
GNUNET_assert (NULL != wire);
ret = run_test (test, plugin, wire);
json_decref (wire);
ret = run_test (test, plugin);
TALER_WIRE_plugin_unload (plugin);
if (GNUNET_OK != ret)
{

View File

@ -1,21 +1,25 @@
# This file is in the public domain.
#
[test]
[account-taler-bank]
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
TEST_RESPONSE_FILE = test_wire_plugin_test.json
WIRE_JSON = test_wire_plugin_test.json
[sepa]
# Our bank account URL
URL = payto://x-taler-bank/2
# Which wire plugin should we used to access the account?
PLUGIN = taler_bank
[account-sepa]
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
SEPA_RESPONSE_FILE = test_wire_plugin_sepa.json
WIRE_JSON = test_wire_plugin_sepa.json
# Which wire plugin should we used to access the account?
PLUGIN = ebics
[exchange-wire-test]
# For transfers made by the exchange, we need to know
# the URL of the bank (where the /admin/add/incoming API
# is avaialble).
BANK_URL = http://localhost/
[taler]
CURRENCY = "EUR"

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
(C) 2015, 2016, 2017 GNUnet e.V. and Inria
(C) 2015-2018 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 wire/test_wire_plugin_transactions_test.c
* @brief Tests performing actual transactions with the TEST wire plugin against FAKEBANK
* @file wire/test_wire_plugin_transactions_taler-bank.c
* @brief Tests performing actual transactions with the taler-bank wire plugin against FAKEBANK
* @author Christian Grothoff
*/
#include "platform.h"
@ -24,7 +24,6 @@
#include "taler_wire_plugin.h"
#include "taler_fakebank_lib.h"
#include <gnunet/gnunet_json_lib.h>
#include <jansson.h>
/**
@ -35,21 +34,14 @@
/**
* Input for the wire transfer details.
* Destination account to use.
*/
static const char *json_proto =
"{ \"type\":\"test\", \"bank_url\":\"http://localhost:8088/\", \"account_number\":42 }";
static const char *dest_account = "payto://x-taler-bank/localhost:8088/42";
/**
* Private key used to sign wire details.
* Origin account, section in the configuration file.
*/
static struct TALER_MasterPrivateKeyP priv_key;
/**
* Public key matching #priv_key.
*/
static struct TALER_MasterPublicKeyP pub_key;
static const char *my_account = "account-test";
/**
* Our configuration.
@ -250,6 +242,7 @@ confirmation_cb (void *cls,
}
serial_target = serial_id;
hh = plugin->get_history (plugin->cls,
my_account,
TALER_BANK_DIRECTION_BOTH,
NULL, 0,
5,
@ -294,7 +287,6 @@ prepare_cb (void *cls,
static void
run (void *cls)
{
json_t *wire;
struct TALER_Amount amount;
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
@ -308,18 +300,15 @@ run (void *cls)
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("KUDOS:5.01",
&amount));
wire = json_loads (json_proto,
0,
NULL);
fb = TALER_FAKEBANK_start (8088);
ph = plugin->prepare_wire_transfer (plugin->cls,
wire,
my_account,
dest_account,
&amount,
"https://exchange.net/",
&wtid,
&prepare_cb,
NULL);
json_decref (wire);
}
@ -327,23 +316,16 @@ int
main (int argc,
const char *const argv[])
{
struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
GNUNET_log_setup ("test-wire-plugin-transactions-test",
"WARNING",
NULL);
cfg = GNUNET_CONFIGURATION_create ();
GNUNET_assert (GNUNET_OK ==
GNUNET_CONFIGURATION_load (cfg,
"test_wire_plugin_transactions_test.conf"));
pk = GNUNET_CRYPTO_eddsa_key_create_from_file ("test_wire_plugin_key.priv");
priv_key.eddsa_priv = *pk;
GNUNET_free (pk);
GNUNET_CRYPTO_eddsa_key_get_public (&priv_key.eddsa_priv,
&pub_key.eddsa_pub);
"test_wire_plugin_transactions_taler-bank.conf"));
global_ret = GNUNET_OK;
plugin = TALER_WIRE_plugin_load (cfg,
"test");
"taler_bank");
GNUNET_assert (NULL != plugin);
GNUNET_SCHEDULER_run (&run,
NULL);
@ -353,4 +335,4 @@ main (int argc,
return 0;
}
/* end of test_wire_plugin_transactions_test.c */
/* end of test_wire_plugin_transactions_taler-bank.c */

View File

@ -0,0 +1,12 @@
# This file is in the public domain.
#
[account-test]
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
TALER_BANK_AUTH_METHOD = NONE
URL = payto://x-taler-bank/localhost:8088/2
[taler]
CURRENCY = "KUDOS"

Some files were not shown because too many files have changed in this diff Show More