make wallet-cli runnable under qtart
This commit is contained in:
parent
cb2f4c21d8
commit
825d2c4352
@ -1,4 +1,4 @@
|
||||
import { clk } from "@gnu-taler/taler-util";
|
||||
import { clk } from "@gnu-taler/taler-util/clk";
|
||||
import {
|
||||
getBackupStartState,
|
||||
getRecoveryStartState,
|
||||
|
@ -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 = [
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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({
|
||||
|
@ -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.
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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": {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
59
packages/taler-util/src/compat.node.ts
Normal file
59
packages/taler-util/src/compat.node.ts
Normal 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];
|
||||
}
|
53
packages/taler-util/src/compat.qtart.ts
Normal file
53
packages/taler-util/src/compat.qtart.ts
Normal 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);
|
||||
}
|
39
packages/taler-util/src/http-common.ts
Normal file
39
packages/taler-util/src/http-common.ts
Normal 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");
|
||||
}
|
17
packages/taler-util/src/http-impl.node.d.ts
vendored
Normal file
17
packages/taler-util/src/http-impl.node.d.ts
vendored
Normal 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>;
|
||||
}
|
175
packages/taler-util/src/http-impl.node.ts
Normal file
175
packages/taler-util/src/http-impl.node.ts
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
127
packages/taler-util/src/http-impl.qtart.ts
Normal file
127
packages/taler-util/src/http-impl.qtart.ts
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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";
|
||||
|
@ -21,4 +21,3 @@ initNodePrng();
|
||||
export * from "./index.js";
|
||||
export * from "./talerconfig.js";
|
||||
export * from "./globbing/minimatch.js";
|
||||
export { clk } from "./clk.js";
|
||||
|
@ -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";
|
||||
|
36
packages/taler-util/src/qtart.ts
Normal file
36
packages/taler-util/src/qtart.ts
Normal 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;
|
@ -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");
|
||||
}
|
||||
|
@ -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}"`,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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 {
|
||||
/**
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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.
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -58,7 +58,7 @@ import {
|
||||
import {
|
||||
HttpRequestLibrary,
|
||||
readSuccessResponseJsonOrThrow,
|
||||
} from "./util/http.js";
|
||||
} from "@gnu-taler/taler-util/http";
|
||||
import {
|
||||
getBankStatusUrl,
|
||||
getBankWithdrawalInfo,
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
60
packages/taler-wallet-core/src/host-common.ts
Normal file
60
packages/taler-wallet-core/src/host-common.ts
Normal 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;
|
||||
}
|
@ -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}'`);
|
120
packages/taler-wallet-core/src/host-impl.qtart.ts
Normal file
120
packages/taler-wallet-core/src/host-impl.qtart.ts
Normal 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,
|
||||
};
|
||||
}
|
47
packages/taler-wallet-core/src/host.ts
Normal file
47
packages/taler-wallet-core/src/host.ts
Normal 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;
|
||||
}
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -29,7 +29,7 @@ import {
|
||||
HttpRequestLibrary,
|
||||
readSuccessResponseJsonOrThrow,
|
||||
checkSuccessResponseOrThrow,
|
||||
} from "../util/http.js";
|
||||
} from "@gnu-taler/taler-util/http";
|
||||
import {
|
||||
AmountString,
|
||||
codecForAny,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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,
|
||||
|
@ -55,6 +55,7 @@ export const buildConfig = {
|
||||
format: 'esm',
|
||||
platform: 'neutral',
|
||||
mainFields: ["module", "main"],
|
||||
conditions: ["qtart"],
|
||||
sourcemap: true,
|
||||
define: {
|
||||
'__VERSION__': `"${_package.version}"`,
|
||||
|
@ -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",
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
||||
/**
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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({
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user