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 {
|
import {
|
||||||
getBackupStartState,
|
getBackupStartState,
|
||||||
getRecoveryStartState,
|
getRecoveryStartState,
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { expect } from "chai";
|
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", () => {
|
describe("payto uri format", () => {
|
||||||
const valids = [
|
const valids = [
|
||||||
|
@ -19,19 +19,19 @@
|
|||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
buildCodecForObject,
|
buildCodecForObject,
|
||||||
|
codecForBoolean,
|
||||||
codecForNumber,
|
codecForNumber,
|
||||||
codecForString,
|
codecForString,
|
||||||
codecForBoolean,
|
|
||||||
codecOptional,
|
codecOptional,
|
||||||
j2s,
|
j2s,
|
||||||
Logger,
|
Logger,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
|
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
|
||||||
import {
|
import {
|
||||||
getDefaultNodeWallet2,
|
|
||||||
NodeHttpLib,
|
|
||||||
WalletApiOperation,
|
|
||||||
Wallet,
|
|
||||||
AccessStats,
|
AccessStats,
|
||||||
|
createNativeWalletHost2,
|
||||||
|
Wallet,
|
||||||
|
WalletApiOperation,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} 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.
|
// Validate the configuration file for this benchmark.
|
||||||
const b1conf = codecForBench1Config().decode(configJson);
|
const b1conf = codecForBench1Config().decode(configJson);
|
||||||
|
|
||||||
const myHttpLib = new NodeHttpLib();
|
const myHttpLib = createPlatformHttpLib({
|
||||||
myHttpLib.setThrottling(false);
|
enableThrottling: false,
|
||||||
|
});
|
||||||
|
|
||||||
const numIter = b1conf.iterations ?? 1;
|
const numIter = b1conf.iterations ?? 1;
|
||||||
const numDeposits = b1conf.deposits ?? 5;
|
const numDeposits = b1conf.deposits ?? 5;
|
||||||
@ -81,7 +82,7 @@ export async function runBench1(configJson: any): Promise<void> {
|
|||||||
console.log("wallet DB stats", j2s(getDbStats!()));
|
console.log("wallet DB stats", j2s(getDbStats!()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await getDefaultNodeWallet2({
|
const res = await createNativeWalletHost2({
|
||||||
// No persistent DB storage.
|
// No persistent DB storage.
|
||||||
persistentStoragePath: undefined,
|
persistentStoragePath: undefined,
|
||||||
httpLib: myHttpLib,
|
httpLib: myHttpLib,
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
codecOptional,
|
codecOptional,
|
||||||
Logger,
|
Logger,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
|
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
|
||||||
import {
|
import {
|
||||||
checkReserve,
|
checkReserve,
|
||||||
createFakebankReserve,
|
createFakebankReserve,
|
||||||
@ -31,9 +32,8 @@ import {
|
|||||||
depositCoin,
|
depositCoin,
|
||||||
downloadExchangeInfo,
|
downloadExchangeInfo,
|
||||||
findDenomOrThrow,
|
findDenomOrThrow,
|
||||||
NodeHttpLib,
|
|
||||||
refreshCoin,
|
refreshCoin,
|
||||||
SynchronousCryptoWorkerFactoryNode,
|
SynchronousCryptoWorkerFactoryPlain,
|
||||||
withdrawCoin,
|
withdrawCoin,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
|
|
||||||
@ -50,12 +50,13 @@ export async function runBench2(configJson: any): Promise<void> {
|
|||||||
const benchConf = codecForBench2Config().decode(configJson);
|
const benchConf = codecForBench2Config().decode(configJson);
|
||||||
const curr = benchConf.currency;
|
const curr = benchConf.currency;
|
||||||
const cryptoDisp = new CryptoDispatcher(
|
const cryptoDisp = new CryptoDispatcher(
|
||||||
new SynchronousCryptoWorkerFactoryNode(),
|
new SynchronousCryptoWorkerFactoryPlain(),
|
||||||
);
|
);
|
||||||
const cryptoApi = cryptoDisp.cryptoApi;
|
const cryptoApi = cryptoDisp.cryptoApi;
|
||||||
|
|
||||||
const http = new NodeHttpLib();
|
const http = createPlatformHttpLib({
|
||||||
http.setThrottling(false);
|
enableThrottling: false,
|
||||||
|
});
|
||||||
|
|
||||||
const numIter = benchConf.iterations ?? 1;
|
const numIter = benchConf.iterations ?? 1;
|
||||||
const numDeposits = benchConf.deposits ?? 5;
|
const numDeposits = benchConf.deposits ?? 5;
|
||||||
|
@ -25,12 +25,12 @@ import {
|
|||||||
j2s,
|
j2s,
|
||||||
Logger,
|
Logger,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
|
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
|
||||||
import {
|
import {
|
||||||
getDefaultNodeWallet2,
|
|
||||||
NodeHttpLib,
|
|
||||||
WalletApiOperation,
|
|
||||||
Wallet,
|
|
||||||
AccessStats,
|
AccessStats,
|
||||||
|
createNativeWalletHost2,
|
||||||
|
Wallet,
|
||||||
|
WalletApiOperation,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
import benchMerchantIDGenerator from "./benchMerchantIDGenerator.js";
|
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");
|
throw new Error("Payto template url must contain '${id}' placeholder");
|
||||||
}
|
}
|
||||||
|
|
||||||
const myHttpLib = new NodeHttpLib();
|
const myHttpLib = createPlatformHttpLib({
|
||||||
myHttpLib.setThrottling(false);
|
enableThrottling: false,
|
||||||
|
});
|
||||||
|
|
||||||
const numIter = b3conf.iterations ?? 1;
|
const numIter = b3conf.iterations ?? 1;
|
||||||
const numDeposits = b3conf.deposits ?? 5;
|
const numDeposits = b3conf.deposits ?? 5;
|
||||||
@ -89,7 +90,7 @@ export async function runBench3(configJson: any): Promise<void> {
|
|||||||
console.log("wallet DB stats", j2s(getDbStats!()));
|
console.log("wallet DB stats", j2s(getDbStats!()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await getDefaultNodeWallet2({
|
const res = await createNativeWalletHost2({
|
||||||
// No persistent DB storage.
|
// No persistent DB storage.
|
||||||
persistentStoragePath: undefined,
|
persistentStoragePath: undefined,
|
||||||
httpLib: myHttpLib,
|
httpLib: myHttpLib,
|
||||||
|
@ -41,17 +41,15 @@ import {
|
|||||||
MerchantTemplateAddDetails,
|
MerchantTemplateAddDetails,
|
||||||
parsePaytoUri,
|
parsePaytoUri,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
|
TalerError,
|
||||||
TalerProtocolDuration,
|
TalerProtocolDuration,
|
||||||
WalletNotification,
|
WalletNotification,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
BankAccessApi,
|
|
||||||
BankApi,
|
BankApi,
|
||||||
BankServiceHandle,
|
BankServiceHandle,
|
||||||
HarnessExchangeBankAccount,
|
HarnessExchangeBankAccount,
|
||||||
NodeHttpLib,
|
|
||||||
openPromise,
|
openPromise,
|
||||||
TalerError,
|
|
||||||
WalletCoreApiClient,
|
WalletCoreApiClient,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
import { deepStrictEqual } from "assert";
|
import { deepStrictEqual } from "assert";
|
||||||
@ -83,6 +81,7 @@ import {
|
|||||||
RemoteWallet,
|
RemoteWallet,
|
||||||
WalletNotificationWaiter,
|
WalletNotificationWaiter,
|
||||||
} from "@gnu-taler/taler-wallet-core/remote";
|
} from "@gnu-taler/taler-wallet-core/remote";
|
||||||
|
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
|
||||||
|
|
||||||
const logger = new Logger("harness.ts");
|
const logger = new Logger("harness.ts");
|
||||||
|
|
||||||
@ -507,7 +506,7 @@ class LibEuFinBankService extends BankServiceBase implements BankServiceHandle {
|
|||||||
sandboxProc: ProcessWrapper | undefined;
|
sandboxProc: ProcessWrapper | undefined;
|
||||||
nexusProc: ProcessWrapper | undefined;
|
nexusProc: ProcessWrapper | undefined;
|
||||||
|
|
||||||
http = new NodeHttpLib();
|
http = createPlatformHttpLib();
|
||||||
|
|
||||||
static async create(
|
static async create(
|
||||||
gc: GlobalTestState,
|
gc: GlobalTestState,
|
||||||
@ -794,7 +793,7 @@ export class FakebankService
|
|||||||
{
|
{
|
||||||
proc: ProcessWrapper | undefined;
|
proc: ProcessWrapper | undefined;
|
||||||
|
|
||||||
http = new NodeHttpLib();
|
http = createPlatformHttpLib();
|
||||||
|
|
||||||
// We store "created" accounts during setup and
|
// We store "created" accounts during setup and
|
||||||
// register them after startup.
|
// register them after startup.
|
||||||
|
@ -23,7 +23,6 @@ import os from "os";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import {
|
import {
|
||||||
Amounts,
|
Amounts,
|
||||||
clk,
|
|
||||||
Configuration,
|
Configuration,
|
||||||
decodeCrock,
|
decodeCrock,
|
||||||
Logger,
|
Logger,
|
||||||
@ -38,6 +37,7 @@ import { GlobalTestState, runTestWithState } from "./harness/harness.js";
|
|||||||
import { getTestInfo, runTests } from "./integrationtests/testrunner.js";
|
import { getTestInfo, runTests } from "./integrationtests/testrunner.js";
|
||||||
import { lintExchangeDeployment } from "./lint.js";
|
import { lintExchangeDeployment } from "./lint.js";
|
||||||
import { runEnvFull } from "./env-full.js";
|
import { runEnvFull } from "./env-full.js";
|
||||||
|
import { clk } from "@gnu-taler/taler-util/clk";
|
||||||
|
|
||||||
const logger = new Logger("taler-harness:index.ts");
|
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!).",
|
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
|
advancedCli
|
||||||
.subcommand("bench1", "bench1", {
|
.subcommand("bench1", "bench1", {
|
||||||
help: "Run the 'bench1' benchmark",
|
help: "Run the 'bench1' benchmark",
|
||||||
@ -272,7 +281,7 @@ testingCli
|
|||||||
help: "Produce less output.",
|
help: "Produce less output.",
|
||||||
})
|
})
|
||||||
.flag("noTimeout", ["--no-timeout"], {
|
.flag("noTimeout", ["--no-timeout"], {
|
||||||
help: "Do not time out tests."
|
help: "Do not time out tests.",
|
||||||
})
|
})
|
||||||
.action(async (args) => {
|
.action(async (args) => {
|
||||||
await runTests({
|
await runTests({
|
||||||
|
@ -24,22 +24,18 @@ import {
|
|||||||
Duration,
|
Duration,
|
||||||
durationFromSpec,
|
durationFromSpec,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import { createPlatformHttpLib, readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
|
||||||
NodeHttpLib,
|
|
||||||
readSuccessResponseJsonOrThrow,
|
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
|
||||||
import { makeNoFeeCoinConfig } from "../harness/denomStructures.js";
|
import { makeNoFeeCoinConfig } from "../harness/denomStructures.js";
|
||||||
import {
|
import {
|
||||||
BankService,
|
BankService,
|
||||||
ExchangeService,
|
ExchangeService,
|
||||||
GlobalTestState,
|
GlobalTestState,
|
||||||
MerchantPrivateApi,
|
|
||||||
MerchantService,
|
MerchantService,
|
||||||
setupDb,
|
setupDb,
|
||||||
WalletCli,
|
WalletCli,
|
||||||
getPayto,
|
getPayto,
|
||||||
} from "../harness/harness.js";
|
} from "../harness/harness.js";
|
||||||
import { startWithdrawViaBank, withdrawViaBank } from "../harness/helpers.js";
|
import { withdrawViaBank } from "../harness/helpers.js";
|
||||||
|
|
||||||
async function applyTimeTravel(
|
async function applyTimeTravel(
|
||||||
timetravelDuration: Duration,
|
timetravelDuration: Duration,
|
||||||
@ -69,7 +65,7 @@ async function applyTimeTravel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const http = new NodeHttpLib();
|
const http = createPlatformHttpLib();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic time travel test.
|
* Basic time travel test.
|
||||||
|
@ -17,11 +17,14 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { Duration, j2s, NotificationType } from "@gnu-taler/taler-util";
|
import {
|
||||||
|
Duration,
|
||||||
|
j2s,
|
||||||
|
NotificationType,
|
||||||
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
BankAccessApi,
|
BankAccessApi,
|
||||||
BankApi,
|
BankApi,
|
||||||
NodeHttpLib,
|
|
||||||
WalletApiOperation,
|
WalletApiOperation,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
|
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
|
||||||
@ -37,6 +40,7 @@ import {
|
|||||||
} from "../harness/harness.js";
|
} from "../harness/harness.js";
|
||||||
import { EnvOptions, SimpleTestEnvironmentNg } from "../harness/helpers.js";
|
import { EnvOptions, SimpleTestEnvironmentNg } from "../harness/helpers.js";
|
||||||
import * as http from "node:http";
|
import * as http from "node:http";
|
||||||
|
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
|
||||||
|
|
||||||
export async function createKycTestkudosEnvironment(
|
export async function createKycTestkudosEnvironment(
|
||||||
t: GlobalTestState,
|
t: GlobalTestState,
|
||||||
@ -336,12 +340,12 @@ export async function runKycTest(t: GlobalTestState) {
|
|||||||
// We now simulate the user interacting with the KYC service,
|
// We now simulate the user interacting with the KYC service,
|
||||||
// which would usually done in the browser.
|
// which would usually done in the browser.
|
||||||
|
|
||||||
const httpClient = new NodeHttpLib();
|
const httpLib = createPlatformHttpLib();
|
||||||
const kycServerResp = await httpClient.get(kycNotif.kycUrl);
|
const kycServerResp = await httpLib.get(kycNotif.kycUrl);
|
||||||
const kycLoginResp = await kycServerResp.json();
|
const kycLoginResp = await kycServerResp.json();
|
||||||
console.log("kyc server resp:", j2s(kycLoginResp));
|
console.log("kyc server resp:", j2s(kycLoginResp));
|
||||||
const kycProofUrl = kycLoginResp.redirect_uri;
|
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("proof resp status", proofHttpResp.status);
|
||||||
console.log("resp headers", proofHttpResp.headers.toJSON());
|
console.log("resp headers", proofHttpResp.headers.toJSON());
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ import {
|
|||||||
encodeCrock,
|
encodeCrock,
|
||||||
getRandomBytes,
|
getRandomBytes,
|
||||||
} from "@gnu-taler/taler-util";
|
} 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 {
|
import {
|
||||||
BankService,
|
BankService,
|
||||||
ExchangeService,
|
ExchangeService,
|
||||||
@ -38,7 +39,7 @@ import {
|
|||||||
withdrawViaBank,
|
withdrawViaBank,
|
||||||
} from "../harness/helpers.js";
|
} from "../harness/helpers.js";
|
||||||
|
|
||||||
const httpLib = new NodeHttpLib();
|
const httpLib = createPlatformHttpLib();
|
||||||
|
|
||||||
interface Context {
|
interface Context {
|
||||||
merchant: MerchantService;
|
merchant: MerchantService;
|
||||||
|
@ -24,8 +24,8 @@ import {
|
|||||||
BankApi,
|
BankApi,
|
||||||
BankAccessApi,
|
BankAccessApi,
|
||||||
BankServiceHandle,
|
BankServiceHandle,
|
||||||
NodeHttpLib,
|
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
|
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run test for basic, bank-integrated withdrawal and payment.
|
* Run test for basic, bank-integrated withdrawal and payment.
|
||||||
@ -35,7 +35,7 @@ export async function runPaymentDemoTest(t: GlobalTestState) {
|
|||||||
let bankInterface: BankServiceHandle = {
|
let bankInterface: BankServiceHandle = {
|
||||||
baseUrl: "https://bank.demo.taler.net/",
|
baseUrl: "https://bank.demo.taler.net/",
|
||||||
bankAccessApiBaseUrl: "https://bank.demo.taler.net/",
|
bankAccessApiBaseUrl: "https://bank.demo.taler.net/",
|
||||||
http: new NodeHttpLib(),
|
http: createPlatformHttpLib(),
|
||||||
};
|
};
|
||||||
let user = await BankApi.createRandomBankUser(bankInterface);
|
let user = await BankApi.createRandomBankUser(bankInterface);
|
||||||
let wop = await BankAccessApi.createWithdrawalOperation(
|
let wop = await BankAccessApi.createWithdrawalOperation(
|
||||||
|
@ -17,23 +17,10 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { j2s } from "@gnu-taler/taler-util";
|
|
||||||
import {
|
import {
|
||||||
checkReserve,
|
|
||||||
CryptoDispatcher,
|
|
||||||
depositCoin,
|
|
||||||
downloadExchangeInfo,
|
|
||||||
findDenomOrThrow,
|
|
||||||
NodeHttpLib,
|
|
||||||
refreshCoin,
|
|
||||||
SynchronousCryptoWorkerFactoryNode,
|
|
||||||
TalerError,
|
|
||||||
topupReserveWithDemobank,
|
|
||||||
WalletApiOperation,
|
WalletApiOperation,
|
||||||
withdrawCoin,
|
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
import { GlobalTestState, WalletCli } from "../harness/harness.js";
|
import { GlobalTestState, WalletCli } from "../harness/harness.js";
|
||||||
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run test for the different crypto workers.
|
* Run test for the different crypto workers.
|
||||||
|
@ -17,17 +17,16 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* 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 {
|
import {
|
||||||
checkReserve,
|
checkReserve,
|
||||||
CryptoDispatcher,
|
CryptoDispatcher,
|
||||||
depositCoin,
|
depositCoin,
|
||||||
downloadExchangeInfo,
|
downloadExchangeInfo,
|
||||||
findDenomOrThrow,
|
findDenomOrThrow,
|
||||||
NodeHttpLib,
|
|
||||||
refreshCoin,
|
refreshCoin,
|
||||||
SynchronousCryptoWorkerFactoryNode,
|
SynchronousCryptoWorkerFactoryPlain,
|
||||||
TalerError,
|
|
||||||
topupReserveWithDemobank,
|
topupReserveWithDemobank,
|
||||||
withdrawCoin,
|
withdrawCoin,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
@ -42,9 +41,9 @@ export async function runWalletDblessTest(t: GlobalTestState) {
|
|||||||
|
|
||||||
const { bank, exchange } = await createSimpleTestkudosEnvironment(t);
|
const { bank, exchange } = await createSimpleTestkudosEnvironment(t);
|
||||||
|
|
||||||
const http = new NodeHttpLib();
|
const http = createPlatformHttpLib();
|
||||||
const cryptiDisp = new CryptoDispatcher(
|
const cryptiDisp = new CryptoDispatcher(
|
||||||
new SynchronousCryptoWorkerFactoryNode(),
|
new SynchronousCryptoWorkerFactoryPlain(),
|
||||||
);
|
);
|
||||||
const cryptoApi = cryptiDisp.cryptoApi;
|
const cryptoApi = cryptiDisp.cryptoApi;
|
||||||
|
|
||||||
|
@ -37,13 +37,13 @@ import {
|
|||||||
Configuration,
|
Configuration,
|
||||||
decodeCrock,
|
decodeCrock,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
|
||||||
NodeHttpLib,
|
|
||||||
readSuccessResponseJsonOrThrow,
|
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process";
|
||||||
import { delayMs } from "./harness/harness.js";
|
import { delayMs } from "./harness/harness.js";
|
||||||
|
import {
|
||||||
|
createPlatformHttpLib,
|
||||||
|
readSuccessResponseJsonOrThrow,
|
||||||
|
} from "@gnu-taler/taler-util/http";
|
||||||
|
|
||||||
interface BasicConf {
|
interface BasicConf {
|
||||||
mainCurrency: string;
|
mainCurrency: string;
|
||||||
@ -53,7 +53,7 @@ interface PubkeyConf {
|
|||||||
masterPublicKey: string;
|
masterPublicKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const httpLib = new NodeHttpLib();
|
const httpLib = createPlatformHttpLib();
|
||||||
|
|
||||||
interface ShellResult {
|
interface ShellResult {
|
||||||
stdout: string;
|
stdout: string;
|
||||||
|
@ -15,12 +15,38 @@
|
|||||||
},
|
},
|
||||||
"./twrpc": {
|
"./twrpc": {
|
||||||
"default": "./lib/twrpc.js"
|
"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": {
|
"imports": {
|
||||||
"#twrpc-impl": {
|
"#twrpc-impl": {
|
||||||
"node": "./lib/twrpc-impl.node.js",
|
"node": "./lib/twrpc-impl.node.js"
|
||||||
"default": "./lib/twrpc-impl.missing.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": {
|
"scripts": {
|
||||||
|
@ -17,10 +17,12 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import process from "process";
|
import {
|
||||||
import path from "path";
|
processExit,
|
||||||
import readline from "readline";
|
processArgv,
|
||||||
import { devNull } from "os";
|
readlinePrompt,
|
||||||
|
pathBasename,
|
||||||
|
} from "#compat-impl";
|
||||||
|
|
||||||
export namespace clk {
|
export namespace clk {
|
||||||
class Converter<T> {}
|
class Converter<T> {}
|
||||||
@ -359,13 +361,13 @@ export namespace clk {
|
|||||||
console.error(
|
console.error(
|
||||||
`error: unknown option '--${r.key}' for ${currentName}`,
|
`error: unknown option '--${r.key}' for ${currentName}`,
|
||||||
);
|
);
|
||||||
process.exit(-1);
|
processExit(-1);
|
||||||
throw Error("not reached");
|
throw Error("not reached");
|
||||||
}
|
}
|
||||||
if (d.isFlag) {
|
if (d.isFlag) {
|
||||||
if (r.value !== undefined) {
|
if (r.value !== undefined) {
|
||||||
console.error(`error: flag '--${r.key}' does not take a value`);
|
console.error(`error: flag '--${r.key}' does not take a value`);
|
||||||
process.exit(-1);
|
processExit(-1);
|
||||||
throw Error("not reached");
|
throw Error("not reached");
|
||||||
}
|
}
|
||||||
storeFlag(d, true);
|
storeFlag(d, true);
|
||||||
@ -373,7 +375,7 @@ export namespace clk {
|
|||||||
if (r.value === undefined) {
|
if (r.value === undefined) {
|
||||||
if (i === unparsedArgs.length - 1) {
|
if (i === unparsedArgs.length - 1) {
|
||||||
console.error(`error: option '--${r.key}' needs an argument`);
|
console.error(`error: option '--${r.key}' needs an argument`);
|
||||||
process.exit(-1);
|
processExit(-1);
|
||||||
throw Error("not reached");
|
throw Error("not reached");
|
||||||
}
|
}
|
||||||
storeOption(d, unparsedArgs[i + 1]);
|
storeOption(d, unparsedArgs[i + 1]);
|
||||||
@ -391,7 +393,7 @@ export namespace clk {
|
|||||||
const opt = this.shortOptions[chr];
|
const opt = this.shortOptions[chr];
|
||||||
if (!opt) {
|
if (!opt) {
|
||||||
console.error(`error: option '-${chr}' not known`);
|
console.error(`error: option '-${chr}' not known`);
|
||||||
process.exit(-1);
|
processExit(-1);
|
||||||
}
|
}
|
||||||
if (opt.isFlag) {
|
if (opt.isFlag) {
|
||||||
storeFlag(opt, true);
|
storeFlag(opt, true);
|
||||||
@ -399,7 +401,7 @@ export namespace clk {
|
|||||||
if (si == optShort.length - 1) {
|
if (si == optShort.length - 1) {
|
||||||
if (i === unparsedArgs.length - 1) {
|
if (i === unparsedArgs.length - 1) {
|
||||||
console.error(`error: option '-${chr}' needs an argument`);
|
console.error(`error: option '-${chr}' needs an argument`);
|
||||||
process.exit(-1);
|
processExit(-1);
|
||||||
throw Error("not reached");
|
throw Error("not reached");
|
||||||
} else {
|
} else {
|
||||||
storeOption(opt, unparsedArgs[i + 1]);
|
storeOption(opt, unparsedArgs[i + 1]);
|
||||||
@ -418,7 +420,7 @@ export namespace clk {
|
|||||||
const subcmd = this.subcommandMap[argVal];
|
const subcmd = this.subcommandMap[argVal];
|
||||||
if (!subcmd) {
|
if (!subcmd) {
|
||||||
console.error(`error: unknown command '${argVal}'`);
|
console.error(`error: unknown command '${argVal}'`);
|
||||||
process.exit(-1);
|
processExit(-1);
|
||||||
throw Error("not reached");
|
throw Error("not reached");
|
||||||
}
|
}
|
||||||
foundSubcommand = subcmd.commandGroup;
|
foundSubcommand = subcmd.commandGroup;
|
||||||
@ -427,7 +429,7 @@ export namespace clk {
|
|||||||
const d = this.arguments[posArgIndex];
|
const d = this.arguments[posArgIndex];
|
||||||
if (!d) {
|
if (!d) {
|
||||||
console.error(`error: too many arguments for ${currentName}`);
|
console.error(`error: too many arguments for ${currentName}`);
|
||||||
process.exit(-1);
|
processExit(-1);
|
||||||
throw Error("not reached");
|
throw Error("not reached");
|
||||||
}
|
}
|
||||||
myArgs[d.name] = unparsedArgs[i];
|
myArgs[d.name] = unparsedArgs[i];
|
||||||
@ -437,7 +439,7 @@ export namespace clk {
|
|||||||
|
|
||||||
if (parsedArgs[this.argKey].help) {
|
if (parsedArgs[this.argKey].help) {
|
||||||
this.printHelp(progname, parents);
|
this.printHelp(progname, parents);
|
||||||
process.exit(0);
|
processExit(0);
|
||||||
throw Error("not reached");
|
throw Error("not reached");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,7 +452,7 @@ export namespace clk {
|
|||||||
console.error(
|
console.error(
|
||||||
`error: missing positional argument '${d.name}' for ${currentName}`,
|
`error: missing positional argument '${d.name}' for ${currentName}`,
|
||||||
);
|
);
|
||||||
process.exit(-1);
|
processExit(-1);
|
||||||
throw Error("not reached");
|
throw Error("not reached");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,7 +466,7 @@ export namespace clk {
|
|||||||
} else {
|
} else {
|
||||||
const name = option.flagspec.join(",");
|
const name = option.flagspec.join(",");
|
||||||
console.error(`error: missing option '${name}'`);
|
console.error(`error: missing option '${name}'`);
|
||||||
process.exit(-1);
|
processExit(-1);
|
||||||
throw Error("not reached");
|
throw Error("not reached");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -492,16 +494,16 @@ export namespace clk {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`An error occurred while running ${currentName}`);
|
console.error(`An error occurred while running ${currentName}`);
|
||||||
console.error(e);
|
console.error(e);
|
||||||
process.exit(1);
|
processExit(1);
|
||||||
}
|
}
|
||||||
Promise.resolve(r).catch((e) => {
|
Promise.resolve(r).catch((e) => {
|
||||||
console.error(`An error occurred while running ${currentName}`);
|
console.error(`An error occurred while running ${currentName}`);
|
||||||
console.error(e);
|
console.error(e);
|
||||||
process.exit(1);
|
processExit(1);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.printHelp(progname, parents);
|
this.printHelp(progname, parents);
|
||||||
process.exit(-1);
|
processExit(-1);
|
||||||
throw Error("not reached");
|
throw Error("not reached");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -524,15 +526,15 @@ export namespace clk {
|
|||||||
if (cmdlineArgs) {
|
if (cmdlineArgs) {
|
||||||
args = cmdlineArgs;
|
args = cmdlineArgs;
|
||||||
} else {
|
} else {
|
||||||
args = process.argv.slice(1);
|
args = processArgv().slice(1);
|
||||||
}
|
}
|
||||||
if (args.length < 1) {
|
if (args.length < 1) {
|
||||||
console.error(
|
console.error(
|
||||||
"Error while parsing command line arguments: not enough arguments",
|
"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);
|
const rest = args.slice(1);
|
||||||
|
|
||||||
this.mainCommand.run(progname, [], rest, {});
|
this.mainCommand.run(progname, [], rest, {});
|
||||||
@ -622,15 +624,6 @@ export namespace clk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function prompt(question: string): Promise<string> {
|
export function prompt(question: string): Promise<string> {
|
||||||
const stdinReadline = readline.createInterface({
|
return readlinePrompt(question);
|
||||||
input: process.stdin,
|
|
||||||
output: process.stdout,
|
|
||||||
});
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
|
||||||
stdinReadline.question(question, (res) => {
|
|
||||||
resolve(res);
|
|
||||||
stdinReadline.close();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of GNU Taler
|
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
|
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
|
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/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
export function processExit(status: number): never;
|
||||||
* Imports.
|
export function processArgv(): string[];
|
||||||
*/
|
export function readlinePrompt(prompt: string): Promise<string>;
|
||||||
import { CryptoWorkerFactory } from "./crypto-dispatcher.js";
|
export function pathBasename(s: string): string;
|
||||||
import { CryptoWorker } from "./cryptoWorkerInterface.js";
|
export function setUnhandledRejectionHandler(h: (e: any) => void): void;
|
||||||
import { SynchronousCryptoWorkerNode } from "./synchronousWorkerNode.js";
|
export function getenv(name: string): string | undefined;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
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,
|
CancellationToken,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { TalerErrorCode } 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");
|
const logger = new Logger("http.ts");
|
||||||
|
|
||||||
@ -352,3 +354,7 @@ export function getExpiry(
|
|||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createPlatformHttpLib(args?: HttpLibArgs): HttpRequestLibrary {
|
||||||
|
return new impl.HttpLibImpl(args);
|
||||||
|
}
|
@ -19,3 +19,7 @@
|
|||||||
import { loadBrowserPrng } from "./prng-browser.js";
|
import { loadBrowserPrng } from "./prng-browser.js";
|
||||||
loadBrowserPrng();
|
loadBrowserPrng();
|
||||||
export * from "./index.js";
|
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 "./index.js";
|
||||||
export * from "./talerconfig.js";
|
export * from "./talerconfig.js";
|
||||||
export * from "./globbing/minimatch.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 "./contract-terms.js";
|
||||||
export * from "./base64.js";
|
export * from "./base64.js";
|
||||||
export * from "./merchant-api-types.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/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { RpcConnectArgs, RpcServerArgs } from "./twrpc.js";
|
||||||
|
|
||||||
// Not implemented.
|
// 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 = {
|
export const buildConfig = {
|
||||||
entryPoints: ["src/index.ts"],
|
entryPoints: ["src/index.ts"],
|
||||||
outfile: "dist/taler-wallet-cli.mjs",
|
outfile: "dist/taler-wallet-cli.qtart.mjs",
|
||||||
bundle: true,
|
bundle: true,
|
||||||
minify: false,
|
minify: false,
|
||||||
target: [
|
target: [
|
||||||
@ -61,7 +61,11 @@ export const buildConfig = {
|
|||||||
],
|
],
|
||||||
format: 'esm',
|
format: 'esm',
|
||||||
platform: 'neutral',
|
platform: 'neutral',
|
||||||
|
mainFields: ["module", "main"],
|
||||||
|
conditions: ["qtart"],
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
|
// quickjs standard library
|
||||||
|
external: ["std", "os"],
|
||||||
define: {
|
define: {
|
||||||
'__VERSION__': `"${_package.version}"`,
|
'__VERSION__': `"${_package.version}"`,
|
||||||
'__GIT_HASH__': `"${GIT_HASH}"`,
|
'__GIT_HASH__': `"${GIT_HASH}"`,
|
||||||
|
@ -21,12 +21,11 @@ import {
|
|||||||
addPaytoQueryParams,
|
addPaytoQueryParams,
|
||||||
AgeRestriction,
|
AgeRestriction,
|
||||||
classifyTalerUri,
|
classifyTalerUri,
|
||||||
clk,
|
|
||||||
codecForList,
|
codecForList,
|
||||||
codecForString,
|
codecForString,
|
||||||
CoreApiResponse,
|
CoreApiResponse,
|
||||||
decodeCrock,
|
|
||||||
encodeCrock,
|
encodeCrock,
|
||||||
|
getErrorDetailFromException,
|
||||||
getRandomBytes,
|
getRandomBytes,
|
||||||
j2s,
|
j2s,
|
||||||
Logger,
|
Logger,
|
||||||
@ -35,20 +34,26 @@ import {
|
|||||||
RecoveryMergeStrategy,
|
RecoveryMergeStrategy,
|
||||||
setDangerousTimetravel,
|
setDangerousTimetravel,
|
||||||
setGlobalLogLevelFromString,
|
setGlobalLogLevelFromString,
|
||||||
|
summarizeTalerErrorDetail,
|
||||||
TalerUriType,
|
TalerUriType,
|
||||||
WalletNotification,
|
WalletNotification,
|
||||||
} from "@gnu-taler/taler-util";
|
} 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 { JsonMessage, runRpcServer } from "@gnu-taler/taler-util/twrpc";
|
||||||
import {
|
import {
|
||||||
|
createNativeWalletHost,
|
||||||
|
createNativeWalletHost2,
|
||||||
CryptoDispatcher,
|
CryptoDispatcher,
|
||||||
getDefaultNodeWallet,
|
|
||||||
getDefaultNodeWallet2,
|
|
||||||
getErrorDetailFromException,
|
|
||||||
nativeCrypto,
|
nativeCrypto,
|
||||||
NodeHttpLib,
|
//NodeThreadCryptoWorkerFactory,
|
||||||
NodeThreadCryptoWorkerFactory,
|
//SynchronousCryptoWorkerFactoryPlain,
|
||||||
summarizeTalerErrorDetail,
|
|
||||||
SynchronousCryptoWorkerFactoryNode,
|
|
||||||
TalerCryptoInterface,
|
TalerCryptoInterface,
|
||||||
Wallet,
|
Wallet,
|
||||||
WalletApiOperation,
|
WalletApiOperation,
|
||||||
@ -60,8 +65,6 @@ import {
|
|||||||
getClientFromRemoteWallet,
|
getClientFromRemoteWallet,
|
||||||
makeNotificationWaiter,
|
makeNotificationWaiter,
|
||||||
} from "@gnu-taler/taler-wallet-core/remote";
|
} 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
|
// This module also serves as the entry point for the crypto
|
||||||
// thread worker, and thus must expose these two handlers.
|
// thread worker, and thus must expose these two handlers.
|
||||||
@ -76,13 +79,13 @@ const EXIT_EXCEPTION = 4;
|
|||||||
const EXIT_API_ERROR = 5;
|
const EXIT_API_ERROR = 5;
|
||||||
const EXIT_RETRIES_EXCEEDED = 6;
|
const EXIT_RETRIES_EXCEEDED = 6;
|
||||||
|
|
||||||
process.on("unhandledRejection", (error: any) => {
|
setUnhandledRejectionHandler((error: any) => {
|
||||||
logger.error("unhandledRejection", error.message);
|
logger.error("unhandledRejection", error.message);
|
||||||
logger.error("stack", error.stack);
|
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 {
|
function assertUnreachable(x: never): never {
|
||||||
throw new Error("Didn't expect to get here");
|
throw new Error("Didn't expect to get here");
|
||||||
@ -99,7 +102,7 @@ async function doPay(
|
|||||||
if (result.status === PreparePayResultType.InsufficientBalance) {
|
if (result.status === PreparePayResultType.InsufficientBalance) {
|
||||||
console.log("contract", result.contractTerms);
|
console.log("contract", result.contractTerms);
|
||||||
console.error("insufficient balance");
|
console.error("insufficient balance");
|
||||||
process.exit(1);
|
processExit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (result.status === PreparePayResultType.AlreadyConfirmed) {
|
if (result.status === PreparePayResultType.AlreadyConfirmed) {
|
||||||
@ -108,8 +111,7 @@ async function doPay(
|
|||||||
} else {
|
} else {
|
||||||
console.log("payment already in progress");
|
console.log("payment already in progress");
|
||||||
}
|
}
|
||||||
|
processExit(0);
|
||||||
process.exit(0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (result.status === "payment-possible") {
|
if (result.status === "payment-possible") {
|
||||||
@ -154,7 +156,7 @@ function applyVerbose(verbose: boolean): void {
|
|||||||
declare const __VERSION__: string;
|
declare const __VERSION__: string;
|
||||||
function printVersion(): void {
|
function printVersion(): void {
|
||||||
console.log(__VERSION__);
|
console.log(__VERSION__);
|
||||||
process.exit(0);
|
processExit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const walletCli = clk
|
export const walletCli = clk
|
||||||
@ -203,7 +205,7 @@ export const walletCli = clk
|
|||||||
type WalletCliArgsType = clk.GetArgType<typeof walletCli>;
|
type WalletCliArgsType = clk.GetArgType<typeof walletCli>;
|
||||||
|
|
||||||
function checkEnvFlag(name: string): boolean {
|
function checkEnvFlag(name: string): boolean {
|
||||||
const val = process.env[name];
|
const val = getenv(name);
|
||||||
if (val == "1") {
|
if (val == "1") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -238,11 +240,10 @@ async function createLocalWallet(
|
|||||||
notificationHandler?: (n: WalletNotification) => void,
|
notificationHandler?: (n: WalletNotification) => void,
|
||||||
): Promise<Wallet> {
|
): Promise<Wallet> {
|
||||||
const dbPath = walletCliArgs.wallet.walletDbFile ?? defaultWalletDbPath;
|
const dbPath = walletCliArgs.wallet.walletDbFile ?? defaultWalletDbPath;
|
||||||
const myHttpLib = new NodeHttpLib();
|
const myHttpLib = createPlatformHttpLib({
|
||||||
if (walletCliArgs.wallet.noThrottle) {
|
enableThrottling: walletCliArgs.wallet.noThrottle ? false : true,
|
||||||
myHttpLib.setThrottling(false);
|
});
|
||||||
}
|
const wallet = await createNativeWalletHost({
|
||||||
const wallet = await getDefaultNodeWallet({
|
|
||||||
persistentStoragePath: dbPath !== ":memory:" ? dbPath : undefined,
|
persistentStoragePath: dbPath !== ":memory:" ? dbPath : undefined,
|
||||||
httpLib: myHttpLib,
|
httpLib: myHttpLib,
|
||||||
notifyHandler: (n) => {
|
notifyHandler: (n) => {
|
||||||
@ -268,7 +269,7 @@ async function createLocalWallet(
|
|||||||
const ed = getErrorDetailFromException(e);
|
const ed = getErrorDetailFromException(e);
|
||||||
console.error("Operation failed: " + summarizeTalerErrorDetail(ed));
|
console.error("Operation failed: " + summarizeTalerErrorDetail(ed));
|
||||||
console.error("Error details:", JSON.stringify(ed, undefined, 2));
|
console.error("Error details:", JSON.stringify(ed, undefined, 2));
|
||||||
process.exit(1);
|
processExit(1);
|
||||||
} finally {
|
} finally {
|
||||||
logger.trace("operation with wallet finished, stopping");
|
logger.trace("operation with wallet finished, stopping");
|
||||||
logger.trace("stopped wallet");
|
logger.trace("stopped wallet");
|
||||||
@ -357,7 +358,7 @@ walletCli
|
|||||||
requestJson = JSON.parse(args.api.request);
|
requestJson = JSON.parse(args.api.request);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Invalid JSON");
|
console.error("Invalid JSON");
|
||||||
process.exit(1);
|
processExit(1);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const resp = await wallet.makeCoreApiRequest(
|
const resp = await wallet.makeCoreApiRequest(
|
||||||
@ -367,12 +368,12 @@ walletCli
|
|||||||
console.log(JSON.stringify(resp, undefined, 2));
|
console.log(JSON.stringify(resp, undefined, 2));
|
||||||
if (resp.type === "error") {
|
if (resp.type === "error") {
|
||||||
if (args.api.expectSuccess) {
|
if (args.api.expectSuccess) {
|
||||||
process.exit(EXIT_API_ERROR);
|
processExit(EXIT_API_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(`Got exception while handling API request ${e}`);
|
logger.error(`Got exception while handling API request ${e}`);
|
||||||
process.exit(EXIT_EXCEPTION);
|
processExit(EXIT_EXCEPTION);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
logger.info("finished handling API request");
|
logger.info("finished handling API request");
|
||||||
@ -475,7 +476,7 @@ walletCli
|
|||||||
});
|
});
|
||||||
wallet.ws.stop();
|
wallet.ws.stop();
|
||||||
if (resp.retriesExceeded && args.finishPendingOpt.failOnMaxRetries) {
|
if (resp.retriesExceeded && args.finishPendingOpt.failOnMaxRetries) {
|
||||||
process.exit(EXIT_RETRIES_EXCEEDED);
|
processExit(EXIT_RETRIES_EXCEEDED);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -594,7 +595,7 @@ walletCli
|
|||||||
const selectedExchange = withdrawInfo.defaultExchangeBaseUrl;
|
const selectedExchange = withdrawInfo.defaultExchangeBaseUrl;
|
||||||
if (!selectedExchange) {
|
if (!selectedExchange) {
|
||||||
console.error("no suggested exchange!");
|
console.error("no suggested exchange!");
|
||||||
process.exit(1);
|
processExit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const res = await wallet.client.call(
|
const res = await wallet.client.call(
|
||||||
@ -1014,9 +1015,10 @@ advancedCli
|
|||||||
help: "Run the 'bench-internal' benchmark",
|
help: "Run the 'bench-internal' benchmark",
|
||||||
})
|
})
|
||||||
.action(async (args) => {
|
.action(async (args) => {
|
||||||
const myHttpLib = new NodeHttpLib();
|
const myHttpLib = createPlatformHttpLib({
|
||||||
myHttpLib.setThrottling(false);
|
enableThrottling: false,
|
||||||
const res = await getDefaultNodeWallet2({
|
});
|
||||||
|
const res = await createNativeWalletHost2({
|
||||||
// No persistent DB storage.
|
// No persistent DB storage.
|
||||||
persistentStoragePath: undefined,
|
persistentStoragePath: undefined,
|
||||||
httpLib: myHttpLib,
|
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
|
advancedCli
|
||||||
.subcommand("genSegwit", "gen-segwit")
|
.subcommand("genSegwit", "gen-segwit")
|
||||||
.requiredArgument("paytoUri", clk.STRING)
|
.requiredArgument("paytoUri", clk.STRING)
|
||||||
@ -1229,7 +1222,7 @@ advancedCli
|
|||||||
);
|
);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.log("could not parse coin list:", e.message);
|
console.log("could not parse coin list:", e.message);
|
||||||
process.exit(1);
|
processExit(1);
|
||||||
}
|
}
|
||||||
for (const c of coinPubList) {
|
for (const c of coinPubList) {
|
||||||
await wallet.client.call(WalletApiOperation.SetCoinSuspended, {
|
await wallet.client.call(WalletApiOperation.SetCoinSuspended, {
|
||||||
@ -1254,7 +1247,7 @@ advancedCli
|
|||||||
);
|
);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.log("could not parse coin list:", e.message);
|
console.log("could not parse coin list:", e.message);
|
||||||
process.exit(1);
|
processExit(1);
|
||||||
}
|
}
|
||||||
for (const c of coinPubList) {
|
for (const c of coinPubList) {
|
||||||
await wallet.client.call(WalletApiOperation.SetCoinSuspended, {
|
await wallet.client.call(WalletApiOperation.SetCoinSuspended, {
|
||||||
@ -1420,33 +1413,33 @@ async function read(stream: NodeJS.ReadStream) {
|
|||||||
return Buffer.concat(chunks).toString("utf8");
|
return Buffer.concat(chunks).toString("utf8");
|
||||||
}
|
}
|
||||||
|
|
||||||
testCli
|
// testCli
|
||||||
.subcommand("cryptoworker", "cryptoworker")
|
// .subcommand("cryptoworker", "cryptoworker")
|
||||||
.maybeOption("impl", ["--impl"], clk.STRING)
|
// .maybeOption("impl", ["--impl"], clk.STRING)
|
||||||
.action(async (args) => {
|
// .action(async (args) => {
|
||||||
let cryptoApi: TalerCryptoInterface;
|
// let cryptoApi: TalerCryptoInterface;
|
||||||
if (!args.cryptoworker.impl || args.cryptoworker.impl === "node") {
|
// if (!args.cryptoworker.impl || args.cryptoworker.impl === "node") {
|
||||||
const workerFactory = new NodeThreadCryptoWorkerFactory();
|
// const workerFactory = new NodeThreadCryptoWorkerFactory();
|
||||||
const cryptoDisp = new CryptoDispatcher(workerFactory);
|
// const cryptoDisp = new CryptoDispatcher(workerFactory);
|
||||||
cryptoApi = cryptoDisp.cryptoApi;
|
// cryptoApi = cryptoDisp.cryptoApi;
|
||||||
} else if (args.cryptoworker.impl === "sync") {
|
// } else if (args.cryptoworker.impl === "sync") {
|
||||||
const workerFactory = new SynchronousCryptoWorkerFactoryNode();
|
// const workerFactory = new SynchronousCryptoWorkerFactoryPlain();
|
||||||
const cryptoDisp = new CryptoDispatcher(workerFactory);
|
// const cryptoDisp = new CryptoDispatcher(workerFactory);
|
||||||
cryptoApi = cryptoDisp.cryptoApi;
|
// cryptoApi = cryptoDisp.cryptoApi;
|
||||||
} else if (args.cryptoworker.impl === "none") {
|
// } else if (args.cryptoworker.impl === "none") {
|
||||||
cryptoApi = nativeCrypto;
|
// cryptoApi = nativeCrypto;
|
||||||
} else {
|
// } else {
|
||||||
throw Error(`invalid crypto worker type ${args.cryptoworker.impl}`);
|
// throw Error(`invalid crypto worker type ${args.cryptoworker.impl}`);
|
||||||
}
|
// }
|
||||||
|
|
||||||
const input = "foo";
|
// const input = "foo";
|
||||||
console.log(`testing crypto worker by hashing string '${input}'`);
|
// console.log(`testing crypto worker by hashing string '${input}'`);
|
||||||
const res = await cryptoApi.hashString({ str: input });
|
// const res = await cryptoApi.hashString({ str: input });
|
||||||
console.log(res);
|
// console.log(res);
|
||||||
});
|
// });
|
||||||
|
|
||||||
export function main() {
|
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");
|
logger.warn("Allowing withdrawal of late denominations for debugging");
|
||||||
walletCoreDebugFlags.denomselAllowLate = true;
|
walletCoreDebugFlags.denomselAllowLate = true;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,13 @@
|
|||||||
"default": "./lib/index.js"
|
"default": "./lib/index.js"
|
||||||
},
|
},
|
||||||
"./remote": {
|
"./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": {
|
"devDependencies": {
|
||||||
|
@ -33,13 +33,10 @@ import {
|
|||||||
j2s,
|
j2s,
|
||||||
Logger,
|
Logger,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
|
TalerError,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { TalerError } from "./errors.js";
|
import { HttpRequestLibrary, readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
|
||||||
import {
|
|
||||||
HttpRequestLibrary,
|
|
||||||
readSuccessResponseJsonOrThrow,
|
|
||||||
} from "./util/http.js";
|
|
||||||
|
|
||||||
const logger = new Logger("bank-api-client.ts");
|
const logger = new Logger("bank-api-client.ts");
|
||||||
|
|
||||||
|
@ -21,8 +21,6 @@ import {
|
|||||||
CryptoWorker,
|
CryptoWorker,
|
||||||
CryptoWorkerResponseMessage,
|
CryptoWorkerResponseMessage,
|
||||||
} from "./cryptoWorkerInterface.js";
|
} from "./cryptoWorkerInterface.js";
|
||||||
import { SynchronousCryptoWorkerFactoryNode } from "./synchronousWorkerFactoryNode.js";
|
|
||||||
import { processRequestWithImpl } from "./worker-common.js";
|
|
||||||
|
|
||||||
export class MyCryptoWorker implements CryptoWorker {
|
export class MyCryptoWorker implements CryptoWorker {
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { j2s, Logger, TalerErrorCode } from "@gnu-taler/taler-util";
|
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 { openPromise } from "../../util/promiseUtils.js";
|
||||||
import { timer, performanceNow, TimerHandle } from "../../util/timer.js";
|
import { timer, performanceNow, TimerHandle } from "../../util/timer.js";
|
||||||
import { nullCrypto, TalerCryptoInterface } from "../cryptoImplementation.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,
|
stringifyError as safeStringifyError,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
} from "@gnu-taler/taler-util";
|
} 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 { TalerCryptoInterfaceR } from "../cryptoImplementation.js";
|
||||||
import {
|
import {
|
||||||
CryptoWorkerRequestMessage,
|
CryptoWorkerRequestMessage,
|
||||||
|
@ -2659,6 +2659,9 @@ function onMetaDbUpgradeNeeded(
|
|||||||
/**
|
/**
|
||||||
* Return a promise that resolves
|
* Return a promise that resolves
|
||||||
* to the taler wallet db.
|
* to the taler wallet db.
|
||||||
|
*
|
||||||
|
* @param onVersionChange Called when another client concurrenctly connects to the database
|
||||||
|
* with a higher version.
|
||||||
*/
|
*/
|
||||||
export async function openTalerDatabase(
|
export async function openTalerDatabase(
|
||||||
idbFactory: IDBFactory,
|
idbFactory: IDBFactory,
|
||||||
|
@ -58,7 +58,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
HttpRequestLibrary,
|
HttpRequestLibrary,
|
||||||
readSuccessResponseJsonOrThrow,
|
readSuccessResponseJsonOrThrow,
|
||||||
} from "./util/http.js";
|
} from "@gnu-taler/taler-util/http";
|
||||||
import {
|
import {
|
||||||
getBankStatusUrl,
|
getBankStatusUrl,
|
||||||
getBankWithdrawalInfo,
|
getBankWithdrawalInfo,
|
||||||
|
@ -32,7 +32,7 @@ import {
|
|||||||
HttpRequestLibrary,
|
HttpRequestLibrary,
|
||||||
HttpRequestOptions,
|
HttpRequestOptions,
|
||||||
HttpResponse,
|
HttpResponse,
|
||||||
} from "./util/http.js";
|
} from "@gnu-taler/taler-util/http";
|
||||||
|
|
||||||
const logger = new Logger("dev-experiments.ts");
|
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,
|
shimIndexedDB,
|
||||||
} from "@gnu-taler/idb-bridge";
|
} from "@gnu-taler/idb-bridge";
|
||||||
import { AccessStats } 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 * as fs from "fs";
|
||||||
import { NodeThreadCryptoWorkerFactory } from "../crypto/workers/nodeThreadWorker.js";
|
import { NodeThreadCryptoWorkerFactory } from "./crypto/workers/nodeThreadWorker.js";
|
||||||
import { SynchronousCryptoWorkerFactoryNode } from "../crypto/workers/synchronousWorkerFactoryNode.js";
|
import { SynchronousCryptoWorkerFactoryPlain } from "./crypto/workers/synchronousWorkerFactoryPlain.js";
|
||||||
import { openTalerDatabase } from "../index.js";
|
import { openTalerDatabase } from "./index.js";
|
||||||
import { HttpRequestLibrary } from "../util/http.js";
|
import {
|
||||||
import { SetTimeoutTimerAPI } from "../util/timer.js";
|
createPlatformHttpLib,
|
||||||
import { Wallet } from "../wallet.js";
|
} from "@gnu-taler/taler-util/http";
|
||||||
import { NodeHttpLib } from "./NodeHttpLib.js";
|
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.
|
* Get a wallet instance with default settings for node.
|
||||||
*
|
*
|
||||||
* Extended version that allows getting DB stats.
|
* Extended version that allows getting DB stats.
|
||||||
*/
|
*/
|
||||||
export async function getDefaultNodeWallet2(
|
export async function createNativeWalletHost2(
|
||||||
args: DefaultNodeWalletArgs = {},
|
args: DefaultNodeWalletArgs = {},
|
||||||
): Promise<{
|
): Promise<{
|
||||||
wallet: Wallet;
|
wallet: Wallet;
|
||||||
@ -127,7 +84,8 @@ export async function getDefaultNodeWallet2(
|
|||||||
if (args.persistentStoragePath === undefined) {
|
if (args.persistentStoragePath === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tmpPath = `${args.persistentStoragePath}-${makeId(5)}.tmp`;
|
const tmpPath = `${args.persistentStoragePath}-${makeTempfileId(5)}.tmp`;
|
||||||
|
logger.trace("exported DB dump");
|
||||||
const dbContent = myBackend.exportDump();
|
const dbContent = myBackend.exportDump();
|
||||||
fs.writeFileSync(tmpPath, JSON.stringify(dbContent, undefined, 2), {
|
fs.writeFileSync(tmpPath, JSON.stringify(dbContent, undefined, 2), {
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
@ -147,7 +105,9 @@ export async function getDefaultNodeWallet2(
|
|||||||
if (args.httpLib) {
|
if (args.httpLib) {
|
||||||
myHttpLib = args.httpLib;
|
myHttpLib = args.httpLib;
|
||||||
} else {
|
} else {
|
||||||
myHttpLib = new NodeHttpLib();
|
myHttpLib = createPlatformHttpLib({
|
||||||
|
enableThrottling: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const myVersionChange = (): Promise<void> => {
|
const myVersionChange = (): Promise<void> => {
|
||||||
@ -165,7 +125,7 @@ export async function getDefaultNodeWallet2(
|
|||||||
const cryptoWorkerType = args.cryptoWorkerType ?? "node-worker-thread";
|
const cryptoWorkerType = args.cryptoWorkerType ?? "node-worker-thread";
|
||||||
if (cryptoWorkerType === "sync") {
|
if (cryptoWorkerType === "sync") {
|
||||||
logger.info("using synchronous crypto worker");
|
logger.info("using synchronous crypto worker");
|
||||||
workerFactory = new SynchronousCryptoWorkerFactoryNode();
|
workerFactory = new SynchronousCryptoWorkerFactoryPlain();
|
||||||
} else if (cryptoWorkerType === "node-worker-thread") {
|
} else if (cryptoWorkerType === "node-worker-thread") {
|
||||||
try {
|
try {
|
||||||
// Try if we have worker threads available, fails in older node versions.
|
// Try if we have worker threads available, fails in older node versions.
|
||||||
@ -179,7 +139,7 @@ export async function getDefaultNodeWallet2(
|
|||||||
logger.warn(
|
logger.warn(
|
||||||
"worker threads not available, falling back to synchronous workers",
|
"worker threads not available, falling back to synchronous workers",
|
||||||
);
|
);
|
||||||
workerFactory = new SynchronousCryptoWorkerFactoryNode();
|
workerFactory = new SynchronousCryptoWorkerFactoryPlain();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw Error(`unsupported crypto worker type '${cryptoWorkerType}'`);
|
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";
|
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 * 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 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.
|
* Module entry point for the wallet when used as a node module.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Errors
|
|
||||||
export * from "./errors.js";
|
|
||||||
|
|
||||||
// Util functionality
|
// Util functionality
|
||||||
export * from "./util/promiseUtils.js";
|
export * from "./util/promiseUtils.js";
|
||||||
export * from "./util/query.js";
|
export * from "./util/query.js";
|
||||||
export * from "./util/http.js";
|
|
||||||
|
|
||||||
export * from "./versions.js";
|
export * from "./versions.js";
|
||||||
|
|
||||||
@ -67,3 +63,5 @@ export * from "./util/timer.js";
|
|||||||
export * from "./util/denominations.js";
|
export * from "./util/denominations.js";
|
||||||
|
|
||||||
export { SynchronousCryptoWorkerFactoryPlain } from "./crypto/workers/synchronousWorkerFactoryPlain.js";
|
export { SynchronousCryptoWorkerFactoryPlain } from "./crypto/workers/synchronousWorkerFactoryPlain.js";
|
||||||
|
export * from "./host-common.js";
|
||||||
|
export * from "./host.js";
|
||||||
|
@ -30,18 +30,14 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
WalletNotification,
|
|
||||||
BalancesResponse,
|
|
||||||
AmountJson,
|
|
||||||
DenominationPubKey,
|
|
||||||
TalerProtocolTimestamp,
|
|
||||||
CancellationToken,
|
CancellationToken,
|
||||||
|
CoinRefreshRequest,
|
||||||
DenominationInfo,
|
DenominationInfo,
|
||||||
RefreshGroupId,
|
RefreshGroupId,
|
||||||
CoinRefreshRequest,
|
|
||||||
RefreshReason,
|
RefreshReason,
|
||||||
|
WalletNotification,
|
||||||
} from "@gnu-taler/taler-util";
|
} 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 { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
|
||||||
import {
|
import {
|
||||||
ExchangeDetailsRecord,
|
ExchangeDetailsRecord,
|
||||||
@ -49,9 +45,6 @@ import {
|
|||||||
RefreshReasonDetails,
|
RefreshReasonDetails,
|
||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
} from "./db.js";
|
} 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 { AsyncCondition } from "./util/promiseUtils.js";
|
||||||
import {
|
import {
|
||||||
DbAccess,
|
DbAccess,
|
||||||
|
@ -85,13 +85,13 @@ import {
|
|||||||
ConfigRecordKey,
|
ConfigRecordKey,
|
||||||
WalletBackupConfState,
|
WalletBackupConfState,
|
||||||
} from "../../db.js";
|
} from "../../db.js";
|
||||||
import { TalerError } from "../../errors.js";
|
import { TalerError } from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState } from "../../internal-wallet-state.js";
|
import { InternalWalletState } from "../../internal-wallet-state.js";
|
||||||
import { assertUnreachable } from "../../util/assertUnreachable.js";
|
import { assertUnreachable } from "../../util/assertUnreachable.js";
|
||||||
import {
|
import {
|
||||||
readSuccessResponseJsonOrThrow,
|
readSuccessResponseJsonOrThrow,
|
||||||
readTalerErrorResponse,
|
readTalerErrorResponse,
|
||||||
} from "../../util/http.js";
|
} from "@gnu-taler/taler-util/http";
|
||||||
import {
|
import {
|
||||||
checkDbInvariant,
|
checkDbInvariant,
|
||||||
checkLogicInvariant,
|
checkLogicInvariant,
|
||||||
|
@ -42,7 +42,7 @@ import {
|
|||||||
ExchangeDetailsRecord,
|
ExchangeDetailsRecord,
|
||||||
ExchangeRecord,
|
ExchangeRecord,
|
||||||
} from "../db.js";
|
} 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 { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
|
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
|
||||||
import { GetReadWriteAccess } from "../util/query.js";
|
import { GetReadWriteAccess } from "../util/query.js";
|
||||||
|
@ -62,10 +62,10 @@ import {
|
|||||||
OperationStatus,
|
OperationStatus,
|
||||||
TransactionStatus,
|
TransactionStatus,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { TalerError } from "../errors.js";
|
import { TalerError } from "@gnu-taler/taler-util";
|
||||||
import { checkWithdrawalKycStatus, KycPendingInfo, KycUserType } from "../index.js";
|
import { KycPendingInfo, KycUserType } from "../index.js";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.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 { OperationAttemptResult } from "../util/retries.js";
|
||||||
import { makeTransactionId, spendCoins } from "./common.js";
|
import { makeTransactionId, spendCoins } from "./common.js";
|
||||||
import { getExchangeDetails } from "./exchanges.js";
|
import { getExchangeDetails } from "./exchanges.js";
|
||||||
|
@ -41,6 +41,7 @@ import {
|
|||||||
NotificationType,
|
NotificationType,
|
||||||
parsePaytoUri,
|
parsePaytoUri,
|
||||||
Recoup,
|
Recoup,
|
||||||
|
TalerError,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
TalerProtocolDuration,
|
TalerProtocolDuration,
|
||||||
TalerProtocolTimestamp,
|
TalerProtocolTimestamp,
|
||||||
@ -49,6 +50,7 @@ import {
|
|||||||
WireFeeMap,
|
WireFeeMap,
|
||||||
WireInfo,
|
WireInfo,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
|
import { HttpRequestLibrary, readSuccessResponseTextOrThrow, readSuccessResponseJsonOrThrow, getExpiry } from "@gnu-taler/taler-util/http";
|
||||||
import {
|
import {
|
||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
DenominationVerificationStatus,
|
DenominationVerificationStatus,
|
||||||
@ -56,14 +58,7 @@ import {
|
|||||||
ExchangeRecord,
|
ExchangeRecord,
|
||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { TalerError } from "../errors.js";
|
|
||||||
import { InternalWalletState, TrustInfo } from "../internal-wallet-state.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 { checkDbInvariant } from "../util/invariants.js";
|
||||||
import {
|
import {
|
||||||
DbAccess,
|
DbAccess,
|
||||||
|
@ -25,7 +25,7 @@ import {
|
|||||||
LibtoolVersion,
|
LibtoolVersion,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState, MerchantInfo } from "../internal-wallet-state.js";
|
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");
|
const logger = new Logger("taler-wallet-core:merchants.ts");
|
||||||
|
|
||||||
|
@ -94,13 +94,12 @@ import {
|
|||||||
makePendingOperationFailedError,
|
makePendingOperationFailedError,
|
||||||
TalerError,
|
TalerError,
|
||||||
TalerProtocolViolationError,
|
TalerProtocolViolationError,
|
||||||
} from "../errors.js";
|
} from "@gnu-taler/taler-util";
|
||||||
import { GetReadWriteAccess } from "../index.browser.js";
|
import { GetReadWriteAccess } from "../index.js";
|
||||||
import {
|
import {
|
||||||
EXCHANGE_COINS_LOCK,
|
EXCHANGE_COINS_LOCK,
|
||||||
InternalWalletState,
|
InternalWalletState,
|
||||||
} from "../internal-wallet-state.js";
|
} from "../internal-wallet-state.js";
|
||||||
import { PendingTaskType } from "../pending-types.js";
|
|
||||||
import { assertUnreachable } from "../util/assertUnreachable.js";
|
import { assertUnreachable } from "../util/assertUnreachable.js";
|
||||||
import {
|
import {
|
||||||
CoinSelectionTally,
|
CoinSelectionTally,
|
||||||
@ -114,7 +113,7 @@ import {
|
|||||||
readTalerErrorResponse,
|
readTalerErrorResponse,
|
||||||
readUnexpectedResponseDetails,
|
readUnexpectedResponseDetails,
|
||||||
throwUnexpectedRequestError,
|
throwUnexpectedRequestError,
|
||||||
} from "../util/http.js";
|
} from "@gnu-taler/taler-util/http";
|
||||||
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
|
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
|
||||||
import {
|
import {
|
||||||
OperationAttemptResult,
|
OperationAttemptResult,
|
||||||
|
@ -81,16 +81,15 @@ import {
|
|||||||
WithdrawalGroupStatus,
|
WithdrawalGroupStatus,
|
||||||
WithdrawalRecordType,
|
WithdrawalRecordType,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { TalerError } from "../errors.js";
|
import { TalerError } from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
import {
|
import {
|
||||||
makeTransactionId,
|
makeTransactionId,
|
||||||
runOperationWithErrorReporting,
|
runOperationWithErrorReporting,
|
||||||
spendCoins,
|
spendCoins,
|
||||||
} from "../operations/common.js";
|
} 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 { checkDbInvariant } from "../util/invariants.js";
|
||||||
import { GetReadOnlyAccess } from "../util/query.js";
|
|
||||||
import {
|
import {
|
||||||
OperationAttemptResult,
|
OperationAttemptResult,
|
||||||
OperationAttemptResultType,
|
OperationAttemptResultType,
|
||||||
|
@ -49,7 +49,7 @@ import {
|
|||||||
WithdrawCoinSource,
|
WithdrawCoinSource,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.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 { checkDbInvariant } from "../util/invariants.js";
|
||||||
import { GetReadWriteAccess } from "../util/query.js";
|
import { GetReadWriteAccess } from "../util/query.js";
|
||||||
import {
|
import {
|
||||||
|
@ -63,7 +63,7 @@ import {
|
|||||||
RefreshReasonDetails,
|
RefreshReasonDetails,
|
||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { TalerError } from "../errors.js";
|
import { TalerError } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
EXCHANGE_COINS_LOCK,
|
EXCHANGE_COINS_LOCK,
|
||||||
InternalWalletState,
|
InternalWalletState,
|
||||||
@ -72,7 +72,7 @@ import { assertUnreachable } from "../util/assertUnreachable.js";
|
|||||||
import {
|
import {
|
||||||
readSuccessResponseJsonOrThrow,
|
readSuccessResponseJsonOrThrow,
|
||||||
readUnexpectedResponseDetails,
|
readUnexpectedResponseDetails,
|
||||||
} from "../util/http.js";
|
} from "@gnu-taler/taler-util/http";
|
||||||
import { checkDbInvariant } from "../util/invariants.js";
|
import { checkDbInvariant } from "../util/invariants.js";
|
||||||
import { GetReadWriteAccess } from "../util/query.js";
|
import { GetReadWriteAccess } from "../util/query.js";
|
||||||
import {
|
import {
|
||||||
|
@ -29,7 +29,7 @@ import {
|
|||||||
HttpRequestLibrary,
|
HttpRequestLibrary,
|
||||||
readSuccessResponseJsonOrThrow,
|
readSuccessResponseJsonOrThrow,
|
||||||
checkSuccessResponseOrThrow,
|
checkSuccessResponseOrThrow,
|
||||||
} from "../util/http.js";
|
} from "@gnu-taler/taler-util/http";
|
||||||
import {
|
import {
|
||||||
AmountString,
|
AmountString,
|
||||||
codecForAny,
|
codecForAny,
|
||||||
|
@ -45,12 +45,12 @@ import {
|
|||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
TipRecord,
|
TipRecord,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { makeErrorDetail } from "../errors.js";
|
import { makeErrorDetail } from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
import {
|
import {
|
||||||
getHttpResponseErrorDetails,
|
getHttpResponseErrorDetails,
|
||||||
readSuccessResponseJsonOrThrow,
|
readSuccessResponseJsonOrThrow,
|
||||||
} from "../util/http.js";
|
} from "@gnu-taler/taler-util/http";
|
||||||
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
|
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
|
||||||
import {
|
import {
|
||||||
OperationAttemptResult,
|
OperationAttemptResult,
|
||||||
|
@ -85,7 +85,7 @@ import {
|
|||||||
getErrorDetailFromException,
|
getErrorDetailFromException,
|
||||||
makeErrorDetail,
|
makeErrorDetail,
|
||||||
TalerError,
|
TalerError,
|
||||||
} from "../errors.js";
|
} from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
import {
|
import {
|
||||||
makeCoinAvailable,
|
makeCoinAvailable,
|
||||||
@ -99,7 +99,7 @@ import {
|
|||||||
readSuccessResponseJsonOrErrorCode,
|
readSuccessResponseJsonOrErrorCode,
|
||||||
readSuccessResponseJsonOrThrow,
|
readSuccessResponseJsonOrThrow,
|
||||||
throwUnexpectedRequestError,
|
throwUnexpectedRequestError,
|
||||||
} from "../util/http.js";
|
} from "@gnu-taler/taler-util/http";
|
||||||
import {
|
import {
|
||||||
checkDbInvariant,
|
checkDbInvariant,
|
||||||
checkLogicInvariant,
|
checkLogicInvariant,
|
||||||
|
@ -19,10 +19,10 @@ import {
|
|||||||
CoreApiResponse,
|
CoreApiResponse,
|
||||||
j2s,
|
j2s,
|
||||||
Logger,
|
Logger,
|
||||||
|
TalerError,
|
||||||
WalletNotification,
|
WalletNotification,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { connectRpc, JsonMessage } from "@gnu-taler/taler-util/twrpc";
|
import { connectRpc, JsonMessage } from "@gnu-taler/taler-util/twrpc";
|
||||||
import { TalerError } from "./errors.js";
|
|
||||||
import { OpenedPromise, openPromise } from "./index.js";
|
import { OpenedPromise, openPromise } from "./index.js";
|
||||||
import { WalletCoreApiClient } from "./wallet-api-types.js";
|
import { WalletCoreApiClient } from "./wallet-api-types.js";
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ import {
|
|||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
WithdrawalGroupRecord,
|
WithdrawalGroupRecord,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { TalerError } from "../errors.js";
|
import { TalerError } from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
import { PendingTaskType } from "../pending-types.js";
|
import { PendingTaskType } from "../pending-types.js";
|
||||||
import { GetReadWriteAccess } from "./query.js";
|
import { GetReadWriteAccess } from "./query.js";
|
||||||
|
@ -129,7 +129,7 @@ import {
|
|||||||
maybeInitDevMode,
|
maybeInitDevMode,
|
||||||
setDevMode,
|
setDevMode,
|
||||||
} from "./dev-experiments.js";
|
} from "./dev-experiments.js";
|
||||||
import { getErrorDetailFromException, TalerError } from "./errors.js";
|
import { getErrorDetailFromException, TalerError } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
ActiveLongpollInfo,
|
ActiveLongpollInfo,
|
||||||
ExchangeOperations,
|
ExchangeOperations,
|
||||||
@ -247,7 +247,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
HttpRequestLibrary,
|
HttpRequestLibrary,
|
||||||
readSuccessResponseJsonOrThrow,
|
readSuccessResponseJsonOrThrow,
|
||||||
} from "./util/http.js";
|
} from "@gnu-taler/taler-util/http";
|
||||||
import { checkDbInvariant } from "./util/invariants.js";
|
import { checkDbInvariant } from "./util/invariants.js";
|
||||||
import {
|
import {
|
||||||
AsyncCondition,
|
AsyncCondition,
|
||||||
|
@ -55,6 +55,7 @@ export const buildConfig = {
|
|||||||
format: 'esm',
|
format: 'esm',
|
||||||
platform: 'neutral',
|
platform: 'neutral',
|
||||||
mainFields: ["module", "main"],
|
mainFields: ["module", "main"],
|
||||||
|
conditions: ["qtart"],
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
define: {
|
define: {
|
||||||
'__VERSION__': `"${_package.version}"`,
|
'__VERSION__': `"${_package.version}"`,
|
||||||
|
@ -18,27 +18,25 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
|
createNativeWalletHost,
|
||||||
DefaultNodeWalletArgs,
|
DefaultNodeWalletArgs,
|
||||||
getDefaultNodeWallet,
|
|
||||||
getErrorDetailFromException,
|
|
||||||
handleWorkerError,
|
handleWorkerError,
|
||||||
handleWorkerMessage,
|
handleWorkerMessage,
|
||||||
Headers,
|
|
||||||
HttpRequestLibrary,
|
|
||||||
HttpRequestOptions,
|
|
||||||
HttpResponse,
|
|
||||||
NodeHttpLib,
|
|
||||||
OpenedPromise,
|
OpenedPromise,
|
||||||
openPromise,
|
openPromise,
|
||||||
Wallet,
|
Wallet,
|
||||||
WALLET_EXCHANGE_PROTOCOL_VERSION,
|
|
||||||
WALLET_MERCHANT_PROTOCOL_VERSION,
|
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CoreApiMessageEnvelope,
|
CoreApiMessageEnvelope,
|
||||||
CoreApiResponse,
|
CoreApiResponse,
|
||||||
CoreApiResponseSuccess,
|
CoreApiResponseSuccess,
|
||||||
|
createPlatformHttpLib,
|
||||||
|
getErrorDetailFromException,
|
||||||
|
Headers,
|
||||||
|
HttpRequestLibrary,
|
||||||
|
HttpRequestOptions,
|
||||||
|
HttpResponse,
|
||||||
Logger,
|
Logger,
|
||||||
WalletNotification,
|
WalletNotification,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
@ -51,7 +49,7 @@ const logger = new Logger("taler-wallet-embedded/index.ts");
|
|||||||
export class NativeHttpLib implements HttpRequestLibrary {
|
export class NativeHttpLib implements HttpRequestLibrary {
|
||||||
useNfcTunnel = false;
|
useNfcTunnel = false;
|
||||||
|
|
||||||
private nodeHttpLib: HttpRequestLibrary = new NodeHttpLib();
|
private httpLib: HttpRequestLibrary = createPlatformHttpLib();
|
||||||
|
|
||||||
private requestId = 1;
|
private requestId = 1;
|
||||||
|
|
||||||
@ -62,7 +60,7 @@ export class NativeHttpLib implements HttpRequestLibrary {
|
|||||||
constructor(private sendMessage: (m: string) => void) {}
|
constructor(private sendMessage: (m: string) => void) {}
|
||||||
|
|
||||||
fetch(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
|
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> {
|
get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
|
||||||
@ -83,7 +81,7 @@ export class NativeHttpLib implements HttpRequestLibrary {
|
|||||||
);
|
);
|
||||||
return p.promise;
|
return p.promise;
|
||||||
} else {
|
} 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;
|
return p.promise;
|
||||||
} else {
|
} else {
|
||||||
return this.nodeHttpLib.postJson(url, body, opt);
|
return this.httpLib.postJson(url, body, opt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +156,7 @@ class NativeWalletMessageHandler {
|
|||||||
walletArgs: DefaultNodeWalletArgs | undefined;
|
walletArgs: DefaultNodeWalletArgs | undefined;
|
||||||
maybeWallet: Wallet | undefined;
|
maybeWallet: Wallet | undefined;
|
||||||
wp = openPromise<Wallet>();
|
wp = openPromise<Wallet>();
|
||||||
httpLib = new NodeHttpLib();
|
httpLib = createPlatformHttpLib();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a request from the native wallet.
|
* Handle a request from the native wallet.
|
||||||
@ -181,7 +179,7 @@ class NativeWalletMessageHandler {
|
|||||||
|
|
||||||
const reinit = async () => {
|
const reinit = async () => {
|
||||||
logger.info("in reinit");
|
logger.info("in reinit");
|
||||||
const w = await getDefaultNodeWallet(this.walletArgs);
|
const w = await createNativeWalletHost(this.walletArgs);
|
||||||
this.maybeWallet = w;
|
this.maybeWallet = w;
|
||||||
const resp = await w.handleCoreApiRequest(
|
const resp = await w.handleCoreApiRequest(
|
||||||
"initWallet",
|
"initWallet",
|
||||||
|
@ -17,44 +17,26 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import {
|
|
||||||
AccessStats,
|
|
||||||
DefaultNodeWalletArgs,
|
|
||||||
getErrorDetailFromException,
|
|
||||||
Headers,
|
|
||||||
HttpRequestLibrary,
|
|
||||||
HttpRequestOptions,
|
|
||||||
HttpResponse,
|
|
||||||
openPromise,
|
|
||||||
openTalerDatabase,
|
|
||||||
SetTimeoutTimerAPI,
|
|
||||||
SynchronousCryptoWorkerFactoryPlain,
|
|
||||||
Wallet,
|
|
||||||
WalletApiOperation,
|
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CoreApiMessageEnvelope,
|
CoreApiMessageEnvelope,
|
||||||
CoreApiResponse,
|
CoreApiResponse,
|
||||||
CoreApiResponseSuccess,
|
CoreApiResponseSuccess,
|
||||||
|
createPlatformHttpLib,
|
||||||
|
getErrorDetailFromException,
|
||||||
InitRequest,
|
InitRequest,
|
||||||
j2s,
|
|
||||||
Logger,
|
Logger,
|
||||||
setGlobalLogLevelFromString,
|
setGlobalLogLevelFromString,
|
||||||
setPRNG,
|
setPRNG,
|
||||||
WalletNotification,
|
WalletNotification,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { BridgeIDBFactory } from "@gnu-taler/idb-bridge";
|
import { qjsOs } from "@gnu-taler/taler-util/qtart";
|
||||||
import { MemoryBackend } from "@gnu-taler/idb-bridge";
|
import {
|
||||||
import { shimIndexedDB } from "@gnu-taler/idb-bridge";
|
createNativeWalletHost2,
|
||||||
import { IDBFactory } from "@gnu-taler/idb-bridge";
|
DefaultNodeWalletArgs,
|
||||||
|
openPromise,
|
||||||
import * as _qjsOsImp from "os";
|
Wallet,
|
||||||
// @ts-ignore
|
WalletApiOperation,
|
||||||
import * as _qjsStdImp from "std";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
|
|
||||||
const textDecoder = new TextDecoder();
|
|
||||||
const textEncoder = new TextEncoder();
|
|
||||||
|
|
||||||
setGlobalLogLevelFromString("trace");
|
setGlobalLogLevelFromString("trace");
|
||||||
|
|
||||||
@ -66,210 +48,19 @@ setPRNG(function (x: Uint8Array, n: number) {
|
|||||||
for (let i = 0; i < v.length; i++) v[i] = 0;
|
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");
|
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 {
|
function sendNativeMessage(ev: CoreApiMessageEnvelope): void {
|
||||||
const m = JSON.stringify(ev);
|
const m = JSON.stringify(ev);
|
||||||
qjsOs.postMessageToHost(m);
|
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 {
|
class NativeWalletMessageHandler {
|
||||||
walletArgs: DefaultNodeWalletArgs | undefined;
|
walletArgs: DefaultNodeWalletArgs | undefined;
|
||||||
initRequest: InitRequest = {};
|
initRequest: InitRequest = {};
|
||||||
maybeWallet: Wallet | undefined;
|
maybeWallet: Wallet | undefined;
|
||||||
wp = openPromise<Wallet>();
|
wp = openPromise<Wallet>();
|
||||||
httpLib = new NativeHttpLib();
|
httpLib = createPlatformHttpLib();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a request from the native wallet.
|
* Handle a request from the native wallet.
|
||||||
@ -292,7 +83,7 @@ class NativeWalletMessageHandler {
|
|||||||
|
|
||||||
const reinit = async () => {
|
const reinit = async () => {
|
||||||
logger.info("in reinit");
|
logger.info("in reinit");
|
||||||
const wR = await getWallet(this.walletArgs);
|
const wR = await createNativeWalletHost2(this.walletArgs);
|
||||||
const w = wR.wallet;
|
const w = wR.wallet;
|
||||||
this.maybeWallet = w;
|
this.maybeWallet = w;
|
||||||
const resp = await w.handleCoreApiRequest("initWallet", "native-init", {
|
const resp = await w.handleCoreApiRequest("initWallet", "native-init", {
|
||||||
@ -422,7 +213,7 @@ globalThis.installNativeWalletListener = installNativeWalletListener;
|
|||||||
globalThis.makeWallet = getWallet;
|
globalThis.makeWallet = getWallet;
|
||||||
|
|
||||||
export async function testWithGv() {
|
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.InitWallet, {});
|
||||||
await w.wallet.client.call(WalletApiOperation.RunIntegrationTest, {
|
await w.wallet.client.call(WalletApiOperation.RunIntegrationTest, {
|
||||||
amountToSpend: "KUDOS:1",
|
amountToSpend: "KUDOS:1",
|
||||||
@ -438,7 +229,7 @@ export async function testWithGv() {
|
|||||||
|
|
||||||
export async function testWithLocal() {
|
export async function testWithLocal() {
|
||||||
console.log("running local test");
|
console.log("running local test");
|
||||||
const w = await getWallet({
|
const w = await createNativeWalletHost2({
|
||||||
persistentStoragePath: "walletdb.json",
|
persistentStoragePath: "walletdb.json",
|
||||||
});
|
});
|
||||||
console.log("created wallet");
|
console.log("created wallet");
|
||||||
|
@ -18,19 +18,17 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
|
Logger,
|
||||||
|
RequestThrottler,
|
||||||
|
TalerErrorCode,
|
||||||
HttpRequestLibrary,
|
HttpRequestLibrary,
|
||||||
HttpRequestOptions,
|
HttpRequestOptions,
|
||||||
HttpResponse,
|
HttpResponse,
|
||||||
Headers,
|
Headers,
|
||||||
TalerError,
|
TalerError,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
|
||||||
import {
|
|
||||||
Logger,
|
|
||||||
RequestThrottler,
|
|
||||||
stringToBytes,
|
|
||||||
TalerErrorCode,
|
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
|
|
||||||
|
|
||||||
const logger = new Logger("browserHttpLib");
|
const logger = new Logger("browserHttpLib");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,11 +22,12 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { j2s, Logger } from "@gnu-taler/taler-util";
|
|
||||||
import {
|
import {
|
||||||
|
j2s,
|
||||||
|
Logger,
|
||||||
getErrorDetailFromException,
|
getErrorDetailFromException,
|
||||||
nativeCrypto,
|
} from "@gnu-taler/taler-util";
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
import { nativeCrypto } from "@gnu-taler/taler-wallet-core";
|
||||||
|
|
||||||
const logger = new Logger("browserWorkerEntry.ts");
|
const logger = new Logger("browserWorkerEntry.ts");
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
/* eslint-disable react-hooks/rules-of-hooks */
|
||||||
import { Amounts, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
|
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 { isFuture, parse } from "date-fns";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import { alertFromError, useAlertContext } from "../../context/alert.js";
|
import { alertFromError, useAlertContext } from "../../context/alert.js";
|
||||||
|
@ -20,16 +20,14 @@ import {
|
|||||||
NotificationType,
|
NotificationType,
|
||||||
PreparePayResult,
|
PreparePayResult,
|
||||||
PreparePayResultType,
|
PreparePayResultType,
|
||||||
TalerErrorDetail,
|
|
||||||
TalerProtocolTimestamp,
|
TalerProtocolTimestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect } from "preact/hooks";
|
||||||
import { alertFromError, useAlertContext } from "../../context/alert.js";
|
import { alertFromError, useAlertContext } from "../../context/alert.js";
|
||||||
import { useBackendContext } from "../../context/backend.js";
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useTranslationContext } from "../../context/translation.js";
|
import { useTranslationContext } from "../../context/translation.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { withSafe } from "../../mui/handlers.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState({
|
export function useComponentState({
|
||||||
|
@ -15,11 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Amounts,
|
Amounts, TalerProtocolTimestamp
|
||||||
TalerErrorDetail,
|
|
||||||
TalerProtocolTimestamp,
|
|
||||||
} from "@gnu-taler/taler-util";
|
} 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 { isFuture, parse } from "date-fns";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import { alertFromError, useAlertContext } from "../../context/alert.js";
|
import { alertFromError, useAlertContext } from "../../context/alert.js";
|
||||||
|
@ -16,12 +16,9 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
AbsoluteTime,
|
AbsoluteTime,
|
||||||
Amounts,
|
Amounts, TalerProtocolTimestamp
|
||||||
TalerErrorDetail,
|
|
||||||
TalerProtocolTimestamp,
|
|
||||||
} from "@gnu-taler/taler-util";
|
} 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 { alertFromError, useAlertContext } from "../../context/alert.js";
|
||||||
import { useBackendContext } from "../../context/backend.js";
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useTranslationContext } from "../../context/translation.js";
|
import { useTranslationContext } from "../../context/translation.js";
|
||||||
|
@ -20,8 +20,9 @@ import {
|
|||||||
Amounts,
|
Amounts,
|
||||||
ExchangeListItem,
|
ExchangeListItem,
|
||||||
ExchangeTosStatus,
|
ExchangeTosStatus,
|
||||||
|
TalerError,
|
||||||
} from "@gnu-taler/taler-util";
|
} 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 { useState } from "preact/hooks";
|
||||||
import { alertFromError, useAlertContext } from "../../context/alert.js";
|
import { alertFromError, useAlertContext } from "../../context/alert.js";
|
||||||
import { useBackendContext } from "../../context/backend.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
|
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/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
import { TalerErrorDetail } from "@gnu-taler/taler-util";
|
import { TalerErrorDetail, TalerError } from "@gnu-taler/taler-util";
|
||||||
import { TalerError } from "@gnu-taler/taler-wallet-core";
|
|
||||||
import { useEffect, useMemo, useState } from "preact/hooks";
|
import { useEffect, useMemo, useState } from "preact/hooks";
|
||||||
import { BackgroundError } from "../wxApi.js";
|
import { BackgroundError } from "../wxApi.js";
|
||||||
|
|
||||||
|
@ -19,8 +19,9 @@ import {
|
|||||||
Logger,
|
Logger,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
TalerUriType,
|
TalerUriType,
|
||||||
|
TalerError,
|
||||||
} from "@gnu-taler/taler-util";
|
} 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 { BackgroundOperations } from "../wxApi.js";
|
||||||
import {
|
import {
|
||||||
BackgroundPlatformAPI,
|
BackgroundPlatformAPI,
|
||||||
|
@ -22,12 +22,12 @@
|
|||||||
import {
|
import {
|
||||||
CryptoWorker,
|
CryptoWorker,
|
||||||
CryptoWorkerFactory,
|
CryptoWorkerFactory,
|
||||||
SynchronousCryptoWorker,
|
SynchronousCryptoWorkerPlain,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
|
|
||||||
export class SynchronousCryptoWorkerFactory implements CryptoWorkerFactory {
|
export class SynchronousCryptoWorkerFactory implements CryptoWorkerFactory {
|
||||||
startWorker(): CryptoWorker {
|
startWorker(): CryptoWorker {
|
||||||
return new SynchronousCryptoWorker();
|
return new SynchronousCryptoWorkerPlain();
|
||||||
}
|
}
|
||||||
|
|
||||||
getConcurrency(): number {
|
getConcurrency(): number {
|
||||||
|
@ -21,14 +21,12 @@ import {
|
|||||||
Logger,
|
Logger,
|
||||||
RequestThrottler,
|
RequestThrottler,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
} from "@gnu-taler/taler-util";
|
|
||||||
import {
|
|
||||||
Headers,
|
Headers,
|
||||||
HttpRequestLibrary,
|
HttpRequestLibrary,
|
||||||
HttpRequestOptions,
|
HttpRequestOptions,
|
||||||
HttpResponse,
|
HttpResponse,
|
||||||
TalerError,
|
TalerError,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-util";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of the [[HttpRequestLibrary]] using the
|
* An implementation of the [[HttpRequestLibrary]] using the
|
||||||
|
@ -29,20 +29,18 @@ import {
|
|||||||
NotificationType,
|
NotificationType,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
TalerErrorDetail,
|
TalerErrorDetail,
|
||||||
WalletDiagnostics,
|
WalletDiagnostics
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
TalerError,
|
|
||||||
WalletCoreApiClient,
|
WalletCoreApiClient,
|
||||||
WalletCoreOpKeys,
|
WalletCoreOpKeys,
|
||||||
WalletCoreRequestType,
|
WalletCoreRequestType,
|
||||||
WalletCoreResponseType,
|
WalletCoreResponseType
|
||||||
WalletOperations,
|
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
import {
|
import {
|
||||||
MessageFromBackend,
|
MessageFromBackend,
|
||||||
MessageFromFrontendBackground,
|
MessageFromFrontendBackground,
|
||||||
MessageFromFrontendWallet,
|
MessageFromFrontendWallet
|
||||||
} from "./platform/api.js";
|
} from "./platform/api.js";
|
||||||
import { platform } from "./platform/foreground.js";
|
import { platform } from "./platform/foreground.js";
|
||||||
|
|
||||||
|
@ -32,14 +32,14 @@ import {
|
|||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
TalerUriType,
|
TalerUriType,
|
||||||
WalletDiagnostics,
|
WalletDiagnostics,
|
||||||
|
makeErrorDetail,
|
||||||
|
getErrorDetailFromException,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
DbAccess,
|
DbAccess,
|
||||||
deleteTalerDatabase,
|
deleteTalerDatabase,
|
||||||
exportDb,
|
exportDb,
|
||||||
getErrorDetailFromException,
|
|
||||||
importDb,
|
importDb,
|
||||||
makeErrorDetail,
|
|
||||||
OpenedPromise,
|
OpenedPromise,
|
||||||
openPromise,
|
openPromise,
|
||||||
openTalerDatabase,
|
openTalerDatabase,
|
||||||
|
Loading…
Reference in New Issue
Block a user