wallet-core: do not rely on reserve history for withdrawals
This commit is contained in:
parent
1607c728bc
commit
9d66078852
@ -43,15 +43,9 @@ export interface ReserveStatus {
|
|||||||
* Balance left in the reserve.
|
* Balance left in the reserve.
|
||||||
*/
|
*/
|
||||||
balance: AmountString;
|
balance: AmountString;
|
||||||
|
|
||||||
/**
|
|
||||||
* Transaction history for the reserve.
|
|
||||||
*/
|
|
||||||
history: ReserveTransaction[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const codecForReserveStatus = (): Codec<ReserveStatus> =>
|
export const codecForReserveStatus = (): Codec<ReserveStatus> =>
|
||||||
buildCodecForObject<ReserveStatus>()
|
buildCodecForObject<ReserveStatus>()
|
||||||
.property("balance", codecForString())
|
.property("balance", codecForString())
|
||||||
.property("history", codecForList(codecForReserveTransaction()))
|
|
||||||
.build("ReserveStatus");
|
.build("ReserveStatus");
|
||||||
|
@ -597,9 +597,6 @@ export interface PlanchetRecord {
|
|||||||
|
|
||||||
denomPubHash: string;
|
denomPubHash: string;
|
||||||
|
|
||||||
// FIXME: maybe too redundant?
|
|
||||||
denomPub: DenominationPubKey;
|
|
||||||
|
|
||||||
blindingKey: string;
|
blindingKey: string;
|
||||||
|
|
||||||
withdrawSig: string;
|
withdrawSig: string;
|
||||||
@ -607,10 +604,6 @@ export interface PlanchetRecord {
|
|||||||
coinEv: CoinEnvelope;
|
coinEv: CoinEnvelope;
|
||||||
|
|
||||||
coinEvHash: string;
|
coinEvHash: string;
|
||||||
|
|
||||||
coinValue: AmountJson;
|
|
||||||
|
|
||||||
isFromTip: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -685,11 +678,6 @@ export interface CoinRecord {
|
|||||||
*/
|
*/
|
||||||
coinPriv: string;
|
coinPriv: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Key used by the exchange used to sign the coin.
|
|
||||||
*/
|
|
||||||
denomPub: DenominationPubKey;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hash of the public key that signs the coin.
|
* Hash of the public key that signs the coin.
|
||||||
*/
|
*/
|
||||||
@ -1378,6 +1366,8 @@ export interface WithdrawalGroupRecord {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* When was the withdrawal operation completed?
|
* When was the withdrawal operation completed?
|
||||||
|
*
|
||||||
|
* FIXME: We should probably drop this and introduce an OperationStatus field.
|
||||||
*/
|
*/
|
||||||
timestampFinish?: Timestamp;
|
timestampFinish?: Timestamp;
|
||||||
|
|
||||||
|
@ -419,7 +419,6 @@ export async function importBackup(
|
|||||||
coinPub: compCoin.coinPub,
|
coinPub: compCoin.coinPub,
|
||||||
suspended: false,
|
suspended: false,
|
||||||
exchangeBaseUrl: backupExchangeDetails.base_url,
|
exchangeBaseUrl: backupExchangeDetails.base_url,
|
||||||
denomPub: backupDenomination.denom_pub,
|
|
||||||
denomPubHash,
|
denomPubHash,
|
||||||
status: backupCoin.fresh
|
status: backupCoin.fresh
|
||||||
? CoinStatus.Fresh
|
? CoinStatus.Fresh
|
||||||
|
@ -315,7 +315,7 @@ export async function getCandidatePayCoins(
|
|||||||
candidateCoins.push({
|
candidateCoins.push({
|
||||||
availableAmount: coin.currentAmount,
|
availableAmount: coin.currentAmount,
|
||||||
coinPub: coin.coinPub,
|
coinPub: coin.coinPub,
|
||||||
denomPub: coin.denomPub,
|
denomPub: denom.denomPub,
|
||||||
feeDeposit: denom.feeDeposit,
|
feeDeposit: denom.feeDeposit,
|
||||||
exchangeBaseUrl: denom.exchangeBaseUrl,
|
exchangeBaseUrl: denom.exchangeBaseUrl,
|
||||||
});
|
});
|
||||||
@ -1397,7 +1397,7 @@ export async function generateDepositPermissions(
|
|||||||
coinPub: coin.coinPub,
|
coinPub: coin.coinPub,
|
||||||
contractTermsHash: contractData.contractTermsHash,
|
contractTermsHash: contractData.contractTermsHash,
|
||||||
denomPubHash: coin.denomPubHash,
|
denomPubHash: coin.denomPubHash,
|
||||||
denomKeyType: coin.denomPub.cipher,
|
denomKeyType: denom.denomPub.cipher,
|
||||||
denomSig: coin.denomSig,
|
denomSig: coin.denomSig,
|
||||||
exchangeBaseUrl: coin.exchangeBaseUrl,
|
exchangeBaseUrl: coin.exchangeBaseUrl,
|
||||||
feeDeposit: denom.feeDeposit,
|
feeDeposit: denom.feeDeposit,
|
||||||
|
@ -164,18 +164,34 @@ async function recoupWithdrawCoin(
|
|||||||
cs: WithdrawCoinSource,
|
cs: WithdrawCoinSource,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const reservePub = cs.reservePub;
|
const reservePub = cs.reservePub;
|
||||||
const reserve = await ws.db
|
const d = await ws.db
|
||||||
.mktx((x) => ({
|
.mktx((x) => ({
|
||||||
reserves: x.reserves,
|
reserves: x.reserves,
|
||||||
|
denominations: x.denominations,
|
||||||
}))
|
}))
|
||||||
.runReadOnly(async (tx) => {
|
.runReadOnly(async (tx) => {
|
||||||
return tx.reserves.get(reservePub);
|
const reserve = await tx.reserves.get(reservePub);
|
||||||
|
if (!reserve) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const denomInfo = await ws.getDenomInfo(
|
||||||
|
ws,
|
||||||
|
tx,
|
||||||
|
reserve.exchangeBaseUrl,
|
||||||
|
coin.denomPubHash,
|
||||||
|
);
|
||||||
|
if (!denomInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return { reserve, denomInfo };
|
||||||
});
|
});
|
||||||
if (!reserve) {
|
if (!d) {
|
||||||
// FIXME: We should at least emit some pending operation / warning for this?
|
// FIXME: We should at least emit some pending operation / warning for this?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { reserve, denomInfo } = d;
|
||||||
|
|
||||||
ws.notify({
|
ws.notify({
|
||||||
type: NotificationType.RecoupStarted,
|
type: NotificationType.RecoupStarted,
|
||||||
});
|
});
|
||||||
@ -184,7 +200,7 @@ async function recoupWithdrawCoin(
|
|||||||
blindingKey: coin.blindingKey,
|
blindingKey: coin.blindingKey,
|
||||||
coinPriv: coin.coinPriv,
|
coinPriv: coin.coinPriv,
|
||||||
coinPub: coin.coinPub,
|
coinPub: coin.coinPub,
|
||||||
denomPub: coin.denomPub,
|
denomPub: denomInfo.denomPub,
|
||||||
denomPubHash: coin.denomPubHash,
|
denomPubHash: coin.denomPubHash,
|
||||||
denomSig: coin.denomSig,
|
denomSig: coin.denomSig,
|
||||||
});
|
});
|
||||||
@ -253,6 +269,28 @@ async function recoupRefreshCoin(
|
|||||||
coin: CoinRecord,
|
coin: CoinRecord,
|
||||||
cs: RefreshCoinSource,
|
cs: RefreshCoinSource,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const d = await ws.db
|
||||||
|
.mktx((x) => ({
|
||||||
|
coins: x.coins,
|
||||||
|
denominations: x.denominations,
|
||||||
|
}))
|
||||||
|
.runReadOnly(async (tx) => {
|
||||||
|
const denomInfo = await ws.getDenomInfo(
|
||||||
|
ws,
|
||||||
|
tx,
|
||||||
|
coin.exchangeBaseUrl,
|
||||||
|
coin.denomPubHash,
|
||||||
|
);
|
||||||
|
if (!denomInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return { denomInfo };
|
||||||
|
});
|
||||||
|
if (!d) {
|
||||||
|
// FIXME: We should at least emit some pending operation / warning for this?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ws.notify({
|
ws.notify({
|
||||||
type: NotificationType.RecoupStarted,
|
type: NotificationType.RecoupStarted,
|
||||||
});
|
});
|
||||||
@ -261,7 +299,7 @@ async function recoupRefreshCoin(
|
|||||||
blindingKey: coin.blindingKey,
|
blindingKey: coin.blindingKey,
|
||||||
coinPriv: coin.coinPriv,
|
coinPriv: coin.coinPriv,
|
||||||
coinPub: coin.coinPub,
|
coinPub: coin.coinPub,
|
||||||
denomPub: coin.denomPub,
|
denomPub: d.denomInfo.denomPub,
|
||||||
denomPubHash: coin.denomPubHash,
|
denomPubHash: coin.denomPubHash,
|
||||||
denomSig: coin.denomSig,
|
denomSig: coin.denomSig,
|
||||||
});
|
});
|
||||||
|
@ -395,7 +395,7 @@ async function refreshMelt(
|
|||||||
oldCoin.exchangeBaseUrl,
|
oldCoin.exchangeBaseUrl,
|
||||||
);
|
);
|
||||||
let meltReqBody: any;
|
let meltReqBody: any;
|
||||||
if (oldCoin.denomPub.cipher === DenomKeyType.Rsa) {
|
if (oldDenom.denomPub.cipher === DenomKeyType.Rsa) {
|
||||||
meltReqBody = {
|
meltReqBody = {
|
||||||
coin_pub: oldCoin.coinPub,
|
coin_pub: oldCoin.coinPub,
|
||||||
confirm_sig: derived.confirmSig,
|
confirm_sig: derived.confirmSig,
|
||||||
@ -671,7 +671,6 @@ async function refreshReveal(
|
|||||||
coinPriv: pc.coinPriv,
|
coinPriv: pc.coinPriv,
|
||||||
coinPub: pc.coinPub,
|
coinPub: pc.coinPub,
|
||||||
currentAmount: denom.value,
|
currentAmount: denom.value,
|
||||||
denomPub: denom.denomPub,
|
|
||||||
denomPubHash: denom.denomPubHash,
|
denomPubHash: denom.denomPubHash,
|
||||||
denomSig: {
|
denomSig: {
|
||||||
cipher: DenomKeyType.Rsa,
|
cipher: DenomKeyType.Rsa,
|
||||||
|
@ -587,8 +587,8 @@ async function updateReserve(
|
|||||||
logger.trace(`got reserve status ${j2s(result.response)}`);
|
logger.trace(`got reserve status ${j2s(result.response)}`);
|
||||||
|
|
||||||
const reserveInfo = result.response;
|
const reserveInfo = result.response;
|
||||||
const balance = Amounts.parseOrThrow(reserveInfo.balance);
|
const reserveBalance = Amounts.parseOrThrow(reserveInfo.balance);
|
||||||
const currency = balance.currency;
|
const currency = reserveBalance.currency;
|
||||||
|
|
||||||
await updateWithdrawalDenoms(ws, reserve.exchangeBaseUrl);
|
await updateWithdrawalDenoms(ws, reserve.exchangeBaseUrl);
|
||||||
const denoms = await getCandidateWithdrawalDenoms(
|
const denoms = await getCandidateWithdrawalDenoms(
|
||||||
@ -598,73 +598,50 @@ async function updateReserve(
|
|||||||
|
|
||||||
const newWithdrawalGroup = await ws.db
|
const newWithdrawalGroup = await ws.db
|
||||||
.mktx((x) => ({
|
.mktx((x) => ({
|
||||||
coins: x.coins,
|
|
||||||
planchets: x.planchets,
|
planchets: x.planchets,
|
||||||
withdrawalGroups: x.withdrawalGroups,
|
withdrawalGroups: x.withdrawalGroups,
|
||||||
reserves: x.reserves,
|
reserves: x.reserves,
|
||||||
|
denominations: x.denominations,
|
||||||
}))
|
}))
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const newReserve = await tx.reserves.get(reserve.reservePub);
|
const newReserve = await tx.reserves.get(reserve.reservePub);
|
||||||
if (!newReserve) {
|
if (!newReserve) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let amountReservePlus = Amounts.getZero(currency);
|
let amountReservePlus = reserveBalance;
|
||||||
let amountReserveMinus = Amounts.getZero(currency);
|
let amountReserveMinus = Amounts.getZero(currency);
|
||||||
|
|
||||||
// Subtract withdrawal groups for this reserve from the available amount.
|
// Subtract amount allocated in unfinished withdrawal groups
|
||||||
|
// for this reserve from the available amount.
|
||||||
await tx.withdrawalGroups.indexes.byReservePub
|
await tx.withdrawalGroups.indexes.byReservePub
|
||||||
.iter(reservePub)
|
.iter(reservePub)
|
||||||
.forEach((wg) => {
|
.forEachAsync(async (wg) => {
|
||||||
const cost = wg.denomsSel.totalWithdrawCost;
|
if (wg.timestampFinish) {
|
||||||
amountReserveMinus = Amounts.add(amountReserveMinus, cost).amount;
|
return;
|
||||||
});
|
|
||||||
|
|
||||||
for (const entry of reserveInfo.history) {
|
|
||||||
switch (entry.type) {
|
|
||||||
case ReserveTransactionType.Credit:
|
|
||||||
amountReservePlus = Amounts.add(
|
|
||||||
amountReservePlus,
|
|
||||||
Amounts.parseOrThrow(entry.amount),
|
|
||||||
).amount;
|
|
||||||
break;
|
|
||||||
case ReserveTransactionType.Recoup:
|
|
||||||
amountReservePlus = Amounts.add(
|
|
||||||
amountReservePlus,
|
|
||||||
Amounts.parseOrThrow(entry.amount),
|
|
||||||
).amount;
|
|
||||||
break;
|
|
||||||
case ReserveTransactionType.Closing:
|
|
||||||
amountReserveMinus = Amounts.add(
|
|
||||||
amountReserveMinus,
|
|
||||||
Amounts.parseOrThrow(entry.amount),
|
|
||||||
).amount;
|
|
||||||
break;
|
|
||||||
case ReserveTransactionType.Withdraw: {
|
|
||||||
// Now we check if the withdrawal transaction
|
|
||||||
// is part of any withdrawal known to this wallet.
|
|
||||||
const planchet = await tx.planchets.indexes.byCoinEvHash.get(
|
|
||||||
entry.h_coin_envelope,
|
|
||||||
);
|
|
||||||
if (planchet) {
|
|
||||||
// Amount is already accounted in some withdrawal session
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const coin = await tx.coins.indexes.byCoinEvHash.get(
|
|
||||||
entry.h_coin_envelope,
|
|
||||||
);
|
|
||||||
if (coin) {
|
|
||||||
// Amount is already accounted in some withdrawal session
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Amount has been claimed by some withdrawal we don't know about
|
|
||||||
amountReserveMinus = Amounts.add(
|
|
||||||
amountReserveMinus,
|
|
||||||
Amounts.parseOrThrow(entry.amount),
|
|
||||||
).amount;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
await tx.planchets.indexes.byGroup
|
||||||
}
|
.iter(wg.withdrawalGroupId)
|
||||||
|
.forEachAsync(async (pr) => {
|
||||||
|
if (pr.withdrawalDone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const denomInfo = await ws.getDenomInfo(
|
||||||
|
ws,
|
||||||
|
tx,
|
||||||
|
wg.exchangeBaseUrl,
|
||||||
|
pr.denomPubHash,
|
||||||
|
);
|
||||||
|
if (!denomInfo) {
|
||||||
|
logger.error(`no denom info found for ${pr.denomPubHash}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
amountReserveMinus = Amounts.add(
|
||||||
|
amountReserveMinus,
|
||||||
|
denomInfo.value,
|
||||||
|
denomInfo.feeWithdraw,
|
||||||
|
).amount;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const remainingAmount = Amounts.sub(
|
const remainingAmount = Amounts.sub(
|
||||||
amountReservePlus,
|
amountReservePlus,
|
||||||
|
@ -374,7 +374,6 @@ async function processTipImpl(
|
|||||||
walletTipId: walletTipId,
|
walletTipId: walletTipId,
|
||||||
},
|
},
|
||||||
currentAmount: denom.value,
|
currentAmount: denom.value,
|
||||||
denomPub: denom.denomPub,
|
|
||||||
denomPubHash: denom.denomPubHash,
|
denomPubHash: denom.denomPubHash,
|
||||||
denomSig: { cipher: DenomKeyType.Rsa, rsa_signature: denomSigRsa },
|
denomSig: { cipher: DenomKeyType.Rsa, rsa_signature: denomSigRsa },
|
||||||
exchangeBaseUrl: tipRecord.exchangeBaseUrl,
|
exchangeBaseUrl: tipRecord.exchangeBaseUrl,
|
||||||
|
@ -418,10 +418,7 @@ async function processPlanchetGenerate(
|
|||||||
coinIdx,
|
coinIdx,
|
||||||
coinPriv: r.coinPriv,
|
coinPriv: r.coinPriv,
|
||||||
coinPub: r.coinPub,
|
coinPub: r.coinPub,
|
||||||
coinValue: r.coinValue,
|
|
||||||
denomPub: r.denomPub,
|
|
||||||
denomPubHash: r.denomPubHash,
|
denomPubHash: r.denomPubHash,
|
||||||
isFromTip: false,
|
|
||||||
reservePub: r.reservePub,
|
reservePub: r.reservePub,
|
||||||
withdrawalDone: false,
|
withdrawalDone: false,
|
||||||
withdrawSig: r.withdrawSig,
|
withdrawSig: r.withdrawSig,
|
||||||
@ -557,6 +554,7 @@ async function processPlanchetVerifyAndStoreCoin(
|
|||||||
.mktx((x) => ({
|
.mktx((x) => ({
|
||||||
withdrawalGroups: x.withdrawalGroups,
|
withdrawalGroups: x.withdrawalGroups,
|
||||||
planchets: x.planchets,
|
planchets: x.planchets,
|
||||||
|
denominations: x.denominations,
|
||||||
}))
|
}))
|
||||||
.runReadOnly(async (tx) => {
|
.runReadOnly(async (tx) => {
|
||||||
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
|
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
|
||||||
@ -570,16 +568,29 @@ async function processPlanchetVerifyAndStoreCoin(
|
|||||||
logger.warn("processPlanchet: planchet already withdrawn");
|
logger.warn("processPlanchet: planchet already withdrawn");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return { planchet, exchangeBaseUrl: withdrawalGroup.exchangeBaseUrl };
|
const denomInfo = await ws.getDenomInfo(
|
||||||
|
ws,
|
||||||
|
tx,
|
||||||
|
withdrawalGroup.exchangeBaseUrl,
|
||||||
|
planchet.denomPubHash,
|
||||||
|
);
|
||||||
|
if (!denomInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
planchet,
|
||||||
|
denomInfo,
|
||||||
|
exchangeBaseUrl: withdrawalGroup.exchangeBaseUrl,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!d) {
|
if (!d) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { planchet, exchangeBaseUrl } = d;
|
const { planchet, denomInfo } = d;
|
||||||
|
|
||||||
const planchetDenomPub = planchet.denomPub;
|
const planchetDenomPub = denomInfo.denomPub;
|
||||||
if (planchetDenomPub.cipher !== DenomKeyType.Rsa) {
|
if (planchetDenomPub.cipher !== DenomKeyType.Rsa) {
|
||||||
throw Error(`cipher (${planchetDenomPub.cipher}) not supported`);
|
throw Error(`cipher (${planchetDenomPub.cipher}) not supported`);
|
||||||
}
|
}
|
||||||
@ -623,9 +634,9 @@ async function processPlanchetVerifyAndStoreCoin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let denomSig: UnblindedSignature;
|
let denomSig: UnblindedSignature;
|
||||||
if (planchet.denomPub.cipher === DenomKeyType.Rsa) {
|
if (planchetDenomPub.cipher === DenomKeyType.Rsa) {
|
||||||
denomSig = {
|
denomSig = {
|
||||||
cipher: planchet.denomPub.cipher,
|
cipher: planchetDenomPub.cipher,
|
||||||
rsa_signature: denomSigRsa,
|
rsa_signature: denomSigRsa,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@ -636,12 +647,11 @@ async function processPlanchetVerifyAndStoreCoin(
|
|||||||
blindingKey: planchet.blindingKey,
|
blindingKey: planchet.blindingKey,
|
||||||
coinPriv: planchet.coinPriv,
|
coinPriv: planchet.coinPriv,
|
||||||
coinPub: planchet.coinPub,
|
coinPub: planchet.coinPub,
|
||||||
currentAmount: planchet.coinValue,
|
currentAmount: denomInfo.value,
|
||||||
denomPub: planchet.denomPub,
|
|
||||||
denomPubHash: planchet.denomPubHash,
|
denomPubHash: planchet.denomPubHash,
|
||||||
denomSig,
|
denomSig,
|
||||||
coinEvHash: planchet.coinEvHash,
|
coinEvHash: planchet.coinEvHash,
|
||||||
exchangeBaseUrl: exchangeBaseUrl,
|
exchangeBaseUrl: d.exchangeBaseUrl,
|
||||||
status: CoinStatus.Fresh,
|
status: CoinStatus.Fresh,
|
||||||
coinSource: {
|
coinSource: {
|
||||||
type: CoinSourceType.Withdraw,
|
type: CoinSourceType.Withdraw,
|
||||||
|
@ -77,6 +77,8 @@ export interface AvailableCoinInfo {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Coin's denomination public key.
|
* Coin's denomination public key.
|
||||||
|
*
|
||||||
|
* FIXME: We should only need the denomPubHash here, if at all.
|
||||||
*/
|
*/
|
||||||
denomPub: DenominationPubKey;
|
denomPub: DenominationPubKey;
|
||||||
|
|
||||||
|
@ -24,23 +24,62 @@
|
|||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
AcceptManualWithdrawalResult,
|
AcceptManualWithdrawalResult,
|
||||||
AcceptWithdrawalResponse, AmountJson, Amounts, BalancesResponse, codecForAbortPayWithRefundRequest,
|
AcceptWithdrawalResponse,
|
||||||
|
AmountJson,
|
||||||
|
Amounts,
|
||||||
|
BalancesResponse,
|
||||||
|
codecForAbortPayWithRefundRequest,
|
||||||
codecForAcceptBankIntegratedWithdrawalRequest,
|
codecForAcceptBankIntegratedWithdrawalRequest,
|
||||||
codecForAcceptExchangeTosRequest,
|
codecForAcceptExchangeTosRequest,
|
||||||
codecForAcceptManualWithdrawalRequet,
|
codecForAcceptManualWithdrawalRequet,
|
||||||
codecForAcceptTipRequest,
|
codecForAcceptTipRequest,
|
||||||
codecForAddExchangeRequest, codecForAny, codecForApplyRefundRequest,
|
codecForAddExchangeRequest,
|
||||||
|
codecForAny,
|
||||||
|
codecForApplyRefundRequest,
|
||||||
codecForConfirmPayRequest,
|
codecForConfirmPayRequest,
|
||||||
codecForCreateDepositGroupRequest, codecForDeleteTransactionRequest, codecForForceRefreshRequest,
|
codecForCreateDepositGroupRequest,
|
||||||
codecForGetExchangeTosRequest, codecForGetExchangeWithdrawalInfo, codecForGetFeeForDeposit, codecForGetWithdrawalDetailsForAmountRequest,
|
codecForDeleteTransactionRequest,
|
||||||
codecForGetWithdrawalDetailsForUri, codecForImportDbRequest, codecForIntegrationTestArgs, codecForListKnownBankAccounts, codecForPreparePayRequest,
|
codecForForceRefreshRequest,
|
||||||
codecForPrepareTipRequest, codecForRetryTransactionRequest, codecForSetCoinSuspendedRequest, codecForSetWalletDeviceIdRequest, codecForTestPayArgs,
|
codecForGetExchangeTosRequest,
|
||||||
codecForTrackDepositGroupRequest, codecForTransactionsRequest, codecForWithdrawFakebankRequest, codecForWithdrawTestBalance, CoinDumpJson, CoreApiResponse, durationFromSpec,
|
codecForGetExchangeWithdrawalInfo,
|
||||||
durationMin, ExchangeListItem,
|
codecForGetFeeForDeposit,
|
||||||
ExchangesListRespose, getDurationRemaining, GetExchangeTosResult, isTimestampExpired,
|
codecForGetWithdrawalDetailsForAmountRequest,
|
||||||
j2s, KnownBankAccounts, Logger, ManualWithdrawalDetails, NotificationType, parsePaytoUri, PaytoUri, RefreshReason, TalerErrorCode,
|
codecForGetWithdrawalDetailsForUri,
|
||||||
|
codecForImportDbRequest,
|
||||||
|
codecForIntegrationTestArgs,
|
||||||
|
codecForListKnownBankAccounts,
|
||||||
|
codecForPreparePayRequest,
|
||||||
|
codecForPrepareTipRequest,
|
||||||
|
codecForRetryTransactionRequest,
|
||||||
|
codecForSetCoinSuspendedRequest,
|
||||||
|
codecForSetWalletDeviceIdRequest,
|
||||||
|
codecForTestPayArgs,
|
||||||
|
codecForTrackDepositGroupRequest,
|
||||||
|
codecForTransactionsRequest,
|
||||||
|
codecForWithdrawFakebankRequest,
|
||||||
|
codecForWithdrawTestBalance,
|
||||||
|
CoinDumpJson,
|
||||||
|
CoreApiResponse,
|
||||||
|
durationFromSpec,
|
||||||
|
durationMin,
|
||||||
|
ExchangeListItem,
|
||||||
|
ExchangesListRespose,
|
||||||
|
getDurationRemaining,
|
||||||
|
GetExchangeTosResult,
|
||||||
|
isTimestampExpired,
|
||||||
|
j2s,
|
||||||
|
KnownBankAccounts,
|
||||||
|
Logger,
|
||||||
|
ManualWithdrawalDetails,
|
||||||
|
NotificationType,
|
||||||
|
parsePaytoUri,
|
||||||
|
PaytoUri,
|
||||||
|
RefreshReason,
|
||||||
|
TalerErrorCode,
|
||||||
Timestamp,
|
Timestamp,
|
||||||
timestampMin, URL, WalletNotification
|
timestampMin,
|
||||||
|
URL,
|
||||||
|
WalletNotification,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
DenomInfo,
|
DenomInfo,
|
||||||
@ -50,7 +89,7 @@ import {
|
|||||||
MerchantOperations,
|
MerchantOperations,
|
||||||
NotificationListener,
|
NotificationListener,
|
||||||
RecoupOperations,
|
RecoupOperations,
|
||||||
ReserveOperations
|
ReserveOperations,
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
import { CryptoApi, CryptoWorkerFactory } from "./crypto/workers/cryptoApi.js";
|
import { CryptoApi, CryptoWorkerFactory } from "./crypto/workers/cryptoApi.js";
|
||||||
import {
|
import {
|
||||||
@ -59,12 +98,12 @@ import {
|
|||||||
exportDb,
|
exportDb,
|
||||||
importDb,
|
importDb,
|
||||||
ReserveRecordStatus,
|
ReserveRecordStatus,
|
||||||
WalletStoresV1
|
WalletStoresV1,
|
||||||
} from "./db.js";
|
} from "./db.js";
|
||||||
import {
|
import {
|
||||||
makeErrorDetails,
|
makeErrorDetails,
|
||||||
OperationFailedAndReportedError,
|
OperationFailedAndReportedError,
|
||||||
OperationFailedError
|
OperationFailedError,
|
||||||
} from "./errors.js";
|
} from "./errors.js";
|
||||||
import { exportBackup } from "./operations/backup/export.js";
|
import { exportBackup } from "./operations/backup/export.js";
|
||||||
import {
|
import {
|
||||||
@ -77,7 +116,7 @@ import {
|
|||||||
loadBackupRecovery,
|
loadBackupRecovery,
|
||||||
processBackupForProvider,
|
processBackupForProvider,
|
||||||
removeBackupProvider,
|
removeBackupProvider,
|
||||||
runBackupCycle
|
runBackupCycle,
|
||||||
} from "./operations/backup/index.js";
|
} from "./operations/backup/index.js";
|
||||||
import { setWalletDeviceId } from "./operations/backup/state.js";
|
import { setWalletDeviceId } from "./operations/backup/state.js";
|
||||||
import { getBalances } from "./operations/balance.js";
|
import { getBalances } from "./operations/balance.js";
|
||||||
@ -85,7 +124,7 @@ import {
|
|||||||
createDepositGroup,
|
createDepositGroup,
|
||||||
getFeeForDeposit,
|
getFeeForDeposit,
|
||||||
processDepositGroup,
|
processDepositGroup,
|
||||||
trackDepositGroup
|
trackDepositGroup,
|
||||||
} from "./operations/deposits.js";
|
} from "./operations/deposits.js";
|
||||||
import {
|
import {
|
||||||
acceptExchangeTermsOfService,
|
acceptExchangeTermsOfService,
|
||||||
@ -94,62 +133,64 @@ import {
|
|||||||
getExchangeRequestTimeout,
|
getExchangeRequestTimeout,
|
||||||
getExchangeTrust,
|
getExchangeTrust,
|
||||||
updateExchangeFromUrl,
|
updateExchangeFromUrl,
|
||||||
updateExchangeTermsOfService
|
updateExchangeTermsOfService,
|
||||||
} from "./operations/exchanges.js";
|
} from "./operations/exchanges.js";
|
||||||
import { getMerchantInfo } from "./operations/merchants.js";
|
import { getMerchantInfo } from "./operations/merchants.js";
|
||||||
import {
|
import {
|
||||||
confirmPay,
|
confirmPay,
|
||||||
preparePayForUri,
|
preparePayForUri,
|
||||||
processDownloadProposal,
|
processDownloadProposal,
|
||||||
processPurchasePay
|
processPurchasePay,
|
||||||
} from "./operations/pay.js";
|
} from "./operations/pay.js";
|
||||||
import { getPendingOperations } from "./operations/pending.js";
|
import { getPendingOperations } from "./operations/pending.js";
|
||||||
import { createRecoupGroup, processRecoupGroup } from "./operations/recoup.js";
|
import { createRecoupGroup, processRecoupGroup } from "./operations/recoup.js";
|
||||||
import {
|
import {
|
||||||
autoRefresh,
|
autoRefresh,
|
||||||
createRefreshGroup,
|
createRefreshGroup,
|
||||||
processRefreshGroup
|
processRefreshGroup,
|
||||||
} from "./operations/refresh.js";
|
} from "./operations/refresh.js";
|
||||||
import {
|
import {
|
||||||
abortFailedPayWithRefund,
|
abortFailedPayWithRefund,
|
||||||
applyRefund,
|
applyRefund,
|
||||||
processPurchaseQueryRefund
|
processPurchaseQueryRefund,
|
||||||
} from "./operations/refund.js";
|
} from "./operations/refund.js";
|
||||||
import {
|
import {
|
||||||
createReserve,
|
createReserve,
|
||||||
createTalerWithdrawReserve,
|
createTalerWithdrawReserve,
|
||||||
getFundingPaytoUris,
|
getFundingPaytoUris,
|
||||||
processReserve
|
processReserve,
|
||||||
} from "./operations/reserves.js";
|
} from "./operations/reserves.js";
|
||||||
import {
|
import {
|
||||||
runIntegrationTest,
|
runIntegrationTest,
|
||||||
testPay,
|
testPay,
|
||||||
withdrawTestBalance
|
withdrawTestBalance,
|
||||||
} from "./operations/testing.js";
|
} from "./operations/testing.js";
|
||||||
import { acceptTip, prepareTip, processTip } from "./operations/tip.js";
|
import { acceptTip, prepareTip, processTip } from "./operations/tip.js";
|
||||||
import {
|
import {
|
||||||
deleteTransaction,
|
deleteTransaction,
|
||||||
getTransactions,
|
getTransactions,
|
||||||
retryTransaction
|
retryTransaction,
|
||||||
} from "./operations/transactions.js";
|
} from "./operations/transactions.js";
|
||||||
import {
|
import {
|
||||||
getExchangeWithdrawalInfo,
|
getExchangeWithdrawalInfo,
|
||||||
getWithdrawalDetailsForUri,
|
getWithdrawalDetailsForUri,
|
||||||
processWithdrawGroup
|
processWithdrawGroup,
|
||||||
} from "./operations/withdraw.js";
|
} from "./operations/withdraw.js";
|
||||||
import {
|
import {
|
||||||
PendingOperationsResponse, PendingTaskInfo, PendingTaskType
|
PendingOperationsResponse,
|
||||||
|
PendingTaskInfo,
|
||||||
|
PendingTaskType,
|
||||||
} from "./pending-types.js";
|
} from "./pending-types.js";
|
||||||
import { assertUnreachable } from "./util/assertUnreachable.js";
|
import { assertUnreachable } from "./util/assertUnreachable.js";
|
||||||
import { AsyncOpMemoMap, AsyncOpMemoSingle } from "./util/asyncMemo.js";
|
import { AsyncOpMemoMap, AsyncOpMemoSingle } from "./util/asyncMemo.js";
|
||||||
import {
|
import {
|
||||||
HttpRequestLibrary,
|
HttpRequestLibrary,
|
||||||
readSuccessResponseJsonOrThrow
|
readSuccessResponseJsonOrThrow,
|
||||||
} from "./util/http.js";
|
} from "./util/http.js";
|
||||||
import {
|
import {
|
||||||
AsyncCondition,
|
AsyncCondition,
|
||||||
OpenedPromise,
|
OpenedPromise,
|
||||||
openPromise
|
openPromise,
|
||||||
} from "./util/promiseUtils.js";
|
} from "./util/promiseUtils.js";
|
||||||
import { DbAccess, GetReadWriteAccess } from "./util/query.js";
|
import { DbAccess, GetReadWriteAccess } from "./util/query.js";
|
||||||
import { TimerGroup } from "./util/timer.js";
|
import { TimerGroup } from "./util/timer.js";
|
||||||
@ -455,7 +496,10 @@ async function getExchangeTos(
|
|||||||
) {
|
) {
|
||||||
throw Error("exchange is in invalid state");
|
throw Error("exchange is in invalid state");
|
||||||
}
|
}
|
||||||
if (acceptedFormat && acceptedFormat.findIndex(f => f === contentType) !== -1) {
|
if (
|
||||||
|
acceptedFormat &&
|
||||||
|
acceptedFormat.findIndex((f) => f === contentType) !== -1
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
acceptedEtag: exchangeDetails.termsOfServiceAcceptedEtag,
|
acceptedEtag: exchangeDetails.termsOfServiceAcceptedEtag,
|
||||||
currentEtag,
|
currentEtag,
|
||||||
@ -464,7 +508,12 @@ async function getExchangeTos(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const tosDownload = await downloadTosFromAcceptedFormat(ws, exchangeBaseUrl, getExchangeRequestTimeout(), acceptedFormat);
|
const tosDownload = await downloadTosFromAcceptedFormat(
|
||||||
|
ws,
|
||||||
|
exchangeBaseUrl,
|
||||||
|
getExchangeRequestTimeout(),
|
||||||
|
acceptedFormat,
|
||||||
|
);
|
||||||
|
|
||||||
if (tosDownload.tosContentType === contentType) {
|
if (tosDownload.tosContentType === contentType) {
|
||||||
return {
|
return {
|
||||||
@ -474,7 +523,7 @@ async function getExchangeTos(
|
|||||||
contentType,
|
contentType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
await updateExchangeTermsOfService(ws, exchangeBaseUrl, tosDownload)
|
await updateExchangeTermsOfService(ws, exchangeBaseUrl, tosDownload);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
acceptedEtag: exchangeDetails.termsOfServiceAcceptedEtag,
|
acceptedEtag: exchangeDetails.termsOfServiceAcceptedEtag,
|
||||||
@ -482,7 +531,6 @@ async function getExchangeTos(
|
|||||||
content: tosDownload.tosText,
|
content: tosDownload.tosText,
|
||||||
contentType: tosDownload.tosContentType,
|
contentType: tosDownload.tosContentType,
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listKnownBankAccounts(
|
async function listKnownBankAccounts(
|
||||||
@ -641,9 +689,15 @@ async function dumpCoins(ws: InternalWalletState): Promise<CoinDumpJson> {
|
|||||||
}
|
}
|
||||||
withdrawalReservePub = ws.reservePub;
|
withdrawalReservePub = ws.reservePub;
|
||||||
}
|
}
|
||||||
|
const denomInfo = await ws.getDenomInfo(
|
||||||
|
ws,
|
||||||
|
tx,
|
||||||
|
c.exchangeBaseUrl,
|
||||||
|
c.denomPubHash,
|
||||||
|
);
|
||||||
coinsJson.coins.push({
|
coinsJson.coins.push({
|
||||||
coin_pub: c.coinPub,
|
coin_pub: c.coinPub,
|
||||||
denom_pub: c.denomPub,
|
denom_pub: denomInfo?.denomPub!,
|
||||||
denom_pub_hash: c.denomPubHash,
|
denom_pub_hash: c.denomPubHash,
|
||||||
denom_value: Amounts.stringify(denom.value),
|
denom_value: Amounts.stringify(denom.value),
|
||||||
exchange_base_url: c.exchangeBaseUrl,
|
exchange_base_url: c.exchangeBaseUrl,
|
||||||
@ -1030,7 +1084,7 @@ export async function handleCoreApiRequest(
|
|||||||
try {
|
try {
|
||||||
logger.error("Caught unexpected exception:");
|
logger.error("Caught unexpected exception:");
|
||||||
logger.error(e.stack);
|
logger.error(e.stack);
|
||||||
} catch (e) { }
|
} catch (e) {}
|
||||||
return {
|
return {
|
||||||
type: "error",
|
type: "error",
|
||||||
operation,
|
operation,
|
||||||
@ -1236,7 +1290,10 @@ class InternalWalletStateImpl implements InternalWalletState {
|
|||||||
* Run an async function after acquiring a list of locks, identified
|
* Run an async function after acquiring a list of locks, identified
|
||||||
* by string tokens.
|
* by string tokens.
|
||||||
*/
|
*/
|
||||||
async runSequentialized<T>(tokens: string[], f: () => Promise<T>): Promise<T> {
|
async runSequentialized<T>(
|
||||||
|
tokens: string[],
|
||||||
|
f: () => Promise<T>,
|
||||||
|
): Promise<T> {
|
||||||
// Make sure locks are always acquired in the same order
|
// Make sure locks are always acquired in the same order
|
||||||
tokens = [...tokens].sort();
|
tokens = [...tokens].sort();
|
||||||
|
|
||||||
@ -1269,4 +1326,3 @@ class InternalWalletStateImpl implements InternalWalletState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user