| Internet-Draft | The GNU Taler Protocol | June 2026 |
| Gütschow | Expires 20 December 2026 | [Page] |
[ TBW ]¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 20 December 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document.¶
[ TBW ]¶
Beware that this document is still work-in-progress and may contain errors. Use at your own risk!¶
"abc" denotes the literal string abc encoded as ASCII [RFC20], without trailing '\0' character.¶
a | b denotes the concatenation of a with b¶
len(a) denotes the length in bytes of the byte string a¶
padZero(y, a) denotes the byte string a, zero-padded to the length of y bytes¶
bits(x)/bytes(x) denotes the minimal number of bits/bytes necessary to represent the multiple precision integer x¶
uint(y, x) denotes the y least significant bits of the integer x, zero-padded and encoded in network byte order (big endian)¶
uintY(x) where Y is a positive integer number is equivalent to uint(Y, x)¶
random(y) denotes a randomly generated sequence of y bits¶
a * b (mod N) / a ** b (mod N) denotes the multiplication / exponentiation of multiple precision integers a and b, modulo N¶
for, if, variable assignment =, and conditional operators are to be interpreted like their Python/Julia equivalents¶
data.key denotes the property key on the object data¶
0..n denotes the exclusive range of integer numbers from 0 to n, i.e., 0, 1, 2, ..., n-1¶
⟨dataᵢ⟩ within a context of i = 0..n denotes n objects dataᵢ, represented in memory as a continuous array¶
⟨dataᵢ.key⟩ within a context of i = 0..n denotes an array of the n properties key of all n objects dataᵢ¶
// todo: maybe change this description to something more similar to protocol functions (Julia-inspired syntax)¶
SHA-256(msg) -> hash
Input:
msg input message of length L < 2^61 octets
Output:
hash message digest of fixed length HashLen = 32 octets
¶
hash is the output of SHA-256 as per Sections 4.1, 5.1, 6.1, and 6.2 of [RFC6234].¶
SHA-512(msg) -> hash
Input:
msg input message of length L < 2^125 octets
Output:
hash message digest of fixed length HashLen = 64 octets
¶
hash is the output of SHA-512 as per Sections 4.2, 5.2, 6.3, and 6.4 of [RFC6234].¶
SHA-512-256(msg) -> hash
Input:
msg input message of length L < 2^125 octets
Output:
hash message digest of fixed length HashLen = 32 octets
¶
The output hash corresponds to the first 32 octets of the output of SHA-512 defined in Section 3.1.2:¶
temp = SHA-512(msg) hash = temp[0:31]¶
Note that this operation differs from SHA-512/256 as defined in [SHS] in the initial hash value.¶
The Hashed Key Derivation Function (HKDF) used in Taler is an instantiation of [RFC5869]
with two different hash functions for the Extract and Expand step as suggested in [HKDF]:
HKDF-Extract uses HMAC-SHA512, while HKDF-Expand uses HMAC-SHA256 (cf. Section 3.2.1).¶
HKDF(salt, IKM, info, L) -> OKM
Inputs:
salt optional salt value (a non-secret random value);
if not provided, it is set to a string of 64 zeros.
IKM input keying material
info optional context and application specific information
(can be a zero-length string)
L length of output keying material in octets
(<= 255*32 = 8160)
Output:
OKM output keying material (of L octets)
¶
The output OKM is calculated as follows:¶
PRK = HKDF-Extract(salt, IKM) with Hash = SHA-512 (HashLen = 64) OKM = HKDF-Expand(PRK, info, L) with Hash = SHA-256 (HashLen = 32)¶
Based on the HKDF defined in Section 3.3.1, this function returns an OKM that is smaller than a given multiple precision integer N.¶
HKDF-Mod(N, salt, IKM, info) -> OKM
Inputs:
N multiple precision integer
salt optional salt value (a non-secret random value);
if not provided, it is set to a string of 64 zeros.
IKM input keying material
info optional context and application specific information
(can be a zero-length string)
Output:
OKM output keying material (smaller than N)
¶
The final output OKM is determined deterministically based on a counter initialized at zero.¶
counter = 0
do until OKM < N:
x = HKDF(salt, IKM, info | uint16(counter), bytes(N))
OKM = uint(bits(N), x)
counter += 1
¶
Taler uses EdDSA instantiated with curve25519 as Ed25519, as defined in Section 5.1 of [RFC8032]. In particular, Taler does not make use of Ed25519ph or Ed25519ctx as defined in that document.¶
Ed25519-GetPub(priv) -> pub
Input:
priv private Ed25519 key
Output:
pub public Ed25519 key
¶
pub is calculated as described in Section 5.1.5 of [RFC8032].¶
Ed25519-Keygen() -> (priv, pub)
Output:
priv private Ed25519 key
pub public Ed25519 key
¶
priv and pub are calculated as described in Section 5.1.5 of [RFC8032],
which is equivalent to the following:¶
priv = random(256) pub = Ed25519-GetPub(priv)¶
Taler uses Elliptic Curve Diffie-Hellman (ECDH) on curve25519 as defined in Section 6.1 of [RFC7748], but reuses Ed25519 keypairs for one side of the agreement instead of random bytes. Depending on whether the private or public part is from Ed25519, two different functions are used.¶
ECDH-Ed25519-Priv(priv, pub) -> shared
Input:
priv private Ed25519 key
pub public X25519 key
Output:
shared shared secret based on the given keys
¶
shared is calculated as follows, using the function X25519 defined in Section 5 of [RFC7748]:¶
priv' = SHA-512-256(priv) // todo: missing bit clamping from https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_sign/ed25519/ref10/keypair.c#L71 shared' = X25519(priv', pub) shared = SHA-512(shared')¶
ECDH-Ed25519-Pub(priv, pub) -> shared
Input:
priv private X25519 key
pub public Ed25519 key
Output:
shared shared secret based on the given keys
¶
shared is calculated as follows, using the function X25519 defined in Section 5 of [RFC7748],
and Convert-Point-Ed25519-Curve25519(p) which implements the birational map of Section 4.1 of [RFC7748]:¶
pub' = Convert-Point-Ed25519-Curve25519(pub) shared' = X25519(priv, pub') shared = SHA-512(shared')¶
ECDH-GetPub(priv) -> pub
Input:
priv private X25519 key
Output:
pub public X25519 key
¶
pub is calculated according to Section 6.1 of [RFC7748]:¶
pub = X25519(priv, 9)¶
RSA-FDH(msg, pubkey) -> fdh
Inputs:
msg message
pubkey RSA public key consisting of modulus N and public exponent e
Output:
fdh full-domain hash of msg over pubkey.N
¶
fdh is calculated based on HKDF-Mod from Section 3.3.2 as follows:¶
info = "RSA-FDA FTpsW!"
salt = uint16(bytes(pubkey.N)) | uint16(bytes(pubkey.e))
| pubkey.N | pubkey.e
fdh = HKDF-Mod(pubkey.N, salt, msg, info)
¶
The resulting fdh can be used to test against a malicious RSA pubkey
by verifying that the greatest common denominator (gcd) of fdh and pubkey.N is 1.¶
RSA-FDH-Derive(bks, pubkey) -> out
Inputs:
bks blinding key secret of length L = 32 octets
pubkey RSA public key consisting of modulus N and public exponent e
Output:
out full-domain hash of bks over pubkey.N
¶
out is calculated based on HKDF-Mod from Section 3.3.2 as follows:¶
info = "Blinding KDF" salt = "Blinding KDF extractor HMAC key" fdh = HKDF-Mod(pubkey.N, salt, bks, info)¶
RSA-FDH-Blind(msg, bks, pubkey) -> out
Inputs:
msg message
bks blinding key secret of length L = 32 octets
pubkey RSA public key consisting of modulus N and public exponent e
Output:
out message blinded for pubkey
¶
out is calculated based on RSA-FDH from Section 3.6.1 as follows:¶
data = RSA-FDH(msg, pubkey) r = RSA-FDH-Derive(bks, pubkey) r_e = r ** pubkey.e (mod pubkey.N) out = r_e * data (mod pubkey.N)¶
RSA-FDH-Sign(data, privkey) -> sig
Inputs:
data data to be signed, an integer smaller than privkey.N
privkey RSA private key consisting of modulus N and private exponent d
Output:
sig signature on data by privkey
¶
sig is calculated as follows:¶
sig = data ** privkey.d (mod privkey.N)¶
RSA-FDH-Unblind(sig, bks, pubkey) -> out
Inputs:
sig blind signature
bks blinding key secret of length L = 32 octets
pubkey RSA public key consisting of modulus N and public exponent e
Output:
out unblinded signature
¶
out is calculated as follows:¶
r = RSA-FDH-Derive(bks, pubkey) r_inv = inverse of r (mod pubkey.N) out = sig * r_inv (mod pubkey.N)¶
RSA-FDH-Verify(msg, sig, pubkey) -> out
Inputs:
msg message
sig signature of pubkey over msg
pubkey RSA public key consisting of modulus N and public exponent e
Output:
out true, if sig is a valid signature
¶
out is calculated based on RSA-FDH from Section 3.6.1 as follows:¶
data = RSA-FDH(msg, pubkey) exp = sig ** pubkey.e (mod pubkey.N) out = (data == exp)¶
Amounts are represented in Taler as positive fixed-point values
consisting of value as the non-negative integer part of the base currency,
the fraction given in units of one hundred millionth (1e-8) of the base currency,
and currency as the 3-11 ASCII characters identifying the currency.¶
Whenever used in the protocol, the binary representation of an amount is
uint64(amount.value) | uint32(amount.fraction) | padZero(12, amount.currency).¶
Absolute timestamps are represented as uint64(x) where x corresponds to
the microseconds since 1970-01-01 00:00 CEST (the UNIX epoch).
The special value 0xFFFFFFFFFFFFFFFF represents "never".¶
All messages to be signed in Taler start with a header containing their total size (including the header) and a fixed signing context (purpose) as registered by GANA in the GNUnet Signature Purposes registry. Taler-specific purposes start at 1000.¶
Gen-Msg(purpose, msg) -> out
Inputs:
purpose signature purpose as registered at GANA
msg message content (excl. header) to be signed
Output:
out complete message (incl. header) to be signed
¶
out is formed as follows:¶
out = uint32(len(msg) + 8) | uint32(purpose) | msg¶
There are a certain number of single-argument functions which are often needed, and therefore omit the parentheses of the typical function syntax:¶
Knows data specifies data that is known a priori at the start of the protocol operation¶
Determine data specifies data that is determined according to the business logic and current state of the protocol entity¶
Check cond verifies that the boolean condition or variable cond is true,
or aborts the protocol operation otherwise¶
Persist data persists the given data to the local database¶
data = Lookup by key retrieves previously persisted data by the given key¶
Sum ⟨dataᵢ⟩ is valid for numerical objects dataᵢ including amounts (cf. Section 4.1),
and denotes the numerical sum of these objects¶
Some more functions that are commonly used throughout Section 5:¶
Hash-Denom(denom) = SHA-512(uint32(0) | uint32(1) | denom.pub) Hash-Planchet(planchet, denom) = SHA-512( SHA-512( denom.pub ) | uint32(0x1) | planchet ) Hash-Contract(contract) = SHA-512( canonicalJSON(contract) | 0x0) Check-Subtract(value, subtrahend) = Check value >= subtrahend Persist value -= subtrahend¶
canonicalJSON(data) canonicalizes data represented as JSON
according to the JSON Canonicalization Scheme (JCS) defined in [RFC8785].
Note that data as input to canonicalJSON is restricted as follows for Taler:¶
For JSON Object member names, only strings matching the regular expression ^[0-9A-Z_a-z]+$ or the literal names $forgettable or $forgotten are allowed.
This makes the sorting of object members easier, as [RFC8785] requires sorting by UTF-16 code points.¶
Floating point numbers are forbidden. Numbers must be integers in the range -(2**53 - 1) to (2**52) - 1.¶
The Taler payment protocol is a token-based e-cash system which ensures anonymity for payers (much like physical cash), while guaranteing income transparency on the payees' side (much like most digital payment systems). Contrary to what the name might suggest, Taler neither is a separate currency (as cryptocurrencies do) nor is it tied to a specific currency. Instead, the payment system operator offering the Taler payment protocol can freely choose the assets backing the payment system.¶
The basic system consists of three types of entities:¶
The Taler exchange is run by the payment system operator. It is the central, trusted entity which hands out e-cash and holds the corresponding value.¶
A Taler wallet manages e-cash in self-custody for end users.¶
A Taler merchant can redeem e-cash at the exchange after the wallet authorized a deposit permission during a payment.¶
E-cash in Taler is represented as digital tokens called coins.
They are public-private keypairs where ownership of the coin
is equivalent to the knowledge of the private key coin.priv.
Every coin has an initial value corresponding to a denomination (denom) offered by the exchange.
The validity of coins is signaled by the presence of
a valid denomination signature coin.sig on the (hash of the) public key coin.pub.
To ensure payer anonymity, the exchange generates coin.sig without learning the actual (hash of) coin.pub
using a blind signature scheme.¶
Wallets obtain coins from the exchange during withdrawal (cf. Section 5.1.1) and use them during payment at merchants, who in turn deposit them at the exchange (cf. Section 5.2.1). Residual value on partly spent coins can be refreshed by the wallet subsequently in order to obtain unlinkable change (cf. Section 5.3.1). Taler also supports receiving e-cash in a wallet without acting as a merchant using wallet-to-wallet payments (W2W, cf. Section 5.4), which are always handled via the exchange.¶
Honest operation of the exchange can be optionally supervised by an independant third-party Taler auditor. This supervision is not part of the basic Taler protocol and thus not part of this document.¶
- exchange -
/ \
Withdrawal / \ Deposit
Refresh / W2W \
/ \
wallet ----------- merchant
Payment
¶
// todo: capitalize wallet, exchange, merchant everywhere?¶
In the default configuration, Taler uses RSA-FDH (cf. Section 3.6.1) for (blind) denomination signatures and Ed25519 (cf. Section 3.4.1) signatures everywhere else. Clause-Schnorr Signatures (cf. Section 3.6.2) provide an alternative blind signature scheme operating on Elliptic Curves. As their usage is still experimental, they are not described as part of this document.¶
Taler has optional support for age-restricted coins, enabling privacy-preserving age restriction. As an optional feature, it is not part of the basic Taler protocol and thus left out of the description in this document.¶
The wallet generates n > 0 coins ⟨coinᵢ⟩ and requests n signatures ⟨blind_sigᵢ⟩ from the exchange,
attributing value to the coins according to n chosen denominations ⟨denomᵢ⟩.
The total value and withdrawal fee (defined by the exchange per denomination)
must be smaller or equal to the amount stored in the single reserve used for withdrawal.¶
// todo: document TALER_MAX_COINS = 64 per operation (due to CS-encoding)¶
// todo: extend with extra roundtrip for CBS¶
wallet exchange
Knows ⟨denomᵢ⟩ Knows ⟨denomᵢ.priv⟩
| |
+-----------------------------+ |
| (W1) reserve key generation | |
+-----------------------------+ |
| |
|----------- (bank transfer) ----------->|
| (subject: reserve.pub, amount: value) |
| |
| +------------------------------+
| | Persist (reserve.pub, value) |
| +------------------------------+
| |
+-----------------------------------+ |
| (W2) coin generation and blinding | |
+-----------------------------------+ |
| |
|-------------- /withdraw -------------->|
| (reserve.pub, planchets, sig) |
| |
| +--------------------------------+
| | (E1) coin issuance and signing |
| +--------------------------------+
| |
|<---------- (⟨blind_sigᵢ⟩) -------------|
| |
+----------------------+ |
| (W3) coin unblinding | |
+----------------------+ |
| |
¶
where (for RSA, without age-restriction)¶
(W1) reserve key generation (wallet) reserve = Ed25519-Keygen() Persist (reserve, value)¶
The wallet derives coins and blinding secrets using a HKDF from a single seed per withdrawal operation, together with an integer index. This is strictly speaking an implementation detail since the seed is never revealed to any other party, and might be chosen to be implemented differently.¶
(W2) coin generation and blinding (wallet)
batch_seed = random(256)
Persist batch_seed
for i in 0..n:
coin_seedᵢ = HKDF(uint32(i), batch_seed, "taler-withdrawal-coin-derivation", 64)
blind_secretᵢ = coin_seedᵢ[32:]
coinᵢ.priv = coin_seedᵢ[:32]
coinᵢ.pub = Ed25519-GetPub(coinᵢ.priv)
h_denomᵢ = Hash-Denom(denomᵢ)
planchetᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub)
h_planchetᵢ = Hash-Planchet(planchetᵢ, denomᵢ)
planchets = (⟨h_denomᵢ⟩, ⟨planchetᵢ⟩)
msg = Gen-Msg(WALLET_RESERVE_WITHDRAW,
( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩
| SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) ))
sig = Ed25519-Sign(reserve.priv, msg)
// todo: exchange.git uses different derivation than wallet-core.git (above):
⟨coin_seedᵢ⟩ = HKDF(uint32(n), batch_seed, "taler-withdraw-secrets", 32*n)
for i in 0..n:
blind_secretᵢ = HKDF("bks", coin_seedᵢ, "", 32)
coinᵢ.priv = HKDF("coin", coin_seedᵢ, "", 32)
¶
(E1) coin issuance and signing (exchange)
(⟨h_denomᵢ⟩, ⟨planchetᵢ⟩) = planchets
for i in 0..n:
denomᵢ = Lookup by h_denomᵢ
Check denomᵢ known and not withdraw-expired
h_planchetᵢ = Hash-Planchet(planchetᵢ, denomᵢ)
msg = Gen-Msg(WALLET_RESERVE_WITHDRAW,
( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩
| SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) ))
Check Ed25519-Verify(reserve.pub, msg, sig)
Check reserve KYC status ok or not needed
total = Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
Check-Subtract(reserve.balance, total)
for i in 0..n:
blind_sigᵢ = RSA-FDH-Sign(planchetᵢ, denomᵢ.priv)
Persist withdrawal // todo: what exactly? should be checked first for replay?
¶
(W3) coin unblinding (wallet) for i in 0..n: coinᵢ.sig = RSA-FDH-Unblind(blind_sigᵢ, blind_secretᵢ, denomᵢ.pub) Check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) coinᵢ.value = denomᵢ.value coinᵢ.h_denom = h_denomᵢ coinᵢ.blind_secret = blind_secretᵢ // todo: why save blind_secret, if batch_seed already persisted? Persist ⟨coinᵢ⟩¶
The wallet obtains contract information for an order from the merchant
after claiming it with a nonce.
Payment of the order is prepared by signing (partial) deposit authorizations ⟨depositᵢ⟩ with coins ⟨coinᵢ⟩ of certain denominations ⟨denomᵢ⟩,
where the sum of all contributions (contributionᵢ + denomᵢ.fee_deposit <= denomᵢ.value)
must match the contract.price plus potential deposit fees ⟨denomᵢ.fee_deposit⟩.
The payment is complete as soon as the merchant successfully redeems the deposit authorizations at the exchange.¶
Deposit could also be used directly by a wallet with its own payto and a minimal contract.¶
// todo: should we integrate payment templates here?¶
wallet merchant exchange
Knows ⟨coinᵢ⟩ Knows merchant.priv Knows exchange.priv
| Knows exchange, payto Knows ⟨denomᵢ⟩
| | |
| +-----------------------+ |
| | (M1) order generation | |
| +-----------------------+ |
| | |
|<--- (QR-Code / NFC / URI) ---| |
| (order.{id,token?}) | |
| | |
+-----------------------+ | |
| (W1) nonce generation | | |
+-----------------------+ | |
| | |
|-- /orders/{order.id}/claim ->| |
| (nonce.pub, order.token?) | |
| | |
| +--------------------------+ |
| | (M2) contract generation | |
| +--------------------------+ |
| | |
|<-- (contract, merchant.pub, -| |
| sig) | |
| | |
+--------------------------+ | |
| (W2) payment preparation | | |
+--------------------------+ | |
| | |
|--- /orders/{order.id}/pay -->| |
| (⟨depositᵢ⟩) | |
| | |
| +--------------------------+ |
| | (M3) deposit preparation | |
| +--------------------------+ |
| | |
| |-------- /batch-deposit ----->|
| | (info, h_contract, ⟨depositᵢ⟩|
| | merchant.pub, sig) |
| | |
| | +--------------------+
| | | (E1) deposit check |
| | +--------------------+
| | |
| |<------ (time_deposit, -------|
| | exchange.pub, sig) |
| | |
| +---------------------------+ |
| | (M4) deposit verification | |
| +---------------------------+ |
| | |
|<----------- (sig) -----------| |
| | |
+---------------------------+ | |
| (W3) payment verification | | |
+---------------------------+ | |
| | |
¶
where (without age restriction, policy and wallet data hash)¶
(M1) order generation (merchant) wire_salt = random(128) Determine price, and ASCII strings id, info, token? Persist order = (id, price, info, token?, wire_salt)¶
(W1) nonce generation (wallet) nonce = Ed25519-Keygen() Persist nonce.priv¶
Note that the private key of nonce is currently not used anywhere in the protocol.
However, it could be used in the future to prove ownership of an order transaction,
enabling use-cases such as "unclaiming" or transferring an order to another person,
or proving the payment without resorting to the individual coins.¶
(M2) contract generation (merchant)
Check order.token? == token?
h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64)
timestamp = now()
Determine refund_deadline, wire_deadline from timestamp
Determine max_fees from price
contract = (order.{id,price,info,token?}, exchange, h_wire, timestamp, refund_deadline, wire_deadline, max_fees)
contract.nonce = nonce.pub
Persist contract
h_contract = Hash-Contract(contract)
msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
sig = Ed25519-Sign(merchant.priv, msg)
¶
(W2) payment preparation (wallet)
h_contract = Hash-Contract(contract)
msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
Check Ed25519-Verify(merchant.pub, msg, sig)
Check contract.nonce == nonce
// TODO: double-check extra hash check?
for i in 0..n:
Determine coinᵢ, denomᵢ, contribution_netᵢ for contract.{exchange,price,max_fees}
contribution_grossᵢ = contribution_netᵢ + denomᵢ.fee_deposit
Check-Subtract(coinᵢ.value, contribution_grossᵢ)
msgᵢ = Gen-Msg(WALLET_COIN_DEPOSIT,
( h_contract | uint256(0x0)
| uint512(0x0) | contract.h_wire | coinᵢ.h_denom
| timestamp | contract.refund_deadline
| contribution_grossᵢ | denomᵢ.fee_deposit
| merchant.pub | uint512(0x0) ))
sigᵢ = Ed25519-Sign(coinᵢ.priv, msgᵢ)
depositᵢ = (coinᵢ.{pub,sig,h_denom}, contribution_grossᵢ, sigᵢ)
Persist (contract, ⟨sigᵢ⟩, ⟨depositᵢ⟩)
¶
(M3) deposit preparation (merchant)
for i in 0..n:
denomᵢ = Lookup by depositᵢ.coin.h_denom
contribution_netᵢ = depositᵢ.contribution_gross - denomᵢ.fee_deposit
Check Sum ⟨contribution_netᵢ⟩ >= contract.price - contract.max_fees
info.time = contract.{timestamp, wire_deadline, refund_deadline}
info.wire = (payto, wire_salt)
h_contract = Hash-Contract(contract)
msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
sig = Ed25519-Sign(merchant.priv, msg)
¶
TODO: what about wire_fees, those should be checked for as well, or do we just assume merchant will pay those? see src/backend/taler-merchant-httpd_post-orders-ORDER_ID-pay.c:2760¶
(E1) deposit check (exchange)
h_wire = HKDF(info.wire.wire_salt, info.wire.payto, "merchant-wire-signature", 64)
for i in 0..n:
coinᵢ = depositᵢ.coin
denomᵢ = Lookup by coinᵢ.h_denom
Check denomᵢ known and not deposit-expired
msgᵢ = Gen-Msg(WALLET_COIN_DEPOSIT,
( h_contract | uint256(0x0)
| uint512(0x0) | h_wire | coinᵢ.h_denom
| info.time.timestamp | info.time.refund_deadline
| depositᵢ.contribution_gross | denomᵢ.fee_deposit
| merchant.pub | uint512(0x0) ))
Check Ed25519-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig)
Check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub)
Check-Subtract(coinᵢ.value, depositᵢ.contribution_gross)
Persist deposit-record
schedule bank transfer to payto
time_deposit = now()
msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT,
( h_contract | h_wire | uint512(0x0)
| time_deposit | info.time.wire_deadline
| info.time.refund_deadline
| Sum ⟨depositᵢ.contribution_gross⟩
| SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub ))
sig = Ed25519-Sign(exchange.priv, msg)
¶
(M2) deposit verification (merchant)
h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64)
msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT,
( h_contract | h_wire | uint512(0x0)
| time_deposit | contract.wire_deadline
| contract.refund_deadline
| Sum ⟨depositᵢ.contribution⟩
| SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub ))
Check Ed25519-Verify(exchange.pub, msg, sig)
msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract)
sig = Ed25519-Sign(merchant.priv, msg)
¶
(W3) payment verification (wallet) msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract) Check Ed25519-Verify(merchant.pub, msg, sig)¶
A wallet can request a refund for an order from the merchant after it has been completed successfully
(cf. Section 5.2.1) and before the merchant has been paid out by the exchange (i.e., before contract.wire_deadline).
The merchant needs to approve the refund via its business logic,
and is free to decide the total amount of the refund
as well as which coins' deposit operations are (potentially partly) invalidated.
After the exchange has accepted the refund request,
the coins obtain their (partial) value back.
The wallet should proceed to refresh (cf. Section 5.3.1) the coins before spending them again
to obtain unlinkability.¶
In case the wallet itself has used deposit to its own payto, it can act as the merchant in the protocol below.¶
wallet merchant exchange
Knows order.id Knows merchant.priv Knows deposit_record
Knows contract | for coinᵢ.pub
| | |
+---------------------+ | |
| (W1) refund request | | |
+---------------------+ | |
| | |
|- /orders/{order.id}/refund ->| |
| (h_contract) | |
| | |
| +------------------------+ |
| | (M1) refund processing | |
| +------------------------+ |
| | |
| |- /coins/{coinᵢ.pub}/refund ->|
| | (valueᵢ, h_contract, id, |
| | merchant.pub, sigᵢ) |
| | |
| | +-------------------+
| | | (E1) refund check |
| | +-------------------+
| | |
| |<--- (exchange.pub, sigᵢ) ----|
| | |
| +--------------------------+ |
| | (M2) refund confirmation | |
| +--------------------------+ |
| | |
|<-----(value, ⟨refundᵢ⟩,------| |
| merchant.pub) | | // todo: why merchant.pub if no sig transmitted?
| | |
+-----------------------+ | |
| (W2) refund reception | | |
+-----------------------+ | |
| | |
¶
where (for RSA, without age-restriction)¶
(W1) refund request (wallet) h_contract = Hash-Contract(contract)¶
(M1) refund processing (merchant)
Check h_contract known and refund possible
time = now()
⟨coinᵢ⟩ = Lookup by h_contract
id = uint32(random(32))
for i in 0..n:
denomᵢ = Lookup by coinᵢ.h_denom
valueᵢ = refund amount // todo: split wisely
msgᵢ = Gen-Msg(MERCHANT_REFUND,
( h_contract | coinᵢ.pub | id | valueᵢ | denomᵢ.fee_refund ))
sigᵢ = Ed25519-Sign(merchant.priv, msgᵢ)
¶
(E1) refund check and confirmation (exchange)
deposit_record = Lookup by h_contract // todo: needs to be persisted before with order.id and used coins!
Check refund possible (prior to wire transfer deadline)
for i in 0..n:
Check coinᵢ.pub part of deposit_record
denomᵢ = Lookup by coinᵢ.pub
msgᵢ = Gen-Msg(MERCHANT_REFUND,
( h_contract | coinᵢ.pub | id | valueᵢ | denomᵢ.fee_refund ))
Check Ed25519-Verify(merchant.pub, msgᵢ, sigᵢ)
Check valueᵢ >= denomᵢ.fee_refund
remove/update scheduled wire transfer
Persist coinᵢ.value += valueᵢ - denomᵢ.fee_refund
msgᵢ = Gen-Msg(MERCHANT_REFUND_OK, SHA-512(order.id))
sigᵢ = Ed25519-Sign(exchange.priv, msgᵢ)
¶
(M2) refund confirmation (merchant) for i in 0..n: msgᵢ = Gen-Msg(MERCHANT_REFUND_OK, SHA-512(order.id)) Check Ed25519-Verify(exchange.pub, msgᵢ, sigᵢ) update business logic refundᵢ = (valueᵢ, sigᵢ, id, coinᵢ.pub, time) value = sum ⟨valueᵢ⟩¶
(W2) refund reception (wallet) for i in 0..n: (valueᵢ, sigᵢ, id, coinᵢ.pub, time) = refundᵢ update persistent transaction information refresh ⟨coinᵢ⟩¶
The wallet obtains n new coins ⟨coinᵢ⟩ of denominations ⟨denomᵢ⟩
in exchange for one old coin of denomination denom from the exchange.
There are three reasons why a wallet needs to do this:¶
Obtaining unlinkable change after using only a part of the coin's value during a payment (cf. Section 5.2.1),
i.e., where coin.value < denom.value¶
Obtaining unlinkable change after a successful refund (cf. Section 5.2.2)¶
Renewing a coin before it deposit-expires¶
The sum of the refresh fee of denom and the new denominations' values and withdrawal fees (defined by the exchange)
must be smaller or equal to the residual value of the old coin (coin.value).¶
The private key of each new coin candidate ⟨coinₖᵢ.priv⟩ is transitively derived from the old coin's private key coin.priv
via a 512-bit secret ⟨sharedₖᵢ⟩ according to Refresh-Derive.
The secret is regeneratable with the knowledge of coin.priv via the link protocol (cf. Section 5.3.2).
The derivation ensures that ownership of coins (knowledge of the private key) is correctly transferred,
and thereby that value transfer among untrusted parties can only happen via payment and deposit, not via refresh.¶
Refresh-Derive(shared, i, denom) =
planchet_seed = HKDF(uint32(i), shared, "taler-coin-derivation", 32)
blind_secret = HKDF("bks", planchet_seed, "", 32)
coin.priv = HKDF("coin", planchet_seed, "", 32)
coin.pub = Ed25519-GetPub(coin.priv)
planchet = RSA-FDH-Blind(SHA-512(coin.pub), blind_secret, denom.pub)
h_planchet = Hash-Planchet(planchet, denom)
return (coin, blind_secret, planchet, h_planchet)
¶
Taler uses a cut-and-choose protocol with the fixed parameter κ=3 to enforce correct derivation
of ⟨sharedₖᵢ⟩ from a single seed per batch of planchets ⟨batch_seedₖ⟩
(in (κ-1)/κ of the cases, making income concealment for tax evasion purposes unpractical).¶
Refreshing consists of two parts:¶
Melting of the old coin and commiting to κ batches of blinded planchet candidates¶
Revelation of κ-1 secrets ⟨revealed_seedₖ⟩ to prove the proper construction of the (revealed) batches of blinded planchet candidates.¶
wallet exchange
Knows ⟨denomᵢ⟩ Knows ⟨denomᵢ.priv⟩
Knows coin |
| |
+-------------------+ |
| (W1) coin melting | |
+-------------------+ |
| |
|---------------- /melt ---------------->|
| (coin.{pub,sig,h_denom}, value, |
| refresh_seed, planchets, sig) |
| |
| +---------------------------------------+
| | (E1) gamma selection and coin signing |
| +---------------------------------------+
| |
|<------ (ɣ, exchange.pub, sig) ---------|
| |
+------------------------+ |
| (W2) secret revelation | |
+------------------------+ |
| |
|------------ /reveal-melt ------------->|
| (commitment, ⟨revealed_seedₖ⟩) |
| |
| +----------------------------+
| | (E2) commitment validation |
| +----------------------------+
| |
|<---------- (⟨blind_sigᵢ⟩) -------------|
| |
+----------------------+ |
| (W3) coin unblinding | |
+----------------------+ |
| |
¶
where (for RSA, without age-restriction)¶
(W1) coin melting (wallet)
refresh_seed = random(512)
⟨batch_seedₖ⟩ = HKDF("refresh-batch-seeds", refresh_seed, coin.priv, k*64)
for k in 0..κ:
⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32)
for i in 0..n:
transferₖᵢ.pub = ECDH-GetPub(transferₖᵢ.priv)
sharedₖᵢ = ECDH-Ed25519-Pub(transferₖᵢ.priv, coin.pub)
(coinₖᵢ, blind_secretₖᵢ, planchetₖᵢ, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub
| value | ⟨h_planchetsₖ⟩ )
for i in 0..n:
h_denomᵢ = Hash-Denom(denomᵢ)
planchets = (⟨h_denomᵢ⟩, ⟨planchetₖᵢ⟩, ⟨transferₖᵢ.pub⟩))
msg = Gen-Msg(WALLET_COIN_MELT,
( commitment | coin.h_denom | uint256(0x0)
| value | denom.fee_refresh ))
sig = Ed25519-Sign(coin.priv, msg)
Persist (coin.denom.pub, ...) // todo: double-check
¶
(E1) gamma selection and coin signing (exchange)
denom = Lookup by coin.h_denom
Check denom known and not deposit-expired
Check RSA-FDH-Verify(SHA-512(coin.pub), coin.sig, denom.pub)
Check coin.pub known and dirty
(⟨h_denomᵢ⟩, ⟨planchetₖᵢ⟩, ⟨transferₖᵢ.pub⟩)) = planchets
for i in 0..n:
denomᵢ = Lookup by h_denomᵢ
Check denomᵢ known and not withdraw-expired
value' = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
Check value' == value
Check-Subtract(coin.value, value)
for k in 0..κ:
for i in 0..n:
h_planchetₖᵢ = Hash-Planchet(planchetₖᵢ, denomᵢ)
h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub
| value | ⟨h_planchetsₖ⟩ )
msg = Gen-Msg(WALLET_COIN_MELT,
( commitment | coin.h_denom | uint256(0x0)
| value | denom.fee_refresh ))
Check Ed25519-Verify(coin.pub, msg, sig)
refresh_record = Lookup by commitment
(ɣ, _, _, done, _) = refresh_record
if refresh_record not found:
ɣ = 0..κ at random
for i in 0..n:
blind_sigᵢ = RSA-FDH-Sign(planchetᵧᵢ, denomᵧᵢ.priv)
link_info = (refresh_seed, ⟨transferₖᵢ.pub⟩, ⟨h_denomᵢ⟩, coin_sig)
Persist refresh_record = (commitment, ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ, false, link_info)
msg = Gen-Msg(EXCHANGE_CONFIRM_MELT,
( commitment | uint32(ɣ) ))
sig = Ed25519-Sign(exchange.priv, msg)
¶
(W2) secret revelation (wallet)
Check exchange.pub known
msg = Gen-Msg(EXCHANGE_CONFIRM_MELT,
( commitment | uint32(ɣ) ))
Check Ed25519-Verify(exchange.pub, msg, sig)
Persist refresh-challenge // what exactly?
for k in 0..κ and k != ɣ:
revealed_seedₖ = batch_seedₖ
¶
(E2) commitment validation (exchange)
refresh_record = Lookup by commitment
(ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ, done, _) = refresh_record
Check not done // todo: sure?
for k in 0..κ and k != ɣ:
⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32)
for i in 0..n:
transferₖᵢ.pub = ECDH-GetPub(transferₖᵢ.priv)
sharedₖᵢ = ECDH-Ed25519-Pub(transferₖᵢ.priv, coin.pub)
(_, _, _, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
commitment' = SHA-512( refresh_seed | uint256(0x0) | coin.pub
| value | ⟨h_planchetsₖ⟩ )
Check commitment == commitment'
Persist refresh_record = (_, _, _, true, _)
¶
(W3) coin unblinding (wallet) for i in 0..n: coinᵧᵢ.sig = RSA-FDH-Unblind(blind_sigᵧᵢ, blind_secretᵧᵢ, denomᵢ.pub) Check RSA-FDH-Verify(SHA-512(coinᵧᵢ.pub), coinᵧᵢ.sig, denomᵢ.pub) coinᵧᵢ.h_denom = h_denomᵢ Persist ⟨coinᵧᵢ⟩¶
Coins ⟨coinᵧᵢ⟩ obtained via the refresh protocol (cf. Section 5.3.1) can be regenerated
with the knowledge of the old coin's private key coin.priv using the link protocol,
integrated in the coin history endpoint.¶
wallet exchange
Knows coin Knows refresh_record for coin.pub
| |
+----------------------+ |
| (W1) history request | |
+----------------------+ |
| |
|------ /coins/{coin.pub}/history ------>|
| (sig) |
| |
| +----------------------------+
| | (E1) refresh secret lookup |
| +----------------------------+
| |
|<------------- (melt_info) -------------|
| |
+-----------------------+ |
| (W2) coin acquisition | |
+-----------------------+ |
| |
¶
where (for RSA, without age-restriction)¶
(W1) history request (wallet) msg = Gen-Msg(COIN_HISTORY_REQUEST, uint64(0x0)) sig = Ed25519-Sign(coin.priv, msg)¶
(E1) refresh secret lookup (exchange) refresh_record = Lookup by coin.pub (ɣ, ⟨blind_sigᵢ⟩, _, done, link_info) = refresh_record if done: melt_info = (ɣ, link_info, ⟨blind_sigᵢ⟩) else: melt_info = (ɣ, link_info)¶
(W2) coin acquisition (wallet)
(ɣ, link_info, ⟨blind_sigᵢ⟩?) = melt_info
(refresh_seed, ⟨transferₖᵢ.pub⟩, ⟨h_denomᵢ⟩, coin_sig) = link_info
for i in 0..n:
denomᵢ = Lookup by h_denomᵢ
for k in 0..κ:
for i in 0..n:
sharedₖᵢ = ECDH-Ed25519-Priv(coin.priv, transferₖᵢ.pub)
(coinₖᵢ, blind_secretₖᵢ _, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub
| value | ⟨h_planchetsₖ⟩ )
msg = Gen-Msg(WALLET_COIN_MELT,
( commitment | coin.h_denom | uint256(0x0)
| value | denom.fee_refresh ))
Check Ed25519-Verify(coin.pub, msg, sig)
if ⟨blind_sigᵢ⟩ returned:
for i in 0..n:
coinᵧᵢ.sig = RSA-FDH-Unblind(blind_sigᵧᵢ, blind_secretᵧᵢ, denomᵢ.pub)
Check RSA-FDH-Verify(SHA-512(coinᵧᵢ.pub), coinᵧᵢ.sig, denomᵢ.pub)
coinᵧᵢ.h_denom = h_denomᵢ
Persist ⟨coinᵧᵢ⟩
¶
// todo: introductory text¶
Transactions in E-Cash between wallets. Commonly referred to as peer-to-peer transactions. In Taler, interaction with exchange, therefore called wallet-to-wallet transactions.¶
// todo¶
// todo¶
[ TBD ]¶
None.¶
This appendix provides two sets of test vectors for testing Taler Protocol implementations. They are generated by going through the protocol operations in the following order:¶
Withdraw two coins coin₀ and coin₁ from a single reserve (cf. Section 5.1.1).¶
Pay for one order with the full value of coin₀ and a partial value of coin₁ (cf. Section 5.2.1).¶
Obtain a partial refund on coin₀ used to pay for the order (cf. Section 5.2.2).¶
Refresh the now-dirty coin₁ to two new coins coin₂ and coin₃ (cf. Section 5.3.1).¶
Regenerate coin₂ and coin₃ with the knowledge of coin₁ (cf. Section 5.3.2).¶
Create an account for w2w transfers (cf. Section 5.4.1).¶
Send a payment to account with the full value of coin₂, obtaining coin₄ (cf. Section 5.4.2).¶
Request a payment to account, which is paid with the full value of coin₄, obtaining coin₅ (cf. Section 5.4.3).¶
Recoup the value of coin₅ obtained via withdrawal from account (cf. Section 5.1.2).¶
Recoup the value of coin₃ obtained via refresh from coin₁ (cf. Section 5.3.3).¶
// todo: p2p sending full coins only works without fees, should we set fees to zero?¶
// todo: refund would be slightly more interesting with 2 coins being (partially) refunded, should we change to full refund coin0 + partial refund coin1 (coin1 value after fee_deposit + fee_refund should then match denom2 + denom3)¶
The test vectors in this document have been generated by the GNU Taler reference implementation written in C. All binary data is provided in hexadecimal notation. Big numbers for RSA are represented in big-endian byte order (most significant byte first).¶
exchange.master_pub = 3cb5e9823db2b335fdb3f284ae960e56be8b081c6819b8
b0217f38b095b4313b
exchange.priv = 1bf4149fa644b3c7f2bf02da4703ff2de3fa160dce0c75
0eabfa0f7ac70a2442
exchange.pub = 4c130aae3246831808a162e2d4330de394e5f0d7dff75a
80f3fa045b1a43eabb
exchange.url = https://exchange.taler.example.org/
denom₀.pub.n = ba42b9e75e48847bab175ed4797384d68a430fd849a914
aa68438349743b2728d9ca97709c15d5c81c7d9d11c84e
c9c60cec03aed4b36ad153768eb30cf9845474e97ee9d7
475ebc926d87135d56926b7df1cea3ec38897a74bc3e24
5e59ffa76e5aff2c2f2eb84e7133a879f4229221f91c7b
ddf96088e4020e16444d94acc30708069f4a1bde3dcbea
d32e3916a9f6722adb3d63bb9075dde49258fbd28a9a09
251fa08b64faee53a506b9f637136c72af7382b9243fa8
996ad4d72cc1b05cfb45271cb3187b4eeec0b5f9e847cd
cc0c3ddef9787164a1aee7e4e1ee6de1b95282c1ff646a
a70aabd1df0003f75d3585e9e740916cee4f7bca0cd82f
72104d
denom₀.pub.e = 010001
denom₀.priv.d = 07bc432fbc6eeb0f9ec2a4f5d2886d65228f57ee5ddac7
88af355493bd9fc247d449b161e41d95080f44d93bd693
8d162c4db5f6720f7479768ba73000f330df780e856e8d
3a01d1778c546e85b3157820de24bc9290238782271a36
94f38c6645f3420331ff70f8930377f4fea25beaccc775
ce796f9ca7c97d0fc82ce7a6880dd437e6b30695ca51a5
8f0239aef7481932550ce61b5b085313cb888c893cc1bb
ef6b47c607e1189f8cccb04eeb9f87b9e53f414850f8b9
23ce83e2859c0434a3d14d861e864fd5ce701af1161139
8a73515b7056b2828e564cdec6052bdbb43ff435178577
198de0676e5d378ade9b31844e81e2218ba3b3aec5bff9
a26621
denom₀.value = KUDOS:5.12
denom₀.fee_withdraw = KUDOS:0.01
denom₀.fee_deposit = KUDOS:0.01
denom₀.fee_refresh = KUDOS:0.01
denom₀.fee_refund = KUDOS:0.01
denom₁.pub.n = d5455fb79449df7bf548e384d5201d75f4f7697ef9b805
1eb8536f66783e70ca2d524fefa6840c1f87d9d8814868
15d5ace866a32269b05bdba7ff6024811c19456026b0a5
9da4db96f58729d286c90be8cd9cbf575d346202b43282
d601b5751ff77a88a0742a692349a0e3fa8ed5518fc537
8fcf434929b541c942573abef310b87776e976e81cf650
a04399fabddbde677163918250c4d45f5e90ee7539de50
0a8e915be1d5f17d79cb9585ccdf5dba42ef53c24eae7d
7c93c4e0a432788245b7c76a587f0ac6889d3038953136
2d51c2ad65cb1e28b4ad4cc97fcd2a4aedd6612ae8888e
b2dbc4de0cf30f46a43b76cc8c3245eefe013195a237f0
2d13b5
denom₁.pub.e = 010001
denom₁.priv.d = 5a520e0e663bbeecf55d5015d258ce8145247ec62aa5d7
6d422cfbb1cedccad1a4ce06527d17d1368d47221ce678
463eff02516d6746914ce5c2c9318364366d31675e1b53
9866d8249f89059a4640e0cd503ec0fe13d7fa6620c07f
8e789ccba4a51f7cbb4f5722bdb211f45b09a0a7f11659
c88ac37939b3accfba4bccf24ea260e21aaec0dfbf8571
78438986e963e9e356277c3badc34fc91b705d9956b4be
dc0947d65924d7dd31e564de798eb06837380caed6813d
3761d119901c5540eb74c3cb674fdfd64b67d78ebd40de
0cafd53c038a0a8e87890b953b8b94a9771a6efb3142ca
02db873053f8d1de2082db4366fc072aafc723c036a2fb
999605
denom₁.value = KUDOS:2.56
denom₁.fee_withdraw = KUDOS:0.01
denom₁.fee_deposit = KUDOS:0.01
denom₁.fee_refresh = KUDOS:0.01
denom₁.fee_refund = KUDOS:0.01
denom₂.pub.n = d1320993658042f995f09ed66ad2283e457a1f44cad96f
1cf5a6299ac93d61724a7448d70e56a38666ec66352a58
10ca3e544d957dedb5789e135589fcb8b139c65a2f70d7
c421dfd4e27cf38d463074d286730be23d446fc0781151
9b068346b28b86b94acff35524eb62436852714c92f03b
0bac21bb6bf727734be7871b702f928c3ced4bad9ba2d4
9cec6a86d1019181c65c16abf7b391f103c90c3eaaaa9d
5298a4a60c71bdaf0f08246327a62399bf4c424c1ed771
8cb5df178daa9710ec9d098e99ed0456de2fbaf7865ba4
7931c52d1cac78cacc1bc451782587487a6189addfe5dc
df6f190f28816707e3d1e104b5a1f7a570b75c1951588f
3289ef
denom₂.pub.e = 010001
denom₂.priv.d = 311849a8066faa083218b4e6444e8af44650e94ab7427d
31b232eb5bb0b3cf8d478cefd54cbfa783f0deb503f02a
97d226fb98f3a708a508a82c886c285bddf7dda5a7b197
69a7bb84961cd5f9c749b2f8ff65bb99be6033cbdb41d1
418d2f3ce5c519fa9d649d4a53ae4c32dea64e81b6905b
831b44155cbab5cef8b6defde09295662cdf2189f2763e
de05bea0acb707b9b6b087dcebb9ca81f1c2785d6560f9
3bf1a1860676cccceb079c22b48b9fdef68e1d286164f1
32ffddeab29fa7a704e1bc3f9f5e28c39675f17136aed8
542aa4470a14a7380a4016b21d43ef5b9e2b241ced017c
54247c12fd6beccb7bd356820b0246553c751b3e071468
c2efdd
denom₂.value = KUDOS:0.16
denom₂.fee_withdraw = KUDOS:0.01
denom₂.fee_deposit = KUDOS:0.01
denom₂.fee_refresh = KUDOS:0.01
denom₂.fee_refund = KUDOS:0.01
denom₃.pub.n = bddb1806b7b7663d94bedbeccbb515987fcae457b5920d
85b8485edac6ab0e73e6991780e9f1fa6f88e500e0ccd9
eeb80d8e3b66289a7cf1b25bd56d05081d583864ad225a
36f8d38391782a85bfe320d439443e8ea19f555edb36fc
b58af81ca5168ca7c42dda68f191bd1587b4da27703384
f2aad300a043549c67fc6defd98bd1074b3bab1bc0d89b
87d81f8a1f555a3b4ce2b4ca7b0b1d8f446fa93258db51
1cc3bc2ff62871cb4c746f6806d0b839db8cc560da06eb
5619d9d42309a77161373708db18e453ff0a1d6f63281c
54ffacada7c8bf5ca7281ffe7e2e5be03fc006c064d820
f719e672429628837399a565dd9d70f6e9ed91e1eb0567
978015
denom₃.pub.e = 010001
denom₃.priv.d = 1e3370867d0a0d1705b18e0d848d73f7d422f920e4aec2
3cea7184b9463a26c1e9704d4cacaa5952b8683eb403e7
8a5c628cfa1b9da0e44d9462f1b7f66c1c883b419f484e
d3de257e113c1828e4e5e3592f1a3bddf78d7f88927ee9
9892c3362ca2c226dbd90971d68a54404d069d5f5704b3
04e131fb3058959338781a203993804f64def2980210dc
3f4e77c1207400e922032952cb154bb1b776d164cce69e
418844f2f4b20850c0a23d30059362edadfe00fbfa04b6
833fc4606deda441eb345c49b52d91063da7c55ecea7b3
2e99a49a823264955803ce3ee220a2cc64cd4321e18ae2
8bb1071c7746e8a3d69adef5113e21a01cab6809668793
c2a94f
denom₃.value = KUDOS:0.08
denom₃.fee_withdraw = KUDOS:0.01
denom₃.fee_deposit = KUDOS:0.01
denom₃.fee_refresh = KUDOS:0.01
denom₃.fee_refund = KUDOS:0.01
merchant.priv = 7607240acc4563ebe2e38e76eaf61d74160d71c9a6670a
e2e5147ce848767037
merchant.pub = f54d646619723f7fa2ce79267953fdd8654cfcd0f2cfc0
c5e880e3e0d0ab19f0
merchant.url = https://merchant.taler.example.org/
merchant.payto = payto://x-taler-bank/bank.taler.example.org/merchant
¶
(W1) reserve key generation (wallet)
reserve.priv = d9641dab5c7f2474573871c25cae2f6c8924ace4157a56
d128b1432e55a6c6ba
reserve.pub = ec8ad5e4c6abcf4d0d597f0066a0e3ea0370d221973e06
e73f4287148cb93c38
¶
(W2) coin generation and blinding (wallet)
batch_seed = 466431296486ed9cd71fc207254820a2c4a85aeb0b2041
494f8bf1f8cd30f113
coin_seed₀ = 88899daa409b8c4c4a91c8e39030d247d00b292eff952e
36067fbe33f2d7ce48
coin_seed₁ = f5be0b8790719072c7043c257f2c114cd8cb45ea5d98c4
aa25d9025f45f1caeb
blind_secret₀ = a3cbe2b0babf5bdfb98f45804acf63225c16e09be1677f
705393df3f5de98ebf
blind_secret₁ = 8ea6335ed3ff8a41f9fae95e9fd87382be402d6c26ef97
2381dee6b3c1e9d775
coin₀.priv = a5a38bb23a9f36564f9d4e566cdd7e3521b04f8ea4175d
a726ac4e3ac540b485
coin₁.priv = ae98c5207fe31e20a7b7a8677b780c6735ce69df32d308
5d504010f23c59064a
coin₀.pub = afbdf99f1a794add25bad59c9a3f442714e166adc99c57
db991947669a36b185
coin₁.pub = aa7118596b1adf89543e9afcf2925e516a782eed9d75c8
abc4a470e7efdd7fe4
h_denom₀ = 8653090e3f3f5efac9770a5dd0c97813940c191c9fa171
3367f8bb1279b1e3d27f32d16ea7699501e5fc4d176725
d76ab0307f9bdf9d0d263c2256a7f1c63143
h_denom₁ = 1de6052bce12feaa2d26e3bba1789b0c2295028279f3ea
ff8e1345448ba05390cff49656ed72a1f7ef6ee2c4a80f
80eefa0d0f906e74305b0215c4194ab1c7b8
planchet₀ = 64ede0cf40a952b95af4b09b22c6fd27a1ff6d0ae3920e
a03fe14a8b547f1e14a2eea98443e7aa092075f6585103
fb06c1d68fb4616bf33ae30a207e51840f1ae655a73418
4a73d804df42650ce296954c9b61e249486468f7fda8a7
4f85ec6ccdb34a66819fbeaf846b10087ffaf8e734428a
907b96c6a112f8f943b3a8d55455c01b0daf55456acf07
15874ff90fba45b265e2021a70997100fb18bfbef822c8
475820b4855f141730751bcfad2e7eb46dd6c6a400a8a6
a98486c4a2bce4d3407a0d0ba612faebb49428ba3a1986
eddadb04b6790fbb7bd27cc0bd98341c60ec5b98027520
2aedd4b0cf3b7ee1732c67c84db4b30b60b551cbf84922
2c60e1
planchet₁ = 41f8a6622c99b6a9c30d52d19fc2c20a39a481219de4b0
f3e7f604efed5d962554ea13255785d10a037e14abd75d
80a0aa9861df9d80a051c418ff197e68bd0eee61e6fd5d
3214633734659e8bbd7dd2fe566bd41c537a5759bad9bb
ca6b2eb7e3217e7d3cbefff192d83bd0527dbe0eff56df
0b2e2062670d2873ca7766a7c1255e6fcf2c37e95de19f
cad244f3986bab45c0a3aca726fc682455ea1166a23891
52d7a67d181f035de6bdb97633888f0594c2f63dd39651
34372507faf84f753759abd662d4c7e8df7e01149ff44d
a3499ed49406d9398e86c4abf12a1f57f38002115ef728
3e4d5422b41c752a0cce8c2c1a9b84e109b4554fc1150e
f578b9
h_planchet₀ = 888da42dbf4897a5d6d9420b827e340c74c0b0e973e140
ad1c7b9abfc400a9ec5a86b3185e4c656dbe8e01125604
616de83fa28910fae513d016f56c3ca94eb4
h_planchet₁ = f20015bbe0147313e59f9e64ee9e836c192b5d504dbd33
28fcfce41fbe9310d6aa96d91fdd77599c122cc5390ad7
cb86d2c6d4261bd5950e7ec0f86959613841
sig = 2ae0027dd4a196c4741d60e3e7ff933d7374fa5534d15f
1cc2e96e9ab3392894c65ab6a0af31ccdb395db5c52677
0077642502e27d4619d385a91dd13854e706
¶
(E1) coin issuance and signing (exchange)
total = KUDOS:7.7
blind_sig₀ = 139af4e7e7f6e9e0391e22f78cb0b2c334e6d6667633a1
92f56fde38ac9feec7841be59b2aa993084c50f70dfd96
73dc723eec9d84c28ebd5f8104d1b0b29cf118cd64cbe3
3925524a3363a1a6f24f59d47a404cf74680eee97cdeb8
cc44a0b84f3db4496d9bed1224c0fbda64d40f84ef5447
d764fb714ca8e72a23a882e749e47a519310f6d87b3e8d
dc3e4ac4e541cdbfe7550c92f41c511b544d9fbdc2a729
6b958e9df0b1394210aef4f1a780a129883d4bcb45425e
ef96204a7eaa9daa7d21827ecbf4571cefe6dbe65314e6
1c688175c1e81e01d4f0377c9c2312b6cc3487f2dd2cf8
d85efde346f4d1a12de70bec4038b0bea0c505087c0e67
ce4694
blind_sig₁ = 89649cf83e2ba2cd75c7a1d01809c33d5d02fec8164925
6215f13a8965ad37d8d47c264668ec187e46d75210ce60
1362fbb15f61a24ec1d337dd2589b5a4ee0e77a8d9734a
ca6a149b9f9b3a78c158b1f8cb243b8cbe9c2212290e8b
c731d02382463f3e1cf0fc86c5472b8c96c5eaeac9c906
4fdf7411f97935f27fd0bf88200440c7cc3e6960c5c515
6aedb5a820077be08f1ac4b00780258d7b843b21d7baf2
d969d1b3c77a08b4fe14b216fe78c7e4c0ae964fc06bf3
dfe7ff20ec85fa4d5e5aec24d89848645ffadb0de3da80
c6e41b96571ef28e3d132b7c3fd16a289e32389cffb653
b4744fabf882342f0db9aef110351d8ffd7a3b86701f0f
5034c8
¶
(W3) coin unblinding (wallet)
coin₀.sig = 9303650b8896b6619ca061f4bf44c1b02fb60784a2659d
e7512b599e7e6524ad5a283c81d289d3ec75fc81e7f336
bd41dd67a41b72a98d9171f0461c188535b555019079a1
8b4491903797f62a034e5f277ce8f7f3f56fbe47e44113
561fd414c841c2da97da1ae92df0e2cfbe3c26ad37f8ba
b9bd71365ac536204cda7c21dc39891b5cbb4e213f75d4
e09c248a7a59f6322fd011619b29ec25ad621dfa2d6fae
9f8b91935caf54d0d30f4a03bf6beed6637a4054efbad7
a41f9da434f07cfd2f7619e8a3c82b30e079ff9fe13fc0
23fba7d627f4fb21e14467ce1db27767831eb1c3ad5c39
f61437a0a70c300fd84cd9280474bb507768a868ea92fa
905428
coin₁.sig = 355aa5b6bf10591375f22a014640cd242f470834759fab
4421b4d237a149bc3e5fbc8f073e1348da770983cd36df
9cf72a30bfe55776744b2d787acb06ba25ccdef36e5269
771fe4a38a1109e8b96437a3cc625003f7fdf4d0cc8793
35584c5b13f2b29be1ea4d8a2da7826d608179d98b5edb
11261e2048fbbba217a52ae862d1e78e30c0787ee40908
f1bca708c434d5412a262eca6bcd0ac5890bfbbe7df6d6
9a5544d0054716dfda47791546c177223606c0645858a1
5b36887619a9d70a515f42897758bbe181765d7184cc92
b5e2d743df1db9b30394b697117ad24811812febe0a3c5
7cbb5cf7f6857378f57cd1149f745ed95fd1d342e9860f
516469
¶
(M1) order generation (merchant) wire_salt = 4c5249caea865380e0e519fc38177686 order.id = ORDER-40 order.price = KUDOS:7.42 order.max_fees = KUDOS:0.01¶
(W1) nonce generation (wallet)
nonce.pub = 6729d69abd7d8218e02c953317b46bba2522efc2c19a7d
a63194e9ef40fcbd0d
¶
(M2) contract generation (merchant)
h_wire = ec4e7258747a4de49628a27907b74f0aad097cfafaa595
320f81786375430b67ae1ada754299bbc0472f5a1e8bae
6fe8e5dc52683dd09490ff06fb2fa4f20ec2
timestamp = Sat Feb 14 13:37:42 2026
pay_deadline = Sat Feb 14 14:07:42 2026
refund_deadline = Sun Feb 15 13:37:42 2026
wire_deadline = Mon Feb 16 13:37:42 2026
contract = {
"version": 0,
"summary": "Free Software Support",
"order_id": "ORDER-40",
"products": [],
"timestamp": {
"t_s": 1771076262
},
"refund_deadline": {
"t_s": 1771162662
},
"pay_deadline": {
"t_s": 1771078062
},
"wire_transfer_deadline": {
"t_s": 1771249062
},
"merchant_pub": "YN6P8SGSE8ZQZ8PEF4K7JMZXV1JMSZ6GYB7W1HF8G3HY1M5B37R0",
"merchant_base_url": "https://merchant.taler.example.org/",
"merchant": {
"name": "Taler Merchant"
},
"h_wire": "XH774P3MF96Y95H8M9WGFDTF1APGJZ7TZAJSACGFG5W66XA31DKTW6PTEN19KEY08WQNM7MBNSQYHSEWA9M3VM4MJ3ZGDYSFMKS0XGG",
"wire_method": "XXXX",
"exchanges": [
{
"url": "https://exchange.taler.example.org/",
"priority": 1024,
"master_pub": "7JTYK0HXPASKBZDKYA2AX5GEATZ8P20WD0CVHC11FWWB15DM64XG"
}
],
"nonce": "CWMXD6NXFP11HR1CJMSHFD3BQ8JJ5VY2R6D7V9HHJKMYYG7WQM6G",
"amount": "KUDOS:7.42",
"max_fee": "KUDOS:0.01"
}
h_contract = cc934a29efa612754edba0453d1b0ba175d6830d3ec2b2
839a9a539d845a5da4622498e3819cb49206810688a93d
e1989c6542d2cf1d71f64dc998193e76d535
sig = c249ee766b4dd256560d48b367d05a41cf7169306247ec
56c51c39412b34c11015e365c2b2836761ba020b7175e6
0870572a6c7f780eeabb6f0777783064d602
¶
(W2) payment preparation (wallet)
contribution_net₀ = KUDOS:5.11
contribution_net₁ = KUDOS:2.3
sig₀ = 8876dce4ca504df355f17c783e8327ea29b2cbcd7dde60
8b473644af3580621d3eb429022f6d1c4937328db478c6
ed9784f6788e2e18dc16927efd4bc9c10908
sig₁ = ee97e05b9bcd4b49b4518b8d6fdd90b5003178ec6bb4ac
b5e9b14f189050c2320489042299b563e8ce0ba889d88c
30d4b438b6d9a1609be3326ea9e12ee46a0b
¶
(M3) deposit preparation (merchant)¶
(E1) deposit check (exchange)
time_deposit = Sat Feb 14 13:37:44 2026
sig = dffeeb7b3ee0094a2356a3bfed370ae0f082af00ab09a8
35de67db0a8720488419c4da975e9b548e7e554767f1d1
b380afc2ea72a93f83885ca3fbc138e4f80d
¶
(M2) deposit verification (merchant)
sig = e66d503c843d72da4b6eb95f872396449de25cbf80b9c2
43d8c9ed2bde533d2460542c685bfdbf2eabc755510a60
b97ed289711fd3f353cd80aa791e2e2f1e0d
¶
(W1) refund request (wallet)¶
(M1) refund processing (merchant)
id = 14365434601518496594
value₀ = KUDOS:2.1
sig₀ = a626cc04101e3abe295c217de5636be135be8935dfaf0b
3589a8c7de22c3e6c7a581a031c0691476e82ee0e9798c
3c8c8be6bc1066bd955acf4d1ce59eb7e10d
¶
(E1) refund check and confirmation (exchange)
sig₀ = be9dd11dec0e2fb2a2dfbc778b2d9bb21c665ecfa4d1b6
c9d42747902f071631f84a946881bc787f8627a6871226
4b06410b445f37e569edb7dbf77193b5990b
¶
(M2) refund confirmation (merchant) value = KUDOS:2.1¶
(W1) coin melting (wallet)
refresh_seed = 466431296486ed9cd71fc207254820a2c4a85aeb0b2041
494f8bf1f8cd30f11394223cf8a82995804957876e9fa7
1163506b4e5b8c8fa4db1b95d3e8c5c5fb5a
for k = 0:
batch_seedₖ = 415e62ec89f6397c834087efe396b127c6d5bdfe360145
a6abbfc7a88c6504eee6e3f59db026cc5742c4065fd917
bbf2b7f52e82e88409263130300279e52617
transferₖ₀.priv = 5a08ea181dddd480b2e1e2294e1d886efe45070858adb3
26e2d562dc330c8ca4
transferₖ₁.priv = dccf4e1880b343687c7f39df603386a27510bd356ae1bc
86f45ef9b7a8927613
transferₖ₀.pub = ab4a35f7fc78a3d07e5828f0fae0295ebbe93e9ecc63c1
09703f15816af8d000
transferₖ₁.pub = 09893d5ef58fe696207e93cbd0cceb311265aa89745705
9c40f366d59b028a29
sharedₖ₀ = a7a9524dfc565b8006470ab90cba091a809f1de14eeda0
dc98408f880d5cf587c2aa338ea1e3153ed8c624044f15
19f3d8e8d6c731d7af6d0383b136a48a8e7f
sharedₖ₁ = dd7051d4c4b03d78f0c5365ea0a2947f48604beee2e792
0349d7a4371dbff00d9986202459277250461ab936d5f7
45bb6c6c28e8badd7936002f25d9b97a65b2
coinₖ₀.priv = c4fe7e3e64453e477e736ebabe2e5a7c4919eb2fa4bf02
9e536d56a7eec59c9a
coinₖ₁.priv = 409db2ea007fff35506607da6c67e52204097be8a114b7
a654f53da392886e48
coinₖ₀.pub = 65a93d56a84e8110fc2ae9c6ace89ddd9c625bd632501b
663fefd047ee31feed
coinₖ₁.pub = 7c61cf322d34ead1de43869690c70e22d9bae8f2354e0d
d07e71c5736b506f41
blind_secretₖ₀ = 0f53bafa492d5d90dd3ad13ba7b37b3c4a8b167dffc63f
7940dcb340275be90b
blind_secretₖ₁ = 799169aeddfec48aaab23a88df8d74318870c7b6cb3922
5a3c43d4576db61f88
planchetₖ₀ = 90997841cb9d3b4bef237213b1ee1da9286e775c1cc10d
c78e32f836d7897500a587babeefed3f310a5b509c29d1
91804b89d2aee75073f4d49d7ff5f60be991e0ae1a148e
134b92c5c9537a59bc30516e0244e714ec2b6067337ffd
970fd3987799d2ac7b3e3430068c923974751864ed8f4d
087b0cd62e0edb807d9ac4bf70e68c9d774a0f26947413
0f4fa5a8f4e0a5ac67ef6c73a1cb486792605f447384ea
9deb040f851953db240a5f23401e0d75f93f810132ed9e
782ba05b4f78ce7cafc501925ce0bde12e58ef48861969
038a95802a1d943b2ce31a3278a1cb4f14cc12ff2ffee1
3aba0ee151145a3b7abdd1a00e0684a57a1b2a0fd97348
81243b
planchetₖ₁ = 0562f874175e223f087e7950f2082e869bca1ded7087be
27a98f1f9be30a38f33e9a98ca5efb6f15536df774ce32
1efd36909d67869e704db3ff352c76298c0d33014a32ff
636c22866b5e5d6dbc5e6c50f630d95112e8916fd23ec4
88eb457f55ad3a29d50c3c7387b4ee1045cbe4d37241e9
b958d1642b34f4a15a259b344163dbf73bc55e5b99e0d4
6ba59b7dca0a1fbe859f6f7d5756d6afff58571fdac617
48a81e5e94e784c2d5de60a192d86c0529e35f2efa29d4
2e0ce8526db5fd3258a470a43d55c184287fe0c639ff8a
eea1ee64a61d6466d15e20c8203a73ce1b2fc7b2949947
f9804b0710e41351af67cc30b86108dca7ea0208062ae6
0be2e5
h_planchetₖ₀ = ba016fdedb6c5033f1f870edd9ab10d0dd116c0e3a8483
0ddd44c9aed5a2844aea96146cf7c72153a910ca3d3de1
faab7fca5e1c4079e2e0c680410cf68ee27b
h_planchetₖ₁ = c4f4451f85f02c950fba164ccc52fd2123576ddad6ab87
c25b175a33aed8afff5c5866b2fbcec6ef67a1367f91e5
da56041371bc9e8cefd2a93ad386504568a3
h_planchetsₖ = 7e420d01a6413fb6cc300257031f37ab2d456583d3189c
3e5f7049c1c61f49c7918289b7cd169cebd0bb81ca0a83
2f2c4c6c7529e861a88d65c6d3ef5fd540ff
for k = 1:
batch_seedₖ = 0311a9766673b9df343498afcd6f6926b9bd6352201351
5c072a0b5a3af98aeb8158869f3ece08532473980a652d
d6479f296a28504b850791f9775e3b0f8436
transferₖ₀.priv = 4a3d33228a7c1d6330fb2ec827256aad75f7ea56712072
f8ff69238b47e98895
transferₖ₁.priv = ad0b2c176a9564ea7aee7c55ddd92af22d777ea4a8605b
d486d8ccc31bb1402f
transferₖ₀.pub = 1839010739d0450e9a0702167d5106dbcb0a81641e0080
716785850e60bc541d
transferₖ₁.pub = 16a20ed27a63c204d94ef3d0c2b554343567d319afc70d
856cc6a61c3bdaa142
sharedₖ₀ = 32751bb2cf2a3106593aac0fb38172943c54a96992b22a
7927ce37fcf0b9b4aa4671033c93ece3c70f2033e4c52a
953bf46254db7f6d7feb926d78b28bb60901
sharedₖ₁ = 2c4118e0d52fe1851410fb70b60cc5b3fa6bbb53df8acc
5a379c65d9f89c2ba37ea732e76d081e03d845dd2f3ef8
a53b682941d4e598a17823ea06cd28058e8d
coinₖ₀.priv = 16977a220b0bfcb2a0ef82faa3afea901d0cf3e6829905
85b63d9c855be5d5aa
coinₖ₁.priv = 1b5bab529c09f313f2a18e3d2712c1a7e0548f15651b40
af12b22c17ca861273
coinₖ₀.pub = 4b53ebc94ca1a96ca4795a0ce1254f70ba2fdd72528d57
4f4bb47d26fe845239
coinₖ₁.pub = 6de1ac4370a5336349ed50cfd4d6298b193504c1325614
9cb0fa85c1f9a178c7
blind_secretₖ₀ = 30e3a32819cdc5a2e9b28d7953bba0038eaf701ceaf691
a6bf44833ae6f305a9
blind_secretₖ₁ = 62ac62d9df0b5f71f0b43329baa15c5e79f46b09fe432f
cdeb3a9100b31a0fa2
planchetₖ₀ = 73cbbfab1a58b12c55b4eecf705c9db8fb953f52bfb4fa
9922f39641efb85802659d266f979c819294af91cb395c
f93f67a0a6b561e445b0a138f04e6428222ccb5ef92a21
1da2218953e8aa108f339bbce666a71640f555570d6fec
a446b67be82edfa3f90697d5558d7b7707bb87bf790d35
35e429d705367f0ceb3fea30f7666ed428679bed440540
3f2a9cf5cf75a43f16fc5b63276e9a18bea0c0864587c0
a884d1a474f3a248e36183a067e59496d8968427c55e63
ac3f0052fc20fea5b9f01b19ee776e7c1bdfa576be42d2
1e7214606bea3277878cb7beb7015a242bd9bfaee620dc
ccdfda8f34b7174ed28766000d22dd552cd487fdaf9cd0
cc1f5f
planchetₖ₁ = 922bf6290e8f5e8746f68cda30cee9d368f3988b93d26d
c753e082fe56db8191430ce8ef78f1175d651b491e6cc6
7e0cf34253abcf3c3bbae44b4620ca0879171710caac83
89704d99d5d49da5de04b44f03d03b30df9d872611d779
6c6903fdee77a68a29aabac5c332588aaecf2a5e8cbfe4
446d33f423040e7eda99268f2ca558615b541cd912ef30
2a9f079705b9d1785839a593d07d8b79756f0077cb5169
a3a88f08ee7371419c940f35250d2aa52bcfc6beab73b9
72f31d3b5de16e55c590788cb4443dc0de7d2cb6993555
0b04b8bcc2c33669f97ed1d36f29c594c6f0f84a72bd73
f76c7617e748356f7989a98deca97f360fcf2d087f9bcc
d61732
h_planchetₖ₀ = 6523f04c8eacea9af24c08905eb009c8e15a6c62adfae2
fcee6813bba22ba014c3c831cae6fb63a2a889af0e99ea
d40a39b57d42043dd67fe4e446c7691558c3
h_planchetₖ₁ = f86bf5d2bf56d182ff70b0ecf635401bce1dab4bd52f40
023adef27ca2a4165d19b0de911a6a6e41e5811ea1d9cd
9bb17dbf9ee518b1f3bfec644e8a36f7fb2e
h_planchetsₖ = f63471c1555835b7032ffa41db189f013d1f26dd5bf1fd
c79bf18ea8c3f24ec7f31f0b4c1688d7a6a729c7b2c2ee
f42a5ce3524ca23a566d74e708eac487b1dc
for k = 2:
batch_seedₖ = 35e8f7b41e25d3ca2c8e70d02be85843b53703f199809f
98527e316bda6a457a2fab9d327f96242c4e0afbd6214f
aee6a5172aca2b49ad37a12590dc3bbc3f58
transferₖ₀.priv = 1ca613b8fbe4a341b2fc617687553e778de9b8d40c462a
88bc1ac169d2984da7
transferₖ₁.priv = bb8196a4a5b9a646c5424664b8505eb8781ded006c74e7
cc719e7a2cc730ae88
transferₖ₀.pub = e21056df9fdf4cd94a4175f94a48fce623db6f2bc4097d
818f839ec421fe974f
transferₖ₁.pub = 0409b44ddfed03111d7dab5af1b71d8b2597ee50772a9f
272e799133718fd571
sharedₖ₀ = 01a355feafd4daafcd6341597b73eee6ca7ad5dfb78758
88a373434043d17087ab882cca24e580985068eb1b39b8
c0e7f81f03ccca337961cd1d6a528abd3cd1
sharedₖ₁ = 39cdc5377c7295d1e997a43fdc2aac0f5005e64211f7f0
9da36db035ddad5bc32c2dcc4bd5fe1e0d3e471ae8fcfa
9cac0fefa8078570b894164194829e54dabf
coinₖ₀.priv = 343d0b591fc0c495d2e7cb68f2cb0c24d9b274e3ae8504
1dd94454d2856059f6
coinₖ₁.priv = 57c9447a9a34e4b7ed5f4bc71122d76dee9583cb578eb8
eff2e837bc9a0591e6
coinₖ₀.pub = 3b9fb220f7679b3488f8e2710f41d5e6cfee1b6c23bef3
04d9dbfc404341fccd
coinₖ₁.pub = 6ee277e7bcd2584c5ee8f46cc24b531515a33388e6035d
00b33df8ce6a7edaee
blind_secretₖ₀ = f5d25d0124dce3b3529325b6512fd34293448b710217fa
885ba5d80705d16a64
blind_secretₖ₁ = 974dc1b68e4a7613346375372ead838842bab6335d1696
af3b6a2ad249fee4ee
planchetₖ₀ = 55e3f72dfd4c53ba21f6f9e108c34f8f6bf0b69dd8daab
bf4386a7e3a31c7e33ecda1dc54a31a1d5f97b810806f9
d845af20716f4aad733026ff0f6ad0219a1f92d937ade7
6b268f8d436fdf6ca63d9051008efa3af022c9846fdecc
79ce27c8e59042795f2f8a4bc0a792b44bc64c8e6e7c27
3a107d96081142b1d06718e28b32bc733ff36e7b95538f
b2ec30d1242965f9ab40debe4ed845227b7a4a4d795286
6b798253cf1eb664a1d832c2397d044122d24982fb88bd
68f81efa45f80bdbe918680004bdea5dc2e4e76dfed297
af200350793f820aa094e1b26c79e2c2faff866410e741
1bb662c94be10a841ec9db1432d198a4a01c2911326cec
da502a
planchetₖ₁ = 978fca7d513afac1d6ec6561fd967218bf0d84e388a23f
b085f1c37493d765090b90c0c23ba20647114c0f34d8db
1041809a66dfeab96c36477be8342ff4f2660cfd8f0011
14f2aab3d12d983d82b92fcfb38e97fa83142980a9f919
7e794859f439b18491600ea92bcdc1af7ebe4784eb0229
bd287b9c71cdde177d4576d7907f9d643a363e09685293
b7474c990d5d8cdc119d7448d768412e40e8461ea88109
461e37f3a76178df074c942e99062ce717eba48903c7a0
cd27889429ac5a657ca50efd441363c18f9968b33962e4
960bf2005fc0fb2b8050024bf34e8f608f4dade5554aab
1d28478f5bc1c5939c939ff5b9b11f225e7eb275887d3a
475a34
h_planchetₖ₀ = 5c9603dc7240248f0a93107bce217562d60943c9976770
d11c1fd4c9f73fe7f2f3696696b37fb0fa73331c301e8d
d7adceafb23456bfcb6d44063d61f5db239e
h_planchetₖ₁ = d905de829b86b21eb19c036320d3e11571e6ca8390e81f
86e4569e3cc12189ea10cd752c738972a809fa292818a1
f13367c389a3f505f084c5001615de3b647e
h_planchetsₖ = 802838537720786bbe61a0752397056c3a7f7bc332a54d
c5a649452a9a74fef79c2c59deaf1e03cf73f85f241f28
61a50f2f859d95c3c660ab5e57b448526036
value = KUDOS:0.27
commitment = 2bce7a8fc6d5ad50b648c15ff1e5967c2491bfde09c7f0
465026f6603bb42e4723e8e601a0004989d8676f205f6e
3f8da392830cdfa77c47a342db4eaed2ec2a
sig = 43b9734d2547872fcad7c40d151f5a4557be026b5058c3
74a434be7b7e0be110cd7ad27f01ed33ab0c335f3d135e
c9b3abb47a761e0640ac272b6ce8b032ff0b
¶
(E1) gamma selection and coin signing (exchange)
ɣ = 1
blind_sig₀ = 1885a01f26936f28dfd201a7dd615dacd68a287e17aa8d
d1e4f3a1925047049a491c1b76ca687431faf6fd290deb
4dc80601ba79d7754c84a8d8550a0b166104b6ec7e611f
bc577aff339033d421035578029068a84bd4fe5e212670
fd2ce7091ef9950ee48778aae2855a99555308ae90b72a
b1fb53fea714841c94e256705aae8efeebc877efd376a8
78c6e2cb4e811a267484e4da8e13206c1dde7ef3646528
c1d8bf48096eabcc454ed5ee180e3fe7229785a51a3b8b
d3fba176c05baf0eb82e99d225cfa5f9359d07d2510013
301a7bdf815a7db36d400bc00ca861dbe1402ec6dacbba
901e8c8e4e8f1912e63e2a440e667cea243d46e08026d3
738fab
blind_sig₁ = 9c10e5b88fe3563bdaba5934447baad4d05e66a9075102
7a5cbdd2cf0cd9bcaaec68935d3c85509894247ab569d9
7fd4825a7972d48dbab1c9cbf791776c96a8dcf48caf2e
542f5e50f86d794066b7bc82e975c55995b40cf10d00e6
4904530dad5c396543fadf88b79f81b00c8ebbaf2f6731
f434c5c3d72b451586d0412b0272a6c6ac5ba5ef59af57
969d4f985ea513187c8020a318d4a9c9711ccfdb5aa8b9
ebd93b20e129c3447771a1dfb1b1bbdccaef56323005c5
6116b1df97ace4ae6f6865e5467a96d2fad9cb4c9cf0c0
3cf774ad1dd8f5b0a0fe218ac78b37c4435e4f5616c2fd
96dba36f3c7c1973835fe53c95839ed11c792ad011c9fb
1ce55c
sig = 4ac44b89d7e2b0d117c9ffbbfa883db10b551f10b47123
bf69af42fe8544fbd90efb7d52b649f47d69e0f89ee9ef
10f3b7a3c35eddaecb58243171dbcafc4801
¶
(W2) secret revelation (wallet)
revealed_seed₀ = 415e62ec89f6397c834087efe396b127c6d5bdfe360145
a6abbfc7a88c6504eee6e3f59db026cc5742c4065fd917
bbf2b7f52e82e88409263130300279e52617
revealed_seed₂ = 35e8f7b41e25d3ca2c8e70d02be85843b53703f199809f
98527e316bda6a457a2fab9d327f96242c4e0afbd6214f
aee6a5172aca2b49ad37a12590dc3bbc3f58
¶
(E2) commitment validation (exchange)¶
(W3) coin unblinding (wallet)
coinᵧ₀.sig = 68773443fe6cf88ddea6f6614213f12ec7ded4fbb39fa0
a4ffc1a68bbfc363be0b33bd03a41d31c8ffe331614ee4
b986679ac8e51aaa0903eee492d0ff81327589c842ac80
a6b47e0833840935e9cd543fbbb91c5b80a591e01eb34d
7bb5aa3fe837b22f8dcfcaf0ee9d71d93c866f00a8f787
def0b79eaa4e6e96c420990b05c2b82c378757ce220e96
734e547a6962148848d2ebb66e9c67a40115a958d21c05
c7e0a0db72e505076e35ddca7b09b603b55dad394c1d12
ff4b6b219feafc3ca24c43c36ad2da9fa632ec1bcfc057
2db80d0afcc9875182def7983385f872005033d7ea7080
bce0df982a134f5ae2dccb2cdc304278c809979252ac28
2a76e1
coinᵧ₁.sig = 1551b2dcf6daa264d4c96452f46c88e7b4ca3955642da4
5e375cb319602897ceb75eda64060afd17002b63fdd39c
c0f86c473a530813c23958573431e2fcd2277cc853f5ab
6a20a9e7499154420f0cd8d13990d45423e61a6651a614
7e8a146a10fd5d63e085c2c4c133d4db0827df1d4fed10
d1e6eadc566e167a17fd36ee884900db9a8cc4b82a02b7
ca0cfacb7d391f535da3011ca469146f239d621fcdfc10
563bfe6ac4c962109e2fc39aa236151f15a9c85b8e0e4e
2ee4f6b6b5f54337c184936e2fe4029e2d39ffe6953f7d
cf208ba062334e8595dbb9784857df770377a59dee2a8b
ee4e9c10e662f15dedd0379849ebc4a7a02a31f1ebd8a8
e55432
coin₂ = coinᵧ₀
coin₃ = coinᵧ₁
¶
[ TBD ]¶
This work was supported in part by the German Federal Ministry of Education and Research (BMBF) within the project Concrete Contracts.¶