wallet-core: introduce easier syntax for transactions

This commit is contained in:
Florian Dold 2022-09-13 13:25:41 +02:00
parent 13e7a67477
commit 48540f6264
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
22 changed files with 428 additions and 667 deletions

View File

@ -46,9 +46,13 @@ function upgradeFromStoreMap(
): void {
if (oldVersion === 0) {
for (const n in storeMap) {
const swi: StoreWithIndexes<StoreDescriptor<unknown>, any> = storeMap[n];
const swi: StoreWithIndexes<
any,
StoreDescriptor<unknown>,
any
> = storeMap[n];
const storeDesc: StoreDescriptor<unknown> = swi.store;
const s = db.createObjectStore(storeDesc.name, {
const s = db.createObjectStore(swi.storeName, {
autoIncrement: storeDesc.autoIncrement,
keyPath: storeDesc.keyPath,
});
@ -117,9 +121,7 @@ export async function openTalerDatabase(
const metaDb = new DbAccess(metaDbHandle, walletMetadataStore);
let currentMainVersion: string | undefined;
await metaDb
.mktx((x) => ({
metaConfig: x.metaConfig,
}))
.mktx((stores) => [stores.metaConfig])
.runReadWrite(async (tx) => {
const dbVersionRecord = await tx.metaConfig.get(CURRENT_DB_CONFIG_KEY);
if (!dbVersionRecord) {
@ -141,9 +143,7 @@ export async function openTalerDatabase(
// We consider this a pre-release
// development version, no migration is done.
await metaDb
.mktx((x) => ({
metaConfig: x.metaConfig,
}))
.mktx((stores) => [stores.metaConfig])
.runReadWrite(async (tx) => {
await tx.metaConfig.put({
key: CURRENT_DB_CONFIG_KEY,

View File

@ -1079,7 +1079,7 @@ export interface PurchaseRecord {
/**
* Pending refunds for the purchase. A refund is pending
* when the merchant reports a transient error from the exchange.
*
*
* FIXME: Put this into a separate object store?
*/
refunds: { [refundKey: string]: WalletRefundItem };
@ -1733,7 +1733,8 @@ export interface OperationAttemptLongpollResult {
export const WalletStoresV1 = {
coins: describeStore(
describeContents<CoinRecord>("coins", {
"coins",
describeContents<CoinRecord>({
keyPath: "coinPub",
}),
{
@ -1743,17 +1744,20 @@ export const WalletStoresV1 = {
},
),
reserves: describeStore(
describeContents<ReserveRecord>("reserves", {
"reserves",
describeContents<ReserveRecord>({
keyPath: "reservePub",
}),
{},
),
config: describeStore(
describeContents<ConfigRecord>("config", { keyPath: "key" }),
"config",
describeContents<ConfigRecord>({ keyPath: "key" }),
{},
),
auditorTrust: describeStore(
describeContents<AuditorTrustRecord>("auditorTrust", {
"auditorTrust",
describeContents<AuditorTrustRecord>({
keyPath: ["currency", "auditorBaseUrl"],
}),
{
@ -1764,7 +1768,8 @@ export const WalletStoresV1 = {
},
),
exchangeTrust: describeStore(
describeContents<ExchangeTrustRecord>("exchangeTrust", {
"exchangeTrust",
describeContents<ExchangeTrustRecord>({
keyPath: ["currency", "exchangeBaseUrl"],
}),
{
@ -1775,7 +1780,8 @@ export const WalletStoresV1 = {
},
),
denominations: describeStore(
describeContents<DenominationRecord>("denominations", {
"denominations",
describeContents<DenominationRecord>({
keyPath: ["exchangeBaseUrl", "denomPubHash"],
}),
{
@ -1783,19 +1789,22 @@ export const WalletStoresV1 = {
},
),
exchanges: describeStore(
describeContents<ExchangeRecord>("exchanges", {
"exchanges",
describeContents<ExchangeRecord>({
keyPath: "baseUrl",
}),
{},
),
exchangeDetails: describeStore(
describeContents<ExchangeDetailsRecord>("exchangeDetails", {
"exchangeDetails",
describeContents<ExchangeDetailsRecord>({
keyPath: ["exchangeBaseUrl", "currency", "masterPublicKey"],
}),
{},
),
proposals: describeStore(
describeContents<ProposalRecord>("proposals", { keyPath: "proposalId" }),
"proposals",
describeContents<ProposalRecord>({ keyPath: "proposalId" }),
{
byUrlAndOrderId: describeIndex("byUrlAndOrderId", [
"merchantBaseUrl",
@ -1804,7 +1813,8 @@ export const WalletStoresV1 = {
},
),
refreshGroups: describeStore(
describeContents<RefreshGroupRecord>("refreshGroups", {
"refreshGroups",
describeContents<RefreshGroupRecord>({
keyPath: "refreshGroupId",
}),
{
@ -1812,13 +1822,15 @@ export const WalletStoresV1 = {
},
),
recoupGroups: describeStore(
describeContents<RecoupGroupRecord>("recoupGroups", {
"recoupGroups",
describeContents<RecoupGroupRecord>({
keyPath: "recoupGroupId",
}),
{},
),
purchases: describeStore(
describeContents<PurchaseRecord>("purchases", { keyPath: "proposalId" }),
"purchases",
describeContents<PurchaseRecord>({ keyPath: "proposalId" }),
{
byFulfillmentUrl: describeIndex(
"byFulfillmentUrl",
@ -1831,7 +1843,8 @@ export const WalletStoresV1 = {
},
),
tips: describeStore(
describeContents<TipRecord>("tips", { keyPath: "walletTipId" }),
"tips",
describeContents<TipRecord>({ keyPath: "walletTipId" }),
{
byMerchantTipIdAndBaseUrl: describeIndex("byMerchantTipIdAndBaseUrl", [
"merchantTipId",
@ -1840,7 +1853,8 @@ export const WalletStoresV1 = {
},
),
withdrawalGroups: describeStore(
describeContents<WithdrawalGroupRecord>("withdrawalGroups", {
"withdrawalGroups",
describeContents<WithdrawalGroupRecord>({
keyPath: "withdrawalGroupId",
}),
{
@ -1853,7 +1867,8 @@ export const WalletStoresV1 = {
},
),
planchets: describeStore(
describeContents<PlanchetRecord>("planchets", { keyPath: "coinPub" }),
"planchets",
describeContents<PlanchetRecord>({ keyPath: "coinPub" }),
{
byGroupAndIndex: describeIndex("byGroupAndIndex", [
"withdrawalGroupId",
@ -1864,13 +1879,15 @@ export const WalletStoresV1 = {
},
),
bankWithdrawUris: describeStore(
describeContents<BankWithdrawUriRecord>("bankWithdrawUris", {
"bankWithdrawUris",
describeContents<BankWithdrawUriRecord>({
keyPath: "talerWithdrawUri",
}),
{},
),
backupProviders: describeStore(
describeContents<BackupProviderRecord>("backupProviders", {
"backupProviders",
describeContents<BackupProviderRecord>({
keyPath: "baseUrl",
}),
{
@ -1884,7 +1901,8 @@ export const WalletStoresV1 = {
},
),
depositGroups: describeStore(
describeContents<DepositGroupRecord>("depositGroups", {
"depositGroups",
describeContents<DepositGroupRecord>({
keyPath: "depositGroupId",
}),
{
@ -1892,29 +1910,34 @@ export const WalletStoresV1 = {
},
),
tombstones: describeStore(
describeContents<TombstoneRecord>("tombstones", { keyPath: "id" }),
"tombstones",
describeContents<TombstoneRecord>({ keyPath: "id" }),
{},
),
operationRetries: describeStore(
describeContents<OperationRetryRecord>("operationRetries", {
"operationRetries",
describeContents<OperationRetryRecord>({
keyPath: "id",
}),
{},
),
ghostDepositGroups: describeStore(
describeContents<GhostDepositGroupRecord>("ghostDepositGroups", {
"ghostDepositGroups",
describeContents<GhostDepositGroupRecord>({
keyPath: "contractTermsHash",
}),
{},
),
balancesPerCurrency: describeStore(
describeContents<BalancePerCurrencyRecord>("balancesPerCurrency", {
"balancesPerCurrency",
describeContents<BalancePerCurrencyRecord>({
keyPath: "currency",
}),
{},
),
peerPushPaymentIncoming: describeStore(
describeContents<PeerPushPaymentIncomingRecord>("peerPushPaymentIncoming", {
"peerPushPaymentIncoming",
describeContents<PeerPushPaymentIncomingRecord>({
keyPath: "peerPushPaymentIncomingId",
}),
{
@ -1925,7 +1948,8 @@ export const WalletStoresV1 = {
},
),
peerPullPaymentIncoming: describeStore(
describeContents<PeerPullPaymentIncomingRecord>("peerPullPaymentIncoming", {
"peerPullPaymentIncoming",
describeContents<PeerPullPaymentIncomingRecord>({
keyPath: "peerPullPaymentIncomingId",
}),
{
@ -1936,21 +1960,17 @@ export const WalletStoresV1 = {
},
),
peerPullPaymentInitiations: describeStore(
describeContents<PeerPullPaymentInitiationRecord>(
"peerPullPaymentInitiations",
{
keyPath: "pursePub",
},
),
"peerPullPaymentInitiations",
describeContents<PeerPullPaymentInitiationRecord>({
keyPath: "pursePub",
}),
{},
),
peerPushPaymentInitiations: describeStore(
describeContents<PeerPushPaymentInitiationRecord>(
"peerPushPaymentInitiations",
{
keyPath: "pursePub",
},
),
"peerPushPaymentInitiations",
describeContents<PeerPushPaymentInitiationRecord>({
keyPath: "pursePub",
}),
{},
),
};
@ -1962,7 +1982,8 @@ export interface MetaConfigRecord {
export const walletMetadataStore = {
metaConfig: describeStore(
describeContents<MetaConfigRecord>("metaConfig", { keyPath: "key" }),
"metaConfig",
describeContents<MetaConfigRecord>({ keyPath: "key" }),
{},
),
};

View File

@ -76,20 +76,20 @@ export async function exportBackup(
): Promise<WalletBackupContentV1> {
await provideBackupState(ws);
return ws.db
.mktx((x) => ({
config: x.config,
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
coins: x.coins,
denominations: x.denominations,
purchases: x.purchases,
proposals: x.proposals,
refreshGroups: x.refreshGroups,
backupProviders: x.backupProviders,
tips: x.tips,
recoupGroups: x.recoupGroups,
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [
x.config,
x.exchanges,
x.exchangeDetails,
x.coins,
x.denominations,
x.purchases,
x.proposals,
x.refreshGroups,
x.backupProviders,
x.tips,
x.recoupGroups,
x.withdrawalGroups,
])
.runReadWrite(async (tx) => {
const bs = await getWalletBackupState(ws, tx);

View File

@ -224,22 +224,22 @@ export async function importBackup(
logger.info(`importing backup ${j2s(backupBlobArg)}`);
return ws.db
.mktx((x) => ({
config: x.config,
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
coins: x.coins,
denominations: x.denominations,
purchases: x.purchases,
proposals: x.proposals,
refreshGroups: x.refreshGroups,
backupProviders: x.backupProviders,
tips: x.tips,
recoupGroups: x.recoupGroups,
withdrawalGroups: x.withdrawalGroups,
tombstones: x.tombstones,
depositGroups: x.depositGroups,
}))
.mktx((x) => [
x.config,
x.exchangeDetails,
x.exchanges,
x.coins,
x.denominations,
x.purchases,
x.proposals,
x.refreshGroups,
x.backupProviders,
x.tips,
x.recoupGroups,
x.withdrawalGroups,
x.tombstones,
x.depositGroups,
])
.runReadWrite(async (tx) => {
// FIXME: validate schema!
const backupBlob = backupBlobArg as WalletBackupContentV1;

View File

@ -264,7 +264,7 @@ async function runBackupCycleForProvider(
args: BackupForProviderArgs,
): Promise<OperationAttemptResult> {
const provider = await ws.db
.mktx((x) => ({ backupProviders: x.backupProviders }))
.mktx((x) => [x.backupProviders])
.runReadOnly(async (tx) => {
return tx.backupProviders.get(args.backupProviderBaseUrl);
});
@ -322,9 +322,9 @@ async function runBackupCycleForProvider(
if (resp.status === HttpStatusCode.NotModified) {
await ws.db
.mktx((x) => ({ backupProvider: x.backupProviders }))
.mktx((x) => [x.backupProviders])
.runReadWrite(async (tx) => {
const prov = await tx.backupProvider.get(provider.baseUrl);
const prov = await tx.backupProviders.get(provider.baseUrl);
if (!prov) {
return;
}
@ -333,7 +333,7 @@ async function runBackupCycleForProvider(
tag: BackupProviderStateTag.Ready,
nextBackupTimestamp: getNextBackupTimestamp(),
};
await tx.backupProvider.put(prov);
await tx.backupProviders.put(prov);
});
return {
type: OperationAttemptResultType.Finished,
@ -367,10 +367,7 @@ async function runBackupCycleForProvider(
// FIXME: check if the provider is overcharging us!
await ws.db
.mktx((x) => ({
backupProviders: x.backupProviders,
operationRetries: x.operationRetries,
}))
.mktx((x) => [x.backupProviders, x.operationRetries])
.runReadWrite(async (tx) => {
const provRec = await tx.backupProviders.get(provider.baseUrl);
checkDbInvariant(!!provRec);
@ -407,7 +404,7 @@ async function runBackupCycleForProvider(
if (resp.status === HttpStatusCode.NoContent) {
await ws.db
.mktx((x) => ({ backupProviders: x.backupProviders }))
.mktx((x) => [x.backupProviders])
.runReadWrite(async (tx) => {
const prov = await tx.backupProviders.get(provider.baseUrl);
if (!prov) {
@ -435,12 +432,9 @@ async function runBackupCycleForProvider(
const cryptoData = await computeBackupCryptoData(ws.cryptoApi, blob);
await importBackup(ws, blob, cryptoData);
await ws.db
.mktx((x) => ({
backupProvider: x.backupProviders,
operationRetries: x.operationRetries,
}))
.mktx((x) => [x.backupProviders, x.operationRetries])
.runReadWrite(async (tx) => {
const prov = await tx.backupProvider.get(provider.baseUrl);
const prov = await tx.backupProviders.get(provider.baseUrl);
if (!prov) {
logger.warn("backup provider not found anymore");
return;
@ -453,7 +447,7 @@ async function runBackupCycleForProvider(
prov.state = {
tag: BackupProviderStateTag.Retrying,
};
await tx.backupProvider.put(prov);
await tx.backupProviders.put(prov);
});
logger.info("processed existing backup");
// Now upload our own, merged backup.
@ -480,7 +474,7 @@ export async function processBackupForProvider(
backupProviderBaseUrl: string,
): Promise<OperationAttemptResult> {
const provider = await ws.db
.mktx((x) => ({ backupProviders: x.backupProviders }))
.mktx((x) => [x.backupProviders])
.runReadOnly(async (tx) => {
return await tx.backupProviders.get(backupProviderBaseUrl);
});
@ -509,7 +503,7 @@ export async function removeBackupProvider(
req: RemoveBackupProviderRequest,
): Promise<void> {
await ws.db
.mktx(({ backupProviders }) => ({ backupProviders }))
.mktx((x) => [x.backupProviders])
.runReadWrite(async (tx) => {
await tx.backupProviders.delete(req.provider);
});
@ -539,7 +533,7 @@ export async function runBackupCycle(
req: RunBackupCycleRequest,
): Promise<void> {
const providers = await ws.db
.mktx((x) => ({ backupProviders: x.backupProviders }))
.mktx((x) => [x.backupProviders])
.runReadOnly(async (tx) => {
if (req.providers) {
const rs = await Promise.all(
@ -605,7 +599,7 @@ export async function addBackupProvider(
await provideBackupState(ws);
const canonUrl = canonicalizeBaseUrl(req.backupProviderBaseUrl);
await ws.db
.mktx((x) => ({ backupProviders: x.backupProviders }))
.mktx((x) => [x.backupProviders])
.runReadWrite(async (tx) => {
const oldProv = await tx.backupProviders.get(canonUrl);
if (oldProv) {
@ -628,7 +622,7 @@ export async function addBackupProvider(
codecForSyncTermsOfServiceResponse(),
);
await ws.db
.mktx((x) => ({ backupProviders: x.backupProviders }))
.mktx((x) => [x.backupProviders])
.runReadWrite(async (tx) => {
let state: BackupProviderState;
if (req.activate) {
@ -807,10 +801,7 @@ export async function getBackupInfo(
): Promise<BackupInfo> {
const backupConfig = await provideBackupState(ws);
const providerRecords = await ws.db
.mktx((x) => ({
backupProviders: x.backupProviders,
operationRetries: x.operationRetries,
}))
.mktx((x) => [x.backupProviders, x.operationRetries])
.runReadOnly(async (tx) => {
return await tx.backupProviders.iter().mapAsync(async (bp) => {
const opId = RetryTags.forBackup(bp);
@ -853,7 +844,7 @@ export async function getBackupRecovery(
): Promise<BackupRecovery> {
const bs = await provideBackupState(ws);
const providers = await ws.db
.mktx((x) => ({ backupProviders: x.backupProviders }))
.mktx((x) => [x.backupProviders])
.runReadOnly(async (tx) => {
return await tx.backupProviders.iter().toArray();
});
@ -874,7 +865,7 @@ async function backupRecoveryTheirs(
br: BackupRecovery,
) {
await ws.db
.mktx((x) => ({ config: x.config, backupProviders: x.backupProviders }))
.mktx((x) => [x.config, x.backupProviders])
.runReadWrite(async (tx) => {
let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
WALLET_BACKUP_STATE_KEY,
@ -924,7 +915,7 @@ export async function loadBackupRecovery(
): Promise<void> {
const bs = await provideBackupState(ws);
const providers = await ws.db
.mktx((x) => ({ backupProviders: x.backupProviders }))
.mktx((x) => [x.backupProviders])
.runReadOnly(async (tx) => {
return await tx.backupProviders.iter().toArray();
});
@ -954,7 +945,7 @@ export async function exportBackupEncrypted(
await provideBackupState(ws);
const blob = await exportBackup(ws);
const bs = await ws.db
.mktx((x) => ({ config: x.config }))
.mktx((x) => [x.config])
.runReadOnly(async (tx) => {
return await getWalletBackupState(ws, tx);
});

View File

@ -29,9 +29,7 @@ export async function provideBackupState(
ws: InternalWalletState,
): Promise<WalletBackupConfState> {
const bs: ConfigRecord | undefined = await ws.db
.mktx((x) => ({
config: x.config,
}))
.mktx((stores) => [stores.config])
.runReadOnly(async (tx) => {
return await tx.config.get(WALLET_BACKUP_STATE_KEY);
});
@ -47,9 +45,7 @@ export async function provideBackupState(
// and be based on hostname
const deviceId = `wallet-core-${encodeCrock(d)}`;
return await ws.db
.mktx((x) => ({
config: x.config,
}))
.mktx((x) => [x.config])
.runReadWrite(async (tx) => {
let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
WALLET_BACKUP_STATE_KEY,
@ -87,9 +83,7 @@ export async function setWalletDeviceId(
): Promise<void> {
await provideBackupState(ws);
await ws.db
.mktx((x) => ({
config: x.config,
}))
.mktx((x) => [x.config])
.runReadWrite(async (tx) => {
let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
WALLET_BACKUP_STATE_KEY,

View File

@ -139,12 +139,7 @@ export async function getBalances(
logger.trace("starting to compute balance");
const wbal = await ws.db
.mktx((x) => ({
coins: x.coins,
refreshGroups: x.refreshGroups,
purchases: x.purchases,
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.coins, x.refreshGroups, x.purchases, x.withdrawalGroups])
.runReadOnly(async (tx) => {
return getBalancesInsideTransaction(ws, tx);
});

View File

@ -83,9 +83,7 @@ export async function processDepositGroup(
} = {},
): Promise<OperationAttemptResult> {
const depositGroup = await ws.db
.mktx((x) => ({
depositGroups: x.depositGroups,
}))
.mktx((x) => [x.depositGroups])
.runReadOnly(async (tx) => {
return tx.depositGroups.get(depositGroupId);
});
@ -141,7 +139,7 @@ export async function processDepositGroup(
});
await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess());
await ws.db
.mktx((x) => ({ depositGroups: x.depositGroups }))
.mktx((x) => [x.depositGroups])
.runReadWrite(async (tx) => {
const dg = await tx.depositGroups.get(depositGroupId);
if (!dg) {
@ -153,9 +151,7 @@ export async function processDepositGroup(
}
await ws.db
.mktx((x) => ({
depositGroups: x.depositGroups,
}))
.mktx((x) => [x.depositGroups])
.runReadWrite(async (tx) => {
const dg = await tx.depositGroups.get(depositGroupId);
if (!dg) {
@ -185,9 +181,7 @@ export async function trackDepositGroup(
body: any;
}[] = [];
const depositGroup = await ws.db
.mktx((x) => ({
depositGroups: x.depositGroups,
}))
.mktx((x) => [x.depositGroups])
.runReadOnly(async (tx) => {
return tx.depositGroups.get(req.depositGroupId);
});
@ -247,10 +241,7 @@ export async function getFeeForDeposit(
const exchangeInfos: { url: string; master_pub: string }[] = [];
await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadOnly(async (tx) => {
const allExchanges = await tx.exchanges.iter().toArray();
for (const e of allExchanges) {
@ -315,10 +306,7 @@ export async function prepareDepositGroup(
const exchangeInfos: { url: string; master_pub: string }[] = [];
await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadOnly(async (tx) => {
const allExchanges = await tx.exchanges.iter().toArray();
for (const e of allExchanges) {
@ -417,10 +405,7 @@ export async function createDepositGroup(
const exchangeInfos: { url: string; master_pub: string }[] = [];
await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadOnly(async (tx) => {
const allExchanges = await tx.exchanges.iter().toArray();
for (const e of allExchanges) {
@ -532,12 +517,13 @@ export async function createDepositGroup(
};
await ws.db
.mktx((x) => ({
depositGroups: x.depositGroups,
coins: x.coins,
refreshGroups: x.refreshGroups,
denominations: x.denominations,
}))
.mktx((x) => [
x.depositGroups,
x.coins,
x.recoupGroups,
x.denominations,
x.refreshGroups,
])
.runReadWrite(async (tx) => {
await applyCoinSpend(
ws,
@ -565,12 +551,7 @@ export async function getEffectiveDepositAmount(
const exchangeSet: Set<string> = new Set();
await ws.db
.mktx((x) => ({
coins: x.coins,
denominations: x.denominations,
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
}))
.mktx((x) => [x.coins, x.denominations, x.exchanges, x.exchangeDetails])
.runReadOnly(async (tx) => {
for (let i = 0; i < pcs.coinPubs.length; i++) {
const coin = await tx.coins.get(pcs.coinPubs[i]);
@ -637,12 +618,7 @@ export async function getTotalFeesForDepositAmount(
const exchangeSet: Set<string> = new Set();
await ws.db
.mktx((x) => ({
coins: x.coins,
denominations: x.denominations,
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
}))
.mktx((x) => [x.coins, x.denominations, x.exchanges, x.exchangeDetails])
.runReadOnly(async (tx) => {
for (let i = 0; i < pcs.coinPubs.length; i++) {
const coin = await tx.coins.get(pcs.coinPubs[i]);

View File

@ -161,10 +161,7 @@ export async function getExchangeDetails(
}
getExchangeDetails.makeContext = (db: DbAccess<typeof WalletStoresV1>) =>
db.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
}));
db.mktx((x) => [x.exchanges, x.exchangeDetails]);
export async function updateExchangeTermsOfService(
ws: InternalWalletState,
@ -172,10 +169,7 @@ export async function updateExchangeTermsOfService(
tos: ExchangeTosDownloadResult,
): Promise<void> {
await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadWrite(async (tx) => {
const d = await getExchangeDetails(tx, exchangeBaseUrl);
if (d) {
@ -193,10 +187,7 @@ export async function acceptExchangeTermsOfService(
etag: string | undefined,
): Promise<void> {
await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadWrite(async (tx) => {
const d = await getExchangeDetails(tx, exchangeBaseUrl);
if (d) {
@ -326,10 +317,7 @@ async function provideExchangeRecord(
exchangeDetails: ExchangeDetailsRecord | undefined;
}> {
return await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadWrite(async (tx) => {
let exchange = await tx.exchanges.get(baseUrl);
if (!exchange) {
@ -569,14 +557,14 @@ export async function updateExchangeFromUrlHandler(
logger.trace("updating exchange info in database");
const updated = await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
denominations: x.denominations,
coins: x.coins,
refreshGroups: x.refreshGroups,
recoupGroups: x.recoupGroups,
}))
.mktx((x) => [
x.exchanges,
x.exchangeDetails,
x.denominations,
x.coins,
x.refreshGroups,
x.recoupGroups,
])
.runReadWrite(async (tx) => {
const r = await tx.exchanges.get(baseUrl);
if (!r) {
@ -770,12 +758,12 @@ export async function getExchangeTrust(
let isAudited = false;
return await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
exchangesTrustStore: x.exchangeTrust,
auditorTrust: x.auditorTrust,
}))
.mktx((x) => [
x.exchanges,
x.exchangeDetails,
x.exchangeTrust,
x.auditorTrust,
])
.runReadOnly(async (tx) => {
const exchangeDetails = await getExchangeDetails(
tx,
@ -786,7 +774,7 @@ export async function getExchangeTrust(
throw Error(`exchange ${exchangeInfo.baseUrl} details not available`);
}
const exchangeTrustRecord =
await tx.exchangesTrustStore.indexes.byExchangeMasterPub.get(
await tx.exchangeTrust.indexes.byExchangeMasterPub.get(
exchangeDetails.masterPublicKey,
);
if (

View File

@ -120,7 +120,7 @@ export async function getTotalPaymentCost(
pcs: PayCoinSelection,
): Promise<AmountJson> {
return ws.db
.mktx((x) => ({ coins: x.coins, denominations: x.denominations }))
.mktx((x) => [x.coins, x.denominations])
.runReadOnly(async (tx) => {
const costs: AmountJson[] = [];
for (let i = 0; i < pcs.coinPubs.length; i++) {
@ -222,12 +222,7 @@ export async function getCandidatePayCoins(
const wireFeesPerExchange: Record<string, AmountJson> = {};
await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
denominations: x.denominations,
coins: x.coins,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations, x.coins])
.runReadOnly(async (tx) => {
const exchanges = await tx.exchanges.iter().toArray();
for (const exchange of exchanges) {
@ -459,13 +454,13 @@ async function recordConfirmPay(
};
await ws.db
.mktx((x) => ({
proposals: x.proposals,
purchases: x.purchases,
coins: x.coins,
refreshGroups: x.refreshGroups,
denominations: x.denominations,
}))
.mktx((x) => [
x.proposals,
x.purchases,
x.coins,
x.refreshGroups,
x.denominations,
])
.runReadWrite(async (tx) => {
const p = await tx.proposals.get(proposal.proposalId);
if (p) {
@ -489,7 +484,7 @@ async function failProposalPermanently(
err: TalerErrorDetail,
): Promise<void> {
await ws.db
.mktx((x) => ({ proposals: x.proposals }))
.mktx((x) => [x.proposals])
.runReadWrite(async (tx) => {
const p = await tx.proposals.get(proposalId);
if (!p) {
@ -567,13 +562,10 @@ export async function processDownloadProposal(
proposalId: string,
options: {} = {},
): Promise<OperationAttemptResult> {
const res = ws.db.mktx2((x) => [x.auditorTrust, x.coins])
const proposal = await ws.db
.mktx((x) => ({ proposals: x.proposals }))
.mktx((x) => [x.proposals])
.runReadOnly(async (tx) => {
return tx.proposals.get(proposalId);
return await tx.proposals.get(proposalId);
});
if (!proposal) {
@ -608,7 +600,7 @@ export async function processDownloadProposal(
const opId = RetryTags.forProposalClaim(proposal);
const retryRecord = await ws.db
.mktx((x) => ({ operationRetries: x.operationRetries }))
.mktx((x) => [x.operationRetries])
.runReadOnly(async (tx) => {
return tx.operationRetries.get(opId);
});
@ -748,7 +740,7 @@ export async function processDownloadProposal(
logger.trace(`extracted contract data: ${j2s(contractData)}`);
await ws.db
.mktx((x) => ({ proposals: x.proposals, purchases: x.purchases }))
.mktx((x) => [x.purchases, x.proposals])
.runReadWrite(async (tx) => {
const p = await tx.proposals.get(proposalId);
if (!p) {
@ -807,7 +799,7 @@ async function startDownloadProposal(
noncePriv: string | undefined,
): Promise<string> {
const oldProposal = await ws.db
.mktx((x) => ({ proposals: x.proposals }))
.mktx((x) => [x.proposals])
.runReadOnly(async (tx) => {
return tx.proposals.indexes.byUrlAndOrderId.get([
merchantBaseUrl,
@ -855,7 +847,7 @@ async function startDownloadProposal(
};
await ws.db
.mktx((x) => ({ proposals: x.proposals }))
.mktx((x) => [x.proposals])
.runReadWrite(async (tx) => {
const existingRecord = await tx.proposals.indexes.byUrlAndOrderId.get([
merchantBaseUrl,
@ -880,7 +872,7 @@ async function storeFirstPaySuccess(
): Promise<void> {
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
await ws.db
.mktx((x) => ({ purchases: x.purchases }))
.mktx((x) => [x.purchases])
.runReadWrite(async (tx) => {
const purchase = await tx.purchases.get(proposalId);
@ -916,7 +908,7 @@ async function storePayReplaySuccess(
sessionId: string | undefined,
): Promise<void> {
await ws.db
.mktx((x) => ({ purchases: x.purchases }))
.mktx((x) => [x.purchases])
.runReadWrite(async (tx) => {
const purchase = await tx.purchases.get(proposalId);
@ -950,9 +942,9 @@ async function handleInsufficientFunds(
logger.trace("handling insufficient funds, trying to re-select coins");
const proposal = await ws.db
.mktx((x) => ({ purchaes: x.purchases }))
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
return tx.purchaes.get(proposalId);
return tx.purchases.get(proposalId);
});
if (!proposal) {
return;
@ -990,7 +982,7 @@ async function handleInsufficientFunds(
const prevPayCoins: PreviousPayCoins = [];
await ws.db
.mktx((x) => ({ coins: x.coins, denominations: x.denominations }))
.mktx((x) => [x.coins, x.denominations])
.runReadOnly(async (tx) => {
for (let i = 0; i < proposal.payCoinSelection.coinPubs.length; i++) {
const coinPub = proposal.payCoinSelection.coinPubs[i];
@ -1036,12 +1028,7 @@ async function handleInsufficientFunds(
logger.trace("re-selected coins");
await ws.db
.mktx((x) => ({
purchases: x.purchases,
coins: x.coins,
denominations: x.denominations,
refreshGroups: x.refreshGroups,
}))
.mktx((x) => [x.purchases, x.coins, x.denominations, x.refreshGroups])
.runReadWrite(async (tx) => {
const p = await tx.purchases.get(proposalId);
if (!p) {
@ -1060,7 +1047,7 @@ async function unblockBackup(
proposalId: string,
): Promise<void> {
await ws.db
.mktx((x) => ({ backupProviders: x.backupProviders }))
.mktx((x) => [x.backupProviders])
.runReadWrite(async (tx) => {
await tx.backupProviders.indexes.byPaymentProposalId
.iter(proposalId)
@ -1081,7 +1068,7 @@ export async function checkPaymentByProposalId(
sessionId?: string,
): Promise<PreparePayResult> {
let proposal = await ws.db
.mktx((x) => ({ proposals: x.proposals }))
.mktx((x) => [x.proposals])
.runReadOnly(async (tx) => {
return tx.proposals.get(proposalId);
});
@ -1095,7 +1082,7 @@ export async function checkPaymentByProposalId(
}
logger.trace("using existing purchase for same product");
proposal = await ws.db
.mktx((x) => ({ proposals: x.proposals }))
.mktx((x) => [x.proposals])
.runReadOnly(async (tx) => {
return tx.proposals.get(existingProposalId);
});
@ -1118,7 +1105,7 @@ export async function checkPaymentByProposalId(
// First check if we already paid for it.
const purchase = await ws.db
.mktx((x) => ({ purchases: x.purchases }))
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
return tx.purchases.get(proposalId);
});
@ -1176,7 +1163,7 @@ export async function checkPaymentByProposalId(
"automatically re-submitting payment with different session ID",
);
await ws.db
.mktx((x) => ({ purchases: x.purchases }))
.mktx((x) => [x.purchases])
.runReadWrite(async (tx) => {
const p = await tx.purchases.get(proposalId);
if (!p) {
@ -1230,7 +1217,7 @@ export async function getContractTermsDetails(
proposalId: string,
): Promise<WalletContractData> {
const proposal = await ws.db
.mktx((x) => ({ proposals: x.proposals }))
.mktx((x) => [x.proposals])
.runReadOnly(async (tx) => {
return tx.proposals.get(proposalId);
});
@ -1296,7 +1283,7 @@ export async function generateDepositPermissions(
denom: DenominationRecord;
}> = [];
await ws.db
.mktx((x) => ({ coins: x.coins, denominations: x.denominations }))
.mktx((x) => [x.coins, x.denominations])
.runReadOnly(async (tx) => {
for (let i = 0; i < payCoinSel.coinPubs.length; i++) {
const coin = await tx.coins.get(payCoinSel.coinPubs[i]);
@ -1359,7 +1346,7 @@ export async function runPayForConfirmPay(
switch (res.type) {
case OperationAttemptResultType.Finished: {
const purchase = await ws.db
.mktx((x) => ({ purchases: x.purchases }))
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
return tx.purchases.get(proposalId);
});
@ -1399,7 +1386,7 @@ export async function confirmPay(
`executing confirmPay with proposalId ${proposalId} and sessionIdOverride ${sessionIdOverride}`,
);
const proposal = await ws.db
.mktx((x) => ({ proposals: x.proposals }))
.mktx((x) => [x.proposals])
.runReadOnly(async (tx) => {
return tx.proposals.get(proposalId);
});
@ -1414,7 +1401,7 @@ export async function confirmPay(
}
const existingPurchase = await ws.db
.mktx((x) => ({ purchases: x.purchases }))
.mktx((x) => [x.purchases])
.runReadWrite(async (tx) => {
const purchase = await tx.purchases.get(proposalId);
if (
@ -1508,7 +1495,7 @@ export async function processPurchasePay(
} = {},
): Promise<OperationAttemptResult> {
const purchase = await ws.db
.mktx((x) => ({ purchases: x.purchases }))
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
return tx.purchases.get(proposalId);
});
@ -1568,20 +1555,12 @@ export async function processPurchasePay(
);
logger.trace(`got resp ${JSON.stringify(resp)}`);
const payOpId = RetryTags.forPay(purchase);
const payRetryRecord = await ws.db
.mktx((x) => ({ operationRetries: x.operationRetries }))
.runReadOnly(async (tx) => {
return await tx.operationRetries.get(payOpId);
});
if (resp.status === HttpStatusCode.BadRequest) {
const errDetails = await readUnexpectedResponseDetails(resp);
logger.warn("unexpected 400 response for /pay");
logger.warn(j2s(errDetails));
await ws.db
.mktx((x) => ({ purchases: x.purchases }))
.mktx((x) => [x.purchases])
.runReadWrite(async (tx) => {
const purch = await tx.purchases.get(proposalId);
if (!purch) {
@ -1683,7 +1662,7 @@ export async function refuseProposal(
proposalId: string,
): Promise<void> {
const success = await ws.db
.mktx((x) => ({ proposals: x.proposals }))
.mktx((x) => [x.proposals])
.runReadWrite(async (tx) => {
const proposal = await tx.proposals.get(proposalId);
if (!proposal) {

View File

@ -242,13 +242,14 @@ export async function initiatePeerToPeerPush(
});
const coinSelRes: PeerCoinSelection | undefined = await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
coins: x.coins,
denominations: x.denominations,
refreshGroups: x.refreshGroups,
peerPushPaymentInitiations: x.peerPushPaymentInitiations,
}))
.mktx((x) => [
x.exchanges,
x.coins,
x.denominations,
x.refreshGroups,
x.peerPullPaymentInitiations,
x.peerPushPaymentInitiations,
])
.runReadWrite(async (tx) => {
const sel = await selectPeerCoins(ws, tx, instructedAmount);
if (!sel) {
@ -401,9 +402,7 @@ export async function checkPeerPushPayment(
const peerPushPaymentIncomingId = encodeCrock(getRandomBytes(32));
await ws.db
.mktx((x) => ({
peerPushPaymentIncoming: x.peerPushPaymentIncoming,
}))
.mktx((x) => [x.peerPushPaymentIncoming])
.runReadWrite(async (tx) => {
await tx.peerPushPaymentIncoming.add({
peerPushPaymentIncomingId,
@ -456,10 +455,7 @@ async function getMergeReserveInfo(
const newReservePair = await ws.cryptoApi.createEddsaKeypair({});
const mergeReserveInfo: MergeReserveInfo = await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.exchanges, x.withdrawalGroups])
.runReadWrite(async (tx) => {
const ex = await tx.exchanges.get(req.exchangeBaseUrl);
checkDbInvariant(!!ex);
@ -482,7 +478,7 @@ export async function acceptPeerPushPayment(
req: AcceptPeerPushPaymentRequest,
): Promise<void> {
const peerInc = await ws.db
.mktx((x) => ({ peerPushPaymentIncoming: x.peerPushPaymentIncoming }))
.mktx((x) => [x.peerPushPaymentIncoming])
.runReadOnly(async (tx) => {
return tx.peerPushPaymentIncoming.get(req.peerPushPaymentIncomingId);
});
@ -564,7 +560,7 @@ export async function acceptPeerPullPayment(
req: AcceptPeerPullPaymentRequest,
): Promise<void> {
const peerPullInc = await ws.db
.mktx((x) => ({ peerPullPaymentIncoming: x.peerPullPaymentIncoming }))
.mktx((x) => [x.peerPullPaymentIncoming])
.runReadOnly(async (tx) => {
return tx.peerPullPaymentIncoming.get(req.peerPullPaymentIncomingId);
});
@ -579,13 +575,13 @@ export async function acceptPeerPullPayment(
peerPullInc.contractTerms.amount,
);
const coinSelRes: PeerCoinSelection | undefined = await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
coins: x.coins,
denominations: x.denominations,
refreshGroups: x.refreshGroups,
peerPullPaymentIncoming: x.peerPullPaymentIncoming,
}))
.mktx((x) => [
x.exchanges,
x.coins,
x.denominations,
x.refreshGroups,
x.peerPullPaymentIncoming,
])
.runReadWrite(async (tx) => {
const sel = await selectPeerCoins(ws, tx, instructedAmount);
if (!sel) {
@ -689,9 +685,7 @@ export async function checkPeerPullPayment(
const peerPullPaymentIncomingId = encodeCrock(getRandomBytes(32));
await ws.db
.mktx((x) => ({
peerPullPaymentIncoming: x.peerPullPaymentIncoming,
}))
.mktx((x) => [x.peerPullPaymentIncoming])
.runReadWrite(async (tx) => {
await tx.peerPullPaymentIncoming.add({
peerPullPaymentIncomingId,
@ -775,11 +769,9 @@ export async function initiatePeerRequestForPay(
});
await ws.db
.mktx((x) => ({
peerPullPaymentInitiation: x.peerPullPaymentInitiations,
}))
.mktx((x) => [x.peerPullPaymentInitiations])
.runReadWrite(async (tx) => {
await tx.peerPullPaymentInitiation.put({
await tx.peerPullPaymentInitiations.put({
amount: req.amount,
contractTerms,
exchangeBaseUrl: req.exchangeBaseUrl,

View File

@ -357,21 +357,21 @@ export async function getPendingOperations(
): Promise<PendingOperationsResponse> {
const now = AbsoluteTime.now();
return await ws.db
.mktx((x) => ({
backupProviders: x.backupProviders,
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
refreshGroups: x.refreshGroups,
coins: x.coins,
withdrawalGroups: x.withdrawalGroups,
proposals: x.proposals,
tips: x.tips,
purchases: x.purchases,
planchets: x.planchets,
depositGroups: x.depositGroups,
recoupGroups: x.recoupGroups,
operationRetries: x.operationRetries,
}))
.mktx((x) => [
x.backupProviders,
x.exchanges,
x.exchangeDetails,
x.refreshGroups,
x.coins,
x.withdrawalGroups,
x.proposals,
x.tips,
x.purchases,
x.planchets,
x.depositGroups,
x.recoupGroups,
x.operationRetries,
])
.runReadWrite(async (tx) => {
const resp: PendingOperationsResponse = {
pendingOperations: [],

View File

@ -96,12 +96,12 @@ async function recoupTipCoin(
// Thus we just put the coin to sleep.
// FIXME: somehow report this to the user
await ws.db
.mktx((x) => ({
recoupGroups: x.recoupGroups,
denominations: WalletStoresV1.denominations,
refreshGroups: WalletStoresV1.refreshGroups,
coins: WalletStoresV1.coins,
}))
.mktx((stores) => [
stores.recoupGroups,
stores.denominations,
stores.refreshGroups,
stores.coins,
])
.runReadWrite(async (tx) => {
const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
if (!recoupGroup) {
@ -123,9 +123,7 @@ async function recoupWithdrawCoin(
): Promise<void> {
const reservePub = cs.reservePub;
const denomInfo = await ws.db
.mktx((x) => ({
denominations: x.denominations,
}))
.mktx((x) => [x.denominations])
.runReadOnly(async (tx) => {
const denomInfo = await ws.getDenomInfo(
ws,
@ -169,12 +167,7 @@ async function recoupWithdrawCoin(
// FIXME: verify that our expectations about the amount match
await ws.db
.mktx((x) => ({
coins: x.coins,
denominations: x.denominations,
recoupGroups: x.recoupGroups,
refreshGroups: x.refreshGroups,
}))
.mktx((x) => [x.coins, x.denominations, x.recoupGroups, x.refreshGroups])
.runReadWrite(async (tx) => {
const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
if (!recoupGroup) {
@ -207,10 +200,7 @@ async function recoupRefreshCoin(
cs: RefreshCoinSource,
): Promise<void> {
const d = await ws.db
.mktx((x) => ({
coins: x.coins,
denominations: x.denominations,
}))
.mktx((x) => [x.coins, x.denominations])
.runReadOnly(async (tx) => {
const denomInfo = await ws.getDenomInfo(
ws,
@ -257,12 +247,7 @@ async function recoupRefreshCoin(
}
await ws.db
.mktx((x) => ({
coins: x.coins,
denominations: x.denominations,
recoupGroups: x.recoupGroups,
refreshGroups: x.refreshGroups,
}))
.mktx((x) => [x.coins, x.denominations, x.recoupGroups, x.refreshGroups])
.runReadWrite(async (tx) => {
const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
if (!recoupGroup) {
@ -319,9 +304,7 @@ export async function processRecoupGroupHandler(
): Promise<OperationAttemptResult> {
const forceNow = options.forceNow ?? false;
let recoupGroup = await ws.db
.mktx((x) => ({
recoupGroups: x.recoupGroups,
}))
.mktx((x) => [x.recoupGroups])
.runReadOnly(async (tx) => {
return tx.recoupGroups.get(recoupGroupId);
});
@ -343,9 +326,7 @@ export async function processRecoupGroupHandler(
await Promise.all(ps);
recoupGroup = await ws.db
.mktx((x) => ({
recoupGroups: x.recoupGroups,
}))
.mktx((x) => [x.recoupGroups])
.runReadOnly(async (tx) => {
return tx.recoupGroups.get(recoupGroupId);
});
@ -366,10 +347,7 @@ export async function processRecoupGroupHandler(
for (let i = 0; i < recoupGroup.coinPubs.length; i++) {
const coinPub = recoupGroup.coinPubs[i];
await ws.db
.mktx((x) => ({
coins: x.coins,
reserves: x.reserves,
}))
.mktx((x) => [x.coins, x.reserves])
.runReadOnly(async (tx) => {
const coin = await tx.coins.get(coinPub);
if (!coin) {
@ -414,12 +392,7 @@ export async function processRecoupGroupHandler(
}
await ws.db
.mktx((x) => ({
recoupGroups: x.recoupGroups,
denominations: WalletStoresV1.denominations,
refreshGroups: WalletStoresV1.refreshGroups,
coins: WalletStoresV1.coins,
}))
.mktx((x) => [x.recoupGroups, x.denominations, x.refreshGroups, x.coins])
.runReadWrite(async (tx) => {
const rg2 = await tx.recoupGroups.get(recoupGroupId);
if (!rg2) {
@ -497,10 +470,7 @@ async function processRecoup(
coinIdx: number,
): Promise<void> {
const coin = await ws.db
.mktx((x) => ({
recoupGroups: x.recoupGroups,
coins: x.coins,
}))
.mktx((x) => [x.recoupGroups, x.coins])
.runReadOnly(async (tx) => {
const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
if (!recoupGroup) {

View File

@ -155,10 +155,7 @@ async function refreshCreateSession(
);
const d = await ws.db
.mktx((x) => ({
refreshGroups: x.refreshGroups,
coins: x.coins,
}))
.mktx((x) => [x.refreshGroups, x.coins])
.runReadWrite(async (tx) => {
const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
if (!refreshGroup) {
@ -197,9 +194,7 @@ async function refreshCreateSession(
// to update and filter withdrawable denoms.
const { availableAmount, availableDenoms } = await ws.db
.mktx((x) => ({
denominations: x.denominations,
}))
.mktx((x) => [x.denominations])
.runReadOnly(async (tx) => {
const oldDenom = await ws.getDenomInfo(
ws,
@ -237,10 +232,7 @@ async function refreshCreateSession(
)} too small`,
);
await ws.db
.mktx((x) => ({
coins: x.coins,
refreshGroups: x.refreshGroups,
}))
.mktx((x) => [x.coins, x.refreshGroups])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
if (!rg) {
@ -259,10 +251,7 @@ async function refreshCreateSession(
// Store refresh session for this coin in the database.
await ws.db
.mktx((x) => ({
refreshGroups: x.refreshGroups,
coins: x.coins,
}))
.mktx((x) => [x.refreshGroups, x.coins])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
if (!rg) {
@ -300,11 +289,7 @@ async function refreshMelt(
coinIndex: number,
): Promise<void> {
const d = await ws.db
.mktx((x) => ({
refreshGroups: x.refreshGroups,
coins: x.coins,
denominations: x.denominations,
}))
.mktx((x) => [x.refreshGroups, x.coins, x.denominations])
.runReadWrite(async (tx) => {
const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
if (!refreshGroup) {
@ -414,9 +399,7 @@ async function refreshMelt(
if (resp.status === HttpStatusCode.NotFound) {
const errDetails = await readUnexpectedResponseDetails(resp);
await ws.db
.mktx((x) => ({
refreshGroups: x.refreshGroups,
}))
.mktx((x) => [x.refreshGroups])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
if (!rg) {
@ -446,9 +429,7 @@ async function refreshMelt(
refreshSession.norevealIndex = norevealIndex;
await ws.db
.mktx((x) => ({
refreshGroups: x.refreshGroups,
}))
.mktx((x) => [x.refreshGroups])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
if (!rg) {
@ -538,11 +519,7 @@ async function refreshReveal(
): Promise<void> {
logger.info("doing refresh reveal");
const d = await ws.db
.mktx((x) => ({
refreshGroups: x.refreshGroups,
coins: x.coins,
denominations: x.denominations,
}))
.mktx((x) => [x.refreshGroups, x.coins, x.denominations])
.runReadOnly(async (tx) => {
const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
if (!refreshGroup) {
@ -703,10 +680,7 @@ async function refreshReveal(
}
await ws.db
.mktx((x) => ({
coins: x.coins,
refreshGroups: x.refreshGroups,
}))
.mktx((x) => [x.coins, x.refreshGroups])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
if (!rg) {
@ -740,12 +714,8 @@ export async function processRefreshGroup(
logger.info(`processing refresh group ${refreshGroupId}`);
const refreshGroup = await ws.db
.mktx((x) => ({
refreshGroups: x.refreshGroups,
}))
.runReadOnly(async (tx) => {
return tx.refreshGroups.get(refreshGroupId);
});
.mktx((x) => [x.refreshGroups])
.runReadOnly(async (tx) => tx.refreshGroups.get(refreshGroupId));
if (!refreshGroup) {
return {
type: OperationAttemptResultType.Finished,
@ -801,7 +771,7 @@ async function processRefreshSession(
`processing refresh session for coin ${coinIndex} of group ${refreshGroupId}`,
);
let refreshGroup = await ws.db
.mktx((x) => ({ refreshGroups: x.refreshGroups }))
.mktx((x) => [x.refreshGroups])
.runReadOnly(async (tx) => {
return tx.refreshGroups.get(refreshGroupId);
});
@ -814,7 +784,7 @@ async function processRefreshSession(
if (!refreshGroup.refreshSessionPerCoin[coinIndex]) {
await refreshCreateSession(ws, refreshGroupId, coinIndex);
refreshGroup = await ws.db
.mktx((x) => ({ refreshGroups: x.refreshGroups }))
.mktx((x) => [x.refreshGroups])
.runReadOnly(async (tx) => {
return tx.refreshGroups.get(refreshGroupId);
});
@ -981,12 +951,7 @@ export async function autoRefresh(
durationFromSpec({ days: 1 }),
);
await ws.db
.mktx((x) => ({
coins: x.coins,
denominations: x.denominations,
refreshGroups: x.refreshGroups,
exchanges: x.exchanges,
}))
.mktx((x) => [x.coins, x.denominations, x.refreshGroups, x.exchanges])
.runReadWrite(async (tx) => {
const exchange = await tx.exchanges.get(exchangeBaseUrl);
if (!exchange) {

View File

@ -78,9 +78,7 @@ export async function prepareRefund(
}
const purchase = await ws.db
.mktx((x) => ({
purchases: x.purchases,
}))
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
return tx.purchases.indexes.byMerchantUrlAndOrderId.get([
parseResult.merchantBaseUrl,
@ -335,12 +333,7 @@ async function acceptRefunds(
const now = TalerProtocolTimestamp.now();
await ws.db
.mktx((x) => ({
purchases: x.purchases,
coins: x.coins,
denominations: x.denominations,
refreshGroups: x.refreshGroups,
}))
.mktx((x) => [x.purchases, x.coins, x.denominations, x.refreshGroups])
.runReadWrite(async (tx) => {
const p = await tx.purchases.get(proposalId);
if (!p) {
@ -517,9 +510,7 @@ export async function applyRefund(
}
const purchase = await ws.db
.mktx((x) => ({
purchases: x.purchases,
}))
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
return tx.purchases.indexes.byMerchantUrlAndOrderId.get([
parseResult.merchantBaseUrl,
@ -544,9 +535,7 @@ export async function applyRefundFromPurchaseId(
logger.info("processing purchase for refund");
const success = await ws.db
.mktx((x) => ({
purchases: x.purchases,
}))
.mktx((x) => [x.purchases])
.runReadWrite(async (tx) => {
const p = await tx.purchases.get(proposalId);
if (!p) {
@ -569,9 +558,7 @@ export async function applyRefundFromPurchaseId(
}
const purchase = await ws.db
.mktx((x) => ({
purchases: x.purchases,
}))
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
return tx.purchases.get(proposalId);
});
@ -642,7 +629,7 @@ async function queryAndSaveAwaitingRefund(
Amounts.cmp(refundAwaiting, purchase.refundAwaiting) !== 0
) {
await ws.db
.mktx((x) => ({ purchases: x.purchases }))
.mktx((x) => [x.purchases])
.runReadWrite(async (tx) => {
const p = await tx.purchases.get(purchase.proposalId);
if (!p) {
@ -667,9 +654,7 @@ export async function processPurchaseQueryRefund(
): Promise<OperationAttemptResult> {
const waitForAutoRefund = options.waitForAutoRefund ?? false;
const purchase = await ws.db
.mktx((x) => ({
purchases: x.purchases,
}))
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
return tx.purchases.get(proposalId);
});
@ -729,9 +714,7 @@ export async function processPurchaseQueryRefund(
const abortingCoins: AbortingCoin[] = [];
await ws.db
.mktx((x) => ({
coins: x.coins,
}))
.mktx((x) => [x.coins])
.runReadOnly(async (tx) => {
for (let i = 0; i < purchase.payCoinSelection.coinPubs.length; i++) {
const coinPub = purchase.payCoinSelection.coinPubs[i];
@ -796,9 +779,7 @@ export async function abortFailedPayWithRefund(
proposalId: string,
): Promise<void> {
await ws.db
.mktx((x) => ({
purchases: x.purchases,
}))
.mktx((x) => [x.purchases])
.runReadWrite(async (tx) => {
const purchase = await tx.purchases.get(proposalId);
if (!purchase) {

View File

@ -467,7 +467,7 @@ export async function testPay(
throw Error("payment not done");
}
const purchase = await ws.db
.mktx((x) => ({ purchases: x.purchases }))
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
return tx.purchases.get(result.proposalId);
});

View File

@ -71,9 +71,7 @@ export async function prepareTip(
}
let tipRecord = await ws.db
.mktx((x) => ({
tips: x.tips,
}))
.mktx((x) => [x.tips])
.runReadOnly(async (tx) => {
return tx.tips.indexes.byMerchantTipIdAndBaseUrl.get([
res.merchantTipId,
@ -100,13 +98,13 @@ export async function prepareTip(
await updateExchangeFromUrl(ws, tipPickupStatus.exchange_url);
//FIXME: is this needed? withdrawDetails is not used
// * if the intention is to update the exchange information in the database
// * if the intention is to update the exchange information in the database
// maybe we can use another name. `get` seems like a pure-function
const withdrawDetails = await getExchangeWithdrawalInfo(
ws,
tipPickupStatus.exchange_url,
amount,
undefined
undefined,
);
const walletTipId = encodeCrock(getRandomBytes(32));
@ -136,9 +134,7 @@ export async function prepareTip(
denomSelUid,
};
await ws.db
.mktx((x) => ({
tips: x.tips,
}))
.mktx((x) => [x.tips])
.runReadWrite(async (tx) => {
await tx.tips.put(newTipRecord);
});
@ -166,9 +162,7 @@ export async function processTip(
} = {},
): Promise<OperationAttemptResult> {
const tipRecord = await ws.db
.mktx((x) => ({
tips: x.tips,
}))
.mktx((x) => [x.tips])
.runReadOnly(async (tx) => {
return tx.tips.get(walletTipId);
});
@ -196,9 +190,7 @@ export async function processTip(
for (const dh of denomsForWithdraw.selectedDenoms) {
const denom = await ws.db
.mktx((x) => ({
denominations: x.denominations,
}))
.mktx((x) => [x.denominations])
.runReadOnly(async (tx) => {
return tx.denominations.get([
tipRecord.exchangeBaseUrl,
@ -324,11 +316,7 @@ export async function processTip(
}
await ws.db
.mktx((x) => ({
coins: x.coins,
tips: x.tips,
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.coins, x.tips, x.withdrawalGroups])
.runReadWrite(async (tx) => {
const tr = await tx.tips.get(walletTipId);
if (!tr) {
@ -355,9 +343,7 @@ export async function acceptTip(
tipId: string,
): Promise<void> {
const found = await ws.db
.mktx((x) => ({
tips: x.tips,
}))
.mktx((x) => [x.tips])
.runReadWrite(async (tx) => {
const tipRecord = await tx.tips.get(tipId);
if (!tipRecord) {

View File

@ -126,24 +126,24 @@ export async function getTransactions(
const transactions: Transaction[] = [];
await ws.db
.mktx((x) => ({
coins: x.coins,
denominations: x.denominations,
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
proposals: x.proposals,
purchases: x.purchases,
refreshGroups: x.refreshGroups,
tips: x.tips,
withdrawalGroups: x.withdrawalGroups,
planchets: x.planchets,
recoupGroups: x.recoupGroups,
depositGroups: x.depositGroups,
tombstones: x.tombstones,
peerPushPaymentInitiations: x.peerPushPaymentInitiations,
peerPullPaymentIncoming: x.peerPullPaymentIncoming,
operationRetries: x.operationRetries,
}))
.mktx((x) => [
x.coins,
x.denominations,
x.depositGroups,
x.exchangeDetails,
x.exchanges,
x.operationRetries,
x.peerPullPaymentIncoming,
x.peerPushPaymentInitiations,
x.planchets,
x.proposals,
x.purchases,
x.recoupGroups,
x.recoupGroups,
x.tips,
x.tombstones,
x.withdrawalGroups,
])
.runReadOnly(async (tx) => {
tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => {
const amount = Amounts.parseOrThrow(pi.amount);
@ -609,10 +609,7 @@ export async function deleteTransaction(
) {
const withdrawalGroupId = rest[0];
await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
tombstones: x.tombstones,
}))
.mktx((x) => [x.withdrawalGroups, x.tombstones])
.runReadWrite(async (tx) => {
const withdrawalGroupRecord = await tx.withdrawalGroups.get(
withdrawalGroupId,
@ -628,11 +625,7 @@ export async function deleteTransaction(
} else if (type === TransactionType.Payment) {
const proposalId = rest[0];
await ws.db
.mktx((x) => ({
proposals: x.proposals,
purchases: x.purchases,
tombstones: x.tombstones,
}))
.mktx((x) => [x.proposals, x.purchases, x.tombstones])
.runReadWrite(async (tx) => {
let found = false;
const proposal = await tx.proposals.get(proposalId);
@ -654,10 +647,7 @@ export async function deleteTransaction(
} else if (type === TransactionType.Refresh) {
const refreshGroupId = rest[0];
await ws.db
.mktx((x) => ({
refreshGroups: x.refreshGroups,
tombstones: x.tombstones,
}))
.mktx((x) => [x.refreshGroups, x.tombstones])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
if (rg) {
@ -670,10 +660,7 @@ export async function deleteTransaction(
} else if (type === TransactionType.Tip) {
const tipId = rest[0];
await ws.db
.mktx((x) => ({
tips: x.tips,
tombstones: x.tombstones,
}))
.mktx((x) => [x.tips, x.tombstones])
.runReadWrite(async (tx) => {
const tipRecord = await tx.tips.get(tipId);
if (tipRecord) {
@ -686,10 +673,7 @@ export async function deleteTransaction(
} else if (type === TransactionType.Deposit) {
const depositGroupId = rest[0];
await ws.db
.mktx((x) => ({
depositGroups: x.depositGroups,
tombstones: x.tombstones,
}))
.mktx((x) => [x.depositGroups, x.tombstones])
.runReadWrite(async (tx) => {
const tipRecord = await tx.depositGroups.get(depositGroupId);
if (tipRecord) {
@ -704,11 +688,7 @@ export async function deleteTransaction(
const executionTimeStr = rest[1];
await ws.db
.mktx((x) => ({
proposals: x.proposals,
purchases: x.purchases,
tombstones: x.tombstones,
}))
.mktx((x) => [x.proposals, x.purchases, x.tombstones])
.runReadWrite(async (tx) => {
const purchase = await tx.purchases.get(proposalId);
if (purchase) {
@ -726,10 +706,7 @@ export async function deleteTransaction(
} else if (type === TransactionType.PeerPullDebit) {
const peerPullPaymentIncomingId = rest[0];
await ws.db
.mktx((x) => ({
peerPullPaymentIncoming: x.peerPullPaymentIncoming,
tombstones: x.tombstones,
}))
.mktx((x) => [x.peerPullPaymentIncoming, x.tombstones])
.runReadWrite(async (tx) => {
const debit = await tx.peerPullPaymentIncoming.get(
peerPullPaymentIncomingId,
@ -747,10 +724,7 @@ export async function deleteTransaction(
} else if (type === TransactionType.PeerPushDebit) {
const pursePub = rest[0];
await ws.db
.mktx((x) => ({
peerPushPaymentInitiations: x.peerPushPaymentInitiations,
tombstones: x.tombstones,
}))
.mktx((x) => [x.peerPushPaymentInitiations, x.tombstones])
.runReadWrite(async (tx) => {
const debit = await tx.peerPushPaymentInitiations.get(pursePub);
if (debit) {

View File

@ -195,9 +195,9 @@ export interface ExchangeWithdrawDetails {
/**
* If the exchange supports age-restricted coins it will return
* the array of ages.
*
*
*/
ageRestrictionOptions?: number[],
ageRestrictionOptions?: number[];
}
/**
@ -248,7 +248,7 @@ export function selectWithdrawalDenominations(
for (const d of denoms) {
let count = 0;
const cost = Amounts.add(d.value, d.feeWithdraw).amount;
for (; ;) {
for (;;) {
if (Amounts.cmp(remaining, cost) < 0) {
break;
}
@ -412,7 +412,7 @@ export async function getCandidateWithdrawalDenoms(
exchangeBaseUrl: string,
): Promise<DenominationRecord[]> {
return await ws.db
.mktx((x) => ({ denominations: x.denominations }))
.mktx((x) => [x.denominations])
.runReadOnly(async (tx) => {
const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl.getAll(
exchangeBaseUrl,
@ -434,9 +434,7 @@ async function processPlanchetGenerate(
coinIdx: number,
): Promise<void> {
let planchet = await ws.db
.mktx((x) => ({
planchets: x.planchets,
}))
.mktx((x) => [x.planchets])
.runReadOnly(async (tx) => {
return tx.planchets.indexes.byGroupAndIndex.get([
withdrawalGroup.withdrawalGroupId,
@ -462,9 +460,7 @@ async function processPlanchetGenerate(
const denomPubHash = maybeDenomPubHash;
const denom = await ws.db
.mktx((x) => ({
denominations: x.denominations,
}))
.mktx((x) => [x.denominations])
.runReadOnly(async (tx) => {
return ws.getDenomInfo(
ws,
@ -500,7 +496,7 @@ async function processPlanchetGenerate(
lastError: undefined,
};
await ws.db
.mktx((x) => ({ planchets: x.planchets }))
.mktx((x) => [x.planchets])
.runReadWrite(async (tx) => {
const p = await tx.planchets.indexes.byGroupAndIndex.get([
withdrawalGroup.withdrawalGroupId,
@ -529,12 +525,12 @@ async function processPlanchetExchangeRequest(
`processing planchet exchange request ${withdrawalGroup.withdrawalGroupId}/${coinIdx}`,
);
const d = await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
planchets: x.planchets,
exchanges: x.exchanges,
denominations: x.denominations,
}))
.mktx((x) => [
x.withdrawalGroups,
x.planchets,
x.exchanges,
x.denominations,
])
.runReadOnly(async (tx) => {
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
withdrawalGroup.withdrawalGroupId,
@ -599,7 +595,7 @@ async function processPlanchetExchangeRequest(
logger.trace("withdrawal request failed", e);
logger.trace(e);
await ws.db
.mktx((x) => ({ planchets: x.planchets }))
.mktx((x) => [x.planchets])
.runReadWrite(async (tx) => {
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
withdrawalGroup.withdrawalGroupId,
@ -631,12 +627,12 @@ async function processPlanchetExchangeBatchRequest(
.map((x) => x.count)
.reduce((a, b) => a + b);
const d = await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
planchets: x.planchets,
exchanges: x.exchanges,
denominations: x.denominations,
}))
.mktx((x) => [
x.withdrawalGroups,
x.planchets,
x.exchanges,
x.denominations,
])
.runReadOnly(async (tx) => {
const reqBody: { planchets: ExchangeWithdrawRequest[] } = {
planchets: [],
@ -705,11 +701,7 @@ async function processPlanchetVerifyAndStoreCoin(
resp: WithdrawResponse,
): Promise<void> {
const d = await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
planchets: x.planchets,
denominations: x.denominations,
}))
.mktx((x) => [x.withdrawalGroups, x.planchets, x.denominations])
.runReadOnly(async (tx) => {
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
withdrawalGroup.withdrawalGroupId,
@ -768,7 +760,7 @@ async function processPlanchetVerifyAndStoreCoin(
if (!isValid) {
await ws.db
.mktx((x) => ({ planchets: x.planchets }))
.mktx((x) => [x.planchets])
.runReadWrite(async (tx) => {
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
withdrawalGroup.withdrawalGroupId,
@ -823,11 +815,7 @@ async function processPlanchetVerifyAndStoreCoin(
// withdrawal succeeded. If so, mark the withdrawal
// group as finished.
const firstSuccess = await ws.db
.mktx((x) => ({
coins: x.coins,
withdrawalGroups: x.withdrawalGroups,
planchets: x.planchets,
}))
.mktx((x) => [x.coins, x.withdrawalGroups, x.planchets])
.runReadWrite(async (tx) => {
const p = await tx.planchets.get(planchetCoinPub);
if (!p || p.withdrawalDone) {
@ -858,10 +846,7 @@ export async function updateWithdrawalDenoms(
`updating denominations used for withdrawal for ${exchangeBaseUrl}`,
);
const exchangeDetails = await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadOnly(async (tx) => {
return ws.exchangeOps.getExchangeDetails(tx, exchangeBaseUrl);
});
@ -890,7 +875,8 @@ export async function updateWithdrawalDenoms(
denom.verificationStatus === DenominationVerificationStatus.Unverified
) {
logger.trace(
`Validating denomination (${current + 1}/${denominations.length
`Validating denomination (${current + 1}/${
denominations.length
}) signature of ${denom.denomPubHash}`,
);
let valid = false;
@ -919,7 +905,7 @@ export async function updateWithdrawalDenoms(
if (updatedDenominations.length > 0) {
logger.trace("writing denomination batch to db");
await ws.db
.mktx((x) => ({ denominations: x.denominations }))
.mktx((x) => [x.denominations])
.runReadWrite(async (tx) => {
for (let i = 0; i < updatedDenominations.length; i++) {
const denom = updatedDenominations[i];
@ -973,7 +959,7 @@ async function queryReserve(
if (
resp.status === 404 &&
result.talerErrorResponse.code ===
TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
) {
ws.notify({
type: NotificationType.ReserveNotYetFound,
@ -988,9 +974,7 @@ async function queryReserve(
logger.trace(`got reserve status ${j2s(result.response)}`);
await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.withdrawalGroups])
.runReadWrite(async (tx) => {
const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
if (!wg) {
@ -1011,7 +995,7 @@ export async function processWithdrawalGroup(
): Promise<OperationAttemptResult> {
logger.trace("processing withdrawal group", withdrawalGroupId);
const withdrawalGroup = await ws.db
.mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
.mktx((x) => [x.withdrawalGroups])
.runReadOnly(async (tx) => {
return tx.withdrawalGroups.get(withdrawalGroupId);
});
@ -1080,7 +1064,7 @@ export async function processWithdrawalGroup(
if (withdrawalGroup.denomsSel.selectedDenoms.length === 0) {
await ws.db
.mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
.mktx((x) => [x.withdrawalGroups])
.runReadWrite(async (tx) => {
const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
if (!wg) {
@ -1148,11 +1132,7 @@ export async function processWithdrawalGroup(
let errorsPerCoin: Record<number, TalerErrorDetail> = {};
await ws.db
.mktx((x) => ({
coins: x.coins,
withdrawalGroups: x.withdrawalGroups,
planchets: x.planchets,
}))
.mktx((x) => [x.coins, x.withdrawalGroups, x.planchets])
.runReadWrite(async (tx) => {
const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
if (!wg) {
@ -1202,7 +1182,9 @@ export async function processWithdrawalGroup(
};
}
const AGE_MASK_GROUPS = "8:10:12:14:16:18".split(":").map(n => parseInt(n, 10))
const AGE_MASK_GROUPS = "8:10:12:14:16:18"
.split(":")
.map((n) => parseInt(n, 10));
export async function getExchangeWithdrawalInfo(
ws: InternalWalletState,
@ -1237,14 +1219,14 @@ export async function getExchangeWithdrawalInfo(
exchange,
);
let hasDenomWithAgeRestriction = false
let hasDenomWithAgeRestriction = false;
let earliestDepositExpiration: TalerProtocolTimestamp | undefined;
for (let i = 0; i < selectedDenoms.selectedDenoms.length; i++) {
const ds = selectedDenoms.selectedDenoms[i];
// FIXME: Do in one transaction!
const denom = await ws.db
.mktx((x) => ({ denominations: x.denominations }))
.mktx((x) => [x.denominations])
.runReadOnly(async (tx) => {
return ws.getDenomInfo(ws, tx, exchangeBaseUrl, ds.denomPubHash);
});
@ -1262,13 +1244,14 @@ export async function getExchangeWithdrawalInfo(
) {
earliestDepositExpiration = expireDeposit;
}
hasDenomWithAgeRestriction = hasDenomWithAgeRestriction || denom.denomPub.age_mask > 0
hasDenomWithAgeRestriction =
hasDenomWithAgeRestriction || denom.denomPub.age_mask > 0;
}
checkLogicInvariant(!!earliestDepositExpiration);
const possibleDenoms = await ws.db
.mktx((x) => ({ denominations: x.denominations }))
.mktx((x) => [x.denominations])
.runReadOnly(async (tx) => {
const ds = await tx.denominations.indexes.byExchangeBaseUrl.getAll(
exchangeBaseUrl,
@ -1290,7 +1273,7 @@ export async function getExchangeWithdrawalInfo(
) {
logger.warn(
`wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` +
`(exchange has ${exchangeDetails.protocolVersion}), checking for updates`,
`(exchange has ${exchangeDetails.protocolVersion}), checking for updates`,
);
}
}
@ -1325,7 +1308,9 @@ export async function getExchangeWithdrawalInfo(
withdrawalAmountRaw: Amounts.stringify(instructedAmount),
// TODO: remove hardcoding, this should be calculated from the denominations info
// force enabled for testing
ageRestrictionOptions: hasDenomWithAgeRestriction ? AGE_MASK_GROUPS : undefined
ageRestrictionOptions: hasDenomWithAgeRestriction
? AGE_MASK_GROUPS
: undefined,
};
return ret;
}
@ -1369,11 +1354,7 @@ export async function getWithdrawalDetailsForUri(
const exchanges: ExchangeListItem[] = [];
await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
denominations: x.denominations,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations])
.runReadOnly(async (tx) => {
const exchangeRecords = await tx.exchanges.iter().toArray();
for (const r of exchangeRecords) {
@ -1409,11 +1390,7 @@ export async function getFundingPaytoUrisTx(
withdrawalGroupId: string,
): Promise<string[]> {
return await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails, x.withdrawalGroups])
.runReadWrite((tx) => getFundingPaytoUris(tx, withdrawalGroupId));
}
@ -1461,9 +1438,7 @@ async function getWithdrawalGroupRecordTx(
},
): Promise<WithdrawalGroupRecord | undefined> {
return await db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.withdrawalGroups])
.runReadOnly(async (tx) => {
return tx.withdrawalGroups.get(req.withdrawalGroupId);
});
@ -1490,9 +1465,7 @@ async function registerReserveWithBank(
withdrawalGroupId: string,
): Promise<void> {
const withdrawalGroup = await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.withdrawalGroups])
.runReadOnly(async (tx) => {
return await tx.withdrawalGroups.get(withdrawalGroupId);
});
@ -1526,9 +1499,7 @@ async function registerReserveWithBank(
codecForBankWithdrawalOperationPostResponse(),
);
await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.withdrawalGroups])
.runReadWrite(async (tx) => {
const r = await tx.withdrawalGroups.get(withdrawalGroupId);
if (!r) {
@ -1606,9 +1577,7 @@ async function processReserveBankStatus(
if (status.aborted) {
logger.info("bank aborted the withdrawal");
await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.withdrawalGroups])
.runReadWrite(async (tx) => {
const r = await tx.withdrawalGroups.get(withdrawalGroupId);
if (!r) {
@ -1648,9 +1617,7 @@ async function processReserveBankStatus(
}
await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.withdrawalGroups])
.runReadWrite(async (tx) => {
const r = await tx.withdrawalGroups.get(withdrawalGroupId);
if (!r) {
@ -1753,13 +1720,13 @@ export async function internalCreateWithdrawalGroup(
);
await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
reserves: x.reserves,
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
exchangeTrust: x.exchangeTrust,
}))
.mktx((x) => [
x.withdrawalGroups,
x.reserves,
x.exchanges,
x.exchangeDetails,
x.exchangeTrust,
])
.runReadWrite(async (tx) => {
await tx.withdrawalGroups.add(withdrawalGroup);
await tx.reserves.put({
@ -1790,7 +1757,7 @@ export async function acceptWithdrawalFromUri(
},
): Promise<AcceptWithdrawalResponse> {
const existingWithdrawalGroup = await ws.db
.mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
.mktx((x) => [x.withdrawalGroups])
.runReadOnly(async (tx) => {
return await tx.withdrawalGroups.indexes.byTalerWithdrawUri.get(
req.talerWithdrawUri,
@ -1899,12 +1866,12 @@ export async function createManualWithdrawal(
const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
const exchangePaytoUris = await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
exchangeTrust: x.exchangeTrust,
}))
.mktx((x) => [
x.withdrawalGroups,
x.exchanges,
x.exchangeDetails,
x.exchangeTrust,
])
.runReadWrite(async (tx) => {
return await getFundingPaytoUris(tx, withdrawalGroup.withdrawalGroupId);
});

View File

@ -36,6 +36,7 @@ import {
} from "@gnu-taler/idb-bridge";
import { Logger } from "@gnu-taler/taler-util";
import { performanceNow } from "./timer.js";
import { access } from "fs";
const logger = new Logger("query.ts");
@ -280,7 +281,6 @@ export interface IndexDescriptor {
export interface StoreDescriptor<RecordType> {
_dummy: undefined & RecordType;
name: string;
keyPath?: IDBKeyPath | IDBKeyPath[];
autoIncrement?: boolean;
}
@ -291,10 +291,9 @@ export interface StoreOptions {
}
export function describeContents<RecordType = never>(
name: string,
options: StoreOptions,
): StoreDescriptor<RecordType> {
return { name, keyPath: options.keyPath, _dummy: undefined as any };
return { keyPath: options.keyPath, _dummy: undefined as any };
}
export function describeIndex(
@ -345,9 +344,11 @@ export interface StoreReadWriteAccessor<RecordType, IndexMap> {
}
export interface StoreWithIndexes<
StoreName extends string,
SD extends StoreDescriptor<unknown>,
IndexMap,
> {
storeName: StoreName;
store: SD;
indexMap: IndexMap;
@ -362,11 +363,17 @@ export type GetRecordType<T> = T extends StoreDescriptor<infer X> ? X : unknown;
const storeWithIndexesSymbol = Symbol("StoreWithIndexesMark");
export function describeStore<SD extends StoreDescriptor<unknown>, IndexMap>(
export function describeStore<
StoreName extends string,
SD extends StoreDescriptor<unknown>,
IndexMap,
>(
name: StoreName,
s: SD,
m: IndexMap,
): StoreWithIndexes<SD, IndexMap> {
): StoreWithIndexes<StoreName, SD, IndexMap> {
return {
storeName: name,
store: s,
indexMap: m,
mark: storeWithIndexesSymbol,
@ -375,6 +382,7 @@ export function describeStore<SD extends StoreDescriptor<unknown>, IndexMap>(
export type GetReadOnlyAccess<BoundStores> = {
[P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes<
infer SN,
infer SD,
infer IM
>
@ -384,6 +392,7 @@ export type GetReadOnlyAccess<BoundStores> = {
export type GetReadWriteAccess<BoundStores> = {
[P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes<
infer SN,
infer SD,
infer IM
>
@ -404,8 +413,12 @@ export interface TransactionContext<BoundStores> {
runReadOnly<T>(f: ReadOnlyTransactionFunction<BoundStores, T>): Promise<T>;
}
type CheckDescriptor<T> = T extends StoreWithIndexes<infer SD, infer IM>
? StoreWithIndexes<SD, IM>
type CheckDescriptor<T> = T extends StoreWithIndexes<
infer SN,
infer SD,
infer IM
>
? StoreWithIndexes<SN, SD, IM>
: unknown;
type GetPickerType<F, SM> = F extends (x: SM) => infer Out
@ -477,13 +490,13 @@ function runTx<Arg, Res>(
function makeReadContext(
tx: IDBTransaction,
storePick: { [n: string]: StoreWithIndexes<any, any> },
storePick: { [n: string]: StoreWithIndexes<any, any, any> },
): any {
const ctx: { [s: string]: StoreReadOnlyAccessor<any, any> } = {};
for (const storeAlias in storePick) {
const indexes: { [s: string]: IndexReadOnlyAccessor<any> } = {};
const swi = storePick[storeAlias];
const storeName = swi.store.name;
const storeName = swi.storeName;
for (const indexAlias in storePick[storeAlias].indexMap) {
const indexDescriptor: IndexDescriptor =
storePick[storeAlias].indexMap[indexAlias];
@ -526,13 +539,13 @@ function makeReadContext(
function makeWriteContext(
tx: IDBTransaction,
storePick: { [n: string]: StoreWithIndexes<any, any> },
storePick: { [n: string]: StoreWithIndexes<any, any, any> },
): any {
const ctx: { [s: string]: StoreReadWriteAccessor<any, any> } = {};
for (const storeAlias in storePick) {
const indexes: { [s: string]: IndexReadWriteAccessor<any> } = {};
const swi = storePick[storeAlias];
const storeName = swi.store.name;
const storeName = swi.storeName;
for (const indexAlias in storePick[storeAlias].indexMap) {
const indexDescriptor: IndexDescriptor =
storePick[storeAlias].indexMap[indexAlias];
@ -585,25 +598,11 @@ function makeWriteContext(
return ctx;
}
const storeList = [
{ name: "foo" as const, value: 1 as const },
{ name: "bar" as const, value: 2 as const },
];
// => { foo: { value: 1}, bar: {value: 2} }
type StoreList = typeof storeList;
type StoreNames = StoreList[number] extends { name: infer I } ? I : never;
type H = StoreList[number] & { name: "foo"};
type Cleanup<V> = V extends { name: infer N, value: infer X} ? {name: N, value: X} : never;
type G = {
[X in StoreNames]: {
X: StoreList[number] & { name: X };
};
};
type StoreNamesOf<X> = X extends { [x: number]: infer F }
? F extends { storeName: infer I }
? I
: never
: never;
/**
* Type-safe access to a database with a particular store map.
@ -617,36 +616,41 @@ export class DbAccess<StoreMap> {
return this.db;
}
mktx2<
/**
* Run a transaction with selected object stores.
*
* The {@link namePicker} must be a function that selects a list of object
* stores from all available object stores.
*/
mktx<
StoreNames extends keyof StoreMap,
Stores extends StoreMap[StoreNames],
StoreList extends Stores[],
>(namePicker: (x: StoreMap) => StoreList): StoreList {
return namePicker(this.stores);
}
mktx<
PickerType extends (x: StoreMap) => unknown,
BoundStores extends GetPickerType<PickerType, StoreMap>,
>(f: PickerType): TransactionContext<BoundStores> {
const storePick = f(this.stores) as any;
BoundStores extends {
[X in StoreNamesOf<StoreList>]: StoreList[number] & { storeName: X };
},
>(namePicker: (x: StoreMap) => StoreList): TransactionContext<BoundStores> {
const storePick = namePicker(this.stores) as any;
if (typeof storePick !== "object" || storePick === null) {
throw Error();
}
const storeNames: string[] = [];
for (const storeAlias of Object.keys(storePick)) {
const swi = (storePick as any)[storeAlias] as StoreWithIndexes<any, any>;
const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } =
{};
for (const swiPicked of storePick) {
const swi = swiPicked as StoreWithIndexes<any, any, any>;
if (swi.mark !== storeWithIndexesSymbol) {
throw Error("invalid store descriptor returned from selector function");
}
storeNames.push(swi.store.name);
storeNames.push(swi.storeName);
accessibleStores[swi.storeName] = swi;
}
const runReadOnly = <T>(
txf: ReadOnlyTransactionFunction<BoundStores, T>,
): Promise<T> => {
const tx = this.db.transaction(storeNames, "readonly");
const readContext = makeReadContext(tx, storePick);
const readContext = makeReadContext(tx, accessibleStores);
return runTx(tx, readContext, txf);
};
@ -654,7 +658,7 @@ export class DbAccess<StoreMap> {
txf: ReadWriteTransactionFunction<BoundStores, T>,
): Promise<T> => {
const tx = this.db.transaction(storeNames, "readwrite");
const writeContext = makeWriteContext(tx, storePick);
const writeContext = makeWriteContext(tx, accessibleStores);
return runTx(tx, writeContext, txf);
};

View File

@ -201,8 +201,9 @@ export async function scheduleRetry(
errorDetail?: TalerErrorDetail,
): Promise<void> {
return await ws.db
.mktx((x) => ({ operationRetries: x.operationRetries }))
.mktx((x) => [x.operationRetries])
.runReadWrite(async (tx) => {
tx.operationRetries
scheduleRetryInTx(ws, tx, opId, errorDetail);
});
}

View File

@ -328,7 +328,7 @@ export async function storeOperationError(
e: TalerErrorDetail,
): Promise<void> {
await ws.db
.mktx((x) => ({ operationRetries: x.operationRetries }))
.mktx((x) => [x.operationRetries])
.runReadWrite(async (tx) => {
const retryRecord = await tx.operationRetries.get(pendingTaskId);
if (!retryRecord) {
@ -345,7 +345,7 @@ export async function storeOperationFinished(
pendingTaskId: string,
): Promise<void> {
await ws.db
.mktx((x) => ({ operationRetries: x.operationRetries }))
.mktx((x) => [x.operationRetries])
.runReadWrite(async (tx) => {
await tx.operationRetries.delete(pendingTaskId);
});
@ -356,7 +356,7 @@ export async function storeOperationPending(
pendingTaskId: string,
): Promise<void> {
await ws.db
.mktx((x) => ({ operationRetries: x.operationRetries }))
.mktx((x) => [x.operationRetries])
.runReadWrite(async (tx) => {
const retryRecord = await tx.operationRetries.get(pendingTaskId);
if (!retryRecord) {
@ -542,7 +542,7 @@ async function runTaskLoop(
*/
async function fillDefaults(ws: InternalWalletState): Promise<void> {
await ws.db
.mktx((x) => ({ config: x.config, auditorTrustStore: x.auditorTrust }))
.mktx((x) => [x.config, x.auditorTrust])
.runReadWrite(async (tx) => {
let applied = false;
await tx.config.iter().forEach((x) => {
@ -552,7 +552,7 @@ async function fillDefaults(ws: InternalWalletState): Promise<void> {
});
if (!applied) {
for (const c of builtinAuditors) {
await tx.auditorTrustStore.put(c);
await tx.auditorTrust.put(c);
}
}
// FIXME: make sure exchanges are added transactionally to
@ -634,9 +634,7 @@ async function listKnownBankAccounts(
): Promise<KnownBankAccounts> {
const accounts: { [account: string]: PaytoUri } = {};
await ws.db
.mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.withdrawalGroups])
.runReadOnly(async (tx) => {
const withdrawalGroups = await tx.withdrawalGroups.iter().toArray();
for (const r of withdrawalGroups) {
@ -660,11 +658,7 @@ async function getExchanges(
): Promise<ExchangesListResponse> {
const exchanges: ExchangeListItem[] = [];
await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
denominations: x.denominations,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations])
.runReadOnly(async (tx) => {
const exchangeRecords = await tx.exchanges.iter().toArray();
for (const r of exchangeRecords) {
@ -708,11 +702,7 @@ async function getExchangeDetailedInfo(
): Promise<ExchangeFullDetails> {
//TODO: should we use the forceUpdate parameter?
const exchange = await ws.db
.mktx((x) => ({
exchanges: x.exchanges,
exchangeDetails: x.exchangeDetails,
denominations: x.denominations,
}))
.mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations])
.runReadOnly(async (tx) => {
const ex = await tx.exchanges.get(exchangeBaseurl);
const dp = ex?.detailsPointer;
@ -790,9 +780,7 @@ async function setCoinSuspended(
suspended: boolean,
): Promise<void> {
await ws.db
.mktx((x) => ({
coins: x.coins,
}))
.mktx((x) => [x.coins])
.runReadWrite(async (tx) => {
const c = await tx.coins.get(coinPub);
if (!c) {
@ -811,11 +799,7 @@ async function dumpCoins(ws: InternalWalletState): Promise<CoinDumpJson> {
const coinsJson: CoinDumpJson = { coins: [] };
logger.info("dumping coins");
await ws.db
.mktx((x) => ({
coins: x.coins,
denominations: x.denominations,
withdrawalGroups: x.withdrawalGroups,
}))
.mktx((x) => [x.coins, x.denominations, x.withdrawalGroups])
.runReadOnly(async (tx) => {
const coins = await tx.coins.iter().toArray();
for (const c of coins) {
@ -1065,11 +1049,7 @@ async function dispatchRequestInternal(
const req = codecForForceRefreshRequest().decode(payload);
const coinPubs = req.coinPubList.map((x) => ({ coinPub: x }));
const refreshGroupId = await ws.db
.mktx((x) => ({
refreshGroups: x.refreshGroups,
denominations: x.denominations,
coins: x.coins,
}))
.mktx((x) => [x.refreshGroups, x.denominations, x.coins])
.runReadWrite(async (tx) => {
return await createRefreshGroup(
ws,
@ -1164,10 +1144,7 @@ async function dispatchRequestInternal(
}
case "listCurrencies": {
return await ws.db
.mktx((x) => ({
auditorTrust: x.auditorTrust,
exchangeTrust: x.exchangeTrust,
}))
.mktx((x) => [x.auditorTrust, x.exchangeTrust])
.runReadOnly(async (tx) => {
const trustedAuditors = await tx.auditorTrust.iter().toArray();
const trustedExchanges = await tx.exchangeTrust.iter().toArray();