This commit is contained in:
Florian Dold 2021-08-19 19:26:37 +02:00
parent f5a8ae33e3
commit a576fdfbf8
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
2 changed files with 51 additions and 49 deletions

View File

@ -2,7 +2,7 @@
"compileOnSave": true, "compileOnSave": true,
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"target": "ES6", "target": "ES2018",
"module": "ESNext", "module": "ESNext",
"moduleResolution": "node", "moduleResolution": "node",
"sourceMap": true, "sourceMap": true,

View File

@ -242,11 +242,7 @@ function deriveBlobSecret(bc: WalletBackupConfState): Uint8Array {
} }
interface BackupForProviderArgs { interface BackupForProviderArgs {
backupConfig: WalletBackupConfState; backupProviderBaseUrl: string;
provider: BackupProviderRecord;
currentBackupHash: ArrayBuffer;
encBackup: ArrayBuffer;
backupJson: WalletBackupContentV1;
/** /**
* Should we attempt one more upload after trying * Should we attempt one more upload after trying
@ -267,13 +263,22 @@ async function runBackupCycleForProvider(
ws: InternalWalletState, ws: InternalWalletState,
args: BackupForProviderArgs, args: BackupForProviderArgs,
): Promise<void> { ): Promise<void> {
const { const provider = await ws.db
backupConfig, .mktx((x) => ({ backupProviders: x.backupProviders }))
provider, .runReadOnly(async (tx) => {
currentBackupHash, return tx.backupProviders.get(args.backupProviderBaseUrl);
encBackup, });
backupJson,
} = args; if (!provider) {
logger.warn("provider disappeared");
return;
}
const backupJson = await exportBackup(ws);
const backupConfig = await provideBackupState(ws);
const encBackup = await encryptBackup(backupConfig, backupJson);
const currentBackupHash = hash(encBackup);
const accountKeyPair = deriveAccountKeyPair(backupConfig, provider.baseUrl); const accountKeyPair = deriveAccountKeyPair(backupConfig, provider.baseUrl);
const newHash = encodeCrock(currentBackupHash); const newHash = encodeCrock(currentBackupHash);
@ -301,11 +306,11 @@ async function runBackupCycleForProvider(
headers: { headers: {
"content-type": "application/octet-stream", "content-type": "application/octet-stream",
"sync-signature": syncSig, "sync-signature": syncSig,
"if-none-match": encodeCrock(currentBackupHash), "if-none-match": newHash,
...(provider.lastBackupHash ...(provider.lastBackupHash
? { ? {
"if-match": provider.lastBackupHash, "if-match": provider.lastBackupHash,
} }
: {}), : {}),
}, },
}); });
@ -366,7 +371,11 @@ async function runBackupCycleForProvider(
provRec.currentPaymentProposalId = proposalId; provRec.currentPaymentProposalId = proposalId;
// FIXME: allocate error code for this! // FIXME: allocate error code for this!
await tx.backupProviders.put(provRec); await tx.backupProviders.put(provRec);
await incrementBackupRetryInTx(tx, args.provider.baseUrl, undefined); await incrementBackupRetryInTx(
tx,
args.backupProviderBaseUrl,
undefined,
);
}); });
if (doPay) { if (doPay) {
@ -418,6 +427,7 @@ async function runBackupCycleForProvider(
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
const prov = await tx.backupProvider.get(provider.baseUrl); const prov = await tx.backupProvider.get(provider.baseUrl);
if (!prov) { if (!prov) {
logger.warn("backup provider not found anymore");
return; return;
} }
prov.lastBackupHash = encodeCrock(hash(backupEnc)); prov.lastBackupHash = encodeCrock(hash(backupEnc));
@ -446,7 +456,7 @@ async function runBackupCycleForProvider(
await ws.db await ws.db
.mktx((x) => ({ backupProviders: x.backupProviders })) .mktx((x) => ({ backupProviders: x.backupProviders }))
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
incrementBackupRetryInTx(tx, args.provider.baseUrl, err); incrementBackupRetryInTx(tx, args.backupProviderBaseUrl, err);
}); });
} }
@ -504,17 +514,8 @@ export async function processBackupForProvider(
incrementBackupRetry(ws, backupProviderBaseUrl, err); incrementBackupRetry(ws, backupProviderBaseUrl, err);
const run = async () => { const run = async () => {
const backupJson = await exportBackup(ws);
const backupConfig = await provideBackupState(ws);
const encBackup = await encryptBackup(backupConfig, backupJson);
const currentBackupHash = hash(encBackup);
await runBackupCycleForProvider(ws, { await runBackupCycleForProvider(ws, {
provider, backupProviderBaseUrl: provider.baseUrl,
backupJson,
backupConfig,
encBackup,
currentBackupHash,
retryAfterPayment: true, retryAfterPayment: true,
}); });
}; };
@ -531,16 +532,20 @@ export const codecForRemoveBackupProvider = (): Codec<RemoveBackupProviderReques
.property("provider", codecForString()) .property("provider", codecForString())
.build("RemoveBackupProviderRequest"); .build("RemoveBackupProviderRequest");
export async function removeBackupProvider(ws: InternalWalletState, req: RemoveBackupProviderRequest): Promise<void> { export async function removeBackupProvider(
await ws.db.mktx(({ backupProviders }) => ({ backupProviders })) ws: InternalWalletState,
req: RemoveBackupProviderRequest,
): Promise<void> {
await ws.db
.mktx(({ backupProviders }) => ({ backupProviders }))
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
await tx.backupProviders.delete(req.provider) await tx.backupProviders.delete(req.provider);
}) });
} }
export interface RunBackupCycleRequest { export interface RunBackupCycleRequest {
/** /**
* List of providers to backup or empty for all known providers. * List of providers to backup or empty for all known providers.
*/ */
providers?: Array<string>; providers?: Array<string>;
} }
@ -557,28 +562,25 @@ export const codecForRunBackupCycle = (): Codec<RunBackupCycleRequest> =>
* 2. Download, verify and import backups from connected sync accounts. * 2. Download, verify and import backups from connected sync accounts.
* 3. Upload the updated backup blob. * 3. Upload the updated backup blob.
*/ */
export async function runBackupCycle(ws: InternalWalletState, req: RunBackupCycleRequest): Promise<void> { export async function runBackupCycle(
ws: InternalWalletState,
req: RunBackupCycleRequest,
): Promise<void> {
const providers = await ws.db const providers = await ws.db
.mktx((x) => ({ backupProviders: x.backupProviders })) .mktx((x) => ({ backupProviders: x.backupProviders }))
.runReadOnly(async (tx) => { .runReadOnly(async (tx) => {
if (req.providers) { if (req.providers) {
const rs = await Promise.all(req.providers.map(id => tx.backupProviders.get(id))) const rs = await Promise.all(
return rs.filter(notEmpty) req.providers.map((id) => tx.backupProviders.get(id)),
);
return rs.filter(notEmpty);
} }
return await tx.backupProviders.iter(req.providers).toArray(); return await tx.backupProviders.iter().toArray();
}); });
const backupJson = await exportBackup(ws);
const backupConfig = await provideBackupState(ws);
const encBackup = await encryptBackup(backupConfig, backupJson);
const currentBackupHash = hash(encBackup);
for (const provider of providers) { for (const provider of providers) {
await runBackupCycleForProvider(ws, { await runBackupCycleForProvider(ws, {
provider, backupProviderBaseUrl: provider.baseUrl,
backupJson,
backupConfig,
encBackup,
currentBackupHash,
retryAfterPayment: true, retryAfterPayment: true,
}); });
} }
@ -645,7 +647,7 @@ export async function addBackupProvider(
return; return;
} }
}); });
const termsUrl = new URL("terms", canonUrl); const termsUrl = new URL("config", canonUrl);
const resp = await ws.http.get(termsUrl.href); const resp = await ws.http.get(termsUrl.href);
const terms = await readSuccessResponseJsonOrThrow( const terms = await readSuccessResponseJsonOrThrow(
resp, resp,
@ -680,7 +682,7 @@ export async function addBackupProvider(
}); });
} }
export async function restoreFromRecoverySecret(): Promise<void> { } export async function restoreFromRecoverySecret(): Promise<void> {}
/** /**
* Information about one provider. * Information about one provider.
@ -907,7 +909,7 @@ async function backupRecoveryTheirs(
if (!existingProv) { if (!existingProv) {
await tx.backupProviders.put({ await tx.backupProviders.put({
baseUrl: prov.url, baseUrl: prov.url,
name: 'not-defined', name: "not-defined",
paymentProposalIds: [], paymentProposalIds: [],
state: { state: {
tag: BackupProviderStateTag.Ready, tag: BackupProviderStateTag.Ready,