summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-12-18 19:25:26 +0100
committerFlorian Dold <florian@dold.me>2023-12-18 19:25:26 +0100
commit12a9b08c6f8c31f684239a30fc39acc9189c6571 (patch)
tree3755eb1a6b7659d44059ddb4cf85c832a8f88a8e
parenta488ce70d6dbfe08845eccaeb2375b367f7c307a (diff)
downloadwallet-core-12a9b08c6f8c31f684239a30fc39acc9189c6571.tar.gz
wallet-core-12a9b08c6f8c31f684239a30fc39acc9189c6571.tar.bz2
wallet-core-12a9b08c6f8c31f684239a30fc39acc9189c6571.zip
wallet-core: towards properly handling peer-pull-debit expiry
-rw-r--r--packages/taler-harness/src/harness/harness.ts28
-rw-r--r--packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts54
-rw-r--r--packages/taler-util/src/taler-crypto.ts6
-rw-r--r--packages/taler-util/src/transactions-types.ts1
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoImplementation.ts28
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoTypes.ts10
-rw-r--r--packages/taler-wallet-core/src/db.ts12
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts168
-rw-r--r--packages/taler-wallet-core/src/operations/pending.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/refresh.ts23
10 files changed, 286 insertions, 46 deletions
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts
index a6ed59032..27c54b8b4 100644
--- a/packages/taler-harness/src/harness/harness.ts
+++ b/packages/taler-harness/src/harness/harness.ts
@@ -582,7 +582,7 @@ class BankServiceBase {
protected globalTestState: GlobalTestState,
protected bankConfig: BankConfig,
protected configFile: string,
- ) { }
+ ) {}
}
export interface HarnessExchangeBankAccount {
@@ -602,7 +602,8 @@ export interface HarnessExchangeBankAccount {
*/
export class FakebankService
extends BankServiceBase
- implements BankServiceHandle {
+ implements BankServiceHandle
+{
proc: ProcessWrapper | undefined;
http = createPlatformHttpLib({ enableThrottling: false });
@@ -732,7 +733,8 @@ export class FakebankService
*/
export class LibeufinBankService
extends BankServiceBase
- implements BankServiceHandle {
+ implements BankServiceHandle
+{
proc: ProcessWrapper | undefined;
http = createPlatformHttpLib({ enableThrottling: false });
@@ -928,11 +930,11 @@ export class ExchangeService implements ExchangeServiceInterface {
private exchangeBankAccounts: HarnessExchangeBankAccount[] = [];
- setTimetravel(t: number | undefined): void {
+ setTimetravel(tMs: number | undefined): void {
if (this.isRunning()) {
throw Error("can't set time travel while the exchange is running");
}
- this.currentTimetravelOffsetMs = t;
+ this.currentTimetravelOffsetMs = tMs;
}
private get timetravelArg(): string | undefined {
@@ -1184,7 +1186,7 @@ export class ExchangeService implements ExchangeServiceInterface {
private exchangeConfig: ExchangeConfig,
private configFilename: string,
private keyPair: EddsaKeyPair,
- ) { }
+ ) {}
get name() {
return this.exchangeConfig.name;
@@ -1540,7 +1542,7 @@ export class MerchantService implements MerchantServiceInterface {
private globalState: GlobalTestState,
private merchantConfig: MerchantConfig,
private configFilename: string,
- ) { }
+ ) {}
private currentTimetravelOffsetMs: number | undefined;
@@ -1878,7 +1880,7 @@ export class WalletService {
constructor(
private globalState: GlobalTestState,
private opts: WalletServiceOptions,
- ) { }
+ ) {}
get socketPath() {
const unixPath = path.join(
@@ -1987,7 +1989,7 @@ export class WalletClient {
return client.call(operation, payload);
}
- constructor(private args: WalletClientArgs) { }
+ constructor(private args: WalletClientArgs) {}
async connect(): Promise<void> {
const waiter = this.waiter;
@@ -2054,9 +2056,11 @@ export class WalletCli {
? `--crypto-worker=${cliOpts.cryptoWorkerType}`
: "";
const logName = `wallet-${self.name}`;
- const command = `taler-wallet-cli ${self.timetravelArg ?? ""
- } ${cryptoWorkerArg} --no-throttle -LTRACE --skip-defaults --wallet-db '${self.dbfile
- }' api '${op}' ${shellWrap(JSON.stringify(payload))}`;
+ const command = `taler-wallet-cli ${
+ self.timetravelArg ?? ""
+ } ${cryptoWorkerArg} --no-throttle -LTRACE --skip-defaults --wallet-db '${
+ self.dbfile
+ }' api '${op}' ${shellWrap(JSON.stringify(payload))}`;
const resp = await sh(self.globalTestState, logName, command);
logger.info("--- wallet core response ---");
logger.info(resp);
diff --git a/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts b/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts
index a98ea89bb..583dba28d 100644
--- a/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts
+++ b/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts
@@ -22,17 +22,16 @@ import {
AmountString,
Duration,
NotificationType,
- TalerUriAction,
TransactionMajorState,
TransactionMinorState,
TransactionType,
WalletNotification,
j2s,
- stringifyTalerUri,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js";
import {
+ applyTimeTravelV2,
createSimpleTestkudosEnvironmentV2,
createWalletDaemonWithClient,
withdrawViaBankV2,
@@ -139,7 +138,7 @@ export async function runPeerToPeerPushTest(t: GlobalTestState) {
const acceptResp = await w2.walletClient.call(
WalletApiOperation.ConfirmPeerPushCredit,
{
- peerPushCreditId: checkResp.peerPushCreditId,
+ transactionId: checkResp.transactionId,
},
);
@@ -157,6 +156,7 @@ export async function runPeerToPeerPushTest(t: GlobalTestState) {
console.log(`txn1: ${j2s(txn1)}`);
console.log(`txn2: ${j2s(txn2)}`);
+ // We expect insufficient balance here!
const ex1 = await t.assertThrowsTalerErrorAsync(async () => {
await w1.walletClient.call(WalletApiOperation.InitiatePeerPushDebit, {
partialContractTerms: {
@@ -168,6 +168,54 @@ export async function runPeerToPeerPushTest(t: GlobalTestState) {
});
console.log("got expected exception detail", j2s(ex1.errorDetail));
+
+ const initiateResp2 = await w1.walletClient.call(
+ WalletApiOperation.InitiatePeerPushDebit,
+ {
+ partialContractTerms: {
+ summary: "second tx, will expire",
+ amount: "TESTKUDOS:5" as AmountString,
+ purse_expiration,
+ },
+ },
+ );
+
+ const peerPushReadyCond2 = w1.walletClient.waitForNotificationCond(
+ (x) =>
+ x.type === NotificationType.TransactionStateTransition &&
+ x.newTxState.major === TransactionMajorState.Pending &&
+ x.newTxState.minor === TransactionMinorState.Ready &&
+ x.transactionId === initiateResp2.transactionId,
+ );
+
+ await peerPushReadyCond2;
+
+ const timetravelOffsetMs = Duration.toMilliseconds(
+ Duration.fromSpec({ days: 5 }),
+ );
+
+ await exchange.stop();
+ exchange.setTimetravel(timetravelOffsetMs);
+ await exchange.start();
+ await exchange.pingUntilAvailable();
+
+ await w1.walletClient.call(WalletApiOperation.TestingSetTimetravel, {
+ offsetMs: timetravelOffsetMs,
+ });
+
+ await w1.walletClient.call(
+ WalletApiOperation.TestingWaitTransactionsFinal,
+ {},
+ );
+
+ const txDetails2 = await w1.walletClient.call(
+ WalletApiOperation.GetTransactionById,
+ {
+ transactionId: initiateResp2.transactionId,
+ },
+ );
+
+ console.log(`tx details 2: ${j2s(txDetails2)}`);
}
runPeerToPeerPushTest.suites = ["wallet"];
diff --git a/packages/taler-util/src/taler-crypto.ts b/packages/taler-util/src/taler-crypto.ts
index de870955b..8f8b2ceac 100644
--- a/packages/taler-util/src/taler-crypto.ts
+++ b/packages/taler-util/src/taler-crypto.ts
@@ -844,7 +844,8 @@ export function hashDenomPub(pub: DenominationPubKey): Uint8Array {
return hash(uint8ArrayBuf);
} else {
throw Error(
- `unsupported cipher (${(pub as DenominationPubKey).cipher
+ `unsupported cipher (${
+ (pub as DenominationPubKey).cipher
}), unable to hash`,
);
}
@@ -993,6 +994,7 @@ export enum TalerSignaturePurpose {
WALLET_ACCOUNT_MERGE = 1214,
WALLET_PURSE_ECONTRACT = 1216,
WALLET_PURSE_DELETE = 1220,
+ WALLET_COIN_HISTORY = 1209,
EXCHANGE_CONFIRM_RECOUP = 1039,
EXCHANGE_CONFIRM_RECOUP_REFRESH = 1041,
TALER_SIGNATURE_AML_DECISION = 1350,
@@ -1022,7 +1024,7 @@ export enum WalletAccountMergeFlags {
export class SignaturePurposeBuilder {
private chunks: Uint8Array[] = [];
- constructor(private purposeNum: number) { }
+ constructor(private purposeNum: number) {}
put(bytes: Uint8Array): SignaturePurposeBuilder {
this.chunks.push(Uint8Array.from(bytes));
diff --git a/packages/taler-util/src/transactions-types.ts b/packages/taler-util/src/transactions-types.ts
index b3b197891..740478fb0 100644
--- a/packages/taler-util/src/transactions-types.ts
+++ b/packages/taler-util/src/transactions-types.ts
@@ -121,6 +121,7 @@ export enum TransactionMinorState {
CheckRefund = "check-refund",
CreatePurse = "create-purse",
DeletePurse = "delete-purse",
+ RefreshExpired = "refresh-expired",
Ready = "ready",
Merge = "merge",
Repurchase = "repurchase",
diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
index 36ca128ae..7c6b142fb 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
@@ -103,6 +103,8 @@ import {
EncryptContractForDepositResponse,
EncryptContractRequest,
EncryptContractResponse,
+ SignCoinHistoryRequest,
+ SignCoinHistoryResponse,
SignDeletePurseRequest,
SignDeletePurseResponse,
SignPurseMergeRequest,
@@ -243,6 +245,10 @@ export interface TalerCryptoInterface {
signDeletePurse(
req: SignDeletePurseRequest,
): Promise<SignDeletePurseResponse>;
+
+ signCoinHistoryRequest(
+ req: SignCoinHistoryRequest,
+ ): Promise<SignCoinHistoryResponse>;
}
/**
@@ -427,6 +433,11 @@ export const nullCrypto: TalerCryptoInterface = {
): Promise<SignDeletePurseResponse> {
throw new Error("Function not implemented.");
},
+ signCoinHistoryRequest: function (
+ req: SignCoinHistoryRequest,
+ ): Promise<SignCoinHistoryResponse> {
+ throw new Error("Function not implemented.");
+ },
};
export type WithArg<X> = X extends (req: infer T) => infer R
@@ -1705,6 +1716,23 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
sig: sigResp.sig,
};
},
+ async signCoinHistoryRequest(
+ tci: TalerCryptoInterfaceR,
+ req: SignCoinHistoryRequest,
+ ): Promise<SignCoinHistoryResponse> {
+ const coinHistorySigBlob = buildSigPS(
+ TalerSignaturePurpose.WALLET_COIN_HISTORY,
+ )
+ .put(bufferForUint64(req.startOffset))
+ .build();
+ const sigResp = await tci.eddsaSign(tci, {
+ msg: encodeCrock(coinHistorySigBlob),
+ priv: req.coinPriv,
+ });
+ return {
+ sig: sigResp.sig,
+ };
+ },
};
export interface EddsaSignRequest {
diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
index 2204fac71..df25b87e4 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
@@ -279,6 +279,16 @@ export interface SignDeletePurseResponse {
sig: EddsaSignatureString;
}
+export interface SignCoinHistoryRequest {
+ coinPub: string;
+ coinPriv: string;
+ startOffset: number;
+}
+
+export interface SignCoinHistoryResponse {
+ sig: EddsaSignatureString;
+}
+
export interface SignReservePurseCreateRequest {
mergeTimestamp: TalerProtocolTimestamp;
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index d2c6b8368..6f6aad256 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1799,12 +1799,20 @@ export enum PeerPushDebitStatus {
PendingCreatePurse = 0x0100_0000 /* ACTIVE_START */,
PendingReady = 0x0100_0001,
AbortingDeletePurse = 0x0103_0000,
- AbortingRefresh = 0x0103_0001,
+ /**
+ * Refresh after the purse got deleted by the wallet.
+ */
+ AbortingRefreshDeleted = 0x0103_0001,
+ /**
+ * Refresh after the purse expired.
+ */
+ AbortingRefreshExpired = 0x0103_0002,
SuspendedCreatePurse = 0x0110_0000,
SuspendedReady = 0x0110_0001,
SuspendedAbortingDeletePurse = 0x0113_0000,
- SuspendedAbortingRefresh = 0x0113_0001,
+ SuspendedAbortingRefreshDeleted = 0x0113_0001,
+ SuspendedAbortingRefreshExpired = 0x0113_0002,
Done = 0x0500_0000,
Aborted = 0x0503_0000,
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
index da779a07d..c79aca1ad 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
@@ -365,7 +365,7 @@ async function processPeerPushDebitAbortingDeletePurse(
coinPubs,
RefreshReason.AbortPeerPushDebit,
);
- ppiRec.status = PeerPushDebitStatus.AbortingRefresh;
+ ppiRec.status = PeerPushDebitStatus.AbortingRefreshDeleted;
ppiRec.abortRefreshGroupId = refresh.refreshGroupId;
await tx.peerPushDebit.put(ppiRec);
const newTxState = computePeerPushDebitTransactionState(ppiRec);
@@ -415,7 +415,7 @@ async function transitionPeerPushDebitTransaction(
notifyTransition(ws, transactionId, transitionInfo);
}
-async function processPeerPushDebitAbortingRefresh(
+async function processPeerPushDebitAbortingRefreshDeleted(
ws: InternalWalletState,
peerPushInitiation: PeerPushDebitRecord,
): Promise<TaskRunResult> {
@@ -463,6 +463,54 @@ async function processPeerPushDebitAbortingRefresh(
return TaskRunResult.pending();
}
+async function processPeerPushDebitAbortingRefreshExpired(
+ ws: InternalWalletState,
+ peerPushInitiation: PeerPushDebitRecord,
+): Promise<TaskRunResult> {
+ const pursePub = peerPushInitiation.pursePub;
+ const abortRefreshGroupId = peerPushInitiation.abortRefreshGroupId;
+ checkLogicInvariant(!!abortRefreshGroupId);
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.PeerPushDebit,
+ pursePub: peerPushInitiation.pursePub,
+ });
+ const transitionInfo = await ws.db
+ .mktx((x) => [x.refreshGroups, x.peerPushDebit])
+ .runReadWrite(async (tx) => {
+ const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId);
+ let newOpState: PeerPushDebitStatus | undefined;
+ if (!refreshGroup) {
+ // Maybe it got manually deleted? Means that we should
+ // just go into failed.
+ logger.warn("no aborting refresh group found for deposit group");
+ newOpState = PeerPushDebitStatus.Failed;
+ } else {
+ if (refreshGroup.operationStatus === RefreshOperationStatus.Finished) {
+ newOpState = PeerPushDebitStatus.Expired;
+ } else if (
+ refreshGroup.operationStatus === RefreshOperationStatus.Failed
+ ) {
+ newOpState = PeerPushDebitStatus.Failed;
+ }
+ }
+ if (newOpState) {
+ const newDg = await tx.peerPushDebit.get(pursePub);
+ if (!newDg) {
+ return;
+ }
+ const oldTxState = computePeerPushDebitTransactionState(newDg);
+ newDg.status = newOpState;
+ const newTxState = computePeerPushDebitTransactionState(newDg);
+ await tx.peerPushDebit.put(newDg);
+ return { oldTxState, newTxState };
+ }
+ return undefined;
+ });
+ notifyTransition(ws, transactionId, transitionInfo);
+ // FIXME: Shouldn't this be finished in some cases?!
+ return TaskRunResult.pending();
+}
+
/**
* Process the "pending(ready)" state of a peer-push-debit transaction.
*/
@@ -476,6 +524,10 @@ async function processPeerPushDebitReady(
tag: PendingTaskType.PeerPushDebit,
pursePub,
});
+ const transactionId = constructTaskIdentifier({
+ tag: PendingTaskType.PeerPushDebit,
+ pursePub,
+ });
runLongpollAsync(ws, retryTag, async (ct) => {
const mergeUrl = new URL(
`purses/${pursePub}/merge`,
@@ -510,14 +562,50 @@ async function processPeerPushDebitReady(
};
}
} else if (resp.status === HttpStatusCode.Gone) {
- await transitionPeerPushDebitTransaction(
- ws,
- peerPushInitiation.pursePub,
- {
- stFrom: PeerPushDebitStatus.PendingReady,
- stTo: PeerPushDebitStatus.Expired,
- },
- );
+ const transitionInfo = await ws.db
+ .mktx((x) => [
+ x.peerPushDebit,
+ x.refreshGroups,
+ x.denominations,
+ x.coinAvailability,
+ x.coins,
+ ])
+ .runReadWrite(async (tx) => {
+ const ppiRec = await tx.peerPushDebit.get(pursePub);
+ if (!ppiRec) {
+ return undefined;
+ }
+ if (ppiRec.status !== PeerPushDebitStatus.PendingReady) {
+ return undefined;
+ }
+ const currency = Amounts.currencyOf(ppiRec.amount);
+ const oldTxState = computePeerPushDebitTransactionState(ppiRec);
+ const coinPubs: CoinRefreshRequest[] = [];
+
+ for (let i = 0; i < ppiRec.coinSel.coinPubs.length; i++) {
+ coinPubs.push({
+ amount: ppiRec.coinSel.contributions[i],
+ coinPub: ppiRec.coinSel.coinPubs[i],
+ });
+ }
+
+ const refresh = await createRefreshGroup(
+ ws,
+ tx,
+ currency,
+ coinPubs,
+ RefreshReason.AbortPeerPushDebit,
+ );
+ ppiRec.status = PeerPushDebitStatus.AbortingRefreshExpired;
+ ppiRec.abortRefreshGroupId = refresh.refreshGroupId;
+ await tx.peerPushDebit.put(ppiRec);
+ const newTxState = computePeerPushDebitTransactionState(ppiRec);
+ return {
+ oldTxState,
+ newTxState,
+ };
+ });
+ notifyTransition(ws, transactionId, transitionInfo);
return {
ready: true,
};
@@ -569,8 +657,10 @@ export async function processPeerPushDebit(
return processPeerPushDebitReady(ws, peerPushInitiation);
case PeerPushDebitStatus.AbortingDeletePurse:
return processPeerPushDebitAbortingDeletePurse(ws, peerPushInitiation);
- case PeerPushDebitStatus.AbortingRefresh:
- return processPeerPushDebitAbortingRefresh(ws, peerPushInitiation);
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
+ return processPeerPushDebitAbortingRefreshDeleted(ws, peerPushInitiation);
+ case PeerPushDebitStatus.AbortingRefreshExpired:
+ return processPeerPushDebitAbortingRefreshExpired(ws, peerPushInitiation);
default: {
const txState = computePeerPushDebitTransactionState(peerPushInitiation);
logger.warn(
@@ -722,11 +812,15 @@ export function computePeerPushDebitTransactionActions(
return [TransactionAction.Delete];
case PeerPushDebitStatus.AbortingDeletePurse:
return [TransactionAction.Suspend, TransactionAction.Fail];
- case PeerPushDebitStatus.AbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
return [TransactionAction.Suspend, TransactionAction.Fail];
+ case PeerPushDebitStatus.AbortingRefreshExpired:
+ return [TransactionAction.Resume, TransactionAction.Fail];
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
+ return [TransactionAction.Resume, TransactionAction.Fail];
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
return [TransactionAction.Resume, TransactionAction.Fail];
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
return [TransactionAction.Resume, TransactionAction.Fail];
case PeerPushDebitStatus.SuspendedCreatePurse:
return [TransactionAction.Resume, TransactionAction.Abort];
@@ -773,9 +867,11 @@ export async function abortPeerPushDebitTransaction(
// Network request might already be in-flight!
newStatus = PeerPushDebitStatus.AbortingDeletePurse;
break;
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
- case PeerPushDebitStatus.AbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
+ case PeerPushDebitStatus.AbortingRefreshExpired:
case PeerPushDebitStatus.Done:
case PeerPushDebitStatus.AbortingDeletePurse:
case PeerPushDebitStatus.Aborted:
@@ -824,13 +920,15 @@ export async function failPeerPushDebitTransaction(
}
let newStatus: PeerPushDebitStatus | undefined = undefined;
switch (pushDebitRec.status) {
- case PeerPushDebitStatus.AbortingRefresh:
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
// FIXME: What to do about the refresh group?
newStatus = PeerPushDebitStatus.Failed;
break;
case PeerPushDebitStatus.AbortingDeletePurse:
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
+ case PeerPushDebitStatus.AbortingRefreshExpired:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
case PeerPushDebitStatus.PendingReady:
case PeerPushDebitStatus.SuspendedReady:
case PeerPushDebitStatus.SuspendedCreatePurse:
@@ -887,8 +985,11 @@ export async function suspendPeerPushDebitTransaction(
case PeerPushDebitStatus.PendingCreatePurse:
newStatus = PeerPushDebitStatus.SuspendedCreatePurse;
break;
- case PeerPushDebitStatus.AbortingRefresh:
- newStatus = PeerPushDebitStatus.SuspendedAbortingRefresh;
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
+ newStatus = PeerPushDebitStatus.SuspendedAbortingRefreshDeleted;
+ break;
+ case PeerPushDebitStatus.AbortingRefreshExpired:
+ newStatus = PeerPushDebitStatus.SuspendedAbortingRefreshExpired;
break;
case PeerPushDebitStatus.AbortingDeletePurse:
newStatus = PeerPushDebitStatus.SuspendedAbortingDeletePurse;
@@ -897,7 +998,8 @@ export async function suspendPeerPushDebitTransaction(
newStatus = PeerPushDebitStatus.SuspendedReady;
break;
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
case PeerPushDebitStatus.SuspendedReady:
case PeerPushDebitStatus.SuspendedCreatePurse:
case PeerPushDebitStatus.Done:
@@ -950,8 +1052,11 @@ export async function resumePeerPushDebitTransaction(
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
newStatus = PeerPushDebitStatus.AbortingDeletePurse;
break;
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
- newStatus = PeerPushDebitStatus.AbortingRefresh;
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
+ newStatus = PeerPushDebitStatus.AbortingRefreshDeleted;
+ break;
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
+ newStatus = PeerPushDebitStatus.AbortingRefreshExpired;
break;
case PeerPushDebitStatus.SuspendedReady:
newStatus = PeerPushDebitStatus.PendingReady;
@@ -960,7 +1065,8 @@ export async function resumePeerPushDebitTransaction(
newStatus = PeerPushDebitStatus.PendingCreatePurse;
break;
case PeerPushDebitStatus.PendingCreatePurse:
- case PeerPushDebitStatus.AbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
+ case PeerPushDebitStatus.AbortingRefreshExpired:
case PeerPushDebitStatus.AbortingDeletePurse:
case PeerPushDebitStatus.PendingReady:
case PeerPushDebitStatus.Done:
@@ -1011,17 +1117,27 @@ export function computePeerPushDebitTransactionState(
major: TransactionMajorState.Aborting,
minor: TransactionMinorState.DeletePurse,
};
- case PeerPushDebitStatus.AbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
return {
major: TransactionMajorState.Aborting,
minor: TransactionMinorState.Refresh,
};
+ case PeerPushDebitStatus.AbortingRefreshExpired:
+ return {
+ major: TransactionMajorState.Aborting,
+ minor: TransactionMinorState.RefreshExpired,
+ };
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
return {
major: TransactionMajorState.SuspendedAborting,
minor: TransactionMinorState.DeletePurse,
};
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
+ return {
+ major: TransactionMajorState.SuspendedAborting,
+ minor: TransactionMinorState.RefreshExpired,
+ };
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
return {
major: TransactionMajorState.SuspendedAborting,
minor: TransactionMinorState.Refresh,
diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts
index a9d6c5595..233ca3fa4 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -640,7 +640,7 @@ export async function iterRecordsForPeerPushInitiation(
if (filter.onlyState === "nonfinal") {
const keyRange = GlobalIDB.KeyRange.bound(
PeerPushDebitStatus.PendingCreatePurse,
- PeerPushDebitStatus.AbortingRefresh,
+ PeerPushDebitStatus.AbortingRefreshDeleted,
);
await tx.peerPushDebit.indexes.byStatus.iter(keyRange).forEachAsync(f);
} else {
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts
index c58f03e05..3bbbc2a4b 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -528,6 +528,29 @@ async function refreshMelt(
derived.meltValueWithFee,
)} failed in refresh group ${refreshGroupId} due to conflict`,
);
+
+ const historySig = await ws.cryptoApi.signCoinHistoryRequest({
+ coinPriv: oldCoin.coinPriv,
+ coinPub: oldCoin.coinPub,
+ startOffset: 0,
+ });
+
+ const historyUrl = new URL(
+ `coins/${oldCoin.coinPub}/history`,
+ oldCoin.exchangeBaseUrl,
+ );
+
+ const historyResp = await ws.http.fetch(historyUrl.href, {
+ method: "GET",
+ headers: {
+ "Taler-Coin-History-Signature": historySig.sig,
+ },
+ });
+
+ const historyJson = await historyResp.json();
+ logger.info(`coin history: ${j2s(historyJson)}`);
+
+ // FIXME: Before failing and re-trying, analyse response and adjust amount
}
const meltResponse = await readSuccessResponseJsonOrThrow(