summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-05-05 17:45:09 +0200
committerChristian Grothoff <christian@grothoff.org>2021-05-05 17:45:09 +0200
commit20d68685eee8a3e99b59ed913ad3389f487e3e4b (patch)
tree085e39c03cb70c6bfcb419d7da0b02e0a5000f42 /core
parent446fec10a4e1fb7923f6732f80290fcb6c7bc1af (diff)
downloaddocs-20d68685eee8a3e99b59ed913ad3389f487e3e4b.tar.gz
docs-20d68685eee8a3e99b59ed913ad3389f487e3e4b.tar.bz2
docs-20d68685eee8a3e99b59ed913ad3389f487e3e4b.zip
update spec for W2W
Diffstat (limited to 'core')
-rw-r--r--core/api-common.rst182
-rw-r--r--core/api-exchange.rst735
2 files changed, 609 insertions, 308 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-priv>`.
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-priv>`.
- 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.