make wallet-cli runnable under qtart

This commit is contained in:
Florian Dold 2023-02-15 23:32:42 +01:00
parent cb2f4c21d8
commit 825d2c4352
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
81 changed files with 1069 additions and 1046 deletions

View File

@ -1,4 +1,4 @@
import { clk } from "@gnu-taler/taler-util";
import { clk } from "@gnu-taler/taler-util/clk";
import {
getBackupStartState,
getRecoveryStartState,

View File

@ -20,7 +20,7 @@
*/
import { expect } from "chai";
import { AMOUNT_REGEX, PAYTO_REGEX } from "../../src/utils/constants.js";
import { AMOUNT_REGEX, PAYTO_REGEX } from "./constants.js";
describe("payto uri format", () => {
const valids = [

View File

@ -19,19 +19,19 @@
*/
import {
buildCodecForObject,
codecForBoolean,
codecForNumber,
codecForString,
codecForBoolean,
codecOptional,
j2s,
Logger,
} from "@gnu-taler/taler-util";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
import {
getDefaultNodeWallet2,
NodeHttpLib,
WalletApiOperation,
Wallet,
AccessStats,
createNativeWalletHost2,
Wallet,
WalletApiOperation,
} from "@gnu-taler/taler-wallet-core";
/**
@ -46,8 +46,9 @@ export async function runBench1(configJson: any): Promise<void> {
// Validate the configuration file for this benchmark.
const b1conf = codecForBench1Config().decode(configJson);
const myHttpLib = new NodeHttpLib();
myHttpLib.setThrottling(false);
const myHttpLib = createPlatformHttpLib({
enableThrottling: false,
});
const numIter = b1conf.iterations ?? 1;
const numDeposits = b1conf.deposits ?? 5;
@ -81,7 +82,7 @@ export async function runBench1(configJson: any): Promise<void> {
console.log("wallet DB stats", j2s(getDbStats!()));
}
const res = await getDefaultNodeWallet2({
const res = await createNativeWalletHost2({
// No persistent DB storage.
persistentStoragePath: undefined,
httpLib: myHttpLib,

View File

@ -24,6 +24,7 @@ import {
codecOptional,
Logger,
} from "@gnu-taler/taler-util";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
import {
checkReserve,
createFakebankReserve,
@ -31,9 +32,8 @@ import {
depositCoin,
downloadExchangeInfo,
findDenomOrThrow,
NodeHttpLib,
refreshCoin,
SynchronousCryptoWorkerFactoryNode,
SynchronousCryptoWorkerFactoryPlain,
withdrawCoin,
} from "@gnu-taler/taler-wallet-core";
@ -50,12 +50,13 @@ export async function runBench2(configJson: any): Promise<void> {
const benchConf = codecForBench2Config().decode(configJson);
const curr = benchConf.currency;
const cryptoDisp = new CryptoDispatcher(
new SynchronousCryptoWorkerFactoryNode(),
new SynchronousCryptoWorkerFactoryPlain(),
);
const cryptoApi = cryptoDisp.cryptoApi;
const http = new NodeHttpLib();
http.setThrottling(false);
const http = createPlatformHttpLib({
enableThrottling: false,
});
const numIter = benchConf.iterations ?? 1;
const numDeposits = benchConf.deposits ?? 5;

View File

@ -25,12 +25,12 @@ import {
j2s,
Logger,
} from "@gnu-taler/taler-util";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
import {
getDefaultNodeWallet2,
NodeHttpLib,
WalletApiOperation,
Wallet,
AccessStats,
createNativeWalletHost2,
Wallet,
WalletApiOperation,
} from "@gnu-taler/taler-wallet-core";
import benchMerchantIDGenerator from "./benchMerchantIDGenerator.js";
@ -50,8 +50,9 @@ export async function runBench3(configJson: any): Promise<void> {
throw new Error("Payto template url must contain '${id}' placeholder");
}
const myHttpLib = new NodeHttpLib();
myHttpLib.setThrottling(false);
const myHttpLib = createPlatformHttpLib({
enableThrottling: false,
});
const numIter = b3conf.iterations ?? 1;
const numDeposits = b3conf.deposits ?? 5;
@ -89,7 +90,7 @@ export async function runBench3(configJson: any): Promise<void> {
console.log("wallet DB stats", j2s(getDbStats!()));
}
const res = await getDefaultNodeWallet2({
const res = await createNativeWalletHost2({
// No persistent DB storage.
persistentStoragePath: undefined,
httpLib: myHttpLib,

View File

@ -41,17 +41,15 @@ import {
MerchantTemplateAddDetails,
parsePaytoUri,
stringToBytes,
TalerError,
TalerProtocolDuration,
WalletNotification,
} from "@gnu-taler/taler-util";
import {
BankAccessApi,
BankApi,
BankServiceHandle,
HarnessExchangeBankAccount,
NodeHttpLib,
openPromise,
TalerError,
WalletCoreApiClient,
} from "@gnu-taler/taler-wallet-core";
import { deepStrictEqual } from "assert";
@ -83,6 +81,7 @@ import {
RemoteWallet,
WalletNotificationWaiter,
} from "@gnu-taler/taler-wallet-core/remote";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
const logger = new Logger("harness.ts");
@ -507,7 +506,7 @@ class LibEuFinBankService extends BankServiceBase implements BankServiceHandle {
sandboxProc: ProcessWrapper | undefined;
nexusProc: ProcessWrapper | undefined;
http = new NodeHttpLib();
http = createPlatformHttpLib();
static async create(
gc: GlobalTestState,
@ -794,7 +793,7 @@ export class FakebankService
{
proc: ProcessWrapper | undefined;
http = new NodeHttpLib();
http = createPlatformHttpLib();
// We store "created" accounts during setup and
// register them after startup.

View File

@ -23,7 +23,6 @@ import os from "os";
import path from "path";
import {
Amounts,
clk,
Configuration,
decodeCrock,
Logger,
@ -38,6 +37,7 @@ import { GlobalTestState, runTestWithState } from "./harness/harness.js";
import { getTestInfo, runTests } from "./integrationtests/testrunner.js";
import { lintExchangeDeployment } from "./lint.js";
import { runEnvFull } from "./env-full.js";
import { clk } from "@gnu-taler/taler-util/clk";
const logger = new Logger("taler-harness:index.ts");
@ -74,6 +74,15 @@ const advancedCli = testingCli.subcommand("advancedArgs", "advanced", {
help: "Subcommands for advanced operations (only use if you know what you're doing!).",
});
advancedCli
.subcommand("decode", "decode", {
help: "Decode base32-crockford.",
})
.action((args) => {
const enc = fs.readFileSync(0, "utf8");
console.log(decodeCrock(enc.trim()));
});
advancedCli
.subcommand("bench1", "bench1", {
help: "Run the 'bench1' benchmark",
@ -272,7 +281,7 @@ testingCli
help: "Produce less output.",
})
.flag("noTimeout", ["--no-timeout"], {
help: "Do not time out tests."
help: "Do not time out tests.",
})
.action(async (args) => {
await runTests({

View File

@ -24,22 +24,18 @@ import {
Duration,
durationFromSpec,
} from "@gnu-taler/taler-util";
import {
NodeHttpLib,
readSuccessResponseJsonOrThrow,
} from "@gnu-taler/taler-wallet-core";
import { createPlatformHttpLib, readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { makeNoFeeCoinConfig } from "../harness/denomStructures.js";
import {
BankService,
ExchangeService,
GlobalTestState,
MerchantPrivateApi,
MerchantService,
setupDb,
WalletCli,
getPayto,
} from "../harness/harness.js";
import { startWithdrawViaBank, withdrawViaBank } from "../harness/helpers.js";
import { withdrawViaBank } from "../harness/helpers.js";
async function applyTimeTravel(
timetravelDuration: Duration,
@ -69,7 +65,7 @@ async function applyTimeTravel(
}
}
const http = new NodeHttpLib();
const http = createPlatformHttpLib();
/**
* Basic time travel test.

View File

@ -17,11 +17,14 @@
/**
* Imports.
*/
import { Duration, j2s, NotificationType } from "@gnu-taler/taler-util";
import {
Duration,
j2s,
NotificationType,
} from "@gnu-taler/taler-util";
import {
BankAccessApi,
BankApi,
NodeHttpLib,
WalletApiOperation,
} from "@gnu-taler/taler-wallet-core";
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
@ -37,6 +40,7 @@ import {
} from "../harness/harness.js";
import { EnvOptions, SimpleTestEnvironmentNg } from "../harness/helpers.js";
import * as http from "node:http";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
export async function createKycTestkudosEnvironment(
t: GlobalTestState,
@ -336,12 +340,12 @@ export async function runKycTest(t: GlobalTestState) {
// We now simulate the user interacting with the KYC service,
// which would usually done in the browser.
const httpClient = new NodeHttpLib();
const kycServerResp = await httpClient.get(kycNotif.kycUrl);
const httpLib = createPlatformHttpLib();
const kycServerResp = await httpLib.get(kycNotif.kycUrl);
const kycLoginResp = await kycServerResp.json();
console.log("kyc server resp:", j2s(kycLoginResp));
const kycProofUrl = kycLoginResp.redirect_uri;
const proofHttpResp = await httpClient.get(kycProofUrl);
const proofHttpResp = await httpLib.get(kycProofUrl);
console.log("proof resp status", proofHttpResp.status);
console.log("resp headers", proofHttpResp.headers.toJSON());

View File

@ -24,7 +24,8 @@ import {
encodeCrock,
getRandomBytes,
} from "@gnu-taler/taler-util";
import { NodeHttpLib, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
BankService,
ExchangeService,
@ -38,7 +39,7 @@ import {
withdrawViaBank,
} from "../harness/helpers.js";
const httpLib = new NodeHttpLib();
const httpLib = createPlatformHttpLib();
interface Context {
merchant: MerchantService;

View File

@ -24,8 +24,8 @@ import {
BankApi,
BankAccessApi,
BankServiceHandle,
NodeHttpLib,
} from "@gnu-taler/taler-wallet-core";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
/**
* Run test for basic, bank-integrated withdrawal and payment.
@ -35,7 +35,7 @@ export async function runPaymentDemoTest(t: GlobalTestState) {
let bankInterface: BankServiceHandle = {
baseUrl: "https://bank.demo.taler.net/",
bankAccessApiBaseUrl: "https://bank.demo.taler.net/",
http: new NodeHttpLib(),
http: createPlatformHttpLib(),
};
let user = await BankApi.createRandomBankUser(bankInterface);
let wop = await BankAccessApi.createWithdrawalOperation(

View File

@ -17,23 +17,10 @@
/**
* Imports.
*/
import { j2s } from "@gnu-taler/taler-util";
import {
checkReserve,
CryptoDispatcher,
depositCoin,
downloadExchangeInfo,
findDenomOrThrow,
NodeHttpLib,
refreshCoin,
SynchronousCryptoWorkerFactoryNode,
TalerError,
topupReserveWithDemobank,
WalletApiOperation,
withdrawCoin,
} from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, WalletCli } from "../harness/harness.js";
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
/**
* Run test for the different crypto workers.

View File

@ -17,17 +17,16 @@
/**
* Imports.
*/
import { j2s } from "@gnu-taler/taler-util";
import { j2s, TalerError } from "@gnu-taler/taler-util";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
import {
checkReserve,
CryptoDispatcher,
depositCoin,
downloadExchangeInfo,
findDenomOrThrow,
NodeHttpLib,
refreshCoin,
SynchronousCryptoWorkerFactoryNode,
TalerError,
SynchronousCryptoWorkerFactoryPlain,
topupReserveWithDemobank,
withdrawCoin,
} from "@gnu-taler/taler-wallet-core";
@ -42,9 +41,9 @@ export async function runWalletDblessTest(t: GlobalTestState) {
const { bank, exchange } = await createSimpleTestkudosEnvironment(t);
const http = new NodeHttpLib();
const http = createPlatformHttpLib();
const cryptiDisp = new CryptoDispatcher(
new SynchronousCryptoWorkerFactoryNode(),
new SynchronousCryptoWorkerFactoryPlain(),
);
const cryptoApi = cryptiDisp.cryptoApi;

View File

@ -37,13 +37,13 @@ import {
Configuration,
decodeCrock,
} from "@gnu-taler/taler-util";
import {
NodeHttpLib,
readSuccessResponseJsonOrThrow,
} from "@gnu-taler/taler-wallet-core";
import { URL } from "url";
import { spawn } from "child_process";
import { delayMs } from "./harness/harness.js";
import {
createPlatformHttpLib,
readSuccessResponseJsonOrThrow,
} from "@gnu-taler/taler-util/http";
interface BasicConf {
mainCurrency: string;
@ -53,7 +53,7 @@ interface PubkeyConf {
masterPublicKey: string;
}
const httpLib = new NodeHttpLib();
const httpLib = createPlatformHttpLib();
interface ShellResult {
stdout: string;

View File

@ -15,12 +15,38 @@
},
"./twrpc": {
"default": "./lib/twrpc.js"
},
"./compat": {
"types": "./lib/compat.node.js",
"node": "./lib/compat.node.js",
"qtart": "./lib/compat.qtart.js",
"default": "./lib/not-implemented.js"
},
"./clk": {
"default": "./lib/clk.js"
},
"./http": {
"default": "./lib/http.js"
},
"./qtart": {
"types": "./lib/qtart.js",
"qtart": "./lib/qtart.js",
"default": "./lib/not-implemented.js"
}
},
"imports": {
"#twrpc-impl": {
"node": "./lib/twrpc-impl.node.js",
"default": "./lib/twrpc-impl.missing.js"
"node": "./lib/twrpc-impl.node.js"
},
"#compat-impl": {
"node": "./lib/compat.node.js",
"qtart": "./lib/compat.qtart.js",
"type": "./lib/compat.d.ts"
},
"#http-impl": {
"type": "./lib/http-impl.node.js",
"node": "./lib/http-impl.node.js",
"qtart": "./lib/http-impl.qtart.js"
}
},
"scripts": {

View File

@ -17,10 +17,12 @@
/**
* Imports.
*/
import process from "process";
import path from "path";
import readline from "readline";
import { devNull } from "os";
import {
processExit,
processArgv,
readlinePrompt,
pathBasename,
} from "#compat-impl";
export namespace clk {
class Converter<T> {}
@ -359,13 +361,13 @@ export namespace clk {
console.error(
`error: unknown option '--${r.key}' for ${currentName}`,
);
process.exit(-1);
processExit(-1);
throw Error("not reached");
}
if (d.isFlag) {
if (r.value !== undefined) {
console.error(`error: flag '--${r.key}' does not take a value`);
process.exit(-1);
processExit(-1);
throw Error("not reached");
}
storeFlag(d, true);
@ -373,7 +375,7 @@ export namespace clk {
if (r.value === undefined) {
if (i === unparsedArgs.length - 1) {
console.error(`error: option '--${r.key}' needs an argument`);
process.exit(-1);
processExit(-1);
throw Error("not reached");
}
storeOption(d, unparsedArgs[i + 1]);
@ -391,7 +393,7 @@ export namespace clk {
const opt = this.shortOptions[chr];
if (!opt) {
console.error(`error: option '-${chr}' not known`);
process.exit(-1);
processExit(-1);
}
if (opt.isFlag) {
storeFlag(opt, true);
@ -399,7 +401,7 @@ export namespace clk {
if (si == optShort.length - 1) {
if (i === unparsedArgs.length - 1) {
console.error(`error: option '-${chr}' needs an argument`);
process.exit(-1);
processExit(-1);
throw Error("not reached");
} else {
storeOption(opt, unparsedArgs[i + 1]);
@ -418,7 +420,7 @@ export namespace clk {
const subcmd = this.subcommandMap[argVal];
if (!subcmd) {
console.error(`error: unknown command '${argVal}'`);
process.exit(-1);
processExit(-1);
throw Error("not reached");
}
foundSubcommand = subcmd.commandGroup;
@ -427,7 +429,7 @@ export namespace clk {
const d = this.arguments[posArgIndex];
if (!d) {
console.error(`error: too many arguments for ${currentName}`);
process.exit(-1);
processExit(-1);
throw Error("not reached");
}
myArgs[d.name] = unparsedArgs[i];
@ -437,7 +439,7 @@ export namespace clk {
if (parsedArgs[this.argKey].help) {
this.printHelp(progname, parents);
process.exit(0);
processExit(0);
throw Error("not reached");
}
@ -450,7 +452,7 @@ export namespace clk {
console.error(
`error: missing positional argument '${d.name}' for ${currentName}`,
);
process.exit(-1);
processExit(-1);
throw Error("not reached");
}
}
@ -464,7 +466,7 @@ export namespace clk {
} else {
const name = option.flagspec.join(",");
console.error(`error: missing option '${name}'`);
process.exit(-1);
processExit(-1);
throw Error("not reached");
}
}
@ -492,16 +494,16 @@ export namespace clk {
} catch (e) {
console.error(`An error occurred while running ${currentName}`);
console.error(e);
process.exit(1);
processExit(1);
}
Promise.resolve(r).catch((e) => {
console.error(`An error occurred while running ${currentName}`);
console.error(e);
process.exit(1);
processExit(1);
});
} else {
this.printHelp(progname, parents);
process.exit(-1);
processExit(-1);
throw Error("not reached");
}
}
@ -524,15 +526,15 @@ export namespace clk {
if (cmdlineArgs) {
args = cmdlineArgs;
} else {
args = process.argv.slice(1);
args = processArgv().slice(1);
}
if (args.length < 1) {
console.error(
"Error while parsing command line arguments: not enough arguments",
);
process.exit(-1);
processExit(-1);
}
const progname = path.basename(args[0]);
const progname = pathBasename(args[0]);
const rest = args.slice(1);
this.mainCommand.run(progname, [], rest, {});
@ -622,15 +624,6 @@ export namespace clk {
}
export function prompt(question: string): Promise<string> {
const stdinReadline = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise<string>((resolve, reject) => {
stdinReadline.question(question, (res) => {
resolve(res);
stdinReadline.close();
});
});
return readlinePrompt(question);
}
}

View File

@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
(C) 2019 GNUnet e.V.
(C) 2023 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@ -14,23 +14,9 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Imports.
*/
import { CryptoWorkerFactory } from "./crypto-dispatcher.js";
import { CryptoWorker } from "./cryptoWorkerInterface.js";
import { SynchronousCryptoWorkerNode } from "./synchronousWorkerNode.js";
/**
* The synchronous crypto worker produced by this factory doesn't run in the
* background, but actually blocks the caller until the operation is done.
*/
export class SynchronousCryptoWorkerFactoryNode implements CryptoWorkerFactory {
startWorker(): CryptoWorker {
return new SynchronousCryptoWorkerNode();
}
getConcurrency(): number {
return 1;
}
}
export function processExit(status: number): never;
export function processArgv(): string[];
export function readlinePrompt(prompt: string): Promise<string>;
export function pathBasename(s: string): string;
export function setUnhandledRejectionHandler(h: (e: any) => void): void;
export function getenv(name: string): string | undefined;

View File

@ -0,0 +1,59 @@
/*
This file is part of GNU Taler
(C) 2023 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import process from "node:process";
import readline from "node:readline";
import path from "node:path";
import os from "node:os";
export function processExit(status: number): never {
process.exit(1);
}
export function processArgv(): string[] {
return [...process.argv];
}
export function readlinePrompt(prompt: string): Promise<string> {
const stdinReadline = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise<string>((resolve, reject) => {
stdinReadline.question(prompt, (res) => {
resolve(res);
stdinReadline.close();
});
});
}
export function pathBasename(p: string): string {
return path.basename(p);
}
export function pathHomedir(): string {
return os.homedir();
}
export function setUnhandledRejectionHandler(h: (e: any) => void): void {
process.on("unhandledRejection", (e) => {
h(e);
});
}
export function getenv(name: string): string | undefined {
return process.env[name];
}

View File

@ -0,0 +1,53 @@
/*
This file is part of GNU Taler
(C) 2023 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
// qtart "std" library
// @ts-ignore
import * as std from "std";
export function processExit(status: number): never {
std.exit(status);
throw Error("not reached");
}
export function processArgv(): string[] {
// @ts-ignore
return ["qtart", ...globalThis.scriptArgs];
}
export function readlinePrompt(prompt: string): Promise<string> {
throw new Error("not supported");
}
export function pathBasename(p: string): string {
const slashIndex = p.lastIndexOf("/");
if (slashIndex < 0) {
return p;
}
return p.substring(0, slashIndex);
}
export function pathHomedir(): string {
return std.getenv("HOME");
}
export function setUnhandledRejectionHandler(h: (e: any) => void): void {
// not supported
}
export function getenv(name: string): string | undefined {
return std.getenv(name);
}

View File

@ -0,0 +1,39 @@
/*
This file is part of GNU Taler
(C) 2023 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
SPDX-License-Identifier: AGPL3.0-or-later
*/
const textEncoder = new TextEncoder();
export interface HttpLibArgs {
enableThrottling?: boolean,
}
export function encodeBody(body: any): ArrayBuffer {
if (body == null) {
return new ArrayBuffer(0);
}
if (typeof body === "string") {
return textEncoder.encode(body).buffer;
} else if (ArrayBuffer.isView(body)) {
return body.buffer;
} else if (body instanceof ArrayBuffer) {
return body;
} else if (typeof body === "object") {
return textEncoder.encode(JSON.stringify(body)).buffer;
}
throw new TypeError("unsupported request body type");
}

View File

@ -0,0 +1,17 @@
import { HttpLibArgs } from "./http-common.js";
import { HttpRequestLibrary, HttpRequestOptions, HttpResponse } from "./http.js";
/**
* Implementation of the HTTP request library interface for node.
*/
export declare class HttpLibImpl implements HttpRequestLibrary {
private throttle;
private throttlingEnabled;
constructor(args?: HttpLibArgs);
/**
* Set whether requests should be throttled.
*/
setThrottling(enabled: boolean): void;
fetch(url: string, opt?: HttpRequestOptions): Promise<HttpResponse>;
get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse>;
postJson(url: string, body: any, opt?: HttpRequestOptions): Promise<HttpResponse>;
}

View File

@ -0,0 +1,175 @@
/*
This file is part of GNU Taler
(C) 2019 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* Imports.
*/
import * as http from "node:http";
import { RequestOptions } from "node:http";
import { TalerError } from "./errors.js";
import { encodeBody, HttpLibArgs } from "./http-common.js";
import {
DEFAULT_REQUEST_TIMEOUT_MS,
Headers,
HttpRequestLibrary,
HttpRequestOptions,
HttpResponse,
} from "./http.js";
import {
Logger,
RequestThrottler,
TalerErrorCode,
typedArrayConcat,
URL,
} from "./index.js";
const logger = new Logger("http-impl.node.ts");
const textDecoder = new TextDecoder();
/**
* Implementation of the HTTP request library interface for node.
*/
export class HttpLibImpl implements HttpRequestLibrary {
private throttle = new RequestThrottler();
private throttlingEnabled = true;
constructor(args?: HttpLibArgs) {
this.throttlingEnabled = args?.enableThrottling ?? false;
}
/**
* Set whether requests should be throttled.
*/
setThrottling(enabled: boolean): void {
this.throttlingEnabled = enabled;
}
async fetch(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
const method = opt?.method?.toUpperCase() ?? "GET";
let body = opt?.body;
logger.trace(`Requesting ${method} ${url}`);
const parsedUrl = new URL(url);
if (this.throttlingEnabled && this.throttle.applyThrottle(url)) {
throw TalerError.fromDetail(
TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED,
{
requestMethod: method,
requestUrl: url,
throttleStats: this.throttle.getThrottleStats(url),
},
`request to origin ${parsedUrl.origin} was throttled`,
);
}
let timeoutMs: number | undefined;
if (typeof opt?.timeout?.d_ms === "number") {
timeoutMs = opt.timeout.d_ms;
} else {
timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS;
}
const headers = { ...opt?.headers };
headers["Content-Type"] = "application/json";
let reqBody: ArrayBuffer | undefined;
if (opt?.method == "POST") {
reqBody = encodeBody(opt.body);
}
const options: RequestOptions = {
protocol: parsedUrl.protocol,
port: parsedUrl.port,
host: parsedUrl.host,
method: method,
path: parsedUrl.pathname,
headers: opt?.headers,
};
const chunks: Uint8Array[] = [];
return new Promise((resolve, reject) => {
const req = http.request(options, (res) => {
res.on("data", (d) => {
chunks.push(d);
});
res.on("end", () => {
const headers: Headers = new Headers();
for (const [k, v] of Object.entries(res.headers)) {
if (!v) {
continue;
}
if (typeof v === "string") {
headers.set(k, v);
} else {
headers.set(k, v.join(", "));
}
}
const data = typedArrayConcat(chunks);
const resp: HttpResponse = {
requestMethod: method,
requestUrl: parsedUrl.href,
status: res.statusCode || 0,
headers,
async bytes() {
return data;
},
json() {
const text = textDecoder.decode(data);
return JSON.parse(text);
},
async text() {
const text = textDecoder.decode(data);
return text;
},
};
resolve(resp);
});
res.on("error", (e) => {
reject(e);
});
});
if (reqBody) {
req.write(reqBody);
}
req.end();
});
}
async get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
return this.fetch(url, {
method: "GET",
...opt,
});
}
async postJson(
url: string,
body: any,
opt?: HttpRequestOptions,
): Promise<HttpResponse> {
return this.fetch(url, {
method: "POST",
body,
...opt,
});
}
}

View File

@ -0,0 +1,127 @@
/*
This file is part of GNU Taler
(C) 2019 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* Imports.
*/
import { Logger } from "@gnu-taler/taler-util";
import { TalerError } from "./errors.js";
import { encodeBody, HttpLibArgs } from "./http-common.js";
import {
Headers,
HttpRequestLibrary,
HttpRequestOptions,
HttpResponse,
} from "./http.js";
import { RequestThrottler, TalerErrorCode, URL } from "./index.js";
import { qjsOs } from "./qtart.js";
const logger = new Logger("http-impl.qtart.ts");
const textDecoder = new TextDecoder();
/**
* Implementation of the HTTP request library interface for node.
*/
export class HttpLibImpl implements HttpRequestLibrary {
private throttle = new RequestThrottler();
private throttlingEnabled = true;
constructor(args?: HttpLibArgs) {
this.throttlingEnabled = args?.enableThrottling ?? false;
}
/**
* Set whether requests should be throttled.
*/
setThrottling(enabled: boolean): void {
this.throttlingEnabled = enabled;
}
async fetch(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
const method = opt?.method ?? "GET";
logger.trace(`Requesting ${method} ${url}`);
const parsedUrl = new URL(url);
if (this.throttlingEnabled && this.throttle.applyThrottle(url)) {
throw TalerError.fromDetail(
TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED,
{
requestMethod: method,
requestUrl: url,
throttleStats: this.throttle.getThrottleStats(url),
},
`request to origin ${parsedUrl.origin} was throttled`,
);
}
let data: ArrayBuffer | undefined = undefined;
let headers: string[] = [];
if (opt?.headers) {
for (let headerName of Object.keys(opt.headers)) {
headers.push(`${headerName}: ${opt.headers[headerName]}`);
}
}
if (method.toUpperCase() === "POST") {
data = encodeBody(opt?.body);
}
const res = await qjsOs.fetchHttp(url, {
method,
data,
headers,
});
return {
requestMethod: method,
// FIXME: We don't return headers!
headers: new Headers(),
async bytes() {
return res.data;
},
json() {
const text = textDecoder.decode(res.data);
return JSON.parse(text);
},
async text() {
const text = textDecoder.decode(res.data);
return text;
},
requestUrl: url,
status: res.status,
};
}
async get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
return this.fetch(url, {
method: "GET",
...opt,
});
}
async postJson(
url: string,
body: any,
opt?: HttpRequestOptions,
): Promise<HttpResponse> {
return this.fetch(url, {
method: "POST",
body,
...opt,
});
}
}

View File

@ -34,7 +34,9 @@ import {
CancellationToken,
} from "@gnu-taler/taler-util";
import { TalerErrorCode } from "@gnu-taler/taler-util";
import { makeErrorDetail, TalerError } from "../errors.js";
import { makeErrorDetail, TalerError } from "./errors.js";
import * as impl from "#http-impl";
import { HttpLibArgs } from "./http-common.js";
const logger = new Logger("http.ts");
@ -352,3 +354,7 @@ export function getExpiry(
}
return t;
}
export function createPlatformHttpLib(args?: HttpLibArgs): HttpRequestLibrary {
return new impl.HttpLibImpl(args);
}

View File

@ -19,3 +19,7 @@
import { loadBrowserPrng } from "./prng-browser.js";
loadBrowserPrng();
export * from "./index.js";
// The web stuff doesn't support package.json export declarations yet,
// so we export more stuff here than we should.
export * from "./http.js";

View File

@ -21,4 +21,3 @@ initNodePrng();
export * from "./index.js";
export * from "./talerconfig.js";
export * from "./globbing/minimatch.js";
export { clk } from "./clk.js";

View File

@ -36,3 +36,4 @@ export * from "./CancellationToken.js";
export * from "./contract-terms.js";
export * from "./base64.js";
export * from "./merchant-api-types.js";
export * from "./errors.js";

View File

@ -0,0 +1,36 @@
// @ts-ignore
import * as _qjsOsImp from "os";
// @ts-ignore
import * as _qjsStdImp from "std";
export interface QjsHttpResp {
status: number;
data: ArrayBuffer;
}
export interface QjsHttpOptions {
method: string;
debug?: boolean;
data?: ArrayBuffer;
headers?: string[];
}
export interface QjsOsLib {
fetchHttp(url: string, options?: QjsHttpOptions): Promise<QjsHttpResp>;
postMessageToHost(s: string): void;
setMessageFromHostHandler(h: (s: string) => void): void;
rename(oldPath: string, newPath: string): number;
}
export interface QjsStdLib {
writeFile(filename: string, contents: string): void;
loadFile(filename: string): string;
}
// This is not the nodejs "os" module, but the qjs "os" module.
export const qjsOs: QjsOsLib = _qjsOsImp as any;
export const qjsStd: QjsStdLib = _qjsStdImp as any;

View File

@ -14,4 +14,13 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import type { RpcConnectArgs, RpcServerArgs } from "./twrpc.js";
// Not implemented.
export async function connectRpc<T>(args: RpcConnectArgs<T>): Promise<T> {
throw Error("not implemented");
}
export async function runRpcServer(args: RpcServerArgs): Promise<void> {
throw Error("not implemented");
}

View File

@ -53,7 +53,7 @@ function git_hash() {
export const buildConfig = {
entryPoints: ["src/index.ts"],
outfile: "dist/taler-wallet-cli.mjs",
outfile: "dist/taler-wallet-cli.qtart.mjs",
bundle: true,
minify: false,
target: [
@ -61,7 +61,11 @@ export const buildConfig = {
],
format: 'esm',
platform: 'neutral',
mainFields: ["module", "main"],
conditions: ["qtart"],
sourcemap: true,
// quickjs standard library
external: ["std", "os"],
define: {
'__VERSION__': `"${_package.version}"`,
'__GIT_HASH__': `"${GIT_HASH}"`,

View File

@ -21,12 +21,11 @@ import {
addPaytoQueryParams,
AgeRestriction,
classifyTalerUri,
clk,
codecForList,
codecForString,
CoreApiResponse,
decodeCrock,
encodeCrock,
getErrorDetailFromException,
getRandomBytes,
j2s,
Logger,
@ -35,20 +34,26 @@ import {
RecoveryMergeStrategy,
setDangerousTimetravel,
setGlobalLogLevelFromString,
summarizeTalerErrorDetail,
TalerUriType,
WalletNotification,
} from "@gnu-taler/taler-util";
import { clk } from "@gnu-taler/taler-util/clk";
import {
getenv,
pathHomedir,
processExit,
setUnhandledRejectionHandler,
} from "@gnu-taler/taler-util/compat";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
import { JsonMessage, runRpcServer } from "@gnu-taler/taler-util/twrpc";
import {
createNativeWalletHost,
createNativeWalletHost2,
CryptoDispatcher,
getDefaultNodeWallet,
getDefaultNodeWallet2,
getErrorDetailFromException,
nativeCrypto,
NodeHttpLib,
NodeThreadCryptoWorkerFactory,
summarizeTalerErrorDetail,
SynchronousCryptoWorkerFactoryNode,
//NodeThreadCryptoWorkerFactory,
//SynchronousCryptoWorkerFactoryPlain,
TalerCryptoInterface,
Wallet,
WalletApiOperation,
@ -60,8 +65,6 @@ import {
getClientFromRemoteWallet,
makeNotificationWaiter,
} from "@gnu-taler/taler-wallet-core/remote";
import fs from "fs";
import os from "os";
// This module also serves as the entry point for the crypto
// thread worker, and thus must expose these two handlers.
@ -76,13 +79,13 @@ const EXIT_EXCEPTION = 4;
const EXIT_API_ERROR = 5;
const EXIT_RETRIES_EXCEEDED = 6;
process.on("unhandledRejection", (error: any) => {
setUnhandledRejectionHandler((error: any) => {
logger.error("unhandledRejection", error.message);
logger.error("stack", error.stack);
process.exit(2);
processExit(1);
});
const defaultWalletDbPath = os.homedir + "/" + ".talerwalletdb.json";
const defaultWalletDbPath = pathHomedir() + "/" + ".talerwalletdb.json";
function assertUnreachable(x: never): never {
throw new Error("Didn't expect to get here");
@ -99,7 +102,7 @@ async function doPay(
if (result.status === PreparePayResultType.InsufficientBalance) {
console.log("contract", result.contractTerms);
console.error("insufficient balance");
process.exit(1);
processExit(1);
return;
}
if (result.status === PreparePayResultType.AlreadyConfirmed) {
@ -108,8 +111,7 @@ async function doPay(
} else {
console.log("payment already in progress");
}
process.exit(0);
processExit(0);
return;
}
if (result.status === "payment-possible") {
@ -154,7 +156,7 @@ function applyVerbose(verbose: boolean): void {
declare const __VERSION__: string;
function printVersion(): void {
console.log(__VERSION__);
process.exit(0);
processExit(0);
}
export const walletCli = clk
@ -203,7 +205,7 @@ export const walletCli = clk
type WalletCliArgsType = clk.GetArgType<typeof walletCli>;
function checkEnvFlag(name: string): boolean {
const val = process.env[name];
const val = getenv(name);
if (val == "1") {
return true;
}
@ -238,11 +240,10 @@ async function createLocalWallet(
notificationHandler?: (n: WalletNotification) => void,
): Promise<Wallet> {
const dbPath = walletCliArgs.wallet.walletDbFile ?? defaultWalletDbPath;
const myHttpLib = new NodeHttpLib();
if (walletCliArgs.wallet.noThrottle) {
myHttpLib.setThrottling(false);
}
const wallet = await getDefaultNodeWallet({
const myHttpLib = createPlatformHttpLib({
enableThrottling: walletCliArgs.wallet.noThrottle ? false : true,
});
const wallet = await createNativeWalletHost({
persistentStoragePath: dbPath !== ":memory:" ? dbPath : undefined,
httpLib: myHttpLib,
notifyHandler: (n) => {
@ -268,7 +269,7 @@ async function createLocalWallet(
const ed = getErrorDetailFromException(e);
console.error("Operation failed: " + summarizeTalerErrorDetail(ed));
console.error("Error details:", JSON.stringify(ed, undefined, 2));
process.exit(1);
processExit(1);
} finally {
logger.trace("operation with wallet finished, stopping");
logger.trace("stopped wallet");
@ -357,7 +358,7 @@ walletCli
requestJson = JSON.parse(args.api.request);
} catch (e) {
console.error("Invalid JSON");
process.exit(1);
processExit(1);
}
try {
const resp = await wallet.makeCoreApiRequest(
@ -367,12 +368,12 @@ walletCli
console.log(JSON.stringify(resp, undefined, 2));
if (resp.type === "error") {
if (args.api.expectSuccess) {
process.exit(EXIT_API_ERROR);
processExit(EXIT_API_ERROR);
}
}
} catch (e) {
logger.error(`Got exception while handling API request ${e}`);
process.exit(EXIT_EXCEPTION);
processExit(EXIT_EXCEPTION);
}
});
logger.info("finished handling API request");
@ -475,7 +476,7 @@ walletCli
});
wallet.ws.stop();
if (resp.retriesExceeded && args.finishPendingOpt.failOnMaxRetries) {
process.exit(EXIT_RETRIES_EXCEEDED);
processExit(EXIT_RETRIES_EXCEEDED);
}
});
});
@ -594,7 +595,7 @@ walletCli
const selectedExchange = withdrawInfo.defaultExchangeBaseUrl;
if (!selectedExchange) {
console.error("no suggested exchange!");
process.exit(1);
processExit(1);
return;
}
const res = await wallet.client.call(
@ -1014,9 +1015,10 @@ advancedCli
help: "Run the 'bench-internal' benchmark",
})
.action(async (args) => {
const myHttpLib = new NodeHttpLib();
myHttpLib.setThrottling(false);
const res = await getDefaultNodeWallet2({
const myHttpLib = createPlatformHttpLib({
enableThrottling: false,
});
const res = await createNativeWalletHost2({
// No persistent DB storage.
persistentStoragePath: undefined,
httpLib: myHttpLib,
@ -1060,15 +1062,6 @@ advancedCli
});
});
advancedCli
.subcommand("decode", "decode", {
help: "Decode base32-crockford.",
})
.action((args) => {
const enc = fs.readFileSync(0, "utf8");
console.log(decodeCrock(enc.trim()));
});
advancedCli
.subcommand("genSegwit", "gen-segwit")
.requiredArgument("paytoUri", clk.STRING)
@ -1229,7 +1222,7 @@ advancedCli
);
} catch (e: any) {
console.log("could not parse coin list:", e.message);
process.exit(1);
processExit(1);
}
for (const c of coinPubList) {
await wallet.client.call(WalletApiOperation.SetCoinSuspended, {
@ -1254,7 +1247,7 @@ advancedCli
);
} catch (e: any) {
console.log("could not parse coin list:", e.message);
process.exit(1);
processExit(1);
}
for (const c of coinPubList) {
await wallet.client.call(WalletApiOperation.SetCoinSuspended, {
@ -1420,33 +1413,33 @@ async function read(stream: NodeJS.ReadStream) {
return Buffer.concat(chunks).toString("utf8");
}
testCli
.subcommand("cryptoworker", "cryptoworker")
.maybeOption("impl", ["--impl"], clk.STRING)
.action(async (args) => {
let cryptoApi: TalerCryptoInterface;
if (!args.cryptoworker.impl || args.cryptoworker.impl === "node") {
const workerFactory = new NodeThreadCryptoWorkerFactory();
const cryptoDisp = new CryptoDispatcher(workerFactory);
cryptoApi = cryptoDisp.cryptoApi;
} else if (args.cryptoworker.impl === "sync") {
const workerFactory = new SynchronousCryptoWorkerFactoryNode();
const cryptoDisp = new CryptoDispatcher(workerFactory);
cryptoApi = cryptoDisp.cryptoApi;
} else if (args.cryptoworker.impl === "none") {
cryptoApi = nativeCrypto;
} else {
throw Error(`invalid crypto worker type ${args.cryptoworker.impl}`);
}
// testCli
// .subcommand("cryptoworker", "cryptoworker")
// .maybeOption("impl", ["--impl"], clk.STRING)
// .action(async (args) => {
// let cryptoApi: TalerCryptoInterface;
// if (!args.cryptoworker.impl || args.cryptoworker.impl === "node") {
// const workerFactory = new NodeThreadCryptoWorkerFactory();
// const cryptoDisp = new CryptoDispatcher(workerFactory);
// cryptoApi = cryptoDisp.cryptoApi;
// } else if (args.cryptoworker.impl === "sync") {
// const workerFactory = new SynchronousCryptoWorkerFactoryPlain();
// const cryptoDisp = new CryptoDispatcher(workerFactory);
// cryptoApi = cryptoDisp.cryptoApi;
// } else if (args.cryptoworker.impl === "none") {
// cryptoApi = nativeCrypto;
// } else {
// throw Error(`invalid crypto worker type ${args.cryptoworker.impl}`);
// }
const input = "foo";
console.log(`testing crypto worker by hashing string '${input}'`);
const res = await cryptoApi.hashString({ str: input });
console.log(res);
});
// const input = "foo";
// console.log(`testing crypto worker by hashing string '${input}'`);
// const res = await cryptoApi.hashString({ str: input });
// console.log(res);
// });
export function main() {
if (process.env["TALER_WALLET_DEBUG_DENOMSEL_ALLOW_LATE"]) {
if (getenv("TALER_WALLET_DEBUG_DENOMSEL_ALLOW_LATE")) {
logger.warn("Allowing withdrawal of late denominations for debugging");
walletCoreDebugFlags.denomselAllowLate = true;
}

View File

@ -38,7 +38,13 @@
"default": "./lib/index.js"
},
"./remote": {
"node": "./lib/remote.js"
"default": "./lib/remote.js"
}
},
"imports": {
"#host-impl": {
"node": "./lib/host-impl.node.js",
"qtart": "./lib/host-impl.qtart.js"
}
},
"devDependencies": {

View File

@ -33,13 +33,10 @@ import {
j2s,
Logger,
stringToBytes,
TalerError,
TalerErrorCode,
} from "@gnu-taler/taler-util";
import { TalerError } from "./errors.js";
import {
HttpRequestLibrary,
readSuccessResponseJsonOrThrow,
} from "./util/http.js";
import { HttpRequestLibrary, readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
const logger = new Logger("bank-api-client.ts");

View File

@ -21,8 +21,6 @@ import {
CryptoWorker,
CryptoWorkerResponseMessage,
} from "./cryptoWorkerInterface.js";
import { SynchronousCryptoWorkerFactoryNode } from "./synchronousWorkerFactoryNode.js";
import { processRequestWithImpl } from "./worker-common.js";
export class MyCryptoWorker implements CryptoWorker {
/**

View File

@ -24,7 +24,7 @@
* Imports.
*/
import { j2s, Logger, TalerErrorCode } from "@gnu-taler/taler-util";
import { TalerError } from "../../errors.js";
import { TalerError } from "@gnu-taler/taler-util";
import { openPromise } from "../../util/promiseUtils.js";
import { timer, performanceNow, TimerHandle } from "../../util/timer.js";
import { nullCrypto, TalerCryptoInterface } from "../cryptoImplementation.js";

View File

@ -1,92 +0,0 @@
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Imports.
*/
import { Logger } from "@gnu-taler/taler-util";
import child_process from "child_process";
import type internal from "stream";
import { OpenedPromise, openPromise } from "../../util/promiseUtils.js";
const logger = new Logger("synchronousWorkerFactory.ts");
/**
* Client for the crypto helper process (taler-crypto-worker from exchange.git).
*/
export class CryptoRpcClient {
proc: child_process.ChildProcessByStdio<
internal.Writable,
internal.Readable,
null
>;
requests: Array<{
p: OpenedPromise<any>;
req: any;
}> = [];
constructor() {
const stdoutChunks: Buffer[] = [];
this.proc = child_process.spawn("taler-crypto-worker", {
//stdio: ["pipe", "pipe", "inherit"],
stdio: ["pipe", "pipe", "inherit"],
detached: true,
});
this.proc.on("close", (): void => {
logger.error("child process exited");
});
(this.proc.stdout as any).unref();
(this.proc.stdin as any).unref();
this.proc.unref();
this.proc.stdout.on("data", (x) => {
if (x instanceof Buffer) {
const nlIndex = x.indexOf("\n");
if (nlIndex >= 0) {
const before = x.slice(0, nlIndex);
const after = x.slice(nlIndex + 1);
stdoutChunks.push(after);
const str = Buffer.concat([...stdoutChunks, before]).toString(
"utf-8",
);
const req = this.requests.shift();
if (!req) {
throw Error("request was undefined");
}
if (this.requests.length === 0) {
this.proc.unref();
}
//logger.info(`got response: ${str}`);
req.p.resolve(JSON.parse(str));
} else {
stdoutChunks.push(x);
}
} else {
throw Error(`unexpected data chunk type (${typeof x})`);
}
});
}
async queueRequest(req: any): Promise<any> {
const p = openPromise<any>();
if (this.requests.length === 0) {
this.proc.ref();
}
this.requests.push({ req, p });
this.proc.stdin.write(`${JSON.stringify(req)}\n`);
return p.promise;
}
}

View File

@ -1,174 +0,0 @@
/*
This file is part of GNU Taler
(C) 2019 GNUnet e.V.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { j2s, Logger } from "@gnu-taler/taler-util";
import {
nativeCryptoR,
TalerCryptoInterfaceR,
} from "../cryptoImplementation.js";
import { CryptoWorker } from "./cryptoWorkerInterface.js";
import { CryptoRpcClient } from "./rpcClient.js";
import { processRequestWithImpl } from "./worker-common.js";
const logger = new Logger("synchronousWorker.ts");
/**
* Worker implementation that uses node subprocesses.
*
* The node crypto worker can also use IPC to offload cryptographic
* operations to a helper process (usually written in C / part of taler-exchange).
*/
export class SynchronousCryptoWorkerNode implements CryptoWorker {
/**
* Function to be called when we receive a message from the worker thread.
*/
onmessage: undefined | ((m: any) => void);
/**
* Function to be called when we receive an error from the worker thread.
*/
onerror: undefined | ((m: any) => void);
cryptoImplR: TalerCryptoInterfaceR;
rpcClient: CryptoRpcClient | undefined;
constructor() {
this.onerror = undefined;
this.onmessage = undefined;
this.cryptoImplR = { ...nativeCryptoR };
if (process.env["TALER_WALLET_PRIMITIVE_WORKER"]) {
logger.info("using RPC for some crypto operations");
const rpc = (this.rpcClient = new CryptoRpcClient());
this.cryptoImplR.eddsaSign = async (_, req) => {
return await rpc.queueRequest({
op: "eddsa_sign",
args: {
msg: req.msg,
priv: req.priv,
},
});
};
this.cryptoImplR.setupRefreshPlanchet = async (_, req) => {
const res = await rpc.queueRequest({
op: "setup_refresh_planchet",
args: {
coin_index: req.coinNumber,
transfer_secret: req.transferSecret,
},
});
return {
bks: res.blinding_key,
coinPriv: res.coin_priv,
coinPub: res.coin_pub,
};
};
this.cryptoImplR.rsaBlind = async (_, req) => {
const res = await rpc.queueRequest({
op: "rsa_blind",
args: {
bks: req.bks,
hm: req.hm,
pub: req.pub,
},
});
return {
blinded: res.blinded,
};
};
this.cryptoImplR.keyExchangeEcdheEddsa = async (_, req) => {
const res = await rpc.queueRequest({
op: "kx_ecdhe_eddsa",
args: {
ecdhe_priv: req.ecdhePriv,
eddsa_pub: req.eddsaPub,
},
});
return {
h: res.h,
};
};
this.cryptoImplR.eddsaGetPublic = async (_, req) => {
const res = await rpc.queueRequest({
op: "eddsa_get_public",
args: {
eddsa_priv: req.priv,
},
});
return {
pub: res.eddsa_pub,
};
};
this.cryptoImplR.ecdheGetPublic = async (_, req) => {
const res = await rpc.queueRequest({
op: "ecdhe_get_public",
args: {
ecdhe_priv: req.priv,
},
});
return {
pub: res.ecdhe_pub,
};
};
}
}
/**
* Add an event listener for either an "error" or "message" event.
*/
addEventListener(event: "message" | "error", fn: (x: any) => void): void {
switch (event) {
case "message":
this.onmessage = fn;
break;
case "error":
this.onerror = fn;
break;
}
}
private dispatchMessage(msg: any): void {
if (this.onmessage) {
this.onmessage(msg);
}
}
/**
* Send a message to the worker thread.
*/
postMessage(msg: any): void {
const handleRequest = async () => {
const responseMsg = await processRequestWithImpl(msg, this.cryptoImplR);
try {
setTimeout(() => this.dispatchMessage(responseMsg), 0);
} catch (e) {
logger.error("got error during dispatch", e);
}
};
handleRequest().catch((e) => {
logger.error("Error while handling crypto request:", e);
});
}
/**
* Forcibly terminate the worker thread.
*/
terminate(): void {
// This is a no-op.
}
}

View File

@ -23,7 +23,7 @@ import {
stringifyError as safeStringifyError,
TalerErrorCode,
} from "@gnu-taler/taler-util";
import { getErrorDetailFromException, makeErrorDetail } from "../../errors.js";
import { getErrorDetailFromException, makeErrorDetail } from "@gnu-taler/taler-util";
import { TalerCryptoInterfaceR } from "../cryptoImplementation.js";
import {
CryptoWorkerRequestMessage,

View File

@ -2659,6 +2659,9 @@ function onMetaDbUpgradeNeeded(
/**
* Return a promise that resolves
* to the taler wallet db.
*
* @param onVersionChange Called when another client concurrenctly connects to the database
* with a higher version.
*/
export async function openTalerDatabase(
idbFactory: IDBFactory,

View File

@ -58,7 +58,7 @@ import {
import {
HttpRequestLibrary,
readSuccessResponseJsonOrThrow,
} from "./util/http.js";
} from "@gnu-taler/taler-util/http";
import {
getBankStatusUrl,
getBankWithdrawalInfo,

View File

@ -32,7 +32,7 @@ import {
HttpRequestLibrary,
HttpRequestOptions,
HttpResponse,
} from "./util/http.js";
} from "@gnu-taler/taler-util/http";
const logger = new Logger("dev-experiments.ts");

View File

@ -1,183 +0,0 @@
/*
This file is part of GNU Taler
(C) 2019 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* Imports.
*/
import {
DEFAULT_REQUEST_TIMEOUT_MS,
Headers,
HttpRequestLibrary,
HttpRequestOptions,
HttpResponse,
} from "../util/http.js";
import { RequestThrottler } from "@gnu-taler/taler-util";
import axios, { AxiosResponse } from "axios";
import { TalerError } from "../errors.js";
import { Logger, bytesToString } from "@gnu-taler/taler-util";
import { TalerErrorCode, URL } from "@gnu-taler/taler-util";
const logger = new Logger("NodeHttpLib.ts");
/**
* Implementation of the HTTP request library interface for node.
*/
export class NodeHttpLib implements HttpRequestLibrary {
private throttle = new RequestThrottler();
private throttlingEnabled = true;
/**
* Set whether requests should be throttled.
*/
setThrottling(enabled: boolean): void {
this.throttlingEnabled = enabled;
}
async fetch(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
const method = opt?.method ?? "GET";
let body = opt?.body;
logger.trace(`Requesting ${method} ${url}`);
const parsedUrl = new URL(url);
if (this.throttlingEnabled && this.throttle.applyThrottle(url)) {
throw TalerError.fromDetail(
TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED,
{
requestMethod: method,
requestUrl: url,
throttleStats: this.throttle.getThrottleStats(url),
},
`request to origin ${parsedUrl.origin} was throttled`,
);
}
let timeoutMs: number | undefined;
if (typeof opt?.timeout?.d_ms === "number") {
timeoutMs = opt.timeout.d_ms;
} else {
timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS;
}
// FIXME: Use AbortController / etc. to handle cancellation
let resp: AxiosResponse;
try {
let respPromise = axios.default({
method,
url: url,
responseType: "arraybuffer",
headers: opt?.headers,
validateStatus: () => true,
transformResponse: (x) => x,
data: body,
timeout: timeoutMs,
maxRedirects: 0,
});
if (opt?.cancellationToken) {
respPromise = opt.cancellationToken.racePromise(respPromise);
}
resp = await respPromise;
} catch (e: any) {
throw TalerError.fromDetail(
TalerErrorCode.WALLET_NETWORK_ERROR,
{
requestUrl: url,
requestMethod: method,
},
`${e.message}`,
);
}
const makeText = async (): Promise<string> => {
opt?.cancellationToken?.throwIfCancelled();
const respText = new Uint8Array(resp.data);
return bytesToString(respText);
};
const makeJson = async (): Promise<any> => {
opt?.cancellationToken?.throwIfCancelled();
let responseJson;
const respText = await makeText();
try {
responseJson = JSON.parse(respText);
} catch (e) {
logger.trace(`invalid json: '${resp.data}'`);
throw TalerError.fromDetail(
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
{
httpStatusCode: resp.status,
requestUrl: url,
requestMethod: method,
},
"Could not parse response body as JSON",
);
}
if (responseJson === null || typeof responseJson !== "object") {
logger.trace(`invalid json (not an object): '${respText}'`);
throw TalerError.fromDetail(
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
{
httpStatusCode: resp.status,
requestUrl: url,
requestMethod: method,
},
`invalid JSON`,
);
}
return responseJson;
};
const makeBytes = async () => {
opt?.cancellationToken?.throwIfCancelled();
if (typeof resp.data.byteLength !== "number") {
throw Error("expected array buffer");
}
const buf = resp.data;
return buf;
};
const headers = new Headers();
for (const hn of Object.keys(resp.headers)) {
headers.set(hn, resp.headers[hn]);
}
return {
requestUrl: url,
requestMethod: method,
headers,
status: resp.status,
text: makeText,
json: makeJson,
bytes: makeBytes,
};
}
async get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
return this.fetch(url, {
method: "GET",
...opt,
});
}
async postJson(
url: string,
body: any,
opt?: HttpRequestOptions,
): Promise<HttpResponse> {
return this.fetch(url, {
method: "POST",
body,
...opt,
});
}
}

View File

@ -0,0 +1,60 @@
/*
This file is part of GNU Taler
(C) 2019 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { WalletNotification } from "@gnu-taler/taler-util";
import { HttpRequestLibrary } from "@gnu-taler/taler-util/http";
/**
* Helpers to initiate a wallet in a host environment.
*/
/**
*/
export interface DefaultNodeWalletArgs {
/**
* Location of the wallet database.
*
* If not specified, the wallet starts out with an empty database and
* the wallet database is stored only in memory.
*/
persistentStoragePath?: string;
/**
* Handler for asynchronous notifications from the wallet.
*/
notifyHandler?: (n: WalletNotification) => void;
/**
* If specified, use this as HTTP request library instead
* of the default one.
*/
httpLib?: HttpRequestLibrary;
cryptoWorkerType?: "sync" | "node-worker-thread";
}
/**
* Generate a random alphanumeric ID. Does *not* use cryptographically
* secure randomness.
*/
export function makeTempfileId(length: number): string {
let result = "";
const characters = "abcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
}

View File

@ -30,70 +30,27 @@ import {
shimIndexedDB,
} from "@gnu-taler/idb-bridge";
import { AccessStats } from "@gnu-taler/idb-bridge";
import { Logger, WalletNotification } from "@gnu-taler/taler-util";
import { Logger } from "@gnu-taler/taler-util";
import * as fs from "fs";
import { NodeThreadCryptoWorkerFactory } from "../crypto/workers/nodeThreadWorker.js";
import { SynchronousCryptoWorkerFactoryNode } from "../crypto/workers/synchronousWorkerFactoryNode.js";
import { openTalerDatabase } from "../index.js";
import { HttpRequestLibrary } from "../util/http.js";
import { SetTimeoutTimerAPI } from "../util/timer.js";
import { Wallet } from "../wallet.js";
import { NodeHttpLib } from "./NodeHttpLib.js";
import { NodeThreadCryptoWorkerFactory } from "./crypto/workers/nodeThreadWorker.js";
import { SynchronousCryptoWorkerFactoryPlain } from "./crypto/workers/synchronousWorkerFactoryPlain.js";
import { openTalerDatabase } from "./index.js";
import {
createPlatformHttpLib,
} from "@gnu-taler/taler-util/http";
import { SetTimeoutTimerAPI } from "./util/timer.js";
import { Wallet } from "./wallet.js";
import { DefaultNodeWalletArgs, makeTempfileId } from "./host-common.js";
const logger = new Logger("headless/helpers.ts");
const logger = new Logger("host-impl.node.ts");
export interface DefaultNodeWalletArgs {
/**
* Location of the wallet database.
*
* If not specified, the wallet starts out with an empty database and
* the wallet database is stored only in memory.
*/
persistentStoragePath?: string;
/**
* Handler for asynchronous notifications from the wallet.
*/
notifyHandler?: (n: WalletNotification) => void;
/**
* If specified, use this as HTTP request library instead
* of the default one.
*/
httpLib?: HttpRequestLibrary;
cryptoWorkerType?: "sync" | "node-worker-thread";
}
/**
* Generate a random alphanumeric ID. Does *not* use cryptographically
* secure randomness.
*/
function makeId(length: number): string {
let result = "";
const characters = "abcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
}
/**
* Get a wallet instance with default settings for node.
*/
export async function getDefaultNodeWallet(
args: DefaultNodeWalletArgs = {},
): Promise<Wallet> {
const res = await getDefaultNodeWallet2(args);
return res.wallet;
}
/**
* Get a wallet instance with default settings for node.
*
* Extended version that allows getting DB stats.
*/
export async function getDefaultNodeWallet2(
export async function createNativeWalletHost2(
args: DefaultNodeWalletArgs = {},
): Promise<{
wallet: Wallet;
@ -127,7 +84,8 @@ export async function getDefaultNodeWallet2(
if (args.persistentStoragePath === undefined) {
return;
}
const tmpPath = `${args.persistentStoragePath}-${makeId(5)}.tmp`;
const tmpPath = `${args.persistentStoragePath}-${makeTempfileId(5)}.tmp`;
logger.trace("exported DB dump");
const dbContent = myBackend.exportDump();
fs.writeFileSync(tmpPath, JSON.stringify(dbContent, undefined, 2), {
encoding: "utf-8",
@ -147,7 +105,9 @@ export async function getDefaultNodeWallet2(
if (args.httpLib) {
myHttpLib = args.httpLib;
} else {
myHttpLib = new NodeHttpLib();
myHttpLib = createPlatformHttpLib({
enableThrottling: true,
});
}
const myVersionChange = (): Promise<void> => {
@ -165,7 +125,7 @@ export async function getDefaultNodeWallet2(
const cryptoWorkerType = args.cryptoWorkerType ?? "node-worker-thread";
if (cryptoWorkerType === "sync") {
logger.info("using synchronous crypto worker");
workerFactory = new SynchronousCryptoWorkerFactoryNode();
workerFactory = new SynchronousCryptoWorkerFactoryPlain();
} else if (cryptoWorkerType === "node-worker-thread") {
try {
// Try if we have worker threads available, fails in older node versions.
@ -179,7 +139,7 @@ export async function getDefaultNodeWallet2(
logger.warn(
"worker threads not available, falling back to synchronous workers",
);
workerFactory = new SynchronousCryptoWorkerFactoryNode();
workerFactory = new SynchronousCryptoWorkerFactoryPlain();
}
} else {
throw Error(`unsupported crypto worker type '${cryptoWorkerType}'`);

View File

@ -0,0 +1,120 @@
/*
This file is part of GNU Taler
(C) 2019 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Helpers to create headless wallets.
* @author Florian Dold <dold@taler.net>
*/
/**
* Imports.
*/
import type { IDBFactory } from "@gnu-taler/idb-bridge";
// eslint-disable-next-line no-duplicate-imports
import {
BridgeIDBFactory,
MemoryBackend,
shimIndexedDB,
} from "@gnu-taler/idb-bridge";
import { AccessStats } from "@gnu-taler/idb-bridge";
import { SynchronousCryptoWorkerFactoryPlain } from "./crypto/workers/synchronousWorkerFactoryPlain.js";
import { openTalerDatabase } from "./index.js";
import { Logger } from "@gnu-taler/taler-util";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
import { SetTimeoutTimerAPI } from "./util/timer.js";
import { Wallet } from "./wallet.js";
import { qjsOs, qjsStd } from "@gnu-taler/taler-util/qtart";
import { DefaultNodeWalletArgs, makeTempfileId } from "./host-common.js";
const logger = new Logger("host-impl.qtart.ts");
export async function createNativeWalletHost2(
args: DefaultNodeWalletArgs = {},
): Promise<{
wallet: Wallet;
getDbStats: () => AccessStats;
}> {
BridgeIDBFactory.enableTracing = false;
const myBackend = new MemoryBackend();
myBackend.enableTracing = false;
const storagePath = args.persistentStoragePath;
if (storagePath) {
const dbContentStr = qjsStd.loadFile(storagePath);
if (dbContentStr != null) {
const dbContent = JSON.parse(dbContentStr);
myBackend.importDump(dbContent);
}
myBackend.afterCommitCallback = async () => {
logger.trace("committing database");
// Allow caller to stop persisting the wallet.
if (args.persistentStoragePath === undefined) {
return;
}
const tmpPath = `${args.persistentStoragePath}-${makeTempfileId(5)}.tmp`;
const dbContent = myBackend.exportDump();
logger.trace("exported DB dump");
qjsStd.writeFile(tmpPath, JSON.stringify(dbContent, undefined, 2));
// Atomically move the temporary file onto the DB path.
const res = qjsOs.rename(tmpPath, args.persistentStoragePath);
if (res != 0) {
throw Error("db commit failed at rename");
}
logger.trace("committing database done");
};
}
logger.info("done processing storage path");
BridgeIDBFactory.enableTracing = false;
const myBridgeIdbFactory = new BridgeIDBFactory(myBackend);
const myIdbFactory: IDBFactory = myBridgeIdbFactory as any as IDBFactory;
let myHttpLib;
if (args.httpLib) {
myHttpLib = args.httpLib;
} else {
myHttpLib = createPlatformHttpLib();
}
const myVersionChange = (): Promise<void> => {
logger.error("version change requested, should not happen");
throw Error(
"BUG: wallet DB version change event can't happen with memory IDB",
);
};
shimIndexedDB(myBridgeIdbFactory);
const myDb = await openTalerDatabase(myIdbFactory, myVersionChange);
let workerFactory;
workerFactory = new SynchronousCryptoWorkerFactoryPlain();
const timer = new SetTimeoutTimerAPI();
const w = await Wallet.create(myDb, myHttpLib, timer, workerFactory);
if (args.notifyHandler) {
w.addNotificationListener(args.notifyHandler);
}
return {
wallet: w,
getDbStats: () => myBackend.accessStats,
};
}

View File

@ -0,0 +1,47 @@
/*
This file is part of GNU Taler
(C) 2019 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { DefaultNodeWalletArgs } from "./host-common.js";
import { Wallet } from "./index.js";
import * as hostImpl from "#host-impl";
import { AccessStats } from "@gnu-taler/idb-bridge";
/**
* Helpers to initiate a wallet in a host environment.
*/
/**
* Get a wallet instance.
*/
export async function createNativeWalletHost2(
args: DefaultNodeWalletArgs = {},
): Promise<{
wallet: Wallet;
getDbStats: () => AccessStats;
}> {
return hostImpl.createNativeWalletHost2(args);
}
/**
* Get a wallet instance.
*/
export async function createNativeWalletHost(
args: DefaultNodeWalletArgs = {},
): Promise<Wallet> {
const res = await hostImpl.createNativeWalletHost2(args);
return res.wallet;
}

View File

@ -16,15 +16,8 @@
export * from "./index.js";
// Utils for using the wallet under node
export { NodeHttpLib } from "./headless/NodeHttpLib.js";
export {
getDefaultNodeWallet,
getDefaultNodeWallet2,
DefaultNodeWalletArgs,
} from "./headless/helpers.js";
export * from "./crypto/workers/nodeThreadWorker.js";
export { SynchronousCryptoWorkerNode as SynchronousCryptoWorker } from "./crypto/workers/synchronousWorkerNode.js";
export { SynchronousCryptoWorkerPlain } from "./crypto/workers/synchronousWorkerPlain.js";
export type { AccessStats } from "@gnu-taler/idb-bridge";
export * from "./crypto/workers/synchronousWorkerFactoryNode.js";
export * from "./crypto/workers/synchronousWorkerFactoryPlain.js";

View File

@ -18,13 +18,9 @@
* Module entry point for the wallet when used as a node module.
*/
// Errors
export * from "./errors.js";
// Util functionality
export * from "./util/promiseUtils.js";
export * from "./util/query.js";
export * from "./util/http.js";
export * from "./versions.js";
@ -67,3 +63,5 @@ export * from "./util/timer.js";
export * from "./util/denominations.js";
export { SynchronousCryptoWorkerFactoryPlain } from "./crypto/workers/synchronousWorkerFactoryPlain.js";
export * from "./host-common.js";
export * from "./host.js";

View File

@ -30,18 +30,14 @@
* Imports.
*/
import {
WalletNotification,
BalancesResponse,
AmountJson,
DenominationPubKey,
TalerProtocolTimestamp,
CancellationToken,
CoinRefreshRequest,
DenominationInfo,
RefreshGroupId,
CoinRefreshRequest,
RefreshReason,
WalletNotification,
} from "@gnu-taler/taler-util";
import { CryptoDispatcher } from "./crypto/workers/crypto-dispatcher.js";
import { HttpRequestLibrary } from "@gnu-taler/taler-util/http";
import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
import {
ExchangeDetailsRecord,
@ -49,9 +45,6 @@ import {
RefreshReasonDetails,
WalletStoresV1,
} from "./db.js";
import { PendingOperationsResponse } from "./pending-types.js";
import { AsyncOpMemoMap, AsyncOpMemoSingle } from "./util/asyncMemo.js";
import { HttpRequestLibrary } from "./util/http.js";
import { AsyncCondition } from "./util/promiseUtils.js";
import {
DbAccess,

View File

@ -85,13 +85,13 @@ import {
ConfigRecordKey,
WalletBackupConfState,
} from "../../db.js";
import { TalerError } from "../../errors.js";
import { TalerError } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../../internal-wallet-state.js";
import { assertUnreachable } from "../../util/assertUnreachable.js";
import {
readSuccessResponseJsonOrThrow,
readTalerErrorResponse,
} from "../../util/http.js";
} from "@gnu-taler/taler-util/http";
import {
checkDbInvariant,
checkLogicInvariant,

View File

@ -42,7 +42,7 @@ import {
ExchangeDetailsRecord,
ExchangeRecord,
} from "../db.js";
import { makeErrorDetail, TalerError } from "../errors.js";
import { makeErrorDetail, TalerError } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js";
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
import { GetReadWriteAccess } from "../util/query.js";

View File

@ -62,10 +62,10 @@ import {
OperationStatus,
TransactionStatus,
} from "../db.js";
import { TalerError } from "../errors.js";
import { checkWithdrawalKycStatus, KycPendingInfo, KycUserType } from "../index.js";
import { TalerError } from "@gnu-taler/taler-util";
import { KycPendingInfo, KycUserType } from "../index.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { OperationAttemptResult } from "../util/retries.js";
import { makeTransactionId, spendCoins } from "./common.js";
import { getExchangeDetails } from "./exchanges.js";

View File

@ -41,6 +41,7 @@ import {
NotificationType,
parsePaytoUri,
Recoup,
TalerError,
TalerErrorCode,
TalerProtocolDuration,
TalerProtocolTimestamp,
@ -49,6 +50,7 @@ import {
WireFeeMap,
WireInfo,
} from "@gnu-taler/taler-util";
import { HttpRequestLibrary, readSuccessResponseTextOrThrow, readSuccessResponseJsonOrThrow, getExpiry } from "@gnu-taler/taler-util/http";
import {
DenominationRecord,
DenominationVerificationStatus,
@ -56,14 +58,7 @@ import {
ExchangeRecord,
WalletStoresV1,
} from "../db.js";
import { TalerError } from "../errors.js";
import { InternalWalletState, TrustInfo } from "../internal-wallet-state.js";
import {
getExpiry,
HttpRequestLibrary,
readSuccessResponseJsonOrThrow,
readSuccessResponseTextOrThrow,
} from "../util/http.js";
import { checkDbInvariant } from "../util/invariants.js";
import {
DbAccess,

View File

@ -25,7 +25,7 @@ import {
LibtoolVersion,
} from "@gnu-taler/taler-util";
import { InternalWalletState, MerchantInfo } from "../internal-wallet-state.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
const logger = new Logger("taler-wallet-core:merchants.ts");

View File

@ -94,13 +94,12 @@ import {
makePendingOperationFailedError,
TalerError,
TalerProtocolViolationError,
} from "../errors.js";
import { GetReadWriteAccess } from "../index.browser.js";
} from "@gnu-taler/taler-util";
import { GetReadWriteAccess } from "../index.js";
import {
EXCHANGE_COINS_LOCK,
InternalWalletState,
} from "../internal-wallet-state.js";
import { PendingTaskType } from "../pending-types.js";
import { assertUnreachable } from "../util/assertUnreachable.js";
import {
CoinSelectionTally,
@ -114,7 +113,7 @@ import {
readTalerErrorResponse,
readUnexpectedResponseDetails,
throwUnexpectedRequestError,
} from "../util/http.js";
} from "@gnu-taler/taler-util/http";
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
import {
OperationAttemptResult,

View File

@ -81,16 +81,15 @@ import {
WithdrawalGroupStatus,
WithdrawalRecordType,
} from "../db.js";
import { TalerError } from "../errors.js";
import { TalerError } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js";
import {
makeTransactionId,
runOperationWithErrorReporting,
spendCoins,
} from "../operations/common.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { checkDbInvariant } from "../util/invariants.js";
import { GetReadOnlyAccess } from "../util/query.js";
import {
OperationAttemptResult,
OperationAttemptResultType,

View File

@ -49,7 +49,7 @@ import {
WithdrawCoinSource,
} from "../db.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { checkDbInvariant } from "../util/invariants.js";
import { GetReadWriteAccess } from "../util/query.js";
import {

View File

@ -63,7 +63,7 @@ import {
RefreshReasonDetails,
WalletStoresV1,
} from "../db.js";
import { TalerError } from "../errors.js";
import { TalerError } from "@gnu-taler/taler-util";
import {
EXCHANGE_COINS_LOCK,
InternalWalletState,
@ -72,7 +72,7 @@ import { assertUnreachable } from "../util/assertUnreachable.js";
import {
readSuccessResponseJsonOrThrow,
readUnexpectedResponseDetails,
} from "../util/http.js";
} from "@gnu-taler/taler-util/http";
import { checkDbInvariant } from "../util/invariants.js";
import { GetReadWriteAccess } from "../util/query.js";
import {

View File

@ -29,7 +29,7 @@ import {
HttpRequestLibrary,
readSuccessResponseJsonOrThrow,
checkSuccessResponseOrThrow,
} from "../util/http.js";
} from "@gnu-taler/taler-util/http";
import {
AmountString,
codecForAny,

View File

@ -45,12 +45,12 @@ import {
DenominationRecord,
TipRecord,
} from "../db.js";
import { makeErrorDetail } from "../errors.js";
import { makeErrorDetail } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js";
import {
getHttpResponseErrorDetails,
readSuccessResponseJsonOrThrow,
} from "../util/http.js";
} from "@gnu-taler/taler-util/http";
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
import {
OperationAttemptResult,

View File

@ -85,7 +85,7 @@ import {
getErrorDetailFromException,
makeErrorDetail,
TalerError,
} from "../errors.js";
} from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js";
import {
makeCoinAvailable,
@ -99,7 +99,7 @@ import {
readSuccessResponseJsonOrErrorCode,
readSuccessResponseJsonOrThrow,
throwUnexpectedRequestError,
} from "../util/http.js";
} from "@gnu-taler/taler-util/http";
import {
checkDbInvariant,
checkLogicInvariant,

View File

@ -19,10 +19,10 @@ import {
CoreApiResponse,
j2s,
Logger,
TalerError,
WalletNotification,
} from "@gnu-taler/taler-util";
import { connectRpc, JsonMessage } from "@gnu-taler/taler-util/twrpc";
import { TalerError } from "./errors.js";
import { OpenedPromise, openPromise } from "./index.js";
import { WalletCoreApiClient } from "./wallet-api-types.js";

View File

@ -40,7 +40,7 @@ import {
WalletStoresV1,
WithdrawalGroupRecord,
} from "../db.js";
import { TalerError } from "../errors.js";
import { TalerError } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js";
import { PendingTaskType } from "../pending-types.js";
import { GetReadWriteAccess } from "./query.js";

View File

@ -129,7 +129,7 @@ import {
maybeInitDevMode,
setDevMode,
} from "./dev-experiments.js";
import { getErrorDetailFromException, TalerError } from "./errors.js";
import { getErrorDetailFromException, TalerError } from "@gnu-taler/taler-util";
import {
ActiveLongpollInfo,
ExchangeOperations,
@ -247,7 +247,7 @@ import {
import {
HttpRequestLibrary,
readSuccessResponseJsonOrThrow,
} from "./util/http.js";
} from "@gnu-taler/taler-util/http";
import { checkDbInvariant } from "./util/invariants.js";
import {
AsyncCondition,

View File

@ -55,6 +55,7 @@ export const buildConfig = {
format: 'esm',
platform: 'neutral',
mainFields: ["module", "main"],
conditions: ["qtart"],
sourcemap: true,
define: {
'__VERSION__': `"${_package.version}"`,

View File

@ -18,27 +18,25 @@
* Imports.
*/
import {
createNativeWalletHost,
DefaultNodeWalletArgs,
getDefaultNodeWallet,
getErrorDetailFromException,
handleWorkerError,
handleWorkerMessage,
Headers,
HttpRequestLibrary,
HttpRequestOptions,
HttpResponse,
NodeHttpLib,
OpenedPromise,
openPromise,
Wallet,
WALLET_EXCHANGE_PROTOCOL_VERSION,
WALLET_MERCHANT_PROTOCOL_VERSION,
} from "@gnu-taler/taler-wallet-core";
import {
CoreApiMessageEnvelope,
CoreApiResponse,
CoreApiResponseSuccess,
createPlatformHttpLib,
getErrorDetailFromException,
Headers,
HttpRequestLibrary,
HttpRequestOptions,
HttpResponse,
Logger,
WalletNotification,
} from "@gnu-taler/taler-util";
@ -51,7 +49,7 @@ const logger = new Logger("taler-wallet-embedded/index.ts");
export class NativeHttpLib implements HttpRequestLibrary {
useNfcTunnel = false;
private nodeHttpLib: HttpRequestLibrary = new NodeHttpLib();
private httpLib: HttpRequestLibrary = createPlatformHttpLib();
private requestId = 1;
@ -62,7 +60,7 @@ export class NativeHttpLib implements HttpRequestLibrary {
constructor(private sendMessage: (m: string) => void) {}
fetch(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
return this.nodeHttpLib.fetch(url, opt);
return this.httpLib.fetch(url, opt);
}
get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
@ -83,7 +81,7 @@ export class NativeHttpLib implements HttpRequestLibrary {
);
return p.promise;
} else {
return this.nodeHttpLib.get(url, opt);
return this.httpLib.get(url, opt);
}
}
@ -106,7 +104,7 @@ export class NativeHttpLib implements HttpRequestLibrary {
);
return p.promise;
} else {
return this.nodeHttpLib.postJson(url, body, opt);
return this.httpLib.postJson(url, body, opt);
}
}
@ -158,7 +156,7 @@ class NativeWalletMessageHandler {
walletArgs: DefaultNodeWalletArgs | undefined;
maybeWallet: Wallet | undefined;
wp = openPromise<Wallet>();
httpLib = new NodeHttpLib();
httpLib = createPlatformHttpLib();
/**
* Handle a request from the native wallet.
@ -181,7 +179,7 @@ class NativeWalletMessageHandler {
const reinit = async () => {
logger.info("in reinit");
const w = await getDefaultNodeWallet(this.walletArgs);
const w = await createNativeWalletHost(this.walletArgs);
this.maybeWallet = w;
const resp = await w.handleCoreApiRequest(
"initWallet",

View File

@ -17,44 +17,26 @@
/**
* Imports.
*/
import {
AccessStats,
DefaultNodeWalletArgs,
getErrorDetailFromException,
Headers,
HttpRequestLibrary,
HttpRequestOptions,
HttpResponse,
openPromise,
openTalerDatabase,
SetTimeoutTimerAPI,
SynchronousCryptoWorkerFactoryPlain,
Wallet,
WalletApiOperation,
} from "@gnu-taler/taler-wallet-core";
import {
CoreApiMessageEnvelope,
CoreApiResponse,
CoreApiResponseSuccess,
createPlatformHttpLib,
getErrorDetailFromException,
InitRequest,
j2s,
Logger,
setGlobalLogLevelFromString,
setPRNG,
WalletNotification,
} from "@gnu-taler/taler-util";
import { BridgeIDBFactory } from "@gnu-taler/idb-bridge";
import { MemoryBackend } from "@gnu-taler/idb-bridge";
import { shimIndexedDB } from "@gnu-taler/idb-bridge";
import { IDBFactory } from "@gnu-taler/idb-bridge";
import * as _qjsOsImp from "os";
// @ts-ignore
import * as _qjsStdImp from "std";
const textDecoder = new TextDecoder();
const textEncoder = new TextEncoder();
import { qjsOs } from "@gnu-taler/taler-util/qtart";
import {
createNativeWalletHost2,
DefaultNodeWalletArgs,
openPromise,
Wallet,
WalletApiOperation,
} from "@gnu-taler/taler-wallet-core";
setGlobalLogLevelFromString("trace");
@ -66,210 +48,19 @@ setPRNG(function (x: Uint8Array, n: number) {
for (let i = 0; i < v.length; i++) v[i] = 0;
});
export interface QjsHttpResp {
status: number;
data: ArrayBuffer;
}
export interface QjsHttpOptions {
method: string;
debug?: boolean;
data?: ArrayBuffer;
headers?: string[];
}
export interface QjsOsLib {
fetchHttp(url: string, options?: QjsHttpOptions): Promise<QjsHttpResp>;
postMessageToHost(s: string): void;
setMessageFromHostHandler(h: (s: string) => void): void;
rename(oldPath: string, newPath: string): number;
}
export interface QjsStdLib {
writeFile(filename: string, contents: string): void;
loadFile(filename: string): string;
}
// This is not the nodejs "os" module, but the qjs "os" module.
const qjsOs: QjsOsLib = _qjsOsImp as any;
const qjsStd: QjsStdLib = _qjsStdImp as any;
const logger = new Logger("taler-wallet-embedded/index.ts");
export class NativeHttpLib implements HttpRequestLibrary {
get(
url: string,
opt?: HttpRequestOptions | undefined,
): Promise<HttpResponse> {
return this.fetch(url, {
method: "GET",
...opt,
});
}
postJson(
url: string,
body: any,
opt?: HttpRequestOptions | undefined,
): Promise<HttpResponse> {
return this.fetch(url, {
method: "POST",
body,
...opt,
});
}
async fetch(
url: string,
opt?: HttpRequestOptions | undefined,
): Promise<HttpResponse> {
const method = opt?.method ?? "GET";
let data: ArrayBuffer | undefined = undefined;
let headers: string[] = [];
if (opt?.headers) {
for (let headerName of Object.keys(opt.headers)) {
headers.push(`${headerName}: ${opt.headers[headerName]}`);
}
}
if (method.toUpperCase() === "POST") {
if (opt?.body) {
if (typeof opt.body === "string") {
data = textEncoder.encode(opt.body).buffer;
} else if (ArrayBuffer.isView(opt.body)) {
data = opt.body.buffer;
} else if (opt.body instanceof ArrayBuffer) {
data = opt.body;
} else if (typeof opt.body === "object") {
data = textEncoder.encode(JSON.stringify(opt.body)).buffer;
}
} else {
data = new ArrayBuffer(0);
}
}
const res = await qjsOs.fetchHttp(url, {
method,
data,
headers,
});
return {
requestMethod: method,
headers: new Headers(),
async bytes() {
return res.data;
},
json() {
const text = textDecoder.decode(res.data);
return JSON.parse(text);
},
async text() {
const text = textDecoder.decode(res.data);
return text;
},
requestUrl: url,
status: res.status,
};
}
}
function sendNativeMessage(ev: CoreApiMessageEnvelope): void {
const m = JSON.stringify(ev);
qjsOs.postMessageToHost(m);
}
/**
* Generate a random alphanumeric ID. Does *not* use cryptographically
* secure randomness.
*/
function makeId(length: number): string {
let result = "";
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
}
export async function getWallet(args: DefaultNodeWalletArgs = {}): Promise<{
wallet: Wallet;
getDbStats: () => AccessStats;
}> {
BridgeIDBFactory.enableTracing = false;
const myBackend = new MemoryBackend();
myBackend.enableTracing = false;
const storagePath = args.persistentStoragePath;
if (storagePath) {
const dbContentStr = qjsStd.loadFile(storagePath);
if (dbContentStr != null) {
const dbContent = JSON.parse(dbContentStr);
myBackend.importDump(dbContent);
}
myBackend.afterCommitCallback = async () => {
logger.trace("committing database");
// Allow caller to stop persisting the wallet.
if (args.persistentStoragePath === undefined) {
return;
}
const tmpPath = `${args.persistentStoragePath}-${makeId(5)}.tmp`;
const dbContent = myBackend.exportDump();
qjsStd.writeFile(tmpPath, JSON.stringify(dbContent, undefined, 2));
// Atomically move the temporary file onto the DB path.
const res = qjsOs.rename(tmpPath, args.persistentStoragePath);
if (res != 0) {
throw Error("db commit failed at rename");
}
logger.trace("committing database done");
};
}
console.log("done processing storage path");
BridgeIDBFactory.enableTracing = false;
const myBridgeIdbFactory = new BridgeIDBFactory(myBackend);
const myIdbFactory: IDBFactory = myBridgeIdbFactory as any as IDBFactory;
let myHttpLib;
if (args.httpLib) {
myHttpLib = args.httpLib;
} else {
myHttpLib = new NativeHttpLib();
}
const myVersionChange = (): Promise<void> => {
logger.error("version change requested, should not happen");
throw Error(
"BUG: wallet DB version change event can't happen with memory IDB",
);
};
shimIndexedDB(myBridgeIdbFactory);
const myDb = await openTalerDatabase(myIdbFactory, myVersionChange);
let workerFactory;
workerFactory = new SynchronousCryptoWorkerFactoryPlain();
const timer = new SetTimeoutTimerAPI();
const w = await Wallet.create(myDb, myHttpLib, timer, workerFactory);
if (args.notifyHandler) {
w.addNotificationListener(args.notifyHandler);
}
return {
wallet: w,
getDbStats: () => myBackend.accessStats,
};
}
class NativeWalletMessageHandler {
walletArgs: DefaultNodeWalletArgs | undefined;
initRequest: InitRequest = {};
maybeWallet: Wallet | undefined;
wp = openPromise<Wallet>();
httpLib = new NativeHttpLib();
httpLib = createPlatformHttpLib();
/**
* Handle a request from the native wallet.
@ -292,7 +83,7 @@ class NativeWalletMessageHandler {
const reinit = async () => {
logger.info("in reinit");
const wR = await getWallet(this.walletArgs);
const wR = await createNativeWalletHost2(this.walletArgs);
const w = wR.wallet;
this.maybeWallet = w;
const resp = await w.handleCoreApiRequest("initWallet", "native-init", {
@ -422,7 +213,7 @@ globalThis.installNativeWalletListener = installNativeWalletListener;
globalThis.makeWallet = getWallet;
export async function testWithGv() {
const w = await getWallet();
const w = await createNativeWalletHost2();
await w.wallet.client.call(WalletApiOperation.InitWallet, {});
await w.wallet.client.call(WalletApiOperation.RunIntegrationTest, {
amountToSpend: "KUDOS:1",
@ -438,7 +229,7 @@ export async function testWithGv() {
export async function testWithLocal() {
console.log("running local test");
const w = await getWallet({
const w = await createNativeWalletHost2({
persistentStoragePath: "walletdb.json",
});
console.log("created wallet");

View File

@ -18,19 +18,17 @@
* Imports.
*/
import {
Logger,
RequestThrottler,
TalerErrorCode,
HttpRequestLibrary,
HttpRequestOptions,
HttpResponse,
Headers,
TalerError,
} from "@gnu-taler/taler-wallet-core";
import {
Logger,
RequestThrottler,
stringToBytes,
TalerErrorCode,
} from "@gnu-taler/taler-util";
const logger = new Logger("browserHttpLib");
/**

View File

@ -22,11 +22,12 @@
* Imports.
*/
import { j2s, Logger } from "@gnu-taler/taler-util";
import {
j2s,
Logger,
getErrorDetailFromException,
nativeCrypto,
} from "@gnu-taler/taler-wallet-core";
} from "@gnu-taler/taler-util";
import { nativeCrypto } from "@gnu-taler/taler-wallet-core";
const logger = new Logger("browserWorkerEntry.ts");

View File

@ -16,7 +16,7 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { Amounts, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { isFuture, parse } from "date-fns";
import { useState } from "preact/hooks";
import { alertFromError, useAlertContext } from "../../context/alert.js";

View File

@ -20,16 +20,14 @@ import {
NotificationType,
PreparePayResult,
PreparePayResultType,
TalerErrorDetail,
TalerProtocolTimestamp,
} from "@gnu-taler/taler-util";
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useEffect, useState } from "preact/hooks";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useEffect } from "preact/hooks";
import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";
import { useTranslationContext } from "../../context/translation.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { withSafe } from "../../mui/handlers.js";
import { Props, State } from "./index.js";
export function useComponentState({

View File

@ -15,11 +15,9 @@
*/
import {
Amounts,
TalerErrorDetail,
TalerProtocolTimestamp,
Amounts, TalerProtocolTimestamp
} from "@gnu-taler/taler-util";
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { isFuture, parse } from "date-fns";
import { useState } from "preact/hooks";
import { alertFromError, useAlertContext } from "../../context/alert.js";

View File

@ -16,12 +16,9 @@
import {
AbsoluteTime,
Amounts,
TalerErrorDetail,
TalerProtocolTimestamp,
Amounts, TalerProtocolTimestamp
} from "@gnu-taler/taler-util";
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useState } from "preact/hooks";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";
import { useTranslationContext } from "../../context/translation.js";

View File

@ -20,8 +20,9 @@ import {
Amounts,
ExchangeListItem,
ExchangeTosStatus,
TalerError,
} from "@gnu-taler/taler-util";
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useState } from "preact/hooks";
import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";

View File

@ -13,8 +13,7 @@
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { TalerErrorDetail } from "@gnu-taler/taler-util";
import { TalerError } from "@gnu-taler/taler-wallet-core";
import { TalerErrorDetail, TalerError } from "@gnu-taler/taler-util";
import { useEffect, useMemo, useState } from "preact/hooks";
import { BackgroundError } from "../wxApi.js";

View File

@ -19,8 +19,9 @@ import {
Logger,
TalerErrorCode,
TalerUriType,
TalerError,
} from "@gnu-taler/taler-util";
import { TalerError, WalletOperations } from "@gnu-taler/taler-wallet-core";
import { WalletOperations } from "@gnu-taler/taler-wallet-core";
import { BackgroundOperations } from "../wxApi.js";
import {
BackgroundPlatformAPI,

View File

@ -22,12 +22,12 @@
import {
CryptoWorker,
CryptoWorkerFactory,
SynchronousCryptoWorker,
SynchronousCryptoWorkerPlain,
} from "@gnu-taler/taler-wallet-core";
export class SynchronousCryptoWorkerFactory implements CryptoWorkerFactory {
startWorker(): CryptoWorker {
return new SynchronousCryptoWorker();
return new SynchronousCryptoWorkerPlain();
}
getConcurrency(): number {

View File

@ -21,14 +21,12 @@ import {
Logger,
RequestThrottler,
TalerErrorCode,
} from "@gnu-taler/taler-util";
import {
Headers,
HttpRequestLibrary,
HttpRequestOptions,
HttpResponse,
TalerError,
} from "@gnu-taler/taler-wallet-core";
} from "@gnu-taler/taler-util";
/**
* An implementation of the [[HttpRequestLibrary]] using the

View File

@ -29,20 +29,18 @@ import {
NotificationType,
TalerErrorCode,
TalerErrorDetail,
WalletDiagnostics,
WalletDiagnostics
} from "@gnu-taler/taler-util";
import {
TalerError,
WalletCoreApiClient,
WalletCoreOpKeys,
WalletCoreRequestType,
WalletCoreResponseType,
WalletOperations,
WalletCoreResponseType
} from "@gnu-taler/taler-wallet-core";
import {
MessageFromBackend,
MessageFromFrontendBackground,
MessageFromFrontendWallet,
MessageFromFrontendWallet
} from "./platform/api.js";
import { platform } from "./platform/foreground.js";

View File

@ -32,14 +32,14 @@ import {
TalerErrorCode,
TalerUriType,
WalletDiagnostics,
makeErrorDetail,
getErrorDetailFromException,
} from "@gnu-taler/taler-util";
import {
DbAccess,
deleteTalerDatabase,
exportDb,
getErrorDetailFromException,
importDb,
makeErrorDetail,
OpenedPromise,
openPromise,
openTalerDatabase,