fix tipping and adjust DB
This commit is contained in:
parent
b063382d25
commit
b9e43e652e
@ -94,13 +94,19 @@ runTest(async (t: GlobalTestState) => {
|
||||
|
||||
console.log(ptr);
|
||||
|
||||
t.assertAmountEquals(ptr.tipAmountRaw, "TESTKUDOS:5");
|
||||
t.assertAmountEquals(ptr.tipAmountEffective, "TESTKUDOS:4.85");
|
||||
|
||||
await wallet.acceptTip({
|
||||
walletTipId: ptr.walletTipId,
|
||||
});
|
||||
|
||||
|
||||
await wallet.runUntilDone();
|
||||
|
||||
const bal = await wallet.getBalances();
|
||||
|
||||
console.log(bal);
|
||||
|
||||
t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:4.85");
|
||||
});
|
||||
|
@ -22,6 +22,8 @@
|
||||
*/
|
||||
|
||||
export enum TalerErrorCode {
|
||||
|
||||
|
||||
/**
|
||||
* Special code to indicate no error (or no "code" present).
|
||||
* Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
|
||||
@ -3270,10 +3272,18 @@ export enum TalerErrorCode {
|
||||
*/
|
||||
WALLET_WITHDRAWAL_GROUP_INCOMPLETE = 7015,
|
||||
|
||||
/**
|
||||
* The signature on a coin by the exchange's denomination key (obtained through the merchant via tipping) is invalid after unblinding it.
|
||||
* Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
|
||||
* (A value of 0 indicates that the error is generated client-side).
|
||||
*/
|
||||
WALLET_TIPPING_COIN_SIGNATURE_INVALID = 7016,
|
||||
|
||||
/**
|
||||
* End of error code range.
|
||||
* Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
|
||||
* (A value of 0 indicates that the error is generated client-side).
|
||||
*/
|
||||
END = 9999,
|
||||
|
||||
}
|
||||
|
@ -286,7 +286,6 @@ async function gatherWithdrawalPending(
|
||||
givesLifeness: true,
|
||||
numCoinsTotal,
|
||||
numCoinsWithdrawn,
|
||||
source: wsr.source,
|
||||
withdrawalGroupId: wsr.withdrawalGroupId,
|
||||
lastError: wsr.lastError,
|
||||
retryInfo: wsr.retryInfo,
|
||||
|
@ -818,10 +818,7 @@ async function depleteReserve(
|
||||
const withdrawalRecord: WithdrawalGroupRecord = {
|
||||
withdrawalGroupId: withdrawalGroupId,
|
||||
exchangeBaseUrl: newReserve.exchangeBaseUrl,
|
||||
source: {
|
||||
type: WithdrawalSourceType.Reserve,
|
||||
reservePub: newReserve.reservePub,
|
||||
},
|
||||
reservePub: newReserve.reservePub,
|
||||
rawWithdrawalAmount: withdrawAmount,
|
||||
timestampStart: getTimestampNow(),
|
||||
retryInfo: initRetryInfo(),
|
||||
|
@ -31,6 +31,9 @@ import {
|
||||
updateRetryInfoTimeout,
|
||||
WithdrawalSourceType,
|
||||
TipPlanchet,
|
||||
CoinRecord,
|
||||
CoinSourceType,
|
||||
CoinStatus,
|
||||
} from "../types/dbTypes";
|
||||
import {
|
||||
getExchangeWithdrawalInfo,
|
||||
@ -40,13 +43,14 @@ import {
|
||||
} from "./withdraw";
|
||||
import { updateExchangeFromUrl } from "./exchanges";
|
||||
import { getRandomBytes, encodeCrock } from "../crypto/talerCrypto";
|
||||
import { guardOperationException } from "./errors";
|
||||
import { guardOperationException, makeErrorDetails } from "./errors";
|
||||
import { NotificationType } from "../types/notifications";
|
||||
import { getTimestampNow } from "../util/time";
|
||||
import { readSuccessResponseJsonOrThrow } from "../util/http";
|
||||
import { URL } from "../util/url";
|
||||
import { Logger } from "../util/logging";
|
||||
import { checkDbInvariant } from "../util/invariants";
|
||||
import { TalerErrorCode } from "../TalerErrorCode";
|
||||
|
||||
const logger = new Logger("operations/tip.ts");
|
||||
|
||||
@ -99,7 +103,7 @@ export async function prepareTip(
|
||||
walletTipId: walletTipId,
|
||||
acceptedTimestamp: undefined,
|
||||
rejectedTimestamp: undefined,
|
||||
amount,
|
||||
tipAmountRaw: amount,
|
||||
deadline: tipPickupStatus.expiration,
|
||||
exchangeUrl: tipPickupStatus.exchange_url,
|
||||
merchantBaseUrl: res.merchantBaseUrl,
|
||||
@ -109,10 +113,10 @@ export async function prepareTip(
|
||||
response: undefined,
|
||||
createdTimestamp: getTimestampNow(),
|
||||
merchantTipId: res.merchantTipId,
|
||||
totalFees: Amounts.add(
|
||||
tipAmountEffective: Amounts.sub(amount, Amounts.add(
|
||||
withdrawDetails.overhead,
|
||||
withdrawDetails.withdrawFee,
|
||||
).amount,
|
||||
).amount).amount,
|
||||
retryInfo: initRetryInfo(),
|
||||
lastError: undefined,
|
||||
denomsSel: denomSelectionInfoToState(selectedDenoms),
|
||||
@ -122,10 +126,10 @@ export async function prepareTip(
|
||||
|
||||
const tipStatus: PrepareTipResult = {
|
||||
accepted: !!tipRecord && !!tipRecord.acceptedTimestamp,
|
||||
amount: Amounts.stringify(tipPickupStatus.tip_amount),
|
||||
tipAmountRaw: Amounts.stringify(tipPickupStatus.tip_amount),
|
||||
exchangeBaseUrl: tipPickupStatus.exchange_url,
|
||||
expirationTimestamp: tipPickupStatus.expiration,
|
||||
totalFees: Amounts.stringify(tipRecord.totalFees),
|
||||
tipAmountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
|
||||
walletTipId: tipRecord.walletTipId,
|
||||
};
|
||||
|
||||
@ -182,13 +186,13 @@ async function resetTipRetry(
|
||||
|
||||
async function processTipImpl(
|
||||
ws: InternalWalletState,
|
||||
tipId: string,
|
||||
walletTipId: string,
|
||||
forceNow: boolean,
|
||||
): Promise<void> {
|
||||
if (forceNow) {
|
||||
await resetTipRetry(ws, tipId);
|
||||
await resetTipRetry(ws, walletTipId);
|
||||
}
|
||||
let tipRecord = await ws.db.get(Stores.tips, tipId);
|
||||
let tipRecord = await ws.db.get(Stores.tips, walletTipId);
|
||||
if (!tipRecord) {
|
||||
return;
|
||||
}
|
||||
@ -216,7 +220,7 @@ async function processTipImpl(
|
||||
planchets.push(r);
|
||||
}
|
||||
}
|
||||
await ws.db.mutate(Stores.tips, tipId, (r) => {
|
||||
await ws.db.mutate(Stores.tips, walletTipId, (r) => {
|
||||
if (!r.planchets) {
|
||||
r.planchets = planchets;
|
||||
}
|
||||
@ -224,7 +228,7 @@ async function processTipImpl(
|
||||
});
|
||||
}
|
||||
|
||||
tipRecord = await ws.db.get(Stores.tips, tipId);
|
||||
tipRecord = await ws.db.get(Stores.tips, walletTipId);
|
||||
checkDbInvariant(!!tipRecord, "tip record should be in database");
|
||||
checkDbInvariant(!!tipRecord.planchets, "tip record should have planchets");
|
||||
|
||||
@ -246,55 +250,68 @@ async function processTipImpl(
|
||||
codecForTipResponse(),
|
||||
);
|
||||
|
||||
if (response.reserve_sigs.length !== tipRecord.planchets.length) {
|
||||
if (response.blind_sigs.length !== tipRecord.planchets.length) {
|
||||
throw Error("number of tip responses does not match requested planchets");
|
||||
}
|
||||
|
||||
const withdrawalGroupId = encodeCrock(getRandomBytes(32));
|
||||
const planchets: PlanchetRecord[] = [];
|
||||
const newCoinRecords: CoinRecord[] = [];
|
||||
|
||||
for (let i = 0; i < tipRecord.planchets.length; i++) {
|
||||
const tipPlanchet = tipRecord.planchets[i];
|
||||
const coinEvHash = await ws.cryptoApi.hashEncoded(tipPlanchet.coinEv);
|
||||
const planchet: PlanchetRecord = {
|
||||
blindingKey: tipPlanchet.blindingKey,
|
||||
coinEv: tipPlanchet.coinEv,
|
||||
coinPriv: tipPlanchet.coinPriv,
|
||||
coinPub: tipPlanchet.coinPub,
|
||||
coinValue: tipPlanchet.coinValue,
|
||||
denomPub: tipPlanchet.denomPub,
|
||||
denomPubHash: tipPlanchet.denomPubHash,
|
||||
reservePub: response.reserve_pub,
|
||||
withdrawSig: response.reserve_sigs[i].reserve_sig,
|
||||
isFromTip: true,
|
||||
coinEvHash,
|
||||
coinIdx: i,
|
||||
withdrawalDone: false,
|
||||
withdrawalGroupId: withdrawalGroupId,
|
||||
lastError: undefined,
|
||||
};
|
||||
planchets.push(planchet);
|
||||
for (let i = 0; i < response.blind_sigs.length; i++) {
|
||||
const blindedSig = response.blind_sigs[i].blind_sig;
|
||||
|
||||
const planchet = tipRecord.planchets[i];
|
||||
|
||||
const denomSig = await ws.cryptoApi.rsaUnblind(
|
||||
blindedSig,
|
||||
planchet.blindingKey,
|
||||
planchet.denomPub,
|
||||
);
|
||||
|
||||
const isValid = await ws.cryptoApi.rsaVerify(
|
||||
planchet.coinPub,
|
||||
denomSig,
|
||||
planchet.denomPub,
|
||||
);
|
||||
|
||||
if (!isValid) {
|
||||
await ws.db.runWithWriteTransaction([Stores.planchets], async (tx) => {
|
||||
const tipRecord = await tx.get(Stores.tips, walletTipId);
|
||||
if (!tipRecord) {
|
||||
return;
|
||||
}
|
||||
tipRecord.lastError = makeErrorDetails(
|
||||
TalerErrorCode.WALLET_TIPPING_COIN_SIGNATURE_INVALID,
|
||||
"invalid signature from the exchange (via merchant tip) after unblinding",
|
||||
{},
|
||||
);
|
||||
await tx.put(Stores.tips, tipRecord);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
newCoinRecords.push({
|
||||
blindingKey: planchet.blindingKey,
|
||||
coinPriv: planchet.coinPriv,
|
||||
coinPub: planchet.coinPub,
|
||||
coinSource: {
|
||||
type: CoinSourceType.Tip,
|
||||
coinIndex: i,
|
||||
walletTipId: walletTipId,
|
||||
},
|
||||
currentAmount: planchet.coinValue,
|
||||
denomPub: planchet.denomPub,
|
||||
denomPubHash: planchet.denomPubHash,
|
||||
denomSig: denomSig,
|
||||
exchangeBaseUrl: tipRecord.exchangeUrl,
|
||||
status: CoinStatus.Fresh,
|
||||
suspended: false,
|
||||
});
|
||||
}
|
||||
|
||||
const withdrawalGroup: WithdrawalGroupRecord = {
|
||||
exchangeBaseUrl: tipRecord.exchangeUrl,
|
||||
source: {
|
||||
type: WithdrawalSourceType.Tip,
|
||||
tipId: tipRecord.walletTipId,
|
||||
},
|
||||
timestampStart: getTimestampNow(),
|
||||
withdrawalGroupId: withdrawalGroupId,
|
||||
rawWithdrawalAmount: tipRecord.amount,
|
||||
retryInfo: initRetryInfo(),
|
||||
timestampFinish: undefined,
|
||||
lastError: undefined,
|
||||
denomsSel: tipRecord.denomsSel,
|
||||
};
|
||||
|
||||
await ws.db.runWithWriteTransaction(
|
||||
[Stores.tips, Stores.withdrawalGroups],
|
||||
[Stores.coins, Stores.tips, Stores.withdrawalGroups],
|
||||
async (tx) => {
|
||||
const tr = await tx.get(Stores.tips, tipId);
|
||||
const tr = await tx.get(Stores.tips, walletTipId);
|
||||
if (!tr) {
|
||||
return;
|
||||
}
|
||||
@ -303,16 +320,12 @@ async function processTipImpl(
|
||||
}
|
||||
tr.pickedUp = true;
|
||||
tr.retryInfo = initRetryInfo(false);
|
||||
|
||||
await tx.put(Stores.tips, tr);
|
||||
await tx.put(Stores.withdrawalGroups, withdrawalGroup);
|
||||
for (const p of planchets) {
|
||||
await tx.put(Stores.planchets, p);
|
||||
for (const cr of newCoinRecords) {
|
||||
await tx.put(Stores.coins, cr);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
await processWithdrawGroup(ws, withdrawalGroupId);
|
||||
}
|
||||
|
||||
export async function acceptTip(
|
||||
|
@ -116,63 +116,49 @@ export async function getTransactions(
|
||||
return;
|
||||
}
|
||||
|
||||
switch (wsr.source.type) {
|
||||
case WithdrawalSourceType.Reserve:
|
||||
{
|
||||
const r = await tx.get(Stores.reserves, wsr.source.reservePub);
|
||||
if (!r) {
|
||||
break;
|
||||
}
|
||||
let amountRaw: AmountJson | undefined = undefined;
|
||||
if (wsr.withdrawalGroupId === r.initialWithdrawalGroupId) {
|
||||
amountRaw = r.instructedAmount;
|
||||
} else {
|
||||
amountRaw = wsr.denomsSel.totalWithdrawCost;
|
||||
}
|
||||
let withdrawalDetails: WithdrawalDetails;
|
||||
if (r.bankInfo) {
|
||||
withdrawalDetails = {
|
||||
type: WithdrawalType.TalerBankIntegrationApi,
|
||||
confirmed: true,
|
||||
bankConfirmationUrl: r.bankInfo.confirmUrl,
|
||||
};
|
||||
} else {
|
||||
const exchange = await tx.get(
|
||||
Stores.exchanges,
|
||||
r.exchangeBaseUrl,
|
||||
);
|
||||
if (!exchange) {
|
||||
// FIXME: report somehow
|
||||
break;
|
||||
}
|
||||
withdrawalDetails = {
|
||||
type: WithdrawalType.ManualTransfer,
|
||||
exchangePaytoUris:
|
||||
exchange.wireInfo?.accounts.map((x) => x.payto_uri) ?? [],
|
||||
};
|
||||
}
|
||||
transactions.push({
|
||||
type: TransactionType.Withdrawal,
|
||||
amountEffective: Amounts.stringify(
|
||||
wsr.denomsSel.totalCoinValue,
|
||||
),
|
||||
amountRaw: Amounts.stringify(amountRaw),
|
||||
withdrawalDetails,
|
||||
exchangeBaseUrl: wsr.exchangeBaseUrl,
|
||||
pending: !wsr.timestampFinish,
|
||||
timestamp: wsr.timestampStart,
|
||||
transactionId: makeEventId(
|
||||
TransactionType.Withdrawal,
|
||||
wsr.withdrawalGroupId,
|
||||
),
|
||||
...(wsr.lastError ? { error: wsr.lastError } : {}),
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Tips are reported via their own event
|
||||
break;
|
||||
const r = await tx.get(Stores.reserves, wsr.reservePub);
|
||||
if (!r) {
|
||||
return;
|
||||
}
|
||||
let amountRaw: AmountJson | undefined = undefined;
|
||||
if (wsr.withdrawalGroupId === r.initialWithdrawalGroupId) {
|
||||
amountRaw = r.instructedAmount;
|
||||
} else {
|
||||
amountRaw = wsr.denomsSel.totalWithdrawCost;
|
||||
}
|
||||
let withdrawalDetails: WithdrawalDetails;
|
||||
if (r.bankInfo) {
|
||||
withdrawalDetails = {
|
||||
type: WithdrawalType.TalerBankIntegrationApi,
|
||||
confirmed: true,
|
||||
bankConfirmationUrl: r.bankInfo.confirmUrl,
|
||||
};
|
||||
} else {
|
||||
const exchange = await tx.get(Stores.exchanges, r.exchangeBaseUrl);
|
||||
if (!exchange) {
|
||||
// FIXME: report somehow
|
||||
return;
|
||||
}
|
||||
withdrawalDetails = {
|
||||
type: WithdrawalType.ManualTransfer,
|
||||
exchangePaytoUris:
|
||||
exchange.wireInfo?.accounts.map((x) => x.payto_uri) ?? [],
|
||||
};
|
||||
}
|
||||
transactions.push({
|
||||
type: TransactionType.Withdrawal,
|
||||
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
|
||||
amountRaw: Amounts.stringify(amountRaw),
|
||||
withdrawalDetails,
|
||||
exchangeBaseUrl: wsr.exchangeBaseUrl,
|
||||
pending: !wsr.timestampFinish,
|
||||
timestamp: wsr.timestampStart,
|
||||
transactionId: makeEventId(
|
||||
TransactionType.Withdrawal,
|
||||
wsr.withdrawalGroupId,
|
||||
),
|
||||
...(wsr.lastError ? { error: wsr.lastError } : {}),
|
||||
});
|
||||
});
|
||||
|
||||
// Report pending withdrawals based on reserves that
|
||||
|
@ -242,12 +242,9 @@ async function processPlanchetGenerate(
|
||||
if (!denom) {
|
||||
throw Error("invariant violated");
|
||||
}
|
||||
if (withdrawalGroup.source.type != WithdrawalSourceType.Reserve) {
|
||||
throw Error("invariant violated");
|
||||
}
|
||||
const reserve = await ws.db.get(
|
||||
Stores.reserves,
|
||||
withdrawalGroup.source.reservePub,
|
||||
withdrawalGroup.reservePub,
|
||||
);
|
||||
if (!reserve) {
|
||||
throw Error("invariant violated");
|
||||
@ -420,7 +417,7 @@ async function processPlanchetVerifyAndStoreCoin(
|
||||
|
||||
if (!isValid) {
|
||||
await ws.db.runWithWriteTransaction([Stores.planchets], async (tx) => {
|
||||
let planchet = await ws.db.getIndexed(Stores.planchets.byGroupAndIndex, [
|
||||
let planchet = await tx.getIndexed(Stores.planchets.byGroupAndIndex, [
|
||||
withdrawalGroupId,
|
||||
coinIdx,
|
||||
]);
|
||||
@ -700,7 +697,7 @@ async function processWithdrawGroupImpl(
|
||||
if (finishedForFirstTime) {
|
||||
ws.notify({
|
||||
type: NotificationType.WithdrawGroupFinished,
|
||||
withdrawalSource: withdrawalGroup.source,
|
||||
reservePub: withdrawalGroup.reservePub,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -694,17 +694,28 @@ export interface PlanchetRecord {
|
||||
lastError: TalerErrorDetails | undefined;
|
||||
|
||||
/**
|
||||
* Public key of the reserve, this might be a reserve not
|
||||
* known to the wallet if the planchet is from a tip.
|
||||
* Public key of the reserve that this planchet
|
||||
* is being withdrawn from.
|
||||
*
|
||||
* Can be the empty string (non-null/undefined for DB indexing)
|
||||
* if this is a tipping reserve.
|
||||
*/
|
||||
reservePub: string;
|
||||
|
||||
denomPubHash: string;
|
||||
|
||||
denomPub: string;
|
||||
|
||||
blindingKey: string;
|
||||
|
||||
withdrawSig: string;
|
||||
|
||||
coinEv: string;
|
||||
|
||||
coinEvHash: string;
|
||||
|
||||
coinValue: AmountJson;
|
||||
|
||||
isFromTip: boolean;
|
||||
}
|
||||
|
||||
@ -772,6 +783,8 @@ export interface RefreshCoinSource {
|
||||
|
||||
export interface TipCoinSource {
|
||||
type: CoinSourceType.Tip;
|
||||
walletTipId: string;
|
||||
coinIndex: number;
|
||||
}
|
||||
|
||||
export type CoinSource = WithdrawCoinSource | RefreshCoinSource | TipCoinSource;
|
||||
@ -950,9 +963,9 @@ export interface TipRecord {
|
||||
/**
|
||||
* The tipped amount.
|
||||
*/
|
||||
amount: AmountJson;
|
||||
tipAmountRaw: AmountJson;
|
||||
|
||||
totalFees: AmountJson;
|
||||
tipAmountEffective: AmountJson;
|
||||
|
||||
/**
|
||||
* Timestamp, the tip can't be picked up anymore after this deadline.
|
||||
@ -1481,18 +1494,6 @@ export enum WithdrawalSourceType {
|
||||
Reserve = "reserve",
|
||||
}
|
||||
|
||||
export interface WithdrawalSourceTip {
|
||||
type: WithdrawalSourceType.Tip;
|
||||
tipId: string;
|
||||
}
|
||||
|
||||
export interface WithdrawalSourceReserve {
|
||||
type: WithdrawalSourceType.Reserve;
|
||||
reservePub: string;
|
||||
}
|
||||
|
||||
export type WithdrawalSource = WithdrawalSourceTip | WithdrawalSourceReserve;
|
||||
|
||||
export interface DenominationSelectionInfo {
|
||||
totalCoinValue: AmountJson;
|
||||
totalWithdrawCost: AmountJson;
|
||||
@ -1524,12 +1525,7 @@ export interface DenomSelectionState {
|
||||
export interface WithdrawalGroupRecord {
|
||||
withdrawalGroupId: string;
|
||||
|
||||
/**
|
||||
* Withdrawal source. Fields that don't apply to the respective
|
||||
* withdrawal source type must be null (i.e. can't be absent),
|
||||
* otherwise the IndexedDB indexing won't like us.
|
||||
*/
|
||||
source: WithdrawalSource;
|
||||
reservePub: string;
|
||||
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
|
@ -23,7 +23,6 @@
|
||||
* Imports.
|
||||
*/
|
||||
import { TalerErrorDetails } from "./walletTypes";
|
||||
import { WithdrawalSource } from "./dbTypes";
|
||||
import { ReserveHistorySummary } from "../util/reserveHistoryUtil";
|
||||
|
||||
export enum NotificationType {
|
||||
@ -141,7 +140,7 @@ export interface WithdrawalGroupCreatedNotification {
|
||||
|
||||
export interface WithdrawalGroupFinishedNotification {
|
||||
type: NotificationType.WithdrawGroupFinished;
|
||||
withdrawalSource: WithdrawalSource;
|
||||
reservePub: string;
|
||||
}
|
||||
|
||||
export interface WaitingForRetryNotification {
|
||||
|
@ -22,7 +22,7 @@
|
||||
* Imports.
|
||||
*/
|
||||
import { TalerErrorDetails, BalancesResponse } from "./walletTypes";
|
||||
import { WithdrawalSource, RetryInfo, ReserveRecordStatus } from "./dbTypes";
|
||||
import { RetryInfo, ReserveRecordStatus } from "./dbTypes";
|
||||
import { Timestamp, Duration } from "../util/time";
|
||||
|
||||
export enum PendingOperationType {
|
||||
@ -219,7 +219,6 @@ export interface PendingRecoupOperation {
|
||||
*/
|
||||
export interface PendingWithdrawOperation {
|
||||
type: PendingOperationType.Withdraw;
|
||||
source: WithdrawalSource;
|
||||
lastError: TalerErrorDetails | undefined;
|
||||
retryInfo: RetryInfo;
|
||||
withdrawalGroupId: string;
|
||||
|
@ -593,11 +593,11 @@ export interface TipPickupRequest {
|
||||
* Reserve signature, defined as separate class to facilitate
|
||||
* schema validation with "@Checkable".
|
||||
*/
|
||||
export class ReserveSigSingleton {
|
||||
export class BlindSigWrapper {
|
||||
/**
|
||||
* Reserve signature.
|
||||
*/
|
||||
reserve_sig: string;
|
||||
blind_sig: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -605,15 +605,10 @@ export class ReserveSigSingleton {
|
||||
* to the TipPickupRequest.
|
||||
*/
|
||||
export class TipResponse {
|
||||
/**
|
||||
* Public key of the reserve
|
||||
*/
|
||||
reserve_pub: string;
|
||||
|
||||
/**
|
||||
* The order of the signatures matches the planchets list.
|
||||
*/
|
||||
reserve_sigs: ReserveSigSingleton[];
|
||||
blind_sigs: BlindSigWrapper[];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1166,15 +1161,14 @@ export const codecForMerchantRefundResponse = (): Codec<
|
||||
.property("refunds", codecForList(codecForMerchantRefundPermission()))
|
||||
.build("MerchantRefundResponse");
|
||||
|
||||
export const codecForReserveSigSingleton = (): Codec<ReserveSigSingleton> =>
|
||||
buildCodecForObject<ReserveSigSingleton>()
|
||||
.property("reserve_sig", codecForString())
|
||||
.build("ReserveSigSingleton");
|
||||
export const codecForBlindSigWrapper = (): Codec<BlindSigWrapper> =>
|
||||
buildCodecForObject<BlindSigWrapper>()
|
||||
.property("blind_sig", codecForString())
|
||||
.build("BlindSigWrapper");
|
||||
|
||||
export const codecForTipResponse = (): Codec<TipResponse> =>
|
||||
buildCodecForObject<TipResponse>()
|
||||
.property("reserve_pub", codecForString())
|
||||
.property("reserve_sigs", codecForList(codecForReserveSigSingleton()))
|
||||
.property("blind_sigs", codecForList(codecForBlindSigWrapper()))
|
||||
.build("TipResponse");
|
||||
|
||||
export const codecForRecoup = (): Codec<Recoup> =>
|
||||
|
@ -359,8 +359,8 @@ export interface PrepareTipResult {
|
||||
* Has the tip already been accepted?
|
||||
*/
|
||||
accepted: boolean;
|
||||
amount: AmountString;
|
||||
totalFees: AmountString;
|
||||
tipAmountRaw: AmountString;
|
||||
tipAmountEffective: AmountString;
|
||||
exchangeBaseUrl: string;
|
||||
expirationTimestamp: Timestamp;
|
||||
}
|
||||
@ -368,8 +368,8 @@ export interface PrepareTipResult {
|
||||
export const codecForPrepareTipResult = (): Codec<PrepareTipResult> =>
|
||||
buildCodecForObject<PrepareTipResult>()
|
||||
.property("accepted", codecForBoolean())
|
||||
.property("amount", codecForAmountString())
|
||||
.property("totalFees", codecForAmountString())
|
||||
.property("tipAmountRaw", codecForAmountString())
|
||||
.property("tipAmountEffective", codecForAmountString())
|
||||
.property("exchangeBaseUrl", codecForString())
|
||||
.property("expirationTimestamp", codecForTimestamp)
|
||||
.property("walletTipId", codecForString())
|
||||
|
@ -86,7 +86,6 @@ import {
|
||||
codecForPreparePayRequest,
|
||||
codecForIntegrationTestArgs,
|
||||
WithdrawTestBalanceRequest,
|
||||
withdrawTestBalanceDefaults,
|
||||
codecForWithdrawTestBalance,
|
||||
codecForTestPayArgs,
|
||||
codecForSetCoinSuspendedRequest,
|
||||
@ -916,9 +915,7 @@ export class Wallet {
|
||||
console.error("no withdrawal session found for coin");
|
||||
continue;
|
||||
}
|
||||
if (ws.source.type == "reserve") {
|
||||
withdrawalReservePub = ws.source.reservePub;
|
||||
}
|
||||
withdrawalReservePub = ws.reservePub;
|
||||
}
|
||||
coinsJson.coins.push({
|
||||
coin_pub: c.coinPub,
|
||||
|
Loading…
Reference in New Issue
Block a user