commit 84a863a52709045697e2493c69c739b89028b21a
parent 9c03927d864fafd51116f9f4cad18c2a4ea1cdd8
Author: Mikolai Gütschow <mikolai.guetschow@tu-dresden.de>
Date: Tue, 12 Aug 2025 16:32:26 +0200
protocol: migrate payment to Florian-suggested structure
Diffstat:
2 files changed, 288 insertions(+), 209 deletions(-)
diff --git a/draft-guetschow-taler-protocol.md b/draft-guetschow-taler-protocol.md
@@ -490,74 +490,113 @@ persist (coin[i], blind_secret[i])
~~~
wallet merchant
knows merchant.pub knows merchant.priv
-knows valid *coin knows exchange, payto
+knows valid coin[i] knows exchange, payto
| |
- | wire_salt = random(128)
- | persist order = (id, price, info, token?, wire_salt)
+ | +----------------------+
+ | | (1) order generation |
+ | +----------------------+
| |
|<-------- (order.{id,token?}) ----------|
| |
-nonce = EdDSA-Keygen() |
-persist nonce.priv |
++----------------------+ |
+| (2) nonce generation | |
++----------------------+ |
| |
|------- /orders/{order.id}/claim ------>|
| (nonce.pub, token?) |
| |
- | h_wire = HKDF(wire_salt, payto,
- | "merchant-wire-signature", 64)
- | determine timestamp, refund_deadline, wire_deadline
- | contract = (order.{id,price,info,token?},
- | exchange, h_wire, timestamp,
- | refund_deadline, wire_deadline)
- | check contract.token = token?
- | contract.nonce = nonce
- | persist contract
- | sig0 = EdDSA-Sign(merchant.priv, msg0)
+ | +-------------------------+
+ | | (3) contract generation |
+ | +-------------------------+
| |
- |<----------- contract, sig0 ------------|
+ |<----------- contract, sig -------------|
| |
-check EdDSA-Verify(merchant.pub, msg0, sig0) |
-check contract.nonce = nonce |
-TODO: double-check extra hash check? |
-*(coin, fraction) = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here
-*sig1 = EdDSA-Sign(*coin.priv, *msg1) |
-*deposit = *(coin.{pub,sig,h_denom}, fraction, sig1) |
-persist (contract, sig, *deposit) |
++-------------------------+ |
+| (4) payment preparation | |
++-------------------------+ |
| |
|------- /orders/{order.id}/pay -------->|
- | (*deposit) |
+ | (deposit[i]) |
| |
- | check sum(*deposit.fraction) = contract.price
- | *check Deposit(deposit)
- | sig2 = EdDSA-Sign(merchant.priv, msg2)
+ | +-------------------+
+ | | (5) deposit check |
+ | +-------------------+
| |
- |<---------------- sig2 -----------------|
+ |<---------------- sig ------------------|
| |
-check EdDSA-Verify(merchant.pub, msg2, sig2) |
++--------------------------+ |
+| (6) payment verification | |
++--------------------------+ |
~~~
-where `msg0`, `*msg1`, and `msg2` are formed as follows:
+where (without age restriction, policy and wallet data hash)
~~~
-h_contract = SHA-512(canonicalJSON(contract))
+(1) order generation (merchant)
-msg0 = Sign-Msg(MERCHANT_CONTRACT, h_contract)
-*msg1 = Sign-Msg(WALLET_COIN_DEPOSIT,
- ( h_contract | uint256(0x0)
- | uint512(0x0) | contract.h_wire | *coin.h_denom
- | contract.timestamp | contract.refund_deadline
- | *fraction + *denom.fee_deposit
- | *denom.fee_deposit | merchant.pub | uint512(0x0) ))
-msg2 = Sign-Msg(MERCHANT_PAYMENT_OK, h_contract)
+wire_salt = random(128)
+persist order = (id, price, info, token?, wire_salt)
~~~
-(without age restriction, policy and wallet data hash)
+~~~
+(2) nonce generation (wallet)
+
+nonce = EdDSA-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.
+~~~
+(3) contract generation (merchant)
+
+h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64)
+determine timestamp, refund_deadline, wire_deadline
+contract = (order.{id,price,info,token?}, exchange, h_wire, timestamp, refund_deadline, wire_deadline)
+check contract.order.token = token?
+contract.nonce = nonce.pub
+persist contract
+h_contract = SHA-512(canonicalJSON(contract))
+msg = Sign-Msg(MERCHANT_CONTRACT, h_contract)
+sig = EdDSA-Sign(merchant.priv, msg)
+~~~
+
+~~~
+(4) payment preparation (wallet)
+
+check EdDSA-Verify(merchant.pub, msg, sig)
+check contract.nonce = nonce
+TODO: double-check extra hash check?
+deposit = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here
+msg[i] = Sign-Msg(WALLET_COIN_DEPOSIT,
+ ( h_contract | uint256(0x0)
+ | uint512(0x0) | contract.h_wire | coin[i].h_denom
+ | contract.timestamp | contract.refund_deadline
+ | deposit[i] + denom[i].fee_deposit
+ | denom[i].fee_deposit | merchant.pub | uint512(0x0) ))
+sig[i] = EdDSA-Sign(coin[i].priv, msg[i])
+deposit[i] = (coin[i].{pub,sig,h_denom}, fraction[i], sig[i])
+persist (contract, sig[i], deposit[i])
+~~~
+
+~~~
+(5) deposit check (merchant)
+
+check sum(deposit[i].fraction) == contract.price
+check Deposit(deposit)[i]
+msg = Sign-Msg(MERCHANT_PAYMENT_OK, h_contract)
+sig = EdDSA-Sign(merchant.priv, msg)
+~~~
+
+~~~
+(6) payment verification
+
+check EdDSA-Verify(merchant.pub, msg, sig)
+~~~
+
## Deposit
~~~
diff --git a/draft-guetschow-taler-protocol.xml b/draft-guetschow-taler-protocol.xml
@@ -526,74 +526,113 @@ persist (coin[i], blind_secret[i])
<figure><artwork><![CDATA[
wallet merchant
knows merchant.pub knows merchant.priv
-knows valid *coin knows exchange, payto
+knows valid coin[i] knows exchange, payto
| |
- | wire_salt = random(128)
- | persist order = (id, price, info, token?, wire_salt)
+ | +----------------------+
+ | | (1) order generation |
+ | +----------------------+
| |
|<-------- (order.{id,token?}) ----------|
| |
-nonce = EdDSA-Keygen() |
-persist nonce.priv |
++----------------------+ |
+| (2) nonce generation | |
++----------------------+ |
| |
|------- /orders/{order.id}/claim ------>|
| (nonce.pub, token?) |
| |
- | h_wire = HKDF(wire_salt, payto,
- | "merchant-wire-signature", 64)
- | determine timestamp, refund_deadline, wire_deadline
- | contract = (order.{id,price,info,token?},
- | exchange, h_wire, timestamp,
- | refund_deadline, wire_deadline)
- | check contract.token = token?
- | contract.nonce = nonce
- | persist contract
- | sig0 = EdDSA-Sign(merchant.priv, msg0)
+ | +-------------------------+
+ | | (3) contract generation |
+ | +-------------------------+
| |
- |<----------- contract, sig0 ------------|
+ |<----------- contract, sig -------------|
| |
-check EdDSA-Verify(merchant.pub, msg0, sig0) |
-check contract.nonce = nonce |
-TODO: double-check extra hash check? |
-*(coin, fraction) = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here
-*sig1 = EdDSA-Sign(*coin.priv, *msg1) |
-*deposit = *(coin.{pub,sig,h_denom}, fraction, sig1) |
-persist (contract, sig, *deposit) |
++-------------------------+ |
+| (4) payment preparation | |
++-------------------------+ |
| |
|------- /orders/{order.id}/pay -------->|
- | (*deposit) |
+ | (deposit[i]) |
| |
- | check sum(*deposit.fraction) = contract.price
- | *check Deposit(deposit)
- | sig2 = EdDSA-Sign(merchant.priv, msg2)
+ | +-------------------+
+ | | (5) deposit check |
+ | +-------------------+
| |
- |<---------------- sig2 -----------------|
+ |<---------------- sig ------------------|
| |
-check EdDSA-Verify(merchant.pub, msg2, sig2) |
++--------------------------+ |
+| (6) payment verification | |
++--------------------------+ |
]]></artwork></figure>
-<t>where <spanx style="verb">msg0</spanx>, <spanx style="verb">*msg1</spanx>, and <spanx style="verb">msg2</spanx> are formed as follows:</t>
+<t>where (without age restriction, policy and wallet data hash)</t>
<figure><artwork><![CDATA[
-h_contract = SHA-512(canonicalJSON(contract))
+(1) order generation (merchant)
-msg0 = Sign-Msg(MERCHANT_CONTRACT, h_contract)
-*msg1 = Sign-Msg(WALLET_COIN_DEPOSIT,
- ( h_contract | uint256(0x0)
- | uint512(0x0) | contract.h_wire | *coin.h_denom
- | contract.timestamp | contract.refund_deadline
- | *fraction + *denom.fee_deposit
- | *denom.fee_deposit | merchant.pub | uint512(0x0) ))
-msg2 = Sign-Msg(MERCHANT_PAYMENT_OK, h_contract)
+wire_salt = random(128)
+persist order = (id, price, info, token?, wire_salt)
]]></artwork></figure>
-<t>(without age restriction, policy and wallet data hash)</t>
+<figure><artwork><![CDATA[
+(2) nonce generation (wallet)
+
+nonce = EdDSA-Keygen()
+persist nonce.priv
+]]></artwork></figure>
<t>Note that the private key of <spanx style="verb">nonce</spanx> 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.</t>
+<figure><artwork><![CDATA[
+(3) contract generation (merchant)
+
+h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64)
+determine timestamp, refund_deadline, wire_deadline
+contract = (order.{id,price,info,token?}, exchange, h_wire, timestamp, refund_deadline, wire_deadline)
+check contract.order.token = token?
+contract.nonce = nonce.pub
+persist contract
+h_contract = SHA-512(canonicalJSON(contract))
+msg = Sign-Msg(MERCHANT_CONTRACT, h_contract)
+sig = EdDSA-Sign(merchant.priv, msg)
+]]></artwork></figure>
+
+<figure><artwork><![CDATA[
+(4) payment preparation (wallet)
+
+check EdDSA-Verify(merchant.pub, msg, sig)
+check contract.nonce = nonce
+TODO: double-check extra hash check?
+deposit = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here
+msg[i] = Sign-Msg(WALLET_COIN_DEPOSIT,
+ ( h_contract | uint256(0x0)
+ | uint512(0x0) | contract.h_wire | coin[i].h_denom
+ | contract.timestamp | contract.refund_deadline
+ | deposit[i] + denom[i].fee_deposit
+ | denom[i].fee_deposit | merchant.pub | uint512(0x0) ))
+sig[i] = EdDSA-Sign(coin[i].priv, msg[i])
+deposit[i] = (coin[i].{pub,sig,h_denom}, fraction[i], sig[i])
+persist (contract, sig[i], deposit[i])
+]]></artwork></figure>
+
+<figure><artwork><![CDATA[
+(5) deposit check (merchant)
+
+check sum(deposit[i].fraction) == contract.price
+check Deposit(deposit)[i]
+msg = Sign-Msg(MERCHANT_PAYMENT_OK, h_contract)
+sig = EdDSA-Sign(merchant.priv, msg)
+]]></artwork></figure>
+
+<figure><artwork><![CDATA[
+(6) payment verification
+
+check EdDSA-Verify(merchant.pub, msg, sig)
+]]></artwork></figure>
+
</section>
<section anchor="deposit"><name>Deposit</name>
@@ -746,7 +785,7 @@ msg3 = Sign-Msg(EXCHANGE_CONFIRM_DEPOSIT,
-<?line 612?>
+<?line 651?>
<section anchor="change-log"><name>Change log</name>
@@ -764,134 +803,135 @@ Education and Research (BMBF) within the project Concrete Contracts.</t>
</back>
<!-- ##markdown-source:
-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
-/wPrnFD5KU0AAA==
+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
-->