From 446fec10a4e1fb7923f6732f80290fcb6c7bc1af Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 3 May 2021 21:47:14 +0200 Subject: first cut at DD13 spec writing --- core/api-exchange.rst | 713 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 712 insertions(+), 1 deletion(-) diff --git a/core/api-exchange.rst b/core/api-exchange.rst index 1e9db1d9..12cb4e1a 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -123,6 +123,15 @@ possibly by using HTTPS. // The exchange's currency. currency: string; + // Purse fee, charged only if a purse is abandoned + // and was not covered by the account limit. + purse_fee: Amount; + + // Non-negative number of concurrent purses that any + // account holder is allowed to create without having + // to pay the purse_fee. + purse_account_limit: integer; + // EdDSA master public key of the exchange, used to sign entries // in ``denoms`` and ``signkeys``. master_public_key: EddsaPublicKey; @@ -918,7 +927,7 @@ exchange. .. ts:def:: TransactionHistoryItem // Union discriminated by the "type" field. - type ReserveTransaction = + type TransactionHistoryItem = | ReserveWithdrawTransaction | ReserveCreditTransaction | ReserveClosingTransaction @@ -1971,3 +1980,705 @@ Refunds // the relevant subset of the transactions. history: CoinSpendHistoryItem[]; } + + + + + + + + + + + + + + + +.. _exchange_w2w: + +-------------------------- +Wallet-to-wallet transfers +-------------------------- + +TODO for the spec: + + * endpoint to create purse with KYC'ed account OR payment, + but without deposit + * endpoint to DELETE account is missing (note: may need + two variants: bank-administrative (offline key?) + and user-driven) + * specify new database schema at exchange + * specify what each signature is made over precisely + * add KYC fee to /keys + * add extended account history fee to /keys + * add purse 'GET' expiration (purse_expiration is when + deposits are no longer allowed, but we may still want + to return the purse status for a bit longer, right? + Or only as part of the coins/accounts status? What about + long-pollers that are active at this expiration time? + [should not DELETE the purse data while the long pollers + are still potentially waking up, so needs a grace period, at least]) + * update wire transfer API to enable WAD IDs (and while we are + at it, should probably also write extended version to allow + _merchants_ to query for their inbound transfers, so spec + for both WADs and regular WTID!) + * specify WAD ID format (keep separte from + WTIDs and reserve public keys!) + * Update coin history replies to include purse actions. + * Merging a purse into an account that is never KYC'ed means + the funds are lost. We need to specify an account expiration + time (after funds are merged, but KYC never happens). + +Discussion: + + * Should the account-withdraw be a separate endpoint from + the reserve withdraw, or should we *extend* the reserve-withdraw + with the "missing KYC" response? + * Should the account-history be a separate endpoint from + the reserve history, or should we *extend* the reserve-history + with the new history entries and the payment option? + * Should we return somewhere a list of the desired + KYC attributes that the wallet should (optionally) + POST to the exchange when initiating KYC? (see: "attributes"). + +Notes: + + * Current API does not allow 'merging' a purse directly into + a regular bank account. The merge MUST go into an account. + * What do we do with KYC'ed accounts where the user fails to + drain the funds? Just keep them? Or do we require getting an + IBAN as part of KYC and 'close' the account that way? + + +Purses +^^^^^^ + + +.. http:GET:: /purses/$PURSE_PUB + + Obtain information about a purse. The request header must + contain a *Purse-Request-Signature*. + + **Request:** + + *Purse-Request-Signature*: The client must provide Base-32 encoded EdDSA signature made with ``$PURSE_PRIV``, affirming its authorization to download the purse status. The purpose used MUST be ``TALER_SIGNATURE_PURSE_STATUS`` (NUMBER: TBD). + + :query merge_timeout_ms=NUMBER: *Optional.* If specified, + the exchange + will wait up to ``timeout_ms`` milliseconds for completion + of a merge operation before sending the HTTP response. + :query deposit_timeout_ms=NUMBER: *Optional.* If specified, + the exchange + will wait up to ``timeout_ms`` milliseconds for completion + of a deposit operation before sending the HTTP response. + :query contract=BOOLEAN: *Optional.* If 'false' is specified, + the exchange will not return the encrypted contract, saving + bandwidth for clients that already know it. + + **Response:** + + :http:statuscode:`200 OK`: + The operation succeeded, the exchange provides details + about the purse. + The response will include a `PurseStatus` object. + :http:statuscode:`401 Unauthorized`: + The *Purse-Request-Signature* is invalid. + This response comes with a standard `ErrorDetail` response. + :http:statuscode:`404 Not found`: + The purse is unknown to the exchange. + + **Details:** + + .. ts:def:: PurseStatus + + interface PurseStatus { + + // Total amount that must be paid into the purse. + total_purse_amount: Amount; + + // Total amount deposited into the purse so far. + total_deposit_amount: Amount; + + // Indicative time by which the purse expires + // if it has not been merged into an account. At this + // point, all of the deposits made will be auto-refunded. + purse_expiration: Timestamp; + + // Indicative time at which the exchange is answering the + // status request. Used as part of `exchange_sig`. + status_timestamp: Timestamp; + + // Maximum deposit fees that can be charged under the contract. + max_deposit_fees: Amount; + + // SHA-512 hash of the contact of the purse. + h_contract_terms: HashCode; + + // EdDSA signature of the exchange affirming the purse status. + exchange_sig: EddsaSignature; + + // AES-GCM Encrypted contract terms using encryption + // key derived from DH of 'contract_pub' and the 'purse_pub'. + // Optional, may be omitted if not desired by the client. + e_contract_terms?: byte[]; + + // If a merge request was received, information about the + // merge request. Omitted if the purse has not yet received + // a merge request. + merge_request?: MergeRequest; + + } + + +.. http:POST:: /purses/$PURSE_PUB/deposit + + Create a purse without an account, but with associated payment. + + **Request:** The request body must be a `PurseRequest` object. + + **Response:** + + :http:statuscode:`200 OK`: + The operation succeeded, the exchange confirms that all + coins were deposited into the purse. + The response will include a `PursePaymentSuccess` object. + :http:statuscode:`202 Accepted`: + The payment was accepted, but insufficient to reach the + specified purse balance. The client should make further + purse deposits before the expiration deadline. + The response will include a `PursePaymentAccepted` object. + :http:statuscode:`401 Unauthorized`: + A coin signature is invalid. + This response comes with a standard `ErrorDetail` response. + :http:statuscode:`403 Forbidden`: + The server is denying the operation as a purse with a + different contract or total amount already exists. + This response comes with a standard `PurseConflict` response. + :http:statuscode:`404 Not found`: + Either the denomination key is not recognized (expired or invalid) or + the wire type is not recognized. + :http:statuscode:`409 Conflict`: + The deposit operation has either failed because a coin has insufficient + residual value, or because the same public key of the coin has been + previously used with a different denomination. Which case it is + can be decided by looking at the error code + (``TALER_EC_EXCHANGE_DEPOSIT_INSUFFICIENT_FUNDS`` or ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``). + The fields of the response are the same in both cases. + The request should not be repeated again with this coin. + In this case, the response is a `PurseDepositDoubleSpendError`. + If the value of all successful coins is below the purse fee, + the exchange may not setup the purse at all. + + + **Details:** + + .. ts:def:: PursePaymentSuccess + + interface PursePaymentSuccess { + + // Total amount paid into the purse. + total_purse_amount: Amount; + + // Total deposit fees charged. + total_deposit_fees: Amount; + + // EdDSA signature of the exchange affirming the payment. + // Signs over the above and the purse public key and + // the hash of the contract terms. + exchange_sig: EddsaSignature; + + } + + .. ts:def:: PursePaymentAccepted + + interface PursePaymentAccepted { + + // Total amount paid so far into the purse, in this + // and previous requests. + total_amount_deposited: Amount; + + // Total amount contributed by the current request. + total_amount_contributed: Amount; + + } + + .. ts:def:: PurseConflict + + interface PurseConflict { + + // Total amount to be paid into the purse as per + // the previous request. + total_purse_amount: Amount; + + // SHA-512 hash of the contact of the purse. + h_contract_terms: HashCode; + + // Indicative time by which the purse should expire + // if it has not been merged into an account. At this + // point, all of the deposits made should be + // auto-refunded. + purse_expiration: Timestamp; + + // EdDSA signature of the purse confirming that the + // above details hold for this purse. + purse_sig: EddsaSignature; + + } + + .. ts:def:: PurseRequest + + interface PurseRequest { + + // EdDSA signature of the purse confirming the key + // invariants associated with the purse. + // (amount, h_contract_terms, expiration). + purse_sig: EddsaSignature; + + // Total amount to be paid into the purse. + // Clients may make several requests, i.e. if a + // first request failed with a double-spending error. + // The exchange will confirm the creation of the + // purse once the amount given here is reached. + total_purse_amount: Amount; + + // SHA-512 hash of the contact of the purse. + h_contract_terms: HashCode; + + // ECDH contract_public key used to encrypt the contract. + // Optional as the contract terms may already be known + // to the exchange or the other wallet from a different + // interaction. + contract_pub?: EcdhPublicKey; + + // AES-GCM Encrypted contract terms using encryption + // key derived from DH of 'contract_pub' and the 'purse_pub'. + // Optional as the contract terms may already be known + // to the exchange or the other wallet from a different + // interaction. + e_contract_terms?: byte[]; + + // Client-side timestamp of when the payment was made. + timestamp: Timestamp; + + // Indicative time by which the purse should expire + // if it has not been merged into an account. At this + // point, all of the deposits made will be auto-refunded. + purse_expiration: Timestamp; + + // Array of coins being deposited into the purse. + deposits: PurseDeposit[]; + } + + .. ts:def:: PurseDeposit { + + // Public key of the coin being deposited into the purse. + coin_pub: EddsaPublicKey; + + // Amount to be deposited, can be a fraction of the + // coin's total value. + contribution: Amount; + + // Hash of denomination RSA key with which the coin is signed. + denom_pub_hash: HashCode; + + // Exchange's unblinded RSA signature of the coin. + ub_sig: RsaSignature; + + // Signature of `TALER_PurseDepositRequestPS`, + // made by the customer with the + // `coin's private key `. + coin_sig: EddsaSignature; + + } + + .. ts:def:: PurseDepositDoubleSpendError + + interface DepositDoubleSpendError { + // The string constant "insufficient funds". + hint: string; + + // Total amount contributed by the current request. + // Note that some coins may have been successfully + // deposited into the purse, so the total amount + // from these coins is listed here. + total_amount_contributed: Amount; + + // Public keys of coins that could not be deposited + // into the purse, mapped to the coin's histories. + coin_map: EddsaPublicKey -> CoinSpendHistoryItem[]; + } + + + +.. http:POST:: /purses/$PURSE_PUB/merge + + Merge purse with account, adding the value of the purse into + the account. + + **Request:** The request body must be a `MergeRequest` object. + + :query timeout_ms=NUMBER: *Optional.* If specified, the exchange will + wait up to ``timeout_ms`` milliseconds to receive payment before + reporting on the completion of merge operation. Basically + forstalls returning a 202 response for up to timeout milliseconds + to possibly return a 200 response instead. + + **Response:** + + :http:statuscode:`200 OK`: + The operation succeeded, the exchange confirms that the + funds were merged into the account. + The response will include a `MergeSuccess` object. + :http:statuscode:`202 Accepted`: + The operation succeeded, the exchange confirms that the + merge request is valid. Alas, the purse was still not + funded and thus the actual merge is delayed. + The response will include a `MergeAccepted` object. + :http:statuscode:`401 Unauthorized`: + Account signature is invalid. + This response comes with a standard `ErrorDetail` response. + :http:statuscode:`404 Not found`: + The refund operation failed as we could not find the purse. + This response comes with a standard `ErrorDetail` response. + :http:statuscode:`410 Gone`: + The purse has already expired and thus can no longer be merged. + This response comes with a standard `ErrorDetail` response. + :http:statuscode:`429 Too Many Requests`: + This account is not at this exchange, has not yet passed the + KYC checks, or it has exceeded the number of open purses. + The client must include payment to create another purse or + wait until existing purses have expired. + + **Details:** + + .. ts:def:: MergeSuccess + + interface MergeSuccess { + + // Amount merged (excluding deposit fees). + merge_amount: Amount; + + // SHA-512 hash of the contact of the purse. + h_contract_terms: HashCode; + + // Time at which the merge came into effect. + merge_time: Timestamp; + + // EdDSA signature of the exchange affirming the merge. + // Signs over the above and the account public key. + exchange_sig: EddsaSignature; + + } + + .. ts:def:: MergeAccepted + + interface MergeAccepted { + + // The number of open purses under the given account. + // Useful to calculate how many purses still can be created. + open_purses: integer; + + } + + .. ts:def:: MergeRequest + + interface MergeRequest { + + // payto://-URI of the account the purse is to be merged into. + // Must be of the form: "payto://taler/EXCHANGE_URL/ACCOUNT_PUB". + payto_uri: string; + + // EdDSA signature of the exchange affirming the merge. + account_sig: EddsaSignature; + + // EdDSA signature of the purse private key affirming the merge. + purse_sig: EddsaSignature; + + // Array of payments made to pay for the creation of the + // purse. Can be empty, say if no payment is needed. + payments: CreatePurseDeposit[]; + + } + + .. ts:def:: CreatePurseDeposit { + + // Public key of the coin being used to pay for creating a purse. + coin_pub: EddsaPublicKey; + + // Amount to be deposited, can be a fraction of the + // coin's total value. + contribution: Amount; + + // Hash of denomination RSA key with which the coin is signed. + denom_pub_hash: HashCode; + + // Exchange's unblinded RSA signature of the coin. + ub_sig: RsaSignature; + + // Signature of `TALER_PurseCreateRequestPS`, + // made by the customer with the + // `coin's private key `. + coin_sig: EddsaSignature; + + } + + + + +.. _exchange_accounts: + +Accounts +^^^^^^^^ + +.. http:GET:: /accounts/$ACCOUNT_PUB + + Obtain information about an account. The request header must + contain an *Account-Request-Signature*. + + **Request:** + + *Account-Request-Signature*: The client must provide Base-32 encoded EdDSA signature made with ``$ACCOUNT_PRIV``, affirming its authorization to access the account status. The purpose used MUST be ``TALER_SIGNATURE_ACCOUNT_STATUS`` (NUMBER: TBD). + + :query history=BOOLEAN: *Optional.* If specified, the exchange + will return the recent account history. + This is still free of charge. + :query full_history=BOOLEAN: *Optional.* If 'true' is specified, + the exchange will return the full account history. This + may incur a fee that will be charged to the account. + + **Response:** + + :http:statuscode:`200 OK`: + The operation succeeded, the exchange provides details + about the account. + The response will include a `AccountHistory` object. + :http:statuscode:`401 Unauthorized`: + The *Account-Request-Signature* is invalid. + This response comes with a standard `ErrorDetail` response. + :http:statuscode:`404 Not found`: + The account is unknown to the exchange. + + **Details:** + + .. ts:def:: AccountHistory + + interface AccountHistory { + + // Current balance of the account. + balance: Amount; + + // True if the owner of the account currently satisfies + // the required KYC checks. + kyc: boolean; + + // Transaction history for this account. + history: AccountHistoryItem[]; + } + + Objects in the transaction history have the following format: + + .. ts:def:: AccountHistoryItem + + // Union discriminated by the "type" field. + type AccountHistoryItem = + | AccountMergeTransaction + | ReserveWithdrawTransaction + | ReserveCreditTransaction + | ReserveClosingTransaction + | ReserveRecoupTransaction; + + .. ts:def:: AccountMergeTransaction + + interface AccountMergeTransaction { + type: "MERGE"; + + // Amount merged (what was left after fees). + amount: Amount; + + // Purse that was merged. + purse_pub: EddsaPublicKey; + + // Hash of the contract. + h_contract: HashCode; + + // Signature created with the account's private key. + account_sig: EddsaSignature; + + // Signature created with the purse's private key. + purse_sig: EddsaSignature; + + // Deposit fees that were charged to the purse. + deposit_fees: Amount; + } + + +.. http:POST:: /accounts + + Create a new account. + + **Request:** The request body must be a `AccountSetupRequest` object. + + :query timeout_ms=NUMBER: *Optional.* If specified, the exchange will + wait up to ``timeout_ms`` milliseconds for the KYC gateway to + confirm completion of the KYC process. + + **Response:** + + :http:statuscode:`200 Ok`: + The operation succeeded, the exchange confirms that the account + can now be used. + The response will be an `AccountStatus` object. + :http:statuscode:`303 See Other`: + The user should be redirected to the provided location to perform + the required KYC checks to open the account. Afterwards, the + request should be repeated. + :http:statuscode:`504 Gateway Timeout`: + The exchange did not receive a confirmation from the KYC service + within the specified time period. Used when long-polling for the + result. + + **Details:** + + .. ts:def:: AccountSetupRequest + + interface AccountSetupRequest { + + // EdDSA public key for the account. + account_pub: EddsaPublicKey; + + // EdDSA signature of the account affirming the request + // to create the account. + account_sig: EddsaPublicKey; + + // Array of payments made to pay for the creation of the + // account. Can be empty, say if no payment is needed. + payments: CreateAccountDeposit[]; + + // Generic key-value map of attributes about the + // account owner. Useful to pre-fill KYC forms. + // A list of well-known keys is defined in FIXME. + attributes: Object; + + } + + .. ts:def:: CreateAccountDeposit { + + // Public key of the coin being used to pay for creating an + // account. + coin_pub: EddsaPublicKey; + + // Amount to be deposited, can be a fraction of the + // coin's total value. + contribution: Amount; + + // Hash of denomination RSA key with which the coin is signed. + denom_pub_hash: HashCode; + + // Exchange's unblinded RSA signature of the coin. + ub_sig: RsaSignature; + + // Signature of `TALER_AccountCreateRequestPS`, + // made by the customer with the + // `coin's private key `. + coin_sig: EddsaSignature; + } + + .. ts:def:: AccountKycStatus + + interface AccountKycStatus { + + // Current time of the exchange, used as part of + // what the exchange signs over. + now: Timestamp; + + // EdDSA signature of the exchange affirming the account + // is KYC'ed. + exchange_sig: EddsaSignature; + + } + + +.. http:post:: /accounts/$ACCOUNT_PUB/withdraw + + This endpoint is virtually identical to the withdraw endpoint + for reserves, with the only difference being a new response + code `303 See Other` which is returned for accounts that + have not yet completed the required KYC check. + + **Response**: + + :http:statuscode:`303 See Other`: + The user should be redirected to the provided location to perform + the required KYC checks to open the account. Afterwards, the + request should be repeated. + + +.. _exchange_wads: + + +Wads +^^^^ + +These endpoints are used to manage exchange-to-exchange payments in support of +wallet-to-wallet payments. + + +.. http:GET:: /wads/$WAD_ID + + Obtain information about a wad. + + **Request:** + + **Response:** + + :http:statuscode:`200 OK`: + The operation succeeded, the exchange provides details + about the wad. + The response will include a `WadDetails` object. + :http:statuscode:`404 Not found`: + The wad is unknown to the exchange. + + **Details:** + + .. ts:def:: WadDetails + + interface WadDetails { + + // Total transfer amount claimed by the exchange. + total: Amount; + + // Transfers aggregated in the wad. + items: WadItem[]; + } + + Objects in the wad item list have the following format: + + .. ts:def:: WadItem + + interface WadItem { + + // Amount in the purse. + amount: Amount; + + // Purse public key. + purse_pub: EddsaPublicKey; + + // Account public key. + account_pub: EddsaPublicKey; + + // Hash of the contract. + h_contract: HashCode; + + // Signature created with the account's private key. + account_sig: EddsaSignature; + + // Signature created with the purse's private key. + purse_sig: EddsaSignature; + + // Deposit fees that were charged to the purse. + deposit_fees: Amount; + + // Wad fees that was charged to the purse. + wad_fees: Amount; + } -- cgit v1.2.3 From 20d68685eee8a3e99b59ed913ad3389f487e3e4b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 5 May 2021 17:45:09 +0200 Subject: update spec for W2W --- core/api-common.rst | 182 ++++++ core/api-exchange.rst | 735 ++++++++++++++----------- design-documents/013-peer-to-peer-payments.rst | 85 +-- 3 files changed, 662 insertions(+), 340 deletions(-) diff --git a/core/api-common.rst b/core/api-common.rst index 19f6bf55..bb922cc2 100644 --- a/core/api-common.rst +++ b/core/api-common.rst @@ -584,6 +584,13 @@ uses 512-bit hash codes (64 bytes). uint32_t value[4]; }; +.. _WadId: +.. sourcecode:: c + + struct TALER_WadId wad_id { + uint32_t value[6]; + }; + .. _eddsa-coin-pub: .. sourcecode:: c @@ -1022,3 +1029,178 @@ within the struct TALER_TransferPublicKeyP transfer_pub; struct GNUNET_HashCode coin_envelope_hash; }; + + + + +.. _TALER_PurseStatusRequestSignaturePS: +.. sourcecode:: c + + struct TALER_PurseStatusRequestSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_PURSE_STATUS_REQUEST + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + }; + + +.. _TALER_PurseStatusSignaturePS: +.. sourcecode:: c + + struct TALER_PurseStatusResponseSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_PURSE_STATUS_RESPONSE + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_AmountNBO total_purse_amount; + struct TALER_AmountNBO total_deposit_amount; + struct TALER_AmountNBO max_deposit_fees; + struct GNUNET_TIME_AbsoluteNBO purse_expiration; + struct GNUNET_TIME_AbsoluteNBO status_timestamp; + struct GNUNET_HashCode h_contract_terms; + }; + + +.. _TALER_ReserveCloseRequestSignaturePS: +.. sourcecode:: c + + struct TALER_ReserveCloseRequestSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_RESERVE_CLOSE + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + }; + + + +.. _TALER_PurseDepositSignaturePS: +.. sourcecode:: c + + struct TALER_PurseDepositSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_PURSE_DEPOSIT + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_AmountNBO coin_contribution; + struct GNUNET_TIME_AbsoluteNBO purse_expiration; + struct TALER_PursePublicKey purse_pub; + struct GNUNET_HashCode h_contract_terms; + }; + + +.. _TALER_PursePaymentConfirmedSignaturePS: +.. sourcecode:: c + + struct TALER_PursePaymentConfirmedSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_PURSE_PAYMENT_CONFIRMED + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_AmountNBO total_purse_amount; + struct TALER_AmountNBO total_deposit_fees; + struct TALER_PursePublicKey purse_pub; + struct GNUNET_TIME_AbsoluteNBO purse_expiration; + struct GNUNET_HashCode h_contract_terms; + }; + + +.. _TALER_PurseMergeSignaturePS: +.. sourcecode:: c + + struct TALER_PurseMergeSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_PURSE_MERGE + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_ReservePublicKey reserve_pub; + struct GNUNET_TIME_AbsoluteNBO merge_timestamp; + struct GNUNET_TIME_AbsoluteNBO purse_expiration; + struct GNUNET_HashCode h_contract_terms; + struct GNUNET_HashCode h_wire; + }; + + +.. _TALER_AccountMergeSignaturePS: +.. sourcecode:: c + + struct TALER_AccountMergeSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_ACCOUNT_MERGE + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_PursePublicKey purse_pub; + struct GNUNET_TIME_AbsoluteNBO merge_timestamp; + struct GNUNET_TIME_AbsoluteNBO purse_expiration; + struct GNUNET_HashCode h_contract_terms; + struct GNUNET_HashCode h_wire; + }; + + +.. _TALER_PursePaymentSignaturePS: +.. sourcecode:: c + + struct TALER_PursePaymentSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_PURSE_PAYMENT + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_AmountNBO coin_contribution; + struct GNUNET_TIME_AbsoluteNBO purse_expiration; + struct TALER_PursePublicKey purse_pub; + }; + + +.. _TALER_PurseMergeSuccessSignaturePS: +.. sourcecode:: c + + struct TALER_PurseMergeSuccessSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_PURSE_MERGE_SUCCESS + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_ReservePublicKey reserve_pub; + struct TALER_PursePublicKey purse_pub; + struct TALER_AmountNBO merge_amount; + struct GNUNET_TIME_AbsoluteNBO contract_time; + struct GNUNET_HashCode h_contract_terms; + struct GNUNET_HashCode h_wire; + }; + + +.. _TALER_AccountSetupRequestSignaturePS: +.. sourcecode:: c + + struct TALER_AccountSetupRequestSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_TIME_AbsoluteNBO kyc_timestamp; + struct GNUNET_HashCode h_wire; + }; + + +.. _TALER_AccountSetupSuccessSignaturePS: +.. sourcecode:: c + + struct TALER_AccountSetupRequestSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_ACCOUNT_SETUP_SUCCESS + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_ReservePublicKey reserve_pub; + struct GNUNET_TIME_AbsoluteNBO now; + }; + + +.. _TALER_WadDataSignaturePS: +.. sourcecode:: c + + struct TALER_WadDataSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_ACCOUNT_SETUP_SUCCESS + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_HashCode wad_data; + struct TALER_WadId wad_id; + }; diff --git a/core/api-exchange.rst b/core/api-exchange.rst index 12cb4e1a..654ce31e 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -123,21 +123,12 @@ possibly by using HTTPS. // The exchange's currency. currency: string; - // Purse fee, charged only if a purse is abandoned - // and was not covered by the account limit. - purse_fee: Amount; - - // Non-negative number of concurrent purses that any - // account holder is allowed to create without having - // to pay the purse_fee. - purse_account_limit: integer; - // EdDSA master public key of the exchange, used to sign entries // in ``denoms`` and ``signkeys``. master_public_key: EddsaPublicKey; - // Relative duration until inactive reserves are closed; not signed, expressed as - // a string in relative time in microseconds, i.e. "/Delay(1000)/" for 1 second. + // Relative duration until inactive reserves are closed; + // not signed (!), can change without notice. reserve_closing_delay: RelativeTime; // Denominations offered by this exchange. @@ -146,6 +137,11 @@ possibly by using HTTPS. // Denominations for which the exchange currently offers/requests recoup. recoup: Recoup[]; + // Fees relevant for wallet-to-wallet (or peer-to-peer) payments. + // If no fees are provided for a given time range, then the + // exchange simply does not support purses/p2p-payments at that time. + p2p_fees: P2Pfees[]; + // The date when the denomination keys were last updated. list_issue_date: Timestamp; @@ -173,6 +169,68 @@ possibly by using HTTPS. eddsa_pub: EddsaPublicKey; } + .. ts:def:: P2PFees + + interface P2PFees { + + // What date (inclusive) does these fees go into effect? + start_date: Timestamp; + + // What date (exclusive) does this fees stop going into effect? + end_date: Timestamp; + + // KYC fee, charged when a user wants to create an account. + // The first year of the account_annual_fee after the KYC is + // always included. + kyc_fee: Amount; + + // Account history fee, charged when a user wants to + // obtain the full account history, and not just the + // recent transactions in an account. + account_history_fee: Amount; + + // Annual fee charged for having an open account at the + // exchange. Charged to the account. If the account + // balance is insufficient to cover this fee, the account + // is automatically deleted/closed. (Note that the exchange + // will keep the account history around for longer for + // regulatory reasons.) + account_annual_fee: Amount; + + // How long will the exchange preserve the account + // history? After an account was deleted/closed, the + // exchange will retain the account history for + // legal reasons until this time. + legal_history_retention: RelativeTime; + + // How long does the exchange promise to keep funds + // an account for which the KYC has never happened + // after a purse was merged into an account? Basically, + // after this time funds in an account without KYC are + // forfeit. + account_kyc_timeout: RelativeTime; + + // Purse fee, charged only if a purse is abandoned + // and was not covered by the account limit. + purse_fee: Amount; + + // Non-negative number of concurrent purses that any + // account holder is allowed to create without having + // to pay the purse_fee. + purse_account_limit: integer; + + // How long does an exchange keep a purse around + // after a purse has expired (or been successfully + // merged)? A 'GET' request for a purse will + // succeed until the purse expiration time plus this + // value. + purse_timeout: RelativeTime; + + // Signature of `TALER_P2PFeesPS`. + master_sig: EddsaSignature; + + } + .. ts:def:: Denom interface Denom { @@ -326,6 +384,10 @@ possibly by using HTTPS. // Object mapping names of wire methods (i.e. "sepa" or "x-taler-bank") // to wire fees. fees: { method : AggregateTransferFee }; + + // List of exchanges that this exchange is partnering + // with to enable wallet-to-wallet transfers. + wads: ExchangePartner[]; } The specification for the account object is: @@ -370,6 +432,34 @@ possibly by using HTTPS. sig: EddsaSignature; } + .. ts:def:: ExchangePartner + + interface ExchangePartner { + // Base URL of the partner exchange. + partner_base_url: string; + + // Public master key of the partner exchange. + partner_master_pub: EddsaPublicKey; + + // Wallet-to-wallet transfer wad fee charged. + wad_fee: Amount; + + // Exchange-to-exchange wad (wire) transfer frequency. + wad_frequency: RelativeTime; + + // When did this partnership begin (under these conditions)? + start_date: Timestamp; + + // How long is this partnership expected to last? + end_date: Timestamp; + + // Signature using the exchange's offline key + // with purpose ``TALER_SIGNATURE_MASTER_PARTNER_DETAILS``. + master_sig: EddsaSignature; + } + + + ---------------------------------------------- Management operations authorized by master key @@ -890,23 +980,26 @@ exchange. .. http:get:: /reserves/$RESERVE_PUB - Request information about a reserve. - - .. note:: - The client currently does not have to demonstrate knowledge of the private - key of the reserve to make this request, which makes the reserve's public - key privileged information known only to the client, their bank, and the - exchange. In the future, we might wish to revisit this decision to improve - security, such as by having the client EdDSA-sign an ECDHE key to be used - to derive a symmetric key to encrypt the response. This would be useful if - for example HTTPS were not used for communication with the exchange. + Request information about a reserve or an account. **Request:** + *Account-Request-Signature*: The client must provide Base-32 encoded EdDSA signature made with ``$ACCOUNT_PRIV``, affirming its authorization to access the account status. The purpose used MUST be ``TALER_SIGNATURE_ACCOUNT_STATUS`` (NUMBER: TBD). + + :query history=BOOLEAN: *Optional.* If specified, the exchange + will return the recent account history. + This is still free of charge. + :query full_history=BOOLEAN: *Optional.* If 'true' is specified, + the exchange will return the full account history. This + may incur a fee that will be charged to the account. + **Response:** :http:statuscode:`200 OK`: The exchange responds with a `ReserveStatus` object; the reserve was known to the exchange. + :http:statuscode:`401 Unauthorized`: + The *Account-Request-Signature* is invalid. + This response comes with a standard `ErrorDetail` response. :http:statuscode:`404 Not found`: The reserve key does not belong to a reserve known to the exchange. @@ -918,6 +1011,14 @@ exchange. // Balance left in the reserve. balance: Amount; + // True if the owner of the account currently satisfies + // the required KYC checks. + kyc_passed: boolean; + + // True if the reserve history includes a merge of a purse + // and thus the owner must pass KYC checks before withdrawing. + kyc_required: boolean; + // Transaction history for this reserve. history: TransactionHistoryItem[]; } @@ -928,11 +1029,36 @@ exchange. // Union discriminated by the "type" field. type TransactionHistoryItem = + | AccountMergeTransaction | ReserveWithdrawTransaction | ReserveCreditTransaction | ReserveClosingTransaction | ReserveRecoupTransaction; + .. ts:def:: AccountMergeTransaction + + interface AccountMergeTransaction { + type: "MERGE"; + + // Amount merged (what was left after fees). + amount: Amount; + + // Purse that was merged. + purse_pub: EddsaPublicKey; + + // Hash of the contract. + h_contract: HashCode; + + // Signature created with the account's private key. + account_sig: EddsaSignature; + + // Signature created with the purse's private key. + purse_sig: EddsaSignature; + + // Deposit fees that were charged to the purse. + deposit_fees: Amount; + } + .. ts:def:: ReserveWithdrawTransaction interface ReserveWithdrawTransaction { @@ -1046,6 +1172,15 @@ exchange. will again yield the same response, so if the network goes down during the transaction or before the client can commit the coin signature to disk, the coin is not lost. + :http:statuscode:`202 Accepted`: + This reserve has received funds from a purse and must + be upgraded to an account (with KYC) before the withdraw can + complete. Note that this response does NOT affirm that the + withdraw will ultimately complete with the requested amount. + The user should be redirected to the provided location to perform + the required KYC checks to open the account before withdrawing. + Afterwards, the request should be repeated. + The response will be an `AccountKycRedirect` object. :http:statuscode:`403 Forbidden`: The signature is invalid. :http:statuscode:`404 Not found`: @@ -1084,7 +1219,6 @@ exchange. } - .. ts:def:: WithdrawResponse interface WithdrawResponse { @@ -1111,6 +1245,62 @@ exchange. history: TransactionHistoryItem[] } +.. http:DELETE:: /reserves/$RESERVE_PUB + + Forcefully closes a reserve. + The request header must contain an *Account-Request-Signature*. + + **Request:** + + *Account-Request-Signature*: The client must provide Base-32 encoded EdDSA signature made with ``$ACCOUNT_PRIV``, affirming its authorization to delete the account. The purpose used MUST be ``TALER_SIGNATURE_RESERVE_CLOSE``. + + :query force=BOOLEAN: *Optional.* If set to 'true' specified, the exchange + will delete the account even if there is a balance remaining. + + **Response:** + + :http:statuscode:`200 OK`: + The operation succeeded, the exchange provides details + about the account deletion. + The response will include a `ReserveClosedResponse` object. + :http:statuscode:`401 Unauthorized`: + The *Account-Request-Signature* is invalid. + This response comes with a standard `ErrorDetail` response. + :http:statuscode:`404 Not found`: + The account is unknown to the exchange. + :http:statuscode:`409 Conflict`: + The account is still has digital cash in it, the associated + wire method is ``void'' and the *force* option was not provided. + This response comes with a standard `ErrorDetail` response. + + **Details:** + + .. ts:def:: ReserveClosedResponse + + interface ReserveClosedResponse { + + // Final balance of the account. + closing_amount: Amount; + + // Current time of the exchange, used as part of + // what the exchange signs over. + close_time: Timestamp; + + // Hash of the wire account into which the remaining + // balance will be transferred. Note: may be the + // hash over ``payto://void/`, in which case the + // balance is forfeit to the profit of the exchange. + h_wire: HashCode; + + // This is a signature over a + // struct ``TALER_AccountDeleteConfirmationPS`` with purpose + // ``TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED``. + exchange_sig: EddsaSignature; + + } + + + .. _deposit-par: ------- @@ -1987,14 +2177,6 @@ Refunds - - - - - - - - .. _exchange_w2w: -------------------------- @@ -2003,66 +2185,43 @@ Wallet-to-wallet transfers TODO for the spec: - * endpoint to create purse with KYC'ed account OR payment, - but without deposit - * endpoint to DELETE account is missing (note: may need - two variants: bank-administrative (offline key?) - and user-driven) - * specify new database schema at exchange - * specify what each signature is made over precisely - * add KYC fee to /keys - * add extended account history fee to /keys - * add purse 'GET' expiration (purse_expiration is when - deposits are no longer allowed, but we may still want - to return the purse status for a bit longer, right? - Or only as part of the coins/accounts status? What about - long-pollers that are active at this expiration time? - [should not DELETE the purse data while the long pollers - are still potentially waking up, so needs a grace period, at least]) + * Update coin history replies to include purse actions: + - TALER_SIGNATURE_PURSE_PAYMENT! + * Update reserve history replies to include merge & kyc actions: + - TALER_SIGNATURE_ACCOUNT_MERGE + - TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST + * specify new database schema at exchange (add SQL to DD13!) * update wire transfer API to enable WAD IDs (and while we are at it, should probably also write extended version to allow _merchants_ to query for their inbound transfers, so spec - for both WADs and regular WTID!) - * specify WAD ID format (keep separte from - WTIDs and reserve public keys!) - * Update coin history replies to include purse actions. - * Merging a purse into an account that is never KYC'ed means - the funds are lost. We need to specify an account expiration - time (after funds are merged, but KYC never happens). + for both WADs and regular WTID!); also add flag to + tell exchange for inbound wire transfers that they are + from a partner bank where KYC fees would be waived! Discussion: - * Should the account-withdraw be a separate endpoint from - the reserve withdraw, or should we *extend* the reserve-withdraw - with the "missing KYC" response? - * Should the account-history be a separate endpoint from - the reserve history, or should we *extend* the reserve-history - with the new history entries and the payment option? - * Should we return somewhere a list of the desired - KYC attributes that the wallet should (optionally) - POST to the exchange when initiating KYC? (see: "attributes"). - -Notes: - - * Current API does not allow 'merging' a purse directly into - a regular bank account. The merge MUST go into an account. - * What do we do with KYC'ed accounts where the user fails to - drain the funds? Just keep them? Or do we require getting an - IBAN as part of KYC and 'close' the account that way? - + * when the user POSTs to /kyc for a reserve with a payto URI + that differs from the URI that was used to establish the + reserve, do we 409 conflict or accept? + That seems like an attack vector: + Say I learn your account pub and wire you money from my + bank account, thus blocking you from /kyc'ing your account!) + * when the user POSTs to /kyc for an account with a payto URI + that differs from the URI that was previously used for a + /kyc for the same account, do we allow the KYC to proceed + and update the bank account? Is there an attack vector? -Purses -^^^^^^ .. http:GET:: /purses/$PURSE_PUB Obtain information about a purse. The request header must - contain a *Purse-Request-Signature*. + contain a *Purse-Request-Signature*. Endpoint used by + the party that did not create the purse. **Request:** - *Purse-Request-Signature*: The client must provide Base-32 encoded EdDSA signature made with ``$PURSE_PRIV``, affirming its authorization to download the purse status. The purpose used MUST be ``TALER_SIGNATURE_PURSE_STATUS`` (NUMBER: TBD). + *Purse-Request-Signature*: The client must provide Base-32 encoded EdDSA signature made with ``$PURSE_PRIV``, affirming its authorization to download the purse status. The purpose used MUST be ``TALER_SIGNATURE_PURSE_STATUS_REQUEST``. :query merge_timeout_ms=NUMBER: *Optional.* If specified, the exchange @@ -2118,6 +2277,9 @@ Purses // EdDSA signature of the exchange affirming the purse status. exchange_sig: EddsaSignature; + // EdDSA public key exchange used for exchange_sig. + exchange_pub: EddsaPublicKey; + // AES-GCM Encrypted contract terms using encryption // key derived from DH of 'contract_pub' and the 'purse_pub'. // Optional, may be omitted if not desired by the client. @@ -2133,7 +2295,7 @@ Purses .. http:POST:: /purses/$PURSE_PUB/deposit - Create a purse without an account, but with associated payment. + Deposit money into a purse. Endpoint used by the buyer. **Request:** The request body must be a `PurseRequest` object. @@ -2173,59 +2335,6 @@ Purses **Details:** - .. ts:def:: PursePaymentSuccess - - interface PursePaymentSuccess { - - // Total amount paid into the purse. - total_purse_amount: Amount; - - // Total deposit fees charged. - total_deposit_fees: Amount; - - // EdDSA signature of the exchange affirming the payment. - // Signs over the above and the purse public key and - // the hash of the contract terms. - exchange_sig: EddsaSignature; - - } - - .. ts:def:: PursePaymentAccepted - - interface PursePaymentAccepted { - - // Total amount paid so far into the purse, in this - // and previous requests. - total_amount_deposited: Amount; - - // Total amount contributed by the current request. - total_amount_contributed: Amount; - - } - - .. ts:def:: PurseConflict - - interface PurseConflict { - - // Total amount to be paid into the purse as per - // the previous request. - total_purse_amount: Amount; - - // SHA-512 hash of the contact of the purse. - h_contract_terms: HashCode; - - // Indicative time by which the purse should expire - // if it has not been merged into an account. At this - // point, all of the deposits made should be - // auto-refunded. - purse_expiration: Timestamp; - - // EdDSA signature of the purse confirming that the - // above details hold for this purse. - purse_sig: EddsaSignature; - - } - .. ts:def:: PurseRequest interface PurseRequest { @@ -2245,31 +2354,39 @@ Purses // SHA-512 hash of the contact of the purse. h_contract_terms: HashCode; - // ECDH contract_public key used to encrypt the contract. - // Optional as the contract terms may already be known - // to the exchange or the other wallet from a different - // interaction. - contract_pub?: EcdhPublicKey; - - // AES-GCM Encrypted contract terms using encryption - // key derived from DH of 'contract_pub' and the 'purse_pub'. - // Optional as the contract terms may already be known - // to the exchange or the other wallet from a different - // interaction. - e_contract_terms?: byte[]; - // Client-side timestamp of when the payment was made. - timestamp: Timestamp; + payment_timestamp: Timestamp; // Indicative time by which the purse should expire // if it has not been merged into an account. At this // point, all of the deposits made will be auto-refunded. purse_expiration: Timestamp; + // Optional encrypted contract, in case the buyer is + // proposing the contract and thus establishing the + // purse with the payment. + contract?: EncryptedContract; + // Array of coins being deposited into the purse. deposits: PurseDeposit[]; } + .. ts:def:: EncryptedContract { + + // ECDH contract_public key used to encrypt the contract. + // Optional as the contract terms may already be known + // to the exchange or the other wallet from a different + // interaction. + contract_pub: EcdhPublicKey; + + // AES-GCM Encrypted contract terms using encryption + // key derived from DH of 'contract_pub' and the 'purse_pub'. + // Optional as the contract terms may already be known + // to the exchange or the other wallet from a different + // interaction. + e_contract_terms: byte[]; + } + .. ts:def:: PurseDeposit { // Public key of the coin being deposited into the purse. @@ -2292,11 +2409,71 @@ Purses } + .. ts:def:: PursePaymentSuccess + + interface PursePaymentSuccess { + + // Total amount paid into the purse. + total_purse_amount: Amount; + + // Total deposit fees charged. + total_deposit_fees: Amount; + + // EdDSA signature of the exchange affirming the payment, + // of purpose TALER_SIGNATURE_PURSE_PAYMENT_CONFIRMED + // Signs over the above and the purse public key and + // the hash of the contract terms. + exchange_sig: EddsaSignature; + + // public key used to create the signature. + exchange_pub: EddsaPublicKey; + + } + + .. ts:def:: PursePaymentAccepted + + interface PursePaymentAccepted { + + // Total amount paid so far into the purse, in this + // and previous requests. + total_amount_deposited: Amount; + + // Total amount contributed by the current request. + total_amount_contributed: Amount; + + } + + .. ts:def:: PurseConflict + + interface PurseConflict { + + // Total amount to be paid into the purse as per + // the previous request. + total_purse_amount: Amount; + + // SHA-512 hash of the contact of the purse. + h_contract_terms: HashCode; + + // Indicative time by which the purse should expire + // if it has not been merged into an account. At this + // point, all of the deposits made should be + // auto-refunded. + purse_expiration: Timestamp; + + // EdDSA signature of the purse confirming that the + // above details hold for this purse. + purse_sig: EddsaSignature; + + } + .. ts:def:: PurseDepositDoubleSpendError interface DepositDoubleSpendError { - // The string constant "insufficient funds". - hint: string; + // Taler error code. + code: number; + + // Human-readable description of the error, i.e. "insufficient funds". + hint?: string; // Total amount contributed by the current request. // Note that some coins may have been successfully @@ -2310,11 +2487,10 @@ Purses } - .. http:POST:: /purses/$PURSE_PUB/merge Merge purse with account, adding the value of the purse into - the account. + the account. Endpoint to be used by the seller. **Request:** The request body must be a `MergeRequest` object. @@ -2352,49 +2528,37 @@ Purses **Details:** - .. ts:def:: MergeSuccess - - interface MergeSuccess { - - // Amount merged (excluding deposit fees). - merge_amount: Amount; - - // SHA-512 hash of the contact of the purse. - h_contract_terms: HashCode; - - // Time at which the merge came into effect. - merge_time: Timestamp; - - // EdDSA signature of the exchange affirming the merge. - // Signs over the above and the account public key. - exchange_sig: EddsaSignature; - - } - - .. ts:def:: MergeAccepted - - interface MergeAccepted { - - // The number of open purses under the given account. - // Useful to calculate how many purses still can be created. - open_purses: integer; - - } - .. ts:def:: MergeRequest interface MergeRequest { // payto://-URI of the account the purse is to be merged into. - // Must be of the form: "payto://taler/EXCHANGE_URL/ACCOUNT_PUB". + // Must be of the form: "payto://taler/EXCHANGE_URL/RESERVE_PUB". payto_uri: string; - // EdDSA signature of the exchange affirming the merge. - account_sig: EddsaSignature; + // EdDSA signature of the account/reserve affirming the merge. + // Must be of purpose TALER_SIGNATURE_ACCOUNT_MERGE + reserve_sig: EddsaSignature; // EdDSA signature of the purse private key affirming the merge. + // Must be of purpose TALER_SIGNATURE_PURSE_MERGE. purse_sig: EddsaSignature; + // SHA-512 hash of the contact of the purse. + h_contract_terms: HashCode; + + // Client-side timestamp of when the merge request was made. + merge_timestamp: Timestamp; + + // Indicative time by which the purse should expire + // if it has not been paid. + purse_expiration: Timestamp; + + // Optional encrypted contract, in case the seller is + // proposing the contract and thus establishing the + // purse with the payment. + contract?: EncryptedContract; + // Array of payments made to pay for the creation of the // purse. Can be empty, say if no payment is needed. payments: CreatePurseDeposit[]; @@ -2416,106 +2580,55 @@ Purses // Exchange's unblinded RSA signature of the coin. ub_sig: RsaSignature; - // Signature of `TALER_PurseCreateRequestPS`, + // Signature of purpose TALER_SIGNATURE_PURSE_PAYMENT. // made by the customer with the // `coin's private key `. coin_sig: EddsaSignature; } + .. ts:def:: MergeSuccess + interface MergeSuccess { + // Amount merged (excluding deposit fees). + merge_amount: Amount; -.. _exchange_accounts: - -Accounts -^^^^^^^^ - -.. http:GET:: /accounts/$ACCOUNT_PUB - - Obtain information about an account. The request header must - contain an *Account-Request-Signature*. - - **Request:** - - *Account-Request-Signature*: The client must provide Base-32 encoded EdDSA signature made with ``$ACCOUNT_PRIV``, affirming its authorization to access the account status. The purpose used MUST be ``TALER_SIGNATURE_ACCOUNT_STATUS`` (NUMBER: TBD). - - :query history=BOOLEAN: *Optional.* If specified, the exchange - will return the recent account history. - This is still free of charge. - :query full_history=BOOLEAN: *Optional.* If 'true' is specified, - the exchange will return the full account history. This - may incur a fee that will be charged to the account. - - **Response:** - - :http:statuscode:`200 OK`: - The operation succeeded, the exchange provides details - about the account. - The response will include a `AccountHistory` object. - :http:statuscode:`401 Unauthorized`: - The *Account-Request-Signature* is invalid. - This response comes with a standard `ErrorDetail` response. - :http:statuscode:`404 Not found`: - The account is unknown to the exchange. - - **Details:** - - .. ts:def:: AccountHistory + // SHA-512 hash of the contact of the purse. + h_contract_terms: HashCode; - interface AccountHistory { + // Time at which the merge came into effect. + // Maximum of the "payment_timestamp" and the + // "merge_timestamp". + contract_time: Timestamp; - // Current balance of the account. - balance: Amount; + // EdDSA signature of the exchange affirming the merge of + // purpose TALER_SIGNATURE_PURSE_MERGE_SUCCESS. + // Signs over the above and the account public key. + exchange_sig: EddsaSignature; - // True if the owner of the account currently satisfies - // the required KYC checks. - kyc: boolean; + // public key used to create the signature. + exchange_pub: EddsaPublicKey; - // Transaction history for this account. - history: AccountHistoryItem[]; } - Objects in the transaction history have the following format: - - .. ts:def:: AccountHistoryItem - - // Union discriminated by the "type" field. - type AccountHistoryItem = - | AccountMergeTransaction - | ReserveWithdrawTransaction - | ReserveCreditTransaction - | ReserveClosingTransaction - | ReserveRecoupTransaction; - - .. ts:def:: AccountMergeTransaction - - interface AccountMergeTransaction { - type: "MERGE"; - - // Amount merged (what was left after fees). - amount: Amount; - - // Purse that was merged. - purse_pub: EddsaPublicKey; - - // Hash of the contract. - h_contract: HashCode; + .. ts:def:: MergeAccepted - // Signature created with the account's private key. - account_sig: EddsaSignature; + interface MergeAccepted { - // Signature created with the purse's private key. - purse_sig: EddsaSignature; + // The number of remaining purses that can still be opened + // under the given account. + remaining_purses: integer; - // Deposit fees that were charged to the purse. - deposit_fees: Amount; } -.. http:POST:: /accounts +.. http:POST:: /reserves/$RESERVE_PUB/kyc - Create a new account. + Upgrade a reserve to an *account*. The reserve must + (from wire transfers or merges of purses) already have a + sufficient balance to cover the KYC fee. The signature + affirms that the KYC fee can and should be charged to the reserve. **Request:** The request body must be a `AccountSetupRequest` object. @@ -2529,10 +2642,16 @@ Accounts The operation succeeded, the exchange confirms that the account can now be used. The response will be an `AccountStatus` object. - :http:statuscode:`303 See Other`: + :http:statuscode:`202 Accepted`: The user should be redirected to the provided location to perform the required KYC checks to open the account. Afterwards, the request should be repeated. + The response will be an `AccountKycRedirect` object. + :http:statuscode:`409 Conflict`: + The reserve or account was previously associated with a different + payto URI, and changing the associated bank account is not + permitted. FIXME: should we allow it? Should we use PATCH for this? + Or only conflict if this was an account and not a reserve!?? :http:statuscode:`504 Gateway Timeout`: The exchange did not receive a confirmation from the KYC service within the specified time period. Used when long-polling for the @@ -2544,44 +2663,25 @@ Accounts interface AccountSetupRequest { - // EdDSA public key for the account. - account_pub: EddsaPublicKey; + // Time of the request to perform the KYC. Determines + // the KYC fee charged by the exchange. Must be + // reasonably close to the current time of the exchange. + kyc_timestamp: Timestamp; + + // Bank account to be associated with the account. + // Can be ``payto://void/`` to not associate the + // account with any bank account. In this case, + // closing the account will result in the balance + // being forfeit. If the provided wire method is + // not supported by the exchange *and* not ``void``, + // this is a ``Bad Request`` (HTTP status 400). + payto_uri: string; // EdDSA signature of the account affirming the request - // to create the account. + // to create the account, must be of purpose + // TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST account_sig: EddsaPublicKey; - // Array of payments made to pay for the creation of the - // account. Can be empty, say if no payment is needed. - payments: CreateAccountDeposit[]; - - // Generic key-value map of attributes about the - // account owner. Useful to pre-fill KYC forms. - // A list of well-known keys is defined in FIXME. - attributes: Object; - - } - - .. ts:def:: CreateAccountDeposit { - - // Public key of the coin being used to pay for creating an - // account. - coin_pub: EddsaPublicKey; - - // Amount to be deposited, can be a fraction of the - // coin's total value. - contribution: Amount; - - // Hash of denomination RSA key with which the coin is signed. - denom_pub_hash: HashCode; - - // Exchange's unblinded RSA signature of the coin. - ub_sig: RsaSignature; - - // Signature of `TALER_AccountCreateRequestPS`, - // made by the customer with the - // `coin's private key `. - coin_sig: EddsaSignature; } .. ts:def:: AccountKycStatus @@ -2593,25 +2693,26 @@ Accounts now: Timestamp; // EdDSA signature of the exchange affirming the account - // is KYC'ed. + // is KYC'ed, must be of purpose + // TALER_SIGNATURE_ACCOUNT_SETUP_SUCCESS. exchange_sig: EddsaSignature; + // public key used to create the signature. + exchange_pub: EddsaPublicKey; } + .. ts:def:: AccountKycRedirect -.. http:post:: /accounts/$ACCOUNT_PUB/withdraw + interface AccountKycRedirect { + + // URL that the user should open in a browser to + // proceed with the KYC process. + kyc_url: string; + + } - This endpoint is virtually identical to the withdraw endpoint - for reserves, with the only difference being a new response - code `303 See Other` which is returned for accounts that - have not yet completed the required KYC check. - **Response**: - :http:statuscode:`303 See Other`: - The user should be redirected to the provided location to perform - the required KYC checks to open the account. Afterwards, the - request should be repeated. .. _exchange_wads: @@ -2621,7 +2722,7 @@ Wads ^^^^ These endpoints are used to manage exchange-to-exchange payments in support of -wallet-to-wallet payments. +wallet-to-wallet payments. Only another exchange should access this endpoint. .. http:GET:: /wads/$WAD_ID @@ -2650,7 +2751,15 @@ wallet-to-wallet payments. // Transfers aggregated in the wad. items: WadItem[]; - } + + // EdDSA signature of the exchange affirming the wad + // data is correct, must be of purpose + // TALER_SIGNATURE_WAD_DATA. + exchange_sig: EddsaSignature; + + // public key used to create the signature. + exchange_pub: EddsaPublicKey; + } Objects in the wad item list have the following format: @@ -2661,19 +2770,29 @@ wallet-to-wallet payments. // Amount in the purse. amount: Amount; + // payto://-URI of the account the purse is to be merged into. + // Must be of the form: "payto://taler/EXCHANGE_URL/RESERVE_PUB". + payto_uri: string; + // Purse public key. purse_pub: EddsaPublicKey; - // Account public key. - account_pub: EddsaPublicKey; - // Hash of the contract. h_contract: HashCode; + // Indicative time by which the purse should expire + // if it has not been paid. + purse_expiration: Timestamp; + + // Client-side timestamp of when the merge request was made. + merge_timestamp: Timestamp; + // Signature created with the account's private key. + // Must be of purpose TALER_SIGNATURE_ACCOUNT_MERGE account_sig: EddsaSignature; // Signature created with the purse's private key. + // Must be of purpose TALER_SIGNATURE_PURSE_MERGE purse_sig: EddsaSignature; // Deposit fees that were charged to the purse. diff --git a/design-documents/013-peer-to-peer-payments.rst b/design-documents/013-peer-to-peer-payments.rst index 914d87fe..0d3a767c 100644 --- a/design-documents/013-peer-to-peer-payments.rst +++ b/design-documents/013-peer-to-peer-payments.rst @@ -278,29 +278,44 @@ Contract metadata for W2W payments can be exchanged in three ways: Account creation ---------------- -1. The payee generates an account key, which also yields a - ``payto://taler/$EXCHANGE_BASE_URL/$ACCOUNT_PUB`` +An account is simply a reserve that has been subjected to +KYC. A reserve that has seen a purse merged into it must +be upgraded to an account before further withdraw (or close) +operations are allowed. The usual closure deadline for a +reserve is extended to the KYC deadline. + +1. The payee generates a reserve key, which also yields a + ``payto://taler/$EXCHANGE_BASE_URL/$RESERVE_PUB`` target address (for which the payee knows the corresponding - account private key). -2. When withdrawing from an account, the exchange first checks if the - customer has satisfied the KYC requirements. If not, the customer - is redirected to a Web page where they can perform the necessary - KYC operation. + reserve private key). +2. When withdrawing from a reserve that has experienced + merge operations and thus must be an account, the exchange + first checks if the customer has satisfied the KYC requirements. + If not, the customer is redirected to a Web page where they + can perform the necessary KYC operation. 3. For this, the exchange wire gateway is extended with a request to - check the KYC status of a customer based on an ``ACCOUNT_PUB``. - Possible replies are ``in-progress`` and ``succeeded``. An ``in-progress`` - status should be accompanied with + check the KYC status of a customer based on an ``RESERVE_PUB``. + Possible replies are ``in-progress`` and ``succeeded``. + An ``in-progress`` status should be accompanied with information how the customer may complete the KYC check. -4. A new exchange endpoint ``/account/$ACCOUNT_PUB/kyc`` - allows wallets to request a KYC for an - ``$ACCOUNT_PUB``. Such a request may include the requirement to pay - a **KYC fee**. The KYC fee may be charged to that account (if it exists), - or could be waved if the account was established via a wire transfer - from a partner bank. -5. If the account owner fails to perform the KYC check, the funds - in an account remain inaccessible. After a configurable duration, +4. A new exchange endpoint ``/reserves/$RESERVE_PUB/kyc`` + allows wallets to request a KYC for a + ``$RESERVE_PUB``. Such a request may include the requirement to pay + a **KYC fee**. + The KYC fee may be charged to the reserve (a sufficient + balance can be provided by the wallet by creating a purse + and merging the purse with the reserve, if needed), + or could be waved if the reserve was established via a wire transfer + from a partner bank where KYC is free. For this, the Wire + gateway API is extended with a flag that informs the exchange + that the incoming wire transfer implies a free KYC check. +5. If the account owner fails to perform the KYC check, all funds + in an reserve remain inaccessible. After a configurable duration, the funds may be considered forfeit and become the property of - the exchange where the account is located. + the exchange where the reserve is located. +6. The exchange may charge an annual **account fee**, and can + close accounts where the account balance is insufficient to + cover the account fee. Withdrawing from accounts @@ -328,21 +343,27 @@ Withdrawing from accounts Account deletion ---------------- -1. The account owner can delete an account by signing a deletion message - with the account private key. -2. This basically resets the KYC data at the exchange, preventing further use of - the account. This is helpful in case a user is concerned about having - accidentally disclosed the account private key to a third party. -3. If funds remain in the account, an error message is generated instead. The - user can pass an extra override parameter to delete accounts even if - they still contain funds. +1. A reserve owner can delete a reserve by signing a deletion message + with the reserve private key. +2. This basically resets the KYC data at the exchange, preventing + further use of the account. This is helpful in case a user is + concerned about having + accidentally disclosed the reserve private key to a third party. +3. If funds remain in the reserve, the exchange will close the + reserve and wire the funds to the associated bank account. + If no bank account is associated with the reserve, + an error message is generated instead. The + user can pass an extra override parameter to delete reserves + even if they still contain funds. 4. A related endpoint should exist for the exchange operator, possibly - using messages signed with the exchange offline key. This could be - useful in case customers die or are otherwise in need for manual - intervention that requires an account to be deleted. In this case, + using messages signed with a new exchange management key. + This could be useful in case customers die or are otherwise + in need for manual intervention that requires an account to + be deleted. In this case, remaining funds in the account should be wired to a bank account - designated in the message with the offline signature. The audit - report should contain a special note for all of these account deletions. + designated in the message with the management signature. The audit + report should contain a special note for all of these types of + account deletions. -- cgit v1.2.3 From 7214412a05e38b8b957257c3ee99d8acb6571179 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 6 May 2021 15:33:52 +0200 Subject: work on w2w spec --- core/api-common.rst | 27 ++ core/api-exchange.rst | 403 ++++++++++++++++++++----- design-documents/013-peer-to-peer-payments.rst | 102 ++++++- 3 files changed, 455 insertions(+), 77 deletions(-) diff --git a/core/api-common.rst b/core/api-common.rst index bb922cc2..5c05db0c 100644 --- a/core/api-common.rst +++ b/core/api-common.rst @@ -1033,6 +1033,30 @@ within the +.. _TALER_ReserveStatusRequestSignaturePS: +.. sourcecode:: c + + struct TALER_PurseStatusRequestSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_RESERVE_STATUS_REQUEST + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + }; + + +.. _TALER_ReserveHistoryRequestSignaturePS: +.. sourcecode:: c + + struct TALER_PurseStatusRequestSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_RESERVE_HISTORY_REQUEST + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_AmountNBO history_fee; + struct GNUNET_TIME_AbsoluteNBO request_timestamp; + }; + + .. _TALER_PurseStatusRequestSignaturePS: .. sourcecode:: c @@ -1115,6 +1139,7 @@ within the struct TALER_ReservePublicKey reserve_pub; struct GNUNET_TIME_AbsoluteNBO merge_timestamp; struct GNUNET_TIME_AbsoluteNBO purse_expiration; + struct TALER_AmountNBO purse_value_after_fees; struct GNUNET_HashCode h_contract_terms; struct GNUNET_HashCode h_wire; }; @@ -1131,6 +1156,7 @@ within the struct TALER_PursePublicKey purse_pub; struct GNUNET_TIME_AbsoluteNBO merge_timestamp; struct GNUNET_TIME_AbsoluteNBO purse_expiration; + struct TALER_AmountNBO purse_value_after_fees; struct GNUNET_HashCode h_contract_terms; struct GNUNET_HashCode h_wire; }; @@ -1176,6 +1202,7 @@ within the */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; struct GNUNET_TIME_AbsoluteNBO kyc_timestamp; + struct TALER_AmountNBO kyc_fee; struct GNUNET_HashCode h_wire; }; diff --git a/core/api-exchange.rst b/core/api-exchange.rst index 654ce31e..27886c24 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -978,14 +978,12 @@ exchange. advertise those terms of service. -.. http:get:: /reserves/$RESERVE_PUB +.. http:post:: /reserves/$RESERVE_PUB/status Request information about a reserve or an account. **Request:** - *Account-Request-Signature*: The client must provide Base-32 encoded EdDSA signature made with ``$ACCOUNT_PRIV``, affirming its authorization to access the account status. The purpose used MUST be ``TALER_SIGNATURE_ACCOUNT_STATUS`` (NUMBER: TBD). - :query history=BOOLEAN: *Optional.* If specified, the exchange will return the recent account history. This is still free of charge. @@ -993,6 +991,8 @@ exchange. the exchange will return the full account history. This may incur a fee that will be charged to the account. + The request body must be a `ReserveStatusRequest` object. + **Response:** :http:statuscode:`200 OK`: @@ -1005,6 +1005,14 @@ exchange. **Details:** + .. ts:def:: ReserveStatusRequest + + interface ReserveStatusRequest { + // Signature of type + // TALER_SIGNATURE_RESERVE_STATUS_REQUEST + reserve_sig: EddsaSignature; + } + .. ts:def:: ReserveStatus interface ReserveStatus { @@ -1020,6 +1028,7 @@ exchange. kyc_required: boolean; // Transaction history for this reserve. + // May be partial (!). history: TransactionHistoryItem[]; } @@ -1030,29 +1039,84 @@ exchange. // Union discriminated by the "type" field. type TransactionHistoryItem = | AccountMergeTransaction + | AccountSetupTransaction + | ReserveHistoryTransaction | ReserveWithdrawTransaction | ReserveCreditTransaction | ReserveClosingTransaction | ReserveRecoupTransaction; + .. ts:def:: AccountHistoryTransaction + + interface AccountHistoryTransaction { + type: "HISTORY"; + + // Fee agreed to by the reserve owner. + history_fee: Amount; + + // Time when the request was made. + request_timestamp: Timestamp; + + // Signature created with the reserve's private key. + // Must be of purpose TALER_SIGNATURE_RESERVE_HISTORY_REQUEST. + reserve_sig: EddsaSignature; + + } + + .. ts:def:: AccountSetupTransaction + + interface AccountSetupTransaction { + type: "SETUP"; + + // KYC fee agreed to by the reserve owner. + kyc_fee: Amount; + + // Time when the KYC was triggered. + kyc_timestamp: Timestamp; + + // Hash of the wire details of the account. + // Note that this hash is unsalted and potentially + // private (as it could be inverted), hence access + // to this endpoint must be authorized using the + // private key of the reserve. + h_wire: HashCode; + + // Signature created with the reserve's private key. + // Must be of purpose TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST. + reserve_sig: EddsaSignature; + + } + .. ts:def:: AccountMergeTransaction interface AccountMergeTransaction { type: "MERGE"; - // Amount merged (what was left after fees). + // Actual amount merged (what was left after fees). amount: Amount; + // Minimum amount merged (amount signed by the + // reserve and purse signatures). + minimum_amount: Amount; + // Purse that was merged. purse_pub: EddsaPublicKey; + // Time of the merge. + merge_timestamp: Timestamp; + + // Expiration time of the purse. + purse_expiration: Timestamp; + // Hash of the contract. h_contract: HashCode; - // Signature created with the account's private key. - account_sig: EddsaSignature; + // Signature created with the reserve's private key. + // Must be of purpose TALER_SIGNATURE_ACCOUNT_MERGE. + reserve_sig: EddsaSignature; // Signature created with the purse's private key. + // Must be of purpose TALER_SIGNATURE_PURSE_MERGE. purse_sig: EddsaSignature; // Deposit fees that were charged to the purse. @@ -1155,6 +1219,46 @@ exchange. coin_pub: CoinPublicKey; } + +.. http:post:: /reserves/$RESERVE_PUB/history + + Request information about the full history of + a reserve or an account. + + **Request:** + + The request body must be a `ReserveHistoryRequest` object. + + **Response:** + + :http:statuscode:`200 OK`: + The exchange responds with a `ReserveStatus` object; the reserve was known to the exchange. + :http:statuscode:`401 Unauthorized`: + The *Account-Request-Signature* is invalid. + This response comes with a standard `ErrorDetail` response. + :http:statuscode:`404 Not found`: + The reserve key does not belong to a reserve known to the exchange. + :http:statuscode:`412 Precondition failed`: + The balance in the reserve is insufficient to pay for the history request. + This response comes with a standard `ErrorDetail` response. + + **Details:** + + .. ts:def:: ReserveHistoryRequest + + interface ReserveHistoryRequest { + // Signature of type + // TALER_SIGNATURE_RESERVE_HISTORY_REQUEST + reserve_sig: EddsaSignature; + + // Time when the client made the request. + // Timestamp must be reasonably close to the time of + // the exchange, otherwise the exchange may reject + // the request. + request_timestamp: Timestamp; + } + + .. http:post:: /reserves/$RESERVE_PUB/withdraw Withdraw a coin of the specified denomination. Note that the client should @@ -1450,101 +1554,244 @@ denomination. .. ts:def:: CoinSpendHistoryItem - interface CoinSpendHistoryItem { - // Either "DEPOSIT", "MELT", "REFUND", "RECOUP", - // "OLD-COIN-RECOUP" or "RECOUP-REFRESH". - type: string; + // Union discriminated by the "type" field. + type CoinSpendHistoryItem = + | CoinDepositTransaction + | CoinMeltTransaction + | CoinRefundTransaction + | CoinRecoupTransaction + | CoinOldCoinRecoupTransaction + | CoinRecoupRefreshTransaction + | CoinPursePaymentTransaction; + + + .. ts:def:: CoinDepositTransaction + + interface CoinDepositTransaction { + type: "DEPOSIT"; // The total amount of the coin's value absorbed (or restored in the // case of a refund) by this transaction. - // Note that for deposit and melt this means the amount given includes - // the transaction fee, while for refunds the amount given excludes - // the transaction fee. The current coin value can thus be computed by - // subtracting deposit and melt amounts and adding refund amounts from - // the coin's denomination value. + // The amount given includes + // the deposit fee. The current coin value can thus be computed by + // subtracting this amount. amount: Amount; - // Deposit fee in case of type "DEPOSIT". + // Deposit fee. deposit_fee: Amount; - // Public key of the merchant, for "DEPOSIT" operations. - merchant_pub?: EddsaPublicKey; + // Public key of the merchant. + merchant_pub: EddsaPublicKey; // Date when the operation was made. - // Only for "DEPOSIT", "RECOUP", "OLD-COIN-RECOUP" and - // "RECOUP-REFRESH" operations. - timestamp?: Timestamp; + timestamp: Timestamp; // Date until which the merchant can issue a refund to the customer via the - // exchange, possibly zero if refunds are not allowed. Only for "DEPOSIT" operations. - refund_deadline?: Timestamp; + // exchange, possibly zero if refunds are not allowed. + refund_deadline: Timestamp; - // Signature by the coin, only present if ``type`` is "DEPOSIT", "MELT", "RECOUP", - // or "RECOUP-REFRESH". - coin_sig?: EddsaSignature; + // Signature by the coin. + coin_sig: EddsaSignature; - // Deposit fee in case of type "MELT". - melt_fee: Amount; + // Hash of the bank account from where we received the funds. + h_wire: HashCode; - // Commitment from the melt operation, only for "MELT". - rc?: TALER_RefreshCommitmentP; + // Hash of the public denomination key used to sign the coin. + // FIXME: why do we care to have this? + h_denom_pub: HashCode; + + // Hash over the proposal data of the contract that + // is being paid. + h_contract_terms: HashCode; + + } + + .. ts:def:: CoinMeltTransaction + + interface CoinMeltTransaction { + type: "MELT"; + + // The total amount of the coin's value absorbed by this transaction. + // Note that for melt this means the amount given includes + // the melt fee. The current coin value can thus be computed by + // subtracting the amounts. + amount: Amount; + + // Signature by the coin. + coin_sig: EddsaSignature; + + // Melt fee. + melt_fee: Amount; - // Hash of the bank account from where we received the funds, - // only present if ``type`` is "DEPOSIT". - h_wire?: HashCode; + // Commitment from the melt operation. + rc: TALER_RefreshCommitmentP; // Hash of the public denomination key used to sign the coin. - // only present if ``type`` is "DEPOSIT", "RECOUP", - // "RECOUP-REFRESH", or "MELT". - h_denom_pub?: HashCode; + // FIXME: why do we care to have this? + h_denom_pub: HashCode; - // Deposit fee in case of type "REFUND". - refund_fee?: Amount; + } - // Hash over the proposal data of the contract that - // is being paid (if type is "DEPOSIT") or refunded (if - // ``type`` is "REFUND"); otherwise absent. - h_contract_terms?: HashCode; + .. ts:def:: CoinRefundTransaction - // Refund transaction ID. Only present if ``type`` is - // "REFUND". - rtransaction_id?: Integer; + interface CoinRefundTransaction { + type: "REFUND"; - // Coin blinding key. Only present if ``type`` is - // "RECOUP" or "RECOUP-REFRESH". - coin_blind?: DenominationBlindingKeyP; + // The total amount of the coin's value restored + // by this transaction. + // The amount given excludes the transaction fee. + // The current coin value can thus be computed by + // adding the amounts to the coin's denomination value. + amount: Amount; - // Reserve receiving the recoup. Only present if ``type`` is - // "RECOUP". - reserve_pub?: EddsaPublicKey; + // Refund fee in case of type "REFUND". + refund_fee: Amount; + + // Hash over the proposal data of the contract that + // is being refunded. + h_contract_terms: HashCode; + + // Refund transaction ID. + rtransaction_id: Integer; // `EdDSA Signature ` authorizing the REFUND. Made with // the `public key of the merchant `. - // Only present if ``type`` is "REFUND". - merchant_sig?: EddsaSignature; + merchant_sig: EddsaSignature; + + } + + .. ts:def:: CoinRecoupTransaction + + interface CoinRecoupTransaction { + type: "RECOUP"; + + // The total amount of the coin's value absorbed + // by this transaction. + // The current coin value can thus be computed by + // subtracting the amount from + // the coin's denomination value. + amount: Amount; + + // Date when the operation was made. + timestamp: Timestamp; + + // Signature by the coin. + coin_sig: EddsaSignature; + + // Hash of the public denomination key used to sign the coin. + // FIXME: why do we care to have this? + h_denom_pub: HashCode; + + // Coin blinding key. + coin_blind: DenominationBlindingKeyP; + + // Reserve receiving the recoup. + reserve_pub: EddsaPublicKey; + + // Signature by the exchange, must be + // of type TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP. + exchange_sig: EddsaSignature; + + // Public key used to sign ``exchange_sig`` + exchange_pub: EddsaPublicKey; + + } + + .. ts:def:: CoinOldCoinRecoupTransaction + + interface CoinOldCoinRecoupTransaction { + type: "OLD-COIN-RECOUP"; + + // The total amount of the coin's value restored + // by this transaction. + // The current coin value can thus be computed by + // adding the amount to the coin's denomination value. + amount: Amount; + + // Date when the operation was made. + timestamp: Timestamp; + + // Signature by the exchange + // of type TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH. + exchange_sig: EddsaSignature; + + // Public key used to sign ``exchange_sig``, + exchange_pub: EddsaPublicKey; + + } + + .. ts:def:: CoinRecoupRefreshTransaction + + interface CoinRecoupRefreshTransaction { + type: "RECOUP-REFRESH"; + + // The total amount of the coin's value absorbed + // by this transaction. + // The current coin value can thus be computed by + // subtracting this amounts from + // the coin's denomination value. + amount: Amount; - // Public key of the reserve that will receive the funds, for "RECOUP" operations. - reserve_pub?: EddsaPublicKey; + // Date when the operation was made. + timestamp: Timestamp; + + // Signature by the coin. + coin_sig: EddsaSignature; - // Signature by the exchange, only present if ``type`` is "RECOUP", - // "OLD-COIN-RECOUP" or "RECOUP-REFRESH". Signature is - // of type TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP for "RECOUP", - // and of type TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH otherwise. - exchange_sig?: EddsaSignature; + // Hash of the public denomination key used to sign the coin. + // FIXME: why do we care to have this? + h_denom_pub: HashCode; + + // Coin blinding key. + coin_blind: DenominationBlindingKeyP; + + // Signature by the exchange + // of type TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH. + exchange_sig: EddsaSignature; // Public key used to sign ``exchange_sig``, - // only present if ``exchange_sig`` present. - exchange_pub?: EddsaPublicKey; + exchange_pub: EddsaPublicKey; - // Blinding factor of the revoked new coin, - // only present if ``type`` is "REFRESH_RECOUP". + // Blinding factor of the revoked new coin. new_coin_blinding_secret: RsaBlindingKeySecret; - // Blinded public key of the revoked new coin, - // only present if ``type`` is "REFRESH_RECOUP". + // Blinded public key of the revoked new coin. new_coin_ev: RsaBlindingKeySecret; } + .. ts:def:: CoinPursePaymentTransaction + + interface CoinPursePaymentTransaction { + type: "PURSE_PAYMENT"; + + // The total amount of the coin's value absorbed + // by this transaction. + // Note that this means the amount given includes + // the deposit fee. The current coin value can thus be computed by + // subtracting the amount from + // the coin's denomination value. + amount: Amount; + + // Deposit fee. + deposit_fee: Amount; + + // Public key of the purse. + purse_pub: EddsaPublicKey; + + // Date when the purse was set to expire. + purse_expiration: Timestamp; + + // Signature by the coin. + coin_sig: EddsaSignature; + + // Hash of the public denomination key used to sign the coin. + // FIXME: why do we care to have this? + h_denom_pub: HashCode; + + } + + + ---------- Refreshing ---------- @@ -2185,12 +2432,12 @@ Wallet-to-wallet transfers TODO for the spec: - * Update coin history replies to include purse actions: - - TALER_SIGNATURE_PURSE_PAYMENT! - * Update reserve history replies to include merge & kyc actions: - - TALER_SIGNATURE_ACCOUNT_MERGE - - TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST + * add reserve history requests (with fee!) + to reserve history (changes balance!) * specify new database schema at exchange (add SQL to DD13!) + - something for in-progress kyc vs. completed kyc? + => add kyc_date to reserves? + => or have separate KYC table instead of NULLs in reserves! * update wire transfer API to enable WAD IDs (and while we are at it, should probably also write extended version to allow _merchants_ to query for their inbound transfers, so spec @@ -2544,6 +2791,12 @@ Discussion: // Must be of purpose TALER_SIGNATURE_PURSE_MERGE. purse_sig: EddsaSignature; + // Minimum amount that must be credited to the reserve, that is + // the total value of the purse minus the deposit fees. + // If the deposit fees are lower, the contribution to the + // reserve can be higher! + minimum_amount_contributed: Amount; + // SHA-512 hash of the contact of the purse. h_contract_terms: HashCode; @@ -2677,10 +2930,10 @@ Discussion: // this is a ``Bad Request`` (HTTP status 400). payto_uri: string; - // EdDSA signature of the account affirming the request + // EdDSA signature of the reserve affirming the request // to create the account, must be of purpose // TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST - account_sig: EddsaPublicKey; + reserve_sig: EddsaPublicKey; } @@ -2787,9 +3040,9 @@ wallet-to-wallet payments. Only another exchange should access this endpoint. // Client-side timestamp of when the merge request was made. merge_timestamp: Timestamp; - // Signature created with the account's private key. + // Signature created with the reserve's private key. // Must be of purpose TALER_SIGNATURE_ACCOUNT_MERGE - account_sig: EddsaSignature; + reserve_sig: EddsaSignature; // Signature created with the purse's private key. // Must be of purpose TALER_SIGNATURE_PURSE_MERGE diff --git a/design-documents/013-peer-to-peer-payments.rst b/design-documents/013-peer-to-peer-payments.rst index 0d3a767c..3a998c78 100644 --- a/design-documents/013-peer-to-peer-payments.rst +++ b/design-documents/013-peer-to-peer-payments.rst @@ -19,7 +19,7 @@ This will be used for payments via e-mail and other messaging apps, as well as possibly for transfers via NFC/QR code between mobile phones. Invoice Flow User Experience ----------------------------------- +---------------------------- .. graphviz:: @@ -69,7 +69,7 @@ Invoice Flow User Experience } Donation Flow User Experience -------------------------------------- +----------------------------- .. graphviz:: @@ -618,6 +618,104 @@ Additional considerations Taler's "one-hop withdrawal loohole". +Exchange database schema changes +-------------------------------- + +We need to exchange the existing reserves table to include bits for KYC-needed +and KYC-passed. Also, we need to store the payto://-URI of the bank account. + +Finally, we may need to keep some link to the KYC data, even though the +exchange technically does not need it, but likely there might be regulatory +reasons to have that association for legal inquiries. (However, it would +also be possible to keep that link only in the external KYC service's +database.) + + + +.. sourcecode:: sql + + -- Everything in one big transaction + BEGIN; + -- Check patch versioning is in place. + SELECT _v.register_patch('exchange-TBD', NULL, NULL); + -- + ALTER TABLE reserves + ADD COLUMN kyc_needed BOOLEAN NOT NULL DEFAULT (false) + ADD COLUMN kyc_passed BOOLEAN NOT NULL DEFAULT (false) + ADD COLUMN payto_uri TEXT DEFAULT (NULL) + ADD COLUMN kyc_link TEXT DEFAULT (NULL); + COMMENT ON COLUMN reserves.kyc_needed + IS 'set to true once a reserve was merged with a purse'; + COMMENT ON COLUMN reserves.kyc_passed + IS 'set to true once the user performed the KYC check'; + COMMENT ON COLUMN reserves.payto_uri + IS 'bank account details to use in case reserve is closed'; + COMMENT ON COLUMN reserves.kyc_link + IS 'optional link to KYC data'; + -- + CREATE TABLE IF NOT EXISTS kyc_requests + (kyc_request_serial_id BIGSERIAL UNIQUE + ,reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE + ,kyc_date INT8 NOT NULL + ,kyc_fee_val INT8 NOT NULL + ,kyc_fee_frac INT4 NOT NULL + ,payto_uri TEXT NOT NULL + ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) + ,PRIMARY KEY (reserve_pub, kyc_date) + ); + CREATE TABLE IF NOT EXISTS mergers + (merge_request_serial_id BIGSERIAL UNIQUE + ,reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE + ,purse_url TEXT NOT NULL, + ,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32), + ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) + ,purse_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) + ,merge_timestamp INT8 NOT NULL + ,purse_expiration INT8 NOT NULL + ,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)) + ,h_wire BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)) + ,purse_val INT8 NOT NULL + ,purse_frac INT4 NOT NULL + ,PRIMARY KEY (purse_pub) + ); + CREATE TABLE IF NOT EXISTS contracts + (contract_serial_id BIGSERIAL UNIQUE + ,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32), + ,pub_ckey BYTEA NOT NULL CHECK (LENGTH(pub_ckey)=32)), + ,e_contract BYTEA NOT NULL, + ,PRIMARY KEY (purse_pub) + ); + CREATE TABLE IF NOT EXISTS history_requests + (reserve_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32), + ,request_timestamp INT8 NOT NULL + ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) + ,PRIMARY KEY (reserve_pub,request_timestamp) + ); + CREATE TABLE IF NOT EXISTS purse_deposits + (purse_deposit_serial_id BIGSERIAL UNIQUE + ,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32), + ,purse_expiration INT8 NOT NULL + ,coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE + ,amount_with_fee_val INT8 NOT NULL + ,amount_with_fee_frac INT4 NOT NULL + ,coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64) + ,PRIMARY KEY (purse_pub,coin_pub) + ); + CREATE TABLE IF NOT EXISTS wads + (wad_serial_id BIGSERIAL UNIQUE + ,reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE + ,kyc_date INT8 NOT NULL + ,kyc_fee_val INT8 NOT NULL + ,kyc_fee_frac INT4 NOT NULL + ,payto_uri TEXT NOT NULL + ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) + ,PRIMARY KEY (reserve_pub, kyc_date) + ); + -- Complete transaction + COMMIT; + + + Alternatives ============ -- cgit v1.2.3 From 4713a43afead8008efb81afeb0639dc98015c5a1 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 8 May 2021 01:07:39 +0200 Subject: more dd13 spec updates --- core/api-common.rst | 32 +++------- core/api-exchange.rst | 87 +++++++------------------- design-documents/013-peer-to-peer-payments.rst | 73 ++++++++++++++++----- 3 files changed, 93 insertions(+), 99 deletions(-) diff --git a/core/api-common.rst b/core/api-common.rst index 5c05db0c..bcb0570f 100644 --- a/core/api-common.rst +++ b/core/api-common.rst @@ -1036,7 +1036,7 @@ within the .. _TALER_ReserveStatusRequestSignaturePS: .. sourcecode:: c - struct TALER_PurseStatusRequestSignaturePS { + struct TALER_ReserveStatusRequestSignaturePS { /** * purpose.purpose = TALER_SIGNATURE_RESERVE_STATUS_REQUEST */ @@ -1047,7 +1047,7 @@ within the .. _TALER_ReserveHistoryRequestSignaturePS: .. sourcecode:: c - struct TALER_PurseStatusRequestSignaturePS { + struct TALER_ReserveHistoryRequestSignaturePS { /** * purpose.purpose = TALER_SIGNATURE_RESERVE_HISTORY_REQUEST */ @@ -1068,7 +1068,7 @@ within the }; -.. _TALER_PurseStatusSignaturePS: +.. _TALER_PurseStatusResponseSignaturePS: .. sourcecode:: c struct TALER_PurseStatusResponseSignaturePS { @@ -1112,12 +1112,12 @@ within the }; -.. _TALER_PursePaymentConfirmedSignaturePS: +.. _TALER_PurseDepositConfirmedSignaturePS: .. sourcecode:: c - struct TALER_PursePaymentConfirmedSignaturePS { + struct TALER_PurseDepositConfirmedSignaturePS { /** - * purpose.purpose = TALER_SIGNATURE_PURSE_PAYMENT_CONFIRMED + * purpose.purpose = TALER_SIGNATURE_PURSE_DEPOSIT_CONFIRMED */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; struct TALER_AmountNBO total_purse_amount; @@ -1162,20 +1162,6 @@ within the }; -.. _TALER_PursePaymentSignaturePS: -.. sourcecode:: c - - struct TALER_PursePaymentSignaturePS { - /** - * purpose.purpose = TALER_SIGNATURE_PURSE_PAYMENT - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - struct TALER_AmountNBO coin_contribution; - struct GNUNET_TIME_AbsoluteNBO purse_expiration; - struct TALER_PursePublicKey purse_pub; - }; - - .. _TALER_PurseMergeSuccessSignaturePS: .. sourcecode:: c @@ -1225,9 +1211,11 @@ within the struct TALER_WadDataSignaturePS { /** - * purpose.purpose = TALER_SIGNATURE_ACCOUNT_SETUP_SUCCESS + * purpose.purpose = TALER_SIGNATURE_WAD_DATA */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - struct GNUNET_HashCode wad_data; + struct GNUNET_TIME_AbsoluteNBO wad_execution_time; + struct TALER_AmountNBO total_amount; + struct GNUNET_HashCode h_items; struct TALER_WadId wad_id; }; diff --git a/core/api-exchange.rst b/core/api-exchange.rst index 27886c24..a2e32579 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -1046,9 +1046,9 @@ exchange. | ReserveClosingTransaction | ReserveRecoupTransaction; - .. ts:def:: AccountHistoryTransaction + .. ts:def:: ReserveHistoryTransaction - interface AccountHistoryTransaction { + interface ReserveHistoryTransaction { type: "HISTORY"; // Fee agreed to by the reserve owner. @@ -1562,7 +1562,7 @@ denomination. | CoinRecoupTransaction | CoinOldCoinRecoupTransaction | CoinRecoupRefreshTransaction - | CoinPursePaymentTransaction; + | CoinPurseDepositTransaction; .. ts:def:: CoinDepositTransaction @@ -1759,10 +1759,10 @@ denomination. new_coin_ev: RsaBlindingKeySecret; } - .. ts:def:: CoinPursePaymentTransaction + .. ts:def:: CoinPurseDepositTransaction - interface CoinPursePaymentTransaction { - type: "PURSE_PAYMENT"; + interface CoinPurseDepositTransaction { + type: "PURSE_DEPOSIT"; // The total amount of the coin's value absorbed // by this transaction. @@ -2432,12 +2432,11 @@ Wallet-to-wallet transfers TODO for the spec: - * add reserve history requests (with fee!) - to reserve history (changes balance!) - * specify new database schema at exchange (add SQL to DD13!) - - something for in-progress kyc vs. completed kyc? - => add kyc_date to reserves? - => or have separate KYC table instead of NULLs in reserves! + * update DD13 eDB SQL: tables for incoming wads! + * do we need some special entry in account/reserve + histories for incoming WAD-transfers vs. other merges, + or do we re-use the existing 'merge' entry and just + generate it from the incoming-wad table? * update wire transfer API to enable WAD IDs (and while we are at it, should probably also write extended version to allow _merchants_ to query for their inbound transfers, so spec @@ -2445,19 +2444,6 @@ TODO for the spec: tell exchange for inbound wire transfers that they are from a partner bank where KYC fees would be waived! -Discussion: - - * when the user POSTs to /kyc for a reserve with a payto URI - that differs from the URI that was used to establish the - reserve, do we 409 conflict or accept? - That seems like an attack vector: - Say I learn your account pub and wire you money from my - bank account, thus blocking you from /kyc'ing your account!) - * when the user POSTs to /kyc for an account with a payto URI - that differs from the URI that was previously used for a - /kyc for the same account, do we allow the KYC to proceed - and update the bank account? Is there an attack vector? - .. http:GET:: /purses/$PURSE_PUB @@ -2551,12 +2537,12 @@ Discussion: :http:statuscode:`200 OK`: The operation succeeded, the exchange confirms that all coins were deposited into the purse. - The response will include a `PursePaymentSuccess` object. + The response will include a `PurseDepositSuccess` object. :http:statuscode:`202 Accepted`: The payment was accepted, but insufficient to reach the specified purse balance. The client should make further purse deposits before the expiration deadline. - The response will include a `PursePaymentAccepted` object. + The response will include a `PurseDepositAccepted` object. :http:statuscode:`401 Unauthorized`: A coin signature is invalid. This response comes with a standard `ErrorDetail` response. @@ -2656,9 +2642,9 @@ Discussion: } - .. ts:def:: PursePaymentSuccess + .. ts:def:: PurseDepositSuccess - interface PursePaymentSuccess { + interface PurseDepositSuccess { // Total amount paid into the purse. total_purse_amount: Amount; @@ -2667,7 +2653,7 @@ Discussion: total_deposit_fees: Amount; // EdDSA signature of the exchange affirming the payment, - // of purpose TALER_SIGNATURE_PURSE_PAYMENT_CONFIRMED + // of purpose TALER_SIGNATURE_PURSE_DEPOSIT_CONFIRMED // Signs over the above and the purse public key and // the hash of the contract terms. exchange_sig: EddsaSignature; @@ -2677,9 +2663,9 @@ Discussion: } - .. ts:def:: PursePaymentAccepted + .. ts:def:: PurseDepositAccepted - interface PursePaymentAccepted { + interface PurseDepositAccepted { // Total amount paid so far into the purse, in this // and previous requests. @@ -2812,32 +2798,6 @@ Discussion: // purse with the payment. contract?: EncryptedContract; - // Array of payments made to pay for the creation of the - // purse. Can be empty, say if no payment is needed. - payments: CreatePurseDeposit[]; - - } - - .. ts:def:: CreatePurseDeposit { - - // Public key of the coin being used to pay for creating a purse. - coin_pub: EddsaPublicKey; - - // Amount to be deposited, can be a fraction of the - // coin's total value. - contribution: Amount; - - // Hash of denomination RSA key with which the coin is signed. - denom_pub_hash: HashCode; - - // Exchange's unblinded RSA signature of the coin. - ub_sig: RsaSignature; - - // Signature of purpose TALER_SIGNATURE_PURSE_PAYMENT. - // made by the customer with the - // `coin's private key `. - coin_sig: EddsaSignature; - } .. ts:def:: MergeSuccess @@ -2882,6 +2842,8 @@ Discussion: (from wire transfers or merges of purses) already have a sufficient balance to cover the KYC fee. The signature affirms that the KYC fee can and should be charged to the reserve. + The request always updates the payto URI associated with + the reserve, even if the KYC process fails or is not completed. **Request:** The request body must be a `AccountSetupRequest` object. @@ -2900,11 +2862,6 @@ Discussion: the required KYC checks to open the account. Afterwards, the request should be repeated. The response will be an `AccountKycRedirect` object. - :http:statuscode:`409 Conflict`: - The reserve or account was previously associated with a different - payto URI, and changing the associated bank account is not - permitted. FIXME: should we allow it? Should we use PATCH for this? - Or only conflict if this was an account and not a reserve!?? :http:statuscode:`504 Gateway Timeout`: The exchange did not receive a confirmation from the KYC service within the specified time period. Used when long-polling for the @@ -3002,6 +2959,10 @@ wallet-to-wallet payments. Only another exchange should access this endpoint. // Total transfer amount claimed by the exchange. total: Amount; + // Indicative time by which the wad was given to the + // bank to execute the wire transfer. + wad_execution_time: Timestamp; + // Transfers aggregated in the wad. items: WadItem[]; diff --git a/design-documents/013-peer-to-peer-payments.rst b/design-documents/013-peer-to-peer-payments.rst index 3a998c78..5e786203 100644 --- a/design-documents/013-peer-to-peer-payments.rst +++ b/design-documents/013-peer-to-peer-payments.rst @@ -655,29 +655,44 @@ database.) -- CREATE TABLE IF NOT EXISTS kyc_requests (kyc_request_serial_id BIGSERIAL UNIQUE - ,reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE + ,reserve_uuid INT8 NOT NULL REFERENCES reserves (reserve_uuid) ON DELETE CASCADE ,kyc_date INT8 NOT NULL + ,kyc_retry INT8 NOT NULL ,kyc_fee_val INT8 NOT NULL ,kyc_fee_frac INT4 NOT NULL - ,payto_uri TEXT NOT NULL ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) - ,PRIMARY KEY (reserve_pub, kyc_date) + ,kyc_id TEXT NOT NULL + ,PRIMARY KEY (reserve_uuid, kyc_date) ); + COMMENT ON COLUMN kyc_requests.reserve_uuid + IS 'Reserve for which the KYC request was triggered.'; + COMMENT ON COLUMN kyc_requests.reserve_sig + IS 'Signature affirming the KYC request'; + COMMENT ON COLUMN kyc_requests.kyc_fee_val + IS 'Amount paid by the reserve for the KYC process.'; + COMMENT ON COLUMN kyc_requests.kyc_date + IS 'When was the KYC process originally initiated.'; + COMMENT ON COLUMN kyc_requests.kyc_retry + IS 'Timestamp when we should next query the KYC backend for the KYC status. The maximum possible numeric value indicates that we do not need to ever check the status of this KYC process again.'; + COMMENT ON COLUMN kyc_requests.kyc_id + IS 'ID of the KYC process, used to compute the URL returned to the client as well as for the exchange to check if the KYC has completed. Format depends on the KYC process of the bank.'; + -- CREATE TABLE IF NOT EXISTS mergers (merge_request_serial_id BIGSERIAL UNIQUE - ,reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE + ,reserve_uuid BYTEA NOT NULL REFERENCES reserves (reserve_uuid) ON DELETE CASCADE ,purse_url TEXT NOT NULL, ,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32), ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) - ,purse_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) + ,purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)) ,merge_timestamp INT8 NOT NULL ,purse_expiration INT8 NOT NULL ,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)) - ,h_wire BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)) + ,h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)) ,purse_val INT8 NOT NULL ,purse_frac INT4 NOT NULL ,PRIMARY KEY (purse_pub) ); + -- CREATE TABLE IF NOT EXISTS contracts (contract_serial_id BIGSERIAL UNIQUE ,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32), @@ -685,12 +700,23 @@ database.) ,e_contract BYTEA NOT NULL, ,PRIMARY KEY (purse_pub) ); + -- CREATE TABLE IF NOT EXISTS history_requests - (reserve_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32), + (reserve_uuid INT8 NOT NULL REFERENCES reserves(reserve_uuid) ON DELETE CASCADE, ,request_timestamp INT8 NOT NULL ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) - ,PRIMARY KEY (reserve_pub,request_timestamp) + ,PRIMARY KEY (reserve_uuid,request_timestamp) ); + -- + CREATE TABLE IF NOT EXISTS close_requests + (reserve_uuid INT8 NOT NULL REFERENCES reserves(reserve_uuid) ON DELETE CASCADE, + ,close_timestamp INT8 NOT NULL + ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) + ,close_val INT8 NOT NULL + ,close_frac INT4 NOT NULL + ,PRIMARY KEY (reserve_uuid,close_timestamp) + ); + -- CREATE TABLE IF NOT EXISTS purse_deposits (purse_deposit_serial_id BIGSERIAL UNIQUE ,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32), @@ -701,16 +727,35 @@ database.) ,coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64) ,PRIMARY KEY (purse_pub,coin_pub) ); + -- CREATE TABLE IF NOT EXISTS wads (wad_serial_id BIGSERIAL UNIQUE - ,reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE - ,kyc_date INT8 NOT NULL - ,kyc_fee_val INT8 NOT NULL - ,kyc_fee_frac INT4 NOT NULL - ,payto_uri TEXT NOT NULL + ,wad_id BYTEA PRIMARY KEY CHECK (LENGTH(wad_id)=24) + ,exchange_url TEXT NOT NULL + ,amount_val INT8 NOT NULL + ,amount_frac INT4 NOT NULL + ,execution_time INT8 NOT NULL + ,UNIQUE (exchange_url, execution_time) + ); + -- + CREATE TABLE IF NOT EXISTS wad_entries + (wad_entry_serial_id BIGSERIAL UNIQUE + ,wad_serial_id INT8 REFERENCES wads (wad_serial_id) ON DELETE CASCADE + ,reserve_pub BYTEA NOT NULL CHECK(LENGTH(reserve_pub)=32) + ,purse_pub BYTEA PRIMARY KEY CHECK(LENGTH(purse_pub)=32) + ,h_contract BYTEA NOT NULL CHECK(LENGTH(h_contract)=64) + ,purse_expiration INT8 NOT NULL + ,merge_timestamp INT8 NOT NULL + ,amount_with_fee_val INT8 NOT NULL + ,amount_with_fee_frac INT4 NOT NULL + ,wad_fee_val INT8 NOT NULL + ,wad_fee_frac INT4 NOT NULL + ,deposit_fees_val INT8 NOT NULL + ,deposit_fees_frac INT4 NOT NULL ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) - ,PRIMARY KEY (reserve_pub, kyc_date) + ,purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)) ); + -- FIXME: need more tables for exchange RECEIVING a wad! -- Complete transaction COMMIT; -- cgit v1.2.3 From 4e406e059e1fb5f9f91e22e197e9fc9b076dbd9f Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 9 May 2021 13:41:43 +0200 Subject: document test vector for #6855 --- core/api-merchant.rst | 17 +++++++++++----- design-documents/018-contract-json.rst | 36 +++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/core/api-merchant.rst b/core/api-merchant.rst index c1de77e7..60840756 100644 --- a/core/api-merchant.rst +++ b/core/api-merchant.rst @@ -1541,12 +1541,19 @@ Creating orders The backend has successfully created the proposal. The response is a :ts:type:`PostOrderResponse`. :http:statuscode:`404 Not found`: - The order given used products from the inventory, but those were not found - in the inventory. Or the merchant instance is unknown (including possibly the instance being not configured for new orders). Details in the - error code. NOTE: no good way to find out which product is not in the - inventory, we MAY want to specify that in the reply. + Possible reasons are: + (1) The order given used products from the inventory, but those were + not found in the inventory. + (2) The merchant instance is unknown (including possibly the instance + being not configured for new orders). + (3) The wire method specified is not supported by the backend. + Details in the error code. + NOTE: currently the client has no good way to find out which product + is not in the inventory, we MAY want to specify that in the reply. :http:statuscode:`409 Conflict`: - A different proposal already exists under the specified order ID. + A different proposal already exists under the specified order ID, + or the requested currency is not supported by this backend. Details in + the error code. :http:statuscode:`410 Gone`: The order given used products from the inventory that are out of stock. The response is a :ts:type:`OutOfStockResponse`. diff --git a/design-documents/018-contract-json.rst b/design-documents/018-contract-json.rst index 4208febc..2162d6ed 100644 --- a/design-documents/018-contract-json.rst +++ b/design-documents/018-contract-json.rst @@ -102,7 +102,7 @@ member of the parent object. }, } - => + => { ...props, @@ -146,6 +146,40 @@ resulting bytes are terminated with a single 0-byte and then hashed with SHA512. +Test vector +----------- + +The follwing input contains top-level and nested forgettable +fields, as well as booleans, integers, strings and objects +as well as non-forgettable fields. It is thus suitable as +a minimal interoperability test: + +.. code-block:: json + + { + "k1": 1, + "_forgettable": { + "k1": "SALT" + }, + "k2": { + "n1": true, + "_forgettable": { + "n1": "salt" + } + }, + "k3": { + "n1": "string" + } + } + +Hashing the above contract results in the following Crockford base32 encoded +hash +``287VXK8T6PXKD05W8Y94QJNEFCMRXBC9S7KNKTWGH2G2J2D7RYKPSHNH1HG9NT1K2HRHGC67W6QM6GEC4BSN1DPNEBCS0AVDT2DBP5G''. + +Note that typically the salt values must be chosen at random, only for this test we use static salt values. + + + Discussion / Q&A ================ -- cgit v1.2.3 From 1cb61c4f8e0a1b02d9689d375c39ab4554880347 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 9 May 2021 14:34:50 +0200 Subject: add inbound wad tables --- core/api-exchange.rst | 5 - design-documents/013-peer-to-peer-payments.rst | 185 ++++++++++++++++++++++++- 2 files changed, 178 insertions(+), 12 deletions(-) diff --git a/core/api-exchange.rst b/core/api-exchange.rst index a2e32579..d3000259 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -2432,11 +2432,6 @@ Wallet-to-wallet transfers TODO for the spec: - * update DD13 eDB SQL: tables for incoming wads! - * do we need some special entry in account/reserve - histories for incoming WAD-transfers vs. other merges, - or do we re-use the existing 'merge' entry and just - generate it from the incoming-wad table? * update wire transfer API to enable WAD IDs (and while we are at it, should probably also write extended version to allow _merchants_ to query for their inbound transfers, so spec diff --git a/design-documents/013-peer-to-peer-payments.rst b/design-documents/013-peer-to-peer-payments.rst index 5e786203..eda407e6 100644 --- a/design-documents/013-peer-to-peer-payments.rst +++ b/design-documents/013-peer-to-peer-payments.rst @@ -631,6 +631,8 @@ also be possible to keep that link only in the external KYC service's database.) +TODO/FIXME: update the following SQL: add missing comments! + .. sourcecode:: sql @@ -664,6 +666,8 @@ database.) ,kyc_id TEXT NOT NULL ,PRIMARY KEY (reserve_uuid, kyc_date) ); + COMMENT ON TABLE kyc_requests + IS ''; COMMENT ON COLUMN kyc_requests.reserve_uuid IS 'Reserve for which the KYC request was triggered.'; COMMENT ON COLUMN kyc_requests.reserve_sig @@ -692,6 +696,32 @@ database.) ,purse_frac INT4 NOT NULL ,PRIMARY KEY (purse_pub) ); + COMMENT ON TABLE mergers + IS ''; + COMMENT ON COLUMN mergers.reserve_uuid + IS ''; + COMMENT ON COLUMN mergers.purse_url + IS ''; + COMMENT ON COLUMN mergers.purse_pub + IS ''; + COMMENT ON COLUMN mergers.reserve_sig + IS ''; + COMMENT ON COLUMN mergers.purse_sig + IS ''; + COMMENT ON COLUMN mergers.merge_timestamp + IS ''; + COMMENT ON COLUMN mergers.purse_expiration + IS ''; + COMMENT ON COLUMN mergers.h_contract_terms + IS ''; + COMMENT ON COLUMN mergers.h_wire + IS ''; + COMMENT ON COLUMN mergers.purse_val + IS ''; + CREATE INDEX IF NOT EXISTS mergers_reserve_uuid + ON mergers (reserve_uuid); + COMMENT ON INDEX mergers_reserve_uuid + IS ''; -- CREATE TABLE IF NOT EXISTS contracts (contract_serial_id BIGSERIAL UNIQUE @@ -700,13 +730,31 @@ database.) ,e_contract BYTEA NOT NULL, ,PRIMARY KEY (purse_pub) ); + COMMENT ON TABLE contracts + IS ''; + COMMENT ON COLUMN contracts.purse_pub + IS ''; + COMMENT ON COLUMN contracts.pub_ckey + IS ''; + COMMENT ON COLUMN contracts.e_contract + IS ''; -- CREATE TABLE IF NOT EXISTS history_requests (reserve_uuid INT8 NOT NULL REFERENCES reserves(reserve_uuid) ON DELETE CASCADE, ,request_timestamp INT8 NOT NULL ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) + ,history_fee_val INT8 NOT NULL + ,history_fee_frac INT4 NOT NULL ,PRIMARY KEY (reserve_uuid,request_timestamp) ); + COMMENT ON TABLE history_requests + IS ''; + COMMENT ON COLUMN history_requests.request_timestamp + IS ''; + COMMENT ON COLUMN history_requests.reserve_sig + IS ''; + COMMENT ON COLUMN history_requests.history_fee_val + IS ''; -- CREATE TABLE IF NOT EXISTS close_requests (reserve_uuid INT8 NOT NULL REFERENCES reserves(reserve_uuid) ON DELETE CASCADE, @@ -716,6 +764,14 @@ database.) ,close_frac INT4 NOT NULL ,PRIMARY KEY (reserve_uuid,close_timestamp) ); + COMMENT ON TABLE close_requests + IS ''; + COMMENT ON COLUMN close_requests.close_timestamp + IS ''; + COMMENT ON COLUMN close_requests.reserve_sig + IS ''; + COMMENT ON COLUMN close_requests.close_val + IS ''; -- CREATE TABLE IF NOT EXISTS purse_deposits (purse_deposit_serial_id BIGSERIAL UNIQUE @@ -727,20 +783,104 @@ database.) ,coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64) ,PRIMARY KEY (purse_pub,coin_pub) ); + COMMENT ON TABLE purse_deposits + IS ''; + COMMENT ON COLUMN purse_deposits.purse_pub + IS ''; + COMMENT ON COLUMN purse_deposits.purse_expiration + IS ''; + COMMENT ON COLUMN purse_deposits.coin_pub + IS ''; + COMMENT ON COLUMN purse_deposits.amount_with_fee_val + IS ''; + COMMENT ON COLUMN purse_deposits.coin_sig + IS ''; -- - CREATE TABLE IF NOT EXISTS wads - (wad_serial_id BIGSERIAL UNIQUE + CREATE TABLE IF NOT EXISTS wads_out + (wad_out_serial_id BIGSERIAL UNIQUE ,wad_id BYTEA PRIMARY KEY CHECK (LENGTH(wad_id)=24) - ,exchange_url TEXT NOT NULL + ,target_exchange_url TEXT NOT NULL ,amount_val INT8 NOT NULL ,amount_frac INT4 NOT NULL ,execution_time INT8 NOT NULL ,UNIQUE (exchange_url, execution_time) ); + COMMENT ON TABLE wads_out + IS ''; + COMMENT ON COLUMN wads_out.wad_id + IS ''; + COMMENT ON COLUMN wads_out.target_exchange_url + IS ''; + COMMENT ON COLUMN wads_out.amount_val + IS ''; + COMMENT ON COLUMN wads_out.execution_time + IS ''; + -- + CREATE TABLE IF NOT EXISTS wad_out_entries + (wad_out_entry_serial_id BIGSERIAL UNIQUE + ,wad_out_serial_id INT8 REFERENCES wads_out (wad_out_serial_id) ON DELETE CASCADE + ,reserve_pub BYTEA NOT NULL CHECK(LENGTH(reserve_pub)=32) + ,purse_pub BYTEA PRIMARY KEY CHECK(LENGTH(purse_pub)=32) + ,h_contract BYTEA NOT NULL CHECK(LENGTH(h_contract)=64) + ,purse_expiration INT8 NOT NULL + ,merge_timestamp INT8 NOT NULL + ,amount_with_fee_val INT8 NOT NULL + ,amount_with_fee_frac INT4 NOT NULL + ,wad_fee_val INT8 NOT NULL + ,wad_fee_frac INT4 NOT NULL + ,deposit_fees_val INT8 NOT NULL + ,deposit_fees_frac INT4 NOT NULL + ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) + ,purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)) + ); + COMMENT ON TABLE wad_out_entries + IS ''; + COMMENT ON COLUMN wad_out_entries.wad_out_serial_id + IS ''; + COMMENT ON COLUMN wad_out_entries.reserve_pub + IS ''; + COMMENT ON COLUMN wad_out_entries.purse_pub + IS ''; + COMMENT ON COLUMN wad_out_entries.h_contract + IS ''; + COMMENT ON COLUMN wad_out_entries.purse_expiration + IS ''; + COMMENT ON COLUMN wad_out_entries.merge_timestamp + IS ''; + COMMENT ON COLUMN wad_out_entries.amount_with_fee_val + IS ''; + COMMENT ON COLUMN wad_out_entries.wad_fee_val + IS ''; + COMMENT ON COLUMN wad_out_entries.deposit_fees_val + IS ''; + COMMENT ON COLUMN wad_out_entries.reserve_sig + IS ''; + COMMENT ON COLUMN wad_out_entries.purse_sig + IS ''; + -- + CREATE TABLE IF NOT EXISTS wads_in + (wad_in_serial_id BIGSERIAL UNIQUE + ,wad_id BYTEA PRIMARY KEY CHECK (LENGTH(wad_id)=24) + ,origin_exchange_url TEXT NOT NULL + ,amount_val INT8 NOT NULL + ,amount_frac INT4 NOT NULL + ,arrival_time INT8 NOT NULL + ,UNIQUE (wad_id, origin_exchange_url) + ); + COMMENT ON TABLE wads_in_entries + IS ''; + COMMENT ON COLUMN wads_in.wad_id + IS ''; + COMMENT ON COLUMN wads_in.origin_exchange_url + IS ''; + COMMENT ON COLUMN wads_in.amount_val + IS ''; + COMMENT ON COLUMN wads_in.arrival_time + IS ''; -- - CREATE TABLE IF NOT EXISTS wad_entries - (wad_entry_serial_id BIGSERIAL UNIQUE - ,wad_serial_id INT8 REFERENCES wads (wad_serial_id) ON DELETE CASCADE + CREATE TABLE IF NOT EXISTS wad_in_entries + (wad_in_entry_serial_id BIGSERIAL UNIQUE + ,wad_in_serial_id INT8 REFERENCES wads_in (wad_serial_id) ON DELETE CASCADE ,reserve_pub BYTEA NOT NULL CHECK(LENGTH(reserve_pub)=32) ,purse_pub BYTEA PRIMARY KEY CHECK(LENGTH(purse_pub)=32) ,h_contract BYTEA NOT NULL CHECK(LENGTH(h_contract)=64) @@ -755,7 +895,38 @@ database.) ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) ,purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)) ); - -- FIXME: need more tables for exchange RECEIVING a wad! + COMMENT ON TABLE wad_in_entries + IS ''; + COMMENT ON COLUMN wad_in_entries.wad_in_serial_id + IS ''; + COMMENT ON COLUMN wad_in_entries.reserve_pub + IS ''; + COMMENT ON COLUMN wad_in_entries.purse_pub + IS ''; + COMMENT ON COLUMN wad_in_entries.h_contract + IS ''; + COMMENT ON COLUMN wad_in_entries.purse_expiration + IS ''; + COMMENT ON COLUMN wad_in_entries.merge_timestamp + IS ''; + COMMENT ON COLUMN wad_in_entries.amount_with_fee_val + IS ''; + COMMENT ON COLUMN wad_in_entries.wad_fee_val + IS ''; + COMMENT ON COLUMN wad_in_entries.deposit_fees_val + IS ''; + COMMENT ON COLUMN wad_in_entries.reserve_sig + IS ''; + COMMENT ON COLUMN wad_in_entries.purse_sig + IS ''; + CREATE INDEX IF NOT EXISTS wad_in_entries_wad_in_serial + ON wad_in_entries (wad_in_serial_id); + CREATE INDEX IF NOT EXISTS wad_in_entries_reserve_pub + ON wad_in_entries (reserve_pub); + COMMENT ON INDEX wad_in_entries_wad_in_serial + IS ''; + COMMENT ON INDEX wad_in_entries_reserve_pub + IS ''; -- Complete transaction COMMIT; -- cgit v1.2.3 From 3579cf06efb3dfe242179136e62abbb5ae51ce6a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 9 May 2021 19:52:42 +0200 Subject: define merchant-bank facade --- core/api-bank-merchant.rst | 122 +++++++++++++++++++++++++++++++++++++++++++++ core/index.rst | 5 +- 2 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 core/api-bank-merchant.rst diff --git a/core/api-bank-merchant.rst b/core/api-bank-merchant.rst new file mode 100644 index 00000000..790d3f74 --- /dev/null +++ b/core/api-bank-merchant.rst @@ -0,0 +1,122 @@ +.. + This file is part of GNU TALER. + Copyright (C) 2021 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see + +============================ +Taler Bank Merchant HTTP API +============================ + +This section describes an API offered by the Taler wire gateway. The API is +used by the merchant to query for incoming transactions. + +This API is TO BE implemented by the Taler Demo Bank, as well as by +LibEuFin (work in progress). + + +-------------- +Authentication +-------------- + +The bank library authenticates requests to the bank merchant API using +`HTTP basic auth `_. + +-------------------------------- +Querying the transaction history +-------------------------------- + + +.. http:get:: ${BASE_URL}/history + + Return a list of transactions made from an exchange to the merchant. + + Incoming transactions must contain a valid wire transfer identifier and + exchange base URL. If a bank transaction does not conform to the right + syntax, the wire gateway must not report it to the merchant via this + endpoint. + + The bank account of the merchant is determined via the base URL and/or the + user name in the ``Authorization`` header. In fact, the transaction history + might come from a "virtual" account, where multiple real bank accounts are + merged into one history. + + Transactions are identified by an opaque numeric identifier, referred to here + as *row ID*. The semantics of the row ID (including its sorting order) are + determined by the bank server and completely opaque to the client. + + The list of returned transactions is determined by a row ID *starting point* + and a signed non-zero integer *delta*: + + * If *delta* is positive, return a list of up to *delta* transactions (all matching + the filter criteria) strictly **after** the starting point. The transactions are sorted + in **ascending** order of the row ID. + * If *delta* is negative, return a list of up to *-delta* transactions (all matching + the filter criteria) strictly **before** the starting point. The transactions are sorted + in **descending** order of the row ID. + + If *starting point* is not explicitly given, it defaults to: + + * A value that is **smaller** than all other row IDs if *delta* is **positive**. + * A value that is **larger** than all other row IDs if *delta* is **negative**. + + **Request** + + :query start: *Optional.* + Row identifier to explicitly set the *starting point* of the query. + :query delta: + The *delta* value that determines the range of the query. + :query long_poll_ms: *Optional.* If this parameter is specified and the + result of the query would be empty, the bank will wait up to ``long_poll_ms`` + milliseconds for new transactions that match the query to arrive and only + then send the HTTP response. A client must never rely on this behavior, as + the bank may return a response immediately or after waiting only a fraction + of ``long_poll_ms``. + + **Response** + + :http:statuscode:`200 OK`: JSON object of type `MerchantIncomingHistory`. + :http:statuscode:`400 Bad request`: Request malformed. The bank replies with an `ErrorDetail` object. + :http:statuscode:`401 Unauthorized`: Authentication failed, likely the credentials are wrong. + :http:statuscode:`404 Not found`: The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object. + + .. ts:def:: MerchantIncomingHistory + + interface MerchantIncomingHistory { + + // Array of incoming transactions. + incoming_transactions : MerchantIncomingBankTransaction[]; + + } + + .. ts:def:: MerchantIncomingBankTransaction + + interface MerchantIncomingBankTransaction { + + // Opaque identifier of the returned record. + row_id: SafeUint64; + + // Date of the transaction. + date: Timestamp; + + // Amount transferred. + amount: Amount; + + // Payto URI to identify the sender of funds. + debit_account: string; + + // Base URL of the exchange where the transfer originated form. + exchange_url: string; + + // The wire transfer identifier. + wtid: WireTransferIdentifierRawP; + } diff --git a/core/index.rst b/core/index.rst index 95037d48..7ead2193 100644 --- a/core/index.rst +++ b/core/index.rst @@ -32,12 +32,13 @@ interfaces between the core components of Taler. api-common api-error api-exchange - api-wire api-merchant api-auditor + api-sync + api-wire + api-bank-merchant api-bank-integration api-bank-access wireformats - api-sync taler-uri errors -- cgit v1.2.3 From 7641ad6d2ab742e1b8305b9db59bcc24f317e77a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 9 May 2021 20:01:02 +0200 Subject: extend wire gateway spec for wads --- core/api-exchange.rst | 16 ---------------- core/api-wire.rst | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/core/api-exchange.rst b/core/api-exchange.rst index d3000259..3f80d4d7 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -2419,28 +2419,12 @@ Refunds } - - - - - .. _exchange_w2w: -------------------------- Wallet-to-wallet transfers -------------------------- -TODO for the spec: - - * update wire transfer API to enable WAD IDs (and while we are - at it, should probably also write extended version to allow - _merchants_ to query for their inbound transfers, so spec - for both WADs and regular WTID!); also add flag to - tell exchange for inbound wire transfers that they are - from a partner bank where KYC fees would be waived! - - - .. http:GET:: /purses/$PURSE_PUB Obtain information about a purse. The request header must diff --git a/core/api-wire.rst b/core/api-wire.rst index fbf27469..e05aa848 100644 --- a/core/api-wire.rst +++ b/core/api-wire.rst @@ -175,7 +175,15 @@ Querying the transaction history .. ts:def:: IncomingBankTransaction - interface IncomingBankTransaction { + // Union discriminated by the "type" field. + type IncomingBankTransaction = + | IncomingReserveTransaction + | IncomingWadTransaction; + + .. ts:def:: IncomingReserveTransaction + + interface IncomingReserveTransaction { + type: "RESERVE"; // Opaque identifier of the returned record. row_id: SafeUint64; @@ -195,6 +203,47 @@ Querying the transaction history // The reserve public key extracted from the transaction details. reserve_pub: EddsaPublicKey; + + // Set to 'true' if this wire transfer originates from + // a bank account with a partner bank that implies that + // the KYC has already been satisfied for this reserve + // because of it. + // FIXME: is this acceptable? Seems like users could + // easily be tricked into wiring funds to a reserve that + // they do NOT own, and then being held responsibe for + // the resulting account. So when setting this bit, + // we'd need at least a reserve_sig over the credit_account, + // which we do not get here... (DANGEROUS API). + // => need to discuss KYC more with real banks. + kyc_completed: boolean; + } + + .. ts:def:: IncomingWadTransaction + + interface IncomingWadTransaction { + type: "WAD"; + + // Opaque identifier of the returned record. + row_id: SafeUint64; + + // Date of the transaction. + date: Timestamp; + + // Amount transferred. + amount: Amount; + + // Payto URI to identify the receiver of funds. + // This must be one of the exchange's bank accounts. + credit_account: string; + + // Payto URI to identify the sender of funds. + debit_account: string; + + // Base URL of the exchange that originated the wad. + origin_exchange_url: string; + + // The reserve public key extracted from the transaction details. + wad_id: WadId; } -- cgit v1.2.3 From 9e4bbd6735d350f7b55bb30ee418309db01c8fa9 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 13 May 2021 19:03:49 +0200 Subject: clarifications --- core/api-merchant.rst | 3 +++ design-documents/013-peer-to-peer-payments.rst | 18 ++++++------------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/core/api-merchant.rst b/core/api-merchant.rst index 60840756..21956e5d 100644 --- a/core/api-merchant.rst +++ b/core/api-merchant.rst @@ -1970,6 +1970,9 @@ the contract!) to minimize risks from information leakage. :http:statuscode:`200 OK`: The merchant deleted the specified fields from the contract of order $ORDER_ID. + :http:statuscode:`204 No content`: + The merchant had already deleted the specified fields + from the contract of order $ORDER_ID. :http:statuscode:`400 Bad request`: The request is malformed or one of the paths is invalid. :http:statuscode:`404 Not found`: diff --git a/design-documents/013-peer-to-peer-payments.rst b/design-documents/013-peer-to-peer-payments.rst index eda407e6..a938b5a1 100644 --- a/design-documents/013-peer-to-peer-payments.rst +++ b/design-documents/013-peer-to-peer-payments.rst @@ -24,23 +24,20 @@ Invoice Flow User Experience .. graphviz:: digraph invoice { - settings [ - label = "Invoice flow"; - ]; ranksep="0.5" { rank = same; "inbox"; "begin"; } { rank = same; "sending"; "receiving2"; } { rank = same; "receiving"; "paying"; } { rank = same; "mid"; "midbox"; } { rank = same; "body"; "amount"; } - begin [label="Seller Inbox",shape=box]; + begin [label="Payer Inbox",shape=box]; body [label="compose\nE-mail message"]; amount [label="specify\ninvoice details"]; receiving [label="receiving...",shape=diamond]; sending [label="transmitting...",shape=diamond]; - mid [label="Seller Inbox",shape=box]; + mid [label="Payee Inbox",shape=box]; notified [label="Notification:\npayment received"]; - end [label="Seller Inbox",shape=box]; + end [label="Payee Inbox",shape=box]; begin -> body [label="(1) new"]; body -> amount [label="(2) attach invoice"]; amount -> body [label="(3) Ok"]; @@ -49,14 +46,14 @@ Invoice Flow User Experience mid -> receiving [style=dashed]; receiving -> notified [style=dashed]; notified -> end [label="(9) Acknowledge"]; - inbox [label="Buyer Inbox",shape=box]; + inbox [label="Payer Inbox",shape=box]; receiving2 [label="receiving...",shape=diamond]; - midbox [label="Buyer Inbox",shape=box]; + midbox [label="Payer Inbox",shape=box]; open [label="message with\nattached invoice"]; confirm [label="review invoice"]; paying [label="paying...", shape=diamond]; paid [label="message with\npaid invoice"]; - finbox [label="Buyer Inbox",shape=box]; + finbox [label="Payer Inbox",shape=box]; inbox -> receiving2 [style=dashed]; receiving2 -> sending [label="Internet\n(pEp)",style=dashed,dir=back]; receiving2 -> midbox [style=dashed]; @@ -75,9 +72,6 @@ Donation Flow User Experience digraph donation { ranksep="0.5" - settings [ - label = "Donation flow"; - ]; { rank = same; "inbox"; "begin"; } { rank = same; "sending"; "receiving2"; } { rank = same; "body"; "amount"; } -- cgit v1.2.3 From b944ce40628194e9f77d769fab8c6004446c9e21 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 15 May 2021 13:29:53 +0200 Subject: misc spec fixes --- core/api-common.rst | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++ core/api-exchange.rst | 101 ++++++++++++++++++++-------------- 2 files changed, 208 insertions(+), 41 deletions(-) diff --git a/core/api-common.rst b/core/api-common.rst index bcb0570f..81b85ab9 100644 --- a/core/api-common.rst +++ b/core/api-common.rst @@ -500,6 +500,13 @@ uses 512-bit hash codes (64 bytes). uint8_t hash[64]; // usually SHA-512 }; +.. _TALER_EcdhEphemeralPublicKeyP: +.. sourcecode:: c + + struct TALER_EcdhEphemeralPublicKeyP { + uint8_t ecdh_pub[32]; + }; + .. _reserve-pub: .. sourcecode:: c @@ -760,6 +767,9 @@ within the struct TALER_ExchangePublicKeyP signkey_pub; }; +.. _TALER_ExchangeKeySetPS: +.. sourcecode:: c + struct TALER_ExchangeKeySetPS { /** * purpose.purpose = TALER_SIGNATURE_EXCHANGE_KEY_SET @@ -1219,3 +1229,141 @@ within the struct GNUNET_HashCode h_items; struct TALER_WadId wad_id; }; + +.. _TALER_WadPartnerSignaturePS: +.. sourcecode:: c + + struct TALER_WadPartnerSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_MASTER_PARTNER_DETAILS + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_HashCode h_partner_base_url; + struct TALER_MasterPublicKeyP master_public_key; + struct GNUNET_TIME_AbsoluteNBO start_date; + struct GNUNET_TIME_AbsoluteNBO end_date; + struct TALER_AmountNBO wad_fee; + struct GNUNET_TIME_RelativeNBO wad_frequency; + }; + + +.. _TALER_P2PFeesPS: +.. sourcecode:: c + + struct TALER_P2PFeesPS { + /** + * purpose.purpose = TALER_SIGNATURE_P2P_FEES + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_TIME_AbsoluteNBO start_date; + struct GNUNET_TIME_AbsoluteNBO end_date; + struct TALER_AmountNBO kyc_fee; + struct TALER_AmountNBO purse_fee; + struct TALER_AmountNBO account_history_fee; + struct TALER_AmountNBO account_annual_fee; + struct GNUNET_TIME_RelativeNBO account_kyc_timeout; + struct GNUNET_TIME_RelativeNBO purse_timeout; + uint32_t purse_account_limit; + }; + + +.. _TALER_DenominationKeyAnnouncementPS: +.. sourcecode:: c + + struct TALER_DenominationKeyAnnouncementPS { + /** + * purpose.purpose = TALER_SIGNATURE_SM_DENOMINATION_KEY + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_HashCode h_denom_pub; + struct GNUNET_HashCode h_section_name; + struct GNUNET_TIME_AbsoluteNBO anchor_time; + struct GNUNET_TIME_RelativeNBO duration_withdraw; + }; + + +.. _TALER_SigningKeyAnnouncementPS: +.. sourcecode:: c + + struct TALER_SigningKeyAnnouncementPS { + /** + * purpose.purpose = TALER_SIGNATURE_SM_SIGNING_KEY . + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_ExchangePublicKeyP exchange_pub; + struct GNUNET_TIME_AbsoluteNBO anchor_time; + struct GNUNET_TIME_RelativeNBO duration; + }; + +.. _TALER_MasterDenominationKeyRevocationPS: +.. sourcecode:: c + + struct TALER_MasterDenominationKeyRevocationPS { + /** + * purpose.purpose = TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_HashCode h_denom_pub; + }; + + +.. _TALER_MasterSigningKeyRevocationPS: +.. sourcecode:: c + + struct TALER_MasterSigningKeyRevocationPS { + /** + * purpose.purpose = TALER_SIGNATURE_MASTER_SIGNING_KEY_REVOKED. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_ExchangePublicKeyP exchange_pub; + }; + + +.. _TALER_MasterAddAuditorPS: +.. sourcecode:: c + + struct TALER_MasterAddAuditorPS { + /** + * purpose.purpose = TALER_SIGNATURE_MASTER_ADD_AUDITOR + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_TIME_AbsoluteNBO start_date; + struct TALER_AuditorPublicKeyP auditor_pub; + struct GNUNET_HashCode h_auditor_url; + }; + +.. _TALER_MasterDelAuditorPS: +.. sourcecode:: c + + struct TALER_MasterDelAuditorPS { + /** + * purpose.purpose = TALER_SIGNATURE_MASTER_DEL_AUDITOR + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_TIME_AbsoluteNBO end_date; + struct TALER_AuditorPublicKeyP auditor_pub; + }; + +.. _TALER_MasterAddWirePS: +.. sourcecode:: c + + struct TALER_MasterAddWirePS { + /** + * purpose.purpose = TALER_SIGNATURE_MASTER_ADD_WIRE. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_TIME_AbsoluteNBO start_date; + struct GNUNET_HashCode h_wire GNUNET_PACKED; + }; + +.. _TALER_MasterDelWirePS: +.. sourcecode:: c + + struct TALER_MasterDelWirePS { + /** + * purpose.purpose = TALER_SIGNATURE_MASTER_DEL_WIRE. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_TIME_AbsoluteNBO end_date; + struct GNUNET_HashCode h_wire GNUNET_PACKED; + }; diff --git a/core/api-exchange.rst b/core/api-exchange.rst index 3f80d4d7..44dfd2a5 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -140,7 +140,7 @@ possibly by using HTTPS. // Fees relevant for wallet-to-wallet (or peer-to-peer) payments. // If no fees are provided for a given time range, then the // exchange simply does not support purses/p2p-payments at that time. - p2p_fees: P2Pfees[]; + p2p_fees: P2PFees[]; // The date when the denomination keys were last updated. list_issue_date: Timestamp; @@ -160,6 +160,7 @@ possibly by using HTTPS. // is sabotaging end-user anonymity by giving disjoint denomination keys to // different users. If an exchange were to do this, this signature allows the // clients to demonstrate to the public that the exchange is dishonest. + // Signature of `TALER_ExchangeKeySetPS` eddsa_sig: EddsaSignature; // Public EdDSA key of the exchange that was used to generate the signature. @@ -197,10 +198,9 @@ possibly by using HTTPS. // regulatory reasons.) account_annual_fee: Amount; - // How long will the exchange preserve the account - // history? After an account was deleted/closed, the - // exchange will retain the account history for - // legal reasons until this time. + // How long will the exchange preserve the account history? + // After an account was deleted/closed, the exchange will + // retain the account history for legal reasons until this time. legal_history_retention: RelativeTime; // How long does the exchange promise to keep funds @@ -217,13 +217,12 @@ possibly by using HTTPS. // Non-negative number of concurrent purses that any // account holder is allowed to create without having // to pay the purse_fee. - purse_account_limit: integer; + purse_account_limit: Integer; - // How long does an exchange keep a purse around - // after a purse has expired (or been successfully - // merged)? A 'GET' request for a purse will - // succeed until the purse expiration time plus this - // value. + // How long does an exchange keep a purse around after a purse + // has expired (or been successfully merged)? A 'GET' request + // for a purse will succeed until the purse expiration time + // plus this value. purse_timeout: RelativeTime; // Signature of `TALER_P2PFeesPS`. @@ -308,6 +307,7 @@ possibly by using HTTPS. stamp_end: Timestamp; // Signature over ``key`` and ``stamp_expire`` by the exchange master key. + // Signature of `TALER_ExchangeSigningKeyValidityPS`. // Must have purpose ``TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY``. master_sig: EddsaSignature; } @@ -453,7 +453,8 @@ possibly by using HTTPS. // How long is this partnership expected to last? end_date: Timestamp; - // Signature using the exchange's offline key + // Signature using the exchange's offline key over + // `TALER_WadPartnerSignaturePS` // with purpose ``TALER_SIGNATURE_MASTER_PARTNER_DETAILS``. master_sig: EddsaSignature; } @@ -542,7 +543,10 @@ Management operations authorized by master key // Fee charged by the exchange for refunding a coin of this denomination. fee_refund: Amount; - // Signature over this denomination by the denomination security module. + // Signature by the denomination security module + // over `TALER_DenominationKeyAnnouncementPS` + // for this denomination with purpose + // ``TALER_SIGNATURE_SM_DENOMINATION_KEY``. denom_secmod_sig: EddsaSignature; } @@ -564,7 +568,9 @@ Management operations authorized by master key // henceforth no longer be considered valid in legal disputes. stamp_end: Timestamp; - // Signature over this signing key by the signkey security module. + // Signature over `TALER_SigningKeyAnnouncementPS` + // for this signing key by the signkey security + // module using purpose ``TALER_SIGNATURE_SM_SIGNING_KEY``. signkey_secmod_sig: EddsaSignature; } @@ -607,7 +613,8 @@ Management operations authorized by master key // Hash of the public (RSA) key of the denomination. h_denom_pub: HashCode; - // Signature of `TALER_DenominationKeyValidityPS`. + // Signature over `TALER_DenominationKeyValidityPS`. + // Must have purpose ``TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY`` master_sig: EddsaSignature; } @@ -618,7 +625,8 @@ Management operations authorized by master key // The actual exchange's EdDSA signing public key. key: EddsaPublicKey; - // Signature by the exchange master key. + // Signature by the exchange master key over + // `TALER_ExchangeSigningKeyValidityPS`. // Must have purpose ``TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY``. master_sig: EddsaSignature; @@ -646,7 +654,8 @@ Management operations authorized by master key interface DenomRevocationSignature { - // Signature by the exchange master key. + // Signature by the exchange master key over a + // `TALER_MasterDenominationKeyRevocationPS`. // Must have purpose ``TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED``. master_sig: EddsaSignature; @@ -673,7 +682,8 @@ Management operations authorized by master key interface SignkeyRevocationSignature { - // Signature by the exchange master key. + // Signature by the exchange master key over a + // `TALER_MasterSigningKeyRevocationPS`. // Must have purpose ``TALER_SIGNATURE_MASTER_SIGN_KEY_REVOKED``. master_sig: EddsaSignature; @@ -712,8 +722,9 @@ Management operations authorized by master key // The auditor's EdDSA signing public key. auditor_pub: EddsaPublicKey; - // Signature by the exchange master key. - // Must have purpose ``TALER_SIGNATURE_MASTER_AUDITOR_ADD``. + // Signature by the exchange master ke yover a + // `TALER_MasterAddAuditorPS`. + // Must have purpose ``TALER_SIGNATURE_MASTER_ADD_AUDITOR``. master_sig: EddsaSignature; // When does the auditor become active? @@ -756,7 +767,8 @@ Management operations authorized by master key interface AuditorTeardownMessage { - // Signature by the exchange master key. + // Signature by the exchange master key over a + // `TALER_MasterDelAuditorPS`. // Must have purpose ``TALER_SIGNATURE_MASTER_AUDITOR_DEL``. master_sig: EddsaSignature; @@ -849,7 +861,8 @@ Management operations authorized by master key // with purpose ``TALER_SIGNATURE_MASTER_WIRE_DETAILS``. master_sig_wire: EddsaSignature; - // Signature using the exchange's offline key + // Signature using the exchange's offline key over a + // `TALER_MasterAddWirePS` // with purpose ``TALER_SIGNATURE_MASTER_WIRE_ADD``. master_sig_add: EddsaSignature; @@ -896,7 +909,8 @@ Management operations authorized by master key // ``payto://`` URL identifying the account and wire method payto_uri: string; - // Signature using the exchange's offline key + // Signature using the exchange's offline key over a + // `TALER_MasterDelWirePS`. // with purpose ``TALER_SIGNATURE_MASTER_WIRE_DEL``. master_sig_del: EddsaSignature; @@ -1063,7 +1077,7 @@ exchange. } - .. ts:def:: AccountSetupTransaction + .. ts:def:: AccountSetupTransaction interface AccountSetupTransaction { type: "SETUP"; @@ -1374,7 +1388,7 @@ exchange. The account is unknown to the exchange. :http:statuscode:`409 Conflict`: The account is still has digital cash in it, the associated - wire method is ``void'' and the *force* option was not provided. + wire method is ``void`` and the *force* option was not provided. This response comes with a standard `ErrorDetail` response. **Details:** @@ -2477,7 +2491,7 @@ Wallet-to-wallet transfers purse_expiration: Timestamp; // Indicative time at which the exchange is answering the - // status request. Used as part of `exchange_sig`. + // status request. Used as part of 'exchange_sig'. status_timestamp: Timestamp; // Maximum deposit fees that can be charged under the contract. @@ -2489,13 +2503,13 @@ Wallet-to-wallet transfers // EdDSA signature of the exchange affirming the purse status. exchange_sig: EddsaSignature; - // EdDSA public key exchange used for exchange_sig. + // EdDSA public key exchange used for 'exchange_sig'. exchange_pub: EddsaPublicKey; // AES-GCM Encrypted contract terms using encryption // key derived from DH of 'contract_pub' and the 'purse_pub'. // Optional, may be omitted if not desired by the client. - e_contract_terms?: byte[]; + e_contract_terms?: string; // If a merge request was received, information about the // merge request. Omitted if the purse has not yet received @@ -2583,23 +2597,27 @@ Wallet-to-wallet transfers deposits: PurseDeposit[]; } - .. ts:def:: EncryptedContract { + .. ts:def:: EncryptedContract + + interface EncryptedContract { // ECDH contract_public key used to encrypt the contract. // Optional as the contract terms may already be known // to the exchange or the other wallet from a different // interaction. - contract_pub: EcdhPublicKey; + contract_pub: TALER_EcdhEphemeralPublicKeyP; // AES-GCM Encrypted contract terms using encryption - // key derived from DH of 'contract_pub' and the 'purse_pub'. + // key derived from DH of ``contract_pub`` and the ``purse_pub``. // Optional as the contract terms may already be known // to the exchange or the other wallet from a different // interaction. - e_contract_terms: byte[]; - } + e_contract_terms: string; + } - .. ts:def:: PurseDeposit { + .. ts:def:: PurseDeposit + + interface PurseDeposit { // Public key of the coin being deposited into the purse. coin_pub: EddsaPublicKey; @@ -2614,7 +2632,8 @@ Wallet-to-wallet transfers // Exchange's unblinded RSA signature of the coin. ub_sig: RsaSignature; - // Signature of `TALER_PurseDepositRequestPS`, + // Signature over `TALER_PurseDepositSignaturePS` + // of purpose ``TALER_SIGNATURE_PURSE_DEPOSIT`` // made by the customer with the // `coin's private key `. coin_sig: EddsaSignature; @@ -2810,7 +2829,7 @@ Wallet-to-wallet transfers // The number of remaining purses that can still be opened // under the given account. - remaining_purses: integer; + remaining_purses: Integer; } @@ -2835,7 +2854,7 @@ Wallet-to-wallet transfers :http:statuscode:`200 Ok`: The operation succeeded, the exchange confirms that the account can now be used. - The response will be an `AccountStatus` object. + The response will be an `AccountKycStatus` object. :http:statuscode:`202 Accepted`: The user should be redirected to the provided location to perform the required KYC checks to open the account. Afterwards, the @@ -2890,7 +2909,7 @@ Wallet-to-wallet transfers exchange_pub: EddsaPublicKey; } - .. ts:def:: AccountKycRedirect + .. ts:def:: AccountKycRedirect interface AccountKycRedirect { @@ -2946,13 +2965,13 @@ wallet-to-wallet payments. Only another exchange should access this endpoint. items: WadItem[]; // EdDSA signature of the exchange affirming the wad - // data is correct, must be of purpose - // TALER_SIGNATURE_WAD_DATA. + // data is correct, must be over `TALER_WadDataSignaturePS` + // and of purpose ``TALER_SIGNATURE_WAD_DATA``. exchange_sig: EddsaSignature; // public key used to create the signature. exchange_pub: EddsaPublicKey; - } + } Objects in the wad item list have the following format: -- cgit v1.2.3 From 60f61204fdfa89aa32abf0b916f05021ee89295e Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 15 May 2021 13:30:16 +0200 Subject: remove dangerous API as per discussion with FD --- core/api-wire.rst | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/core/api-wire.rst b/core/api-wire.rst index e05aa848..f82ceaa2 100644 --- a/core/api-wire.rst +++ b/core/api-wire.rst @@ -204,18 +204,6 @@ Querying the transaction history // The reserve public key extracted from the transaction details. reserve_pub: EddsaPublicKey; - // Set to 'true' if this wire transfer originates from - // a bank account with a partner bank that implies that - // the KYC has already been satisfied for this reserve - // because of it. - // FIXME: is this acceptable? Seems like users could - // easily be tricked into wiring funds to a reserve that - // they do NOT own, and then being held responsibe for - // the resulting account. So when setting this bit, - // we'd need at least a reserve_sig over the credit_account, - // which we do not get here... (DANGEROUS API). - // => need to discuss KYC more with real banks. - kyc_completed: boolean; } .. ts:def:: IncomingWadTransaction -- cgit v1.2.3 From d267f23fc695a99d95cbcffc4d35602ccef5c563 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 15 May 2021 16:05:52 +0200 Subject: more work on DD13 spec --- core/api-common.rst | 64 +++++++++---- core/api-exchange.rst | 256 ++++++++++++++++++++++++++++++++++++-------------- core/api-merchant.rst | 4 +- 3 files changed, 234 insertions(+), 90 deletions(-) diff --git a/core/api-common.rst b/core/api-common.rst index 81b85ab9..cc69870b 100644 --- a/core/api-common.rst +++ b/core/api-common.rst @@ -64,6 +64,8 @@ handle the error as if an internal error (500) had been returned. changed, the client MUST follow the link to the new location. If possible, the client SHOULD remember the new URL for the reserve for future requests. + :http:statuscode:`400 Bad request`: + One of the arguments to the request is missing or malformed. :http:statuscode:`500 Internal server error`: This always indicates some serious internal operational error of the exchange, such as a program bug, database problems, etc., and must not be used for @@ -75,10 +77,7 @@ handle the error as if an internal error (500) had been returned. although the auditor API for this is not yet specified. However, as internal server errors are always reported to the exchange operator, a good operator should naturally be able to address them in a timely fashion, especially - within 24h. When generating an internal server error, the exchange responds with - a JSON object containing the following fields: FIXME: What are the fields? - :http:statuscode:`400 Bad request`: - One of the arguments to the request is missing or malformed. + within 24h. Unless specified otherwise, all error status codes (4xx and 5xx) have a message body with an `ErrorDetail` JSON object. @@ -720,11 +719,6 @@ within the struct TALER_MerchantPublicKeyP merchant; }; -.. _TALER_RefreshCommitmentP: -.. sourcecode:: c - - // FIXME: put definition here - .. _TALER_RefreshMeltCoinAffirmationPS: .. sourcecode:: c @@ -810,7 +804,6 @@ within the struct GNUNET_HashCode h_wire_details; }; - .. _TALER_MasterWireFeePS: .. sourcecode:: c @@ -831,7 +824,7 @@ within the struct TALER_DepositTrackPS { /** - * purpose.purpose = TALER_SIGNATURE_MASTER_SEPA_DETAILS || TALER_SIGNATURE_MASTER_TEST_DETAILS + * purpose.purpose = TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; struct GNUNET_HashCode h_contract_terms; @@ -851,7 +844,6 @@ within the struct TALER_AmountNBO deposit_fee; }; - .. _TALER_WireDepositDataPS: .. _TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT: .. sourcecode:: c @@ -934,12 +926,31 @@ within the .. _TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND: .. sourcecode:: c - // FIXME: put definition here + struct TALER_RefundConfirmationPS { + /** + * purpose.purpose = TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_HashCode h_contract_terms; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_MerchantPublicKeyP merchant; + uint64_t rtransaction_id; + struct TALER_AmountNBO refund_amount; + }; .. _TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION: .. sourcecode:: c - // FIXME: put definition here + struct TALER_DepositTrackPS { + /** + * purpose.purpose = TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_HashCode h_contract_terms; + struct GNUNET_HashCode h_wire; + struct TALER_MerchantPublicKeyP merchant; + struct TALER_CoinSpendPublicKeyP coin_pub; + }; .. _TALER_RefundRequestPS: .. sourcecode:: c @@ -957,6 +968,9 @@ within the struct TALER_AmountNBO refund_fee; }; +.. _TALER_MerchantRefundConfirmationPS: +.. sourcecode:: c + struct TALER_MerchantRefundConfirmationPS { /** * purpose.purpose = TALER_SIGNATURE_MERCHANT_REFUND_OK @@ -986,7 +1000,6 @@ within the .. sourcecode:: c struct TALER_RecoupRefreshConfirmationPS { - /** * purpose.purpose = TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH */ @@ -1105,6 +1118,26 @@ within the struct GNUNET_CRYPTO_EccSignaturePurpose purpose; }; +.. _TALER_RefreshCommitmentP: +.. sourcecode:: c + + struct TALER_RefreshCommitmentP { + struct GNUNET_HashCode session_hash; + }; + + +.. _TALER_PurseRequestSignaturePS: +.. sourcecode:: c + + struct TALER_PurseRequestSignaturePS { + /** + * purpose.purpose = TALER_SIGNATURE_PURSE_REQUEST + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_TIME_AbsoluteNBO purse_expiration; + struct TALER_AmountNBO total_purse_amount; + struct GNUNET_HashCode h_contract_terms; + }; .. _TALER_PurseDepositSignaturePS: @@ -1137,7 +1170,6 @@ within the struct GNUNET_HashCode h_contract_terms; }; - .. _TALER_PurseMergeSignaturePS: .. sourcecode:: c diff --git a/core/api-exchange.rst b/core/api-exchange.rst index 44dfd2a5..089890af 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -398,7 +398,8 @@ possibly by using HTTPS. // ``payto://`` URI identifying the account and wire method payto_uri: string; - // Signature using the exchange's offline key + // Signature using the exchange's offline key over + // a `TALER_MasterWireDetailsPS` // with purpose ``TALER_SIGNATURE_MASTER_WIRE_DETAILS``. master_sig: EddsaSignature; } @@ -858,6 +859,7 @@ Management operations authorized by master key payto_uri: string; // Signature using the exchange's offline key + // over a `TALER_MasterWireDetailsPS` // with purpose ``TALER_SIGNATURE_MASTER_WIRE_DETAILS``. master_sig_wire: EddsaSignature; @@ -964,8 +966,9 @@ This part of the API is for the use by auditors interacting with the exchange. interface AuditorSignatureAddMessage { - // Signature by the auditor. - // Must have purpose ``TALER_SIGNATURE_AUDITOR_XXX``. + // Signature by the auditor over a + // `TALER_ExchangeKeyValidityPS`. + // Must have purpose ``TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS``. auditor_sig: EddsaSignature; } @@ -1022,8 +1025,9 @@ exchange. .. ts:def:: ReserveStatusRequest interface ReserveStatusRequest { - // Signature of type - // TALER_SIGNATURE_RESERVE_STATUS_REQUEST + // Signature of purpose + // ``TALER_SIGNATURE_RESERVE_STATUS_REQUEST`` over + // a `TALER_ReserveStatusRequestSignaturePS`. reserve_sig: EddsaSignature; } @@ -1072,7 +1076,8 @@ exchange. request_timestamp: Timestamp; // Signature created with the reserve's private key. - // Must be of purpose TALER_SIGNATURE_RESERVE_HISTORY_REQUEST. + // Must be of purpose ``TALER_SIGNATURE_RESERVE_HISTORY_REQUEST`` over + // a `TALER_ReserveHistoryRequestSignaturePS`. reserve_sig: EddsaSignature; } @@ -1096,7 +1101,8 @@ exchange. h_wire: HashCode; // Signature created with the reserve's private key. - // Must be of purpose TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST. + // Must be of purpose ``TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST`` over + // a `TALER_AccountSetupRequestSignaturePS`. reserve_sig: EddsaSignature; } @@ -1125,12 +1131,17 @@ exchange. // Hash of the contract. h_contract: HashCode; + // Hash of the wire details of the reserve. + h_wire: HashCode; + // Signature created with the reserve's private key. - // Must be of purpose TALER_SIGNATURE_ACCOUNT_MERGE. + // Must be of purpose ``TALER_SIGNATURE_ACCOUNT_MERGE`` over + // a `TALER_AccountMergeSignaturePS`. reserve_sig: EddsaSignature; // Signature created with the purse's private key. - // Must be of purpose TALER_SIGNATURE_PURSE_MERGE. + // Must be of purpose ``TALER_SIGNATURE_PURSE_MERGE`` + // over a `TALER_PurseMergeSignaturePS`. purse_sig: EddsaSignature; // Deposit fees that were charged to the purse. @@ -1151,8 +1162,9 @@ exchange. // Hash of the blinded coin to be signed. h_coin_envelope: HashCode; - // Signature of ``TALER_WithdrawRequestPS`` created with the reserve's - // private key. + // Signature over a `TALER_WithdrawRequestPS` + // with purpose ``TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW`` + // created with the reserve's private key. reserve_sig: EddsaSignature; // Fee that is charged for withdraw. @@ -1198,11 +1210,11 @@ exchange. receiver_account_details: string; // This is a signature over a - // struct ``TALER_ReserveCloseConfirmationPS`` with purpose + // struct `TALER_ReserveCloseConfirmationPS` with purpose // ``TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED``. exchange_sig: EddsaSignature; - // Public key used to create ``exchange_sig``. + // Public key used to create 'exchange_sig'. exchange_pub: EddsaPublicKey; // Time when the reserve was closed. @@ -1219,11 +1231,11 @@ exchange. amount: Amount; // This is a signature over - // a struct ``TALER_PaybackConfirmationPS`` with purpose - // ``TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK``. + // a struct `TALER_RecoupConfirmationPS` with purpose + // ``TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP``. exchange_sig: EddsaSignature; - // Public key used to create ``exchange_sig``. + // Public key used to create 'exchange_sig'. exchange_pub: EddsaPublicKey; // Time when the funds were paid back into the reserve. @@ -1262,7 +1274,8 @@ exchange. interface ReserveHistoryRequest { // Signature of type - // TALER_SIGNATURE_RESERVE_HISTORY_REQUEST + // ``TALER_SIGNATURE_RESERVE_HISTORY_REQUEST`` + // over a `TALER_ReserveHistoryRequestSignaturePS`. reserve_sig: EddsaSignature; // Time when the client made the request. @@ -1332,7 +1345,8 @@ exchange. coin_ev: CoinEnvelope; // Signature of `TALER_WithdrawRequestPS` created with - // the `reserves's private key `. + // the `reserves's private key ` + // using purpose ``TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW``. reserve_sig: EddsaSignature; } @@ -1340,7 +1354,7 @@ exchange. .. ts:def:: WithdrawResponse interface WithdrawResponse { - // The blinded RSA signature over the ``coin_ev``, affirms the coin's + // The blinded RSA signature over the 'coin_ev', affirms the coin's // validity after unblinding. ev_sig: BlindedRsaSignature; @@ -1363,6 +1377,8 @@ exchange. history: TransactionHistoryItem[] } +.. _delete-reserve: + .. http:DELETE:: /reserves/$RESERVE_PUB Forcefully closes a reserve. @@ -1432,7 +1448,6 @@ credited an amount specified in the deposit permission, possibly a fraction of the total coin's value, minus the deposit fee as specified by the coin's denomination. - .. _deposit: .. http:POST:: /coins/$COIN_PUB/deposit @@ -1445,10 +1460,9 @@ denomination. exchange. The exchange MUST return a 307 or 308 redirection to the correct base URL if this is the case. - The request should contain a JSON object with the - following fields: + **Request:** - **Request:** The request body must be a `DepositRequest` object. + The request body must be a `DepositRequest` object. **Response:** @@ -1516,7 +1530,7 @@ denomination. // exchange, possibly zero if refunds are not allowed. refund_deadline: Timestamp; - // Signature of `TALER_DepositRequestPS`, made by the customer with the + // Signature over `TALER_DepositRequestPS`, made by the customer with the // `coin's private key `. coin_sig: EddsaSignature; } @@ -1604,14 +1618,16 @@ denomination. // exchange, possibly zero if refunds are not allowed. refund_deadline: Timestamp; - // Signature by the coin. + // Signature over `TALER_DepositRequestPS`, made by the customer with the + // `coin's private key `. coin_sig: EddsaSignature; // Hash of the bank account from where we received the funds. h_wire: HashCode; // Hash of the public denomination key used to sign the coin. - // FIXME: why do we care to have this? + // Needed because 'coin_sig' signs over this, and + // that is important to fix the coin's denomination. h_denom_pub: HashCode; // Hash over the proposal data of the contract that @@ -1631,7 +1647,9 @@ denomination. // subtracting the amounts. amount: Amount; - // Signature by the coin. + // Signature by the coin over a + // `TALER_RefreshMeltCoinAffirmationPS` of + // purpose ``TALER_SIGNATURE_WALLET_COIN_MELT``. coin_sig: EddsaSignature; // Melt fee. @@ -1641,7 +1659,8 @@ denomination. rc: TALER_RefreshCommitmentP; // Hash of the public denomination key used to sign the coin. - // FIXME: why do we care to have this? + // Needed because 'coin_sig' signs over this, and + // that is important to fix the coin's denomination. h_denom_pub: HashCode; } @@ -1658,7 +1677,7 @@ denomination. // adding the amounts to the coin's denomination value. amount: Amount; - // Refund fee in case of type "REFUND". + // Refund fee. refund_fee: Amount; // Hash over the proposal data of the contract that @@ -1668,7 +1687,9 @@ denomination. // Refund transaction ID. rtransaction_id: Integer; - // `EdDSA Signature ` authorizing the REFUND. Made with + // `EdDSA Signature ` authorizing the REFUND over a + // `TALER_MerchantRefundConfirmationPS` with + // purpose ``TALER_SIGNATURE_MERCHANT_REFUND_OK``. Made with // the `public key of the merchant `. merchant_sig: EddsaSignature; @@ -1689,11 +1710,14 @@ denomination. // Date when the operation was made. timestamp: Timestamp; - // Signature by the coin. + // Signature by the coin over a + // `TALER_RecoupRequestPS` with purpose + // ``TALER_SIGNATURE_WALLET_COIN_RECOUP``. coin_sig: EddsaSignature; // Hash of the public denomination key used to sign the coin. - // FIXME: why do we care to have this? + // Needed because 'coin_sig' signs over this, and + // that is important to fix the coin's denomination. h_denom_pub: HashCode; // Coin blinding key. @@ -1702,11 +1726,12 @@ denomination. // Reserve receiving the recoup. reserve_pub: EddsaPublicKey; - // Signature by the exchange, must be - // of type TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP. + // Signature by the exchange over a + // `TALER_RecoupConfirmationPS`, must be + // of purpose ``TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP``. exchange_sig: EddsaSignature; - // Public key used to sign ``exchange_sig`` + // Public key of the private key used to create 'exchange_sig'. exchange_pub: EddsaPublicKey; } @@ -1725,11 +1750,12 @@ denomination. // Date when the operation was made. timestamp: Timestamp; - // Signature by the exchange - // of type TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH. + // Signature by the exchange over a + // `TALER_RecoupRefreshConfirmationPS` + // of purpose ``TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH``. exchange_sig: EddsaSignature; - // Public key used to sign ``exchange_sig``, + // Public key of the private key used to create 'exchange_sig'. exchange_pub: EddsaPublicKey; } @@ -1749,21 +1775,24 @@ denomination. // Date when the operation was made. timestamp: Timestamp; - // Signature by the coin. + // Signature by the coin over a `TALER_RecoupRequestPS` + // with purpose ``TALER_SIGNATURE_WALLET_COIN_RECOUP``. coin_sig: EddsaSignature; // Hash of the public denomination key used to sign the coin. - // FIXME: why do we care to have this? + // Needed because 'coin_sig' signs over this, and + // that is important to fix the coin's denomination. h_denom_pub: HashCode; // Coin blinding key. coin_blind: DenominationBlindingKeyP; - // Signature by the exchange - // of type TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH. + // Signature by the exchange over a + // `TALER_RecoupRefreshConfirmationPS` + // of purpose ``TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH``. exchange_sig: EddsaSignature; - // Public key used to sign ``exchange_sig``, + // Public key used to sign 'exchange_sig'. exchange_pub: EddsaPublicKey; // Blinding factor of the revoked new coin. @@ -1795,11 +1824,14 @@ denomination. // Date when the purse was set to expire. purse_expiration: Timestamp; - // Signature by the coin. + // Signature by the coin over a + // `TALER_PurseDepositSignaturePS` of + // purpose ``TALER_SIGNATURE_PURSE_DEPOSIT``. coin_sig: EddsaSignature; // Hash of the public denomination key used to sign the coin. - // FIXME: why do we care to have this? + // Needed because 'coin_sig' signs over this, and + // that is important to fix the coin's denomination. h_denom_pub: HashCode; } @@ -2167,6 +2199,7 @@ in using this API. interface RecoupWithdrawalConfirmation { // Tag to distinguish the `RecoupConfirmation` response type. refreshed: false; + // Public key of the reserve that will receive the recoup. reserve_pub: EddsaPublicKey; } @@ -2176,6 +2209,7 @@ in using this API. interface RecoupRefreshConfirmation { // Tag to distinguish the `RecoupConfirmation` response type. refreshed: true; + // Public key of the old coin that will receive the recoup. old_coin_pub: EddsaPublicKey; } @@ -2250,7 +2284,8 @@ typically also view the balance.) deposits: TrackTransferDetail[]; // Signature from the exchange made with purpose - // `TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT`. + // ``TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT`` + // over a `TALER_WireDepositDataPS`. exchange_sig: EddsaSignature; // Public EdDSA key of the exchange that was used to generate the signature. @@ -2286,7 +2321,7 @@ typically also view the balance.) **Request:** - :query merchant_sig: EdDSA signature of the merchant made with purpose ``TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION``, affirming that it is really the merchant who requires obtaining the wire transfer identifier. + :query merchant_sig: EdDSA signature of the merchant made with purpose ``TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION`` over a `TALER_DepositTrackPS`, affirming that it is really the merchant who requires obtaining the wire transfer identifier. **Response:** @@ -2320,7 +2355,8 @@ typically also view the balance.) // Total amount transferred. total_amount: Amount; - // Binary-only Signature_ for purpose `TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE` + // Binary-only Signature_ with purpose ``TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE`` + // over a `TALER_ConfirmWirePS` // whereby the exchange affirms the successful wire transfer. exchange_sig: EddsaSignature; @@ -2392,7 +2428,10 @@ Refunds // EdDSA public key of the merchant. merchant_pub: EddsaPublicKey; - // EdDSA signature of the merchant affirming the refund. + // EdDSA signature of the merchant over a + // `TALER_RefundRequestPS` with purpose + // ``TALER_SIGNATURE_MERCHANT_REFUND`` + // affirming the refund. merchant_sig: EddsaPublicKey; } @@ -2402,7 +2441,9 @@ Refunds interface RefundSuccess { // The EdDSA :ref:`signature` (binary-only) with purpose - // `TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND` using a current signing key of the + // ``TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND`` over + // a `TALER_RecoupRefreshConfirmationPS` + // using a current signing key of the // exchange affirming the successful refund. exchange_sig: EddsaSignature; @@ -2500,7 +2541,10 @@ Wallet-to-wallet transfers // SHA-512 hash of the contact of the purse. h_contract_terms: HashCode; - // EdDSA signature of the exchange affirming the purse status. + // EdDSA signature of the exchange over a + // `TALER_PurseStatusResponseSignaturePS` + // with purpose ``TALER_SIGNATURE_PURSE_STATUS_RESPONSE`` + // affirming the purse status. exchange_sig: EddsaSignature; // EdDSA public key exchange used for 'exchange_sig'. @@ -2523,7 +2567,9 @@ Wallet-to-wallet transfers Deposit money into a purse. Endpoint used by the buyer. - **Request:** The request body must be a `PurseRequest` object. + **Request:** + + The request body must be a `PurseRequest` object. **Response:** @@ -2557,6 +2603,11 @@ Wallet-to-wallet transfers In this case, the response is a `PurseDepositDoubleSpendError`. If the value of all successful coins is below the purse fee, the exchange may not setup the purse at all. + :http:statuscode:`425 Too Early`: + This response type is used if the given purse expiration time + is too far in the future (at least from the perspective + of the exchange). Thus, retrying at a later time may + succeed. The client should look at the ``Date:`` header of the response to see if a minor time difference is to blame and possibly adjust the request accordingly. **Details:** @@ -2565,7 +2616,10 @@ Wallet-to-wallet transfers interface PurseRequest { - // EdDSA signature of the purse confirming the key + // EdDSA signature of the purse over a + // `TALER_PurseRequestSignaturePS` + // of purpose ``TALER_SIGNATURE_PURSE_REQUEST`` + // confirming the key // invariants associated with the purse. // (amount, h_contract_terms, expiration). purse_sig: EddsaSignature; @@ -2651,7 +2705,8 @@ Wallet-to-wallet transfers total_deposit_fees: Amount; // EdDSA signature of the exchange affirming the payment, - // of purpose TALER_SIGNATURE_PURSE_DEPOSIT_CONFIRMED + // of purpose ``TALER_SIGNATURE_PURSE_DEPOSIT_CONFIRMED`` + // over a `TALER_PurseDepositConfirmedSignaturePS`. // Signs over the above and the purse public key and // the hash of the contract terms. exchange_sig: EddsaSignature; @@ -2676,22 +2731,68 @@ Wallet-to-wallet transfers .. ts:def:: PurseConflict - interface PurseConflict { + // Union discriminated by the "type" field. + type PurseConflict = + | PurseMergeConflict + | PurseRequestConflict; - // Total amount to be paid into the purse as per - // the previous request. - total_purse_amount: Amount; + .. ts:def:: PurseMergeConflict + + interface PurseMergeConflict { + type: "MERGE"; // SHA-512 hash of the contact of the purse. h_contract_terms: HashCode; + // Hash of the wire details of the reserve. + h_wire: HashCode; + + // Reserve merging the purse. + reserve_pub: EddsaPublicKey; + // Indicative time by which the purse should expire // if it has not been merged into an account. At this // point, all of the deposits made should be // auto-refunded. purse_expiration: Timestamp; - // EdDSA signature of the purse confirming that the + // When was the merge request generated. + merge_timestamp: Timestamp; + + // Total amount to be merged into the reserve. + // (excludes fees). + purse_value_after_fees: Amount; + + // EdDSA signature of the purse over + // `TALER_PurseMergeSignaturePS` of + // purpose ``TALER_SIGNATURE_PURSE_MERGE`` + // confirming that the + // above details hold for this purse. + purse_sig: EddsaSignature; + } + + .. ts:def:: PurseRequestConflict + + interface PurseRequestConflict { + type: "REQUEST"; + + // SHA-512 hash of the contact of the purse. + h_contract_terms: HashCode; + + // Indicative time by which the purse should expire + // if it has not been merged into an account. At this + // point, all of the deposits made should be + // auto-refunded. + purse_expiration: Timestamp; + + // Total amount to be paid into the purse as per + // the previous request. + total_purse_amount: Amount; + + // EdDSA signature of the purse over + // `TALER_PurseRequestSignaturePS` of + // purpose ``TALER_SIGNATURE_PURSE_REQUEST`` + // confirming that the // above details hold for this purse. purse_sig: EddsaSignature; @@ -2723,7 +2824,9 @@ Wallet-to-wallet transfers Merge purse with account, adding the value of the purse into the account. Endpoint to be used by the seller. - **Request:** The request body must be a `MergeRequest` object. + **Request:** + + The request body must be a `MergeRequest` object. :query timeout_ms=NUMBER: *Optional.* If specified, the exchange will wait up to ``timeout_ms`` milliseconds to receive payment before @@ -2764,15 +2867,17 @@ Wallet-to-wallet transfers interface MergeRequest { // payto://-URI of the account the purse is to be merged into. - // Must be of the form: "payto://taler/EXCHANGE_URL/RESERVE_PUB". + // Must be of the form: 'payto://taler/$EXCHANGE_URL/$RESERVE_PUB'. payto_uri: string; - // EdDSA signature of the account/reserve affirming the merge. - // Must be of purpose TALER_SIGNATURE_ACCOUNT_MERGE + // EdDSA signature of the account/reserve affirming the merge + // over a `TALER_AccountMergeSignaturePS`. + // Must be of purpose ``TALER_SIGNATURE_ACCOUNT_MERGE`` reserve_sig: EddsaSignature; - // EdDSA signature of the purse private key affirming the merge. - // Must be of purpose TALER_SIGNATURE_PURSE_MERGE. + // EdDSA signature of the purse private key affirming the merge + // over a `TALER_PurseMergeSignaturePS`. + // Must be of purpose ``TALER_SIGNATURE_PURSE_MERGE``. purse_sig: EddsaSignature; // Minimum amount that must be credited to the reserve, that is @@ -2814,7 +2919,8 @@ Wallet-to-wallet transfers contract_time: Timestamp; // EdDSA signature of the exchange affirming the merge of - // purpose TALER_SIGNATURE_PURSE_MERGE_SUCCESS. + // purpose ``TALER_SIGNATURE_PURSE_MERGE_SUCCESS`` + // over `TALER_PurseMergeSuccessSignaturePS`. // Signs over the above and the account public key. exchange_sig: EddsaSignature; @@ -2877,17 +2983,18 @@ Wallet-to-wallet transfers kyc_timestamp: Timestamp; // Bank account to be associated with the account. - // Can be ``payto://void/`` to not associate the + // Can be 'payto://void/' to not associate the // account with any bank account. In this case, // closing the account will result in the balance // being forfeit. If the provided wire method is - // not supported by the exchange *and* not ``void``, + // not supported by the exchange *and* not 'void', // this is a ``Bad Request`` (HTTP status 400). payto_uri: string; // EdDSA signature of the reserve affirming the request // to create the account, must be of purpose - // TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST + // ``TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST`` + // and over `TALER_AccountSetupRequestSignaturePS`. reserve_sig: EddsaPublicKey; } @@ -2902,7 +3009,8 @@ Wallet-to-wallet transfers // EdDSA signature of the exchange affirming the account // is KYC'ed, must be of purpose - // TALER_SIGNATURE_ACCOUNT_SETUP_SUCCESS. + // ``TALER_SIGNATURE_ACCOUNT_SETUP_SUCCESS`` + // and over `TALER_AccountSetupRequestSignaturePS`. exchange_sig: EddsaSignature; // public key used to create the signature. @@ -2983,7 +3091,7 @@ wallet-to-wallet payments. Only another exchange should access this endpoint. amount: Amount; // payto://-URI of the account the purse is to be merged into. - // Must be of the form: "payto://taler/EXCHANGE_URL/RESERVE_PUB". + // Must be of the form: 'payto://taler/EXCHANGE_URL/RESERVE_PUB'. payto_uri: string; // Purse public key. @@ -3000,11 +3108,13 @@ wallet-to-wallet payments. Only another exchange should access this endpoint. merge_timestamp: Timestamp; // Signature created with the reserve's private key. - // Must be of purpose TALER_SIGNATURE_ACCOUNT_MERGE + // Must be of purpose ``TALER_SIGNATURE_ACCOUNT_MERGE`` + // and over `TALER_AccountMergeSignaturePS`. reserve_sig: EddsaSignature; // Signature created with the purse's private key. - // Must be of purpose TALER_SIGNATURE_PURSE_MERGE + // Must be of purpose ``TALER_SIGNATURE_PURSE_MERGE`` + // and over `TALER_PurseMergeSignaturePS`. purse_sig: EddsaSignature; // Deposit fees that were charged to the purse. diff --git a/core/api-merchant.rst b/core/api-merchant.rst index 21956e5d..c9d9ca5e 100644 --- a/core/api-merchant.rst +++ b/core/api-merchant.rst @@ -1542,11 +1542,13 @@ Creating orders :ts:type:`PostOrderResponse`. :http:statuscode:`404 Not found`: Possible reasons are: + (1) The order given used products from the inventory, but those were not found in the inventory. (2) The merchant instance is unknown (including possibly the instance being not configured for new orders). (3) The wire method specified is not supported by the backend. + Details in the error code. NOTE: currently the client has no good way to find out which product is not in the inventory, we MAY want to specify that in the reply. @@ -1624,7 +1626,7 @@ Creating orders fulfillment_url?: string; } - The following `ProductSpecification` can be provided if the parts of the + The following `MinimalInventoryProduct` can be provided if the parts of the order are inventory-based, that is if the `PostOrderRequest` uses ``inventory_products``. For such products, which must be in the backend's inventory, the backend can automatically fill in the amount and other details about -- cgit v1.2.3 From 12b20845cdd54858484be235c2110fbf466324bc Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 15 May 2021 20:05:27 +0200 Subject: work on SQL comments --- design-documents/013-peer-to-peer-payments.rst | 67 ++++++++++++-------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/design-documents/013-peer-to-peer-payments.rst b/design-documents/013-peer-to-peer-payments.rst index a938b5a1..06065a12 100644 --- a/design-documents/013-peer-to-peer-payments.rst +++ b/design-documents/013-peer-to-peer-payments.rst @@ -661,7 +661,7 @@ TODO/FIXME: update the following SQL: add missing comments! ,PRIMARY KEY (reserve_uuid, kyc_date) ); COMMENT ON TABLE kyc_requests - IS ''; + IS 'KYC processes initiated by the owner of a reserve'; COMMENT ON COLUMN kyc_requests.reserve_uuid IS 'Reserve for which the KYC request was triggered.'; COMMENT ON COLUMN kyc_requests.reserve_sig @@ -678,44 +678,41 @@ TODO/FIXME: update the following SQL: add missing comments! CREATE TABLE IF NOT EXISTS mergers (merge_request_serial_id BIGSERIAL UNIQUE ,reserve_uuid BYTEA NOT NULL REFERENCES reserves (reserve_uuid) ON DELETE CASCADE - ,purse_url TEXT NOT NULL, + ,reserve_url TEXT NOT NULL, ,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32), ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) ,purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)) ,merge_timestamp INT8 NOT NULL ,purse_expiration INT8 NOT NULL ,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)) - ,h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)) ,purse_val INT8 NOT NULL ,purse_frac INT4 NOT NULL ,PRIMARY KEY (purse_pub) ); COMMENT ON TABLE mergers - IS ''; + IS 'Merge requests where a purse- and account-owner requested merging the purse into the account'; COMMENT ON COLUMN mergers.reserve_uuid - IS ''; - COMMENT ON COLUMN mergers.purse_url - IS ''; + IS 'identifies the reserve'; + COMMENT ON COLUMN mergers.reserve_url + IS 'payto://-URL of the reserve, identifies the exchange and the reserve'; COMMENT ON COLUMN mergers.purse_pub - IS ''; + IS 'public key of the purse'; COMMENT ON COLUMN mergers.reserve_sig - IS ''; + IS 'signature by the reserve private key affirming the merge'; COMMENT ON COLUMN mergers.purse_sig - IS ''; + IS 'signature by the purse private key affirming the merge'; COMMENT ON COLUMN mergers.merge_timestamp - IS ''; + IS 'when was the merge message signed'; COMMENT ON COLUMN mergers.purse_expiration - IS ''; + IS 'when is the purse set to expire'; COMMENT ON COLUMN mergers.h_contract_terms - IS ''; - COMMENT ON COLUMN mergers.h_wire - IS ''; + IS 'hash of the contract terms both sides are to agree upon'; COMMENT ON COLUMN mergers.purse_val - IS ''; + IS 'amount to be transferred from the purse to the reserve (excludes deposit fees)'; CREATE INDEX IF NOT EXISTS mergers_reserve_uuid ON mergers (reserve_uuid); COMMENT ON INDEX mergers_reserve_uuid - IS ''; + IS 'needed in reserve history computation'; -- CREATE TABLE IF NOT EXISTS contracts (contract_serial_id BIGSERIAL UNIQUE @@ -725,13 +722,13 @@ TODO/FIXME: update the following SQL: add missing comments! ,PRIMARY KEY (purse_pub) ); COMMENT ON TABLE contracts - IS ''; + IS 'encrypted contracts associated with purses'; COMMENT ON COLUMN contracts.purse_pub - IS ''; + IS 'public key of the purse that the contract is associated with'; COMMENT ON COLUMN contracts.pub_ckey - IS ''; + IS 'Public ECDH key used to encrypt the contract, to be used with the purse private key for decryption'; COMMENT ON COLUMN contracts.e_contract - IS ''; + IS 'AES-GCM encrypted contract terms (contains gzip compressed JSON after decryption)'; -- CREATE TABLE IF NOT EXISTS history_requests (reserve_uuid INT8 NOT NULL REFERENCES reserves(reserve_uuid) ON DELETE CASCADE, @@ -742,13 +739,13 @@ TODO/FIXME: update the following SQL: add missing comments! ,PRIMARY KEY (reserve_uuid,request_timestamp) ); COMMENT ON TABLE history_requests - IS ''; + IS 'Paid history requests issued by a client against a reserve'; COMMENT ON COLUMN history_requests.request_timestamp - IS ''; + IS 'When was the history request made'; COMMENT ON COLUMN history_requests.reserve_sig - IS ''; + IS 'Signature approving payment for the history request'; COMMENT ON COLUMN history_requests.history_fee_val - IS ''; + IS 'History fee approved by the signature'; -- CREATE TABLE IF NOT EXISTS close_requests (reserve_uuid INT8 NOT NULL REFERENCES reserves(reserve_uuid) ON DELETE CASCADE, @@ -759,13 +756,13 @@ TODO/FIXME: update the following SQL: add missing comments! ,PRIMARY KEY (reserve_uuid,close_timestamp) ); COMMENT ON TABLE close_requests - IS ''; + IS 'Explicit requests by a reserve owner to close a reserve immediately'; COMMENT ON COLUMN close_requests.close_timestamp - IS ''; + IS 'When the request was created by the client'; COMMENT ON COLUMN close_requests.reserve_sig - IS ''; + IS 'Signature affirming that the reserve is to be closed'; COMMENT ON COLUMN close_requests.close_val - IS ''; + IS 'Balance of the reserve at the time of closing, to be wired to the associated bank account (minus the closing fee)'; -- CREATE TABLE IF NOT EXISTS purse_deposits (purse_deposit_serial_id BIGSERIAL UNIQUE @@ -778,17 +775,17 @@ TODO/FIXME: update the following SQL: add missing comments! ,PRIMARY KEY (purse_pub,coin_pub) ); COMMENT ON TABLE purse_deposits - IS ''; + IS 'Requests depositing coins into a purse'; COMMENT ON COLUMN purse_deposits.purse_pub - IS ''; + IS 'Public key of the purse'; COMMENT ON COLUMN purse_deposits.purse_expiration - IS ''; + IS 'When the purse is set to expire'; COMMENT ON COLUMN purse_deposits.coin_pub - IS ''; + IS 'Public key of the coin being deposited'; COMMENT ON COLUMN purse_deposits.amount_with_fee_val - IS ''; + IS 'Total amount being deposited'; COMMENT ON COLUMN purse_deposits.coin_sig - IS ''; + IS 'Signature of the coin affirming the deposit into the purse, of type TALER_SIGNATURE_PURSE_DEPOSIT'; -- CREATE TABLE IF NOT EXISTS wads_out (wad_out_serial_id BIGSERIAL UNIQUE -- cgit v1.2.3 From e3454a99d5fc07f26220aab4de77391be3b43884 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 15 May 2021 22:23:45 +0200 Subject: finish DD13 --- design-documents/013-peer-to-peer-payments.rst | 102 +++++++++++++++---------- 1 file changed, 62 insertions(+), 40 deletions(-) diff --git a/design-documents/013-peer-to-peer-payments.rst b/design-documents/013-peer-to-peer-payments.rst index 06065a12..7be20217 100644 --- a/design-documents/013-peer-to-peer-payments.rst +++ b/design-documents/013-peer-to-peer-payments.rst @@ -624,10 +624,6 @@ reasons to have that association for legal inquiries. (However, it would also be possible to keep that link only in the external KYC service's database.) - -TODO/FIXME: update the following SQL: add missing comments! - - .. sourcecode:: sql -- Everything in one big transaction @@ -763,6 +759,30 @@ TODO/FIXME: update the following SQL: add missing comments! IS 'Signature affirming that the reserve is to be closed'; COMMENT ON COLUMN close_requests.close_val IS 'Balance of the reserve at the time of closing, to be wired to the associated bank account (minus the closing fee)'; + + -- + CREATE TABLE IF NOT EXISTS purse_requests + (purse_deposit_serial_id BIGSERIAL UNIQUE + ,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32), + ,purse_expiration INT8 NOT NULL + ,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64) + ,amount_with_fee_val INT8 NOT NULL + ,amount_with_fee_frac INT4 NOT NULL + ,purse_sig BYTEA NOT NULL CHECK(LENGTH(purse_sig)=64) + ,PRIMARY KEY (purse_pub,coin_pub) + ); + COMMENT ON TABLE purse_requests + IS 'Requests establishing purses, associating them with a contract but without a target reserve'; + COMMENT ON COLUMN purse_requests.purse_pub + IS 'Public key of the purse'; + COMMENT ON COLUMN purse_requests.purse_expiration + IS 'When the purse is set to expire'; + COMMENT ON COLUMN purse_requests.h_contract_terms + IS 'Hash of the contract the parties are to agree to'; + COMMENT ON COLUMN purse_requests.amount_with_fee_val + IS 'Total amount expected to be in the purse'; + COMMENT ON COLUMN purse_requests.purse_sig + IS 'Signature of the purse affirming the purse parameters, of type TALER_SIGNATURE_PURSE_REQUEST'; -- CREATE TABLE IF NOT EXISTS purse_deposits (purse_deposit_serial_id BIGSERIAL UNIQUE @@ -797,15 +817,15 @@ TODO/FIXME: update the following SQL: add missing comments! ,UNIQUE (exchange_url, execution_time) ); COMMENT ON TABLE wads_out - IS ''; + IS 'Wire transfers made to another exchange to transfer purse funds'; COMMENT ON COLUMN wads_out.wad_id - IS ''; + IS 'Unique identifier of the wad, part of the wire transfer subject'; COMMENT ON COLUMN wads_out.target_exchange_url - IS ''; + IS 'Base URL of the target exchange'; COMMENT ON COLUMN wads_out.amount_val - IS ''; + IS 'Amount that was wired'; COMMENT ON COLUMN wads_out.execution_time - IS ''; + IS 'Time when the wire transfer was scheduled'; -- CREATE TABLE IF NOT EXISTS wad_out_entries (wad_out_entry_serial_id BIGSERIAL UNIQUE @@ -824,30 +844,32 @@ TODO/FIXME: update the following SQL: add missing comments! ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) ,purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)) ); + CREATE INDEX IF NOT EXISTS wad_out_entries_index_by_wad + ON wad_out_entries (wad_out_serial_id); COMMENT ON TABLE wad_out_entries - IS ''; + IS 'Purses combined into a wad'; COMMENT ON COLUMN wad_out_entries.wad_out_serial_id - IS ''; + IS 'Wad the purse was part of'; COMMENT ON COLUMN wad_out_entries.reserve_pub - IS ''; + IS 'Target reserve for the purse'; COMMENT ON COLUMN wad_out_entries.purse_pub - IS ''; + IS 'Public key of the purse'; COMMENT ON COLUMN wad_out_entries.h_contract - IS ''; + IS 'Hash of the contract associated with the purse'; COMMENT ON COLUMN wad_out_entries.purse_expiration - IS ''; + IS 'Time when the purse expires'; COMMENT ON COLUMN wad_out_entries.merge_timestamp - IS ''; + IS 'Time when the merge was approved'; COMMENT ON COLUMN wad_out_entries.amount_with_fee_val - IS ''; + IS 'Total amount in the purse'; COMMENT ON COLUMN wad_out_entries.wad_fee_val - IS ''; + IS 'Wat fee charged to the purse'; COMMENT ON COLUMN wad_out_entries.deposit_fees_val - IS ''; + IS 'Total deposit fees charged to the purse'; COMMENT ON COLUMN wad_out_entries.reserve_sig - IS ''; + IS 'Signature by the receiving reserve, of purpose TALER_SIGNATURE_ACCOUNT_MERGE'; COMMENT ON COLUMN wad_out_entries.purse_sig - IS ''; + IS 'Signature by the purse of purpose TALER_SIGNATURE_PURSE_MERGE'; -- CREATE TABLE IF NOT EXISTS wads_in (wad_in_serial_id BIGSERIAL UNIQUE @@ -859,15 +881,15 @@ TODO/FIXME: update the following SQL: add missing comments! ,UNIQUE (wad_id, origin_exchange_url) ); COMMENT ON TABLE wads_in_entries - IS ''; + IS 'Incoming exchange-to-exchange wad wire transfers'; COMMENT ON COLUMN wads_in.wad_id - IS ''; + IS 'Unique identifier of the wad, part of the wire transfer subject'; COMMENT ON COLUMN wads_in.origin_exchange_url - IS ''; + IS 'Base URL of the originating URL, also part of the wire transfer subject'; COMMENT ON COLUMN wads_in.amount_val - IS ''; + IS 'Actual amount that was received by our exchange'; COMMENT ON COLUMN wads_in.arrival_time - IS ''; + IS 'Time when the wad was received'; -- CREATE TABLE IF NOT EXISTS wad_in_entries (wad_in_entry_serial_id BIGSERIAL UNIQUE @@ -887,37 +909,37 @@ TODO/FIXME: update the following SQL: add missing comments! ,purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)) ); COMMENT ON TABLE wad_in_entries - IS ''; + IS 'list of purses aggregated in a wad according to the sending exchange'; COMMENT ON COLUMN wad_in_entries.wad_in_serial_id - IS ''; + IS 'wad for which the given purse was included in the aggregation'; COMMENT ON COLUMN wad_in_entries.reserve_pub - IS ''; + IS 'target account of the purse (must be at the local exchange)'; COMMENT ON COLUMN wad_in_entries.purse_pub - IS ''; + IS 'public key of the purse that was merged'; COMMENT ON COLUMN wad_in_entries.h_contract - IS ''; + IS 'hash of the contract terms of the purse'; COMMENT ON COLUMN wad_in_entries.purse_expiration - IS ''; + IS 'Time when the purse was set to expire'; COMMENT ON COLUMN wad_in_entries.merge_timestamp - IS ''; + IS 'Time when the merge was approved'; COMMENT ON COLUMN wad_in_entries.amount_with_fee_val - IS ''; + IS 'Total amount in the purse'; COMMENT ON COLUMN wad_in_entries.wad_fee_val - IS ''; + IS 'Total wad fees paid by the purse'; COMMENT ON COLUMN wad_in_entries.deposit_fees_val - IS ''; + IS 'Total deposit fees paid when depositing coins into the purse'; COMMENT ON COLUMN wad_in_entries.reserve_sig - IS ''; + IS 'Signature by the receiving reserve, of purpose TALER_SIGNATURE_ACCOUNT_MERGE'; COMMENT ON COLUMN wad_in_entries.purse_sig - IS ''; + IS 'Signature by the purse of purpose TALER_SIGNATURE_PURSE_MERGE'; CREATE INDEX IF NOT EXISTS wad_in_entries_wad_in_serial ON wad_in_entries (wad_in_serial_id); CREATE INDEX IF NOT EXISTS wad_in_entries_reserve_pub ON wad_in_entries (reserve_pub); COMMENT ON INDEX wad_in_entries_wad_in_serial - IS ''; + IS 'needed to lookup all transfers associated with a wad'; COMMENT ON INDEX wad_in_entries_reserve_pub - IS ''; + IS 'needed to compute reserve history'; -- Complete transaction COMMIT; -- cgit v1.2.3 From 145788c384c7417815fd632a5aaac973c67d3136 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 16 May 2021 14:26:20 +0200 Subject: add missing configuration endpoints --- core/api-exchange.rst | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/core/api-exchange.rst b/core/api-exchange.rst index 089890af..9b8b2d12 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -926,6 +926,44 @@ Management operations authorized by master key } +.. http:post:: /management/p2pfees + + Provides fee configuration for purses. + + **Request:** + + The request must be a `P2PFees` message. + + **Response** + + :http:statuscode:`204 No content`: + The configuration update has been processed successfully. The body is empty. + :http:statuscode:`403 Forbidden`: + The signature is invalid. + :http:statuscode:`409 Conflict`: + The exchange has previously received a conflicting configuration message or the currency does not match. + + +.. http:post:: /management/partners + + Enables a partner exchange for wad transfers. + + **Request:** + + The request must be an `ExchangePartner` message. + + **Response** + + :http:statuscode:`204 No content`: + The partner has been added successfully. + :http:statuscode:`403 Forbidden`: + The signature is invalid. + :http:statuscode:`409 Conflict`: + The exchange has previously received a conflicting configuration message or the currency does not match. + + + + --------------- Auditor actions --------------- -- cgit v1.2.3 From a9e4a611c51d5a34c8c363a8abf52276edbcc127 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 16 May 2021 15:24:54 +0200 Subject: add tables for configuration related to p2p payments --- design-documents/013-peer-to-peer-payments.rst | 53 ++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/design-documents/013-peer-to-peer-payments.rst b/design-documents/013-peer-to-peer-payments.rst index 7be20217..c0308bcf 100644 --- a/design-documents/013-peer-to-peer-payments.rst +++ b/design-documents/013-peer-to-peer-payments.rst @@ -631,6 +631,34 @@ database.) -- Check patch versioning is in place. SELECT _v.register_patch('exchange-TBD', NULL, NULL); -- + CREATE TABLE IF NOT EXISTS partners + (partner_serial_id BIGSERIAL UNIQUE + ,partner_master_pub BYTEA NOT NULL CHECK(LENGTH(reserve_pub)=32) + ,start_date INT8 NOT NULL + ,end_date INT8 NOT NULL + ,wad_frequency INT8 NOT NULL + ,wad_fee_val INT8 NOT NULL + ,wad_fee_frac INT4 NOT NULL + ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)) + ,partner_base_url TEXT NOT NULL + ); + COMMENT ON TABLE partners + IS 'exchanges we do wad transfers to'; + COMMENT ON COLUMN partners.partner_master_pub + IS 'offline master public key of the partner'; + COMMENT ON COLUMN partners.start_date + IS 'starting date of the partnership'; + COMMENT ON COLUMN partners.end_date + IS 'end date of the partnership'; + COMMENT ON COLUMN partners.wad_frequency + IS 'how often do we promise to do wad transfers'; + COMMENT ON COLUMN partners.wad_fee_val + IS 'how high is the fee for a wallet to be added to a wad to this partner'; + COMMENT ON COLUMN partners.partner_base_url + IS 'base URL of the REST API for this partner'; + COMMENT ON COLUMN partners.master_sig + IS 'signature of our master public key affirming the partnership, of purpose TALER_SIGNATURE_MASTER_PARTNER_DETAILS'; + -- ALTER TABLE reserves ADD COLUMN kyc_needed BOOLEAN NOT NULL DEFAULT (false) ADD COLUMN kyc_passed BOOLEAN NOT NULL DEFAULT (false) @@ -674,7 +702,9 @@ database.) CREATE TABLE IF NOT EXISTS mergers (merge_request_serial_id BIGSERIAL UNIQUE ,reserve_uuid BYTEA NOT NULL REFERENCES reserves (reserve_uuid) ON DELETE CASCADE + ,partner_serial_id INT8 REFERENCES partners(partner_serial_id) ON DELETE CASCADE, ,reserve_url TEXT NOT NULL, + ,reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32), ,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32), ,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)) ,purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)) @@ -689,8 +719,12 @@ database.) IS 'Merge requests where a purse- and account-owner requested merging the purse into the account'; COMMENT ON COLUMN mergers.reserve_uuid IS 'identifies the reserve'; + COMMENT ON COLUMN mergers.partner_serial_id + IS 'identifies the partner exchange, NULL in case the target reserve lives at this exchange'; COMMENT ON COLUMN mergers.reserve_url IS 'payto://-URL of the reserve, identifies the exchange and the reserve'; + COMMENT ON COLUMN mergers.reserve_pub + IS 'public key of the target reserve'; COMMENT ON COLUMN mergers.purse_pub IS 'public key of the purse'; COMMENT ON COLUMN mergers.reserve_sig @@ -810,7 +844,7 @@ database.) CREATE TABLE IF NOT EXISTS wads_out (wad_out_serial_id BIGSERIAL UNIQUE ,wad_id BYTEA PRIMARY KEY CHECK (LENGTH(wad_id)=24) - ,target_exchange_url TEXT NOT NULL + ,partner_serial_id INT8 NOT NULL REFERENCES partners(partner_serial_id) ON DELETE CASCADE, ,amount_val INT8 NOT NULL ,amount_frac INT4 NOT NULL ,execution_time INT8 NOT NULL @@ -820,8 +854,8 @@ database.) IS 'Wire transfers made to another exchange to transfer purse funds'; COMMENT ON COLUMN wads_out.wad_id IS 'Unique identifier of the wad, part of the wire transfer subject'; - COMMENT ON COLUMN wads_out.target_exchange_url - IS 'Base URL of the target exchange'; + COMMENT ON COLUMN wads_out.partner_serial_id + IS 'target exchange of the wad'; COMMENT ON COLUMN wads_out.amount_val IS 'Amount that was wired'; COMMENT ON COLUMN wads_out.execution_time @@ -940,6 +974,19 @@ database.) IS 'needed to lookup all transfers associated with a wad'; COMMENT ON INDEX wad_in_entries_reserve_pub IS 'needed to compute reserve history'; + -- + CREATE TABLE IF NOT EXISTS partners + (partner_serial_id BIGSERIAL UNIQUE + ,wad_in_serial_id INT8 REFERENCES wads_in (wad_serial_id) ON DELETE CASCADE + ,partner_master_pub BYTEA NOT NULL CHECK(LENGTH(reserve_pub)=32) + ,start_date INT8 NOT NULL + ,end_date INT8 NOT NULL + ,wad_frequency INT8 NOT NULL + ,wad_fee_val INT8 NOT NULL + ,wad_fee_frac INT4 NOT NULL + ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)) + ,partner_base_url TEXT NOT NULL + ); -- Complete transaction COMMIT; -- cgit v1.2.3 From a30edee1db15fdeba205464b6238356d650775aa Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 16 May 2021 15:49:43 +0200 Subject: add table to cache /wire reply from partner exchange --- design-documents/013-peer-to-peer-payments.rst | 50 +++++++++++++++++++++----- manpages/taler-exchange-offline.1.rst | 31 ++++++++++++++++ 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/design-documents/013-peer-to-peer-payments.rst b/design-documents/013-peer-to-peer-payments.rst index c0308bcf..68c75643 100644 --- a/design-documents/013-peer-to-peer-payments.rst +++ b/design-documents/013-peer-to-peer-payments.rst @@ -975,18 +975,41 @@ database.) COMMENT ON INDEX wad_in_entries_reserve_pub IS 'needed to compute reserve history'; -- - CREATE TABLE IF NOT EXISTS partners - (partner_serial_id BIGSERIAL UNIQUE - ,wad_in_serial_id INT8 REFERENCES wads_in (wad_serial_id) ON DELETE CASCADE - ,partner_master_pub BYTEA NOT NULL CHECK(LENGTH(reserve_pub)=32) + CREATE TABLE IF NOT EXISTS p2pfees + (p2pfees_serial_id BIGSERIAL UNIQUE ,start_date INT8 NOT NULL ,end_date INT8 NOT NULL - ,wad_frequency INT8 NOT NULL - ,wad_fee_val INT8 NOT NULL - ,wad_fee_frac INT4 NOT NULL + ,kyc_timeout INT8 NOT NULL + ,purse_timeout INT8 NOT NULL + ,history_retention INT8 NOT NULL + ,purse_account_limit INT NOT NULL + ,kyc_fee_val INT8 NOT NULL + ,kyc_fee_frac INT4 NOT NULL + ,history_fee_val INT8 NOT NULL + ,history_fee_frac INT4 NOT NULL + ,account_fee_val INT8 NOT NULL + ,account_fee_frac INT4 NOT NULL + ,purse_fee_val INT8 NOT NULL + ,purse_fee_frac INT4 NOT NULL ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)) - ,partner_base_url TEXT NOT NULL ); + -- + CREATE TABLE IF NOT EXISTS partner_accounts + (payto_uri VARCHAR PRIMARY KEY + ,partner_serial_id INT8 REFERENCES partners(partner_serial_id) ON DELETE CASCADE, + ,partner_master_sig BYTEA CHECK (LENGTH(partner_master_sig)=64) + ,last_seen INT8 NOT NULL + ); + CREATE INDEX IF NOT EXISTS partner_accounts_index_by_partner_and_time + ON partner_accounts (partner_serial_id,last_seen); + COMMENT ON TABLE partner_accounts + IS 'Table with bank accounts of the partner exchange. Entries never expire as we need to remember the signature for the auditor.'; + COMMENT ON COLUMN wire_accounts.payto_uri + IS 'payto URI (RFC 8905) with the bank account of the partner exchange.'; + COMMENT ON COLUMN wire_accounts.partner_master_sig + IS 'Signature of purpose TALER_SIGNATURE_MASTER_WIRE_DETAILS by the partner master public key'; + COMMENT ON COLUMN wire_accounts.last_seen + IS 'Last time we saw this account as being active at the partner exchange. Used to select the most recent entry, and to detect when we should check again.'; -- Complete transaction COMMIT; @@ -1093,8 +1116,17 @@ Q / A * Q: What determines when a wad transfer can happen between two exchanges? - * Exchanges should explicitly state which other exchanges they are willing + * Exchanges explicitly state which other exchanges they are willing to do wad transfers with (and how often, at what cost). This may involve abstract policies like sharing an auditor, using the same currency and the same (banking) protocol, or other constraints (like a specific list of exchanges). + +* Q: What happens if the owner of a reserve never drains it? + + * Reserves are eventually closed. If the reserve is associated + with a bank account, the remaining funds are sent to that bank + account. If the reserve was created via a merge, and the owner + failed to associate a bank account with it (say because the + KYC step never happened), then the reserve balance is forfeit + to the exchange upon expiration. diff --git a/manpages/taler-exchange-offline.1.rst b/manpages/taler-exchange-offline.1.rst index 6d800f5b..027316b6 100644 --- a/manpages/taler-exchange-offline.1.rst +++ b/manpages/taler-exchange-offline.1.rst @@ -270,6 +270,37 @@ It outputs the signature affirming the wire fees, in a format suitable for the ``upload`` subcommand. +enable-partner +-------------- + +This subcommand informs an exchange about the wad fee and frequency to apply +to a partner exchange. The arguments provided must include: + + (1) Partner exchange base URL. + (2) Partner exchange master public key. + (3) Calendar year for which the fee applies, 'now' for the current year. + (4) Wad frequency, in minutes (for example, '30'). + (5) Wad fee (for example, 'KUDOS:0.1'). + + +p2p-fees +-------- + +This subcommand configures fees related to wallet-to-wallet payments. If this configuration is not provided, wallet-to-wallet payments will be disabled by the exchange. + +The arguments provided must include: + + (1) Calendar year for which the fee applies, 'now' for the current year. + (2) KYC timeout. How long does the exchange keep a reserve open that is waiting for the KYC. + (3) KYC fee. How much will the exchange charge for performing KYC. + (4) Purse timeout. How long does the exchange keep information about a purse around after it expired or was successfully merged? + (5) Purse fee. How much will the exchange charge for an abandoned purse. Also the minimum amount that can be in a purse that is not associated with an account. + (6) Number of free purses per account. + (7) Annual fee charged to an open account. + (8) How long will the exchange preserve an account history. + (9) History fee charged when inquiring about non-recent account history. + + upload ------ -- cgit v1.2.3 From 2961b83cd565b81f6616ff779c65a9a7a6334f48 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 17 May 2021 17:49:02 +0200 Subject: add endpoint to delete bogus wire transfers --- core/api-merchant.rst | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/core/api-merchant.rst b/core/api-merchant.rst index c9d9ca5e..ddd612ca 100644 --- a/core/api-merchant.rst +++ b/core/api-merchant.rst @@ -2359,7 +2359,7 @@ Querying known wire transfers exchange_url: string; // Serial number identifying the transfer in the merchant backend. - // Used for filgering via ``offset``. + // Used for filtering via ``offset``. transfer_serial_id: number; // Time of the execution of the wire transfer by the exchange, according to the exchange @@ -2378,6 +2378,30 @@ Querying known wire transfers } +Deleting wire transfer +---------------------- + +Deleting a wire transfer can be useful in case of a data entry +mistake. In particular, if the exchange base URL was entered +badly, deleting the old entry and adding a correct one is a +good idea. Note that deleting wire transfers is no longer possible +once we got a reply from the exchange. + +.. http:delete:: [/instances/$INSTANCE]/private/transfers/$TID + + Here, the TID ist the 'transfer_serial_id' of the transfer + to delete. + + **Response:** + + :http:statuscode:`204 No content`: + The transfer was deleted. + :http:statuscode:`404 Not found`: + The transfer was already unknown. + :http:statuscode:`409 Conflict`: + The transfer cannot be deleted anymore. + + -------------------- Backend: Giving tips -------------------- -- cgit v1.2.3 From 72065f18ba1f08355aa2a767c05fbda8587b3e3b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 May 2021 17:51:42 +0200 Subject: clarify --- core/api-exchange.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/api-exchange.rst b/core/api-exchange.rst index 9b8b2d12..d588e582 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -2617,7 +2617,9 @@ Wallet-to-wallet transfers The response will include a `PurseDepositSuccess` object. :http:statuscode:`202 Accepted`: The payment was accepted, but insufficient to reach the - specified purse balance. The client should make further + specified purse balance. If an encrypted contract was + provided, it will have been stored in the database. + The client should make further purse deposits before the expiration deadline. The response will include a `PurseDepositAccepted` object. :http:statuscode:`401 Unauthorized`: @@ -2635,12 +2637,16 @@ Wallet-to-wallet transfers residual value, or because the same public key of the coin has been previously used with a different denomination. Which case it is can be decided by looking at the error code - (``TALER_EC_EXCHANGE_DEPOSIT_INSUFFICIENT_FUNDS`` or ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``). + (``TALER_EC_EXCHANGE_DEPOSIT_INSUFFICIENT_FUNDS`` or + ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``). The fields of the response are the same in both cases. The request should not be repeated again with this coin. In this case, the response is a `PurseDepositDoubleSpendError`. If the value of all successful coins is below the purse fee, - the exchange may not setup the purse at all. + the exchange may not setup the purse at all. The encrypted + contract will not have been associated with the purse if this + status code is returned. However, all coins that were not + double-spent will have been deposited into the purse. :http:statuscode:`425 Too Early`: This response type is used if the given purse expiration time is too far in the future (at least from the perspective -- cgit v1.2.3 From d35d8df37b02c23561c5af1d8fc70f69f99a4fab Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 May 2021 18:29:25 +0200 Subject: deal with purse auto-refund on expiration, ensure exchange has max_deposit_fees always --- core/api-common.rst | 17 +++++++++++++++++ core/api-exchange.rst | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/core/api-common.rst b/core/api-common.rst index cc69870b..e5afa872 100644 --- a/core/api-common.rst +++ b/core/api-common.rst @@ -1299,6 +1299,23 @@ within the }; +.. _TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND: +.. sourcecode:: c + + struct TALER_CoinPurseRefundConfirmationPS { + /** + * purpose.purpose = TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct TALER_PursePublicKey purse_pub; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_MerchantPublicKeyP merchant; + struct TALER_AmountNBO remaining_amount; + struct TALER_AmountNBO purse_fee_share; + struct TALER_AmountNBO refund_fee; + }; + + .. _TALER_DenominationKeyAnnouncementPS: .. sourcecode:: c diff --git a/core/api-exchange.rst b/core/api-exchange.rst index d588e582..b8f61a8a 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -1628,7 +1628,8 @@ denomination. | CoinRecoupTransaction | CoinOldCoinRecoupTransaction | CoinRecoupRefreshTransaction - | CoinPurseDepositTransaction; + | CoinPurseDepositTransaction + | CoinPurseRefundTransaction; .. ts:def:: CoinDepositTransaction @@ -1874,6 +1875,39 @@ denomination. } + .. ts:def:: CoinPurseRefundTransaction + + interface CoinPurseRefundTransaction { + type: "PURSE_REFUND"; + + // The total amount of the coin's value restored + // by this transaction. + // The amount given excludes the refund fee. + // The current coin value can thus be computed by + // adding the amount to the coin's denomination value. + amount: Amount; + + // Refund fee (of the coin's denomination). The deposit + // fee will be waived. + refund_fee: Amount; + + // Share of the purse fee charged to this coin. + // The sum of all purse fee shares will match the + // total purse fee. + purse_fee_share: Amount; + + // Public key of the purse that expired. + purse_pub: EddsaPublicKey; + + // Signature by the exchange over a + // `TALER_CoinPurseRefundConfirmationPS` + // of purpose ``TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND``. + exchange_sig: EddsaSignature; + + // Public key used to sign 'exchange_sig'. + exchange_pub: EddsaPublicKey; + + } ---------- @@ -2675,6 +2709,9 @@ Wallet-to-wallet transfers // purse once the amount given here is reached. total_purse_amount: Amount; + // Maximum deposit fees that can be charged under the contract. + max_deposit_fees: Amount; + // SHA-512 hash of the contact of the purse. h_contract_terms: HashCode; @@ -2930,6 +2967,11 @@ Wallet-to-wallet transfers // reserve can be higher! minimum_amount_contributed: Amount; + // Maximum deposit fees that can be charged under the contract. + // The 'total_purse_amount' will be the + // 'minimum_amount_contributed' plus the 'max_deposit_fees'. + max_deposit_fees: Amount; + // SHA-512 hash of the contact of the purse. h_contract_terms: HashCode; -- cgit v1.2.3 From 5ec68febc9ac8f9ed753bf11186553321705fc85 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 May 2021 18:33:08 +0200 Subject: more amounts needed --- core/api-exchange.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/api-exchange.rst b/core/api-exchange.rst index b8f61a8a..d0168376 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -2596,6 +2596,10 @@ Wallet-to-wallet transfers total_purse_amount: Amount; // Total amount deposited into the purse so far. + // If 'total_deposit_amount' minus 'deposit_fees' + // exceeds 'purse_value_after_fees', and a + // 'merge_request' exists for the purse, then the + // purse will (have been) merged with the account. total_deposit_amount: Amount; // Indicative time by which the purse expires @@ -2603,6 +2607,10 @@ Wallet-to-wallet transfers // point, all of the deposits made will be auto-refunded. purse_expiration: Timestamp; + // Desired total amount to be merged into the reserve. + // (excludes fees). + purse_value_after_fees: Amount; + // Indicative time at which the exchange is answering the // status request. Used as part of 'exchange_sig'. status_timestamp: Timestamp; @@ -2610,6 +2618,9 @@ Wallet-to-wallet transfers // Maximum deposit fees that can be charged under the contract. max_deposit_fees: Amount; + // Deposit fees charged so far to all deposited coins. + deposit_fees: Amount; + // SHA-512 hash of the contact of the purse. h_contract_terms: HashCode; -- cgit v1.2.3 From a846c5a30c7291e0bb514e5f639e96e079b30ff4 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 May 2021 18:37:14 +0200 Subject: revise purse amount handling --- core/api-common.rst | 8 ++++---- core/api-exchange.rst | 24 +++++------------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/core/api-common.rst b/core/api-common.rst index e5afa872..41e3b8fa 100644 --- a/core/api-common.rst +++ b/core/api-common.rst @@ -1135,7 +1135,7 @@ within the */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; struct GNUNET_TIME_AbsoluteNBO purse_expiration; - struct TALER_AmountNBO total_purse_amount; + struct TALER_AmountNBO merge_value_after_fees; struct GNUNET_HashCode h_contract_terms; }; @@ -1181,7 +1181,7 @@ within the struct TALER_ReservePublicKey reserve_pub; struct GNUNET_TIME_AbsoluteNBO merge_timestamp; struct GNUNET_TIME_AbsoluteNBO purse_expiration; - struct TALER_AmountNBO purse_value_after_fees; + struct TALER_AmountNBO merge_value_after_fees; struct GNUNET_HashCode h_contract_terms; struct GNUNET_HashCode h_wire; }; @@ -1198,7 +1198,7 @@ within the struct TALER_PursePublicKey purse_pub; struct GNUNET_TIME_AbsoluteNBO merge_timestamp; struct GNUNET_TIME_AbsoluteNBO purse_expiration; - struct TALER_AmountNBO purse_value_after_fees; + struct TALER_AmountNBO merge_value_after_fees; struct GNUNET_HashCode h_contract_terms; struct GNUNET_HashCode h_wire; }; @@ -1214,7 +1214,7 @@ within the struct GNUNET_CRYPTO_EccSignaturePurpose purpose; struct TALER_ReservePublicKey reserve_pub; struct TALER_PursePublicKey purse_pub; - struct TALER_AmountNBO merge_amount; + struct TALER_AmountNBO merge_amount_after_fees; struct GNUNET_TIME_AbsoluteNBO contract_time; struct GNUNET_HashCode h_contract_terms; struct GNUNET_HashCode h_wire; diff --git a/core/api-exchange.rst b/core/api-exchange.rst index d0168376..abb90c7c 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -2592,12 +2592,9 @@ Wallet-to-wallet transfers interface PurseStatus { - // Total amount that must be paid into the purse. - total_purse_amount: Amount; - // Total amount deposited into the purse so far. // If 'total_deposit_amount' minus 'deposit_fees' - // exceeds 'purse_value_after_fees', and a + // exceeds 'merge_value_after_fees', and a // 'merge_request' exists for the purse, then the // purse will (have been) merged with the account. total_deposit_amount: Amount; @@ -2609,15 +2606,12 @@ Wallet-to-wallet transfers // Desired total amount to be merged into the reserve. // (excludes fees). - purse_value_after_fees: Amount; + merge_value_after_fees: Amount; // Indicative time at which the exchange is answering the // status request. Used as part of 'exchange_sig'. status_timestamp: Timestamp; - // Maximum deposit fees that can be charged under the contract. - max_deposit_fees: Amount; - // Deposit fees charged so far to all deposited coins. deposit_fees: Amount; @@ -2718,10 +2712,7 @@ Wallet-to-wallet transfers // first request failed with a double-spending error. // The exchange will confirm the creation of the // purse once the amount given here is reached. - total_purse_amount: Amount; - - // Maximum deposit fees that can be charged under the contract. - max_deposit_fees: Amount; + merge_value_after_fees: Amount; // SHA-512 hash of the contact of the purse. h_contract_terms: HashCode; @@ -2853,7 +2844,7 @@ Wallet-to-wallet transfers // Total amount to be merged into the reserve. // (excludes fees). - purse_value_after_fees: Amount; + merge_value_after_fees: Amount; // EdDSA signature of the purse over // `TALER_PurseMergeSignaturePS` of @@ -2976,12 +2967,7 @@ Wallet-to-wallet transfers // the total value of the purse minus the deposit fees. // If the deposit fees are lower, the contribution to the // reserve can be higher! - minimum_amount_contributed: Amount; - - // Maximum deposit fees that can be charged under the contract. - // The 'total_purse_amount' will be the - // 'minimum_amount_contributed' plus the 'max_deposit_fees'. - max_deposit_fees: Amount; + merge_value_after_fees: Amount; // SHA-512 hash of the contact of the purse. h_contract_terms: HashCode; -- cgit v1.2.3 From 17d2d92487f398aa95f8a55224eb26ebda2e1ada Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 May 2021 19:20:52 +0200 Subject: hw --- core/api-common.rst | 1 + core/api-exchange.rst | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/api-common.rst b/core/api-common.rst index 41e3b8fa..45e24ddf 100644 --- a/core/api-common.rst +++ b/core/api-common.rst @@ -1064,6 +1064,7 @@ within the * purpose.purpose = TALER_SIGNATURE_RESERVE_STATUS_REQUEST */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_TIME_AbsoluteNBO request_timestamp; }; diff --git a/core/api-exchange.rst b/core/api-exchange.rst index abb90c7c..23365ef8 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -1055,6 +1055,8 @@ exchange. :http:statuscode:`401 Unauthorized`: The *Account-Request-Signature* is invalid. This response comes with a standard `ErrorDetail` response. + :http:statuscode:`403 Forbidden`: + The provided timestamp is not close to the current time. :http:statuscode:`404 Not found`: The reserve key does not belong to a reserve known to the exchange. @@ -1067,6 +1069,12 @@ exchange. // ``TALER_SIGNATURE_RESERVE_STATUS_REQUEST`` over // a `TALER_ReserveStatusRequestSignaturePS`. reserve_sig: EddsaSignature; + + // Time when the client made the request. + // Timestamp must be reasonably close to the time of + // the exchange, otherwise the exchange may reject + // the request. + request_timestamp: Timestamp; } .. ts:def:: ReserveStatus @@ -1300,6 +1308,8 @@ exchange. :http:statuscode:`401 Unauthorized`: The *Account-Request-Signature* is invalid. This response comes with a standard `ErrorDetail` response. + :http:statuscode:`403 Forbidden`: + The provided timestamp is not close to the current time. :http:statuscode:`404 Not found`: The reserve key does not belong to a reserve known to the exchange. :http:statuscode:`412 Precondition failed`: @@ -2406,7 +2416,7 @@ typically also view the balance.) merchant should come back later and ask again. The response body is a `TrackTransactionAcceptedResponse`. :http:statuscode:`401 Unauthorized`: - The signature is invalid. + A signature is invalid. :http:statuscode:`404 Not found`: The deposit operation is unknown to the exchange. @@ -2663,6 +2673,9 @@ Wallet-to-wallet transfers The response will include a `PurseDepositAccepted` object. :http:statuscode:`401 Unauthorized`: A coin signature is invalid. + FIXME: add message that tells the client + which coin signature(s) were valid. ErrorDetail is not enough + to identify the problematic coin. This response comes with a standard `ErrorDetail` response. :http:statuscode:`403 Forbidden`: The server is denying the operation as a purse with a @@ -2731,6 +2744,7 @@ Wallet-to-wallet transfers contract?: EncryptedContract; // Array of coins being deposited into the purse. + // Maximum length is 128. deposits: PurseDeposit[]; } -- cgit v1.2.3 From 81655daf19f3739127cb78d6553cfaf48640e46a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 May 2021 20:01:39 +0200 Subject: more detailed 401 error --- core/api-exchange.rst | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/core/api-exchange.rst b/core/api-exchange.rst index 23365ef8..06f76582 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -2672,11 +2672,8 @@ Wallet-to-wallet transfers purse deposits before the expiration deadline. The response will include a `PurseDepositAccepted` object. :http:statuscode:`401 Unauthorized`: - A coin signature is invalid. - FIXME: add message that tells the client - which coin signature(s) were valid. ErrorDetail is not enough - to identify the problematic coin. - This response comes with a standard `ErrorDetail` response. + A coin signature is invalid. The response will + include a `PurseDepositSignatureErrorDetail` :http:statuscode:`403 Forbidden`: The server is denying the operation as a purse with a different contract or total amount already exists. @@ -2916,6 +2913,33 @@ Wallet-to-wallet transfers } + .. ts:def:: PurseDepositSignatureErrorDetail + + interface PurseDepositSignatureErrorDetail { + // Taler error code, summarizing the problem. + // Note that for problems about specific + // coins, the 'coin_error_map' should be consulted. + // The 'coin_error_map' will be empty if the + // 'purse_sig' was invalid. In this case, + // the coins will not even have been checked by + // the exchange. + code: number; + + // Human-readable description of the error, i.e. "invalid siganture". + hint?: string; + + // Total amount contributed by the current request. + // Note that some coins may have been successfully + // deposited into the purse, so the total amount + // from these coins is listed here. + total_amount_contributed: Amount; + + // Public keys of coins that could not be deposited + // into the purse, mapped to the coin's histories. + coin_error_map: EddsaPublicKey -> ErrorDetail[]; + } + + .. http:POST:: /purses/$PURSE_PUB/merge Merge purse with account, adding the value of the purse into -- cgit v1.2.3 From 29c6de2b0a5c3cdf6528df7370eed13fc0f78f00 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 May 2021 18:44:31 +0200 Subject: document new auth policy --- taler-merchant-manual.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/taler-merchant-manual.rst b/taler-merchant-manual.rst index 6782c7ef..f8e2eb30 100644 --- a/taler-merchant-manual.rst +++ b/taler-merchant-manual.rst @@ -778,16 +778,17 @@ If everything worked as expected, the command .. code-block:: console - $ curl http://localhost:8888/ + $ curl http://localhost:8888/config -should return the message +should return some basic configuration status data about the service. -.. code-block:: none +Please note that your backend is right now likely globally reachable. You can either: - Hello, I'm a merchant's Taler backend. This HTTP server is not for humans. + * Use the ``--auth=$TOKEN`` command-line option to set an access token to be provided in an ``Authorize: Bearer $TOKEN`` HTTP header. Note that this can be used at anytime to override access control, but remains only in effect until a first instance is created or an existing instance authentication setting is modified. + * Set the ``TALER_MERCHANT_TOKEN`` environment variable to ``$TOKEN`` for the same effect. This method has the advantage of ``$TOKEN`` not being visible as a command-line interface to other local users on the same machine. + * Set up an instance with an authentication token before some unauthorized person has a chance to access the backend. As the backend is useless without any instance and the chances of remote attackers during the initial configuration is low, this is probably sufficient for most use-cases. Still, keep the first two scenarios in mind in case you ever forget your access token! -Please note that your backend is right now likely globally reachable. -Production systems should be configured to bind to a UNIX domain socket +Production systems should additionally be configured to bind to a UNIX domain socket and use TLS for improved network privacy, see :ref:`Secure setup `. -- cgit v1.2.3 From 749545b404b015b20cbabb8a670839de3aaf96b8 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 20 May 2021 12:17:39 +0200 Subject: document 409 case, see #6863 --- core/api-wire.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/api-wire.rst b/core/api-wire.rst index f82ceaa2..654d72f3 100644 --- a/core/api-wire.rst +++ b/core/api-wire.rst @@ -345,6 +345,8 @@ exposed by bank gateways in production. Authentication failed, likely the credentials are wrong. :http:statuscode:`404 Not found`: The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object. + :http:statuscode:`409 Conflict`: + The 'reserve_pub' argument was used previously in another transfer, and the specification mandates that reserve public keys must not be reused. .. ts:def:: AddIncomingRequest -- cgit v1.2.3 From 4aa313c843546f2ec7fabaddf2bc42ce5a2af795 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 20 May 2021 18:31:56 +0200 Subject: document wallet transaction deletion --- taler-wallet.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/taler-wallet.rst b/taler-wallet.rst index be471446..4de69520 100644 --- a/taler-wallet.rst +++ b/taler-wallet.rst @@ -486,6 +486,19 @@ Transactions are all operations or events that affect the balance. amountEffective: Amount; } + +:Name: ``"deleteTransaction"`` +:Description: Delete a transaction by ID. +:Request: + .. ts:def:: DeleteTransactionRequest + + interface DeleteTransactionRequest { + // Transaction ID (opaque!) as returned in + // the transaction list response. + transactionId: string; + } +:Response: Returns an empty object + Refunds ------- -- cgit v1.2.3 From f6c20b01e192c9cbb05722a6194b7441f300b09e Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 25 May 2021 20:51:55 +0200 Subject: doc update for #6889 --- core/api-common.rst | 15 ++++++++++++++- core/api-exchange.rst | 43 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/core/api-common.rst b/core/api-common.rst index 45e24ddf..97ce02d1 100644 --- a/core/api-common.rst +++ b/core/api-common.rst @@ -1015,7 +1015,7 @@ within the struct TALER_RecoupConfirmationPS { /** - * purpose.purpose = TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP + * purpose.purpose = TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; struct GNUNET_TIME_AbsoluteNBO timestamp; @@ -1025,6 +1025,19 @@ within the }; +.. _TALER_DenominationUnknownAffirmationPS: +.. sourcecode:: c + + struct TALER_DenominationUnknownAffirmationPS { + /** + * purpose.purpose = TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_TIME_AbsoluteNBO timestamp; + struct GNUNET_HashCode h_denom_pub; + }; + + .. _TALER_ReserveCloseConfirmationPS: .. sourcecode:: c diff --git a/core/api-exchange.rst b/core/api-exchange.rst index 06f76582..8e41447a 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -993,6 +993,7 @@ This part of the API is for the use by auditors interacting with the exchange. The auditor signature is invalid. :http:statuscode:`404 Not found`: The denomination key for which the auditor is providing a signature is unknown. + The response will be a `DenominationUnkownMessage`. :http:statuscode:`410 Gone`: This auditor is no longer supported by the exchange. :http:statuscode:`412 Precondition failed`: @@ -1000,6 +1001,30 @@ This part of the API is for the use by auditors interacting with the exchange. **Details:** + .. ts:def:: DenominationUnknownMessage + + interface DenominationUnknownMessage { + + // Taler error code. + code: number; + + // Signature by the exchange over a + // `TALER_DenominationUnknownAffirmationPS`. + // Must have purpose ``TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN``. + exchange_sig: EddsaSignature; + + // Public key of the exchange used to create + // the 'exchange_sig. + exchange_pub: EddsaPublicKey; + + // Hash of the denomination public key that is unknown. + h_denom_pub: HashCode; + + // When was the signature created. + timestamp: Timestamp; + + } + .. ts:def:: AuditorSignatureAddMessage interface AuditorSignatureAddMessage { @@ -1365,8 +1390,9 @@ exchange. :http:statuscode:`404 Not found`: The denomination key or the reserve are not known to the exchange. If the denomination key is unknown, this suggests a bug in the wallet as the - wallet should have used current denomination keys from ``/keys``. If the - reserve is unknown, the wallet should not report a hard error yet, but + wallet should have used current denomination keys from ``/keys``. + In this case, the response will be a `DenominationUnkownMessage`. + If the reserve is unknown, the wallet should not report a hard error yet, but instead simply wait for up to a day, as the wire transaction might simply not yet have completed and might be known to the exchange in the near future. In this case, the wallet should repeat the exact same request later again @@ -1520,8 +1546,10 @@ denomination. :http:statuscode:`401 Unauthorized`: One of the signatures is invalid. :http:statuscode:`404 Not found`: - Either the denomination key is not recognized (expired or invalid) or - the wire type is not recognized. + Either the denomination key is not recognized (expired or invalid), + or the wire type is not recognized. + If the denomination key is unknown, the response will be + a `DenominationUnkownMessage`. :http:statuscode:`409 Conflict`: The deposit operation has either failed because the coin has insufficient residual value, or because the same public key of the coin has been @@ -1956,6 +1984,8 @@ the API during normal operation. :http:statuscode:`404 Not found`: The exchange does not recognize the denomination key as belonging to the exchange, or it has expired. + If the denomination key is unknown, the response will be + a `DenominationUnkownMessage`. :http:statuscode:`409 Conflict`: The operation is not allowed as the coin has insufficient residual value, or because the same public key of the coin has been @@ -2236,6 +2266,8 @@ in using this API. The denomination key is not in the set of denomination keys where emergency pay back is enabled, or the blinded coin is not known to have been withdrawn. + If the denomination key is unknown, the response will be + a `DenominationUnkownMessage`. :http:statuscode:`409 Conflict`: The operation is not allowed as the coin has insufficient residual value, or because the same public key of the coin has been @@ -2679,8 +2711,7 @@ Wallet-to-wallet transfers different contract or total amount already exists. This response comes with a standard `PurseConflict` response. :http:statuscode:`404 Not found`: - Either the denomination key is not recognized (expired or invalid) or - the wire type is not recognized. + FIXME: when exactly does this happen? :http:statuscode:`409 Conflict`: The deposit operation has either failed because a coin has insufficient residual value, or because the same public key of the coin has been -- cgit v1.2.3 From bb21dbf3a97083964a0172b4e6516eb34cbd48d5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 25 May 2021 21:33:41 +0200 Subject: spec message for denomination expired/revoked/too-early signature (#6889) --- core/api-common.rst | 14 +++++++++++ core/api-exchange.rst | 69 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/core/api-common.rst b/core/api-common.rst index 97ce02d1..9c23b905 100644 --- a/core/api-common.rst +++ b/core/api-common.rst @@ -1038,6 +1038,20 @@ within the }; +.. _TALER_DenominationExpiredAffirmationPS: +.. sourcecode:: c + + struct TALER_DenominationExpiredAffirmationPS { + /** + * purpose.purpose = TALER_SIGNATURE_EXCHANGE_GENERIC_DENOMINATIN_EXPIRED + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_TIME_AbsoluteNBO timestamp; + char operation[8]; + struct GNUNET_HashCode h_denom_pub; + }; + + .. _TALER_ReserveCloseConfirmationPS: .. sourcecode:: c diff --git a/core/api-exchange.rst b/core/api-exchange.rst index 8e41447a..ce482837 100644 --- a/core/api-exchange.rst +++ b/core/api-exchange.rst @@ -1401,12 +1401,45 @@ exchange. The balance of the reserve is not sufficient to withdraw a coin of the indicated denomination. The response is `WithdrawError` object. :http:statuscode:`410 Gone`: - The requested denomination key is no longer valid. It either expired or was revoked. - :http:statuscode:`412 Precondition failed`: - The requested denomination key is not yet valid. It is too early to withdraw. + The requested denomination key is not yet or no longer valid. + It either before the validity start, past the expiration or was revoked. The response is a + `DenominationExpiredMessage`. Clients must evaluate + the error code provided to understand which of the + cases this is and handle it accordingly. **Details:** + .. ts:def:: DenominationExpiredMessage + + interface DenominationExpiredMessage { + + // Taler error code. Note that beyond + // expiration this message format is also + // used if the key is not yet valid, or + // has been revoked. + code: number; + + // Signature by the exchange over a + // `TALER_DenominationExpiredAffirmationPS`. + // Must have purpose ``TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED``. + exchange_sig: EddsaSignature; + + // Public key of the exchange used to create + // the 'exchange_sig. + exchange_pub: EddsaPublicKey; + + // Hash of the denomination public key that is unknown. + h_denom_pub: HashCode; + + // When was the signature created. + timestamp: Timestamp; + + // What kind of operation was requested that now + // failed? + oper: String; + } + + .. ts:def:: WithdrawRequest interface WithdrawRequest { @@ -1559,6 +1592,12 @@ denomination. The fields of the response are the same in both cases. The request should not be repeated again with this coin. In this case, the response is a `DepositDoubleSpendError`. + :http:statuscode:`410 Gone`: + The requested denomination key is not yet or no longer valid. + It either before the validity start, past the expiration or was revoked. The response is a + `DenominationExpiredMessage`. Clients must evaluate + the error code provided to understand which of the + cases this is and handle it accordingly. **Details:** @@ -1993,6 +2032,12 @@ the API during normal operation. can be decided by looking at the error code (``TALER_EC_EXCHANGE_MELT_INSUFFICIENT_FUNDS`` or ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``). The response is `MeltForbiddenResponse` in both cases. + :http:statuscode:`410 Gone`: + The requested denomination key is not yet or no longer valid. + It either before the validity start, past the expiration or was revoked. The response is a + `DenominationExpiredMessage`. Clients must evaluate + the error code provided to understand which of the + cases this is and handle it accordingly. **Details:** @@ -2119,6 +2164,12 @@ the API during normal operation. rather verbose, as it includes most of the original /refresh/melt request, but of course expected to be primarily used for diagnostics. The response body is a `RevealConflictResponse`. + :http:statuscode:`410 Gone`: + The requested denomination key (for the fresh coins) is not yet or no longer valid. + It either before the validity start, past the expiration or was revoked. The response is a + `DenominationExpiredMessage`. Clients must evaluate + the error code provided to understand which of the + cases this is and handle it accordingly. **Details:** @@ -2263,8 +2314,7 @@ in using this API. :http:statuscode:`401 Unauthorized`: The coin's signature is invalid. :http:statuscode:`404 Not found`: - The denomination key is not in the set of denomination - keys where emergency pay back is enabled, or the blinded + The denomination key is unknown, or the blinded coin is not known to have been withdrawn. If the denomination key is unknown, the response will be a `DenominationUnkownMessage`. @@ -2273,8 +2323,15 @@ in using this API. residual value, or because the same public key of the coin has been previously used with a different denomination. Which case it is can be decided by looking at the error code - (``TALER_EC_EXCHANGE_RECOUP_COIN_BALANCE_ZERO`` or ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``). + (``TALER_EC_EXCHANGE_RECOUP_COIN_BALANCE_ZERO`` or + ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``). The response is a `DepositDoubleSpendError`. + :http:statuscode:`410 Gone`: + The requested denomination key is not yet or no longer valid. + It either before the validity start, past the expiration or was not yet revoked. The response is a + `DenominationExpiredMessage`. Clients must evaluate + the error code provided to understand which of the + cases this is and handle it accordingly. **Details:** -- cgit v1.2.3 From fc89f030e20fdc830892c3e0219d82b72c484bb9 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 16 Jun 2021 22:10:00 +0200 Subject: clarify merchant api --- core/api-merchant.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/api-merchant.rst b/core/api-merchant.rst index ddd612ca..ecb4d4e7 100644 --- a/core/api-merchant.rst +++ b/core/api-merchant.rst @@ -2110,10 +2110,11 @@ Informing the backend about incoming wire transfers resolve it. So in that respect, 202 is the right status code as more work remains to be done for a final resolution.) :http:statuscode:`404 Not found`: - The instance is unknown to the exchange. + The instance or account are unknown to the exchange. :http:statuscode:`409 Conflict`: The wire transfer identifier is already known to us, but for a different amount, - wire method or exchange. + wire method or exchange, or the amount reported by the exchange differs from + the amount reported by the merchant. :http:statuscode:`502 Bad gateway`: The exchange returned an error when we asked it about the ``GET /transfer`` status for this wire transfer. Details of the exchange error are returned. -- cgit v1.2.3 From f8f1f3ace882f6cf22f564b6e398c9efab3effa1 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 19 Jun 2021 04:46:35 +0200 Subject: add man page for fakebank --- manpages/taler-fakebank-run.1.rst | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 manpages/taler-fakebank-run.1.rst diff --git a/manpages/taler-fakebank-run.1.rst b/manpages/taler-fakebank-run.1.rst new file mode 100644 index 00000000..240c7142 --- /dev/null +++ b/manpages/taler-fakebank-run.1.rst @@ -0,0 +1,62 @@ +taler-fakebank-run(1) +##################### + +.. only:: html + + Name + ==== + + **taler-fakebank-run** - run Taler "in memory" bank (with RESTful API) + +Synopsis +======== + +**taler-fakebank-run** +[**-c** *FILENAME* | **––config=**\ ‌\ *FILENAME*] +[**-h** | **––help**] +[**-L** *LOGLEVEL* | **––loglevel=**\ ‌\ *LOGLEVEL*] +[**-l** *FILENAME* | **––logfile=**\ ‌\ *FILENAME*] +[**-n** *N* | **––num-threads=**\ \ *N*] +[**-v** | **––version**] + +Description +=========== + +**taler-fakebank-run** is a command-line tool to run a Taler "bank" API (HTTP REST service). The program is useful to provide the bank functionality for benchmarking or testing when LibEuFin is unavailable or otherwise unsuitable. + +Its options are as follows: + +**-C** \| **––connection-close** + Force each HTTP connection to be closed after each request (useful in + combination with **-f** to avoid having to wait for nc to time out). + +**-c** *FILENAME* \| **––config=**\ ‌\ *FILENAME* + Use the configuration and other resources for the merchant to operate + from FILENAME. + +**-h** \| **––help** + Print short help on options. + +**-L** *LOGLEVEL* \| **––loglevel=**\ ‌\ *LOGLEVEL* + Specifies the log level to use. Accepted values are: ``DEBUG``, ``INFO``, + ``WARNING``, ``ERROR``. + +**-l** *FILENAME* \| **––logfile=**\ ‌\ *FILENAME* + Send logging output to *FILENAME*. + +**-n** *N* \| **––num-threads=**\ \ *N* + Use *N* threads in the thread pool. + +**-v** \| **––version** + Print version information. + +See Also +======== + +taler-exchange-httpd(1), taler.conf(5). + +Bugs +==== + +Report bugs by using https://bugs.taler.net or by sending electronic +mail to . -- cgit v1.2.3 From 50f20abb76c227b94fa68e7e8c6879c29de32464 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 22 Jun 2021 15:32:56 +0200 Subject: fix docs --- frags/installing-debian.rst | 2 +- frags/installing-ubuntu.rst | 2 +- taler-auditor-manual.rst | 22 ++++++++++++++++++++-- taler-exchange-manual.rst | 4 ++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/frags/installing-debian.rst b/frags/installing-debian.rst index 2c36e98a..5f8c488b 100644 --- a/frags/installing-debian.rst +++ b/frags/installing-debian.rst @@ -41,7 +41,7 @@ into your keyring and update the package lists: .. code-block:: console - # wget -O - https://taler.net/static/taler-systems.gpg.key | apt-key add - + # wget -O - https://taler.net/taler-systems.gpg.key | apt-key add - # apt update .. note:: diff --git a/frags/installing-ubuntu.rst b/frags/installing-ubuntu.rst index a72579da..af34d358 100644 --- a/frags/installing-ubuntu.rst +++ b/frags/installing-ubuntu.rst @@ -26,7 +26,7 @@ into your keyring and update the package lists: .. code-block:: console - # wget -O - https://taler.net/static/taler-systems.gpg.key | apt-sign add - + # wget -O - https://taler.net/taler-systems.gpg.key | apt-key add - # apt update .. note:: diff --git a/taler-auditor-manual.rst b/taler-auditor-manual.rst index 3fe835c9..6cd30221 100644 --- a/taler-auditor-manual.rst +++ b/taler-auditor-manual.rst @@ -209,13 +209,31 @@ To install the Taler auditor, you can now simply run: .. code-block:: console - # apt install taler-auditor + # apt install -t sid taler-auditor For the auditor, you must manually configure access to the exchange database, the HTTP reverse proxy (typically with TLS certificates) and offline signing. Sample configuration files for the HTTP reverse proxy can be found in -``/etc/taler-exchange/``. +``/etc/taler-auditor/``. + + +Installing the GNU Taler binary packages on Ubuntu +-------------------------------------------------- + +.. include:: frags/installing-ubuntu.rst + +To install the Taler exchange, you can now simply run: + +.. code-block:: console + + # apt install -t focal-fossa taler-exchange + +For the auditor, you must manually configure access to the exchange database, +the HTTP reverse proxy (typically with TLS certificates) and offline signing. + +Sample configuration files for the HTTP reverse proxy can be found in +``/etc/taler-auditor/``. Configuration diff --git a/taler-exchange-manual.rst b/taler-exchange-manual.rst index a143d498..4e2863d8 100644 --- a/taler-exchange-manual.rst +++ b/taler-exchange-manual.rst @@ -319,7 +319,7 @@ To install the Taler exchange, you can now simply run: .. code-block:: console - # apt install taler-exchange + # apt install -t sid taler-exchange Note that the package does not perform any configuration work except for setting up the various users and the systemd service scripts. You still must @@ -340,7 +340,7 @@ To install the Taler exchange, you can now simply run: .. code-block:: console - # apt install taler-exchange + # apt install -t focal-fossa taler-exchange Note that the package does not perform any configuration work except for setting up the various users and the systemd service scripts. You still must -- cgit v1.2.3 From 15fb7c05ab5e825aa7f0dd6248ef8a3a60a6f621 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 22 Jun 2021 15:37:19 +0200 Subject: simplify/cleaner docs --- frags/installing-ubuntu.rst | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/frags/installing-ubuntu.rst b/frags/installing-ubuntu.rst index af34d358..12831043 100644 --- a/frags/installing-ubuntu.rst +++ b/frags/installing-ubuntu.rst @@ -2,21 +2,11 @@ To install the GNU Taler Ubuntu packages, first ensure that you have the right Ubuntu distribution. At this time, the packages are built for Ubuntu 20.04 LTS (Focal Fossa). -A typical ``/etc/apt/sources.list`` file for this setup +A typical ``/etc/apt/sources.list.d/taler.list`` file for this setup would look like this: .. code-block:: - deb http://ch.archive.ubuntu.com/ubuntu/ focal main restricted - deb http://ch.archive.ubuntu.com/ubuntu/ focal-updates main restricted - deb http://ch.archive.ubuntu.com/ubuntu/ focal-security main restricted - deb http://ch.archive.ubuntu.com/ubuntu/ focal universe restricted - deb http://ch.archive.ubuntu.com/ubuntu/ focal-updates universe restricted - deb http://ch.archive.ubuntu.com/ubuntu/ focal-security universe restricted - deb http://ch.archive.ubuntu.com/ubuntu/ focal multiverse restricted - deb http://ch.archive.ubuntu.com/ubuntu/ focal-updates multiverse restricted - deb http://ch.archive.ubuntu.com/ubuntu/ focal-security multiverse restricted - deb https://deb.taler.net/apt/ubuntu/ focal-fossa main The last line is crucial, as it adds the GNU Taler packages. -- cgit v1.2.3 From 00793206842ff1c6824a2ae0010090dab3815138 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 22 Jun 2021 15:38:14 +0200 Subject: simplify/cleaner docs --- frags/installing-debian.rst | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frags/installing-debian.rst b/frags/installing-debian.rst index 5f8c488b..bd6f8796 100644 --- a/frags/installing-debian.rst +++ b/frags/installing-debian.rst @@ -23,15 +23,11 @@ set your ``/etc/apt/preferences`` as follows: Pin: release l=Debian-Security Pin-Priority: 1000 -A typical ``/etc/apt/sources.list`` file for this setup +A typical ``/etc/apt/sources.list.d/taler.list`` file for this setup would look like this: .. code-block:: - deb http://ftp.ch.debian.org/debian/ buster main - deb http://security.debian.org/debian-security buster/updates main - deb http://ftp.ch.debian.org/debian/ testing main - deb http://ftp.ch.debian.org/debian/ unstable main deb https://deb.taler.net/apt/debian sid main The last line is crucial, as it adds the GNU Taler packages. -- cgit v1.2.3 From f47314fe9460324d5bf331d0dc71d5358c6d8e08 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 22 Jun 2021 19:34:20 +0200 Subject: update manuals --- taler-auditor-manual.rst | 17 +++++++++++++++-- taler-exchange-manual.rst | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/taler-auditor-manual.rst b/taler-auditor-manual.rst index 6cd30221..c6d73c0e 100644 --- a/taler-auditor-manual.rst +++ b/taler-auditor-manual.rst @@ -227,7 +227,7 @@ To install the Taler exchange, you can now simply run: .. code-block:: console - # apt install -t focal-fossa taler-exchange + # apt install -t focal-fossa taler-auditor For the auditor, you must manually configure access to the exchange database, the HTTP reverse proxy (typically with TLS certificates) and offline signing. @@ -250,6 +250,19 @@ This section discusses configuration options related to the auditor. .. include:: frags/using-taler-config.rst +.. _SetupBaseUrl: + +Initial configuration +--------------------- + +You need to tell the Taler auditor configuration where the +REST API of the auditor will be available to the public: + +.. code-block:: console + + # taler-config -s auditor -o BASE_URL -V https://auditor.example.com/ + + .. _AuditorKeys: Keys @@ -312,7 +325,7 @@ choosing the backend, it is mandatory to supply the connection string - via an environment variable: ``TALER_AUDITORDB_POSTGRES_CONFIG``. -- via configuration option ``CONFIG``, under section ``[auditordb-BACKEND]``. +- via configuration option ``CONFIG``, under section ``[auditordb-$BACKEND]``. For example, the demo exchange is configured as follows: .. code-block:: ini diff --git a/taler-exchange-manual.rst b/taler-exchange-manual.rst index 4e2863d8..c28ddaa2 100644 --- a/taler-exchange-manual.rst +++ b/taler-exchange-manual.rst @@ -442,7 +442,7 @@ choosing the backend, it is mandatory to supply the connection string - via an environment variable: ``TALER_EXCHANGEDB_POSTGRES_CONFIG``. -- via configuration option ``CONFIG``, under section ``[exchangedb-BACKEND]``. +- via configuration option ``CONFIG``, under section ``[exchangedb-$BACKEND]``. For example, the demo exchange is configured as follows: .. code-block:: ini -- cgit v1.2.3