wallet-core: restructure denomination record for easier querying

This commit is contained in:
Florian Dold 2022-09-14 21:27:03 +02:00
parent c021876b41
commit a66b636dee
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
14 changed files with 390 additions and 247 deletions

View File

@ -892,17 +892,22 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
req: DenominationValidationRequest, req: DenominationValidationRequest,
): Promise<ValidationResult> { ): Promise<ValidationResult> {
const { masterPub, denom } = req; const { masterPub, denom } = req;
const value: AmountJson = {
currency: denom.currency,
fraction: denom.amountFrac,
value: denom.amountVal,
};
const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY) const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY)
.put(decodeCrock(masterPub)) .put(decodeCrock(masterPub))
.put(timestampRoundedToBuffer(denom.stampStart)) .put(timestampRoundedToBuffer(denom.stampStart))
.put(timestampRoundedToBuffer(denom.stampExpireWithdraw)) .put(timestampRoundedToBuffer(denom.stampExpireWithdraw))
.put(timestampRoundedToBuffer(denom.stampExpireDeposit)) .put(timestampRoundedToBuffer(denom.stampExpireDeposit))
.put(timestampRoundedToBuffer(denom.stampExpireLegal)) .put(timestampRoundedToBuffer(denom.stampExpireLegal))
.put(amountToBuffer(denom.value)) .put(amountToBuffer(value))
.put(amountToBuffer(denom.feeWithdraw)) .put(amountToBuffer(denom.fees.feeWithdraw))
.put(amountToBuffer(denom.feeDeposit)) .put(amountToBuffer(denom.fees.feeDeposit))
.put(amountToBuffer(denom.feeRefresh)) .put(amountToBuffer(denom.fees.feeRefresh))
.put(amountToBuffer(denom.feeRefund)) .put(amountToBuffer(denom.fees.feeRefund))
.put(decodeCrock(denom.denomPubHash)) .put(decodeCrock(denom.denomPubHash))
.build(); .build();
const sig = decodeCrock(denom.masterSig); const sig = decodeCrock(denom.masterSig);

View File

