towards new recoup API
This commit is contained in:
parent
fb22009ec4
commit
a05e891d6e
@ -479,6 +479,7 @@ export enum TalerSignaturePurpose {
|
||||
MERCHANT_CONTRACT = 1101,
|
||||
WALLET_COIN_RECOUP = 1203,
|
||||
WALLET_COIN_LINK = 1204,
|
||||
WALLET_COIN_RECOUP_REFRESH = 1206,
|
||||
EXCHANGE_CONFIRM_RECOUP = 1039,
|
||||
EXCHANGE_CONFIRM_RECOUP_REFRESH = 1041,
|
||||
ANASTASIS_POLICY_UPLOAD = 1400,
|
||||
|
@ -46,7 +46,7 @@ import {
|
||||
Duration,
|
||||
codecForDuration,
|
||||
} from "./time.js";
|
||||
import { codecForAmountString } from "./amounts.js";
|
||||
import { Amounts, codecForAmountString } from "./amounts.js";
|
||||
|
||||
/**
|
||||
* Denomination as found in the /keys response from the exchange.
|
||||
@ -149,9 +149,6 @@ export class ExchangeAuditor {
|
||||
denomination_keys: AuditorDenomSig[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Request that we send to the exchange to get a payback.
|
||||
*/
|
||||
export interface RecoupRequest {
|
||||
/**
|
||||
* Hashed enomination public key of the coin we want to get
|
||||
@ -166,11 +163,6 @@ export interface RecoupRequest {
|
||||
*/
|
||||
denom_sig: UnblindedSignature | string;
|
||||
|
||||
/**
|
||||
* Coin public key of the coin we want to refund.
|
||||
*/
|
||||
coin_pub: string;
|
||||
|
||||
/**
|
||||
* Blinding key that was used during withdraw,
|
||||
* used to prove that we were actually withdrawing the coin.
|
||||
@ -178,14 +170,45 @@ export interface RecoupRequest {
|
||||
coin_blind_key_secret: string;
|
||||
|
||||
/**
|
||||
* Signature made by the coin, authorizing the payback.
|
||||
* Signature of TALER_RecoupRequestPS created with the coin's private key.
|
||||
*/
|
||||
coin_sig: string;
|
||||
|
||||
/**
|
||||
* Was the coin refreshed (and thus the recoup should go to the old coin)?
|
||||
* Amount being recouped.
|
||||
*/
|
||||
refreshed: boolean;
|
||||
amount: AmountString;
|
||||
}
|
||||
|
||||
export interface RecoupRefreshRequest {
|
||||
/**
|
||||
* Hashed enomination public key of the coin we want to get
|
||||
* paid back.
|
||||
*/
|
||||
denom_pub_hash: string;
|
||||
|
||||
/**
|
||||
* Signature over the coin public key by the denomination.
|
||||
*
|
||||
* The string variant is for the legacy exchange protocol.
|
||||
*/
|
||||
denom_sig: UnblindedSignature | string;
|
||||
|
||||
/**
|
||||
* Coin's blinding factor.
|
||||
*/
|
||||
coin_blind_key_secret: string;
|
||||
|
||||
/**
|
||||
* Signature of TALER_RecoupRefreshRequestPS created with
|
||||
* the coin's private key.
|
||||
*/
|
||||
coin_sig: string;
|
||||
|
||||
/**
|
||||
* Amount being recouped.
|
||||
*/
|
||||
amount: AmountString;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1131,7 +1154,8 @@ export const codecForLegacyRsaDenominationPubKey = () =>
|
||||
.property("rsa_public_key", codecForString())
|
||||
.build("LegacyRsaDenominationPubKey");
|
||||
|
||||
export const codecForBankWithdrawalOperationPostResponse = (): Codec<BankWithdrawalOperationPostResponse> =>
|
||||
export const codecForBankWithdrawalOperationPostResponse =
|
||||
(): Codec<BankWithdrawalOperationPostResponse> =>
|
||||
buildCodecForObject<BankWithdrawalOperationPostResponse>()
|
||||
.property("transfer_done", codecForBoolean())
|
||||
.build("BankWithdrawalOperationPostResponse");
|
||||
@ -1213,8 +1237,8 @@ export const codecForTax = (): Codec<Tax> =>
|
||||
.property("tax", codecForString())
|
||||
.build("Tax");
|
||||
|
||||
export const codecForInternationalizedString = (): Codec<InternationalizedString> =>
|
||||
codecForMap(codecForString());
|
||||
export const codecForInternationalizedString =
|
||||
(): Codec<InternationalizedString> => codecForMap(codecForString());
|
||||
|
||||
export const codecForProduct = (): Codec<Product> =>
|
||||
buildCodecForObject<Product>()
|
||||
@ -1262,7 +1286,8 @@ export const codecForContractTerms = (): Codec<ContractTerms> =>
|
||||
.property("extra", codecForAny())
|
||||
.build("ContractTerms");
|
||||
|
||||
export const codecForMerchantRefundPermission = (): Codec<MerchantAbortPayRefundDetails> =>
|
||||
export const codecForMerchantRefundPermission =
|
||||
(): Codec<MerchantAbortPayRefundDetails> =>
|
||||
buildCodecForObject<MerchantAbortPayRefundDetails>()
|
||||
.property("refund_amount", codecForAmountString())
|
||||
.property("refund_fee", codecForAmountString())
|
||||
@ -1275,14 +1300,16 @@ export const codecForMerchantRefundPermission = (): Codec<MerchantAbortPayRefund
|
||||
.property("exchange_pub", codecOptional(codecForString()))
|
||||
.build("MerchantRefundPermission");
|
||||
|
||||
export const codecForMerchantRefundResponse = (): Codec<MerchantRefundResponse> =>
|
||||
export const codecForMerchantRefundResponse =
|
||||
(): Codec<MerchantRefundResponse> =>
|
||||
buildCodecForObject<MerchantRefundResponse>()
|
||||
.property("merchant_pub", codecForString())
|
||||
.property("h_contract_terms", codecForString())
|
||||
.property("refunds", codecForList(codecForMerchantRefundPermission()))
|
||||
.build("MerchantRefundResponse");
|
||||
|
||||
export const codecForMerchantBlindSigWrapperV1 = (): Codec<MerchantBlindSigWrapperV1> =>
|
||||
export const codecForMerchantBlindSigWrapperV1 =
|
||||
(): Codec<MerchantBlindSigWrapperV1> =>
|
||||
buildCodecForObject<MerchantBlindSigWrapperV1>()
|
||||
.property("blind_sig", codecForString())
|
||||
.build("BlindSigWrapper");
|
||||
@ -1365,7 +1392,8 @@ export const codecForCheckPaymentResponse = (): Codec<CheckPaymentResponse> =>
|
||||
.property("contract_url", codecOptional(codecForString()))
|
||||
.build("CheckPaymentResponse");
|
||||
|
||||
export const codecForWithdrawOperationStatusResponse = (): Codec<WithdrawOperationStatusResponse> =>
|
||||
export const codecForWithdrawOperationStatusResponse =
|
||||
(): Codec<WithdrawOperationStatusResponse> =>
|
||||
buildCodecForObject<WithdrawOperationStatusResponse>()
|
||||
.property("selection_done", codecForBoolean())
|
||||
.property("transfer_done", codecForBoolean())
|
||||
@ -1419,12 +1447,14 @@ export const codecForExchangeRevealItem = (): Codec<ExchangeRevealItem> =>
|
||||
)
|
||||
.build("ExchangeRevealItem");
|
||||
|
||||
export const codecForExchangeRevealResponse = (): Codec<ExchangeRevealResponse> =>
|
||||
export const codecForExchangeRevealResponse =
|
||||
(): Codec<ExchangeRevealResponse> =>
|
||||
buildCodecForObject<ExchangeRevealResponse>()
|
||||
.property("ev_sigs", codecForList(codecForExchangeRevealItem()))
|
||||
.build("ExchangeRevealResponse");
|
||||
|
||||
export const codecForMerchantCoinRefundSuccessStatus = (): Codec<MerchantCoinRefundSuccessStatus> =>
|
||||
export const codecForMerchantCoinRefundSuccessStatus =
|
||||
(): Codec<MerchantCoinRefundSuccessStatus> =>
|
||||
buildCodecForObject<MerchantCoinRefundSuccessStatus>()
|
||||
.property("type", codecForConstString("success"))
|
||||
.property("coin_pub", codecForString())
|
||||
@ -1436,7 +1466,8 @@ export const codecForMerchantCoinRefundSuccessStatus = (): Codec<MerchantCoinRef
|
||||
.property("execution_time", codecForTimestamp)
|
||||
.build("MerchantCoinRefundSuccessStatus");
|
||||
|
||||
export const codecForMerchantCoinRefundFailureStatus = (): Codec<MerchantCoinRefundFailureStatus> =>
|
||||
export const codecForMerchantCoinRefundFailureStatus =
|
||||
(): Codec<MerchantCoinRefundFailureStatus> =>
|
||||
buildCodecForObject<MerchantCoinRefundFailureStatus>()
|
||||
.property("type", codecForConstString("failure"))
|
||||
.property("coin_pub", codecForString())
|
||||
@ -1448,27 +1479,31 @@ export const codecForMerchantCoinRefundFailureStatus = (): Codec<MerchantCoinRef
|
||||
.property("execution_time", codecForTimestamp)
|
||||
.build("MerchantCoinRefundFailureStatus");
|
||||
|
||||
export const codecForMerchantCoinRefundStatus = (): Codec<MerchantCoinRefundStatus> =>
|
||||
export const codecForMerchantCoinRefundStatus =
|
||||
(): Codec<MerchantCoinRefundStatus> =>
|
||||
buildCodecForUnion<MerchantCoinRefundStatus>()
|
||||
.discriminateOn("type")
|
||||
.alternative("success", codecForMerchantCoinRefundSuccessStatus())
|
||||
.alternative("failure", codecForMerchantCoinRefundFailureStatus())
|
||||
.build("MerchantCoinRefundStatus");
|
||||
|
||||
export const codecForMerchantOrderStatusPaid = (): Codec<MerchantOrderStatusPaid> =>
|
||||
export const codecForMerchantOrderStatusPaid =
|
||||
(): Codec<MerchantOrderStatusPaid> =>
|
||||
buildCodecForObject<MerchantOrderStatusPaid>()
|
||||
.property("refund_amount", codecForString())
|
||||
.property("refunded", codecForBoolean())
|
||||
.build("MerchantOrderStatusPaid");
|
||||
|
||||
export const codecForMerchantOrderRefundPickupResponse = (): Codec<MerchantOrderRefundResponse> =>
|
||||
export const codecForMerchantOrderRefundPickupResponse =
|
||||
(): Codec<MerchantOrderRefundResponse> =>
|
||||
buildCodecForObject<MerchantOrderRefundResponse>()
|
||||
.property("merchant_pub", codecForString())
|
||||
.property("refund_amount", codecForString())
|
||||
.property("refunds", codecForList(codecForMerchantCoinRefundStatus()))
|
||||
.build("MerchantOrderRefundPickupResponse");
|
||||
|
||||
export const codecForMerchantOrderStatusUnpaid = (): Codec<MerchantOrderStatusUnpaid> =>
|
||||
export const codecForMerchantOrderStatusUnpaid =
|
||||
(): Codec<MerchantOrderStatusUnpaid> =>
|
||||
buildCodecForObject<MerchantOrderStatusUnpaid>()
|
||||
.property("taler_pay_uri", codecForString())
|
||||
.property("already_paid_order_id", codecOptional(codecForString()))
|
||||
@ -1550,7 +1585,8 @@ export interface MerchantAbortPayRefundSuccessStatus {
|
||||
exchange_pub: string;
|
||||
}
|
||||
|
||||
export const codecForMerchantAbortPayRefundSuccessStatus = (): Codec<MerchantAbortPayRefundSuccessStatus> =>
|
||||
export const codecForMerchantAbortPayRefundSuccessStatus =
|
||||
(): Codec<MerchantAbortPayRefundSuccessStatus> =>
|
||||
buildCodecForObject<MerchantAbortPayRefundSuccessStatus>()
|
||||
.property("exchange_pub", codecForString())
|
||||
.property("exchange_sig", codecForString())
|
||||
@ -1558,7 +1594,8 @@ export const codecForMerchantAbortPayRefundSuccessStatus = (): Codec<MerchantAbo
|
||||
.property("type", codecForConstString("success"))
|
||||
.build("MerchantAbortPayRefundSuccessStatus");
|
||||
|
||||
export const codecForMerchantAbortPayRefundFailureStatus = (): Codec<MerchantAbortPayRefundFailureStatus> =>
|
||||
export const codecForMerchantAbortPayRefundFailureStatus =
|
||||
(): Codec<MerchantAbortPayRefundFailureStatus> =>
|
||||
buildCodecForObject<MerchantAbortPayRefundFailureStatus>()
|
||||
.property("exchange_code", codecForNumber())
|
||||
.property("exchange_reply", codecForAny())
|
||||
@ -1566,7 +1603,8 @@ export const codecForMerchantAbortPayRefundFailureStatus = (): Codec<MerchantAbo
|
||||
.property("type", codecForConstString("failure"))
|
||||
.build("MerchantAbortPayRefundFailureStatus");
|
||||
|
||||
export const codecForMerchantAbortPayRefundStatus = (): Codec<MerchantAbortPayRefundStatus> =>
|
||||
export const codecForMerchantAbortPayRefundStatus =
|
||||
(): Codec<MerchantAbortPayRefundStatus> =>
|
||||
buildCodecForUnion<MerchantAbortPayRefundStatus>()
|
||||
.discriminateOn("type")
|
||||
.alternative("success", codecForMerchantAbortPayRefundSuccessStatus())
|
||||
@ -1614,14 +1652,14 @@ export interface MerchantConfigResponse {
|
||||
version: string;
|
||||
}
|
||||
|
||||
export const codecForMerchantConfigResponse = (): Codec<MerchantConfigResponse> =>
|
||||
export const codecForMerchantConfigResponse =
|
||||
(): Codec<MerchantConfigResponse> =>
|
||||
buildCodecForObject<MerchantConfigResponse>()
|
||||
.property("currency", codecForString())
|
||||
.property("name", codecForString())
|
||||
.property("version", codecForString())
|
||||
.build("MerchantConfigResponse");
|
||||
|
||||
|
||||
export enum ExchangeProtocolVersion {
|
||||
V9 = 9,
|
||||
V12 = 12,
|
||||
|
@ -2031,9 +2031,9 @@ export class WalletCli {
|
||||
`wallet-${self.name}`,
|
||||
`taler-wallet-cli ${
|
||||
self.timetravelArg ?? ""
|
||||
} --no-throttle --wallet-db '${self.dbfile}' api '${op}' ${shellWrap(
|
||||
JSON.stringify(payload),
|
||||
)}`,
|
||||
} --no-throttle -LTRACE --wallet-db '${
|
||||
self.dbfile
|
||||
}' api '${op}' ${shellWrap(JSON.stringify(payload))}`,
|
||||
);
|
||||
console.log("--- wallet core response ---");
|
||||
console.log(resp);
|
||||
@ -2080,6 +2080,7 @@ export class WalletCli {
|
||||
[
|
||||
"--no-throttle",
|
||||
...this.timetravelArgArr,
|
||||
"-LTRACE",
|
||||
"--wallet-db",
|
||||
this.dbfile,
|
||||
"run-until-done",
|
||||
@ -2095,6 +2096,7 @@ export class WalletCli {
|
||||
"taler-wallet-cli",
|
||||
[
|
||||
"--no-throttle",
|
||||
"-LTRACE",
|
||||
...this.timetravelArgArr,
|
||||
"--wallet-db",
|
||||
this.dbfile,
|
||||
|
@ -249,6 +249,7 @@ walletCli
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
let requestJson;
|
||||
logger.info(`handling 'api' request (${args.api.operation})`);
|
||||
try {
|
||||
requestJson = JSON.parse(args.api.request);
|
||||
} catch (e) {
|
||||
@ -293,12 +294,6 @@ walletCli
|
||||
});
|
||||
});
|
||||
|
||||
async function asyncSleep(milliSeconds: number): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
setTimeout(() => resolve(), milliSeconds);
|
||||
});
|
||||
}
|
||||
|
||||
walletCli
|
||||
.subcommand("runPendingOpt", "run-pending", {
|
||||
help: "Run pending operations.",
|
||||
@ -330,6 +325,7 @@ walletCli
|
||||
.maybeOption("maxRetries", ["--max-retries"], clk.INT)
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
logger.info("running until pending operations are finished");
|
||||
await wallet.ws.runTaskLoop({
|
||||
maxRetries: args.finishPendingOpt.maxRetries,
|
||||
stopWhenDone: true,
|
||||
|
@ -27,7 +27,13 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { AmountJson, DenominationPubKey, ExchangeProtocolVersion } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
AmountJson,
|
||||
AmountString,
|
||||
DenominationPubKey,
|
||||
ExchangeProtocolVersion,
|
||||
UnblindedSignature,
|
||||
} from "@gnu-taler/taler-util";
|
||||
|
||||
export interface RefreshNewDenomInfo {
|
||||
count: number;
|
||||
@ -140,3 +146,29 @@ export interface SignTrackTransactionRequest {
|
||||
merchantPriv: string;
|
||||
merchantPub: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to create a recoup request payload.
|
||||
*/
|
||||
export interface CreateRecoupReqRequest {
|
||||
coinPub: string;
|
||||
coinPriv: string;
|
||||
blindingKey: string;
|
||||
denomPub: DenominationPubKey;
|
||||
denomPubHash: string;
|
||||
denomSig: UnblindedSignature;
|
||||
recoupAmount: AmountJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to create a recoup-refresh request payload.
|
||||
*/
|
||||
export interface CreateRecoupRefreshReqRequest {
|
||||
coinPub: string;
|
||||
coinPriv: string;
|
||||
blindingKey: string;
|
||||
denomPub: DenominationPubKey;
|
||||
denomPubHash: string;
|
||||
denomSig: UnblindedSignature;
|
||||
recoupAmount: AmountJson;
|
||||
}
|
||||
|
@ -26,7 +26,11 @@ import { CoinRecord, DenominationRecord, WireFee } from "../../db.js";
|
||||
|
||||
import { CryptoWorker } from "./cryptoWorkerInterface.js";
|
||||
|
||||
import { RecoupRequest, CoinDepositPermission } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
CoinDepositPermission,
|
||||
RecoupRefreshRequest,
|
||||
RecoupRequest,
|
||||
} from "@gnu-taler/taler-util";
|
||||
|
||||
import {
|
||||
BenchmarkResult,
|
||||
@ -39,6 +43,8 @@ import {
|
||||
import * as timer from "../../util/timer.js";
|
||||
import { Logger } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
CreateRecoupRefreshReqRequest,
|
||||
CreateRecoupReqRequest,
|
||||
DerivedRefreshSession,
|
||||
DerivedTipPlanchet,
|
||||
DeriveRefreshSessionRequest,
|
||||
@ -421,8 +427,18 @@ export class CryptoApi {
|
||||
);
|
||||
}
|
||||
|
||||
createRecoupRequest(coin: CoinRecord): Promise<RecoupRequest> {
|
||||
return this.doRpc<RecoupRequest>("createRecoupRequest", 1, coin);
|
||||
createRecoupRequest(req: CreateRecoupReqRequest): Promise<RecoupRequest> {
|
||||
return this.doRpc<RecoupRequest>("createRecoupRequest", 1, req);
|
||||
}
|
||||
|
||||
createRecoupRefreshRequest(
|
||||
req: CreateRecoupRefreshReqRequest,
|
||||
): Promise<RecoupRefreshRequest> {
|
||||
return this.doRpc<RecoupRefreshRequest>(
|
||||
"createRecoupRefreshRequest",
|
||||
1,
|
||||
req,
|
||||
);
|
||||
}
|
||||
|
||||
deriveRefreshSession(
|
||||
|
@ -25,12 +25,7 @@
|
||||
*/
|
||||
|
||||
// FIXME: Crypto should not use DB Types!
|
||||
import {
|
||||
CoinRecord,
|
||||
DenominationRecord,
|
||||
WireFee,
|
||||
CoinSourceType,
|
||||
} from "../../db.js";
|
||||
import { DenominationRecord, WireFee } from "../../db.js";
|
||||
|
||||
import {
|
||||
buildSigPS,
|
||||
@ -39,6 +34,7 @@ import {
|
||||
ExchangeProtocolVersion,
|
||||
FreshCoin,
|
||||
hashDenomPub,
|
||||
RecoupRefreshRequest,
|
||||
RecoupRequest,
|
||||
RefreshPlanchetInfo,
|
||||
TalerSignaturePurpose,
|
||||
@ -78,6 +74,8 @@ import { Timestamp, timestampTruncateToSecond } from "@gnu-taler/taler-util";
|
||||
|
||||
import { Logger } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
CreateRecoupRefreshReqRequest,
|
||||
CreateRecoupReqRequest,
|
||||
DerivedRefreshSession,
|
||||
DerivedTipPlanchet,
|
||||
DeriveRefreshSessionRequest,
|
||||
@ -261,33 +259,64 @@ export class CryptoImplementation {
|
||||
/**
|
||||
* Create and sign a message to recoup a coin.
|
||||
*/
|
||||
createRecoupRequest(coin: CoinRecord): RecoupRequest {
|
||||
createRecoupRequest(req: CreateRecoupReqRequest): RecoupRequest {
|
||||
const p = buildSigPS(TalerSignaturePurpose.WALLET_COIN_RECOUP)
|
||||
.put(decodeCrock(coin.coinPub))
|
||||
.put(decodeCrock(coin.denomPubHash))
|
||||
.put(decodeCrock(coin.blindingKey))
|
||||
.put(decodeCrock(req.denomPubHash))
|
||||
.put(decodeCrock(req.blindingKey))
|
||||
.put(amountToBuffer(Amounts.jsonifyAmount(req.recoupAmount)))
|
||||
.build();
|
||||
|
||||
const coinPriv = decodeCrock(coin.coinPriv);
|
||||
const coinPriv = decodeCrock(req.coinPriv);
|
||||
const coinSig = eddsaSign(p, coinPriv);
|
||||
if (coin.denomPub.cipher === DenomKeyType.LegacyRsa) {
|
||||
if (req.denomPub.cipher === DenomKeyType.LegacyRsa) {
|
||||
const paybackRequest: RecoupRequest = {
|
||||
coin_blind_key_secret: coin.blindingKey,
|
||||
coin_pub: coin.coinPub,
|
||||
coin_blind_key_secret: req.blindingKey,
|
||||
coin_sig: encodeCrock(coinSig),
|
||||
denom_pub_hash: coin.denomPubHash,
|
||||
denom_sig: coin.denomSig.rsa_signature,
|
||||
refreshed: coin.coinSource.type === CoinSourceType.Refresh,
|
||||
denom_pub_hash: req.denomPubHash,
|
||||
denom_sig: req.denomSig.rsa_signature,
|
||||
amount: Amounts.stringify(req.recoupAmount),
|
||||
};
|
||||
return paybackRequest;
|
||||
} else {
|
||||
const paybackRequest: RecoupRequest = {
|
||||
coin_blind_key_secret: coin.blindingKey,
|
||||
coin_pub: coin.coinPub,
|
||||
coin_blind_key_secret: req.blindingKey,
|
||||
coin_sig: encodeCrock(coinSig),
|
||||
denom_pub_hash: coin.denomPubHash,
|
||||
denom_sig: coin.denomSig,
|
||||
refreshed: coin.coinSource.type === CoinSourceType.Refresh,
|
||||
denom_pub_hash: req.denomPubHash,
|
||||
denom_sig: req.denomSig,
|
||||
amount: Amounts.stringify(req.recoupAmount),
|
||||
};
|
||||
return paybackRequest;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and sign a message to recoup a coin.
|
||||
*/
|
||||
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 = {
|
||||
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;
|
||||
} else {
|
||||
const paybackRequest: 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;
|
||||
}
|
||||
|
@ -651,7 +651,7 @@ async function updateExchangeFromUrlImpl(
|
||||
logger.trace("denom already revoked");
|
||||
continue;
|
||||
}
|
||||
logger.trace("revoking denom", recoupInfo.h_denom_pub);
|
||||
logger.info("revoking denom", recoupInfo.h_denom_pub);
|
||||
oldDenom.isRevoked = true;
|
||||
await tx.denominations.put(oldDenom);
|
||||
const affectedCoins = await tx.coins.indexes.byDenomPubHash
|
||||
@ -662,7 +662,7 @@ async function updateExchangeFromUrlImpl(
|
||||
}
|
||||
}
|
||||
if (newlyRevokedCoinPubs.length != 0) {
|
||||
logger.trace("recouping coins", newlyRevokedCoinPubs);
|
||||
logger.info("recouping coins", newlyRevokedCoinPubs);
|
||||
recoupGroupId = await ws.recoupOps.createRecoupGroup(
|
||||
ws,
|
||||
tx,
|
||||
|
@ -28,6 +28,7 @@ import {
|
||||
Amounts,
|
||||
codecForRecoupConfirmation,
|
||||
getTimestampNow,
|
||||
j2s,
|
||||
NotificationType,
|
||||
RefreshReason,
|
||||
TalerErrorDetails,
|
||||
@ -107,7 +108,7 @@ async function putGroupAsFinished(
|
||||
}
|
||||
}
|
||||
if (allFinished) {
|
||||
logger.trace("all recoups of recoup group are finished");
|
||||
logger.info("all recoups of recoup group are finished");
|
||||
recoupGroup.timestampFinished = getTimestampNow();
|
||||
recoupGroup.retryInfo = initRetryInfo();
|
||||
recoupGroup.lastError = undefined;
|
||||
@ -178,8 +179,17 @@ async function recoupWithdrawCoin(
|
||||
type: NotificationType.RecoupStarted,
|
||||
});
|
||||
|
||||
const recoupRequest = await ws.cryptoApi.createRecoupRequest(coin);
|
||||
const recoupRequest = await ws.cryptoApi.createRecoupRequest({
|
||||
blindingKey: coin.blindingKey,
|
||||
coinPriv: coin.coinPriv,
|
||||
coinPub: coin.coinPub,
|
||||
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}`);
|
||||
const resp = await ws.http.postJson(reqUrl.href, recoupRequest, {
|
||||
timeout: getReserveRequestTimeout(reserve),
|
||||
});
|
||||
@ -188,6 +198,8 @@ async function recoupWithdrawCoin(
|
||||
codecForRecoupConfirmation(),
|
||||
);
|
||||
|
||||
logger.trace(`got recoup confirmation ${j2s(recoupConfirmation)}`);
|
||||
|
||||
if (recoupConfirmation.reserve_pub !== reservePub) {
|
||||
throw Error(`Coin's reserve doesn't match reserve on recoup`);
|
||||
}
|
||||
@ -249,7 +261,15 @@ async function recoupRefreshCoin(
|
||||
type: NotificationType.RecoupStarted,
|
||||
});
|
||||
|
||||
const recoupRequest = await ws.cryptoApi.createRecoupRequest(coin);
|
||||
const recoupRequest = await ws.cryptoApi.createRecoupRefreshRequest({
|
||||
blindingKey: coin.blindingKey,
|
||||
coinPriv: coin.coinPriv,
|
||||
coinPub: coin.coinPub,
|
||||
denomPub: coin.denomPub,
|
||||
denomPubHash: coin.denomPubHash,
|
||||
denomSig: coin.denomSig,
|
||||
recoupAmount: coin.currentAmount,
|
||||
});
|
||||
const reqUrl = new URL(`/coins/${coin.coinPub}/recoup`, coin.exchangeBaseUrl);
|
||||
logger.trace(`making recoup request for ${coin.coinPub}`);
|
||||
|
||||
@ -359,9 +379,14 @@ async function processRecoupGroupImpl(
|
||||
logger.trace("recoup group finished");
|
||||
return;
|
||||
}
|
||||
const ps = recoupGroup.coinPubs.map((x, i) =>
|
||||
processRecoup(ws, recoupGroupId, i),
|
||||
);
|
||||
const ps = recoupGroup.coinPubs.map(async (x, i) => {
|
||||
try {
|
||||
processRecoup(ws, recoupGroupId, i);
|
||||
} catch (e) {
|
||||
logger.warn(`processRecoup failed: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
await Promise.all(ps);
|
||||
|
||||
const reserveSet = new Set<string>();
|
||||
|
@ -30,6 +30,7 @@ import {
|
||||
encodeCrock,
|
||||
getRandomBytes,
|
||||
getTimestampNow,
|
||||
j2s,
|
||||
Logger,
|
||||
NotificationType,
|
||||
randomBytes,
|
||||
@ -538,6 +539,7 @@ async function updateReserve(
|
||||
resp,
|
||||
codecForReserveStatus(),
|
||||
);
|
||||
|
||||
if (result.isError) {
|
||||
if (
|
||||
resp.status === 404 &&
|
||||
@ -555,6 +557,8 @@ async function updateReserve(
|
||||
}
|
||||
}
|
||||
|
||||
logger.trace(`got reserve status ${j2s(result.response)}`);
|
||||
|
||||
const reserveInfo = result.response;
|
||||
const balance = Amounts.parseOrThrow(reserveInfo.balance);
|
||||
const currency = balance.currency;
|
||||
@ -635,8 +639,10 @@ async function updateReserve(
|
||||
}
|
||||
}
|
||||
|
||||
const remainingAmount = Amounts.sub(amountReservePlus, amountReserveMinus)
|
||||
.amount;
|
||||
const remainingAmount = Amounts.sub(
|
||||
amountReservePlus,
|
||||
amountReserveMinus,
|
||||
).amount;
|
||||
const denomSelInfo = selectWithdrawalDenominations(
|
||||
remainingAmount,
|
||||
denoms,
|
||||
|
Loading…
Reference in New Issue
Block a user