fix messaging, small issues and safer types

This commit is contained in:
Florian Dold 2017-05-31 16:04:14 +02:00
parent 7e5ddf3a45
commit 613a14c14f
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
15 changed files with 311 additions and 361 deletions

View File

@ -27,7 +27,7 @@ import {
AmountJson, AmountJson,
CoinRecord, CoinRecord,
DenominationRecord, DenominationRecord,
OfferRecord, ProposalRecord,
PayCoinInfo, PayCoinInfo,
PaybackRequest, PaybackRequest,
PreCoinRecord, PreCoinRecord,
@ -277,9 +277,9 @@ export class CryptoApi {
return this.doRpc<PayCoinInfo>("isValidPaymentSignature", 1, sig, contractHash, merchantPub); return this.doRpc<PayCoinInfo>("isValidPaymentSignature", 1, sig, contractHash, merchantPub);
} }
signDeposit(offer: OfferRecord, signDeposit(proposal: ProposalRecord,
cds: CoinWithDenom[]): Promise<PayCoinInfo> { 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}> { createEddsaKeypair(): Promise<{priv: string, pub: string}> {

View File

@ -29,7 +29,7 @@ import {
CoinRecord, CoinRecord,
CoinStatus, CoinStatus,
DenominationRecord, DenominationRecord,
OfferRecord, ProposalRecord,
PayCoinInfo, PayCoinInfo,
PaybackRequest, PaybackRequest,
PreCoinRecord, PreCoinRecord,
@ -227,7 +227,7 @@ namespace RpcFunctions {
* Generate updated coins (to store in the database) * Generate updated coins (to store in the database)
* and deposit permissions for each given coin. * and deposit permissions for each given coin.
*/ */
export function signDeposit(offer: OfferRecord, export function signDeposit(proposal: ProposalRecord,
cds: CoinWithDenom[]): PayCoinInfo { cds: CoinWithDenom[]): PayCoinInfo {
const ret: PayCoinInfo = []; const ret: PayCoinInfo = [];
@ -235,8 +235,8 @@ namespace RpcFunctions {
const feeList: AmountJson[] = cds.map((x) => x.denom.feeDeposit); const feeList: AmountJson[] = cds.map((x) => x.denom.feeDeposit);
let fees = Amounts.add(Amounts.getZero(feeList[0].currency), ...feeList).amount; let fees = Amounts.add(Amounts.getZero(feeList[0].currency), ...feeList).amount;
// okay if saturates // okay if saturates
fees = Amounts.sub(fees, offer.contract.max_fee).amount; fees = Amounts.sub(fees, proposal.contractTerms.max_fee).amount;
const total = Amounts.add(fees, offer.contract.amount).amount; const total = Amounts.add(fees, proposal.contractTerms.amount).amount;
const amountSpent = native.Amount.getZero(cds[0].coin.currentAmount.currency); const amountSpent = native.Amount.getZero(cds[0].coin.currentAmount.currency);
const amountRemaining = new native.Amount(total); const amountRemaining = new native.Amount(total);
@ -273,11 +273,11 @@ namespace RpcFunctions {
amount_with_fee: coinSpend.toNbo(), amount_with_fee: coinSpend.toNbo(),
coin_pub: native.EddsaPublicKey.fromCrock(cd.coin.coinPub), coin_pub: native.EddsaPublicKey.fromCrock(cd.coin.coinPub),
deposit_fee: new native.Amount(cd.denom.feeDeposit).toNbo(), deposit_fee: new native.Amount(cd.denom.feeDeposit).toNbo(),
h_contract: native.HashCode.fromCrock(offer.H_contract), h_contract: native.HashCode.fromCrock(proposal.contractTermsHash),
h_wire: native.HashCode.fromCrock(offer.contract.H_wire), h_wire: native.HashCode.fromCrock(proposal.contractTerms.H_wire),
merchant: native.EddsaPublicKey.fromCrock(offer.contract.merchant_pub), merchant: native.EddsaPublicKey.fromCrock(proposal.contractTerms.merchant_pub),
refund_deadline: native.AbsoluteTimeNbo.fromTalerString(offer.contract.refund_deadline), refund_deadline: native.AbsoluteTimeNbo.fromTalerString(proposal.contractTerms.refund_deadline),
timestamp: native.AbsoluteTimeNbo.fromTalerString(offer.contract.timestamp), timestamp: native.AbsoluteTimeNbo.fromTalerString(proposal.contractTerms.timestamp),
}); });
const coinSig = native.eddsaSign(d.toPurpose(), const coinSig = native.eddsaSign(d.toPurpose(),

View File

@ -42,13 +42,13 @@ msgstr ""
msgid "Exchanges in the wallet:" msgid "Exchanges in the wallet:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:146 #: src/webex/pages/confirm-contract.tsx:141
#, c-format #, c-format
msgid "You have insufficient funds of the requested currency in your wallet." msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "" msgstr ""
#. tslint:disable-next-line:max-line-length #. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:148 #: src/webex/pages/confirm-contract.tsx:143
#, c-format #, c-format
msgid "" msgid ""
"You do not have any funds from an exchange that is accepted by this " "You do not have any funds from an exchange that is accepted by this "
@ -193,90 +193,90 @@ msgstr ""
msgid "Fatal error: \"%1$s\"." msgid "Fatal error: \"%1$s\"."
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:163 #: src/webex/pages/popup.tsx:161
#, c-format #, c-format
msgid "Balance" msgid "Balance"
msgstr "Saldo" msgstr "Saldo"
#: src/webex/pages/popup.tsx:166 #: src/webex/pages/popup.tsx:164
#, c-format #, c-format
msgid "History" msgid "History"
msgstr "Verlauf" msgstr "Verlauf"
#: src/webex/pages/popup.tsx:169 #: src/webex/pages/popup.tsx:167
#, c-format #, c-format
msgid "Debug" msgid "Debug"
msgstr "Debug" msgstr "Debug"
#: src/webex/pages/popup.tsx:245 #: src/webex/pages/popup.tsx:243
#, c-format #, c-format
msgid "help" msgid "help"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:250 #: src/webex/pages/popup.tsx:248
#, fuzzy, c-format #, fuzzy, c-format
msgid "" msgid ""
"You have no balance to show. Need some\n" "You have no balance to show. Need some\n"
" %1$s getting started?\n" " %1$s getting started?\n"
msgstr "Sie haben kein Digitalgeld. Wollen Sie %1$s? abheben?" msgstr "Sie haben kein Digitalgeld. Wollen Sie %1$s? abheben?"
#: src/webex/pages/popup.tsx:267 #: src/webex/pages/popup.tsx:265
#, c-format #, c-format
msgid "%1$s incoming\n" msgid "%1$s incoming\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:280 #: src/webex/pages/popup.tsx:278
#, c-format #, c-format
msgid "%1$s being spent\n" msgid "%1$s being spent\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:306 #: src/webex/pages/popup.tsx:304
#, c-format #, c-format
msgid "Error: could not retrieve balance information." msgid "Error: could not retrieve balance information."
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:345 #: src/webex/pages/popup.tsx:343
#, fuzzy, c-format #, fuzzy, c-format
msgid "" msgid ""
"Bank requested reserve (%1$s) for\n" "Bank requested reserve (%1$s) for\n"
" %2$s.\n" " %2$s.\n"
msgstr "Bank bestätig anlegen der Reserve (%1$s) bei %2$s" 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 #, fuzzy, c-format
msgid "" msgid ""
"Started to withdraw\n" "Started to withdraw\n"
" %1$s from%2$s(%3$s).\n" " %1$s from%2$s(%3$s).\n"
msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt" 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 #, c-format
msgid "Merchant%1$soffered contract%2$s;\n" msgid "Merchant%1$soffered contract%2$s;\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:376 #: src/webex/pages/popup.tsx:374
#, fuzzy, c-format #, fuzzy, c-format
msgid "Withdrew%1$sfrom%2$s(%3$s).\n" msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt" 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 #, fuzzy, c-format
msgid "" msgid ""
"Paid%1$sto merchant%2$s.\n" "Paid%1$sto merchant%2$s.\n"
" (%3$s)\n" " (%3$s)\n"
msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt" 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 #, c-format
msgid "Unknown event (%1$s)" msgid "Unknown event (%1$s)"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:438 #: src/webex/pages/popup.tsx:436
#, c-format #, c-format
msgid "Error: could not retrieve event history" msgid "Error: could not retrieve event history"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:472 #: src/webex/pages/popup.tsx:470
#, c-format #, c-format
msgid "Your wallet has no events recorded." msgid "Your wallet has no events recorded."
msgstr "Ihre Geldbörse verzeichnet keine Vorkommnisse." msgstr "Ihre Geldbörse verzeichnet keine Vorkommnisse."

View File

@ -42,13 +42,13 @@ msgstr ""
msgid "Exchanges in the wallet:" msgid "Exchanges in the wallet:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:146 #: src/webex/pages/confirm-contract.tsx:141
#, c-format #, c-format
msgid "You have insufficient funds of the requested currency in your wallet." msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "" msgstr ""
#. tslint:disable-next-line:max-line-length #. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:148 #: src/webex/pages/confirm-contract.tsx:143
#, c-format #, c-format
msgid "" msgid ""
"You do not have any funds from an exchange that is accepted by this " "You do not have any funds from an exchange that is accepted by this "
@ -193,90 +193,90 @@ msgstr ""
msgid "Fatal error: \"%1$s\"." msgid "Fatal error: \"%1$s\"."
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:163 #: src/webex/pages/popup.tsx:161
#, c-format #, c-format
msgid "Balance" msgid "Balance"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:166 #: src/webex/pages/popup.tsx:164
#, c-format #, c-format
msgid "History" msgid "History"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:169 #: src/webex/pages/popup.tsx:167
#, c-format #, c-format
msgid "Debug" msgid "Debug"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:245 #: src/webex/pages/popup.tsx:243
#, c-format #, c-format
msgid "help" msgid "help"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:250 #: src/webex/pages/popup.tsx:248
#, c-format #, c-format
msgid "" msgid ""
"You have no balance to show. Need some\n" "You have no balance to show. Need some\n"
" %1$s getting started?\n" " %1$s getting started?\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:267 #: src/webex/pages/popup.tsx:265
#, c-format #, c-format
msgid "%1$s incoming\n" msgid "%1$s incoming\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:280 #: src/webex/pages/popup.tsx:278
#, c-format #, c-format
msgid "%1$s being spent\n" msgid "%1$s being spent\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:306 #: src/webex/pages/popup.tsx:304
#, c-format #, c-format
msgid "Error: could not retrieve balance information." msgid "Error: could not retrieve balance information."
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:345 #: src/webex/pages/popup.tsx:343
#, c-format #, c-format
msgid "" msgid ""
"Bank requested reserve (%1$s) for\n" "Bank requested reserve (%1$s) for\n"
" %2$s.\n" " %2$s.\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:356 #: src/webex/pages/popup.tsx:354
#, c-format #, c-format
msgid "" msgid ""
"Started to withdraw\n" "Started to withdraw\n"
" %1$s from%2$s(%3$s).\n" " %1$s from%2$s(%3$s).\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:366 #: src/webex/pages/popup.tsx:364
#, c-format #, c-format
msgid "Merchant%1$soffered contract%2$s;\n" msgid "Merchant%1$soffered contract%2$s;\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:376 #: src/webex/pages/popup.tsx:374
#, c-format #, c-format
msgid "Withdrew%1$sfrom%2$s(%3$s).\n" msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:386 #: src/webex/pages/popup.tsx:384
#, c-format #, c-format
msgid "" msgid ""
"Paid%1$sto merchant%2$s.\n" "Paid%1$sto merchant%2$s.\n"
" (%3$s)\n" " (%3$s)\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:395 #: src/webex/pages/popup.tsx:393
#, c-format #, c-format
msgid "Unknown event (%1$s)" msgid "Unknown event (%1$s)"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:438 #: src/webex/pages/popup.tsx:436
#, c-format #, c-format
msgid "Error: could not retrieve event history" msgid "Error: could not retrieve event history"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:472 #: src/webex/pages/popup.tsx:470
#, c-format #, c-format
msgid "Your wallet has no events recorded." msgid "Your wallet has no events recorded."
msgstr "" msgstr ""

View File

@ -42,13 +42,13 @@ msgstr ""
msgid "Exchanges in the wallet:" msgid "Exchanges in the wallet:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:146 #: src/webex/pages/confirm-contract.tsx:141
#, c-format #, c-format
msgid "You have insufficient funds of the requested currency in your wallet." msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "" msgstr ""
#. tslint:disable-next-line:max-line-length #. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:148 #: src/webex/pages/confirm-contract.tsx:143
#, c-format #, c-format
msgid "" msgid ""
"You do not have any funds from an exchange that is accepted by this " "You do not have any funds from an exchange that is accepted by this "
@ -193,90 +193,90 @@ msgstr ""
msgid "Fatal error: \"%1$s\"." msgid "Fatal error: \"%1$s\"."
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:163 #: src/webex/pages/popup.tsx:161
#, c-format #, c-format
msgid "Balance" msgid "Balance"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:166 #: src/webex/pages/popup.tsx:164
#, c-format #, c-format
msgid "History" msgid "History"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:169 #: src/webex/pages/popup.tsx:167
#, c-format #, c-format
msgid "Debug" msgid "Debug"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:245 #: src/webex/pages/popup.tsx:243
#, c-format #, c-format
msgid "help" msgid "help"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:250 #: src/webex/pages/popup.tsx:248
#, c-format #, c-format
msgid "" msgid ""
"You have no balance to show. Need some\n" "You have no balance to show. Need some\n"
" %1$s getting started?\n" " %1$s getting started?\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:267 #: src/webex/pages/popup.tsx:265
#, c-format #, c-format
msgid "%1$s incoming\n" msgid "%1$s incoming\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:280 #: src/webex/pages/popup.tsx:278
#, c-format #, c-format
msgid "%1$s being spent\n" msgid "%1$s being spent\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:306 #: src/webex/pages/popup.tsx:304
#, c-format #, c-format
msgid "Error: could not retrieve balance information." msgid "Error: could not retrieve balance information."
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:345 #: src/webex/pages/popup.tsx:343
#, c-format #, c-format
msgid "" msgid ""
"Bank requested reserve (%1$s) for\n" "Bank requested reserve (%1$s) for\n"
" %2$s.\n" " %2$s.\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:356 #: src/webex/pages/popup.tsx:354
#, c-format #, c-format
msgid "" msgid ""
"Started to withdraw\n" "Started to withdraw\n"
" %1$s from%2$s(%3$s).\n" " %1$s from%2$s(%3$s).\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:366 #: src/webex/pages/popup.tsx:364
#, c-format #, c-format
msgid "Merchant%1$soffered contract%2$s;\n" msgid "Merchant%1$soffered contract%2$s;\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:376 #: src/webex/pages/popup.tsx:374
#, c-format #, c-format
msgid "Withdrew%1$sfrom%2$s(%3$s).\n" msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:386 #: src/webex/pages/popup.tsx:384
#, c-format #, c-format
msgid "" msgid ""
"Paid%1$sto merchant%2$s.\n" "Paid%1$sto merchant%2$s.\n"
" (%3$s)\n" " (%3$s)\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:395 #: src/webex/pages/popup.tsx:393
#, c-format #, c-format
msgid "Unknown event (%1$s)" msgid "Unknown event (%1$s)"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:438 #: src/webex/pages/popup.tsx:436
#, c-format #, c-format
msgid "Error: could not retrieve event history" msgid "Error: could not retrieve event history"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:472 #: src/webex/pages/popup.tsx:470
#, c-format #, c-format
msgid "Your wallet has no events recorded." msgid "Your wallet has no events recorded."
msgstr "" msgstr ""

View File

@ -42,13 +42,13 @@ msgstr ""
msgid "Exchanges in the wallet:" msgid "Exchanges in the wallet:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:146 #: src/webex/pages/confirm-contract.tsx:141
#, c-format #, c-format
msgid "You have insufficient funds of the requested currency in your wallet." msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "" msgstr ""
#. tslint:disable-next-line:max-line-length #. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:148 #: src/webex/pages/confirm-contract.tsx:143
#, c-format #, c-format
msgid "" msgid ""
"You do not have any funds from an exchange that is accepted by this " "You do not have any funds from an exchange that is accepted by this "
@ -193,90 +193,90 @@ msgstr ""
msgid "Fatal error: \"%1$s\"." msgid "Fatal error: \"%1$s\"."
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:163 #: src/webex/pages/popup.tsx:161
#, c-format #, c-format
msgid "Balance" msgid "Balance"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:166 #: src/webex/pages/popup.tsx:164
#, c-format #, c-format
msgid "History" msgid "History"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:169 #: src/webex/pages/popup.tsx:167
#, c-format #, c-format
msgid "Debug" msgid "Debug"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:245 #: src/webex/pages/popup.tsx:243
#, c-format #, c-format
msgid "help" msgid "help"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:250 #: src/webex/pages/popup.tsx:248
#, c-format #, c-format
msgid "" msgid ""
"You have no balance to show. Need some\n" "You have no balance to show. Need some\n"
" %1$s getting started?\n" " %1$s getting started?\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:267 #: src/webex/pages/popup.tsx:265
#, c-format #, c-format
msgid "%1$s incoming\n" msgid "%1$s incoming\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:280 #: src/webex/pages/popup.tsx:278
#, c-format #, c-format
msgid "%1$s being spent\n" msgid "%1$s being spent\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:306 #: src/webex/pages/popup.tsx:304
#, c-format #, c-format
msgid "Error: could not retrieve balance information." msgid "Error: could not retrieve balance information."
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:345 #: src/webex/pages/popup.tsx:343
#, c-format #, c-format
msgid "" msgid ""
"Bank requested reserve (%1$s) for\n" "Bank requested reserve (%1$s) for\n"
" %2$s.\n" " %2$s.\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:356 #: src/webex/pages/popup.tsx:354
#, c-format #, c-format
msgid "" msgid ""
"Started to withdraw\n" "Started to withdraw\n"
" %1$s from%2$s(%3$s).\n" " %1$s from%2$s(%3$s).\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:366 #: src/webex/pages/popup.tsx:364
#, c-format #, c-format
msgid "Merchant%1$soffered contract%2$s;\n" msgid "Merchant%1$soffered contract%2$s;\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:376 #: src/webex/pages/popup.tsx:374
#, c-format #, c-format
msgid "Withdrew%1$sfrom%2$s(%3$s).\n" msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:386 #: src/webex/pages/popup.tsx:384
#, c-format #, c-format
msgid "" msgid ""
"Paid%1$sto merchant%2$s.\n" "Paid%1$sto merchant%2$s.\n"
" (%3$s)\n" " (%3$s)\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:395 #: src/webex/pages/popup.tsx:393
#, c-format #, c-format
msgid "Unknown event (%1$s)" msgid "Unknown event (%1$s)"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:438 #: src/webex/pages/popup.tsx:436
#, c-format #, c-format
msgid "Error: could not retrieve event history" msgid "Error: could not retrieve event history"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:472 #: src/webex/pages/popup.tsx:470
#, c-format #, c-format
msgid "Your wallet has no events recorded." msgid "Your wallet has no events recorded."
msgstr "" msgstr ""

View File

@ -42,13 +42,13 @@ msgstr ""
msgid "Exchanges in the wallet:" msgid "Exchanges in the wallet:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:146 #: src/webex/pages/confirm-contract.tsx:141
#, c-format #, c-format
msgid "You have insufficient funds of the requested currency in your wallet." msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "" msgstr ""
#. tslint:disable-next-line:max-line-length #. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:148 #: src/webex/pages/confirm-contract.tsx:143
#, c-format #, c-format
msgid "" msgid ""
"You do not have any funds from an exchange that is accepted by this " "You do not have any funds from an exchange that is accepted by this "
@ -193,90 +193,90 @@ msgstr ""
msgid "Fatal error: \"%1$s\"." msgid "Fatal error: \"%1$s\"."
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:163 #: src/webex/pages/popup.tsx:161
#, c-format #, c-format
msgid "Balance" msgid "Balance"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:166 #: src/webex/pages/popup.tsx:164
#, c-format #, c-format
msgid "History" msgid "History"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:169 #: src/webex/pages/popup.tsx:167
#, c-format #, c-format
msgid "Debug" msgid "Debug"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:245 #: src/webex/pages/popup.tsx:243
#, c-format #, c-format
msgid "help" msgid "help"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:250 #: src/webex/pages/popup.tsx:248
#, c-format #, c-format
msgid "" msgid ""
"You have no balance to show. Need some\n" "You have no balance to show. Need some\n"
" %1$s getting started?\n" " %1$s getting started?\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:267 #: src/webex/pages/popup.tsx:265
#, c-format #, c-format
msgid "%1$s incoming\n" msgid "%1$s incoming\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:280 #: src/webex/pages/popup.tsx:278
#, c-format #, c-format
msgid "%1$s being spent\n" msgid "%1$s being spent\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:306 #: src/webex/pages/popup.tsx:304
#, c-format #, c-format
msgid "Error: could not retrieve balance information." msgid "Error: could not retrieve balance information."
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:345 #: src/webex/pages/popup.tsx:343
#, c-format #, c-format
msgid "" msgid ""
"Bank requested reserve (%1$s) for\n" "Bank requested reserve (%1$s) for\n"
" %2$s.\n" " %2$s.\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:356 #: src/webex/pages/popup.tsx:354
#, c-format #, c-format
msgid "" msgid ""
"Started to withdraw\n" "Started to withdraw\n"
" %1$s from%2$s(%3$s).\n" " %1$s from%2$s(%3$s).\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:366 #: src/webex/pages/popup.tsx:364
#, c-format #, c-format
msgid "Merchant%1$soffered contract%2$s;\n" msgid "Merchant%1$soffered contract%2$s;\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:376 #: src/webex/pages/popup.tsx:374
#, c-format #, c-format
msgid "Withdrew%1$sfrom%2$s(%3$s).\n" msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:386 #: src/webex/pages/popup.tsx:384
#, c-format #, c-format
msgid "" msgid ""
"Paid%1$sto merchant%2$s.\n" "Paid%1$sto merchant%2$s.\n"
" (%3$s)\n" " (%3$s)\n"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:395 #: src/webex/pages/popup.tsx:393
#, c-format #, c-format
msgid "Unknown event (%1$s)" msgid "Unknown event (%1$s)"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:438 #: src/webex/pages/popup.tsx:436
#, c-format #, c-format
msgid "Error: could not retrieve event history" msgid "Error: could not retrieve event history"
msgstr "" msgstr ""
#: src/webex/pages/popup.tsx:472 #: src/webex/pages/popup.tsx:470
#, c-format #, c-format
msgid "Your wallet has no events recorded." msgid "Your wallet has no events recorded."
msgstr "" msgstr ""

View File

@ -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() @Checkable.Class()
export class OfferRecord { export class ProposalRecord {
/** /**
* The contract that was offered by the merchant. * The contract that was offered by the merchant.
*/ */
@Checkable.Value(Contract) @Checkable.Value(Contract)
contract: Contract; contractTerms: Contract;
/** /**
* Signature by the merchant over the contract details. * Signature by the merchant over the contract details.
*/ */
@Checkable.String @Checkable.String
merchant_sig: string; merchantSig: string;
/** /**
* Hash of the contract terms. * Hash of the contract terms.
*/ */
@Checkable.String @Checkable.String
H_contract: string; contractTermsHash: string;
/**
* Time when the offer was made.
*/
@Checkable.Number
offer_time: number;
/** /**
* Serial ID when the offer is stored in the wallet DB. * 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 * Verify that a value matches the schema of this class and convert it into a
* member. * member.
*/ */
static checked: (obj: any) => OfferRecord; static checked: (obj: any) => ProposalRecord;
} }

View File

@ -63,7 +63,7 @@ import {
HistoryLevel, HistoryLevel,
HistoryRecord, HistoryRecord,
Notifier, Notifier,
OfferRecord, ProposalRecord,
PayCoinInfo, PayCoinInfo,
PaybackConfirmation, PaybackConfirmation,
PreCoinRecord, PreCoinRecord,
@ -507,9 +507,9 @@ export namespace Stores {
timestampIndex = new Index<number, HistoryRecord>(this, "timestamp", "timestamp"); timestampIndex = new Index<number, HistoryRecord>(this, "timestamp", "timestamp");
} }
class OffersStore extends Store<OfferRecord> { class ProposalsStore extends Store<ProposalRecord> {
constructor() { constructor() {
super("offers", { super("proposals", {
autoIncrement: true, autoIncrement: true,
keyPath: "id", 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 coins = new CoinsStore();
export const refresh = new Store<RefreshSessionRecord>("refresh", {keyPath: "meltCoinPub"});
export const history = new HistoryStore();
export const offers = new OffersStore();
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 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 nonces = new NonceStore();
export const precoins = new Store<PreCoinRecord>("precoins", {keyPath: "coinPub"});
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 */ /* tslint:enable:completed-docs */
@ -834,32 +834,32 @@ export class Wallet {
* Record all information that is necessary to * Record all information that is necessary to
* pay for a contract in the wallet's database. * pay for a contract in the wallet's database.
*/ */
private async recordConfirmPay(offer: OfferRecord, private async recordConfirmPay(proposal: ProposalRecord,
payCoinInfo: PayCoinInfo, payCoinInfo: PayCoinInfo,
chosenExchange: string): Promise<void> { chosenExchange: string): Promise<void> {
const payReq: PayReq = { const payReq: PayReq = {
coins: payCoinInfo.map((x) => x.sig), coins: payCoinInfo.map((x) => x.sig),
exchange: chosenExchange, exchange: chosenExchange,
merchant_pub: offer.contract.merchant_pub, merchant_pub: proposal.contractTerms.merchant_pub,
order_id: offer.contract.order_id, order_id: proposal.contractTerms.order_id,
}; };
const t: TransactionRecord = { const t: TransactionRecord = {
contract: offer.contract, contract: proposal.contractTerms,
contractHash: offer.H_contract, contractHash: proposal.contractTermsHash,
finished: false, finished: false,
merchantSig: offer.merchant_sig, merchantSig: proposal.merchantSig,
payReq, payReq,
}; };
const historyEntry: HistoryRecord = { const historyEntry: HistoryRecord = {
detail: { detail: {
amount: offer.contract.amount, amount: proposal.contractTerms.amount,
contractHash: offer.H_contract, contractHash: proposal.contractTermsHash,
fulfillmentUrl: offer.contract.fulfillment_url, fulfillmentUrl: proposal.contractTerms.fulfillment_url,
merchantName: offer.contract.merchant.name, merchantName: proposal.contractTerms.merchant.name,
}, },
level: HistoryLevel.User, level: HistoryLevel.User,
subjectId: `contract-${offer.H_contract}`, subjectId: `contract-${proposal.contractTermsHash}`,
timestamp: (new Date()).getTime(), timestamp: (new Date()).getTime(),
type: "pay", type: "pay",
}; };
@ -880,11 +880,13 @@ export class Wallet {
} }
async saveOffer(offer: OfferRecord): Promise<number> { /**
console.log(`saving offer in wallet.ts`); * Save a proposal in the database and return an id for it to
const id = await this.q().putWithResult(Stores.offers, offer); * retrieve it later.
*/
async saveProposal(proposal: ProposalRecord): Promise<number> {
const id = await this.q().putWithResult(Stores.proposals, proposal);
this.notifier.notify(); this.notifier.notify();
console.log(`saved offer with id ${id}`);
if (typeof id !== "number") { if (typeof id !== "number") {
throw Error("db schema wrong"); throw Error("db schema wrong");
} }
@ -896,10 +898,15 @@ export class Wallet {
* Add a contract to the wallet and sign coins, * Add a contract to the wallet and sign coins,
* but do not send them yet. * but do not send them yet.
*/ */
async confirmPay(offer: OfferRecord): Promise<ConfirmPayResult> { async confirmPay(proposalId: number): Promise<ConfirmPayResult> {
console.log("executing confirmPay"); console.log("executing confirmPay");
const proposal = await this.q().get(Stores.proposals, proposalId);
const transaction = await this.q().get(Stores.transactions, offer.H_contract); if (!proposal) {
throw Error(`proposal with id ${proposalId} not found`);
}
const transaction = await this.q().get(Stores.transactions, proposal.contractTermsHash);
if (transaction) { if (transaction) {
// Already payed ... // Already payed ...
@ -907,17 +914,17 @@ export class Wallet {
} }
const res = await this.getCoinsForPayment({ const res = await this.getCoinsForPayment({
allowedAuditors: offer.contract.auditors, allowedAuditors: proposal.contractTerms.auditors,
allowedExchanges: offer.contract.exchanges, allowedExchanges: proposal.contractTerms.exchanges,
depositFeeLimit: offer.contract.max_fee, depositFeeLimit: proposal.contractTerms.max_fee,
paymentAmount: offer.contract.amount, paymentAmount: proposal.contractTerms.amount,
wireFeeAmortization: offer.contract.wire_fee_amortization || 1, wireFeeAmortization: proposal.contractTerms.wire_fee_amortization || 1,
wireFeeLimit: offer.contract.max_wire_fee || Amounts.getZero(offer.contract.amount.currency), wireFeeLimit: proposal.contractTerms.max_wire_fee || Amounts.getZero(proposal.contractTerms.amount.currency),
wireFeeTime: getTalerStampSec(offer.contract.timestamp) || 0, wireFeeTime: getTalerStampSec(proposal.contractTerms.timestamp) || 0,
wireMethod: offer.contract.wire_method, 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); console.log("coin selection result", res);
if (!res) { if (!res) {
@ -926,8 +933,8 @@ export class Wallet {
} }
const {exchangeUrl, cds} = res; const {exchangeUrl, cds} = res;
const ds = await this.cryptoApi.signDeposit(offer, cds); const ds = await this.cryptoApi.signDeposit(proposal, cds);
await this.recordConfirmPay(offer, ds, exchangeUrl); await this.recordConfirmPay(proposal, ds, exchangeUrl);
return "paid"; return "paid";
} }
@ -936,23 +943,29 @@ export class Wallet {
* Check if payment for an offer is possible, or if the offer has already * Check if payment for an offer is possible, or if the offer has already
* been payed for. * 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. // 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) { if (transaction) {
return "insufficient-balance"; return "insufficient-balance";
} }
// If not already payed, check if we could pay for it. // If not already payed, check if we could pay for it.
const res = await this.getCoinsForPayment({ const res = await this.getCoinsForPayment({
allowedAuditors: offer.contract.auditors, allowedAuditors: proposal.contractTerms.auditors,
allowedExchanges: offer.contract.exchanges, allowedExchanges: proposal.contractTerms.exchanges,
depositFeeLimit: offer.contract.max_fee, depositFeeLimit: proposal.contractTerms.max_fee,
paymentAmount: offer.contract.amount, paymentAmount: proposal.contractTerms.amount,
wireFeeAmortization: offer.contract.wire_fee_amortization || 1, wireFeeAmortization: proposal.contractTerms.wire_fee_amortization || 1,
wireFeeLimit: offer.contract.max_wire_fee || Amounts.getZero(offer.contract.amount.currency), wireFeeLimit: proposal.contractTerms.max_wire_fee || Amounts.getZero(proposal.contractTerms.amount.currency),
wireFeeTime: getTalerStampSec(offer.contract.timestamp) || 0, wireFeeTime: getTalerStampSec(proposal.contractTerms.timestamp) || 0,
wireMethod: offer.contract.wire_method, wireMethod: proposal.contractTerms.wire_method,
}); });
if (!res) { if (!res) {
@ -2110,9 +2123,9 @@ export class Wallet {
return denoms; return denoms;
} }
async getOffer(offerId: number): Promise<any> { async getProposal(proposalId: number): Promise<ProposalRecord|undefined> {
const offer = await this.q() .get(Stores.offers, offerId); const proposal = await this.q().get(Stores.proposals, proposalId);
return offer; return proposal;
} }
async getExchanges(): Promise<ExchangeRecord[]> { async getExchanges(): Promise<ExchangeRecord[]> {

View File

@ -69,11 +69,11 @@ export interface MessageMap {
response: string; response: string;
}; };
"confirm-pay": { "confirm-pay": {
request: { offer: types.OfferRecord; }; request: { proposalId: number; };
response: types.ConfirmPayResult; response: types.ConfirmPayResult;
}; };
"check-pay": { "check-pay": {
request: { offer: types.OfferRecord; }; request: { proposalId: number; };
response: types.CheckPayResult; response: types.CheckPayResult;
}; };
"query-payment": { "query-payment": {
@ -96,21 +96,29 @@ export interface MessageMap {
request: { historyEntry: types.HistoryRecord }; request: { historyEntry: types.HistoryRecord };
response: void; response: void;
}; };
"safe-offer": { "save-proposal": {
request: { offer: types.OfferRecord }; request: { proposal: types.ProposalRecord };
response: void; response: void;
}; };
"reserve-creation-info": { "reserve-creation-info": {
request: { baseUrl: string }; request: { baseUrl: string, amount: types.AmountJson };
response: types.ReserveCreationInfo; response: types.ReserveCreationInfo;
} }
"get-history": { "get-history": {
request: { }; request: { };
response: types.HistoryRecord[]; response: types.HistoryRecord[];
}; };
"get-offer": { "get-proposal": {
request: { offerId: number }; request: { proposalId: number };
response: types.OfferRecord | undefined; response: types.ProposalRecord | undefined;
};
"get-coins": {
request: { exchangeBaseUrl: string };
response: any;
};
"refresh-coin": {
request: { coinPub: string };
response: any;
}; };
"get-currencies": { "get-currencies": {
request: { }; request: { };
@ -120,6 +128,10 @@ export interface MessageMap {
request: { currencyRecord: types.CurrencyRecord }; request: { currencyRecord: types.CurrencyRecord };
response: void; response: void;
}; };
"get-exchanges": {
request: { };
response: types.ExchangeRecord[];
};
"get-reserves": { "get-reserves": {
request: { exchangeBaseUrl: string }; request: { exchangeBaseUrl: string };
response: types.ReserveRecord[]; response: types.ReserveRecord[];

View File

@ -280,7 +280,8 @@ async function processProposal(proposal: any) {
const contractHash = await wxApi.hashContract(proposal.data); const contractHash = await wxApi.hashContract(proposal.data);
if (contractHash !== proposal.hash) { 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; return;
} }
@ -301,12 +302,11 @@ async function processProposal(proposal: any) {
type: "offer-contract", type: "offer-contract",
}; };
await wxApi.putHistory(historyEntry); await wxApi.putHistory(historyEntry);
const offerId = await wxApi.saveOffer(proposal); let proposalId = await wxApi.saveProposal(proposal);
const uri = new URI(chrome.extension.getURL( const uri = new URI(chrome.extension.getURL("/src/webex/pages/confirm-contract.html"));
"/src/webex/pages/confirm-contract.html"));
const params = { const params = {
offerId: offerId.toString(), proposalId: proposalId.toString(),
}; };
const target = uri.query(params).href(); const target = uri.query(params).href();
document.location.replace(target); document.location.replace(target);

View File

@ -5,7 +5,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Taler Wallet: Confirm Reserve Creation</title> <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"> <link rel="icon" href="/img/icon.png">

View File

@ -27,7 +27,7 @@ import * as i18n from "../../i18n";
import { import {
Contract, Contract,
ExchangeRecord, ExchangeRecord,
OfferRecord, ProposalRecord,
} from "../../types"; } from "../../types";
import { renderContract } from "../renderHtml"; import { renderContract } from "../renderHtml";
@ -98,11 +98,11 @@ class Details extends React.Component<DetailProps, DetailState> {
} }
interface ContractPromptProps { interface ContractPromptProps {
offerId: number; proposalId: number;
} }
interface ContractPromptState { interface ContractPromptState {
offer: OfferRecord|null; proposal: ProposalRecord|null;
error: string|null; error: string|null;
payDisabled: boolean; payDisabled: boolean;
exchanges: null|ExchangeRecord[]; exchanges: null|ExchangeRecord[];
@ -114,7 +114,7 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt
this.state = { this.state = {
error: null, error: null,
exchanges: null, exchanges: null,
offer: null, proposal: null,
payDisabled: true, payDisabled: true,
}; };
} }
@ -128,26 +128,21 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt
} }
async update() { async update() {
const offer = await wxApi.getOffer(this.props.offerId); const proposal = await wxApi.getProposal(this.props.proposalId);
this.setState({offer} as any); this.setState({proposal} as any);
this.checkPayment(); this.checkPayment();
const exchanges = await wxApi.getExchanges(); const exchanges = await wxApi.getExchanges();
this.setState({exchanges} as any); this.setState({exchanges} as any);
} }
async checkPayment() { async checkPayment() {
const offer = this.state.offer; const payStatus = await wxApi.checkPay(this.props.proposalId);
if (!offer) {
return;
}
const payStatus = await wxApi.checkPay(offer);
if (payStatus === "insufficient-balance") { if (payStatus === "insufficient-balance") {
const msgInsufficient = i18n.str`You have insufficient funds of the requested currency in your wallet.`; const msgInsufficient = i18n.str`You have insufficient funds of the requested currency in your wallet.`;
// tslint:disable-next-line:max-line-length // 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.`; 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) { if (this.state.exchanges && this.state.proposal) {
const acceptedExchangePubs = this.state.offer.contract.exchanges.map((e) => e.master_pub); const acceptedExchangePubs = this.state.proposal.contractTerms.exchanges.map((e) => e.master_pub);
const ex = this.state.exchanges.find((e) => acceptedExchangePubs.indexOf(e.masterPublicKey) >= 0); const ex = this.state.exchanges.find((e) => acceptedExchangePubs.indexOf(e.masterPublicKey) >= 0);
if (ex) { if (ex) {
this.setState({error: msgInsufficient}); this.setState({error: msgInsufficient});
@ -165,28 +160,28 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt
} }
async doPayment() { async doPayment() {
const offer = this.state.offer; const proposal = this.state.proposal;
if (!offer) { if (!proposal) {
return; return;
} }
const payStatus = await wxApi.confirmPay(offer); const payStatus = await wxApi.confirmPay(this.props.proposalId);
switch (payStatus) { switch (payStatus) {
case "insufficient-balance": case "insufficient-balance":
this.checkPayment(); this.checkPayment();
return; return;
case "paid": case "paid":
console.log("contract", offer.contract); console.log("contract", proposal.contractTerms);
document.location.href = offer.contract.fulfillment_url; document.location.href = proposal.contractTerms.fulfillment_url;
break; break;
} }
} }
render() { render() {
if (!this.state.offer) { if (!this.state.proposal) {
return <span>...</span>; return <span>...</span>;
} }
const c = this.state.offer.contract; const c = this.state.proposal.contractTerms;
return ( return (
<div> <div>
<div> <div>
@ -210,8 +205,8 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const url = new URI(document.location.href); const url = new URI(document.location.href);
const query: any = URI.parseQuery(url.query()); 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")!); "contract")!);
}); });

View File

@ -30,15 +30,16 @@ import {
CurrencyRecord, CurrencyRecord,
DenominationRecord, DenominationRecord,
ExchangeRecord, ExchangeRecord,
OfferRecord,
PreCoinRecord, PreCoinRecord,
ReserveCreationInfo, ReserveCreationInfo,
ReserveRecord, ReserveRecord,
} from "../types"; } 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) => { return new Promise<any>((resolve, reject) => {
chrome.runtime.sendMessage({ type, detail }, (resp) => { chrome.runtime.sendMessage({ type, detail }, (resp) => {
if (resp && resp.error) { if (resp && resp.error) {
@ -65,7 +66,7 @@ export function getReserveCreationInfo(baseUrl: string,
* Get all exchanges the wallet knows about. * Get all exchanges the wallet knows about.
*/ */
export function getExchanges(): Promise<ExchangeRecord[]> { 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. * Get all currencies the exchange knows about.
*/ */
export function getCurrencies(): Promise<CurrencyRecord[]> { 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. * Get all reserves for which a payback is available.
*/ */
export function getPaybackReserves(): Promise<ReserveRecord[]> { 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. * Get a proposal stored in the wallet by its proposal id.
* Note that the numeric offer id is not to be confused with
* the string order_id from the contract terms.
*/ */
export function getOffer(offerId: number) { export function getProposal(proposalId: number) {
return callBackend("get-offer", { offerId }); return callBackend("get-proposal", { proposalId });
} }
/** /**
* Check if payment is possible or already done. * Check if payment is possible or already done.
*/ */
export function checkPay(offer: OfferRecord): Promise<CheckPayResult> { export function checkPay(proposalId: number): Promise<CheckPayResult> {
return callBackend("check-pay", { offer }); return callBackend("check-pay", { proposalId });
} }
/** /**
* Pay for an offer. * Pay for a proposal.
*/ */
export function confirmPay(offer: OfferRecord): Promise<ConfirmPayResult> { export function confirmPay(proposalId: number): Promise<ConfirmPayResult> {
return callBackend("confirm-pay", { offer }); return callBackend("confirm-pay", { proposalId });
} }
/** /**
* Hash a contract. Throws if its not a valid contract. * Hash a contract. Throws if its not a valid contract.
*/ */
export function hashContract(contract: object): Promise<string> { 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 * Save a proposal in the wallet. Returns the proposal id that
* the offer is stored under. * the proposal is stored under.
*/ */
export function saveOffer(offer: object): Promise<number> { export function saveProposal(proposal: any): Promise<number> {
return callBackend("save-offer", { offer }); return callBackend("save-proposal", proposal);
} }
/** /**
@ -243,7 +243,7 @@ export function paymentFailed(contractTermsHash: string): Promise<void> {
* cookie was set. * cookie was set.
*/ */
export function getTabCookie(contractTermsHash: string, merchantSig: string): Promise<any> { 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. * database and return the public key.
*/ */
export function generateNonce(): Promise<string> { export function generateNonce(): Promise<string> {
return callBackend("generate-nonce"); return callBackend("generate-nonce", { });
} }

View File

@ -24,7 +24,6 @@
/** /**
* Imports. * Imports.
*/ */
import { Checkable } from "../checkable";
import { BrowserHttpLib } from "../http"; import { BrowserHttpLib } from "../http";
import * as logging from "../logging"; import * as logging from "../logging";
import { import {
@ -34,7 +33,7 @@ import {
import { import {
AmountJson, AmountJson,
Notifier, Notifier,
OfferRecord, ProposalRecord,
} from "../types"; } from "../types";
import { import {
ConfirmReserveRequest, ConfirmReserveRequest,
@ -44,6 +43,7 @@ import {
} from "../wallet"; } from "../wallet";
import { ChromeBadge } from "./chromeBadge"; import { ChromeBadge } from "./chromeBadge";
import { MessageType } from "./messages";
import URI = require("urijs"); import URI = require("urijs");
import Port = chrome.runtime.Port; import Port = chrome.runtime.Port;
@ -60,21 +60,23 @@ const DB_NAME = "taler";
*/ */
const DB_VERSION = 17; const DB_VERSION = 17;
type Handler = (detail: any, sender: MessageSender) => Promise<any>; function handleMessage(db: IDBDatabase,
wallet: Wallet,
function makeHandlers(db: IDBDatabase, sender: MessageSender,
wallet: Wallet): { [msg: string]: Handler } { type: MessageType, detail: any): any {
return { function assertNotFound(t: never): never {
["balances"]: (detail, sender) => { 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(); return wallet.getBalances();
}, case "dump-db":
["dump-db"]: (detail, sender) => {
return exportDb(db); return exportDb(db);
}, case "import-db":
["import-db"]: (detail, sender) => {
return importDb(db, detail.dump); return importDb(db, detail.dump);
}, case "get-tab-cookie":
["get-tab-cookie"]: (detail, sender) => {
if (!sender || !sender.tab || !sender.tab.id) { if (!sender || !sender.tab || !sender.tab.id) {
return Promise.resolve(); return Promise.resolve();
} }
@ -82,11 +84,9 @@ function makeHandlers(db: IDBDatabase,
const info: any = paymentRequestCookies[id] as any; const info: any = paymentRequestCookies[id] as any;
delete paymentRequestCookies[id]; delete paymentRequestCookies[id];
return Promise.resolve(info); return Promise.resolve(info);
}, case "ping":
["ping"]: (detail, sender) => {
return Promise.resolve(); return Promise.resolve();
}, case "reset":
["reset"]: (detail, sender) => {
if (db) { if (db) {
const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite"); const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite");
// tslint:disable-next-line:prefer-for-of // tslint:disable-next-line:prefer-for-of
@ -95,69 +95,36 @@ function makeHandlers(db: IDBDatabase,
} }
} }
deleteDb(); deleteDb();
chrome.browserAction.setBadgeText({ text: "" }); chrome.browserAction.setBadgeText({ text: "" });
console.log("reset done"); console.log("reset done");
// Response is synchronous
return Promise.resolve({}); return Promise.resolve({});
}, case "create-reserve": {
["create-reserve"]: (detail, sender) => {
const d = { const d = {
amount: detail.amount, amount: detail.amount,
exchange: detail.exchange, exchange: detail.exchange,
}; };
const req = CreateReserveRequest.checked(d); const req = CreateReserveRequest.checked(d);
return wallet.createReserve(req); return wallet.createReserve(req);
}, }
["confirm-reserve"]: (detail, sender) => { case "confirm-reserve":
// TODO: make it a checkable
const d = { const d = {
reservePub: detail.reservePub, reservePub: detail.reservePub,
}; };
const req = ConfirmReserveRequest.checked(d); const req = ConfirmReserveRequest.checked(d);
return wallet.confirmReserve(req); return wallet.confirmReserve(req);
}, case "generate-nonce":
["generate-nonce"]: (detail, sender) => {
return wallet.generateNonce(); return wallet.generateNonce();
}, case "confirm-pay":
["confirm-pay"]: (detail, sender) => { if (typeof detail.proposalId !== "number") {
let offer: OfferRecord; throw Error("proposalId must be number");
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);
return wallet.confirmPay(offer); case "check-pay":
}, if (typeof detail.proposalId !== "number") {
["check-pay"]: (detail, sender) => { throw Error("proposalId must be number");
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.checkPay(offer); return wallet.checkPay(detail.proposalId);
}, case "query-payment":
["query-payment"]: (detail: any, sender: MessageSender) => {
if (sender.tab && sender.tab.id) { if (sender.tab && sender.tab.id) {
rateLimitCache[sender.tab.id]++; rateLimitCache[sender.tab.id]++;
if (rateLimitCache[sender.tab.id] > 10) { if (rateLimitCache[sender.tab.id] > 10) {
@ -171,120 +138,98 @@ function makeHandlers(db: IDBDatabase,
} }
} }
return wallet.queryPayment(detail.url); return wallet.queryPayment(detail.url);
}, case "exchange-info":
["exchange-info"]: (detail) => {
if (!detail.baseUrl) { if (!detail.baseUrl) {
return Promise.resolve({ error: "bad url" }); return Promise.resolve({ error: "bad url" });
} }
return wallet.updateExchangeFromUrl(detail.baseUrl); return wallet.updateExchangeFromUrl(detail.baseUrl);
}, case "currency-info":
["currency-info"]: (detail) => {
if (!detail.name) { if (!detail.name) {
return Promise.resolve({ error: "name missing" }); return Promise.resolve({ error: "name missing" });
} }
return wallet.getCurrencyRecord(detail.name); return wallet.getCurrencyRecord(detail.name);
}, case "hash-contract":
["hash-contract"]: (detail) => {
if (!detail.contract) { if (!detail.contract) {
return Promise.resolve({ error: "contract missing" }); return Promise.resolve({ error: "contract missing" });
} }
return wallet.hashContract(detail.contract).then((hash) => { return wallet.hashContract(detail.contract).then((hash) => {
return { hash }; return hash;
}); });
}, case "put-history-entry":
["put-history-entry"]: (detail: any) => {
if (!detail.historyEntry) { if (!detail.historyEntry) {
return Promise.resolve({ error: "historyEntry missing" }); return Promise.resolve({ error: "historyEntry missing" });
} }
return wallet.putHistory(detail.historyEntry); return wallet.putHistory(detail.historyEntry);
}, case "save-proposal":
["save-offer"]: (detail: any) => { console.log("handling save-proposal", detail);
const offer = detail.offer; const checkedRecord = ProposalRecord.checked({
if (!offer) { contractTerms: detail.data,
return Promise.resolve({ error: "offer missing" }); contractTermsHash: detail.hash,
} merchantSig: detail.sig,
console.log("handling safe-offer", detail); });
// FIXME: fully migrate to new terminology return wallet.saveProposal(checkedRecord);
const checkedOffer = OfferRecord.checked(offer); case "reserve-creation-info":
return wallet.saveOffer(checkedOffer);
},
["reserve-creation-info"]: (detail, sender) => {
if (!detail.baseUrl || typeof detail.baseUrl !== "string") { if (!detail.baseUrl || typeof detail.baseUrl !== "string") {
return Promise.resolve({ error: "bad url" }); return Promise.resolve({ error: "bad url" });
} }
const amount = AmountJson.checked(detail.amount); const amount = AmountJson.checked(detail.amount);
return wallet.getReserveCreationInfo(detail.baseUrl, amount); return wallet.getReserveCreationInfo(detail.baseUrl, amount);
}, case "get-history":
["get-history"]: (detail, sender) => {
// TODO: limit history length // TODO: limit history length
return wallet.getHistory(); return wallet.getHistory();
}, case "get-proposal":
["get-offer"]: (detail, sender) => { return wallet.getProposal(detail.proposalId);
return wallet.getOffer(detail.offerId); case "get-exchanges":
},
["get-exchanges"]: (detail, sender) => {
return wallet.getExchanges(); return wallet.getExchanges();
}, case "get-currencies":
["get-currencies"]: (detail, sender) => {
return wallet.getCurrencies(); return wallet.getCurrencies();
}, case "update-currency":
["update-currency"]: (detail, sender) => {
return wallet.updateCurrency(detail.currencyRecord); return wallet.updateCurrency(detail.currencyRecord);
}, case "get-reserves":
["get-reserves"]: (detail, sender) => {
if (typeof detail.exchangeBaseUrl !== "string") { if (typeof detail.exchangeBaseUrl !== "string") {
return Promise.reject(Error("exchangeBaseUrl missing")); return Promise.reject(Error("exchangeBaseUrl missing"));
} }
return wallet.getReserves(detail.exchangeBaseUrl); return wallet.getReserves(detail.exchangeBaseUrl);
}, case "get-payback-reserves":
["get-payback-reserves"]: (detail, sender) => {
return wallet.getPaybackReserves(); return wallet.getPaybackReserves();
}, case "withdraw-payback-reserve":
["withdraw-payback-reserve"]: (detail, sender) => {
if (typeof detail.reservePub !== "string") { if (typeof detail.reservePub !== "string") {
return Promise.reject(Error("reservePub missing")); return Promise.reject(Error("reservePub missing"));
} }
return wallet.withdrawPaybackReserve(detail.reservePub); return wallet.withdrawPaybackReserve(detail.reservePub);
}, case "get-coins":
["get-coins"]: (detail, sender) => {
if (typeof detail.exchangeBaseUrl !== "string") { if (typeof detail.exchangeBaseUrl !== "string") {
return Promise.reject(Error("exchangBaseUrl missing")); return Promise.reject(Error("exchangBaseUrl missing"));
} }
return wallet.getCoins(detail.exchangeBaseUrl); return wallet.getCoins(detail.exchangeBaseUrl);
}, case "get-precoins":
["get-precoins"]: (detail, sender) => {
if (typeof detail.exchangeBaseUrl !== "string") { if (typeof detail.exchangeBaseUrl !== "string") {
return Promise.reject(Error("exchangBaseUrl missing")); return Promise.reject(Error("exchangBaseUrl missing"));
} }
return wallet.getPreCoins(detail.exchangeBaseUrl); return wallet.getPreCoins(detail.exchangeBaseUrl);
}, case "get-denoms":
["get-denoms"]: (detail, sender) => {
if (typeof detail.exchangeBaseUrl !== "string") { if (typeof detail.exchangeBaseUrl !== "string") {
return Promise.reject(Error("exchangBaseUrl missing")); return Promise.reject(Error("exchangBaseUrl missing"));
} }
return wallet.getDenoms(detail.exchangeBaseUrl); return wallet.getDenoms(detail.exchangeBaseUrl);
}, case "refresh-coin":
["refresh-coin"]: (detail, sender) => {
if (typeof detail.coinPub !== "string") { if (typeof detail.coinPub !== "string") {
return Promise.reject(Error("coinPub missing")); return Promise.reject(Error("coinPub missing"));
} }
return wallet.refresh(detail.coinPub); return wallet.refresh(detail.coinPub);
}, case "payback-coin":
["payback-coin"]: (detail, sender) => {
if (typeof detail.coinPub !== "string") { if (typeof detail.coinPub !== "string") {
return Promise.reject(Error("coinPub missing")); return Promise.reject(Error("coinPub missing"));
} }
return wallet.payback(detail.coinPub); return wallet.payback(detail.coinPub);
}, case "payment-failed":
["payment-failed"]: (detail, sender) => {
// For now we just update exchanges (maybe the exchange did something // For now we just update exchanges (maybe the exchange did something
// wrong and the keys were messed up). // wrong and the keys were messed up).
// FIXME: in the future we should look at what actually went wrong. // FIXME: in the future we should look at what actually went wrong.
console.error("payment reported as failed"); console.error("payment reported as failed");
wallet.updateExchanges(); wallet.updateExchanges();
return Promise.resolve(); return Promise.resolve();
}, case "payment-succeeded":
["payment-succeeded"]: (detail, sender) => {
const contractTermsHash = detail.contractTermsHash; const contractTermsHash = detail.contractTermsHash;
const merchantSig = detail.merchantSig; const merchantSig = detail.merchantSig;
if (!contractTermsHash) { if (!contractTermsHash) {
@ -294,24 +239,16 @@ function makeHandlers(db: IDBDatabase,
return Promise.reject(Error("merchantSig missing")); return Promise.reject(Error("merchantSig missing"));
} }
return wallet.paymentSucceeded(contractTermsHash, merchantSig); return wallet.paymentSucceeded(contractTermsHash, merchantSig);
}, 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> {
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
}
}
try { try {
const p = handlers[req.type](req.detail, sender); const p = handleMessage(db, wallet, sender, req.type, req.detail);
const r = await p; const r = await p;
try { try {
sendResponse(r); sendResponse(r);
@ -587,9 +524,8 @@ export async function wxMain() {
// Handlers for messages coming directly from the content // Handlers for messages coming directly from the content
// script on the page // script on the page
const handlers = makeHandlers(db, wallet!);
chrome.runtime.onMessage.addListener((req, sender, sendResponse) => { chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
dispatch(handlers, req, sender, sendResponse); dispatch(db, wallet, req, sender, sendResponse);
return true; return true;
}); });