/* This file is part of GNU Taler (C) 2022 Taler Systems SA 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. 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 TALER; see the file COPYING. If not, see */ /** * Implementation of dev experiments, i.e. scenarios * triggered by taler://dev-experiment URIs. * * @author Florian Dold */ /** * Imports. */ import { DenomLossEventType, Logger, RefreshReason, TalerPreciseTimestamp, encodeCrock, getRandomBytes, parseDevExperimentUri, } from "@gnu-taler/taler-util"; import { HttpRequestLibrary, HttpRequestOptions, HttpResponse, } from "@gnu-taler/taler-util/http"; import { PendingTaskType, constructTaskIdentifier } from "./common.js"; import { DenomLossEventRecord, DenomLossStatus, RefreshGroupRecord, RefreshOperationStatus, timestampPreciseToDb, } from "./db.js"; import { WalletExecutionContext } from "./wallet.js"; const logger = new Logger("dev-experiments.ts"); /** * Apply a dev experiment to the wallet database / state. */ export async function applyDevExperiment( wex: WalletExecutionContext, uri: string, ): Promise { logger.info(`applying dev experiment ${uri}`); const parsedUri = parseDevExperimentUri(uri); if (!parsedUri) { logger.info("unable to parse dev experiment URI"); return; } if (!wex.ws.config.testing.devModeActive) { throw Error("can't handle devmode URI unless devmode is active"); } switch (parsedUri.devExperimentId) { case "start-block-refresh": { wex.ws.devExperimentState.blockRefreshes = true; return; } case "stop-block-refresh": { wex.ws.devExperimentState.blockRefreshes = false; return; } case "insert-pending-refresh": { const refreshGroupId = encodeCrock(getRandomBytes(32)); await wex.db.runReadWriteTx( { storeNames: ["refreshGroups"] }, async (tx) => { const newRg: RefreshGroupRecord = { currency: "TESTKUDOS", expectedOutputPerCoin: [], inputPerCoin: [], oldCoinPubs: [], operationStatus: RefreshOperationStatus.Pending, reason: RefreshReason.Manual, refreshGroupId, statusPerCoin: [], timestampCreated: timestampPreciseToDb(TalerPreciseTimestamp.now()), timestampFinished: undefined, originatingTransactionId: undefined, infoPerExchange: {}, }; await tx.refreshGroups.put(newRg); }, ); wex.taskScheduler.startShepherdTask( constructTaskIdentifier({ tag: PendingTaskType.Refresh, refreshGroupId, }), ); return; } case "insert-denom-loss": { await wex.db.runReadWriteTx( { storeNames: ["denomLossEvents"] }, async (tx) => { const eventId = encodeCrock(getRandomBytes(32)); const newRg: DenomLossEventRecord = { amount: "TESTKUDOS:42", currency: "TESTKUDOS", exchangeBaseUrl: "https://exchange.test.taler.net/", denomLossEventId: eventId, denomPubHashes: [ encodeCrock(getRandomBytes(64)), encodeCrock(getRandomBytes(64)), ], eventType: DenomLossEventType.DenomExpired, status: DenomLossStatus.Done, timestampCreated: timestampPreciseToDb(TalerPreciseTimestamp.now()), }; await tx.denomLossEvents.put(newRg); }, ); return; } } throw Error(`dev-experiment id not understood ${parsedUri.devExperimentId}`); } export class DevExperimentHttpLib implements HttpRequestLibrary { _isDevExperimentLib = true; underlyingLib: HttpRequestLibrary; constructor(lib: HttpRequestLibrary) { this.underlyingLib = lib; } fetch( url: string, opt?: HttpRequestOptions | undefined, ): Promise { logger.trace(`devexperiment httplib ${url}`); return this.underlyingLib.fetch(url, opt); } }