@ -29,7 +29,6 @@ import {
CoinDepositPermission, CoinDepositPermission,
ContractTerms, ContractTerms,
DenominationPubKey, DenominationPubKey,
Duration,
ExchangeSignKeyJson, ExchangeSignKeyJson,
InternationalizedString, InternationalizedString,
MerchantInfo, MerchantInfo,
@ -48,6 +47,7 @@ import {
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { RetryInfo } from "./util/retries.js"; import { RetryInfo } from "./util/retries.js";
import { Event, IDBDatabase } from "@gnu-taler/idb-bridge"; import { Event, IDBDatabase } from "@gnu-taler/idb-bridge";
import { DenomInfo } from "./internal-wallet-state.js";
/** /**
* Name of the Taler database. This is effectively the major * Name of the Taler database. This is effectively the major
@ -213,26 +213,7 @@ export enum DenominationVerificationStatus {
VerifiedBad = "verified-bad", VerifiedBad = "verified-bad",
} }
/** export interface DenomFees {
* Denomination record as stored in the wallet's database.
*/
export interface DenominationRecord {
/**
* Value of one coin of the denomination.
*/
value: AmountJson;
/**
* The denomination public key.
*/
denomPub: DenominationPubKey;
/**
* Hash of the denomination public key.
* Stored in the database for faster lookups.
*/
denomPubHash: string;
/** /**
* Fee for withdrawing. * Fee for withdrawing.
*/ */
@ -252,6 +233,30 @@ export interface DenominationRecord {
* Fee for refunding. * Fee for refunding.
*/ */
feeRefund: AmountJson; feeRefund: AmountJson;
}
/**
* Denomination record as stored in the wallet's database.
*/
export interface DenominationRecord {
currency: string;
amountVal: number;
amountFrac: number;
/**
* The denomination public key.
*/
denomPub: DenominationPubKey;
/**
* Hash of the denomination public key.
* Stored in the database for faster lookups.
*/
denomPubHash: string;
fees: DenomFees;
/** /**
* Validity start date of the denomination. * Validity start date of the denomination.
@ -321,6 +326,32 @@ export interface DenominationRecord {
freshCoinCount?: number; freshCoinCount?: number;
} }
export namespace DenominationRecord {
export function getValue(d: DenominationRecord): AmountJson {
return {
currency: d.currency,
fraction: d.amountFrac,
value: d.amountVal,
};
}
export function toDenomInfo(d: DenominationRecord): DenomInfo {
return {
denomPub: d.denomPub,
denomPubHash: d.denomPubHash,
feeDeposit: d.fees.feeDeposit,
feeRefresh: d.fees.feeRefresh,
feeRefund: d.fees.feeRefund,
feeWithdraw: d.fees.feeWithdraw,
stampExpireDeposit: d.stampExpireDeposit,
stampExpireLegal: d.stampExpireLegal,
stampExpireWithdraw: d.stampExpireWithdraw,
stampStart: d.stampStart,
value: DenominationRecord.getValue(d),
};
}
}
/** /**
* Information about one of the exchange's bank accounts. * Information about one of the exchange's bank accounts.
*/ */
@ -2031,7 +2062,10 @@ export interface DatabaseDump {
version: string; version: string;
} }
async function recoverFromDump(db: IDBDatabase, dump: DatabaseDump): Promise<void> { async function recoverFromDump(
db: IDBDatabase,
dump: DatabaseDump,
): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite"); const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite");
tx.addEventListener("complete", () => { tx.addEventListener("complete", () => {
@ -2048,13 +2082,13 @@ async function recoverFromDump(db: IDBDatabase, dump: DatabaseDump): Promise<voi
}); });
} }
tx.commit(); tx.commit();
}) });
} }
export async function importDb(db: IDBDatabase, object: any): Promise<void> { export async function importDb(db: IDBDatabase, object: any): Promise<void> {
if ("name" in object && "stores" in object && "version" in object) { if ("name" in object && "stores" in object && "version" in object) {
// looks like a database dump // looks like a database dump
const dump = object as DatabaseDump const dump = object as DatabaseDump;
return recoverFromDump(db, dump); return recoverFromDump(db, dump);
} }
@ -2064,10 +2098,12 @@ export async function importDb(db: IDBDatabase, object: any): Promise<void> {
if (TALER_META_DB_NAME in someDatabase) { if (TALER_META_DB_NAME in someDatabase) {
//looks like a taler database //looks like a taler database
const currentMainDbValue = someDatabase[TALER_META_DB_NAME].objectStores.metaConfig.records[0].value.value const currentMainDbValue =
someDatabase[TALER_META_DB_NAME].objectStores.metaConfig.records[0]
.value.value;
if (currentMainDbValue !== TALER_DB_NAME) { if (currentMainDbValue !== TALER_DB_NAME) {
console.log("not the current database version") console.log("not the current database version");
} }
const talerDb = someDatabase[currentMainDbValue]; const talerDb = someDatabase[currentMainDbValue];
@ -2078,17 +2114,17 @@ export async function importDb(db: IDBDatabase, object: any): Promise<void> {
name: talerDb.schema.databaseName, name: talerDb.schema.databaseName,
version: talerDb.schema.databaseVersion, version: talerDb.schema.databaseVersion,
stores: {}, stores: {},
} };
for (let i = 0; i < objectStoreNames.length; i++) { for (let i = 0; i < objectStoreNames.length; i++) {
const name = objectStoreNames[i]; const name = objectStoreNames[i];
const storeDump = {} as { [s: string]: any }; const storeDump = {} as { [s: string]: any };
dump.stores[name] = storeDump; dump.stores[name] = storeDump;
talerDb.objectStores[name].records.map((r: any) => { talerDb.objectStores[name].records.map((r: any) => {
const pkey = r.primaryKey const pkey = r.primaryKey;
const key = typeof pkey === "string" ? pkey : pkey.join(",") const key = typeof pkey === "string" ? pkey : pkey.join(",");
storeDump[key] = r.value; storeDump[key] = r.value;
}) });
} }
return recoverFromDump(db, dump); return recoverFromDump(db, dump);

View File

@ -48,6 +48,7 @@ import {
UnblindedSignature, UnblindedSignature,
BankWithdrawDetails, BankWithdrawDetails,
parseWithdrawUri, parseWithdrawUri,
AmountJson,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js"; import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
import { DenominationRecord } from "./db.js"; import { DenominationRecord } from "./db.js";
@ -158,11 +159,15 @@ export async function withdrawCoin(args: {
const planchet = await cryptoApi.createPlanchet({ const planchet = await cryptoApi.createPlanchet({
coinIndex: 0, coinIndex: 0,
denomPub: denom.denomPub, denomPub: denom.denomPub,
feeWithdraw: denom.feeWithdraw, feeWithdraw: denom.fees.feeWithdraw,
reservePriv: reserveKeyPair.reservePriv, reservePriv: reserveKeyPair.reservePriv,
reservePub: reserveKeyPair.reservePub, reservePub: reserveKeyPair.reservePub,
secretSeed: encodeCrock(getRandomBytes(32)), secretSeed: encodeCrock(getRandomBytes(32)),
value: denom.value, value: {
currency: denom.currency,
fraction: denom.amountFrac,
value: denom.amountVal,
},
}); });
const reqBody: ExchangeWithdrawRequest = { const reqBody: ExchangeWithdrawRequest = {
@ -192,8 +197,8 @@ export async function withdrawCoin(args: {
denomSig: ubSig, denomSig: ubSig,
denomPub: denom.denomPub, denomPub: denom.denomPub,
denomPubHash: denom.denomPubHash, denomPubHash: denom.denomPubHash,
feeDeposit: Amounts.stringify(denom.feeDeposit), feeDeposit: Amounts.stringify(denom.fees.feeDeposit),
feeRefresh: Amounts.stringify(denom.feeRefresh), feeRefresh: Amounts.stringify(denom.fees.feeRefresh),
exchangeBaseUrl: args.exchangeBaseUrl, exchangeBaseUrl: args.exchangeBaseUrl,
}; };
} }
@ -203,7 +208,12 @@ export function findDenomOrThrow(
amount: AmountString, amount: AmountString,
): DenominationRecord { ): DenominationRecord {
for (const d of exchangeInfo.keys.currentDenominations) { for (const d of exchangeInfo.keys.currentDenominations) {
if (Amounts.cmp(d.value, amount) === 0 && isWithdrawableDenom(d)) { const value: AmountJson = {
currency: d.currency,
fraction: d.amountFrac,
value: d.amountVal,
};
if (Amounts.cmp(value, amount) === 0 && isWithdrawableDenom(d)) {
return d; return d;
} }
} }
@ -281,8 +291,12 @@ export async function refreshCoin(req: {
count: 1, count: 1,
denomPub: x.denomPub, denomPub: x.denomPub,
denomPubHash: x.denomPubHash, denomPubHash: x.denomPubHash,
feeWithdraw: x.feeWithdraw, feeWithdraw: x.fees.feeWithdraw,
value: x.value, value: {
currency: x.currency,
fraction: x.amountFrac,
value: x.amountVal,
},
})), })),
}); });

View File

