View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0011166 | Taler | exchange | public | 2026-02-27 15:21 | 2026-02-27 15:21 |
| Reporter | how | Assigned To | Florian Dold | ||
| Priority | urgent | Severity | block | Reproducibility | always |
| Status | assigned | Resolution | open | ||
| Platform | AMD64 | OS | Debian | OS Version | Trixie |
| Summary | 0011166: Coin Demominations are confused | ||||
| Description | ``` # taler-exchange-offline upload < sig-response.json # taler-exchange-offline upload < acct-response.json # taler-exchange-offline upload < fee-response.json # taler-exchange-offline upload < global-response.json ``` BUG: the last step gives an error: > 2026-02-27T14:05:42.020965+0000 taler-exchange-offline-427284 WARNING External protocol violation detected at json_helper.c:101. > 2026-02-27T14:05:42.021036+0000 taler-exchange-offline-427284 WARNING Expected currency `BOOKZ', but amount used currency `BKZ' in field `history_fee' > 2026-02-27T14:05:42.021049+0000 taler-exchange-offline-427284 ERROR Invalid input to set wire fee: history_fee#0 at 0 (skipping) ``` | ||||
| Steps To Reproduce | # Taler Exchange Setup Main documentation: https://docs.taler.net/taler-exchange-manual.html ## Installation `# apt install taler-exchange` ## Configuration Given `BASE_URL=exchange.bookseller.taler.net`... ### Database ``` # apt install taler-exchange-database # (if not there already) # taler-exchange-dbconfig # sudo -u taler-exchange-httpd taler-exchange-dbinit ``` ### Reverse Proxy `# apt install caddy` `/etc/caddy/Caddyfile` should contain: ``` # Debian taler-exchange exchange.bookseller.taler.net { reverse_proxy unix//run/taler-exchange/httpd/exchange-http.sock } ``` ### Coin Denominations Use coins' _code_, not _name_. `taler-harness deployment gen-coin-config --min-amount BKZ:0.01 --max-amount BKZ:100 > /etc/taler-exchange/conf.d/exchange-coins.conf` BUG: you need to fix the generated `exchange-coins.conf`: `sed -i -e 's/COIN_/coin_/g' /etc/taler-exchange/conf.d/exchange-coins.conf` `/etc/taler-exchange/conf.d/currencies.conf`: ``` [currency-bookz] ENABLED = YES name = "BOOKZ" code = "BKZ" fractional_input_digits = 2 fractional_normal_digits = 2 fractional_trailing_zero_digits = 2 alt_unit_names = {"0":"�"} common_amounts = "BKZ:5 BKZ:10 BKZ:25 BKZ:50" ``` Start `taler-exchange`: `systemctl start taler-exchange.target` If something goes wrong, restart from scratch: 1. Stop taler-exchange: `systemctl stop taler-exchange.target` 2. Drop database: `sudo -H -u postgres dropdb 'taler-exchange'` 3. Redo `taler-exchange-dbconfig` 4. Redo coin denominations ### Fee Structure This is run on the offline machine: ``` # apt install taler-exchange-offline # taler-exchange-offline setup ``` Here you get the `MASTER_KEY`. Back online to `/etc/taler-exchange/conf.d/exchange-business.conf`: ``` # Configuration for business-level aspects of the exchange. [exchange] # Here you MUST add the master public key of the offline system # which you can get using `taler-exchange-offline setup`. # This is just an example, your key will be different! # MASTER_PUBLIC_KEY = YE6Q6TR1EDB7FD0S68TGDZGF1P0GHJD2S0XVV8R2S62MYJ6HJ4ZG MASTER_PUBLIC_KEY = $MASTER_KEY # Publicly visible base URL of the exchange. # BASE_URL = https://example.com/ BASE_URL = https://exchange.bookseller.taler.net/ # Here you MUST configure the amount above which transactions are # always subject to manual AML review. AML_THRESHOLD = 10000000 # Attribute encryption key for storing attributes encrypted # in the database. Should be a high-entropy nonce. # Run `openssl rand -hex 32` to obtain a 256 bit nonce ATTRIBUTE_ENCRYPTION_KEY = $(openssl rand -hex 32) # For your terms of service and privacy policy, you should specify # an Etag that must be updated whenever there are significant # changes to either document. The format is up to you, what matters # is that the value is updated and never reused. See the HTTP # specification on Etags. TERMS_DIR = /srv/www/taler.net/bookseller/exchange/ TERMS_ETAG = bookseller-tos-v1 PRIVACY_ETAG = bookseller-prv-v1 # Redirect https://exchange.bookseller.taler.net to this URL TOPLEVEL_REDIRECT_URL = https://bookseller.taler.net/exchange-operator # WARNING Configuration fails to specify option `TINY_AMOUNT' in section `exchange'! TINY_AMOUNT = BOOKZ:0.01 # WARNING Configuration fails to specify option `SHOPPING_URL' in section `exchange'! SHOPPING_URL = https://shop.bookseller.taler.net/ [merchant-exchange-bookseller.taler.net] MASTER_KEY = $MASTER_KEY CURRENCY = BOOKZ EXCHANGE_BASE_URL = https://exchange.bookseller.taler.net/ # Bank accounts used by the exchange should be specified here: [exchange-account-1] # Use for exchange-wirewatch (and listed in /wire) ENABLE_CREDIT = YES # Use for exchange-aggregator (outgoing transfers) ENABLE_DEBIT = YES # Account identifier in the form of an RFC-8905 payto:// URI. # For SEPA, looks like payto://sepa/$IBAN?receiver-name=$NAME # Make sure to URL-encode spaces in $NAME! PAYTO_URI = payto://x-taler-bank/bank.bookseller.taler.net/exchange?receiver-name=TALER%20Bookseller%20Bank #PAYTO_URI = "payto://x-taler-bank/localhost:9099/exchange?receiver-name=TALER%20Bookseller%20Bank" # URL for talking to the bank wire the wire API. WIRE_GATEWAY_URL = https://bank.bookseller.taler.net/accounts/exchange/taler-wire-gateway/ # Credentials to access the account are in a separate # config file with restricted permissions. @inline-secret@ exchange-accountcredentials-1 ../secrets/exchange-accountcredentials-1.secret.conf ``` And the secrets: ``` # This file contains the secret credentials # to access the Taler Wire Gateway API (usually # provided by LibEuFin) for the exchange accounts. # # Each exchange-account-* section should have a matching # exchange-accountcredentials-* section here. # # Each of those sections must be imported via @inline-secret@, # usually in conf.d/exchange-business.conf. [exchange-accountcredentials-1] WIRE_GATEWAY_AUTH_METHOD = bearer TOKEN = secret-token:SOME-SECRET-TOKEN # URL for talking to the bank wire the wire API. WIRE_GATEWAY_URL = https://bank.bookseller.taler.net/accounts/exchange/taler-wire-gateway/ ``` ### Bank Account ### Auditors ### Offline Signing - /var/lib/taler-exchange/offline/master.priv must be readable by taler-exchange-offline Wait for `taler-exchange-secmod-rsa` process to finish setting up keys before running the following commands. This step may take several minutes. On the offline system: ``` $ taler-exchange-offline download > sig-request.json $ taler-exchange-offline sign < sig-request.json > sig-response.json $ taler-exchange-offline enable-account payto://x-taler-bank/bank.bookseller.taler.net/exchange?receiver-name=TALER%20Bookseller%20Bank > acct-response.json $ taler-exchange-offline wire-fee now x-taler-bank BKZ:0 BKZ:0 > fee-response.json $ taler-exchange-offline global-fee now BKZ:0 BKZ:0 BKZ:0 4weeks 6a 4 > global-response.json ``` On the online system: ``` # taler-exchange-offline upload < sig-response.json # taler-exchange-offline upload < acct-response.json # taler-exchange-offline upload < fee-response.json # taler-exchange-offline upload < global-response.json ``` BUG: the last step gives an error: > 2026-02-27T14:05:42.020965+0000 taler-exchange-offline-427284 WARNING External protocol violation detected at json_helper.c:101. > 2026-02-27T14:05:42.021036+0000 taler-exchange-offline-427284 WARNING Expected currency `BOOKZ', but amount used currency `BKZ' in field `history_fee' > 2026-02-27T14:05:42.021049+0000 taler-exchange-offline-427284 ERROR Invalid input to set wire fee: history_fee#0 at 0 (skipping) #### Signing the offline keys Then, generate future keys from the offline system: ``` $ taler-exchange-offline download > future-keys.json $ taler-exchange-offline show < future-keys.json ``` This should show a long file with all coin denominations for this Exchange. Once ready, ``` $ taler-exchange-offline sign < future-keys.json > offline-sigs.json $ taler-exchange-offline upload < offline-sigs.json ``` Restart the Exchange service! ### Terms of Service ## Test Browse to https://$BASE_URL/management/keys | ||||
| Additional Information | Apparently using BOOKZ or BKZ (the coin denominations name or code) is not consistent nor correctly supported by the tools or documented. In any case, I tried several combinations but am left without a working exchange. This is very problematic since I need one running for the coming Geneva book fair. | ||||
| Tags | No tags attached. | ||||
| Attached Files | exchange-setup.md (7,201 bytes)
# Taler Exchange Setup
Main documentation: https://docs.taler.net/taler-exchange-manual.html
## Installation
`# apt install taler-exchange`
## Configuration
Given `BASE_URL=exchange.bookseller.taler.net`...
### Database
```
# apt install taler-exchange-database # (if not there already)
# taler-exchange-dbconfig
# sudo -u taler-exchange-httpd taler-exchange-dbinit
```
### Reverse Proxy
`# apt install caddy`
`/etc/caddy/Caddyfile` should contain:
```
# Debian taler-exchange
exchange.bookseller.taler.net {
reverse_proxy unix//run/taler-exchange/httpd/exchange-http.sock
}
```
### Coin Denominations
Use coins' _code_, not _name_.
`taler-harness deployment gen-coin-config --min-amount BKZ:0.01 --max-amount BKZ:100 > /etc/taler-exchange/conf.d/exchange-coins.conf`
BUG: you need to fix the generated `exchange-coins.conf`:
`sed -i -e 's/COIN_/coin_/g' /etc/taler-exchange/conf.d/exchange-coins.conf`
`/etc/taler-exchange/conf.d/currencies.conf`:
```
[currency-bookz]
ENABLED = YES
name = "BOOKZ"
code = "BKZ"
fractional_input_digits = 2
fractional_normal_digits = 2
fractional_trailing_zero_digits = 2
alt_unit_names = {"0":"📗"}
common_amounts = "BKZ:5 BKZ:10 BKZ:25 BKZ:50"
```
Start `taler-exchange`: `systemctl start taler-exchange.target`
If something goes wrong, restart from scratch:
1. Stop taler-exchange: `systemctl stop taler-exchange.target`
2. Drop database: `sudo -H -u postgres dropdb 'taler-exchange'`
3. Redo `taler-exchange-dbconfig`
4. Redo coin denominations
### Fee Structure
This is run on the offline machine:
```
# apt install taler-exchange-offline
# taler-exchange-offline setup
```
Here you get the `MASTER_KEY`.
Back online to `/etc/taler-exchange/conf.d/exchange-business.conf`:
```
# Configuration for business-level aspects of the exchange.
[exchange]
# Here you MUST add the master public key of the offline system
# which you can get using `taler-exchange-offline setup`.
# This is just an example, your key will be different!
# MASTER_PUBLIC_KEY = YE6Q6TR1EDB7FD0S68TGDZGF1P0GHJD2S0XVV8R2S62MYJ6HJ4ZG
MASTER_PUBLIC_KEY = $MASTER_KEY
# Publicly visible base URL of the exchange.
# BASE_URL = https://example.com/
BASE_URL = https://exchange.bookseller.taler.net/
# Here you MUST configure the amount above which transactions are
# always subject to manual AML review.
AML_THRESHOLD = 10000000
# Attribute encryption key for storing attributes encrypted
# in the database. Should be a high-entropy nonce.
# Run `openssl rand -hex 32` to obtain a 256 bit nonce
ATTRIBUTE_ENCRYPTION_KEY = $(openssl rand -hex 32)
# For your terms of service and privacy policy, you should specify
# an Etag that must be updated whenever there are significant
# changes to either document. The format is up to you, what matters
# is that the value is updated and never reused. See the HTTP
# specification on Etags.
TERMS_DIR = /srv/www/taler.net/bookseller/exchange/
TERMS_ETAG = bookseller-tos-v1
PRIVACY_ETAG = bookseller-prv-v1
# Redirect https://exchange.bookseller.taler.net to this URL
TOPLEVEL_REDIRECT_URL = https://bookseller.taler.net/exchange-operator
# WARNING Configuration fails to specify option `TINY_AMOUNT' in section `exchange'!
TINY_AMOUNT = BOOKZ:0.01
# WARNING Configuration fails to specify option `SHOPPING_URL' in section `exchange'!
SHOPPING_URL = https://shop.bookseller.taler.net/
[merchant-exchange-bookseller.taler.net]
MASTER_KEY = $MASTER_KEY
CURRENCY = BOOKZ
EXCHANGE_BASE_URL = https://exchange.bookseller.taler.net/
# Bank accounts used by the exchange should be specified here:
[exchange-account-1]
# Use for exchange-wirewatch (and listed in /wire)
ENABLE_CREDIT = YES
# Use for exchange-aggregator (outgoing transfers)
ENABLE_DEBIT = YES
# Account identifier in the form of an RFC-8905 payto:// URI.
# For SEPA, looks like payto://sepa/$IBAN?receiver-name=$NAME
# Make sure to URL-encode spaces in $NAME!
PAYTO_URI = payto://x-taler-bank/bank.bookseller.taler.net/exchange?receiver-name=TALER%20Bookseller%20Bank
#PAYTO_URI = "payto://x-taler-bank/localhost:9099/exchange?receiver-name=TALER%20Bookseller%20Bank"
# URL for talking to the bank wire the wire API.
WIRE_GATEWAY_URL = https://bank.bookseller.taler.net/accounts/exchange/taler-wire-gateway/
# Credentials to access the account are in a separate
# config file with restricted permissions.
@inline-secret@ exchange-accountcredentials-1 ../secrets/exchange-accountcredentials-1.secret.conf
```
And the secrets:
```
# This file contains the secret credentials
# to access the Taler Wire Gateway API (usually
# provided by LibEuFin) for the exchange accounts.
#
# Each exchange-account-* section should have a matching
# exchange-accountcredentials-* section here.
#
# Each of those sections must be imported via @inline-secret@,
# usually in conf.d/exchange-business.conf.
[exchange-accountcredentials-1]
WIRE_GATEWAY_AUTH_METHOD = bearer
TOKEN = secret-token:SOME-SECRET-TOKEN
# URL for talking to the bank wire the wire API.
WIRE_GATEWAY_URL = https://bank.bookseller.taler.net/accounts/exchange/taler-wire-gateway/
```
### Bank Account
### Auditors
### Offline Signing
- /var/lib/taler-exchange/offline/master.priv must be readable by taler-exchange-offline
Wait for `taler-exchange-secmod-rsa` process to finish setting up keys before running the following commands. This step may take several minutes.
On the offline system:
```
$ taler-exchange-offline download > sig-request.json
$ taler-exchange-offline sign < sig-request.json > sig-response.json
$ taler-exchange-offline enable-account payto://x-taler-bank/bank.bookseller.taler.net/exchange?receiver-name=TALER%20Bookseller%20Bank > acct-response.json
$ taler-exchange-offline wire-fee now x-taler-bank BKZ:0 BKZ:0 > fee-response.json
$ taler-exchange-offline global-fee now BKZ:0 BKZ:0 BKZ:0 4weeks 6a 4 > global-response.json
```
On the online system:
```
# taler-exchange-offline upload < sig-response.json
# taler-exchange-offline upload < acct-response.json
# taler-exchange-offline upload < fee-response.json
# taler-exchange-offline upload < global-response.json
```
BUG: the last step gives an error:
> 2026-02-27T14:05:42.020965+0000 taler-exchange-offline-427284 WARNING External protocol violation detected at json_helper.c:101.
> 2026-02-27T14:05:42.021036+0000 taler-exchange-offline-427284 WARNING Expected currency `BOOKZ', but amount used currency `BKZ' in field `history_fee'
> 2026-02-27T14:05:42.021049+0000 taler-exchange-offline-427284 ERROR Invalid input to set wire fee: history_fee#0 at 0 (skipping)
#### Signing the offline keys
Then, generate future keys from the offline system:
```
$ taler-exchange-offline download > future-keys.json
$ taler-exchange-offline show < future-keys.json
```
This should show a long file with all coin denominations for this Exchange. Once ready,
```
$ taler-exchange-offline sign < future-keys.json > offline-sigs.json
$ taler-exchange-offline upload < offline-sigs.json
```
Restart the Exchange service!
### Terms of Service
## Test
Browse to https://$BASE_URL/management/keys
# TODO
- [ ] How to generate secrests in Fee Structure?
- [ ] Bank Account
- [ ] Auditors (optional)
- [ ] ToS generation
| ||||