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