summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-08-24 11:11:02 +0200
committerFlorian Dold <florian@dold.me>2022-08-24 11:11:02 +0200
commitd32d2895cedad656d7130522722f40642c439de6 (patch)
treec3a718ce206715242c01f6b68a9a52a1aef84458
parentbc434ebb83a5985a3aa33b94310eb23d694214da (diff)
downloadwallet-core-d32d2895cedad656d7130522722f40642c439de6.tar.gz
wallet-core-d32d2895cedad656d7130522722f40642c439de6.tar.bz2
wallet-core-d32d2895cedad656d7130522722f40642c439de6.zip
-get p2p pull integration test to run through
-rw-r--r--packages/taler-util/src/talerTypes.ts5
-rw-r--r--packages/taler-wallet-cli/src/harness/harness.ts4
-rw-r--r--packages/taler-wallet-cli/src/index.ts12
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts7
-rw-r--r--packages/taler-wallet-core/src/db.ts27
-rw-r--r--packages/taler-wallet-core/src/headless/helpers.ts3
-rw-r--r--packages/taler-wallet-core/src/operations/exchanges.ts11
-rw-r--r--packages/taler-wallet-core/src/operations/peer-to-peer.ts352
-rw-r--r--packages/taler-wallet-core/src/versions.ts9
-rw-r--r--packages/taler-wallet-core/src/wallet.ts21
-rw-r--r--packages/taler-wallet-webextension/src/utils/index.ts1
11 files changed, 341 insertions, 111 deletions
diff --git a/packages/taler-util/src/talerTypes.ts b/packages/taler-util/src/talerTypes.ts
index ee2dee93c..e36236085 100644
--- a/packages/taler-util/src/talerTypes.ts
+++ b/packages/taler-util/src/talerTypes.ts
@@ -1945,3 +1945,8 @@ export interface ExchangeReservePurseRequest {
// if it has not been paid.
purse_expiration: TalerProtocolTimestamp;
}
+
+export interface ExchangePurseDeposits {
+ // Array of coins to deposit into the purse.
+ deposits: PurseDeposit[];
+}
diff --git a/packages/taler-wallet-cli/src/harness/harness.ts b/packages/taler-wallet-cli/src/harness/harness.ts
index 51f0ce649..aa58fa7a4 100644
--- a/packages/taler-wallet-cli/src/harness/harness.ts
+++ b/packages/taler-wallet-cli/src/harness/harness.ts
@@ -1940,7 +1940,7 @@ export class WalletCli {
`wallet-${self.name}`,
`taler-wallet-cli ${
self.timetravelArg ?? ""
- } --no-throttle -LTRACE --wallet-db '${
+ } --no-throttle -LTRACE --skip-defaults --wallet-db '${
self.dbfile
}' api '${op}' ${shellWrap(JSON.stringify(payload))}`,
);
@@ -1990,6 +1990,7 @@ export class WalletCli {
"--no-throttle",
...this.timetravelArgArr,
"-LTRACE",
+ "--skip-defaults",
"--wallet-db",
this.dbfile,
"run-until-done",
@@ -2005,6 +2006,7 @@ export class WalletCli {
"taler-wallet-cli",
[
"--no-throttle",
+ "--skip-defaults",
"-LTRACE",
...this.timetravelArgArr,
"--wallet-db",
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index a1073dc31..aedc2146e 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -197,6 +197,9 @@ export const walletCli = clk
})
.flag("verbose", ["-V", "--verbose"], {
help: "Enable verbose output.",
+ })
+ .flag("skipDefaults", ["--skip-defaults"], {
+ help: "Skip configuring default exchanges.",
});
type WalletCliArgsType = clk.GetArgType<typeof walletCli>;
@@ -233,7 +236,9 @@ async function withWallet<T>(
ws: wallet,
client: wallet.client,
};
- await wallet.handleCoreApiRequest("initWallet", "native-init", {});
+ await wallet.handleCoreApiRequest("initWallet", "native-init", {
+ skipDefaults: walletCliArgs.wallet.skipDefaults,
+ });
const ret = await f(w);
return ret;
} catch (e) {
@@ -1159,10 +1164,7 @@ testCli
const salt = getRandomBytes(32);
tDerive.start();
- const deriv = await AgeRestriction.commitmentDerive(
- commitProof,
- salt,
- );
+ const deriv = await AgeRestriction.commitmentDerive(commitProof, salt);
tDerive.stop();
tCompare.start();
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts b/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts
index e78bd5a29..1be1563ce 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts
@@ -17,6 +17,7 @@
/**
* Imports.
*/
+import { j2s } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js";
import {
@@ -57,6 +58,8 @@ export async function runPeerToPeerPullTest(t: GlobalTestState) {
},
);
+ console.log(`checkResp: ${j2s(checkResp)}`);
+
const acceptResp = await wallet.client.call(
WalletApiOperation.AcceptPeerPullPayment,
{
@@ -64,6 +67,10 @@ export async function runPeerToPeerPullTest(t: GlobalTestState) {
},
);
+ const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {});
+
+ console.log(`transactions: ${j2s(txs)}`);
+
await wallet.runUntilDone();
}
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 266197eb5..bc0bb4f65 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1669,13 +1669,27 @@ export interface PeerPushPaymentIncomingRecord {
contractPriv: string;
- timestampAccepted: TalerProtocolTimestamp;
+ timestamp: TalerProtocolTimestamp;
contractTerms: PeerContractTerms;
// FIXME: add status etc.
}
+export interface PeerPullPaymentIncomingRecord {
+ peerPullPaymentIncomingId: string;
+
+ pursePub: string;
+
+ exchangeBaseUrl: string;
+
+ contractTerms: PeerContractTerms;
+
+ timestamp: TalerProtocolTimestamp;
+
+ contractPriv: string;
+}
+
export const WalletStoresV1 = {
coins: describeStore(
describeContents<CoinRecord>("coins", {
@@ -1853,6 +1867,17 @@ export const WalletStoresV1 = {
]),
},
),
+ peerPullPaymentIncoming: describeStore(
+ describeContents<PeerPullPaymentIncomingRecord>("peerPullPaymentIncoming", {
+ keyPath: "peerPullPaymentIncomingId",
+ }),
+ {
+ byExchangeAndPurse: describeIndex("byExchangeAndPurse", [
+ "exchangeBaseUrl",
+ "pursePub",
+ ]),
+ },
+ ),
peerPullPaymentInitiation: describeStore(
describeContents<PeerPullPaymentInitiationRecord>(
"peerPushPaymentInitiation",
diff --git a/packages/taler-wallet-core/src/headless/helpers.ts b/packages/taler-wallet-core/src/headless/helpers.ts
index 180d19859..63967d4ad 100644
--- a/packages/taler-wallet-core/src/headless/helpers.ts
+++ b/packages/taler-wallet-core/src/headless/helpers.ts
@@ -132,6 +132,7 @@ export async function getDefaultNodeWallet2(
});
// Atomically move the temporary file onto the DB path.
fs.renameSync(tmpPath, args.persistentStoragePath);
+ logger.trace("committing database done");
};
}
@@ -178,7 +179,7 @@ export async function getDefaultNodeWallet2(
}
}
- const timer = new SetTimeoutTimerAPI()
+ const timer = new SetTimeoutTimerAPI();
const w = await Wallet.create(myDb, myHttpLib, timer, workerFactory);
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts
index 6f8da5aee..94ea2cb9c 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -65,10 +65,7 @@ import {
} from "../util/http.js";
import { DbAccess, GetReadOnlyAccess } from "../util/query.js";
import { RetryInfo } from "../util/retries.js";
-import {
- WALLET_CACHE_BREAKER_CLIENT_VERSION,
- WALLET_EXCHANGE_PROTOCOL_VERSION,
-} from "../versions.js";
+import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "../versions.js";
import { guardOperationException } from "./common.js";
const logger = new Logger("exchanges.ts");
@@ -169,7 +166,6 @@ export async function downloadExchangeWithTermsOfService(
contentType: string,
): Promise<ExchangeTosDownloadResult> {
const reqUrl = new URL("terms", exchangeBaseUrl);
- reqUrl.searchParams.set("cacheBreaker", WALLET_CACHE_BREAKER_CLIENT_VERSION);
const headers = {
Accept: contentType,
};
@@ -352,7 +348,6 @@ async function downloadExchangeWireInfo(
timeout: Duration,
): Promise<ExchangeWireJson> {
const reqUrl = new URL("wire", exchangeBaseUrl);
- reqUrl.searchParams.set("cacheBreaker", WALLET_CACHE_BREAKER_CLIENT_VERSION);
const resp = await http.get(reqUrl.href, {
timeout,
@@ -439,7 +434,6 @@ async function downloadExchangeKeysInfo(
timeout: Duration,
): Promise<ExchangeKeysDownloadResult> {
const keysUrl = new URL("keys", baseUrl);
- keysUrl.searchParams.set("cacheBreaker", WALLET_CACHE_BREAKER_CLIENT_VERSION);
const resp = await http.get(keysUrl.href, {
timeout,
@@ -449,9 +443,6 @@ async function downloadExchangeKeysInfo(
codecForExchangeKeysJson(),
);
- logger.trace("received /keys response");
- logger.trace(`${j2s(exchangeKeysJsonUnchecked)}`);
-
if (exchangeKeysJsonUnchecked.denoms.length === 0) {
throw TalerError.fromDetail(
TalerErrorCode.WALLET_EXCHANGE_DENOMINATIONS_INSUFFICIENT,
diff --git a/packages/taler-wallet-core/src/operations/peer-to-peer.ts b/packages/taler-wallet-core/src/operations/peer-to-peer.ts
index eca319a29..7ac165f92 100644
--- a/packages/taler-wallet-core/src/operations/peer-to-peer.ts
+++ b/packages/taler-wallet-core/src/operations/peer-to-peer.ts
@@ -19,23 +19,30 @@
*/
import {
AbsoluteTime,
+ AcceptPeerPullPaymentRequest,
AcceptPeerPushPaymentRequest,
AmountJson,
+ AmountLike,
Amounts,
AmountString,
buildCodecForObject,
+ CheckPeerPullPaymentRequest,
+ CheckPeerPullPaymentResponse,
CheckPeerPushPaymentRequest,
CheckPeerPushPaymentResponse,
Codec,
codecForAmountString,
codecForAny,
codecForExchangeGetContractResponse,
+ CoinPublicKey,
+ constructPayPullUri,
constructPayPushUri,
ContractTermsUtil,
decodeCrock,
Duration,
eddsaGetPublic,
encodeCrock,
+ ExchangePurseDeposits,
ExchangePurseMergeRequest,
ExchangeReservePurseRequest,
getRandomBytes,
@@ -45,7 +52,9 @@ import {
InitiatePeerPushPaymentResponse,
j2s,
Logger,
+ parsePayPullUri,
parsePayPushUri,
+ RefreshReason,
strcmp,
TalerProtocolTimestamp,
UnblindedSignature,
@@ -54,14 +63,15 @@ import {
import {
CoinStatus,
MergeReserveInfo,
- OperationStatus,
ReserveRecordStatus,
- WithdrawalGroupRecord,
+ WalletStoresV1,
} from "../db.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { checkDbInvariant } from "../util/invariants.js";
import { internalCreateWithdrawalGroup } from "./withdraw.js";
+import { GetReadOnlyAccess } from "../util/query.js";
+import { createRefreshGroup } from "./refresh.js";
const logger = new Logger("operations/peer-to-peer.ts");
@@ -105,93 +115,125 @@ interface CoinInfo {
denomSig: UnblindedSignature;
}
+export async function selectPeerCoins(
+ ws: InternalWalletState,
+ tx: GetReadOnlyAccess<{
+ exchanges: typeof WalletStoresV1.exchanges;
+ denominations: typeof WalletStoresV1.denominations;
+ coins: typeof WalletStoresV1.coins;
+ }>,
+ instructedAmount: AmountJson,
+): Promise<PeerCoinSelection | undefined> {
+ const exchanges = await tx.exchanges.iter().toArray();
+ for (const exch of exchanges) {
+ if (exch.detailsPointer?.currency !== instructedAmount.currency) {
+ continue;
+ }
+ const coins = (
+ await tx.coins.indexes.byBaseUrl.getAll(exch.baseUrl)
+ ).filter((x) => x.status === CoinStatus.Fresh);
+ const coinInfos: CoinInfo[] = [];
+ for (const coin of coins) {
+ const denom = await ws.getDenomInfo(
+ ws,
+ tx,
+ coin.exchangeBaseUrl,
+ coin.denomPubHash,
+ );
+ if (!denom) {
+ throw Error("denom not found");
+ }
+ coinInfos.push({
+ coinPub: coin.coinPub,
+ feeDeposit: denom.feeDeposit,
+ value: denom.value,
+ denomPubHash: denom.denomPubHash,
+ coinPriv: coin.coinPriv,
+ denomSig: coin.denomSig,
+ });
+ }
+ if (coinInfos.length === 0) {
+ continue;
+ }
+ coinInfos.sort(
+ (o1, o2) =>
+ -Amounts.cmp(o1.value, o2.value) ||
+ strcmp(o1.denomPubHash, o2.denomPubHash),
+ );
+ let amountAcc = Amounts.getZero(instructedAmount.currency);
+ let depositFeesAcc = Amounts.getZero(instructedAmount.currency);
+ const resCoins: {
+ coinPub: string;
+ coinPriv: string;
+ contribution: AmountString;
+ denomPubHash: string;
+ denomSig: UnblindedSignature;
+ }[] = [];
+ for (const coin of coinInfos) {
+ if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
+ const res: PeerCoinSelection = {
+ exchangeBaseUrl: exch.baseUrl,
+ coins: resCoins,
+ depositFees: depositFeesAcc,
+ };
+ return res;
+ }
+ const gap = Amounts.add(
+ coin.feeDeposit,
+ Amounts.sub(instructedAmount, amountAcc).amount,
+ ).amount;
+ const contrib = Amounts.min(gap, coin.value);
+ amountAcc = Amounts.add(
+ amountAcc,
+ Amounts.sub(contrib, coin.feeDeposit).amount,
+ ).amount;
+ depositFeesAcc = Amounts.add(depositFeesAcc, coin.feeDeposit).amount;
+ resCoins.push({
+ coinPriv: coin.coinPriv,
+ coinPub: coin.coinPub,
+ contribution: Amounts.stringify(contrib),
+ denomPubHash: coin.denomPubHash,
+ denomSig: coin.denomSig,
+ });
+ }
+ continue;
+ }
+ return undefined;
+}
+
export async function initiatePeerToPeerPush(
ws: InternalWalletState,
req: InitiatePeerPushPaymentRequest,
): Promise<InitiatePeerPushPaymentResponse> {
+ // FIXME: actually create a record for retries here!
const instructedAmount = Amounts.parseOrThrow(req.amount);
const coinSelRes: PeerCoinSelection | undefined = await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
coins: x.coins,
denominations: x.denominations,
+ refreshGroups: x.refreshGroups,
}))
- .runReadOnly(async (tx) => {
- const exchanges = await tx.exchanges.iter().toArray();
- for (const exch of exchanges) {
- if (exch.detailsPointer?.currency !== instructedAmount.currency) {
- continue;
- }
- const coins = (
- await tx.coins.indexes.byBaseUrl.getAll(exch.baseUrl)
- ).filter((x) => x.status === CoinStatus.Fresh);
- const coinInfos: CoinInfo[] = [];
- for (const coin of coins) {
- const denom = await ws.getDenomInfo(
- ws,
- tx,
- coin.exchangeBaseUrl,
- coin.denomPubHash,
- );
- if (!denom) {
- throw Error("denom not found");
- }
- coinInfos.push({
- coinPub: coin.coinPub,
- feeDeposit: denom.feeDeposit,
- value: denom.value,
- denomPubHash: denom.denomPubHash,
- coinPriv: coin.coinPriv,
- denomSig: coin.denomSig,
- });
- }
- if (coinInfos.length === 0) {
- continue;
- }
- coinInfos.sort(
- (o1, o2) =>
- -Amounts.cmp(o1.value, o2.value) ||
- strcmp(o1.denomPubHash, o2.denomPubHash),
- );
- let amountAcc = Amounts.getZero(instructedAmount.currency);
- let depositFeesAcc = Amounts.getZero(instructedAmount.currency);
- const resCoins: {
- coinPub: string;
- coinPriv: string;
- contribution: AmountString;
- denomPubHash: string;
- denomSig: UnblindedSignature;
- }[] = [];
- for (const coin of coinInfos) {
- if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
- const res: PeerCoinSelection = {
- exchangeBaseUrl: exch.baseUrl,
- coins: resCoins,
- depositFees: depositFeesAcc,
- };
- return res;
- }
- const gap = Amounts.add(
- coin.feeDeposit,
- Amounts.sub(instructedAmount, amountAcc).amount,
- ).amount;
- const contrib = Amounts.min(gap, coin.value);
- amountAcc = Amounts.add(
- amountAcc,
- Amounts.sub(contrib, coin.feeDeposit).amount,
- ).amount;
- depositFeesAcc = Amounts.add(depositFeesAcc, coin.feeDeposit).amount;
- resCoins.push({
- coinPriv: coin.coinPriv,
- coinPub: coin.coinPub,
- contribution: Amounts.stringify(contrib),
- denomPubHash: coin.denomPubHash,
- denomSig: coin.denomSig,
- });
- }
- continue;
+ .runReadWrite(async (tx) => {
+ const sel = await selectPeerCoins(ws, tx, instructedAmount);
+ if (!sel) {
+ return undefined;
+ }
+
+ const pubs: CoinPublicKey[] = [];
+ for (const c of sel.coins) {
+ const coin = await tx.coins.get(c.coinPub);
+ checkDbInvariant(!!coin);
+ coin.currentAmount = Amounts.sub(
+ coin.currentAmount,
+ Amounts.parseOrThrow(c.contribution),
+ ).amount;
+ await tx.coins.put(coin);
}
- return undefined;
+
+ await createRefreshGroup(ws, tx, pubs, RefreshReason.Pay);
+
+ return sel;
});
logger.info(`selected p2p coins: ${j2s(coinSelRes)}`);
@@ -339,7 +381,7 @@ export async function checkPeerPushPayment(
exchangeBaseUrl: exchangeBaseUrl,
mergePriv: dec.mergePriv,
pursePub: pursePub,
- timestampAccepted: TalerProtocolTimestamp.now(),
+ timestamp: TalerProtocolTimestamp.now(),
contractTerms: dec.contractTerms,
});
});
@@ -478,6 +520,148 @@ export async function acceptPeerPushPayment(
});
}
+/**
+ * FIXME: Bad name!
+ */
+export async function acceptPeerPullPayment(
+ ws: InternalWalletState,
+ req: AcceptPeerPullPaymentRequest,
+) {
+ const peerPullInc = await ws.db
+ .mktx((x) => ({ peerPullPaymentIncoming: x.peerPullPaymentIncoming }))
+ .runReadOnly(async (tx) => {
+ return tx.peerPullPaymentIncoming.get(req.peerPullPaymentIncomingId);
+ });
+
+ if (!peerPullInc) {
+ throw Error(
+ `can't accept unknown incoming p2p pull payment (${req.peerPullPaymentIncomingId})`,
+ );
+ }
+
+ const instructedAmount = Amounts.parseOrThrow(
+ peerPullInc.contractTerms.amount,
+ );
+ const coinSelRes: PeerCoinSelection | undefined = await ws.db
+ .mktx((x) => ({
+ exchanges: x.exchanges,
+ coins: x.coins,
+ denominations: x.denominations,
+ refreshGroups: x.refreshGroups,
+ }))
+ .runReadWrite(async (tx) => {
+ const sel = await selectPeerCoins(ws, tx, instructedAmount);
+ if (!sel) {
+ return undefined;
+ }
+
+ const pubs: CoinPublicKey[] = [];
+ for (const c of sel.coins) {
+ const coin = await tx.coins.get(c.coinPub);
+ checkDbInvariant(!!coin);
+ coin.currentAmount = Amounts.sub(
+ coin.currentAmount,
+ Amounts.parseOrThrow(c.contribution),
+ ).amount;
+ await tx.coins.put(coin);
+ }
+
+ await createRefreshGroup(ws, tx, pubs, RefreshReason.Pay);
+
+ return sel;
+ });
+ logger.info(`selected p2p coins: ${j2s(coinSelRes)}`);
+
+ if (!coinSelRes) {
+ throw Error("insufficient balance");
+ }
+
+ const pursePub = peerPullInc.pursePub;
+
+ const depositSigsResp = await ws.cryptoApi.signPurseDeposits({
+ exchangeBaseUrl: coinSelRes.exchangeBaseUrl,
+ pursePub,
+ coins: coinSelRes.coins,
+ });
+
+ const purseDepositUrl = new URL(
+ `purses/${pursePub}/deposit`,
+ coinSelRes.exchangeBaseUrl,
+ );
+
+ const depositPayload: ExchangePurseDeposits = {
+ deposits: depositSigsResp.deposits,
+ };
+
+ const httpResp = await ws.http.postJson(purseDepositUrl.href, depositPayload);
+ const resp = await readSuccessResponseJsonOrThrow(httpResp, codecForAny());
+ logger.trace(`purse deposit response: ${j2s(resp)}`);
+}
+
+export async function checkPeerPullPayment(
+ ws: InternalWalletState,
+ req: CheckPeerPullPaymentRequest,
+): Promise<CheckPeerPullPaymentResponse> {
+ const uri = parsePayPullUri(req.talerUri);
+
+ if (!uri) {
+ throw Error("got invalid taler://pay-push URI");
+ }
+
+ const exchangeBaseUrl = uri.exchangeBaseUrl;
+ const contractPriv = uri.contractPriv;
+ const contractPub = encodeCrock(eddsaGetPublic(decodeCrock(contractPriv)));
+
+ const getContractUrl = new URL(`contracts/${contractPub}`, exchangeBaseUrl);
+
+ const contractHttpResp = await ws.http.get(getContractUrl.href);
+
+ const contractResp = await readSuccessResponseJsonOrThrow(
+ contractHttpResp,
+ codecForExchangeGetContractResponse(),
+ );
+
+ const pursePub = contractResp.purse_pub;
+
+ const dec = await ws.cryptoApi.decryptContractForDeposit({
+ ciphertext: contractResp.econtract,
+ contractPriv: contractPriv,
+ pursePub: pursePub,
+ });
+
+ const getPurseUrl = new URL(`purses/${pursePub}/merge`, exchangeBaseUrl);
+
+ const purseHttpResp = await ws.http.get(getPurseUrl.href);
+
+ const purseStatus = await readSuccessResponseJsonOrThrow(
+ purseHttpResp,
+ codecForExchangePurseStatus(),
+ );
+
+ const peerPullPaymentIncomingId = encodeCrock(getRandomBytes(32));
+
+ await ws.db
+ .mktx((x) => ({
+ peerPullPaymentIncoming: x.peerPullPaymentIncoming,
+ }))
+ .runReadWrite(async (tx) => {
+ await tx.peerPullPaymentIncoming.add({
+ peerPullPaymentIncomingId,
+ contractPriv: contractPriv,
+ exchangeBaseUrl: exchangeBaseUrl,
+ pursePub: pursePub,
+ timestamp: TalerProtocolTimestamp.now(),
+ contractTerms: dec.contractTerms,
+ });
+ });
+
+ return {
+ amount: purseStatus.balance,
+ contractTerms: dec.contractTerms,
+ peerPullPaymentIncomingId,
+ };
+}
+
export async function initiatePeerRequestForPay(
ws: InternalWalletState,
req: InitiatePeerPullPaymentRequest,
@@ -580,10 +764,18 @@ export async function initiatePeerRequestForPay(
logger.info(`reserve merge response: ${j2s(resp)}`);
- // FIXME: Now create a withdrawal operation!
+ await internalCreateWithdrawalGroup(ws, {
+ amount: Amounts.parseOrThrow(req.amount),
+ exchangeBaseUrl: req.exchangeBaseUrl,
+ reserveStatus: ReserveRecordStatus.QueryingStatus,
+ reserveKeyPair: {
+ priv: mergeReserveInfo.reservePriv,
+ pub: mergeReserveInfo.reservePub,
+ },
+ });
return {
- talerUri: constructPayPushUri({
+ talerUri: constructPayPullUri({
exchangeBaseUrl: req.exchangeBaseUrl,
contractPriv: econtractResp.contractPriv,
}),
diff --git a/packages/taler-wallet-core/src/versions.ts b/packages/taler-wallet-core/src/versions.ts
index f05d884b1..c3bc142f0 100644
--- a/packages/taler-wallet-core/src/versions.ts
+++ b/packages/taler-wallet-core/src/versions.ts
@@ -34,12 +34,3 @@ export const WALLET_MERCHANT_PROTOCOL_VERSION = "2:0:1";
* Uses libtool's current:revision:age versioning.
*/
export const WALLET_BANK_INTEGRATION_PROTOCOL_VERSION = "0:0:0";
-
-/**
- * Cache breaker that is appended to queries such as /keys and /wire
- * to break through caching, if it has been accidentally/badly configured
- * by the exchange.
- *
- * This is only a temporary measure.
- */
-export const WALLET_CACHE_BREAKER_CLIENT_VERSION = "5";
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 0d5918886..ac81660d2 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -39,6 +39,7 @@ import {
codecForAny,
codecForApplyRefundFromPurchaseIdRequest,
codecForApplyRefundRequest,
+ codecForCheckPeerPullPaymentRequest,
codecForCheckPeerPushPaymentRequest,
codecForConfirmPayRequest,
codecForCreateDepositGroupRequest,
@@ -150,7 +151,9 @@ import {
processPurchasePay,
} from "./operations/pay.js";
import {
+ acceptPeerPullPayment,
acceptPeerPushPayment,
+ checkPeerPullPayment,
checkPeerPushPayment,
initiatePeerRequestForPay,
initiatePeerToPeerPush,
@@ -728,7 +731,12 @@ async function dispatchRequestInternal(
switch (operation) {
case "initWallet": {
ws.initCalled = true;
- await fillDefaults(ws);
+ if (typeof payload === "object" && (payload as any).skipDefaults) {
+ logger.info("skipping defaults");
+ } else {
+ logger.info("filling defaults");
+ await fillDefaults(ws);
+ }
return {};
}
case "withdrawTestkudos": {
@@ -1047,6 +1055,15 @@ async function dispatchRequestInternal(
const req = codecForInitiatePeerPullPaymentRequest().decode(payload);
return await initiatePeerRequestForPay(ws, req);
}
+ case "checkPeerPullPayment": {
+ const req = codecForCheckPeerPullPaymentRequest().decode(payload);
+ return await checkPeerPullPayment(ws, req);
+ }
+ case "acceptPeerPullPayment": {
+ const req = codecForAcceptPeerPullPaymentRequest().decode(payload);
+ await acceptPeerPullPayment(ws, req);
+ return {};
+ }
}
throw TalerError.fromDetail(
TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN,
@@ -1239,10 +1256,8 @@ class InternalWalletStateImpl implements InternalWalletState {
const key = `${exchangeBaseUrl}:${denomPubHash}`;
const cached = this.denomCache[key];
if (cached) {
- logger.trace("using cached denom");
return cached;
}
- logger.trace("looking up denom denom");
const d = await tx.denominations.get([exchangeBaseUrl, denomPubHash]);
if (d) {
this.denomCache[key] = d;
diff --git a/packages/taler-wallet-webextension/src/utils/index.ts b/packages/taler-wallet-webextension/src/utils/index.ts
index a48352840..2414fc86a 100644
--- a/packages/taler-wallet-webextension/src/utils/index.ts
+++ b/packages/taler-wallet-webextension/src/utils/index.ts
@@ -68,7 +68,6 @@ function timeout<T>(ms: number, promise: Promise<T>): Promise<T> {
export async function queryToSlashKeys<T>(url: string): Promise<T> {
const endpoint = new URL("keys", url);
- endpoint.searchParams.set("cacheBreaker", new Date().getTime() + "");
const query = fetch(endpoint.href)
.catch(() => {