wallet-core: restructure denomination record for easier querying
This commit is contained in:
parent
c021876b41
commit
a66b636dee
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
|
},
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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],
|
||||||
|
@ -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;
|
||||||
|
@ -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 };
|
||||||
|
@ -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,
|
||||||
|
@ -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 },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user