@ -62,6 +62,7 @@ import {
AbortStatus, AbortStatus,
CoinSourceType, CoinSourceType,
CoinStatus, CoinStatus,
DenominationRecord,
ProposalStatus, ProposalStatus,
RefreshCoinStatus, RefreshCoinStatus,
RefundState, RefundState,
@ -221,10 +222,10 @@ export async function exportBackup(
backupDenoms.push({ backupDenoms.push({
coins: backupCoinsByDenom[denom.denomPubHash] ?? [], coins: backupCoinsByDenom[denom.denomPubHash] ?? [],
denom_pub: denom.denomPub, denom_pub: denom.denomPub,
fee_deposit: Amounts.stringify(denom.feeDeposit), fee_deposit: Amounts.stringify(denom.fees.feeDeposit),
fee_refresh: Amounts.stringify(denom.feeRefresh), fee_refresh: Amounts.stringify(denom.fees.feeRefresh),
fee_refund: Amounts.stringify(denom.feeRefund), fee_refund: Amounts.stringify(denom.fees.feeRefund),
fee_withdraw: Amounts.stringify(denom.feeWithdraw), fee_withdraw: Amounts.stringify(denom.fees.feeWithdraw),
is_offered: denom.isOffered, is_offered: denom.isOffered,
is_revoked: denom.isRevoked, is_revoked: denom.isRevoked,
master_sig: denom.masterSig, master_sig: denom.masterSig,
@ -232,7 +233,7 @@ export async function exportBackup(
stamp_expire_legal: denom.stampExpireLegal, stamp_expire_legal: denom.stampExpireLegal,
stamp_expire_withdraw: denom.stampExpireWithdraw, stamp_expire_withdraw: denom.stampExpireWithdraw,
stamp_start: denom.stampStart, stamp_start: denom.stampStart,
value: Amounts.stringify(denom.value), value: Amounts.stringify(DenominationRecord.getValue(denom)),
list_issue_date: denom.listIssueDate, list_issue_date: denom.listIssueDate,
}); });
}); });

View File

