wallet-core: address DB FIXMEs

This commit is contained in:
Florian Dold 2022-11-02 17:02:42 +01:00
parent 91e069c742
commit 14456bb942
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
3 changed files with 100 additions and 58 deletions

View File

@ -46,6 +46,7 @@ import {
TransactionIdStr, TransactionIdStr,
UnblindedSignature, UnblindedSignature,
WireInfo, WireInfo,
HashCodeString,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
describeContents, describeContents,
@ -1630,16 +1631,12 @@ export interface TombstoneRecord {
id: string; id: string;
} }
export interface BalancePerCurrencyRecord { export enum PeerPushPaymentInitiationStatus {
currency: string; /**
* Initiated, but no purse created yet.
availableNow: AmountString; */
Initiated = 10 /* ACTIVE_START */,
availableExpected: AmountString; PurseCreated = 50 /* DORMANT_START */,
pendingIncoming: AmountString;
pendingOutgoing: AmountString;
} }
/** /**
@ -1653,7 +1650,8 @@ export interface PeerPushPaymentInitiationRecord {
amount: AmountString; amount: AmountString;
contractTerms: any; contractTermsHash: HashCodeString;
/** /**
* Purse public key. Used as the primary key to look * Purse public key. Used as the primary key to look
* up this record. * up this record.
@ -1679,12 +1677,12 @@ export interface PeerPushPaymentInitiationRecord {
purseExpiration: TalerProtocolTimestamp; purseExpiration: TalerProtocolTimestamp;
/**
* Did we successfully create the purse with the exchange?
*/
purseCreated: boolean;
timestampCreated: TalerProtocolTimestamp; timestampCreated: TalerProtocolTimestamp;
/**
* Status of the peer push payment initiation.
*/
status: PeerPushPaymentInitiationStatus;
} }
export interface PeerPullPaymentInitiationRecord { export interface PeerPullPaymentInitiationRecord {
@ -1710,11 +1708,15 @@ export interface PeerPullPaymentInitiationRecord {
pursePriv: string; pursePriv: string;
/** /**
* Contract terms for the other party. * Hash of the contract terms. Also
* * used to look up the contract terms in the DB.
* FIXME: Put into contract terms store.
*/ */
contractTerms: PeerContractTerms; contractTermsHash: string;
/**
* Status of the peer pull payment initiation.
*/
status: OperationStatus;
} }
/** /**
@ -1735,9 +1737,22 @@ export interface PeerPushPaymentIncomingRecord {
timestamp: TalerProtocolTimestamp; timestamp: TalerProtocolTimestamp;
contractTerms: PeerContractTerms; /**
* Hash of the contract terms. Also
* used to look up the contract terms in the DB.
*/
contractTermsHash: string;
// FIXME: add status etc. /**
* Status of the peer push payment incoming initiation.
*/
status: OperationStatus;
}
export enum PeerPullPaymentIncomingStatus {
Proposed = 30 /* USER_ATTENTION_START */,
Accepted = 10 /* ACTIVE_START */,
Paid = 50 /* DORMANT_START */,
} }
export interface PeerPullPaymentIncomingRecord { export interface PeerPullPaymentIncomingRecord {
@ -1751,11 +1766,12 @@ export interface PeerPullPaymentIncomingRecord {
timestampCreated: TalerProtocolTimestamp; timestampCreated: TalerProtocolTimestamp;
paid: boolean;
accepted: boolean;
contractPriv: string; contractPriv: string;
/**
* Status of the peer push payment incoming initiation.
*/
status: PeerPullPaymentIncomingStatus;
} }
/** /**
@ -2061,13 +2077,6 @@ export const WalletStoresV1 = {
}), }),
{}, {},
), ),
balancesPerCurrency: describeStore(
"balancesPerCurrency",
describeContents<BalancePerCurrencyRecord>({
keyPath: "currency",
}),
{},
),
peerPushPaymentIncoming: describeStore( peerPushPaymentIncoming: describeStore(
"peerPushPaymentIncoming", "peerPushPaymentIncoming",
describeContents<PeerPushPaymentIncomingRecord>({ describeContents<PeerPushPaymentIncomingRecord>({

View File

@ -56,6 +56,7 @@ import {
Logger, Logger,
parsePayPullUri, parsePayPullUri,
parsePayPushUri, parsePayPushUri,
PeerContractTerms,
RefreshReason, RefreshReason,
strcmp, strcmp,
TalerProtocolTimestamp, TalerProtocolTimestamp,
@ -64,6 +65,10 @@ import {
WalletAccountMergeFlags, WalletAccountMergeFlags,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
OperationStatus,
PeerPullPaymentIncomingStatus,
PeerPushPaymentIncomingRecord,
PeerPushPaymentInitiationStatus,
ReserveRecord, ReserveRecord,
WalletStoresV1, WalletStoresV1,
WithdrawalGroupStatus, WithdrawalGroupStatus,
@ -247,6 +252,7 @@ export async function initiatePeerToPeerPush(
const coinSelRes: PeerCoinSelection | undefined = await ws.db const coinSelRes: PeerCoinSelection | undefined = await ws.db
.mktx((x) => [ .mktx((x) => [
x.exchanges, x.exchanges,
x.contractTerms,
x.coins, x.coins,
x.coinAvailability, x.coinAvailability,
x.denominations, x.denominations,
@ -272,16 +278,21 @@ export async function initiatePeerToPeerPush(
await tx.peerPushPaymentInitiations.add({ await tx.peerPushPaymentInitiations.add({
amount: Amounts.stringify(instructedAmount), amount: Amounts.stringify(instructedAmount),
contractPriv: econtractResp.contractPriv, contractPriv: econtractResp.contractPriv,
contractTerms, contractTermsHash: hContractTerms,
exchangeBaseUrl: sel.exchangeBaseUrl, exchangeBaseUrl: sel.exchangeBaseUrl,
mergePriv: mergePair.priv, mergePriv: mergePair.priv,
mergePub: mergePair.pub, mergePub: mergePair.pub,
// FIXME: only set this later!
purseCreated: true,
purseExpiration: purseExpiration, purseExpiration: purseExpiration,
pursePriv: pursePair.priv, pursePriv: pursePair.priv,
pursePub: pursePair.pub, pursePub: pursePair.pub,
timestampCreated: TalerProtocolTimestamp.now(), timestampCreated: TalerProtocolTimestamp.now(),
// FIXME: Only set the later when the purse is actually created!
status: PeerPushPaymentInitiationStatus.PurseCreated,
});
await tx.contractTerms.put({
h: hContractTerms,
contractTermsRaw: contractTerms,
}); });
return sel; return sel;
@ -403,8 +414,12 @@ export async function checkPeerPushPayment(
const peerPushPaymentIncomingId = encodeCrock(getRandomBytes(32)); const peerPushPaymentIncomingId = encodeCrock(getRandomBytes(32));
const contractTermsHash = ContractTermsUtil.hashContractTerms(
dec.contractTerms,
);
await ws.db await ws.db
.mktx((x) => [x.peerPushPaymentIncoming]) .mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming])
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
await tx.peerPushPaymentIncoming.add({ await tx.peerPushPaymentIncoming.add({
peerPushPaymentIncomingId, peerPushPaymentIncomingId,
@ -413,7 +428,13 @@ export async function checkPeerPushPayment(
mergePriv: dec.mergePriv, mergePriv: dec.mergePriv,
pursePub: pursePub, pursePub: pursePub,
timestamp: TalerProtocolTimestamp.now(), timestamp: TalerProtocolTimestamp.now(),
contractTerms: dec.contractTerms, contractTermsHash,
status: OperationStatus.Finished,
});
await tx.contractTerms.put({
h: contractTermsHash,
contractTermsRaw: dec.contractTerms,
}); });
}); });
@ -485,10 +506,21 @@ export async function acceptPeerPushPayment(
ws: InternalWalletState, ws: InternalWalletState,
req: AcceptPeerPushPaymentRequest, req: AcceptPeerPushPaymentRequest,
): Promise<AcceptPeerPushPaymentResponse> { ): Promise<AcceptPeerPushPaymentResponse> {
const peerInc = await ws.db let peerInc: PeerPushPaymentIncomingRecord | undefined;
.mktx((x) => [x.peerPushPaymentIncoming]) let contractTerms: PeerContractTerms | undefined;
await ws.db
.mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming])
.runReadOnly(async (tx) => { .runReadOnly(async (tx) => {
return tx.peerPushPaymentIncoming.get(req.peerPushPaymentIncomingId); peerInc = await tx.peerPushPaymentIncoming.get(
req.peerPushPaymentIncomingId,
);
if (!peerInc) {
return;
}
const ctRec = await tx.contractTerms.get(peerInc.contractTermsHash);
if (ctRec) {
contractTerms = ctRec.contractTermsRaw;
}
}); });
if (!peerInc) { if (!peerInc) {
@ -497,9 +529,11 @@ export async function acceptPeerPushPayment(
); );
} }
checkDbInvariant(!!contractTerms);
await updateExchangeFromUrl(ws, peerInc.exchangeBaseUrl); await updateExchangeFromUrl(ws, peerInc.exchangeBaseUrl);
const amount = Amounts.parseOrThrow(peerInc.contractTerms.amount); const amount = Amounts.parseOrThrow(contractTerms.amount);
const mergeReserveInfo = await getMergeReserveInfo(ws, { const mergeReserveInfo = await getMergeReserveInfo(ws, {
exchangeBaseUrl: peerInc.exchangeBaseUrl, exchangeBaseUrl: peerInc.exchangeBaseUrl,
@ -513,14 +547,12 @@ export async function acceptPeerPushPayment(
); );
const sigRes = await ws.cryptoApi.signPurseMerge({ const sigRes = await ws.cryptoApi.signPurseMerge({
contractTermsHash: ContractTermsUtil.hashContractTerms( contractTermsHash: ContractTermsUtil.hashContractTerms(contractTerms),
peerInc.contractTerms,
),
flags: WalletAccountMergeFlags.MergeFullyPaidPurse, flags: WalletAccountMergeFlags.MergeFullyPaidPurse,
mergePriv: peerInc.mergePriv, mergePriv: peerInc.mergePriv,
mergeTimestamp: mergeTimestamp, mergeTimestamp: mergeTimestamp,
purseAmount: Amounts.stringify(amount), purseAmount: Amounts.stringify(amount),
purseExpiration: peerInc.contractTerms.purse_expiration, purseExpiration: contractTerms.purse_expiration,
purseFee: Amounts.stringify(Amounts.getZero(amount.currency)), purseFee: Amounts.stringify(Amounts.getZero(amount.currency)),
pursePub: peerInc.pursePub, pursePub: peerInc.pursePub,
reservePayto, reservePayto,
@ -549,7 +581,7 @@ export async function acceptPeerPushPayment(
amount, amount,
wgInfo: { wgInfo: {
withdrawalType: WithdrawalRecordType.PeerPushCredit, withdrawalType: WithdrawalRecordType.PeerPushCredit,
contractTerms: peerInc.contractTerms, contractTerms,
}, },
exchangeBaseUrl: peerInc.exchangeBaseUrl, exchangeBaseUrl: peerInc.exchangeBaseUrl,
reserveStatus: WithdrawalGroupStatus.QueryingStatus, reserveStatus: WithdrawalGroupStatus.QueryingStatus,
@ -567,9 +599,6 @@ export async function acceptPeerPushPayment(
}; };
} }
/**
* FIXME: Bad name!
*/
export async function acceptPeerPullPayment( export async function acceptPeerPullPayment(
ws: InternalWalletState, ws: InternalWalletState,
req: AcceptPeerPullPaymentRequest, req: AcceptPeerPullPaymentRequest,
@ -619,7 +648,7 @@ export async function acceptPeerPullPayment(
if (!pi) { if (!pi) {
throw Error(); throw Error();
} }
pi.accepted = true; pi.status = PeerPullPaymentIncomingStatus.Accepted;
await tx.peerPullPaymentIncoming.put(pi); await tx.peerPullPaymentIncoming.put(pi);
return sel; return sel;
@ -711,8 +740,7 @@ export async function checkPeerPullPayment(
pursePub: pursePub, pursePub: pursePub,
timestampCreated: TalerProtocolTimestamp.now(), timestampCreated: TalerProtocolTimestamp.now(),
contractTerms: dec.contractTerms, contractTerms: dec.contractTerms,
paid: false, status: PeerPullPaymentIncomingStatus.Proposed,
accepted: false,
}); });
}); });
@ -726,7 +754,7 @@ export async function checkPeerPullPayment(
/** /**
* Initiate a peer pull payment. * Initiate a peer pull payment.
*/ */
export async function initiatePeerRequestForPay( export async function initiatePeerPullPayment(
ws: InternalWalletState, ws: InternalWalletState,
req: InitiatePeerPullPaymentRequest, req: InitiatePeerPullPaymentRequest,
): Promise<InitiatePeerPullPaymentResponse> { ): Promise<InitiatePeerPullPaymentResponse> {
@ -786,14 +814,19 @@ export async function initiatePeerRequestForPay(
}); });
await ws.db await ws.db
.mktx((x) => [x.peerPullPaymentInitiations]) .mktx((x) => [x.peerPullPaymentInitiations, x.contractTerms])
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
await tx.peerPullPaymentInitiations.put({ await tx.peerPullPaymentInitiations.put({
amount: req.amount, amount: req.amount,
contractTerms, contractTermsHash: hContractTerms,
exchangeBaseUrl: req.exchangeBaseUrl, exchangeBaseUrl: req.exchangeBaseUrl,
pursePriv: pursePair.priv, pursePriv: pursePair.priv,
pursePub: pursePair.pub, pursePub: pursePair.pub,
status: OperationStatus.Finished,
});
await tx.contractTerms.put({
contractTermsRaw: contractTerms,
h: hContractTerms,
}); });
}); });

View File

@ -184,7 +184,7 @@ import {
acceptPeerPushPayment, acceptPeerPushPayment,
checkPeerPullPayment, checkPeerPullPayment,
checkPeerPushPayment, checkPeerPushPayment,
initiatePeerRequestForPay, initiatePeerPullPayment,
initiatePeerToPeerPush, initiatePeerToPeerPush,
} from "./operations/pay-peer.js"; } from "./operations/pay-peer.js";
import { getPendingOperations } from "./operations/pending.js"; import { getPendingOperations } from "./operations/pending.js";
@ -1354,7 +1354,7 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
} }
case WalletApiOperation.InitiatePeerPullPayment: { case WalletApiOperation.InitiatePeerPullPayment: {
const req = codecForInitiatePeerPullPaymentRequest().decode(payload); const req = codecForInitiatePeerPullPaymentRequest().decode(payload);
return await initiatePeerRequestForPay(ws, req); return await initiatePeerPullPayment(ws, req);
} }
case WalletApiOperation.CheckPeerPullPayment: { case WalletApiOperation.CheckPeerPullPayment: {
const req = codecForCheckPeerPullPaymentRequest().decode(payload); const req = codecForCheckPeerPullPaymentRequest().decode(payload);