View Issue Details

IDProjectCategoryView StatusLast Update
0011166Talerexchangepublic2026-08-07 19:22
Reporterhow Assigned ToChristian Grothoff  
PriorityurgentSeverityblockReproducibilityalways
Status closedResolutionno change required 
PlatformAMD64OSDebianOS VersionTrixie
Target Version1.6Fixed in Version1.6 
Summary0011166: 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 InformationApparently 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.
TagsNo 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
exchange-setup.md (7,201 bytes)   

Activities

Christian Grothoff

2026-08-07 19:22

manager   ~0029405

Well, seems you used sometimes BKZ, then BOOKS, and possibly changed back and forth (and mixing them up is the issue in the first place).
So you really should do a super-clean setup where you *consistently* only use one currency. Clearly somewhere you are using BOOKS and elsewhere BKZ, and the exchange should not recognize them as being the same thing.

Issue History

Date Modified Username Field Change
2026-02-27 15:21 how New Issue
2026-02-27 15:21 how Status new => assigned
2026-02-27 15:21 how Assigned To => Florian Dold
2026-02-27 15:21 how File Added: exchange-setup.md
2026-08-05 01:26 Christian Grothoff Target Version => 1.6
2026-08-07 19:22 Christian Grothoff Note Added: 0029405
2026-08-07 19:22 Christian Grothoff Assigned To Florian Dold => Christian Grothoff
2026-08-07 19:22 Christian Grothoff Status assigned => resolved
2026-08-07 19:22 Christian Grothoff Resolution open => no change required
2026-08-07 19:22 Christian Grothoff Fixed in Version => 1.6
2026-08-07 19:22 Christian Grothoff Status resolved => closed