diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/crypto/cryptoApi.ts | 6 | ||||
| -rw-r--r-- | src/crypto/cryptoWorker.ts | 18 | ||||
| -rw-r--r-- | src/i18n/de.po | 36 | ||||
| -rw-r--r-- | src/i18n/en-US.po | 36 | ||||
| -rw-r--r-- | src/i18n/fr.po | 36 | ||||
| -rw-r--r-- | src/i18n/it.po | 36 | ||||
| -rw-r--r-- | src/i18n/taler-wallet-webex.pot | 36 | ||||
| -rw-r--r-- | src/types.ts | 18 | ||||
| -rw-r--r-- | src/wallet.ts | 121 | ||||
| -rw-r--r-- | src/webex/messages.ts | 28 | ||||
| -rw-r--r-- | src/webex/notify.ts | 10 | ||||
| -rw-r--r-- | src/webex/pages/confirm-contract.html | 2 | ||||
| -rw-r--r-- | src/webex/pages/confirm-contract.tsx | 41 | ||||
| -rw-r--r-- | src/webex/wxApi.ts | 44 | ||||
| -rw-r--r-- | src/webex/wxBackend.ts | 202 | 
15 files changed, 310 insertions, 360 deletions
diff --git a/src/crypto/cryptoApi.ts b/src/crypto/cryptoApi.ts index c3c1f508a..139f8ae88 100644 --- a/src/crypto/cryptoApi.ts +++ b/src/crypto/cryptoApi.ts @@ -27,7 +27,7 @@ import {    AmountJson,    CoinRecord,    DenominationRecord, -  OfferRecord, +  ProposalRecord,    PayCoinInfo,    PaybackRequest,    PreCoinRecord, @@ -277,9 +277,9 @@ export class CryptoApi {      return this.doRpc<PayCoinInfo>("isValidPaymentSignature", 1, sig, contractHash, merchantPub);    } -  signDeposit(offer: OfferRecord, +  signDeposit(proposal: ProposalRecord,                cds: CoinWithDenom[]): Promise<PayCoinInfo> { -    return this.doRpc<PayCoinInfo>("signDeposit", 3, offer, cds); +    return this.doRpc<PayCoinInfo>("signDeposit", 3, proposal, cds);    }    createEddsaKeypair(): Promise<{priv: string, pub: string}> { diff --git a/src/crypto/cryptoWorker.ts b/src/crypto/cryptoWorker.ts index 85a0425b3..507a080ac 100644 --- a/src/crypto/cryptoWorker.ts +++ b/src/crypto/cryptoWorker.ts @@ -29,7 +29,7 @@ import {    CoinRecord,    CoinStatus,    DenominationRecord, -  OfferRecord, +  ProposalRecord,    PayCoinInfo,    PaybackRequest,    PreCoinRecord, @@ -227,7 +227,7 @@ namespace RpcFunctions {     * Generate updated coins (to store in the database)     * and deposit permissions for each given coin.     */ -  export function signDeposit(offer: OfferRecord, +  export function signDeposit(proposal: ProposalRecord,                                cds: CoinWithDenom[]): PayCoinInfo {      const ret: PayCoinInfo = []; @@ -235,8 +235,8 @@ namespace RpcFunctions {      const feeList: AmountJson[] = cds.map((x) => x.denom.feeDeposit);      let fees = Amounts.add(Amounts.getZero(feeList[0].currency), ...feeList).amount;      // okay if saturates -    fees = Amounts.sub(fees, offer.contract.max_fee).amount; -    const total = Amounts.add(fees, offer.contract.amount).amount; +    fees = Amounts.sub(fees, proposal.contractTerms.max_fee).amount; +    const total = Amounts.add(fees, proposal.contractTerms.amount).amount;      const amountSpent = native.Amount.getZero(cds[0].coin.currentAmount.currency);      const amountRemaining = new native.Amount(total); @@ -273,11 +273,11 @@ namespace RpcFunctions {          amount_with_fee: coinSpend.toNbo(),          coin_pub: native.EddsaPublicKey.fromCrock(cd.coin.coinPub),          deposit_fee: new native.Amount(cd.denom.feeDeposit).toNbo(), -        h_contract: native.HashCode.fromCrock(offer.H_contract), -        h_wire: native.HashCode.fromCrock(offer.contract.H_wire), -        merchant: native.EddsaPublicKey.fromCrock(offer.contract.merchant_pub), -        refund_deadline: native.AbsoluteTimeNbo.fromTalerString(offer.contract.refund_deadline), -        timestamp: native.AbsoluteTimeNbo.fromTalerString(offer.contract.timestamp), +        h_contract: native.HashCode.fromCrock(proposal.contractTermsHash), +        h_wire: native.HashCode.fromCrock(proposal.contractTerms.H_wire), +        merchant: native.EddsaPublicKey.fromCrock(proposal.contractTerms.merchant_pub), +        refund_deadline: native.AbsoluteTimeNbo.fromTalerString(proposal.contractTerms.refund_deadline), +        timestamp: native.AbsoluteTimeNbo.fromTalerString(proposal.contractTerms.timestamp),        });        const coinSig = native.eddsaSign(d.toPurpose(), diff --git a/src/i18n/de.po b/src/i18n/de.po index 1578830b5..d90a9340e 100644 --- a/src/i18n/de.po +++ b/src/i18n/de.po @@ -42,13 +42,13 @@ msgstr ""  msgid "Exchanges in the wallet:"  msgstr "" -#: src/webex/pages/confirm-contract.tsx:146 +#: src/webex/pages/confirm-contract.tsx:141  #, c-format  msgid "You have insufficient funds of the requested currency in your wallet."  msgstr ""  #. tslint:disable-next-line:max-line-length -#: src/webex/pages/confirm-contract.tsx:148 +#: src/webex/pages/confirm-contract.tsx:143  #, c-format  msgid ""  "You do not have any funds from an exchange that is accepted by this " @@ -193,90 +193,90 @@ msgstr ""  msgid "Fatal error: \"%1$s\"."  msgstr "" -#: src/webex/pages/popup.tsx:163 +#: src/webex/pages/popup.tsx:161  #, c-format  msgid "Balance"  msgstr "Saldo" -#: src/webex/pages/popup.tsx:166 +#: src/webex/pages/popup.tsx:164  #, c-format  msgid "History"  msgstr "Verlauf" -#: src/webex/pages/popup.tsx:169 +#: src/webex/pages/popup.tsx:167  #, c-format  msgid "Debug"  msgstr "Debug" -#: src/webex/pages/popup.tsx:245 +#: src/webex/pages/popup.tsx:243  #, c-format  msgid "help"  msgstr "" -#: src/webex/pages/popup.tsx:250 +#: src/webex/pages/popup.tsx:248  #, fuzzy, c-format  msgid ""  "You have no balance to show. Need some\n"  " %1$s getting started?\n"  msgstr "Sie haben kein Digitalgeld. Wollen Sie %1$s? abheben?" -#: src/webex/pages/popup.tsx:267 +#: src/webex/pages/popup.tsx:265  #, c-format  msgid "%1$s incoming\n"  msgstr "" -#: src/webex/pages/popup.tsx:280 +#: src/webex/pages/popup.tsx:278  #, c-format  msgid "%1$s being spent\n"  msgstr "" -#: src/webex/pages/popup.tsx:306 +#: src/webex/pages/popup.tsx:304  #, c-format  msgid "Error: could not retrieve balance information."  msgstr "" -#: src/webex/pages/popup.tsx:345 +#: src/webex/pages/popup.tsx:343  #, fuzzy, c-format  msgid ""  "Bank requested reserve (%1$s) for\n"  " %2$s.\n"  msgstr "Bank bestätig anlegen der Reserve (%1$s) bei %2$s" -#: src/webex/pages/popup.tsx:356 +#: src/webex/pages/popup.tsx:354  #, fuzzy, c-format  msgid ""  "Started to withdraw\n"  " %1$s from%2$s(%3$s).\n"  msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt" -#: src/webex/pages/popup.tsx:366 +#: src/webex/pages/popup.tsx:364  #, c-format  msgid "Merchant%1$soffered contract%2$s;\n"  msgstr "" -#: src/webex/pages/popup.tsx:376 +#: src/webex/pages/popup.tsx:374  #, fuzzy, c-format  msgid "Withdrew%1$sfrom%2$s(%3$s).\n"  msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt" -#: src/webex/pages/popup.tsx:386 +#: src/webex/pages/popup.tsx:384  #, fuzzy, c-format  msgid ""  "Paid%1$sto merchant%2$s.\n"  " (%3$s)\n"  msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt" -#: src/webex/pages/popup.tsx:395 +#: src/webex/pages/popup.tsx:393  #, c-format  msgid "Unknown event (%1$s)"  msgstr "" -#: src/webex/pages/popup.tsx:438 +#: src/webex/pages/popup.tsx:436  #, c-format  msgid "Error: could not retrieve event history"  msgstr "" -#: src/webex/pages/popup.tsx:472 +#: src/webex/pages/popup.tsx:470  #, c-format  msgid "Your wallet has no events recorded."  msgstr "Ihre Geldbörse verzeichnet keine Vorkommnisse." diff --git a/src/i18n/en-US.po b/src/i18n/en-US.po index f7faad5fd..f45591db6 100644 --- a/src/i18n/en-US.po +++ b/src/i18n/en-US.po @@ -42,13 +42,13 @@ msgstr ""  msgid "Exchanges in the wallet:"  msgstr "" -#: src/webex/pages/confirm-contract.tsx:146 +#: src/webex/pages/confirm-contract.tsx:141  #, c-format  msgid "You have insufficient funds of the requested currency in your wallet."  msgstr ""  #. tslint:disable-next-line:max-line-length -#: src/webex/pages/confirm-contract.tsx:148 +#: src/webex/pages/confirm-contract.tsx:143  #, c-format  msgid ""  "You do not have any funds from an exchange that is accepted by this " @@ -193,90 +193,90 @@ msgstr ""  msgid "Fatal error: \"%1$s\"."  msgstr "" -#: src/webex/pages/popup.tsx:163 +#: src/webex/pages/popup.tsx:161  #, c-format  msgid "Balance"  msgstr "" -#: src/webex/pages/popup.tsx:166 +#: src/webex/pages/popup.tsx:164  #, c-format  msgid "History"  msgstr "" -#: src/webex/pages/popup.tsx:169 +#: src/webex/pages/popup.tsx:167  #, c-format  msgid "Debug"  msgstr "" -#: src/webex/pages/popup.tsx:245 +#: src/webex/pages/popup.tsx:243  #, c-format  msgid "help"  msgstr "" -#: src/webex/pages/popup.tsx:250 +#: src/webex/pages/popup.tsx:248  #, c-format  msgid ""  "You have no balance to show. Need some\n"  " %1$s getting started?\n"  msgstr "" -#: src/webex/pages/popup.tsx:267 +#: src/webex/pages/popup.tsx:265  #, c-format  msgid "%1$s incoming\n"  msgstr "" -#: src/webex/pages/popup.tsx:280 +#: src/webex/pages/popup.tsx:278  #, c-format  msgid "%1$s being spent\n"  msgstr "" -#: src/webex/pages/popup.tsx:306 +#: src/webex/pages/popup.tsx:304  #, c-format  msgid "Error: could not retrieve balance information."  msgstr "" -#: src/webex/pages/popup.tsx:345 +#: src/webex/pages/popup.tsx:343  #, c-format  msgid ""  "Bank requested reserve (%1$s) for\n"  " %2$s.\n"  msgstr "" -#: src/webex/pages/popup.tsx:356 +#: src/webex/pages/popup.tsx:354  #, c-format  msgid ""  "Started to withdraw\n"  " %1$s from%2$s(%3$s).\n"  msgstr "" -#: src/webex/pages/popup.tsx:366 +#: src/webex/pages/popup.tsx:364  #, c-format  msgid "Merchant%1$soffered contract%2$s;\n"  msgstr "" -#: src/webex/pages/popup.tsx:376 +#: src/webex/pages/popup.tsx:374  #, c-format  msgid "Withdrew%1$sfrom%2$s(%3$s).\n"  msgstr "" -#: src/webex/pages/popup.tsx:386 +#: src/webex/pages/popup.tsx:384  #, c-format  msgid ""  "Paid%1$sto merchant%2$s.\n"  " (%3$s)\n"  msgstr "" -#: src/webex/pages/popup.tsx:395 +#: src/webex/pages/popup.tsx:393  #, c-format  msgid "Unknown event (%1$s)"  msgstr "" -#: src/webex/pages/popup.tsx:438 +#: src/webex/pages/popup.tsx:436  #, c-format  msgid "Error: could not retrieve event history"  msgstr "" -#: src/webex/pages/popup.tsx:472 +#: src/webex/pages/popup.tsx:470  #, c-format  msgid "Your wallet has no events recorded."  msgstr "" diff --git a/src/i18n/fr.po b/src/i18n/fr.po index 2011e1bb8..17f596b2f 100644 --- a/src/i18n/fr.po +++ b/src/i18n/fr.po @@ -42,13 +42,13 @@ msgstr ""  msgid "Exchanges in the wallet:"  msgstr "" -#: src/webex/pages/confirm-contract.tsx:146 +#: src/webex/pages/confirm-contract.tsx:141  #, c-format  msgid "You have insufficient funds of the requested currency in your wallet."  msgstr ""  #. tslint:disable-next-line:max-line-length -#: src/webex/pages/confirm-contract.tsx:148 +#: src/webex/pages/confirm-contract.tsx:143  #, c-format  msgid ""  "You do not have any funds from an exchange that is accepted by this " @@ -193,90 +193,90 @@ msgstr ""  msgid "Fatal error: \"%1$s\"."  msgstr "" -#: src/webex/pages/popup.tsx:163 +#: src/webex/pages/popup.tsx:161  #, c-format  msgid "Balance"  msgstr "" -#: src/webex/pages/popup.tsx:166 +#: src/webex/pages/popup.tsx:164  #, c-format  msgid "History"  msgstr "" -#: src/webex/pages/popup.tsx:169 +#: src/webex/pages/popup.tsx:167  #, c-format  msgid "Debug"  msgstr "" -#: src/webex/pages/popup.tsx:245 +#: src/webex/pages/popup.tsx:243  #, c-format  msgid "help"  msgstr "" -#: src/webex/pages/popup.tsx:250 +#: src/webex/pages/popup.tsx:248  #, c-format  msgid ""  "You have no balance to show. Need some\n"  " %1$s getting started?\n"  msgstr "" -#: src/webex/pages/popup.tsx:267 +#: src/webex/pages/popup.tsx:265  #, c-format  msgid "%1$s incoming\n"  msgstr "" -#: src/webex/pages/popup.tsx:280 +#: src/webex/pages/popup.tsx:278  #, c-format  msgid "%1$s being spent\n"  msgstr "" -#: src/webex/pages/popup.tsx:306 +#: src/webex/pages/popup.tsx:304  #, c-format  msgid "Error: could not retrieve balance information."  msgstr "" -#: src/webex/pages/popup.tsx:345 +#: src/webex/pages/popup.tsx:343  #, c-format  msgid ""  "Bank requested reserve (%1$s) for\n"  " %2$s.\n"  msgstr "" -#: src/webex/pages/popup.tsx:356 +#: src/webex/pages/popup.tsx:354  #, c-format  msgid ""  "Started to withdraw\n"  " %1$s from%2$s(%3$s).\n"  msgstr "" -#: src/webex/pages/popup.tsx:366 +#: src/webex/pages/popup.tsx:364  #, c-format  msgid "Merchant%1$soffered contract%2$s;\n"  msgstr "" -#: src/webex/pages/popup.tsx:376 +#: src/webex/pages/popup.tsx:374  #, c-format  msgid "Withdrew%1$sfrom%2$s(%3$s).\n"  msgstr "" -#: src/webex/pages/popup.tsx:386 +#: src/webex/pages/popup.tsx:384  #, c-format  msgid ""  "Paid%1$sto merchant%2$s.\n"  " (%3$s)\n"  msgstr "" -#: src/webex/pages/popup.tsx:395 +#: src/webex/pages/popup.tsx:393  #, c-format  msgid "Unknown event (%1$s)"  msgstr "" -#: src/webex/pages/popup.tsx:438 +#: src/webex/pages/popup.tsx:436  #, c-format  msgid "Error: could not retrieve event history"  msgstr "" -#: src/webex/pages/popup.tsx:472 +#: src/webex/pages/popup.tsx:470  #, c-format  msgid "Your wallet has no events recorded."  msgstr "" diff --git a/src/i18n/it.po b/src/i18n/it.po index 2011e1bb8..17f596b2f 100644 --- a/src/i18n/it.po +++ b/src/i18n/it.po @@ -42,13 +42,13 @@ msgstr ""  msgid "Exchanges in the wallet:"  msgstr "" -#: src/webex/pages/confirm-contract.tsx:146 +#: src/webex/pages/confirm-contract.tsx:141  #, c-format  msgid "You have insufficient funds of the requested currency in your wallet."  msgstr ""  #. tslint:disable-next-line:max-line-length -#: src/webex/pages/confirm-contract.tsx:148 +#: src/webex/pages/confirm-contract.tsx:143  #, c-format  msgid ""  "You do not have any funds from an exchange that is accepted by this " @@ -193,90 +193,90 @@ msgstr ""  msgid "Fatal error: \"%1$s\"."  msgstr "" -#: src/webex/pages/popup.tsx:163 +#: src/webex/pages/popup.tsx:161  #, c-format  msgid "Balance"  msgstr "" -#: src/webex/pages/popup.tsx:166 +#: src/webex/pages/popup.tsx:164  #, c-format  msgid "History"  msgstr "" -#: src/webex/pages/popup.tsx:169 +#: src/webex/pages/popup.tsx:167  #, c-format  msgid "Debug"  msgstr "" -#: src/webex/pages/popup.tsx:245 +#: src/webex/pages/popup.tsx:243  #, c-format  msgid "help"  msgstr "" -#: src/webex/pages/popup.tsx:250 +#: src/webex/pages/popup.tsx:248  #, c-format  msgid ""  "You have no balance to show. Need some\n"  " %1$s getting started?\n"  msgstr "" -#: src/webex/pages/popup.tsx:267 +#: src/webex/pages/popup.tsx:265  #, c-format  msgid "%1$s incoming\n"  msgstr "" -#: src/webex/pages/popup.tsx:280 +#: src/webex/pages/popup.tsx:278  #, c-format  msgid "%1$s being spent\n"  msgstr "" -#: src/webex/pages/popup.tsx:306 +#: src/webex/pages/popup.tsx:304  #, c-format  msgid "Error: could not retrieve balance information."  msgstr "" -#: src/webex/pages/popup.tsx:345 +#: src/webex/pages/popup.tsx:343  #, c-format  msgid ""  "Bank requested reserve (%1$s) for\n"  " %2$s.\n"  msgstr "" -#: src/webex/pages/popup.tsx:356 +#: src/webex/pages/popup.tsx:354  #, c-format  msgid ""  "Started to withdraw\n"  " %1$s from%2$s(%3$s).\n"  msgstr "" -#: src/webex/pages/popup.tsx:366 +#: src/webex/pages/popup.tsx:364  #, c-format  msgid "Merchant%1$soffered contract%2$s;\n"  msgstr "" -#: src/webex/pages/popup.tsx:376 +#: src/webex/pages/popup.tsx:374  #, c-format  msgid "Withdrew%1$sfrom%2$s(%3$s).\n"  msgstr "" -#: src/webex/pages/popup.tsx:386 +#: src/webex/pages/popup.tsx:384  #, c-format  msgid ""  "Paid%1$sto merchant%2$s.\n"  " (%3$s)\n"  msgstr "" -#: src/webex/pages/popup.tsx:395 +#: src/webex/pages/popup.tsx:393  #, c-format  msgid "Unknown event (%1$s)"  msgstr "" -#: src/webex/pages/popup.tsx:438 +#: src/webex/pages/popup.tsx:436  #, c-format  msgid "Error: could not retrieve event history"  msgstr "" -#: src/webex/pages/popup.tsx:472 +#: src/webex/pages/popup.tsx:470  #, c-format  msgid "Your wallet has no events recorded."  msgstr "" diff --git a/src/i18n/taler-wallet-webex.pot b/src/i18n/taler-wallet-webex.pot index 2011e1bb8..17f596b2f 100644 --- a/src/i18n/taler-wallet-webex.pot +++ b/src/i18n/taler-wallet-webex.pot @@ -42,13 +42,13 @@ msgstr ""  msgid "Exchanges in the wallet:"  msgstr "" -#: src/webex/pages/confirm-contract.tsx:146 +#: src/webex/pages/confirm-contract.tsx:141  #, c-format  msgid "You have insufficient funds of the requested currency in your wallet."  msgstr ""  #. tslint:disable-next-line:max-line-length -#: src/webex/pages/confirm-contract.tsx:148 +#: src/webex/pages/confirm-contract.tsx:143  #, c-format  msgid ""  "You do not have any funds from an exchange that is accepted by this " @@ -193,90 +193,90 @@ msgstr ""  msgid "Fatal error: \"%1$s\"."  msgstr "" -#: src/webex/pages/popup.tsx:163 +#: src/webex/pages/popup.tsx:161  #, c-format  msgid "Balance"  msgstr "" -#: src/webex/pages/popup.tsx:166 +#: src/webex/pages/popup.tsx:164  #, c-format  msgid "History"  msgstr "" -#: src/webex/pages/popup.tsx:169 +#: src/webex/pages/popup.tsx:167  #, c-format  msgid "Debug"  msgstr "" -#: src/webex/pages/popup.tsx:245 +#: src/webex/pages/popup.tsx:243  #, c-format  msgid "help"  msgstr "" -#: src/webex/pages/popup.tsx:250 +#: src/webex/pages/popup.tsx:248  #, c-format  msgid ""  "You have no balance to show. Need some\n"  " %1$s getting started?\n"  msgstr "" -#: src/webex/pages/popup.tsx:267 +#: src/webex/pages/popup.tsx:265  #, c-format  msgid "%1$s incoming\n"  msgstr "" -#: src/webex/pages/popup.tsx:280 +#: src/webex/pages/popup.tsx:278  #, c-format  msgid "%1$s being spent\n"  msgstr "" -#: src/webex/pages/popup.tsx:306 +#: src/webex/pages/popup.tsx:304  #, c-format  msgid "Error: could not retrieve balance information."  msgstr "" -#: src/webex/pages/popup.tsx:345 +#: src/webex/pages/popup.tsx:343  #, c-format  msgid ""  "Bank requested reserve (%1$s) for\n"  " %2$s.\n"  msgstr "" -#: src/webex/pages/popup.tsx:356 +#: src/webex/pages/popup.tsx:354  #, c-format  msgid ""  "Started to withdraw\n"  " %1$s from%2$s(%3$s).\n"  msgstr "" -#: src/webex/pages/popup.tsx:366 +#: src/webex/pages/popup.tsx:364  #, c-format  msgid "Merchant%1$soffered contract%2$s;\n"  msgstr "" -#: src/webex/pages/popup.tsx:376 +#: src/webex/pages/popup.tsx:374  #, c-format  msgid "Withdrew%1$sfrom%2$s(%3$s).\n"  msgstr "" -#: src/webex/pages/popup.tsx:386 +#: src/webex/pages/popup.tsx:384  #, c-format  msgid ""  "Paid%1$sto merchant%2$s.\n"  " (%3$s)\n"  msgstr "" -#: src/webex/pages/popup.tsx:395 +#: src/webex/pages/popup.tsx:393  #, c-format  msgid "Unknown event (%1$s)"  msgstr "" -#: src/webex/pages/popup.tsx:438 +#: src/webex/pages/popup.tsx:436  #, c-format  msgid "Error: could not retrieve event history"  msgstr "" -#: src/webex/pages/popup.tsx:472 +#: src/webex/pages/popup.tsx:470  #, c-format  msgid "Your wallet has no events recorded."  msgstr "" diff --git a/src/types.ts b/src/types.ts index 4dee93a10..82777f96b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1047,33 +1047,27 @@ export class Contract {  /** - * Offer record, stored in the wallet's database. + * Proposal record, stored in the wallet's database.   */  @Checkable.Class() -export class OfferRecord { +export class ProposalRecord {    /**     * The contract that was offered by the merchant.     */    @Checkable.Value(Contract) -  contract: Contract; +  contractTerms: Contract;    /**     * Signature by the merchant over the contract details.     */    @Checkable.String -  merchant_sig: string; +  merchantSig: string;    /**     * Hash of the contract terms.     */    @Checkable.String -  H_contract: string; - -  /** -   * Time when the offer was made. -   */ -  @Checkable.Number -  offer_time: number; +  contractTermsHash: string;    /**     * Serial ID when the offer is stored in the wallet DB. @@ -1085,7 +1079,7 @@ export class OfferRecord {     * Verify that a value matches the schema of this class and convert it into a     * member.     */ -  static checked: (obj: any) => OfferRecord; +  static checked: (obj: any) => ProposalRecord;  } diff --git a/src/wallet.ts b/src/wallet.ts index 51c99e805..4aa2329d8 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -63,7 +63,7 @@ import {    HistoryLevel,    HistoryRecord,    Notifier, -  OfferRecord, +  ProposalRecord,    PayCoinInfo,    PaybackConfirmation,    PreCoinRecord, @@ -507,9 +507,9 @@ export namespace Stores {      timestampIndex = new Index<number, HistoryRecord>(this, "timestamp", "timestamp");    } -  class OffersStore extends Store<OfferRecord> { +  class ProposalsStore extends Store<ProposalRecord> {      constructor() { -      super("offers", { +      super("proposals", {          autoIncrement: true,          keyPath: "id",        }); @@ -555,19 +555,19 @@ export namespace Stores {      }    } -  export const exchanges = new ExchangeStore(); -  export const exchangeWireFees = new ExchangeWireFeesStore(); -  export const nonces = new NonceStore(); -  export const transactions = new TransactionsStore(); -  export const reserves = new Store<ReserveRecord>("reserves", {keyPath: "reserve_pub"});    export const coins = new CoinsStore(); -  export const refresh = new Store<RefreshSessionRecord>("refresh", {keyPath: "meltCoinPub"}); +  export const config = new ConfigStore(); +  export const currencies = new CurrenciesStore(); +  export const denominations = new DenominationsStore(); +  export const exchangeWireFees = new ExchangeWireFeesStore(); +  export const exchanges = new ExchangeStore();    export const history = new HistoryStore(); -  export const offers = new OffersStore(); +  export const nonces = new NonceStore();    export const precoins = new Store<PreCoinRecord>("precoins", {keyPath: "coinPub"}); -  export const denominations = new DenominationsStore(); -  export const currencies = new CurrenciesStore(); -  export const config = new ConfigStore(); +  export const proposals = new ProposalsStore(); +  export const refresh = new Store<RefreshSessionRecord>("refresh", {keyPath: "meltCoinPub"}); +  export const reserves = new Store<ReserveRecord>("reserves", {keyPath: "reserve_pub"}); +  export const transactions = new TransactionsStore();  }  /* tslint:enable:completed-docs */ @@ -834,32 +834,32 @@ export class Wallet {     * Record all information that is necessary to     * pay for a contract in the wallet's database.     */ -  private async recordConfirmPay(offer: OfferRecord, +  private async recordConfirmPay(proposal: ProposalRecord,                                   payCoinInfo: PayCoinInfo,                                   chosenExchange: string): Promise<void> {      const payReq: PayReq = {        coins: payCoinInfo.map((x) => x.sig),        exchange: chosenExchange, -      merchant_pub: offer.contract.merchant_pub, -      order_id: offer.contract.order_id, +      merchant_pub: proposal.contractTerms.merchant_pub, +      order_id: proposal.contractTerms.order_id,      };      const t: TransactionRecord = { -      contract: offer.contract, -      contractHash: offer.H_contract, +      contract: proposal.contractTerms, +      contractHash: proposal.contractTermsHash,        finished: false, -      merchantSig: offer.merchant_sig, +      merchantSig: proposal.merchantSig,        payReq,      };      const historyEntry: HistoryRecord = {        detail: { -        amount: offer.contract.amount, -        contractHash: offer.H_contract, -        fulfillmentUrl: offer.contract.fulfillment_url, -        merchantName: offer.contract.merchant.name, +        amount: proposal.contractTerms.amount, +        contractHash: proposal.contractTermsHash, +        fulfillmentUrl: proposal.contractTerms.fulfillment_url, +        merchantName: proposal.contractTerms.merchant.name,        },        level: HistoryLevel.User, -      subjectId: `contract-${offer.H_contract}`, +      subjectId: `contract-${proposal.contractTermsHash}`,        timestamp: (new Date()).getTime(),        type: "pay",      }; @@ -880,11 +880,13 @@ export class Wallet {    } -  async saveOffer(offer: OfferRecord): Promise<number> { -    console.log(`saving offer in wallet.ts`); -    const id = await this.q().putWithResult(Stores.offers, offer); +  /** +   * Save a proposal in the database and return an id for it to +   * retrieve it later. +   */ +  async saveProposal(proposal: ProposalRecord): Promise<number> { +    const id = await this.q().putWithResult(Stores.proposals, proposal);      this.notifier.notify(); -    console.log(`saved offer with id ${id}`);      if (typeof id !== "number") {        throw Error("db schema wrong");      } @@ -896,10 +898,15 @@ export class Wallet {     * Add a contract to the wallet and sign coins,     * but do not send them yet.     */ -  async confirmPay(offer: OfferRecord): Promise<ConfirmPayResult> { +  async confirmPay(proposalId: number): Promise<ConfirmPayResult> {      console.log("executing confirmPay"); +    const proposal = await this.q().get(Stores.proposals, proposalId); + +    if (!proposal) { +      throw Error(`proposal with id ${proposalId} not found`); +    } -    const transaction = await this.q().get(Stores.transactions, offer.H_contract); +    const transaction = await this.q().get(Stores.transactions, proposal.contractTermsHash);      if (transaction) {        // Already payed ... @@ -907,17 +914,17 @@ export class Wallet {      }      const res = await this.getCoinsForPayment({ -      allowedAuditors: offer.contract.auditors, -      allowedExchanges: offer.contract.exchanges, -      depositFeeLimit: offer.contract.max_fee, -      paymentAmount: offer.contract.amount, -      wireFeeAmortization: offer.contract.wire_fee_amortization || 1, -      wireFeeLimit: offer.contract.max_wire_fee || Amounts.getZero(offer.contract.amount.currency), -      wireFeeTime: getTalerStampSec(offer.contract.timestamp) || 0, -      wireMethod: offer.contract.wire_method, +      allowedAuditors: proposal.contractTerms.auditors, +      allowedExchanges: proposal.contractTerms.exchanges, +      depositFeeLimit: proposal.contractTerms.max_fee, +      paymentAmount: proposal.contractTerms.amount, +      wireFeeAmortization: proposal.contractTerms.wire_fee_amortization || 1, +      wireFeeLimit: proposal.contractTerms.max_wire_fee || Amounts.getZero(proposal.contractTerms.amount.currency), +      wireFeeTime: getTalerStampSec(proposal.contractTerms.timestamp) || 0, +      wireMethod: proposal.contractTerms.wire_method,      }); -    console.log("max_fee", offer.contract.max_fee); +    console.log("max_fee", proposal.contractTerms.max_fee);      console.log("coin selection result", res);      if (!res) { @@ -926,8 +933,8 @@ export class Wallet {      }      const {exchangeUrl, cds} = res; -    const ds = await this.cryptoApi.signDeposit(offer, cds); -    await this.recordConfirmPay(offer, ds, exchangeUrl); +    const ds = await this.cryptoApi.signDeposit(proposal, cds); +    await this.recordConfirmPay(proposal, ds, exchangeUrl);      return "paid";    } @@ -936,23 +943,29 @@ export class Wallet {     * Check if payment for an offer is possible, or if the offer has already     * been payed for.     */ -  async checkPay(offer: OfferRecord): Promise<CheckPayResult> { +  async checkPay(proposalId: number): Promise<CheckPayResult> { +    const proposal = await this.q().get(Stores.proposals, proposalId); + +    if (!proposal) { +      throw Error(`proposal with id ${proposalId} not found`); +    } +      // First check if we already payed for it. -    const transaction = await this.q().get(Stores.transactions, offer.H_contract); +    const transaction = await this.q().get(Stores.transactions, proposal.contractTermsHash);      if (transaction) {        return "insufficient-balance";      }      // If not already payed, check if we could pay for it.      const res = await this.getCoinsForPayment({ -      allowedAuditors: offer.contract.auditors, -      allowedExchanges: offer.contract.exchanges, -      depositFeeLimit: offer.contract.max_fee, -      paymentAmount: offer.contract.amount, -      wireFeeAmortization: offer.contract.wire_fee_amortization || 1, -      wireFeeLimit: offer.contract.max_wire_fee || Amounts.getZero(offer.contract.amount.currency), -      wireFeeTime: getTalerStampSec(offer.contract.timestamp) || 0, -      wireMethod: offer.contract.wire_method, +      allowedAuditors: proposal.contractTerms.auditors, +      allowedExchanges: proposal.contractTerms.exchanges, +      depositFeeLimit: proposal.contractTerms.max_fee, +      paymentAmount: proposal.contractTerms.amount, +      wireFeeAmortization: proposal.contractTerms.wire_fee_amortization || 1, +      wireFeeLimit: proposal.contractTerms.max_wire_fee || Amounts.getZero(proposal.contractTerms.amount.currency), +      wireFeeTime: getTalerStampSec(proposal.contractTerms.timestamp) || 0, +      wireMethod: proposal.contractTerms.wire_method,      });      if (!res) { @@ -2110,9 +2123,9 @@ export class Wallet {      return denoms;    } -  async getOffer(offerId: number): Promise<any> { -    const offer = await this.q() .get(Stores.offers, offerId); -    return offer; +  async getProposal(proposalId: number): Promise<ProposalRecord|undefined> { +    const proposal = await this.q().get(Stores.proposals, proposalId); +    return proposal;    }    async getExchanges(): Promise<ExchangeRecord[]> { diff --git a/src/webex/messages.ts b/src/webex/messages.ts index 21acfc1d5..27ff9a5b6 100644 --- a/src/webex/messages.ts +++ b/src/webex/messages.ts @@ -69,11 +69,11 @@ export interface MessageMap {      response: string;    };    "confirm-pay": { -    request: { offer: types.OfferRecord; }; +    request: { proposalId: number; };      response: types.ConfirmPayResult;    };    "check-pay": { -    request: { offer: types.OfferRecord; }; +    request: { proposalId: number; };      response: types.CheckPayResult;    };    "query-payment": { @@ -96,21 +96,29 @@ export interface MessageMap {      request: { historyEntry: types.HistoryRecord };      response: void;    }; -  "safe-offer": { -    request: { offer: types.OfferRecord }; +  "save-proposal": { +    request: { proposal: types.ProposalRecord };      response: void;    };    "reserve-creation-info": { -    request: { baseUrl: string }; +    request: { baseUrl: string, amount: types.AmountJson };      response: types.ReserveCreationInfo;    }    "get-history": {      request: { };      response: types.HistoryRecord[];    }; -  "get-offer": { -    request: { offerId: number }; -    response: types.OfferRecord | undefined; +  "get-proposal": { +    request: { proposalId: number }; +    response: types.ProposalRecord | undefined; +  }; +  "get-coins": { +    request: { exchangeBaseUrl: string }; +    response: any; +  }; +  "refresh-coin": { +    request: { coinPub: string }; +    response: any;    };    "get-currencies": {      request: { }; @@ -120,6 +128,10 @@ export interface MessageMap {      request: { currencyRecord: types.CurrencyRecord };      response: void;    }; +  "get-exchanges": { +    request: { }; +    response: types.ExchangeRecord[]; +  };    "get-reserves": {      request: { exchangeBaseUrl: string };      response: types.ReserveRecord[]; diff --git a/src/webex/notify.ts b/src/webex/notify.ts index 81bc6808d..2f38658bd 100644 --- a/src/webex/notify.ts +++ b/src/webex/notify.ts @@ -280,7 +280,8 @@ async function processProposal(proposal: any) {    const contractHash = await wxApi.hashContract(proposal.data);    if (contractHash !== proposal.hash) { -    console.error("merchant-supplied contract hash is wrong"); +    console.error(`merchant-supplied contract hash is wrong (us: ${contractHash}, merchant: ${proposal.hash})`); +    console.dir(proposal.data);      return;    } @@ -301,12 +302,11 @@ async function processProposal(proposal: any) {      type: "offer-contract",    };    await wxApi.putHistory(historyEntry); -  const offerId = await wxApi.saveOffer(proposal); +  let proposalId = await wxApi.saveProposal(proposal); -  const uri = new URI(chrome.extension.getURL( -    "/src/webex/pages/confirm-contract.html")); +  const uri = new URI(chrome.extension.getURL("/src/webex/pages/confirm-contract.html"));    const params = { -    offerId: offerId.toString(), +    proposalId: proposalId.toString(),    };    const target = uri.query(params).href();    document.location.replace(target); diff --git a/src/webex/pages/confirm-contract.html b/src/webex/pages/confirm-contract.html index 6713b2e2c..e5ba68404 100644 --- a/src/webex/pages/confirm-contract.html +++ b/src/webex/pages/confirm-contract.html @@ -5,7 +5,7 @@    <meta charset="UTF-8">    <title>Taler Wallet: Confirm Reserve Creation</title> -  <link rel="stylesheet" type="text/css" href="/src/style/wallet.css"> +  <link rel="stylesheet" type="text/css" href="../style/wallet.css">    <link rel="icon" href="/img/icon.png"> diff --git a/src/webex/pages/confirm-contract.tsx b/src/webex/pages/confirm-contract.tsx index 9b4c93334..c5513f7c6 100644 --- a/src/webex/pages/confirm-contract.tsx +++ b/src/webex/pages/confirm-contract.tsx @@ -27,7 +27,7 @@ import * as i18n from "../../i18n";  import {    Contract,    ExchangeRecord, -  OfferRecord, +  ProposalRecord,  } from "../../types";  import { renderContract } from "../renderHtml"; @@ -98,11 +98,11 @@ class Details extends React.Component<DetailProps, DetailState> {  }  interface ContractPromptProps { -  offerId: number; +  proposalId: number;  }  interface ContractPromptState { -  offer: OfferRecord|null; +  proposal: ProposalRecord|null;    error: string|null;    payDisabled: boolean;    exchanges: null|ExchangeRecord[]; @@ -114,7 +114,7 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt      this.state = {        error: null,        exchanges: null, -      offer: null, +      proposal: null,        payDisabled: true,      };    } @@ -128,26 +128,21 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt    }    async update() { -    const offer = await wxApi.getOffer(this.props.offerId); -    this.setState({offer} as any); +    const proposal = await wxApi.getProposal(this.props.proposalId); +    this.setState({proposal} as any);      this.checkPayment();      const exchanges = await wxApi.getExchanges();      this.setState({exchanges} as any);    }    async checkPayment() { -    const offer = this.state.offer; -    if (!offer) { -      return; -    } -    const payStatus = await wxApi.checkPay(offer); - +    const payStatus = await wxApi.checkPay(this.props.proposalId);      if (payStatus === "insufficient-balance") {        const msgInsufficient = i18n.str`You have insufficient funds of the requested currency in your wallet.`;        // tslint:disable-next-line:max-line-length        const msgNoMatch = i18n.str`You do not have any funds from an exchange that is accepted by this merchant. None of the exchanges accepted by the merchant is known to your wallet.`; -      if (this.state.exchanges && this.state.offer) { -        const acceptedExchangePubs = this.state.offer.contract.exchanges.map((e) => e.master_pub); +      if (this.state.exchanges && this.state.proposal) { +        const acceptedExchangePubs = this.state.proposal.contractTerms.exchanges.map((e) => e.master_pub);          const ex = this.state.exchanges.find((e) => acceptedExchangePubs.indexOf(e.masterPublicKey) >= 0);          if (ex) {            this.setState({error: msgInsufficient}); @@ -165,28 +160,28 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt    }    async doPayment() { -    const offer = this.state.offer; -    if (!offer) { +    const proposal = this.state.proposal; +    if (!proposal) {        return;      } -    const payStatus = await wxApi.confirmPay(offer); +    const payStatus = await wxApi.confirmPay(this.props.proposalId);      switch (payStatus) {        case "insufficient-balance":          this.checkPayment();          return;        case "paid": -        console.log("contract", offer.contract); -        document.location.href = offer.contract.fulfillment_url; +        console.log("contract", proposal.contractTerms); +        document.location.href = proposal.contractTerms.fulfillment_url;          break;      }    }    render() { -    if (!this.state.offer) { +    if (!this.state.proposal) {        return <span>...</span>;      } -    const c = this.state.offer.contract; +    const c = this.state.proposal.contractTerms;      return (        <div>          <div> @@ -210,8 +205,8 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt  document.addEventListener("DOMContentLoaded", () => {    const url = new URI(document.location.href);    const query: any = URI.parseQuery(url.query()); -  const offerId = JSON.parse(query.offerId); +  const proposalId = JSON.parse(query.proposalId); -  ReactDOM.render(<ContractPrompt offerId={offerId}/>, document.getElementById( +  ReactDOM.render(<ContractPrompt proposalId={proposalId}/>, document.getElementById(      "contract")!);  }); diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts index ff601b6f7..4babb2a79 100644 --- a/src/webex/wxApi.ts +++ b/src/webex/wxApi.ts @@ -30,15 +30,16 @@ import {    CurrencyRecord,    DenominationRecord,    ExchangeRecord, -  OfferRecord,    PreCoinRecord,    ReserveCreationInfo,    ReserveRecord,  } from "../types"; +import { MessageType, MessageMap } from "./messages"; -async function callBackend(type: string, detail?: any): Promise<any> { + +async function callBackend<T extends MessageType>(type: T, detail: MessageMap[T]["request"]): Promise<any> {    return new Promise<any>((resolve, reject) => {      chrome.runtime.sendMessage({ type, detail }, (resp) => {        if (resp && resp.error) { @@ -65,7 +66,7 @@ export function getReserveCreationInfo(baseUrl: string,   * Get all exchanges the wallet knows about.   */  export function getExchanges(): Promise<ExchangeRecord[]> { -  return callBackend("get-exchanges"); +  return callBackend("get-exchanges", { });  } @@ -73,7 +74,7 @@ export function getExchanges(): Promise<ExchangeRecord[]> {   * Get all currencies the exchange knows about.   */  export function getCurrencies(): Promise<CurrencyRecord[]> { -  return callBackend("get-currencies"); +  return callBackend("get-currencies", { });  } @@ -114,7 +115,7 @@ export function getReserves(exchangeBaseUrl: string): Promise<ReserveRecord[]> {   * Get all reserves for which a payback is available.   */  export function getPaybackReserves(): Promise<ReserveRecord[]> { -  return callBackend("get-payback-reserves"); +  return callBackend("get-payback-reserves", { });  } @@ -166,41 +167,40 @@ export function payback(coinPub: string): Promise<void> {  }  /** - * Get an offer stored in the wallet by its offer id. - * Note that the numeric offer id is not to be confused with - * the string order_id from the contract terms. + * Get a proposal stored in the wallet by its proposal id.   */ -export function getOffer(offerId: number) { -  return callBackend("get-offer", { offerId }); +export function getProposal(proposalId: number) { +  return callBackend("get-proposal", { proposalId });  }  /**   * Check if payment is possible or already done.   */ -export function checkPay(offer: OfferRecord): Promise<CheckPayResult> { -  return callBackend("check-pay", { offer }); +export function checkPay(proposalId: number): Promise<CheckPayResult> { +  return callBackend("check-pay", { proposalId });  }  /** - * Pay for an offer. + * Pay for a proposal.   */ -export function confirmPay(offer: OfferRecord): Promise<ConfirmPayResult> { -  return callBackend("confirm-pay", { offer }); +export function confirmPay(proposalId: number): Promise<ConfirmPayResult> { +  return callBackend("confirm-pay", { proposalId });  }  /**   * Hash a contract.  Throws if its not a valid contract.   */  export function hashContract(contract: object): Promise<string> { -  return callBackend("confirm-pay", { contract }); +  return callBackend("hash-contract", { contract });  } +  /** - * Save an offer in the wallet.  Returns the offer id that - * the offer is stored under. + * Save a proposal in the wallet.  Returns the proposal id that + * the proposal is stored under.   */ -export function saveOffer(offer: object): Promise<number> { -  return callBackend("save-offer", { offer }); +export function saveProposal(proposal: any): Promise<number> { +  return callBackend("save-proposal", proposal);  }  /** @@ -243,7 +243,7 @@ export function paymentFailed(contractTermsHash: string): Promise<void> {   * cookie was set.   */  export function getTabCookie(contractTermsHash: string, merchantSig: string): Promise<any> { -  return callBackend("get-tab-cookie"); +  return callBackend("get-tab-cookie", { });  }  /** @@ -251,5 +251,5 @@ export function getTabCookie(contractTermsHash: string, merchantSig: string): Pr   * database and return the public key.   */  export function generateNonce(): Promise<string> { -  return callBackend("generate-nonce"); +  return callBackend("generate-nonce", { });  } diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts index 816ce0251..356b2af6e 100644 --- a/src/webex/wxBackend.ts +++ b/src/webex/wxBackend.ts @@ -24,7 +24,6 @@  /**   * Imports.   */ -import { Checkable } from "../checkable";  import { BrowserHttpLib } from "../http";  import * as logging from "../logging";  import { @@ -34,7 +33,7 @@ import {  import {    AmountJson,    Notifier, -  OfferRecord, +  ProposalRecord,  } from "../types";  import {    ConfirmReserveRequest, @@ -44,6 +43,7 @@ import {  } from "../wallet";  import { ChromeBadge } from "./chromeBadge"; +import { MessageType } from "./messages";  import URI = require("urijs");  import Port = chrome.runtime.Port; @@ -60,21 +60,23 @@ const DB_NAME = "taler";   */  const DB_VERSION = 17; -type Handler = (detail: any, sender: MessageSender) => Promise<any>; - -function makeHandlers(db: IDBDatabase, -                      wallet: Wallet): { [msg: string]: Handler } { -  return { -    ["balances"]: (detail, sender) => { +function handleMessage(db: IDBDatabase, +                       wallet: Wallet, +                       sender: MessageSender, +                       type: MessageType, detail: any): any { +  function assertNotFound(t: never): never { +    console.error(`Request type ${t as string} unknown`); +    console.error(`Request detail was ${detail}`); +    return { error: "request unknown", requestType: type } as never; +  } +  switch (type) { +    case "balances":        return wallet.getBalances(); -    }, -    ["dump-db"]: (detail, sender) => { +    case "dump-db":        return exportDb(db); -    }, -    ["import-db"]: (detail, sender) => { +    case "import-db":        return importDb(db, detail.dump); -    }, -    ["get-tab-cookie"]: (detail, sender) => { +    case "get-tab-cookie":        if (!sender || !sender.tab || !sender.tab.id) {          return Promise.resolve();        } @@ -82,11 +84,9 @@ function makeHandlers(db: IDBDatabase,        const info: any = paymentRequestCookies[id] as any;        delete paymentRequestCookies[id];        return Promise.resolve(info); -    }, -    ["ping"]: (detail, sender) => { +    case "ping":        return Promise.resolve(); -    }, -    ["reset"]: (detail, sender) => { +    case "reset":        if (db) {          const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite");          // tslint:disable-next-line:prefer-for-of @@ -95,69 +95,36 @@ function makeHandlers(db: IDBDatabase,          }        }        deleteDb(); -        chrome.browserAction.setBadgeText({ text: "" });        console.log("reset done"); -      // Response is synchronous        return Promise.resolve({}); -    }, -    ["create-reserve"]: (detail, sender) => { +    case "create-reserve": {        const d = {          amount: detail.amount,          exchange: detail.exchange,        };        const req = CreateReserveRequest.checked(d);        return wallet.createReserve(req); -    }, -    ["confirm-reserve"]: (detail, sender) => { -      // TODO: make it a checkable +    } +    case "confirm-reserve":        const d = {          reservePub: detail.reservePub,        };        const req = ConfirmReserveRequest.checked(d);        return wallet.confirmReserve(req); -    }, -    ["generate-nonce"]: (detail, sender) => { +    case "generate-nonce":        return wallet.generateNonce(); -    }, -    ["confirm-pay"]: (detail, sender) => { -      let offer: OfferRecord; -      try { -        offer = OfferRecord.checked(detail.offer); -      } catch (e) { -        if (e instanceof Checkable.SchemaError) { -          console.error("schema error:", e.message); -          return Promise.resolve({ -            detail, -            error: "invalid contract", -            hint: e.message, -          }); -        } else { -          throw e; -        } +    case "confirm-pay": +      if (typeof detail.proposalId !== "number") { +        throw Error("proposalId must be number");        } - -      return wallet.confirmPay(offer); -    }, -    ["check-pay"]: (detail, sender) => { -      let offer: OfferRecord; -      try { -        offer = OfferRecord.checked(detail.offer); -      } catch (e) { -        if (e instanceof Checkable.SchemaError) { -          console.error("schema error:", e.message); -          return Promise.resolve({ -            detail, -            error: "invalid contract", -            hint: e.message, -          }); -        } else { -          throw e; -        } +      return wallet.confirmPay(detail.proposalId); +    case "check-pay": +      if (typeof detail.proposalId !== "number") { +        throw Error("proposalId must be number");        } -      return wallet.checkPay(offer); -    }, -    ["query-payment"]: (detail: any, sender: MessageSender) => { +      return wallet.checkPay(detail.proposalId); +    case "query-payment":        if (sender.tab && sender.tab.id) {          rateLimitCache[sender.tab.id]++;          if (rateLimitCache[sender.tab.id] > 10) { @@ -171,120 +138,98 @@ function makeHandlers(db: IDBDatabase,          }        }        return wallet.queryPayment(detail.url); -    }, -    ["exchange-info"]: (detail) => { +    case "exchange-info":        if (!detail.baseUrl) {          return Promise.resolve({ error: "bad url" });        }        return wallet.updateExchangeFromUrl(detail.baseUrl); -    }, -    ["currency-info"]: (detail) => { +    case "currency-info":        if (!detail.name) {          return Promise.resolve({ error: "name missing" });        }        return wallet.getCurrencyRecord(detail.name); -    }, -    ["hash-contract"]: (detail) => { +    case "hash-contract":        if (!detail.contract) {          return Promise.resolve({ error: "contract missing" });        }        return wallet.hashContract(detail.contract).then((hash) => { -        return { hash }; +        return hash;        }); -    }, -    ["put-history-entry"]: (detail: any) => { +    case "put-history-entry":        if (!detail.historyEntry) {          return Promise.resolve({ error: "historyEntry missing" });        }        return wallet.putHistory(detail.historyEntry); -    }, -    ["save-offer"]: (detail: any) => { -      const offer = detail.offer; -      if (!offer) { -        return Promise.resolve({ error: "offer missing" }); -      } -      console.log("handling safe-offer", detail); -      // FIXME:  fully migrate to new terminology -      const checkedOffer = OfferRecord.checked(offer); -      return wallet.saveOffer(checkedOffer); -    }, -    ["reserve-creation-info"]: (detail, sender) => { +    case "save-proposal": +      console.log("handling save-proposal", detail); +      const checkedRecord = ProposalRecord.checked({ +        contractTerms: detail.data, +        contractTermsHash: detail.hash, +        merchantSig: detail.sig, +      }); +      return wallet.saveProposal(checkedRecord); +    case "reserve-creation-info":        if (!detail.baseUrl || typeof detail.baseUrl !== "string") {          return Promise.resolve({ error: "bad url" });        }        const amount = AmountJson.checked(detail.amount);        return wallet.getReserveCreationInfo(detail.baseUrl, amount); -    }, -    ["get-history"]: (detail, sender) => { +    case "get-history":        // TODO: limit history length        return wallet.getHistory(); -    }, -    ["get-offer"]: (detail, sender) => { -      return wallet.getOffer(detail.offerId); -    }, -    ["get-exchanges"]: (detail, sender) => { +    case "get-proposal": +      return wallet.getProposal(detail.proposalId); +    case "get-exchanges":        return wallet.getExchanges(); -    }, -    ["get-currencies"]: (detail, sender) => { +    case "get-currencies":        return wallet.getCurrencies(); -    }, -    ["update-currency"]: (detail, sender) => { +    case "update-currency":        return wallet.updateCurrency(detail.currencyRecord); -    }, -    ["get-reserves"]: (detail, sender) => { +    case "get-reserves":        if (typeof detail.exchangeBaseUrl !== "string") {          return Promise.reject(Error("exchangeBaseUrl missing"));        }        return wallet.getReserves(detail.exchangeBaseUrl); -    }, -    ["get-payback-reserves"]: (detail, sender) => { +    case "get-payback-reserves":        return wallet.getPaybackReserves(); -    }, -    ["withdraw-payback-reserve"]: (detail, sender) => { +    case "withdraw-payback-reserve":        if (typeof detail.reservePub !== "string") {          return Promise.reject(Error("reservePub missing"));        }        return wallet.withdrawPaybackReserve(detail.reservePub); -    }, -    ["get-coins"]: (detail, sender) => { +    case "get-coins":        if (typeof detail.exchangeBaseUrl !== "string") {          return Promise.reject(Error("exchangBaseUrl missing"));        }        return wallet.getCoins(detail.exchangeBaseUrl); -    }, -    ["get-precoins"]: (detail, sender) => { +    case "get-precoins":        if (typeof detail.exchangeBaseUrl !== "string") {          return Promise.reject(Error("exchangBaseUrl missing"));        }        return wallet.getPreCoins(detail.exchangeBaseUrl); -    }, -    ["get-denoms"]: (detail, sender) => { +    case "get-denoms":        if (typeof detail.exchangeBaseUrl !== "string") {          return Promise.reject(Error("exchangBaseUrl missing"));        }        return wallet.getDenoms(detail.exchangeBaseUrl); -    }, -    ["refresh-coin"]: (detail, sender) => { +    case "refresh-coin":        if (typeof detail.coinPub !== "string") {          return Promise.reject(Error("coinPub missing"));        }        return wallet.refresh(detail.coinPub); -    }, -    ["payback-coin"]: (detail, sender) => { +    case "payback-coin":        if (typeof detail.coinPub !== "string") {          return Promise.reject(Error("coinPub missing"));        }        return wallet.payback(detail.coinPub); -    }, -    ["payment-failed"]: (detail, sender) => { +    case "payment-failed":        // For now we just update exchanges (maybe the exchange did something        // wrong and the keys were messed up).        // FIXME: in the future we should look at what actually went wrong.        console.error("payment reported as failed");        wallet.updateExchanges();        return Promise.resolve(); -    }, -    ["payment-succeeded"]: (detail, sender) => { +    case "payment-succeeded":        const contractTermsHash = detail.contractTermsHash;        const merchantSig = detail.merchantSig;        if (!contractTermsHash) { @@ -294,24 +239,16 @@ function makeHandlers(db: IDBDatabase,          return Promise.reject(Error("merchantSig missing"));        }        return wallet.paymentSucceeded(contractTermsHash, merchantSig); -    }, -  }; -} - - -async function dispatch(handlers: any, req: any, sender: any, sendResponse: any): Promise<void> { -  if (!(req.type in handlers)) { -    console.error(`Request type ${req.type} unknown`); -    console.error(`Request was ${req}`); -    try { -      sendResponse({ error: "request unknown", requestType: req.type }); -    } catch (e) { -      // might fail if tab disconnected -    } +    default: +      // Exhaustiveness check. +      // See https://www.typescriptlang.org/docs/handbook/advanced-types.html +      return assertNotFound(type);    } +} +async function dispatch(db: IDBDatabase, wallet: Wallet, req: any, sender: any, sendResponse: any): Promise<void> {    try { -    const p = handlers[req.type](req.detail, sender); +    const p = handleMessage(db, wallet, sender, req.type, req.detail);      const r = await p;      try {        sendResponse(r); @@ -587,9 +524,8 @@ export async function wxMain() {    // Handlers for messages coming directly from the content    // script on the page -  const handlers = makeHandlers(db, wallet!);    chrome.runtime.onMessage.addListener((req, sender, sendResponse) => { -    dispatch(handlers, req, sender, sendResponse); +    dispatch(db, wallet, req, sender, sendResponse);      return true;    });  | 
