summaryrefslogtreecommitdiff
path: root/design-documents/046-mumimo-contracts.rst
diff options
context:
space:
mode:
Diffstat (limited to 'design-documents/046-mumimo-contracts.rst')
-rw-r--r--design-documents/046-mumimo-contracts.rst709
1 files changed, 709 insertions, 0 deletions
diff --git a/design-documents/046-mumimo-contracts.rst b/design-documents/046-mumimo-contracts.rst
new file mode 100644
index 00000000..8cb35316
--- /dev/null
+++ b/design-documents/046-mumimo-contracts.rst
@@ -0,0 +1,709 @@
+DD 46: Contract Format v1
+#########################
+
+Summary
+=======
+
+The contract v1 format enables a multitude of advanced interactions between
+merchants and wallets, including donations, subscriptions, coupons, currency
+exchange and more.
+
+Motivation
+==========
+
+The existing v0 contract format is too simplistic to
+support many frequenly requested types of contracts.
+
+Requirements
+============
+
+We want Taler to support various interesting use-cases:
+
+ - Unlinkable, uncopyable subscriptions without accounts (reader can pay with
+ Taler to subscribe to online publication, read unlimited number of
+ articles during a certain period, transfer subscription to other devices,
+ maintain unlinkability / full anonymity amongst all anonymous
+ subscribers).
+
+ - Coupons, discounts and stamps -- like receiving a discount on a product,
+ product basket or subscription -- based on previous purchase(s). Again,
+ with unlinkability and anonymity (modulo there being other users eligible
+ for the discount).
+
+ - Subscription tokens lost (due to loss of device without backup) should
+ be recoverable from any previous backup of the subscription.
+
+ - Currency conversion, that is exchanging one currency for another.
+
+ - Donations, including privacy-preserving tax receipts that prove that the
+ user donated to an entity that is eligible for tax-deductions but without
+ revealing which entity the user donated to. At the same time, the entity
+ issuing the tax receipt must be transparent (to the state) with respect to
+ the amount of tax-deductable donations it has received.
+
+ - Throttled political donations where each individual is only allowed to
+ donate anonymously up to a certain amount per year or election cycle.
+
+ - Unlinkable gifts -- enabling the purchase of digital goods (such as
+ articles, albums, etc.) to be consumed by a third party. For example, a
+ newspaper subscription may include a fixed number of articles that can be
+ gifted to others each week, all while maintaining unlinkability and
+ anonymity between the giver and the recipient.
+
+ - Temporally-constrained, unlinkable event ticketing. Allowing visitors to
+ use Taler to purchase a ticket for an event. This ticket grants entry and
+ exit privileges to the event location during a specified time window, while
+ preserving the anonymity of the ticket holder (within the group of all the
+ ticket holders).
+
+ - Event deposit systems. A deposit mechanism for events where customers
+ receive a token alongside their cup or plate, which they are expected to
+ return. This system validates that the cup or plate was legitimately
+ acquired (i.e., not brought from home or stolen from a stack of dirty items)
+ and incentivizes return after use.
+
+
+Proposed Solution
+=================
+
+Merchants will also blindly sign tokens (not coins) to indicate the
+eligibility of a user for certain special offers. Contracts will be modified
+to allow requiring multiple inputs (to be *provisioned* to the merchant) and
+multiple outputs (to be *yielded* by the merchant). The wallet will then allow
+the user to select between the choices that the user could pay for, or possibly
+make an automatic choice if the correct choice is obvious. One output option is
+blindly signed coins from another exchange, possibly in a different currency.
+Another output option is blindly signed donation receipts from a DONation
+AUthority (DONAU). Subscriptions can be modeled by requiring the wallet to
+provision a token of the same type that is also yielded by the contract. For
+security, payments using subscription tokens (and possibly certain other special
+tokens?) will be limited to a list of domains explicitly defined as trusted by
+the token issuer. When paying for a contract, the wallet must additionally sign
+over the selected sub-contract index and a hash committing it to the blinded
+envelopes (if any). The merchant backend will (probably?) need to be changed
+to truly support multiple currencies (ugh).
+
+.. _contract-terms-v1:
+
+New Contract Terms Format
+-------------------------
+
+The contract terms v1 will have the following structure:
+
+.. ts:def:: ContractTermsV1
+
+ interface ContractTermsV1 {
+
+ // This is version 1, the previous contract terms SHOULD
+ // be indicated using "0", but in v0 specifying the version
+ // is optional.
+ version: "1";
+
+ // Unique, free-form identifier for the proposal.
+ // Must be unique within a merchant instance.
+ // For merchants that do not store proposals in their DB
+ // before the customer paid for them, the ``order_id`` can be used
+ // by the frontend to restore a proposal from the information
+ // encoded in it (such as a short product identifier and timestamp).
+ order_id: string;
+
+ // Price to be paid for the transaction. Could be 0.
+ // The price is in addition to other instruments,
+ // such as rations and tokens.
+ // The exchange will subtract deposit fees from that amount
+ // before transferring it to the merchant.
+ price: Amount;
+
+ // URL where the same contract could be ordered again (if
+ // available). Returned also at the public order endpoint
+ // for people other than the actual buyer (hence public,
+ // in case order IDs are guessable).
+ public_reorder_url?: string;
+
+ // Time when this contract was generated.
+ timestamp: Timestamp;
+
+ // After this deadline, the merchant won't accept payments for the contract.
+ pay_deadline: Timestamp;
+
+ // Transfer deadline for the exchange. Must be in the
+ // deposit permissions of coins used to pay for this order.
+ wire_transfer_deadline: Timestamp;
+
+ // Merchant's public key used to sign this proposal; this information
+ // is typically added by the backend. Note that this can be an ephemeral key.
+ merchant_pub: EddsaPublicKey;
+
+ // Base URL of the (public!) merchant backend API.
+ // Must be an absolute URL that ends with a slash.
+ merchant_base_url: string;
+
+ // More info about the merchant (same as in v0).
+ merchant: Merchant;
+
+ // Human-readable description of the contract.
+ summary: string;
+
+ // Map from IETF BCP 47 language tags to localized summaries.
+ summary_i18n?: { [lang_tag: string]: string };
+
+ // URL that will show that the order was successful after
+ // it has been paid for. Optional. When POSTing to the
+ // merchant, the placeholder "${ORDER_ID}" will be
+ // replaced with the actual order ID (useful if the
+ // order ID is generated server-side and needs to be
+ // in the URL).
+ // Note that this placeholder can only be used once.
+ // Either fulfillment_url or fulfillment_message must be specified.
+ fulfillment_url?: string;
+
+ // Message shown to the customer after paying for the order.
+ // Either fulfillment_url or fulfillment_message must be specified.
+ fulfillment_message?: string;
+
+ // Map from IETF BCP 47 language tags to localized fulfillment
+ // messages.
+ fulfillment_message_i18n?: { [lang_tag: string]: string };
+
+ // List of products that are part of the purchase (see `Product`).
+ products: Product[];
+
+ // After this deadline has passed, no refunds will be accepted.
+ refund_deadline: Timestamp;
+
+ // Specifies for how long the wallet should try to get an
+ // automatic refund for the purchase. If this field is
+ // present, the wallet should wait for a few seconds after
+ // the purchase and then automatically attempt to obtain
+ // a refund. The wallet should probe until "delay"
+ // after the payment was successful (i.e. via long polling
+ // or via explicit requests with exponential back-off).
+ //
+ // In particular, if the wallet is offline
+ // at that time, it MUST repeat the request until it gets
+ // one response from the merchant after the delay has expired.
+ // If the refund is granted, the wallet MUST automatically
+ // recover the payment. This is used in case a merchant
+ // knows that it might be unable to satisfy the contract and
+ // desires for the wallet to attempt to get the refund without any
+ // customer interaction. Note that it is NOT an error if the
+ // merchant does not grant a refund.
+ auto_refund?: RelativeTime;
+
+ // Delivery location for (all!) products (same as in v0).
+ delivery_location?: Location;
+
+ // Time indicating when the order should be delivered.
+ // May be overwritten by individual products.
+ delivery_date?: Timestamp;
+
+ // Nonce generated by the wallet and echoed by the merchant
+ // in this field when the proposal is generated.
+ // Note: required in contract, absent in order!
+ nonce: string;
+
+ // Array of possible specific contracts the wallet/customer
+ // may choose from by selecting the respective index when
+ // signing the deposit confirmation.
+ choices: ContractChoice[];
+
+ // Map from token family slugs to meta data about the
+ // respective token family.
+ token_families: { [token_family_slug: string]: TokenFamily };
+
+ // Extra data that is only interpreted by the merchant frontend.
+ // Useful when the merchant needs to store extra information on a
+ // contract without storing it separately in their database.
+ extra?: any;
+
+ // Maximum total deposit fee accepted by the merchant for this contract.
+ max_fee: Amount;
+
+ // Exchanges that the merchant accepts for this currency.
+ exchanges: Exchange[];
+
+ }
+
+.. ts:def:: ContractChoice
+
+ interface ContractChoice {
+
+ // List of inputs the wallet must provision (all of them) to
+ // satisfy the conditions for the contract.
+ inputs: ContractInput[];
+
+ // List of outputs the merchant promises to yield (all of them)
+ // once the contract is paid.
+ outputs: ContractOutput[];
+
+ }
+
+.. ts:def:: ContractInput
+
+ type ContractInput =
+ | ContractInputRation
+ | ContractInputToken;
+
+.. ts:def:: ContractInputRation
+
+ interface ContractInputRation {
+
+ type: "coin";
+
+ // Price to be paid for the transaction.
+ price: Amount;
+
+ // FIXME-DOLD: do we want to move this into a 'details'
+ // sub-structure as done with tokens below?
+ class: "ration";
+
+ // Base URL of the ration authority.
+ ration_authority_url: string;
+
+ };
+
+.. ts:def:: ContractInputToken
+
+ interface ContractInputToken {
+
+ type: "token";
+
+ // Slug of the token family in the
+ // 'token_families' map on the top-level.
+ token_family_slug: string;
+
+ // Number of tokens of this type required.
+ // Defaults to one if the field is not provided.
+ number?: Integer;
+
+ };
+
+.. ts:def:: ContractOutput
+
+ type ContractOutput =
+ | ContractOutputCoin
+ | ContractOutputTaxReceipt
+ | ContractOutputToken;
+
+.. ts:def:: ContractOutputCoin
+
+ interface ContractOutputCoin {
+
+ type: "coins";
+
+ // Amount of coins that will be yielded.
+ // This excludes any applicable withdraw fees.
+ brutto_yield: Amount;
+
+ // Base URL of the exchange that will issue the
+ // coins.
+ exchange_url: string;
+
+ };
+
+.. ts:def:: ContractOutputTaxReceipt
+
+ interface ContractOutputTaxReceipt {
+
+ type: "tax-receipt";
+
+ // Base URL of the donation authority that will
+ // issue the tax receipt.
+ donau_url: string;
+
+ };
+
+.. ts:def:: ContractOutputToken
+
+ interface ContractOutputToken {
+
+ type: "token";
+
+ // Slug of the token family in the
+ // 'token_families' map on the top-level.
+ token_family_slug: string;
+
+ // Start of the validity period of the token.
+ valid_after: Timestamp;
+
+ // Number of tokens to be yelded.
+ // Defaults to one if the field is not provided.
+ number?: Integer;
+
+ }
+
+.. ts:def:: TokenClass
+
+ type TokenClass =
+ | TokenClassSubscription
+ | TokenClassDiscount
+
+.. ts:def:: TokenClassSubscription
+
+ interface TokenClassSubscription {
+
+ class: "subscription";
+
+ // Array of domain names where this subscription
+ // can be safely used (e.g. the issuer warrants that
+ // these sites will re-issue tokens of this type
+ // if the respective contract says so). May contain
+ // "*" for any domain or subdomain.
+ trusted_domains: string[];
+
+ };
+
+.. ts:def:: TokenClassDiscount
+
+ interface TokenClassDiscount {
+
+ class: "discount";
+
+ // Array of domain names where this discount token
+ // is intended to be used. May contain "*" for any
+ // domain or subdomain. Users should be warned about
+ // sites proposing to consume discount tokens of this
+ // type that are not in this list that the merchant
+ // is accepting a coupon from a competitor and thus
+ // may be attaching different semantics (like get 20%
+ // discount for my competitors 30% discount token).
+ expected_domains: string[];
+
+ };
+
+
+.. ts:def:: TokenFamily
+
+ interface TokenFamily {
+
+ // Human-readable name of the token family.
+ name: string;
+
+ // Human-readable description of the semantics of
+ // this token family (for display).
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized descriptions.
+ description_i18n?: { [lang_tag: string]: string };
+
+ // Public keys used to validate tokens issued by this token family.
+ keys: TokenSignerPublicKeyGroup;
+
+ // Class-specific information of the token
+ details: TokenClass;
+
+ // Must a wallet understand this token type to
+ // process contracts that consume or yield it?
+ critical: boolean;
+
+ // Number of tokens issued according to ASS authority
+ // FIXME: this is still rather speculative in the design...
+ ass?: Integer;
+
+ // Signature affirming sum of token issuance deposit (?) fees
+ // collected by an exchange according to the ASS authority.
+ // FIXME: this is still rather speculative in the design...
+ ass_cost?: Amount;
+
+ // Signature affirming the ass by the ASS authority.
+ // FIXME: this is still rather speculative in the design...
+ ass_sig?: EddsaSignature;
+
+ };
+
+
+.. ts:def:: TokenSignerPublicKeyGroup
+
+ type TokenSignerPublicKeyGroup =
+ | TokenSignerPublicKeyGroupRsa
+ | TokenSignerPublicKeyGroupCs;
+
+.. ts:def:: TokenSignerPublicKeyGroupRsa
+
+ interface TokenSignerPublicKeyGroupRsa {
+ cipher: "RSA";
+
+ public_keys: {
+ // RSA public key.
+ rsa_pub: RsaPublicKey;
+
+ // Start time of this key's validity period.
+ valid_after?: Timestamp;
+
+ // End time of this key's validity period.
+ valid_before: Timestamp;
+ }[];
+ }
+
+.. ts:def:: TokenSignerPublicKeyGroupCs
+
+ interface TokenSignerPublicKeyGroupCs {
+ cipher: "CS";
+
+ public_keys: {
+ // CS public key.
+ cs_pub: Cs25519Point;
+
+ // Start time of this key's validity period.
+ valid_after?: Timestamp;
+
+ // End time of this key's validity period.
+ valid_before: Timestamp;
+ }[];
+ }
+
+
+Alternative Contracts
+---------------------
+
+The contract terms object may contain any number of alternative contracts that
+the user must choose between. The alternatives can differ by inputs, outputs
+or other details. The wallet must filter the contracts by those that the user
+can actually pay for, and move those that the user could currently not pay for
+to the end of the rendered list. Similarly, the wallet must move up the
+cheaper contracts, so if a contract has a definitively lower price and
+consumes an available discount token, that contract should be moved up in the
+list.
+
+Which specific alternative contract was chosen by the user is indicated in the
+``choice_index`` field of the :ref:`TALER_DepositRequestPS <taler_depositrequestps>`.
+
+
+Output Commitments
+------------------
+
+When a contract has outputs, the wallet must send an array of blinded tokens,
+coins or tax receipts together with the payment request. The order in the
+array must match the order in the outputs field of the contract. For currency
+outputs, one array element must include all of the required planchets for a
+batch withdrawal, but of course not the reserve signature.
+
+ .. note::
+
+ We can probably spec this rather nicely if we first change the
+ batch-withdraw API to only use a single reserve signature.
+
+This array of blinded values is hashed to create the output commitment hash
+(``h_outputs``) in the :ref:`TALER_DepositRequestPS <taler_depositrequestps>`.
+
+
+
+Subscriptions
+-------------
+
+The user buys a subscription (and possibly at the same time an article) using
+currency and the contract yields an additional subscription token as an
+output. Active subscriptions are listed below the currencies in the wallet
+under a new heading. Subscriptions are never auto-renewing, if the user wants
+to extend the subscription they can trivially pay for it with one click.
+
+When a contract consumes and yields exactly one subscription
+token of the same type in a trusted domain, the wallet may automatically
+approve the transaction without asking the user for confirmation (as it is free).
+
+The token expiration for a subscription can be past the "end date" to enable a
+previous subscription to be used to get a discount on renewing the
+subscription. The wallet should show applicable contracts with a lower price
+that only additionally consume subscription tokens after their end date before
+higher-priced alternative offers.
+
+Subscription tokens are "critical" in that a wallet implementation must
+understand them before allowing a user to interact with this class of token.
+Subscription token secrets should be derived from a master secret associated
+with the subscription, so that the private keys are recoverable from backup.
+To obtain the blind signatures, a merchant must offer an endpoint where
+one can submit the public key of the N-1 subscription token and obtain the
+blinded signature over the N-th subscription token. The wallet can then
+effectively recover the subscription from backup using a binary search.
+
+The merchant SPA should allow the administrator to create (maybe update) and
+delete subscriptions. Each subscription is identified by a subscription
+label and includes a validity period.
+
+The merchant backend must then automatically manage (create, use, delete) the
+respective signing keys. When creating an order, the frontend can just refer
+to the subscription label (and possibly a start date) in the inputs or
+outputs. The backend should then automatically substitute this with the
+respective cryptographic fields for the respective time period and
+subscription label.
+
+
+
+
+Discounts
+---------
+
+To offer a discount based on one or more previous purchases, a merchant must
+yield some discount-specific token as an output with the previous purchase,
+and then offer an alternative contract with a lower price that consumes currency
+and the discount token. The wallet should show contracts with a lower price that
+only additionally consume discount tokens
+
+The merchant SPA should allow the administrator to create (maybe update) and
+delete discount tokens. Each discount token is identified by a discount
+label and includes an expiration time or validity duration.
+
+The merchant backend must then automatically manage (create, use, delete) the
+respective signing keys. When creating an order, the frontend can just refer
+to the discount token label in the inputs or outputs. The backend should then
+automatically substitute this with the respective cryptographic fields for the
+respective discount token.
+
+
+Donation Authority
+------------------
+
+A donation authority (DONAU) implements a service that is expected to be run
+by a government authority that determines eligibility for tax deduction. A
+DONAU blindly signs tax receipts using a protocol very close to that of the
+Taler exchange's withdraw protocol, except that the reserves are not filled
+via wire transfers but instead represent accounts of the organizations
+eligible to issue tax deduction receipts. These accounts are bascially
+expected to have only negative balances, but the DONAU may have a
+per-organization negative balance limit to cap tax deduction receipt
+generation to a plausible account. DONAU administators are expected to be
+able to add, update or remove these accounts using a SPA. Tax receipts
+are blindly signed by keys that always have a usage period of one calendar
+year.
+
+A stand-alone app for tax authorities can scan QR codes representing DONAU
+signatures to validate that a given tax payer has donated a certain amount.
+As RSA signatures are typically very large and a single donation may require
+multiple blind signatures, CS blind signatures must also be supported. To
+avoid encoding the public keys, QR codes with tax receipts should reference
+the DONAU, the year and the amount, but not the specific public key. A
+single donation may nevertheless be rendered using multiple QR codes.
+
+Revocations, refresh, deposits, age-restrictions and other exchange features
+are not applicable for a DONAU.
+
+The merchant SPA should allow the administrator to manage DONAU accounts in
+the merchant backend. Each DONAU account includes a base URL and a private
+signing key for signing the requests to the DONAU on behalf of the eligible
+organization.
+
+When creating an order, the frontend must specify a configured DONAU base URL
+in the outputs. The backend should then automatically interact with the DONAU
+when the wallet supplies the payment request with the blinded tax receipts.
+The DONAU interaction must only happen after the exchange confirmed that the
+contract was successfully paid. A partial error must be returned if the
+exchange interaction was successful but the DONAU interaction failed. In this
+case, the fulfillment action should still be triggered, but the wallet should
+display a warning that the donation receipt could not be obtained. The wallet
+should then re-try the payment (in the background with an exponential
+back-off) to possibly obtain the tax receipt at a later time.
+
+
+Tax Receipts
+------------
+
+Tax receipts differ from coins and tokens in that what is blindly signed over
+should be the taxpayer identification number of the tax payer. The format of
+the taxpayer identification number should simply be a string, with the rest
+being defined by the national authority. The DONAU should indicate in its
+``/config`` response what format this string should have, using possibly both
+an Anastasis-style regex and an Anastasis-style function name (to check things
+like checksums that cannot be validated using a regex). Wallets must then
+validate the regex (if given) and if possible should implement the
+Anastasis-style logic.
+
+Wallets should collect tax receipts by year and offer an
+export functionality. The export should generate either
+
+ (a) a JSON file,
+ (b) a PDF (with QR codes), or
+ (c) a series of screens with QR codes.
+
+Wallets may only implement some of the above options due to resource
+constraints.
+
+The documents should encode the taxpayer ID, the amount and the DONAU
+signature (including the year, excluding the exact public key as there should
+only be one possible).
+
+
+Rationing (future work)
+-----------------------
+
+If per-capita rationing must be imposed on certain transactions, a rationing
+authority (RA) must exist that identifies each eligible human and issues that
+human a number of ration coins for the respective rationing period. An RA
+largely functions like a regular exchange, except that eligible humans will
+need to authenticate directly to withdraw rations (instead of transferring
+fiat to an exchange). Merchants selling rationed goods will be (legally)
+required to collect deposit confirmations in proportion to the amount of
+rationed goods sold. A difference to regular exchanges is that RAs do not
+charge any fees. RAs may or may not allow refreshing rations that are about
+to expire for ration coins in the next period.
+
+Once an RA is added to a wallet, it should automatically try to withdraw the
+maximum amount of ration coins it is eligible for. Available rations should be
+shown below the subscriptions by RA (if any).
+
+ ..note::
+
+ RAs are considered an idea for future work and not part of our current timeline.
+
+
+Limited Donations per Capita (future work)
+------------------------------------------
+
+If per-capita limitations must be imposed on anonymous donations (for example
+for donations to political parties), an RA can be used to issue donation
+rations that limit the amount of donations that can be made for the respective
+period.
+
+ ..note::
+
+ RAs are considered an idea for future work and not part of our current timeline.
+
+
+
+Definition of Done
+==================
+
+ - Merchant backend support for multiple currencies
+ - Merchant backend support for consuming and issuing tokens
+ - Merchant SPA support for configuring new tokens of different types
+ - Wallet-core support for various new contract types
+ - Wallet-core filters for feasible contracts and possibly auto-executes subscriptions
+ - Wallet-GUIs (WebEx, Android, iOS) render new contract types
+ - Wallet-GUIs (WebEx, Android, iOS) allow user to select between multiple contracts
+ - Documentation for developers is up-to-date
+ - Token anonymity set size (ASS) authority implemented, documented
+ - Merchants report anonymity set size increases to ASS authority
+ - Wallets process anonymity set size reports from ASS authority
+ - Bachelor thesis written on applications and design
+ - Academic paper written on DONAU (requirements, design, implementation)
+ - DONAU implemented, documented
+ - DONAU receipt validation application implemented
+ - Integration tests exist in wallet-core
+ - Deliverables accepted by EC
+
+While rationing is part of the design, we expect the actual implementation to
+be done much later and thus should not consider it part of the "DONE" part.
+Rationing is complex, especially as a refunded contract should probably also
+refund the ration.
+
+
+Alternatives
+============
+
+The first draft of this DD included the capability of paying with multiple
+currencies for the same contract (for example, USD:1 and EUR:5) plus tokens
+and rations. However, this is very complex, both for wallets (how to display),
+for other merchant APIs (does the refund API have to become multi-currency as
+well?) and there does not seem to be a good business case for it. So for now,
+the price is always only in one currency.
+
+
+Drawbacks
+=========
+
+Significant change, but actually good ratio compared to use-cases covered.
+
+
+Discussion / Q&A
+================
+
+(This should be filled in with results from discussions on mailing lists / personal communication.)