implement latest recoup protocol

This commit is contained in:
Florian Dold 2022-01-12 15:51:56 +01:00
parent dbdad96b27
commit dc596f1f4d
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
8 changed files with 31 additions and 48 deletions

View File

@ -173,11 +173,6 @@ export interface RecoupRequest {
* Signature of TALER_RecoupRequestPS created with the coin's private key.
*/
coin_sig: string;
/**
* Amount being recouped.
*/
amount: AmountString;
}
export interface RecoupRefreshRequest {
@ -204,11 +199,6 @@ export interface RecoupRefreshRequest {
* the coin's private key.
*/
coin_sig: string;
/**
* Amount being recouped.
*/
amount: AmountString;
}
/**

View File

@ -157,7 +157,6 @@ export interface CreateRecoupReqRequest {
denomPub: DenominationPubKey;
denomPubHash: string;
denomSig: UnblindedSignature;
recoupAmount: AmountJson;
}
/**
@ -170,5 +169,4 @@ export interface CreateRecoupRefreshReqRequest {
denomPub: DenominationPubKey;
denomPubHash: string;
denomSig: UnblindedSignature;
recoupAmount: AmountJson;
}

View File

@ -263,7 +263,6 @@ export class CryptoImplementation {
const p = buildSigPS(TalerSignaturePurpose.WALLET_COIN_RECOUP)
.put(decodeCrock(req.denomPubHash))
.put(decodeCrock(req.blindingKey))
.put(amountToBuffer(Amounts.jsonifyAmount(req.recoupAmount)))
.build();
const coinPriv = decodeCrock(req.coinPriv);
@ -274,7 +273,6 @@ export class CryptoImplementation {
coin_sig: encodeCrock(coinSig),
denom_pub_hash: req.denomPubHash,
denom_sig: req.denomSig.rsa_signature,
amount: Amounts.stringify(req.recoupAmount),
};
return paybackRequest;
} else {
@ -283,7 +281,6 @@ export class CryptoImplementation {
coin_sig: encodeCrock(coinSig),
denom_pub_hash: req.denomPubHash,
denom_sig: req.denomSig,
amount: Amounts.stringify(req.recoupAmount),
};
return paybackRequest;
}
@ -292,33 +289,32 @@ export class CryptoImplementation {
/**
* Create and sign a message to recoup a coin.
*/
createRecoupRefreshRequest(req: CreateRecoupRefreshReqRequest): RecoupRefreshRequest {
createRecoupRefreshRequest(
req: CreateRecoupRefreshReqRequest,
): RecoupRefreshRequest {
const p = buildSigPS(TalerSignaturePurpose.WALLET_COIN_RECOUP_REFRESH)
.put(decodeCrock(req.denomPubHash))
.put(decodeCrock(req.blindingKey))
.put(amountToBuffer(Amounts.jsonifyAmount(req.recoupAmount)))
.build();
const coinPriv = decodeCrock(req.coinPriv);
const coinSig = eddsaSign(p, coinPriv);
if (req.denomPub.cipher === DenomKeyType.LegacyRsa) {
const paybackRequest: RecoupRefreshRequest = {
const recoupRequest: RecoupRefreshRequest = {
coin_blind_key_secret: req.blindingKey,
coin_sig: encodeCrock(coinSig),
denom_pub_hash: req.denomPubHash,
denom_sig: req.denomSig.rsa_signature,
amount: Amounts.stringify(req.recoupAmount),
};
return paybackRequest;
return recoupRequest;
} else {
const paybackRequest: RecoupRefreshRequest = {
const recoupRequest: RecoupRefreshRequest = {
coin_blind_key_secret: req.blindingKey,
coin_sig: encodeCrock(coinSig),
denom_pub_hash: req.denomPubHash,
denom_sig: req.denomSig,
amount: Amounts.stringify(req.recoupAmount),
};
return paybackRequest;
return recoupRequest;
}
}

View File

@ -42,7 +42,6 @@ import {
import { RetryInfo } from "./util/retries.js";
import { PayCoinSelection } from "./util/coinSelection.js";
import { Event, IDBDatabase } from "@gnu-taler/idb-bridge";
import { PendingTaskInfo } from "./pending-types.js";
/**
* Name of the Taler database. This is effectively the major
@ -140,7 +139,7 @@ export interface ReserveRecord {
reservePriv: string;
/**
* The exchange base URL.
* The exchange base URL for the reserve.
*/
exchangeBaseUrl: string;
@ -154,8 +153,6 @@ export interface ReserveRecord {
*/
timestampCreated: Timestamp;
operationStatus: OperationStatus;
/**
* Time when the information about this reserve was posted to the bank.
*
@ -206,13 +203,17 @@ export interface ReserveRecord {
*/
initialDenomSel: DenomSelectionState;
/**
* Current status of the reserve.
*/
reserveStatus: ReserveRecordStatus;
/**
* Was a reserve query requested? If so, query again instead
* of going into dormant status.
* Is there any work to be done for this reserve?
*
* FIXME: Technically redundant, since the reserveStatus would indicate this.
*/
requestedQuery: boolean;
operationStatus: OperationStatus;
/**
* Time of the last successful status query.

View File

@ -457,7 +457,6 @@ export async function importBackup(
exchangeBaseUrl: backupExchangeDetails.base_url,
reservePub,
reservePriv: backupReserve.reserve_priv,
requestedQuery: false,
bankInfo,
timestampCreated: backupReserve.timestamp_created,
timestampBankConfirmed:

View File

@ -43,6 +43,7 @@ import {
ReserveRecordStatus,
WithdrawCoinSource,
WalletStoresV1,
OperationStatus,
} from "../db.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
@ -186,7 +187,6 @@ async function recoupWithdrawCoin(
denomPub: coin.denomPub,
denomPubHash: coin.denomPubHash,
denomSig: coin.denomSig,
recoupAmount: coin.currentAmount,
});
const reqUrl = new URL(`/coins/${coin.coinPub}/recoup`, coin.exchangeBaseUrl);
logger.trace(`requesting recoup via ${reqUrl.href}`);
@ -233,13 +233,9 @@ async function recoupWithdrawCoin(
updatedCoin.status = CoinStatus.Dormant;
const currency = updatedCoin.currentAmount.currency;
updatedCoin.currentAmount = Amounts.getZero(currency);
if (updatedReserve.reserveStatus === ReserveRecordStatus.DORMANT) {
updatedReserve.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
updatedReserve.retryInfo = initRetryInfo();
} else {
updatedReserve.requestedQuery = true;
updatedReserve.retryInfo = initRetryInfo();
}
updatedReserve.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
updatedReserve.retryInfo = initRetryInfo();
updatedReserve.operationStatus = OperationStatus.Pending;
await tx.coins.put(updatedCoin);
await tx.reserves.put(updatedReserve);
await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
@ -268,9 +264,11 @@ async function recoupRefreshCoin(
denomPub: coin.denomPub,
denomPubHash: coin.denomPubHash,
denomSig: coin.denomSig,
recoupAmount: coin.currentAmount,
});
const reqUrl = new URL(`/coins/${coin.coinPub}/recoup`, coin.exchangeBaseUrl);
const reqUrl = new URL(
`/coins/${coin.coinPub}/recoup-refresh`,
coin.exchangeBaseUrl,
);
logger.trace(`making recoup request for ${coin.coinPub}`);
const resp = await ws.http.postJson(reqUrl.href, recoupRequest);
@ -381,7 +379,7 @@ async function processRecoupGroupImpl(
}
const ps = recoupGroup.coinPubs.map(async (x, i) => {
try {
processRecoup(ws, recoupGroupId, i);
await processRecoup(ws, recoupGroupId, i);
} catch (e) {
logger.warn(`processRecoup failed: ${e}`);
throw e;
@ -408,7 +406,7 @@ async function processRecoupGroupImpl(
}
for (const r of reserveSet.values()) {
processReserve(ws, r).catch((e) => {
processReserve(ws, r, true).catch((e) => {
logger.error(`processing reserve ${r} after recoup failed`);
});
}
@ -460,6 +458,9 @@ export async function createRecoupGroup(
return recoupGroupId;
}
/**
* Run the recoup protocol for a single coin in a recoup group.
*/
async function processRecoup(
ws: InternalWalletState,
recoupGroupId: string,
@ -486,7 +487,7 @@ async function processRecoup(
const coin = await tx.coins.get(coinPub);
if (!coin) {
throw Error(`Coin ${coinPub} not found, can't request payback`);
throw Error(`Coin ${coinPub} not found, can't request recoup`);
}
return coin;
});

View File

@ -155,7 +155,6 @@ export async function createReserve(
retryInfo: initRetryInfo(),
lastError: undefined,
currency: req.amount.currency,
requestedQuery: false,
operationStatus: OperationStatus.Pending,
};
@ -255,7 +254,6 @@ export async function forceQueryReserve(
reserve.operationStatus = OperationStatus.Pending;
break;
default:
reserve.requestedQuery = true;
break;
}
reserve.retryInfo = initRetryInfo();

View File

@ -164,7 +164,7 @@ export async function readUnexpectedResponseDetails(
}
return makeErrorDetails(
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
"Unexpected error code in response",
`Unexpected HTTP status (${httpResponse.status}) in response`,
{
requestUrl: httpResponse.requestUrl,
httpStatusCode: httpResponse.status,
@ -220,7 +220,7 @@ export function throwUnexpectedRequestError(
throw new OperationFailedError(
makeErrorDetails(
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
"Unexpected error code in response",
`Unexpected HTTP status ${httpResponse.status} in response`,
{
requestUrl: httpResponse.requestUrl,
httpStatusCode: httpResponse.status,