View Issue Details

IDProjectCategoryView StatusLast Update
0011166Talerexchangepublic2026-02-27 15:21
Reporterhow Assigned ToFlorian Dold  
PriorityurgentSeverityblockReproducibilityalways
Status assignedResolutionopen 
PlatformAMD64OSDebianOS VersionTrixie
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

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