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 --- design-documents/013-peer-to-peer-payments.rst | 85 ++++++++++++++++---------- 1 file changed, 53 insertions(+), 32 deletions(-) (limited to 'design-documents/013-peer-to-peer-payments.rst') 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(-) (limited to 'design-documents/013-peer-to-peer-payments.rst') 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(-) (limited to 'design-documents/013-peer-to-peer-payments.rst') 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 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(-) (limited to 'design-documents/013-peer-to-peer-payments.rst') 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 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(-) (limited to 'design-documents/013-peer-to-peer-payments.rst') 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 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(-) (limited to 'design-documents/013-peer-to-peer-payments.rst') 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(-) (limited to 'design-documents/013-peer-to-peer-payments.rst') 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 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(-) (limited to 'design-documents/013-peer-to-peer-payments.rst') 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(-) (limited to 'design-documents/013-peer-to-peer-payments.rst') 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