implement latest recoup protocol
This commit is contained in:
parent
dbdad96b27
commit
dc596f1f4d
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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.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;
|
||||
});
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user