@ -38,6 +38,7 @@ import {
CoinSource, CoinSource,
CoinSourceType, CoinSourceType,
CoinStatus, CoinStatus,
DenominationRecord,
DenominationVerificationStatus, DenominationVerificationStatus,
DenomSelectionState, DenomSelectionState,
OperationStatus, OperationStatus,
@ -108,7 +109,10 @@ async function recoverPayCoinSelection(
coinRecord.denomPubHash, coinRecord.denomPubHash,
]); ]);
checkBackupInvariant(!!denom); checkBackupInvariant(!!denom);
totalDepositFees = Amounts.add(totalDepositFees, denom.feeDeposit).amount; totalDepositFees = Amounts.add(
totalDepositFees,
denom.fees.feeDeposit,
).amount;
if (!coveredExchanges.has(coinRecord.exchangeBaseUrl)) { if (!coveredExchanges.has(coinRecord.exchangeBaseUrl)) {
const exchangeDetails = await getExchangeDetails( const exchangeDetails = await getExchangeDetails(
@ -175,16 +179,19 @@ async function getDenomSelStateFromBackup(
denomPubHash: string; denomPubHash: string;
count: number; count: number;
}[] = []; }[] = [];
let totalCoinValue = Amounts.getZero(d0.value.currency); let totalCoinValue = Amounts.getZero(d0.currency);
let totalWithdrawCost = Amounts.getZero(d0.value.currency); let totalWithdrawCost = Amounts.getZero(d0.currency);
for (const s of sel) { for (const s of sel) {
const d = await tx.denominations.get([exchangeBaseUrl, s.denom_pub_hash]); const d = await tx.denominations.get([exchangeBaseUrl, s.denom_pub_hash]);
checkBackupInvariant(!!d); checkBackupInvariant(!!d);
totalCoinValue = Amounts.add(totalCoinValue, d.value).amount; totalCoinValue = Amounts.add(
totalCoinValue,
DenominationRecord.getValue(d),
).amount;
totalWithdrawCost = Amounts.add( totalWithdrawCost = Amounts.add(
totalWithdrawCost, totalWithdrawCost,
d.value, DenominationRecord.getValue(d),
d.feeWithdraw, d.fees.feeWithdraw,
).amount; ).amount;
} }
return { return {
@ -352,17 +359,25 @@ export async function importBackup(
`importing backup denomination: ${j2s(backupDenomination)}`, `importing backup denomination: ${j2s(backupDenomination)}`,
); );
const value = Amounts.parseOrThrow(backupDenomination.value);
await tx.denominations.put({ await tx.denominations.put({
denomPub: backupDenomination.denom_pub, denomPub: backupDenomination.denom_pub,
denomPubHash: denomPubHash, denomPubHash: denomPubHash,
exchangeBaseUrl: backupExchangeDetails.base_url, exchangeBaseUrl: backupExchangeDetails.base_url,
exchangeMasterPub: backupExchangeDetails.master_public_key, exchangeMasterPub: backupExchangeDetails.master_public_key,
feeDeposit: Amounts.parseOrThrow(backupDenomination.fee_deposit), fees: {
feeRefresh: Amounts.parseOrThrow(backupDenomination.fee_refresh), feeDeposit: Amounts.parseOrThrow(
feeRefund: Amounts.parseOrThrow(backupDenomination.fee_refund), backupDenomination.fee_deposit,
feeWithdraw: Amounts.parseOrThrow( ),
backupDenomination.fee_withdraw, feeRefresh: Amounts.parseOrThrow(
), backupDenomination.fee_refresh,
),
feeRefund: Amounts.parseOrThrow(backupDenomination.fee_refund),
feeWithdraw: Amounts.parseOrThrow(
backupDenomination.fee_withdraw,
),
},
isOffered: backupDenomination.is_offered, isOffered: backupDenomination.is_offered,
isRevoked: backupDenomination.is_revoked, isRevoked: backupDenomination.is_revoked,
masterSig: backupDenomination.master_sig, masterSig: backupDenomination.master_sig,
@ -371,7 +386,9 @@ export async function importBackup(
stampExpireWithdraw: backupDenomination.stamp_expire_withdraw, stampExpireWithdraw: backupDenomination.stamp_expire_withdraw,
stampStart: backupDenomination.stamp_start, stampStart: backupDenomination.stamp_start,
verificationStatus: DenominationVerificationStatus.VerifiedGood, verificationStatus: DenominationVerificationStatus.VerifiedGood,
value: Amounts.parseOrThrow(backupDenomination.value), currency: value.currency,
amountFrac: value.fraction,
amountVal: value.value,
listIssueDate: backupDenomination.list_issue_date, listIssueDate: backupDenomination.list_issue_date,
}); });
} }
@ -648,7 +665,7 @@ export async function importBackup(
executionTime: backupRefund.execution_time, executionTime: backupRefund.execution_time,
obtainedTime: backupRefund.obtained_time, obtainedTime: backupRefund.obtained_time,
refundAmount: Amounts.parseOrThrow(backupRefund.refund_amount), refundAmount: Amounts.parseOrThrow(backupRefund.refund_amount),
refundFee: denom.feeRefund, refundFee: denom.fees.feeRefund,
rtransactionId: backupRefund.rtransaction_id, rtransactionId: backupRefund.rtransaction_id,
totalRefreshCostBound: Amounts.parseOrThrow( totalRefreshCostBound: Amounts.parseOrThrow(
backupRefund.total_refresh_cost_bound, backupRefund.total_refresh_cost_bound,

View File

@ -44,6 +44,7 @@ import {
URL, URL,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
DenominationRecord,
DepositGroupRecord, DepositGroupRecord,
OperationAttemptResult, OperationAttemptResult,
OperationStatus, OperationStatus,
@ -636,7 +637,10 @@ export async function getTotalFeesForDepositAmount(
const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
.iter(coin.exchangeBaseUrl) .iter(coin.exchangeBaseUrl)
.filter((x) => .filter((x) =>
Amounts.isSameCurrency(x.value, pcs.coinContributions[i]), Amounts.isSameCurrency(
DenominationRecord.getValue(x),
pcs.coinContributions[i],
),
); );
const amountLeft = Amounts.sub( const amountLeft = Amounts.sub(
denom.value, denom.value,

View File

@ -81,15 +81,18 @@ function denominationRecordFromKeys(
let denomPub: DenominationPubKey; let denomPub: DenominationPubKey;
denomPub = denomIn.denom_pub; denomPub = denomIn.denom_pub;
const denomPubHash = encodeCrock(hashDenomPub(denomPub)); const denomPubHash = encodeCrock(hashDenomPub(denomPub));
const value = Amounts.parseOrThrow(denomIn.value);
const d: DenominationRecord = { const d: DenominationRecord = {
denomPub, denomPub,
denomPubHash, denomPubHash,
exchangeBaseUrl, exchangeBaseUrl,
exchangeMasterPub, exchangeMasterPub,
feeDeposit: Amounts.parseOrThrow(denomIn.fee_deposit), fees: {
feeRefresh: Amounts.parseOrThrow(denomIn.fee_refresh), feeDeposit: Amounts.parseOrThrow(denomIn.fee_deposit),
feeRefund: Amounts.parseOrThrow(denomIn.fee_refund), feeRefresh: Amounts.parseOrThrow(denomIn.fee_refresh),
feeWithdraw: Amounts.parseOrThrow(denomIn.fee_withdraw), feeRefund: Amounts.parseOrThrow(denomIn.fee_refund),
feeWithdraw: Amounts.parseOrThrow(denomIn.fee_withdraw),
},
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
masterSig: denomIn.master_sig, masterSig: denomIn.master_sig,
@ -98,7 +101,9 @@ function denominationRecordFromKeys(
stampExpireWithdraw: denomIn.stamp_expire_withdraw, stampExpireWithdraw: denomIn.stamp_expire_withdraw,
stampStart: denomIn.stamp_start, stampStart: denomIn.stamp_start,
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: Amounts.parseOrThrow(denomIn.value), amountFrac: value.fraction,
amountVal: value.value,
currency: value.currency,
listIssueDate, listIssueDate,
}; };
return d; return d;

View File

@ -141,13 +141,20 @@ export async function getTotalPaymentCost(
const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
.iter(coin.exchangeBaseUrl) .iter(coin.exchangeBaseUrl)
.filter((x) => .filter((x) =>
Amounts.isSameCurrency(x.value, pcs.coinContributions[i]), Amounts.isSameCurrency(
DenominationRecord.getValue(x),
pcs.coinContributions[i],
),
); );
const amountLeft = Amounts.sub( const amountLeft = Amounts.sub(
denom.value, DenominationRecord.getValue(denom),
pcs.coinContributions[i], pcs.coinContributions[i],
).amount; ).amount;
const refreshCost = getTotalRefreshCost(allDenoms, denom, amountLeft); const refreshCost = getTotalRefreshCost(
allDenoms,
DenominationRecord.toDenomInfo(denom),
amountLeft,
);
costs.push(pcs.coinContributions[i]); costs.push(pcs.coinContributions[i]);
costs.push(refreshCost); costs.push(refreshCost);
} }
@ -303,7 +310,7 @@ export async function getCandidatePayCoins(
if (!denom) { if (!denom) {
throw Error("db inconsistent"); throw Error("db inconsistent");
} }
if (denom.value.currency !== currency) { if (denom.currency !== currency) {
logger.warn( logger.warn(
`same pubkey for different currencies at exchange ${exchange.baseUrl}`, `same pubkey for different currencies at exchange ${exchange.baseUrl}`,
); );
@ -314,10 +321,10 @@ export async function getCandidatePayCoins(
} }
candidateCoins.push({ candidateCoins.push({
availableAmount: coin.currentAmount, availableAmount: coin.currentAmount,
value: denom.value, value: DenominationRecord.getValue(denom),
coinPub: coin.coinPub, coinPub: coin.coinPub,
denomPub: denom.denomPub, denomPub: denom.denomPub,
feeDeposit: denom.feeDeposit, feeDeposit: denom.fees.feeDeposit,
exchangeBaseUrl: denom.exchangeBaseUrl, exchangeBaseUrl: denom.exchangeBaseUrl,
ageCommitmentProof: coin.ageCommitmentProof, ageCommitmentProof: coin.ageCommitmentProof,
}); });
@ -949,7 +956,7 @@ async function handleInsufficientFunds(
coinPub, coinPub,
contribution: contrib, contribution: contrib,
exchangeBaseUrl: coin.exchangeBaseUrl, exchangeBaseUrl: coin.exchangeBaseUrl,
feeDeposit: denom.feeDeposit, feeDeposit: denom.fees.feeDeposit,
}); });
} }
}); });
@ -1269,7 +1276,7 @@ export async function generateDepositPermissions(
denomKeyType: denom.denomPub.cipher, denomKeyType: denom.denomPub.cipher,
denomSig: coin.denomSig, denomSig: coin.denomSig,
exchangeBaseUrl: coin.exchangeBaseUrl, exchangeBaseUrl: coin.exchangeBaseUrl,
feeDeposit: denom.feeDeposit, feeDeposit: denom.fees.feeDeposit,
merchantPub: contractData.merchantPub, merchantPub: contractData.merchantPub,
refundDeadline: contractData.refundDeadline, refundDeadline: contractData.refundDeadline,
spendAmount: payCoinSel.coinContributions[i], spendAmount: payCoinSel.coinContributions[i],

View File

@ -112,7 +112,11 @@ export function getTotalRefreshCost(
const resultingAmount = Amounts.add( const resultingAmount = Amounts.add(
Amounts.getZero(withdrawAmount.currency), Amounts.getZero(withdrawAmount.currency),
...withdrawDenoms.selectedDenoms.map( ...withdrawDenoms.selectedDenoms.map(
(d) => Amounts.mult(denomMap[d.denomPubHash].value, d.count).amount, (d) =>
Amounts.mult(
DenominationRecord.getValue(denomMap[d.denomPubHash]),
d.count,
).amount,
), ),
).amount; ).amount;
const totalCost = Amounts.sub(amountLeft, resultingAmount).amount; const totalCost = Amounts.sub(amountLeft, resultingAmount).amount;

View File

@ -51,6 +51,7 @@ import {
import { import {
AbortStatus, AbortStatus,
CoinStatus, CoinStatus,
DenominationRecord,
OperationAttemptResult, OperationAttemptResult,
PurchaseRecord, PurchaseRecord,
RefundReason, RefundReason,
@ -148,7 +149,7 @@ async function applySuccessfulRefund(
} }
refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub }; refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub };
const refundAmount = Amounts.parseOrThrow(r.refund_amount); const refundAmount = Amounts.parseOrThrow(r.refund_amount);
const refundFee = denom.feeRefund; const refundFee = denom.fees.feeRefund;
coin.status = CoinStatus.Dormant; coin.status = CoinStatus.Dormant;
coin.currentAmount = Amounts.add(coin.currentAmount, refundAmount).amount; coin.currentAmount = Amounts.add(coin.currentAmount, refundAmount).amount;
coin.currentAmount = Amounts.sub(coin.currentAmount, refundFee).amount; coin.currentAmount = Amounts.sub(coin.currentAmount, refundFee).amount;
@ -162,12 +163,12 @@ async function applySuccessfulRefund(
const amountLeft = Amounts.sub( const amountLeft = Amounts.sub(
Amounts.add(coin.currentAmount, Amounts.parseOrThrow(r.refund_amount)) Amounts.add(coin.currentAmount, Amounts.parseOrThrow(r.refund_amount))
.amount, .amount,
denom.feeRefund, denom.fees.feeRefund,
).amount; ).amount;
const totalRefreshCostBound = getTotalRefreshCost( const totalRefreshCostBound = getTotalRefreshCost(
allDenoms, allDenoms,
denom, DenominationRecord.toDenomInfo(denom),
amountLeft, amountLeft,
); );
@ -176,7 +177,7 @@ async function applySuccessfulRefund(
obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()), obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
executionTime: r.execution_time, executionTime: r.execution_time,
refundAmount: Amounts.parseOrThrow(r.refund_amount), refundAmount: Amounts.parseOrThrow(r.refund_amount),
refundFee: denom.feeRefund, refundFee: denom.fees.feeRefund,
totalRefreshCostBound, totalRefreshCostBound,
coinPub: r.coin_pub, coinPub: r.coin_pub,
rtransactionId: r.rtransaction_id, rtransactionId: r.rtransaction_id,
@ -214,12 +215,12 @@ async function storePendingRefund(
const amountLeft = Amounts.sub( const amountLeft = Amounts.sub(
Amounts.add(coin.currentAmount, Amounts.parseOrThrow(r.refund_amount)) Amounts.add(coin.currentAmount, Amounts.parseOrThrow(r.refund_amount))
.amount, .amount,
denom.feeRefund, denom.fees.feeRefund,
).amount; ).amount;
const totalRefreshCostBound = getTotalRefreshCost( const totalRefreshCostBound = getTotalRefreshCost(
allDenoms, allDenoms,
denom, DenominationRecord.toDenomInfo(denom),
amountLeft, amountLeft,
); );
@ -228,7 +229,7 @@ async function storePendingRefund(
obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()), obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
executionTime: r.execution_time, executionTime: r.execution_time,
refundAmount: Amounts.parseOrThrow(r.refund_amount), refundAmount: Amounts.parseOrThrow(r.refund_amount),
refundFee: denom.feeRefund, refundFee: denom.fees.feeRefund,
totalRefreshCostBound, totalRefreshCostBound,
coinPub: r.coin_pub, coinPub: r.coin_pub,
rtransactionId: r.rtransaction_id, rtransactionId: r.rtransaction_id,
@ -267,12 +268,12 @@ async function storeFailedRefund(
const amountLeft = Amounts.sub( const amountLeft = Amounts.sub(
Amounts.add(coin.currentAmount, Amounts.parseOrThrow(r.refund_amount)) Amounts.add(coin.currentAmount, Amounts.parseOrThrow(r.refund_amount))
.amount, .amount,
denom.feeRefund, denom.fees.feeRefund,
).amount; ).amount;
const totalRefreshCostBound = getTotalRefreshCost( const totalRefreshCostBound = getTotalRefreshCost(
allDenoms, allDenoms,
denom, DenominationRecord.toDenomInfo(denom),
amountLeft, amountLeft,
); );
@ -281,7 +282,7 @@ async function storeFailedRefund(
obtainedTime: TalerProtocolTimestamp.now(), obtainedTime: TalerProtocolTimestamp.now(),
executionTime: r.execution_time, executionTime: r.execution_time,
refundAmount: Amounts.parseOrThrow(r.refund_amount), refundAmount: Amounts.parseOrThrow(r.refund_amount),
refundFee: denom.feeRefund, refundFee: denom.fees.feeRefund,
totalRefreshCostBound, totalRefreshCostBound,
coinPub: r.coin_pub, coinPub: r.coin_pub,
rtransactionId: r.rtransaction_id, rtransactionId: r.rtransaction_id,
@ -314,7 +315,7 @@ async function storeFailedRefund(
coin.currentAmount = Amounts.add(coin.currentAmount, contrib).amount; coin.currentAmount = Amounts.add(coin.currentAmount, contrib).amount;
coin.currentAmount = Amounts.sub( coin.currentAmount = Amounts.sub(
coin.currentAmount, coin.currentAmount,
denom.feeRefund, denom.fees.feeRefund,
).amount; ).amount;
} }
refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub }; refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub };

View File

@ -306,7 +306,7 @@ export async function processTip(
coinIndex: i, coinIndex: i,
walletTipId: walletTipId, walletTipId: walletTipId,
}, },
currentAmount: denom.value, currentAmount: DenominationRecord.getValue(denom),
denomPubHash: denom.denomPubHash, denomPubHash: denom.denomPubHash,
denomSig: { cipher: DenomKeyType.Rsa, rsa_signature: denomSigRsa.sig }, denomSig: { cipher: DenomKeyType.Rsa, rsa_signature: denomSigRsa.sig },
exchangeBaseUrl: tipRecord.exchangeBaseUrl, exchangeBaseUrl: tipRecord.exchangeBaseUrl,

View File

@ -38,25 +38,27 @@ test("withdrawal selection bug repro", (t) => {
"Q21FQSSG4FXNT96Z14CHXM8N1RZAG9GPHAV8PRWS0PZAAVWH7PBW6R97M2CH19KKP65NNSWXY7B6S53PT3CBM342E357ZXDDJ8RDVW8", "Q21FQSSG4FXNT96Z14CHXM8N1RZAG9GPHAV8PRWS0PZAAVWH7PBW6R97M2CH19KKP65NNSWXY7B6S53PT3CBM342E357ZXDDJ8RDVW8",
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
feeDeposit: { fees: {
currency: "KUDOS", feeDeposit: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefresh: { },
currency: "KUDOS", feeRefresh: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefund: { },
currency: "KUDOS", feeRefund: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeWithdraw: { },
currency: "KUDOS", feeWithdraw: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
value: 0,
},
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -75,11 +77,9 @@ test("withdrawal selection bug repro", (t) => {
t_s: 1585229388, t_s: 1585229388,
}, },
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: { currency: "KUDOS",
currency: "KUDOS", amountFrac: 0,
fraction: 0, amountVal: 1000,
value: 1000,
},
listIssueDate: { t_s: 0 }, listIssueDate: { t_s: 0 },
}, },
{ {
@ -94,25 +94,27 @@ test("withdrawal selection bug repro", (t) => {
"447WA23SCBATMABHA0793F92MYTBYVPYMMQHCPKMKVY5P7RZRFMQ6VRW0Y8HRA7177GTBT0TBT08R21DZD129AJ995H9G09XBFE55G8", "447WA23SCBATMABHA0793F92MYTBYVPYMMQHCPKMKVY5P7RZRFMQ6VRW0Y8HRA7177GTBT0TBT08R21DZD129AJ995H9G09XBFE55G8",
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
feeDeposit: { fees: {
currency: "KUDOS", feeDeposit: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefresh: { },
currency: "KUDOS", feeRefresh: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefund: { },
currency: "KUDOS", feeRefund: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeWithdraw: { },
currency: "KUDOS", feeWithdraw: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
value: 0,
},
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -131,11 +133,9 @@ test("withdrawal selection bug repro", (t) => {
t_s: 1585229388, t_s: 1585229388,
}, },
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: { amountFrac: 0,
currency: "KUDOS", amountVal: 10,
fraction: 0, currency: "KUDOS",
value: 10,
},
listIssueDate: { t_s: 0 }, listIssueDate: { t_s: 0 },
}, },
{ {
@ -149,25 +149,27 @@ test("withdrawal selection bug repro", (t) => {
"JS61DTKAFM0BX8Q4XV3ZSKB921SM8QK745Z2AFXTKFMBHHFNBD8TQ5ETJHFNDGBGX22FFN2A2ERNYG1SGSDQWNQHQQ2B14DBVJYJG8R", "JS61DTKAFM0BX8Q4XV3ZSKB921SM8QK745Z2AFXTKFMBHHFNBD8TQ5ETJHFNDGBGX22FFN2A2ERNYG1SGSDQWNQHQQ2B14DBVJYJG8R",
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
feeDeposit: { fees: {
currency: "KUDOS", feeDeposit: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefresh: { },
currency: "KUDOS", feeRefresh: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefund: { },
currency: "KUDOS", feeRefund: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeWithdraw: { },
currency: "KUDOS", feeWithdraw: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
value: 0,
},
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -186,11 +188,9 @@ test("withdrawal selection bug repro", (t) => {
t_s: 1585229388, t_s: 1585229388,
}, },
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: { amountFrac: 0,
currency: "KUDOS", amountVal: 5,
fraction: 0, currency: "KUDOS",
value: 5,
},
listIssueDate: { t_s: 0 }, listIssueDate: { t_s: 0 },
}, },
{ {
@ -205,25 +205,27 @@ test("withdrawal selection bug repro", (t) => {
"8T51NEY81VMPQ180EQ5WR0YH7GMNNT90W55Q0514KZM18AZT71FHJGJHQXGK0WTA7ACN1X2SD0S53XPBQ1A9KH960R48VCVVM6E3TH8", "8T51NEY81VMPQ180EQ5WR0YH7GMNNT90W55Q0514KZM18AZT71FHJGJHQXGK0WTA7ACN1X2SD0S53XPBQ1A9KH960R48VCVVM6E3TH8",
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
feeDeposit: { fees: {
currency: "KUDOS", feeDeposit: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefresh: { },
currency: "KUDOS", feeRefresh: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefund: { },
currency: "KUDOS", feeRefund: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeWithdraw: { },
currency: "KUDOS", feeWithdraw: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
value: 0,
},
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -242,11 +244,9 @@ test("withdrawal selection bug repro", (t) => {
t_s: 1585229388, t_s: 1585229388,
}, },
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: { amountFrac: 0,
currency: "KUDOS", amountVal: 1,
fraction: 0, currency: "KUDOS",
value: 1,
},
listIssueDate: { t_s: 0 }, listIssueDate: { t_s: 0 },
}, },
{ {
@ -260,25 +260,27 @@ test("withdrawal selection bug repro", (t) => {
"A41HW0Q2H9PCNMEWW0C0N45QAYVXZ8SBVRRAHE4W6X24SV1TH38ANTWDT80JXEBW9Z8PVPGT9GFV2EYZWJ5JW5W1N34NFNKHQSZ1PFR", "A41HW0Q2H9PCNMEWW0C0N45QAYVXZ8SBVRRAHE4W6X24SV1TH38ANTWDT80JXEBW9Z8PVPGT9GFV2EYZWJ5JW5W1N34NFNKHQSZ1PFR",
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
feeDeposit: { fees: {
currency: "KUDOS", feeDeposit: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefresh: { },
currency: "KUDOS", feeRefresh: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefund: { },
currency: "KUDOS", feeRefund: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeWithdraw: { },
currency: "KUDOS", feeWithdraw: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
value: 0,
},
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -297,11 +299,9 @@ test("withdrawal selection bug repro", (t) => {
t_s: 1585229388, t_s: 1585229388,
}, },
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: { amountFrac: 10000000,
currency: "KUDOS", amountVal: 0,
fraction: 10000000, currency: "KUDOS",
value: 0,
},
listIssueDate: { t_s: 0 }, listIssueDate: { t_s: 0 },
}, },
{ {
@ -315,25 +315,27 @@ test("withdrawal selection bug repro", (t) => {
"F5NGBX33DTV4595XZZVK0S2MA1VMXFEJQERE5EBP5DS4QQ9EFRANN7YHWC1TKSHT2K6CQWDBRES8D3DWR0KZF5RET40B4AZXZ0RW1ZG", "F5NGBX33DTV4595XZZVK0S2MA1VMXFEJQERE5EBP5DS4QQ9EFRANN7YHWC1TKSHT2K6CQWDBRES8D3DWR0KZF5RET40B4AZXZ0RW1ZG",
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
feeDeposit: { fees: {
currency: "KUDOS", feeDeposit: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefresh: { },
currency: "KUDOS", feeRefresh: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeRefund: { },
currency: "KUDOS", feeRefund: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
}, value: 0,
feeWithdraw: { },
currency: "KUDOS", feeWithdraw: {
fraction: 1000000, currency: "KUDOS",
value: 0, fraction: 1000000,
value: 0,
},
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -352,11 +354,9 @@ test("withdrawal selection bug repro", (t) => {
t_s: 1585229388, t_s: 1585229388,
}, },
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: { amountFrac: 0,
currency: "KUDOS", amountVal: 2,
fraction: 0, currency: "KUDOS",
value: 2,
},
listIssueDate: { t_s: 0 }, listIssueDate: { t_s: 0 },
}, },
]; ];

View File

@ -243,11 +243,19 @@ export function selectWithdrawalDenominations(
let totalWithdrawCost = Amounts.getZero(amountAvailable.currency); let totalWithdrawCost = Amounts.getZero(amountAvailable.currency);
denoms = denoms.filter(isWithdrawableDenom); denoms = denoms.filter(isWithdrawableDenom);
denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value)); denoms.sort((d1, d2) =>
Amounts.cmp(
DenominationRecord.getValue(d2),
DenominationRecord.getValue(d1),
),
);
for (const d of denoms) { for (const d of denoms) {
let count = 0; let count = 0;
const cost = Amounts.add(d.value, d.feeWithdraw).amount; const cost = Amounts.add(
DenominationRecord.getValue(d),
d.fees.feeWithdraw,
).amount;
for (;;) { for (;;) {
if (Amounts.cmp(remaining, cost) < 0) { if (Amounts.cmp(remaining, cost) < 0) {
break; break;
@ -258,7 +266,7 @@ export function selectWithdrawalDenominations(
if (count > 0) { if (count > 0) {
totalCoinValue = Amounts.add( totalCoinValue = Amounts.add(
totalCoinValue, totalCoinValue,
Amounts.mult(d.value, count).amount, Amounts.mult(DenominationRecord.getValue(d), count).amount,
).amount; ).amount;
totalWithdrawCost = Amounts.add( totalWithdrawCost = Amounts.add(
totalWithdrawCost, totalWithdrawCost,
@ -306,22 +314,30 @@ export function selectForcedWithdrawalDenominations(
let totalWithdrawCost = Amounts.getZero(amountAvailable.currency); let totalWithdrawCost = Amounts.getZero(amountAvailable.currency);
denoms = denoms.filter(isWithdrawableDenom); denoms = denoms.filter(isWithdrawableDenom);
denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value)); denoms.sort((d1, d2) =>
Amounts.cmp(
DenominationRecord.getValue(d2),
DenominationRecord.getValue(d1),
),
);
for (const fds of forcedDenomSel.denoms) { for (const fds of forcedDenomSel.denoms) {
const count = fds.count; const count = fds.count;
const denom = denoms.find((x) => { const denom = denoms.find((x) => {
return Amounts.cmp(x.value, fds.value) == 0; return Amounts.cmp(DenominationRecord.getValue(x), fds.value) == 0;
}); });
if (!denom) { if (!denom) {
throw Error( throw Error(
`unable to find denom for forced selection (value ${fds.value})`, `unable to find denom for forced selection (value ${fds.value})`,
); );
} }
const cost = Amounts.add(denom.value, denom.feeWithdraw).amount; const cost = Amounts.add(
DenominationRecord.getValue(denom),
denom.fees.feeWithdraw,
).amount;
totalCoinValue = Amounts.add( totalCoinValue = Amounts.add(
totalCoinValue, totalCoinValue,
Amounts.mult(denom.value, count).amount, Amounts.mult(DenominationRecord.getValue(denom), count).amount,
).amount; ).amount;
totalWithdrawCost = Amounts.add( totalWithdrawCost = Amounts.add(
totalWithdrawCost, totalWithdrawCost,

View File

@ -102,6 +102,7 @@ import {
CoinRecord, CoinRecord,
CoinSourceType, CoinSourceType,
CoinStatus, CoinStatus,
DenominationRecord,
exportDb, exportDb,
importDb, importDb,
OperationAttemptResult, OperationAttemptResult,
@ -731,14 +732,29 @@ async function getExchangeDetailedInfo(
return; return;
} }
const denominations = await tx.denominations.indexes.byExchangeBaseUrl const denominationRecords =
.iter(ex.baseUrl) await tx.denominations.indexes.byExchangeBaseUrl
.toArray(); .iter(ex.baseUrl)
.toArray();
if (!denominations) { if (!denominationRecords) {
return; return;
} }
const denominations: DenomInfo[] = denominationRecords.map((x) => ({
denomPub: x.denomPub,
denomPubHash: x.denomPubHash,
feeDeposit: x.fees.feeDeposit,
feeRefresh: x.fees.feeRefresh,
feeRefund: x.fees.feeRefund,
feeWithdraw: x.fees.feeWithdraw,
stampExpireDeposit: x.stampExpireDeposit,
stampExpireLegal: x.stampExpireLegal,
stampExpireWithdraw: x.stampExpireWithdraw,
stampStart: x.stampStart,
value: DenominationRecord.getValue(x),
}));
return { return {
info: { info: {
exchangeBaseUrl: ex.baseUrl, exchangeBaseUrl: ex.baseUrl,
@ -753,7 +769,7 @@ async function getExchangeDetailedInfo(
auditors: exchangeDetails.auditors, auditors: exchangeDetails.auditors,
wireInfo: exchangeDetails.wireInfo, wireInfo: exchangeDetails.wireInfo,
}, },
denominations: denominations, denominations,
}; };
}); });
@ -969,7 +985,11 @@ async function dumpCoins(ws: InternalWalletState): Promise<CoinDumpJson> {
coin_pub: c.coinPub, coin_pub: c.coinPub,
denom_pub: denomInfo.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({
value: denom.amountVal,
currency: denom.currency,
fraction: denom.amountFrac,
}),
exchange_base_url: c.exchangeBaseUrl, exchange_base_url: c.exchangeBaseUrl,
refresh_parent_coin_pub: refreshParentCoinPub, refresh_parent_coin_pub: refreshParentCoinPub,
remaining_value: Amounts.stringify(c.currentAmount), remaining_value: Amounts.stringify(c.currentAmount),
@ -1566,9 +1586,22 @@ class InternalWalletStateImpl implements InternalWalletState {
} }
const d = await tx.denominations.get([exchangeBaseUrl, denomPubHash]); const d = await tx.denominations.get([exchangeBaseUrl, denomPubHash]);
if (d) { if (d) {
this.denomCache[key] = d; const denomInfo = {
denomPub: d.denomPub,
denomPubHash: d.denomPubHash,
feeDeposit: d.fees.feeDeposit,
feeRefresh: d.fees.feeRefresh,
feeRefund: d.fees.feeRefund,
feeWithdraw: d.fees.feeWithdraw,
stampExpireDeposit: d.stampExpireDeposit,
stampExpireLegal: d.stampExpireLegal,
stampExpireWithdraw: d.stampExpireWithdraw,
stampStart: d.stampStart,
value: DenominationRecord.getValue(d),
};
return denomInfo;
} }
return d; return undefined;
} }
notify(n: WalletNotification): void { notify(n: WalletNotification): void {