fix another withdrawal state machine bug, add 'show-reserve' to advanced CLI

This commit is contained in:
Florian Dold 2020-03-16 17:46:57 +05:30
parent 9e2be07cfc
commit cb2fbc7610
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
3 changed files with 48 additions and 27 deletions

View File

@ -374,7 +374,7 @@ advancedCli
}); });
}); });
advancedCli advancedCli
.subcommand("updateReserve", "update-reserve", { .subcommand("updateReserve", "update-reserve", {
help: "Update reserve status.", help: "Update reserve status.",
}) })
@ -386,6 +386,18 @@ advancedCli
}); });
}); });
advancedCli
.subcommand("updateReserve", "show-reserve", {
help: "Show the current reserve status.",
})
.requiredArgument("reservePub", clk.STRING)
.action(async args => {
await withWallet(args, async wallet => {
const r = await wallet.getReserve(args.updateReserve.reservePub);
console.log("updated reserve:", JSON.stringify(r, undefined, 2));
});
});
const testCli = walletCli.subcommand("testingArgs", "testing", { const testCli = walletCli.subcommand("testingArgs", "testing", {
help: "Subcommands for testing GNU Taler deployments.", help: "Subcommands for testing GNU Taler deployments.",
}); });

View File

@ -41,7 +41,10 @@ import {
getExchangeTrust, getExchangeTrust,
getExchangePaytoUri, getExchangePaytoUri,
} from "./exchanges"; } from "./exchanges";
import { WithdrawOperationStatusResponse, codecForWithdrawOperationStatusResponse } from "../types/talerTypes"; import {
WithdrawOperationStatusResponse,
codecForWithdrawOperationStatusResponse,
} from "../types/talerTypes";
import { assertUnreachable } from "../util/assertUnreachable"; import { assertUnreachable } from "../util/assertUnreachable";
import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto"; import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto";
import { randomBytes } from "../crypto/primitives/nacl-fast"; import { randomBytes } from "../crypto/primitives/nacl-fast";
@ -61,11 +64,7 @@ import { getTimestampNow } from "../util/time";
const logger = new Logger("reserves.ts"); const logger = new Logger("reserves.ts");
async function resetReserveRetry(ws: InternalWalletState, reservePub: string) {
async function resetReserveRetry(
ws: InternalWalletState,
reservePub: string,
) {
await ws.db.mutate(Stores.reserves, reservePub, x => { await ws.db.mutate(Stores.reserves, reservePub, x => {
if (x.retryInfo.active) { if (x.retryInfo.active) {
x.retryInfo = initRetryInfo(); x.retryInfo = initRetryInfo();
@ -209,7 +208,7 @@ export async function forceQueryReserve(
ws: InternalWalletState, ws: InternalWalletState,
reservePub: string, reservePub: string,
): Promise<void> { ): Promise<void> {
await ws.db.runWithWriteTransaction([Stores.reserves], async (tx) => { await ws.db.runWithWriteTransaction([Stores.reserves], async tx => {
const reserve = await tx.get(Stores.reserves, reservePub); const reserve = await tx.get(Stores.reserves, reservePub);
if (!reserve) { if (!reserve) {
return; return;
@ -226,7 +225,6 @@ export async function forceQueryReserve(
reserve.reserveStatus = ReserveRecordStatus.QUERYING_STATUS; reserve.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
reserve.retryInfo = initRetryInfo(); reserve.retryInfo = initRetryInfo();
await tx.put(Stores.reserves, reserve); await tx.put(Stores.reserves, reserve);
}); });
await processReserve(ws, reservePub, true); await processReserve(ws, reservePub, true);
} }
@ -331,7 +329,9 @@ async function processReserveBankStatusImpl(
`unexpected status ${statusResp.status} for bank status query`, `unexpected status ${statusResp.status} for bank status query`,
); );
} }
status = codecForWithdrawOperationStatusResponse().decode(await statusResp.json()); status = codecForWithdrawOperationStatusResponse().decode(
await statusResp.json(),
);
} catch (e) { } catch (e) {
throw e; throw e;
} }
@ -436,7 +436,7 @@ async function updateReserve(
resp = await ws.http.get(reqUrl.href); resp = await ws.http.get(reqUrl.href);
console.log("got reserves/${RESERVE_PUB} response", await resp.json()); console.log("got reserves/${RESERVE_PUB} response", await resp.json());
if (resp.status === 404) { if (resp.status === 404) {
const m = "reserve not known to the exchange yet" const m = "reserve not known to the exchange yet";
throw new OperationFailedError({ throw new OperationFailedError({
type: "waiting", type: "waiting",
message: m, message: m,
@ -492,17 +492,17 @@ async function updateReserve(
await tx.put(Stores.reserveUpdatedEvents, reserveUpdate); await tx.put(Stores.reserveUpdatedEvents, reserveUpdate);
r.reserveStatus = ReserveRecordStatus.WITHDRAWING; r.reserveStatus = ReserveRecordStatus.WITHDRAWING;
} else { } else {
const expectedBalance = Amounts.sub( const expectedBalance = Amounts.add(
r.amountWithdrawAllocated, r.amountWithdrawRemaining,
r.amountWithdrawCompleted, Amounts.sub(r.amountWithdrawAllocated, r.amountWithdrawCompleted)
.amount,
); );
const cmp = Amounts.cmp(balance, expectedBalance.amount); const cmp = Amounts.cmp(balance, expectedBalance.amount);
if (cmp == 0) { if (cmp == 0) {
// Nothing changed, go back to sleep! // Nothing changed, go back to sleep!
r.reserveStatus = ReserveRecordStatus.DORMANT; r.reserveStatus = ReserveRecordStatus.DORMANT;
return;
} }
if (cmp > 0) { else if (cmp > 0) {
const extra = Amounts.sub(balance, expectedBalance.amount).amount; const extra = Amounts.sub(balance, expectedBalance.amount).amount;
r.amountWithdrawRemaining = Amounts.add( r.amountWithdrawRemaining = Amounts.add(
r.amountWithdrawRemaining, r.amountWithdrawRemaining,
@ -513,23 +513,28 @@ async function updateReserve(
// We're missing some money. // We're missing some money.
r.reserveStatus = ReserveRecordStatus.DORMANT; r.reserveStatus = ReserveRecordStatus.DORMANT;
} }
const reserveUpdate: ReserveUpdatedEventRecord = { if (r.reserveStatus !== ReserveRecordStatus.DORMANT) {
reservePub: r.reservePub, const reserveUpdate: ReserveUpdatedEventRecord = {
timestamp: getTimestampNow(), reservePub: r.reservePub,
amountReserveBalance: Amounts.toString(balance), timestamp: getTimestampNow(),
amountExpected: Amounts.toString(expectedBalance.amount), amountReserveBalance: Amounts.toString(balance),
newHistoryTransactions, amountExpected: Amounts.toString(expectedBalance.amount),
reserveUpdateId, newHistoryTransactions,
}; reserveUpdateId,
await tx.put(Stores.reserveUpdatedEvents, reserveUpdate); };
await tx.put(Stores.reserveUpdatedEvents, reserveUpdate);
}
} }
r.lastSuccessfulStatusQuery = getTimestampNow(); r.lastSuccessfulStatusQuery = getTimestampNow();
r.retryInfo = initRetryInfo(); if (r.reserveStatus == ReserveRecordStatus.DORMANT) {
r.retryInfo = initRetryInfo(false);
} else {
r.retryInfo = initRetryInfo();
}
r.reserveTransactions = reserveInfo.history; r.reserveTransactions = reserveInfo.history;
await tx.put(Stores.reserves, r); await tx.put(Stores.reserves, r);
}, },
); );
console.log("updated reserve");
ws.notify({ type: NotificationType.ReserveUpdated }); ws.notify({ type: NotificationType.ReserveUpdated });
} }

View File

@ -720,6 +720,10 @@ export class Wallet {
return await this.ws.db.get(Stores.reserves, reservePub); return await this.ws.db.get(Stores.reserves, reservePub);
} }
async getReserve(reservePub: string): Promise<ReserveRecord | undefined> {
return await this.ws.db.get(Stores.reserves, reservePub);
}
async refuseProposal(proposalId: string): Promise<void> { async refuseProposal(proposalId: string): Promise<void> {
return refuseProposal(this.ws, proposalId); return refuseProposal(this.ws, proposalId);
} }