wallet-core: fix default auditor/exchange loading logic

This commit is contained in:
Florian Dold 2022-10-05 18:31:56 +02:00
parent 99ace8b7d2
commit 957f9a5efb
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
8 changed files with 74 additions and 68 deletions

View File

@ -255,9 +255,9 @@ async function withWallet<T>(
console.error("Error details:", JSON.stringify(ed, undefined, 2));
process.exit(1);
} finally {
logger.info("operation with wallet finished, stopping");
logger.trace("operation with wallet finished, stopping");
wallet.stop();
logger.info("stopped wallet");
logger.trace("stopped wallet");
}
}
@ -495,6 +495,7 @@ walletCli
talerWithdrawUri: uri,
},
);
console.log("accept withdrawal response", res);
}
break;
default:
@ -731,7 +732,7 @@ const advancedCli = walletCli.subcommand("advancedArgs", "advanced", {
advancedCli
.subcommand("init", "init", {
help: "Initialize the wallet (with DB) and exit."
help: "Initialize the wallet (with DB) and exit.",
})
.action(async (args) => {
await withWallet(args, async () => {});

View File

@ -37,7 +37,6 @@ export async function processRequestWithImpl(
reqMsg: CryptoWorkerRequestMessage,
impl: TalerCryptoInterfaceR,
): Promise<CryptoWorkerResponseMessage> {
logger.info(`processing crypto request ${j2s(reqMsg)}`);
if (typeof reqMsg !== "object") {
logger.error("request must be an object");
return {

View File

@ -510,7 +510,7 @@ export interface ExchangeRecord {
permanent: boolean;
/**
* Last time when the exchange was updated.
* Last time when the exchange was updated (both /keys and /wire).
*/
lastUpdate: TalerProtocolTimestamp | undefined;
@ -521,6 +521,10 @@ export interface ExchangeRecord {
*/
nextUpdate: TalerProtocolTimestamp;
lastKeysEtag: string | undefined;
lastWireEtag: string | undefined;
/**
* Next time that we should check if coins need to be refreshed.
*

View File

@ -362,6 +362,8 @@ export async function importBackup(
lastUpdate: undefined,
nextUpdate: TalerProtocolTimestamp.now(),
nextRefreshCheck: TalerProtocolTimestamp.now(),
lastKeysEtag: undefined,
lastWireEtag: undefined,
});
}

View File

@ -63,7 +63,11 @@ import {
readSuccessResponseJsonOrThrow,
readSuccessResponseTextOrThrow,
} from "../util/http.js";
import { DbAccess, GetReadOnlyAccess } from "../util/query.js";
import {
DbAccess,
GetReadOnlyAccess,
GetReadWriteAccess,
} from "../util/query.js";
import {
OperationAttemptResult,
OperationAttemptResultType,
@ -316,17 +320,18 @@ async function downloadExchangeWireInfo(
return wireInfo;
}
async function provideExchangeRecord(
export async function provideExchangeRecordInTx(
ws: InternalWalletState,
tx: GetReadWriteAccess<{
exchanges: typeof WalletStoresV1.exchanges;
exchangeDetails: typeof WalletStoresV1.exchangeDetails;
}>,
baseUrl: string,
now: AbsoluteTime,
): Promise<{
exchange: ExchangeRecord;
exchangeDetails: ExchangeDetailsRecord | undefined;
}> {
return await ws.db
.mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadWrite(async (tx) => {
let exchange = await tx.exchanges.get(baseUrl);
if (!exchange) {
const r: ExchangeRecord = {
@ -336,13 +341,14 @@ async function provideExchangeRecord(
lastUpdate: undefined,
nextUpdate: AbsoluteTime.toTimestamp(now),
nextRefreshCheck: AbsoluteTime.toTimestamp(now),
lastKeysEtag: undefined,
lastWireEtag: undefined,
};
await tx.exchanges.put(r);
exchange = r;
}
const exchangeDetails = await getExchangeDetails(tx, baseUrl);
return { exchange, exchangeDetails };
});
}
interface ExchangeKeysDownloadResult {
@ -499,15 +505,16 @@ export async function updateExchangeFromUrlHandler(
> {
const forceNow = options.forceNow ?? false;
logger.info(`updating exchange info for ${baseUrl}, forced: ${forceNow}`);
console.trace("here");
const now = AbsoluteTime.now();
baseUrl = canonicalizeBaseUrl(baseUrl);
const { exchange, exchangeDetails } = await provideExchangeRecord(
ws,
baseUrl,
now,
);
const { exchange, exchangeDetails } = await ws.db
.mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadWrite(async (tx) => {
return provideExchangeRecordInTx(ws, tx, baseUrl, now);
});
if (
!forceNow &&

View File

@ -50,6 +50,7 @@ async function gatherExchangePending(
now: AbsoluteTime,
resp: PendingOperationsResponse,
): Promise<void> {
// FIXME: We should do a range query here based on the update time.
await tx.exchanges.iter().forEachAsync(async (exch) => {
const opTag = RetryTags.forExchangeUpdate(exch);
let opr = await tx.operationRetries.get(opTag);

View File

@ -1071,7 +1071,7 @@ export async function processWithdrawalGroup(
case WithdrawalGroupStatus.QueryingStatus: {
const doQueryAsync = async () => {
if (ws.stopped) {
logger.info("not long-polling reserve, wallet already stopped");
logger.trace("not long-polling reserve, wallet already stopped");
await storeOperationPending(ws, retryTag);
return;
}
@ -1080,7 +1080,7 @@ export async function processWithdrawalGroup(
try {
ws.activeLongpoll[retryTag] = {
cancel: () => {
logger.info("cancel of reserve longpoll requested");
logger.trace("cancel of reserve longpoll requested");
cts.cancel();
},
};
@ -1094,16 +1094,13 @@ export async function processWithdrawalGroup(
return;
}
delete ws.activeLongpoll[retryTag];
logger.info(
`active longpoll keys (2) ${Object.keys(ws.activeLongpoll)}`,
);
if (!res.ready) {
await storeOperationPending(ws, retryTag);
}
ws.latch.trigger();
};
doQueryAsync();
logger.info(
logger.trace(
"returning early from withdrawal for long-polling in background",
);
return {
@ -1918,12 +1915,12 @@ export async function acceptWithdrawalFromUri(
);
}
// Start withdrawal in the background.
await processWithdrawalGroup(ws, withdrawalGroupId, { forceNow: true }).catch(
(err) => {
// Start withdrawal in the background
processWithdrawalGroup(ws, withdrawalGroupId, {
forceNow: true,
}).catch((err) => {
logger.error("Processing withdrawal (after creation) failed:", err);
},
);
});
return {
reservePub: withdrawalGroup.reservePub,

View File

@ -155,6 +155,7 @@ import {
getExchangeDetails,
getExchangeRequestTimeout,
getExchangeTrust,
provideExchangeRecordInTx,
updateExchangeFromUrl,
updateExchangeFromUrlHandler,
updateExchangeTermsOfService,
@ -583,32 +584,26 @@ async function runTaskLoop(
*/
async function fillDefaults(ws: InternalWalletState): Promise<void> {
await ws.db
.mktx((x) => [x.config, x.auditorTrust])
.mktx((x) => [x.config, x.auditorTrust, x.exchanges, x.exchangeDetails])
.runReadWrite(async (tx) => {
let applied = false;
await tx.config.iter().forEach((x) => {
if (x.key == "currencyDefaultsApplied" && x.value == true) {
applied = true;
const appliedRec = await tx.config.get("currencyDefaultsApplied");
let alreadyApplied = appliedRec ? !!appliedRec.value : false;
if (alreadyApplied) {
logger.info("defaults already applied");
return;
}
});
if (!applied) {
for (const c of builtinAuditors) {
await tx.auditorTrust.put(c);
}
for (const baseUrl of builtinExchanges) {
const now = AbsoluteTime.now();
provideExchangeRecordInTx(ws, tx, baseUrl, now);
}
// FIXME: make sure exchanges are added transactionally to
// DB in first-time default application
await tx.config.put({
key: "currencyDefaultsApplied",
value: true,
});
});
for (const url of builtinExchanges) {
try {
await updateExchangeFromUrl(ws, url, { forceNow: true });
} catch (e) {
logger.warn(
`could not update builtin exchange ${url} during wallet initialization`,
);
}
}
}
async function getExchangeTos(
@ -1719,12 +1714,12 @@ class InternalWalletStateImpl implements InternalWalletState {
* Stop ongoing processing.
*/
stop(): void {
logger.info("stopping (at internal wallet state)");
logger.trace("stopping (at internal wallet state)");
this.stopped = true;
this.timerGroup.stopCurrentAndFutureTimers();
this.cryptoDispatcher.stop();
for (const key of Object.keys(this.activeLongpoll)) {
logger.info(`cancelling active longpoll ${key}`);
logger.trace(`cancelling active longpoll ${key}`);
this.activeLongpoll[key].cancel();
}
}