lsd0009

LSD0009: The GNU Taler Protocol
Log | Files | Refs | README

commit 963d59e9ef80f2bdf2a7ff1c4d2057b81f92712b
parent 84a863a52709045697e2493c69c739b89028b21a
Author: Mikolai Gütschow <mikolai.guetschow@tu-dresden.de>
Date:   Tue, 31 Mar 2026 14:26:40 +0200

protocol: change to using UTF8 i-subscript

Diffstat:
MMakefile | 2++
Mdraft-guetschow-taler-protocol.md | 176+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mdraft-guetschow-taler-protocol.xml | 441++++++++++++++++++++++++++++++++++++++++---------------------------------------
3 files changed, 319 insertions(+), 300 deletions(-)

diff --git a/Makefile b/Makefile @@ -5,6 +5,8 @@ draft-guetschow-taler-protocol.xml: draft-guetschow-taler-protocol.md html: draft-guetschow-taler-protocol.xml xml2rfc --html draft-guetschow-taler-protocol.xml + # workaround for https://github.com/ietf-tools/xml2rfc/issues/628#issuecomment-4162012035 + sed -i 's/\&amp;#/\&#/g' draft-guetschow-taler-protocol.html txt: draft-guetschow-taler-protocol.xml xml2rfc draft-guetschow-taler-protocol.xml diff --git a/draft-guetschow-taler-protocol.md b/draft-guetschow-taler-protocol.md @@ -370,8 +370,7 @@ The special value `0xFFFFFFFFFFFFFFFF` represents "forever". All messages to be signed in Taler start with a header containing their size and a fixed signing context (purpose) as registered by GANA in the [GNUnet Signature Purposes](https://gana.gnunet.org/gnunet-signatures/gnunet_signatures.html) -registry. All Taler-related purposes start at 1000 and can be found in the -[registry sources](https://git.taler.net/gana.git/tree/gnunet-signatures/registry.rec#n221). +registry. Taler-related purposes start at 1000. ~~~ Sign-Msg(purpose, msg) -> out @@ -390,22 +389,24 @@ Output: out = uint32(len(msg)) | uint32(purpose) | msg ~~~ -// todo: explain persist, check, knows, sum +// todo: explain persist, check, knows, sum, indexing (if left of equal sign, single entry; if not refers to whole list) ## Withdrawal -The wallet generates `n > 0` coins (`coin[i]`) and requests `n` signatures (`blind_sig[i]`) from the exchange, -attributing value to the coins according to `n` chosen denominations (`denom[i]`). +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: extend with extra roundtrip for CBS + ~~~ wallet exchange -knows denom[i].pub knows denom[i].priv +knows denomᵢ.pub knows denomᵢ.priv | | -+----------------------------+ | -| (1) reserve key generation | | -+----------------------------+ | ++-----------------------------+ | +| (W1) reserve key generation | | ++-----------------------------+ | | | |----------- (bank transfer) ----------->| | (subject: reserve.pub, amount: value) | @@ -414,75 +415,77 @@ knows denom[i].pub knows denom[i].priv | | persist (reserve.pub, value) | | +------------------------------+ | | -+----------------------------------+ | -| (2) coin generation and blinding | | -+----------------------------------+ | ++-----------------------------------+ | +| (W2) coin generation and blinding | | ++-----------------------------------+ | | | |-------------- /withdraw -------------->| - | (reserve.pub, coin[i].h_denom, | - | blind_coin[i], sig0) | + | (reserve.pub, ~(coinᵢ.h_denom), | + | ~blind_coin, sig0) | | | - | +-------------------------------+ - | | (3) coin issuance and signing | - | +-------------------------------+ + | +--------------------------------+ + | | (E1) coin issuance and signing | + | +--------------------------------+ | | - |<----------- blind_sig[i] --------------| + |<----------- (~blind_sig) --------------| | | -+---------------------+ | -| (4) coin unblinding | | -+---------------------+ | ++----------------------+ | +| (W3) coin unblinding | | ++----------------------+ | ~~~ where (for RSA, without age-restriction) ~~~ -(1) reserve key generation (wallet) +(W1) reserve key generation (wallet) reserve = EdDSA-Keygen() persist (reserve, value) ~~~ -The wallet derives coins and blinding secrets using a HKDF from a master secret per withdrawal and an integer index. +The wallet derives coins and blinding secrets using a HKDF from a single master secret per withdrawal operation, +together with an integer index. This is strictly speaking an implementation detail since the master secret is never revealed to any other party, and might be chosen to be implemented differently. ~~~ -(2) coin generation and blinding (wallet) +(W2) coin generation and blinding (wallet) master_secret = random(256) persist master_secret -coin_seed[i] = HKDF(uint32(i), master_secret, "taler-withdrawal-coin-derivation", 64) -blind_secret[i] = coin_seed[i][32:] -coin[i].priv = coin_seed[i][:32] -coin[i].pub = EdDSA-GetPub(coin[i].priv) -coin[i].h_denom = SHA-512(uint32(0) | uint32(1) | denom[i].pub) -blind_coin[i] = RSA-FDH-Blind(SHA-512(coin[i].pub), blind_secret[i], denom[i].pub) -msg0 = Sign-Msg(WALLET_RESERVE_WITHDRAW, - ( sum(denom[i].value) | sum(denom[i].fee_withdraw) - | SHA-512( SHA-512(denom[i].pub) | uint32(0x1) | blind_coin[i] ) +coin_seedᵢ = HKDF(uint32(i), master_secret, "taler-withdrawal-coin-derivation", 64) +blind_secretᵢ = coin_seedᵢ[32:] +coinᵢ.priv = coin_seedᵢ[:32] +coinᵢ.pub = EdDSA-GetPub(coinᵢ.priv) +coinᵢ.h_denom = SHA-512(uint32(0) | uint32(1) | denomᵢ.pub) +blind_coinᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub) +msg = Sign-Msg(WALLET_RESERVE_WITHDRAW, + ( sum(denomᵢ.value) | sum(denomᵢ.fee_withdraw) + | SHA-512( SHA-512(~(denomᵢ.pub)) | uint32(0x1) | blind_coinᵢ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) )) -sig0 = EdDSA-Sign(reserve.priv, msg0) +sig = EdDSA-Sign(reserve.priv, msg) ~~~ ~~~ -(3) coin issuance and signing (exchange) +(E1) coin issuance and signing (exchange) -denom[i] = Denom-Lookup(coin[i].h_denom) -check denom[i].pub known and not withdrawal-expired +denomᵢ = Denom-Lookup(coinᵢ.h_denom) +check denomᵢ.pub known and not withdrawal-expired check EdDSA-Verify(reserve.pub, msg, sig) check reserve KYC status ok or not needed -check reserve.balance >= sum(denom[i].value + denom[i].fee_withdraw) -reserve.balance -= sum(denom[i].value + denom[i].fee_withdraw) -blind_sig[i] = RSA-FDH-Sign(blind_coin[i], denom[i].priv) +total = sum(~(denomᵢ.value)) + sum(~(denomᵢ.fee_withdraw)) +check reserve.balance >= total +reserve.balance -= total +blind_sigᵢ = RSA-FDH-Sign(blind_coinᵢ, denomᵢ.priv) persist withdrawal ~~~ ~~~ -(4) coin unblinding (wallet) +(W4) coin unblinding (wallet) -coin[i].sig = RSA-FDH-Unblind(blind_sig[i], blind_secret[i], denom[i].pub) -check RSA-FDH-Verify(SHA-512(coin[i].pub), coin[i].sig, denom[i].pub) -persist (coin[i], blind_secret[i]) +coinᵢ.sig = RSA-FDH-Unblind(blind_sigᵢ, blind_secretᵢ, denomᵢ.pub) +check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) +persist (coinᵢ, blind_secretᵢ) ~~~ ## Payment @@ -490,56 +493,57 @@ persist (coin[i], blind_secret[i]) ~~~ wallet merchant knows merchant.pub knows merchant.priv -knows valid coin[i] knows exchange, payto +knows valid coinᵢ knows exchange, payto | | - | +----------------------+ - | | (1) order generation | - | +----------------------+ + | +-----------------------+ + | | (M1) order generation | + | +-----------------------+ | | - |<-------- (order.{id,token?}) ----------| + |<------- (QR-Code / NFC / URI) ---------| + | (order.{id,token?}) | | | -+----------------------+ | -| (2) nonce generation | | -+----------------------+ | ++-----------------------+ | +| (W1) nonce generation | | ++-----------------------+ | | | |------- /orders/{order.id}/claim ------>| | (nonce.pub, token?) | | | - | +-------------------------+ - | | (3) contract generation | - | +-------------------------+ + | +--------------------------+ + | | (M2) contract generation | + | +--------------------------+ | | - |<----------- contract, sig -------------| + |<---------- (contract, sig) ------------| | | -+-------------------------+ | -| (4) payment preparation | | -+-------------------------+ | ++--------------------------+ | +| (W2) payment preparation | | ++--------------------------+ | | | |------- /orders/{order.id}/pay -------->| - | (deposit[i]) | + | (~deposit) | | | - | +-------------------+ - | | (5) deposit check | - | +-------------------+ + | +--------------------+ + | | (M3) deposit check | + | +--------------------+ | | - |<---------------- sig ------------------| + |<--------------- (sig) -----------------| | | -+--------------------------+ | -| (6) payment verification | | -+--------------------------+ | ++---------------------------+ | +| (W3) payment verification | | ++---------------------------+ | ~~~ where (without age restriction, policy and wallet data hash) ~~~ -(1) order generation (merchant) +(M1) order generation (merchant) wire_salt = random(128) persist order = (id, price, info, token?, wire_salt) ~~~ ~~~ -(2) nonce generation (wallet) +(W1) nonce generation (wallet) nonce = EdDSA-Keygen() persist nonce.priv @@ -551,12 +555,12 @@ enabling use-cases such as "unclaiming" or transferring an order to another pers or proving the payment without resorting to the individual coins. ~~~ -(3) contract generation (merchant) +(M2) 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? +check contract.order.token == token contract.nonce = nonce.pub persist contract h_contract = SHA-512(canonicalJSON(contract)) @@ -565,34 +569,38 @@ sig = EdDSA-Sign(merchant.priv, msg) ~~~ ~~~ -(4) payment preparation (wallet) +(W2) 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, +~selection = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here +(coinᵢ, denomᵢ, contributionᵢ) = selectionᵢ +msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT, ( h_contract | uint256(0x0) - | uint512(0x0) | contract.h_wire | coin[i].h_denom + | uint512(0x0) | contract.h_wire | coinᵢ.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]) + | contributionᵢ + denomᵢ.fee_deposit + | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) +sigᵢ = EdDSA-Sign(coinᵢ.priv, msgᵢ) +depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ) +persist (contract, ~sig, ~deposit) ~~~ +// TODO: explain CoinSelection +// TODO: maybe better {sigᵢ} instead of ~sig? + ~~~ -(5) deposit check (merchant) +(M3) deposit check (merchant) -check sum(deposit[i].fraction) == contract.price -check Deposit(deposit)[i] +check sum(depositᵢ.fraction) == contract.price +check Deposit(deposit)ᵢ msg = Sign-Msg(MERCHANT_PAYMENT_OK, h_contract) sig = EdDSA-Sign(merchant.priv, msg) ~~~ ~~~ -(6) payment verification +(W3) payment verification (wallet) check EdDSA-Verify(merchant.pub, msg, sig) ~~~ diff --git a/draft-guetschow-taler-protocol.xml b/draft-guetschow-taler-protocol.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?> - <!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.17 (Ruby 3.1.2) --> + <!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.35 (Ruby 3.1.2) --> <!DOCTYPE rfc [ @@ -29,7 +29,7 @@ </address> </author> - <date year="2025" month="August" day="12"/> + <date year="2026" month="March" day="31"/> <workgroup>independent</workgroup> @@ -403,8 +403,7 @@ The special value `0xFFFFFFFFFFFFFFFF` represents "forever". <t>All messages to be signed in Taler start with a header containing their size and a fixed signing context (purpose) as registered by GANA in the <eref target="https://gana.gnunet.org/gnunet-signatures/gnunet_signatures.html">GNUnet Signature Purposes</eref> -registry. All Taler-related purposes start at 1000 and can be found in the -<eref target="https://git.taler.net/gana.git/tree/gnunet-signatures/registry.rec#n221">registry sources</eref>.</t> +registry. Taler-related purposes start at 1000.</t> <figure><artwork><![CDATA[ Sign-Msg(purpose, msg) -> out @@ -423,24 +422,26 @@ Output: out = uint32(len(msg)) | uint32(purpose) | msg ]]></artwork></figure> -<t>// todo: explain persist, check, knows, sum</t> +<t>// todo: explain persist, check, knows, sum, indexing (if left of equal sign, single entry; if not refers to whole list)</t> </section> </section> <section anchor="withdrawal"><name>Withdrawal</name> -<t>The wallet generates <spanx style="verb">n &gt; 0</spanx> coins (<spanx style="verb">coin[i]</spanx>) and requests <spanx style="verb">n</spanx> signatures (<spanx style="verb">blind_sig[i]</spanx>) from the exchange, -attributing value to the coins according to <spanx style="verb">n</spanx> chosen denominations (<spanx style="verb">denom[i]</spanx>). +<t>The wallet generates <spanx style="verb">n &gt; 0</spanx> coins (<spanx style="verb">coinᵢ</spanx>) and requests <spanx style="verb">n</spanx> signatures (<spanx style="verb">blind_sigᵢ</spanx>) from the exchange, +attributing value to the coins according to <spanx style="verb">n</spanx> chosen denominations (<spanx style="verb">denomᵢ</spanx>). 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.</t> +<t>// todo: extend with extra roundtrip for CBS</t> + <figure><artwork><![CDATA[ wallet exchange -knows denom[i].pub knows denom[i].priv +knows denomᵢ.pub knows denomᵢ.priv | | -+----------------------------+ | -| (1) reserve key generation | | -+----------------------------+ | ++-----------------------------+ | +| (W1) reserve key generation | | ++-----------------------------+ | | | |----------- (bank transfer) ----------->| | (subject: reserve.pub, amount: value) | @@ -449,75 +450,77 @@ knows denom[i].pub knows denom[i].priv | | persist (reserve.pub, value) | | +------------------------------+ | | -+----------------------------------+ | -| (2) coin generation and blinding | | -+----------------------------------+ | ++-----------------------------------+ | +| (W2) coin generation and blinding | | ++-----------------------------------+ | | | |-------------- /withdraw -------------->| - | (reserve.pub, coin[i].h_denom, | - | blind_coin[i], sig0) | + | (reserve.pub, ~(coinᵢ.h_denom), | + | ~blind_coin, sig0) | | | - | +-------------------------------+ - | | (3) coin issuance and signing | - | +-------------------------------+ + | +--------------------------------+ + | | (E1) coin issuance and signing | + | +--------------------------------+ | | - |<----------- blind_sig[i] --------------| + |<----------- (~blind_sig) --------------| | | -+---------------------+ | -| (4) coin unblinding | | -+---------------------+ | ++----------------------+ | +| (W3) coin unblinding | | ++----------------------+ | ]]></artwork></figure> <t>where (for RSA, without age-restriction)</t> <figure><artwork><![CDATA[ -(1) reserve key generation (wallet) +(W1) reserve key generation (wallet) reserve = EdDSA-Keygen() persist (reserve, value) ]]></artwork></figure> -<t>The wallet derives coins and blinding secrets using a HKDF from a master secret per withdrawal and an integer index. +<t>The wallet derives coins and blinding secrets using a HKDF from a single master secret per withdrawal operation, +together with an integer index. This is strictly speaking an implementation detail since the master secret is never revealed to any other party, and might be chosen to be implemented differently.</t> <figure><artwork><![CDATA[ -(2) coin generation and blinding (wallet) +(W2) coin generation and blinding (wallet) master_secret = random(256) persist master_secret -coin_seed[i] = HKDF(uint32(i), master_secret, "taler-withdrawal-coin-derivation", 64) -blind_secret[i] = coin_seed[i][32:] -coin[i].priv = coin_seed[i][:32] -coin[i].pub = EdDSA-GetPub(coin[i].priv) -coin[i].h_denom = SHA-512(uint32(0) | uint32(1) | denom[i].pub) -blind_coin[i] = RSA-FDH-Blind(SHA-512(coin[i].pub), blind_secret[i], denom[i].pub) -msg0 = Sign-Msg(WALLET_RESERVE_WITHDRAW, - ( sum(denom[i].value) | sum(denom[i].fee_withdraw) - | SHA-512( SHA-512(denom[i].pub) | uint32(0x1) | blind_coin[i] ) +coin_seedᵢ = HKDF(uint32(i), master_secret, "taler-withdrawal-coin-derivation", 64) +blind_secretᵢ = coin_seedᵢ[32:] +coinᵢ.priv = coin_seedᵢ[:32] +coinᵢ.pub = EdDSA-GetPub(coinᵢ.priv) +coinᵢ.h_denom = SHA-512(uint32(0) | uint32(1) | denomᵢ.pub) +blind_coinᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub) +msg = Sign-Msg(WALLET_RESERVE_WITHDRAW, + ( sum(denomᵢ.value) | sum(denomᵢ.fee_withdraw) + | SHA-512( SHA-512(~(denomᵢ.pub)) | uint32(0x1) | blind_coinᵢ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) )) -sig0 = EdDSA-Sign(reserve.priv, msg0) +sig = EdDSA-Sign(reserve.priv, msg) ]]></artwork></figure> <figure><artwork><![CDATA[ -(3) coin issuance and signing (exchange) +(E1) coin issuance and signing (exchange) -denom[i] = Denom-Lookup(coin[i].h_denom) -check denom[i].pub known and not withdrawal-expired +denomᵢ = Denom-Lookup(coinᵢ.h_denom) +check denomᵢ.pub known and not withdrawal-expired check EdDSA-Verify(reserve.pub, msg, sig) check reserve KYC status ok or not needed -check reserve.balance >= sum(denom[i].value + denom[i].fee_withdraw) -reserve.balance -= sum(denom[i].value + denom[i].fee_withdraw) -blind_sig[i] = RSA-FDH-Sign(blind_coin[i], denom[i].priv) +total = sum(~(denomᵢ.value)) + sum(~(denomᵢ.fee_withdraw)) +check reserve.balance >= total +reserve.balance -= total +blind_sigᵢ = RSA-FDH-Sign(blind_coinᵢ, denomᵢ.priv) persist withdrawal ]]></artwork></figure> <figure><artwork><![CDATA[ -(4) coin unblinding (wallet) +(W4) coin unblinding (wallet) -coin[i].sig = RSA-FDH-Unblind(blind_sig[i], blind_secret[i], denom[i].pub) -check RSA-FDH-Verify(SHA-512(coin[i].pub), coin[i].sig, denom[i].pub) -persist (coin[i], blind_secret[i]) +coinᵢ.sig = RSA-FDH-Unblind(blind_sigᵢ, blind_secretᵢ, denomᵢ.pub) +check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) +persist (coinᵢ, blind_secretᵢ) ]]></artwork></figure> </section> @@ -526,56 +529,57 @@ persist (coin[i], blind_secret[i]) <figure><artwork><![CDATA[ wallet merchant knows merchant.pub knows merchant.priv -knows valid coin[i] knows exchange, payto +knows valid coinᵢ knows exchange, payto | | - | +----------------------+ - | | (1) order generation | - | +----------------------+ + | +-----------------------+ + | | (M1) order generation | + | +-----------------------+ | | - |<-------- (order.{id,token?}) ----------| + |<------- (QR-Code / NFC / URI) ---------| + | (order.{id,token?}) | | | -+----------------------+ | -| (2) nonce generation | | -+----------------------+ | ++-----------------------+ | +| (W1) nonce generation | | ++-----------------------+ | | | |------- /orders/{order.id}/claim ------>| | (nonce.pub, token?) | | | - | +-------------------------+ - | | (3) contract generation | - | +-------------------------+ + | +--------------------------+ + | | (M2) contract generation | + | +--------------------------+ | | - |<----------- contract, sig -------------| + |<---------- (contract, sig) ------------| | | -+-------------------------+ | -| (4) payment preparation | | -+-------------------------+ | ++--------------------------+ | +| (W2) payment preparation | | ++--------------------------+ | | | |------- /orders/{order.id}/pay -------->| - | (deposit[i]) | + | (~deposit) | | | - | +-------------------+ - | | (5) deposit check | - | +-------------------+ + | +--------------------+ + | | (M3) deposit check | + | +--------------------+ | | - |<---------------- sig ------------------| + |<--------------- (sig) -----------------| | | -+--------------------------+ | -| (6) payment verification | | -+--------------------------+ | ++---------------------------+ | +| (W3) payment verification | | ++---------------------------+ | ]]></artwork></figure> <t>where (without age restriction, policy and wallet data hash)</t> <figure><artwork><![CDATA[ -(1) order generation (merchant) +(M1) order generation (merchant) wire_salt = random(128) persist order = (id, price, info, token?, wire_salt) ]]></artwork></figure> <figure><artwork><![CDATA[ -(2) nonce generation (wallet) +(W1) nonce generation (wallet) nonce = EdDSA-Keygen() persist nonce.priv @@ -587,12 +591,12 @@ enabling use-cases such as "unclaiming" or transferring an order to another pers or proving the payment without resorting to the individual coins.</t> <figure><artwork><![CDATA[ -(3) contract generation (merchant) +(M2) 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? +check contract.order.token == token contract.nonce = nonce.pub persist contract h_contract = SHA-512(canonicalJSON(contract)) @@ -601,34 +605,35 @@ sig = EdDSA-Sign(merchant.priv, msg) ]]></artwork></figure> <figure><artwork><![CDATA[ -(4) payment preparation (wallet) +(W2) 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, +~selection = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here +(coinᵢ, denomᵢ, contributionᵢ) = selectionᵢ +msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT, ( h_contract | uint256(0x0) - | uint512(0x0) | contract.h_wire | coin[i].h_denom + | uint512(0x0) | contract.h_wire | coinᵢ.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]) + | contributionᵢ + denomᵢ.fee_deposit + | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) +sigᵢ = EdDSA-Sign(coinᵢ.priv, msgᵢ) +depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ) +persist (contract, ~sig, ~deposit) ]]></artwork></figure> <figure><artwork><![CDATA[ -(5) deposit check (merchant) +(M3) deposit check (merchant) -check sum(deposit[i].fraction) == contract.price -check Deposit(deposit)[i] +check sum(depositᵢ.fraction) == contract.price +check Deposit(deposit)ᵢ msg = Sign-Msg(MERCHANT_PAYMENT_OK, h_contract) sig = EdDSA-Sign(merchant.priv, msg) ]]></artwork></figure> <figure><artwork><![CDATA[ -(6) payment verification +(W3) payment verification (wallet) check EdDSA-Verify(merchant.pub, msg, sig) ]]></artwork></figure> @@ -696,6 +701,7 @@ msg3 = Sign-Msg(EXCHANGE_CONFIRM_DEPOSIT, <back> + <references title='Normative References' anchor="sec-normative-references"> @@ -785,7 +791,8 @@ msg3 = Sign-Msg(EXCHANGE_CONFIRM_DEPOSIT, -<?line 651?> + +<?line 656?> <section anchor="change-log"><name>Change log</name> @@ -803,135 +810,137 @@ Education and Research (BMBF) within the project Concrete Contracts.</t> </back> <!-- ##markdown-source: -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 -->