wallet-core: implement retries for peer push payments
This commit is contained in:
parent
81157c519b
commit
24694eae73
@ -445,17 +445,19 @@ export interface SignPurseCreationRequest {
|
|||||||
minAge: number;
|
minAge: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SignPurseDepositsRequest {
|
export interface SpendCoinDetails {
|
||||||
pursePub: string;
|
coinPub: string;
|
||||||
exchangeBaseUrl: string;
|
|
||||||
coins: {
|
|
||||||
coinPub: string;
|
|
||||||
coinPriv: string;
|
coinPriv: string;
|
||||||
contribution: AmountString;
|
contribution: AmountString;
|
||||||
denomPubHash: string;
|
denomPubHash: string;
|
||||||
denomSig: UnblindedSignature;
|
denomSig: UnblindedSignature;
|
||||||
ageCommitmentProof: AgeCommitmentProof | undefined;
|
ageCommitmentProof: AgeCommitmentProof | undefined;
|
||||||
}[];
|
}
|
||||||
|
|
||||||
|
export interface SignPurseDepositsRequest {
|
||||||
|
pursePub: string;
|
||||||
|
exchangeBaseUrl: string;
|
||||||
|
coins: SpendCoinDetails[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SignPurseDepositsResponse {
|
export interface SignPurseDepositsResponse {
|
||||||
@ -1451,25 +1453,24 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
|
|||||||
tci: TalerCryptoInterfaceR,
|
tci: TalerCryptoInterfaceR,
|
||||||
req: EncryptContractRequest,
|
req: EncryptContractRequest,
|
||||||
): Promise<EncryptContractResponse> {
|
): Promise<EncryptContractResponse> {
|
||||||
const contractKeyPair = await this.createEddsaKeypair(tci, {});
|
|
||||||
const enc = await encryptContractForMerge(
|
const enc = await encryptContractForMerge(
|
||||||
decodeCrock(req.pursePub),
|
decodeCrock(req.pursePub),
|
||||||
decodeCrock(contractKeyPair.priv),
|
decodeCrock(req.contractPriv),
|
||||||
decodeCrock(req.mergePriv),
|
decodeCrock(req.mergePriv),
|
||||||
req.contractTerms,
|
req.contractTerms,
|
||||||
);
|
);
|
||||||
const sigBlob = buildSigPS(TalerSignaturePurpose.WALLET_PURSE_ECONTRACT)
|
const sigBlob = buildSigPS(TalerSignaturePurpose.WALLET_PURSE_ECONTRACT)
|
||||||
.put(hash(enc))
|
.put(hash(enc))
|
||||||
.put(decodeCrock(contractKeyPair.pub))
|
.put(decodeCrock(req.contractPub))
|
||||||
.build();
|
.build();
|
||||||
const sig = eddsaSign(sigBlob, decodeCrock(req.pursePriv));
|
const sig = eddsaSign(sigBlob, decodeCrock(req.pursePriv));
|
||||||
return {
|
return {
|
||||||
econtract: {
|
econtract: {
|
||||||
contract_pub: contractKeyPair.pub,
|
contract_pub: req.contractPub,
|
||||||
econtract: encodeCrock(enc),
|
econtract: encodeCrock(enc),
|
||||||
econtract_sig: encodeCrock(sig),
|
econtract_sig: encodeCrock(sig),
|
||||||
},
|
},
|
||||||
contractPriv: contractKeyPair.priv,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async decryptContractForMerge(
|
async decryptContractForMerge(
|
||||||
|
@ -176,17 +176,15 @@ export interface EncryptedContract {
|
|||||||
|
|
||||||
export interface EncryptContractRequest {
|
export interface EncryptContractRequest {
|
||||||
contractTerms: any;
|
contractTerms: any;
|
||||||
|
contractPriv: string;
|
||||||
|
contractPub: string;
|
||||||
pursePub: string;
|
pursePub: string;
|
||||||
pursePriv: string;
|
pursePriv: string;
|
||||||
|
|
||||||
mergePriv: string;
|
mergePriv: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EncryptContractResponse {
|
export interface EncryptContractResponse {
|
||||||
econtract: EncryptedContract;
|
econtract: EncryptedContract;
|
||||||
|
|
||||||
contractPriv: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EncryptContractForDepositRequest {
|
export interface EncryptContractForDepositRequest {
|
||||||
|
@ -57,6 +57,7 @@ import {
|
|||||||
AttentionInfo,
|
AttentionInfo,
|
||||||
AbsoluteTime,
|
AbsoluteTime,
|
||||||
Logger,
|
Logger,
|
||||||
|
CoinPublicKeyString,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
DbAccess,
|
DbAccess,
|
||||||
@ -1692,6 +1693,11 @@ export enum PeerPushPaymentInitiationStatus {
|
|||||||
PurseCreated = 50 /* DORMANT_START */,
|
PurseCreated = 50 /* DORMANT_START */,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PeerPushPaymentCoinSelection {
|
||||||
|
contributions: AmountString[];
|
||||||
|
coinPubs: CoinPublicKeyString[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record for a push P2P payment that this wallet initiated.
|
* Record for a push P2P payment that this wallet initiated.
|
||||||
*/
|
*/
|
||||||
@ -1701,8 +1707,13 @@ export interface PeerPushPaymentInitiationRecord {
|
|||||||
*/
|
*/
|
||||||
exchangeBaseUrl: string;
|
exchangeBaseUrl: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructed amount.
|
||||||
|
*/
|
||||||
amount: AmountString;
|
amount: AmountString;
|
||||||
|
|
||||||
|
coinSel: PeerPushPaymentCoinSelection;
|
||||||
|
|
||||||
contractTermsHash: HashCodeString;
|
contractTermsHash: HashCodeString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1727,6 +1738,9 @@ export interface PeerPushPaymentInitiationRecord {
|
|||||||
mergePriv: string;
|
mergePriv: string;
|
||||||
|
|
||||||
contractPriv: string;
|
contractPriv: string;
|
||||||
|
contractPub: string;
|
||||||
|
|
||||||
|
contractTerms: PeerContractTerms;
|
||||||
|
|
||||||
purseExpiration: TalerProtocolTimestamp;
|
purseExpiration: TalerProtocolTimestamp;
|
||||||
|
|
||||||
|
@ -68,9 +68,11 @@ import {
|
|||||||
UnblindedSignature,
|
UnblindedSignature,
|
||||||
WalletAccountMergeFlags,
|
WalletAccountMergeFlags,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
|
import { SpendCoinDetails } from "../crypto/cryptoImplementation.js";
|
||||||
import {
|
import {
|
||||||
OperationStatus,
|
OperationStatus,
|
||||||
PeerPullPaymentIncomingStatus,
|
PeerPullPaymentIncomingStatus,
|
||||||
|
PeerPushPaymentCoinSelection,
|
||||||
PeerPushPaymentIncomingRecord,
|
PeerPushPaymentIncomingRecord,
|
||||||
PeerPushPaymentInitiationStatus,
|
PeerPushPaymentInitiationStatus,
|
||||||
ReserveRecord,
|
ReserveRecord,
|
||||||
@ -80,17 +82,26 @@ import {
|
|||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { TalerError } from "../errors.js";
|
import { TalerError } from "../errors.js";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
import { makeTransactionId, spendCoins } from "../operations/common.js";
|
import {
|
||||||
|
makeTransactionId,
|
||||||
|
runOperationWithErrorReporting,
|
||||||
|
spendCoins,
|
||||||
|
} from "../operations/common.js";
|
||||||
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
|
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
|
||||||
import { checkDbInvariant } from "../util/invariants.js";
|
import { checkDbInvariant } from "../util/invariants.js";
|
||||||
import { GetReadOnlyAccess } from "../util/query.js";
|
import { GetReadOnlyAccess } from "../util/query.js";
|
||||||
|
import {
|
||||||
|
OperationAttemptResult,
|
||||||
|
OperationAttemptResultType,
|
||||||
|
RetryTags,
|
||||||
|
} from "../util/retries.js";
|
||||||
import { getPeerPaymentBalanceDetailsInTx } from "./balance.js";
|
import { getPeerPaymentBalanceDetailsInTx } from "./balance.js";
|
||||||
import { updateExchangeFromUrl } from "./exchanges.js";
|
import { updateExchangeFromUrl } from "./exchanges.js";
|
||||||
import { internalCreateWithdrawalGroup } from "./withdraw.js";
|
import { internalCreateWithdrawalGroup } from "./withdraw.js";
|
||||||
|
|
||||||
const logger = new Logger("operations/peer-to-peer.ts");
|
const logger = new Logger("operations/peer-to-peer.ts");
|
||||||
|
|
||||||
export interface PeerCoinSelection {
|
export interface PeerCoinSelectionDetails {
|
||||||
exchangeBaseUrl: string;
|
exchangeBaseUrl: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,6 +122,9 @@ export interface PeerCoinSelection {
|
|||||||
depositFees: AmountJson;
|
depositFees: AmountJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about a selected coin for peer to peer payments.
|
||||||
|
*/
|
||||||
interface CoinInfo {
|
interface CoinInfo {
|
||||||
/**
|
/**
|
||||||
* Public key of the coin.
|
* Public key of the coin.
|
||||||
@ -131,16 +145,52 @@ interface CoinInfo {
|
|||||||
denomSig: UnblindedSignature;
|
denomSig: UnblindedSignature;
|
||||||
|
|
||||||
maxAge: number;
|
maxAge: number;
|
||||||
|
|
||||||
ageCommitmentProof?: AgeCommitmentProof;
|
ageCommitmentProof?: AgeCommitmentProof;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SelectPeerCoinsResult =
|
export type SelectPeerCoinsResult =
|
||||||
| { type: "success"; result: PeerCoinSelection }
|
| { type: "success"; result: PeerCoinSelectionDetails }
|
||||||
| {
|
| {
|
||||||
type: "failure";
|
type: "failure";
|
||||||
insufficientBalanceDetails: PayPeerInsufficientBalanceDetails;
|
insufficientBalanceDetails: PayPeerInsufficientBalanceDetails;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export async function queryCoinInfosForSelection(
|
||||||
|
ws: InternalWalletState,
|
||||||
|
csel: PeerPushPaymentCoinSelection,
|
||||||
|
): Promise<SpendCoinDetails[]> {
|
||||||
|
let infos: SpendCoinDetails[] = [];
|
||||||
|
await ws.db
|
||||||
|
.mktx((x) => [x.coins, x.denominations])
|
||||||
|
.runReadOnly(async (tx) => {
|
||||||
|
for (let i = 0; i < csel.coinPubs.length; i++) {
|
||||||
|
const coin = await tx.coins.get(csel.coinPubs[i]);
|
||||||
|
if (!coin) {
|
||||||
|
throw Error("coin not found anymore");
|
||||||
|
}
|
||||||
|
const denom = await ws.getDenomInfo(
|
||||||
|
ws,
|
||||||
|
tx,
|
||||||
|
coin.exchangeBaseUrl,
|
||||||
|
coin.denomPubHash,
|
||||||
|
);
|
||||||
|
if (!denom) {
|
||||||
|
throw Error("denom for coin not found anymore");
|
||||||
|
}
|
||||||
|
infos.push({
|
||||||
|
coinPriv: coin.coinPriv,
|
||||||
|
coinPub: coin.coinPub,
|
||||||
|
denomPubHash: coin.denomPubHash,
|
||||||
|
denomSig: coin.denomSig,
|
||||||
|
ageCommitmentProof: coin.ageCommitmentProof,
|
||||||
|
contribution: csel.contributions[i],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
export async function selectPeerCoins(
|
export async function selectPeerCoins(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
tx: GetReadOnlyAccess<{
|
tx: GetReadOnlyAccess<{
|
||||||
@ -228,7 +278,7 @@ export async function selectPeerCoins(
|
|||||||
lastDepositFee = coin.feeDeposit;
|
lastDepositFee = coin.feeDeposit;
|
||||||
}
|
}
|
||||||
if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
|
if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
|
||||||
const res: PeerCoinSelection = {
|
const res: PeerCoinSelectionDetails = {
|
||||||
exchangeBaseUrl: exch.baseUrl,
|
exchangeBaseUrl: exch.baseUrl,
|
||||||
coins: resCoins,
|
coins: resCoins,
|
||||||
depositFees: depositFeesAcc,
|
depositFees: depositFeesAcc,
|
||||||
@ -290,6 +340,94 @@ export async function preparePeerPushPayment(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function processPeerPushOutgoing(
|
||||||
|
ws: InternalWalletState,
|
||||||
|
pursePub: string,
|
||||||
|
): Promise<OperationAttemptResult> {
|
||||||
|
const peerPushInitiation = await ws.db
|
||||||
|
.mktx((x) => [x.peerPushPaymentInitiations])
|
||||||
|
.runReadOnly(async (tx) => {
|
||||||
|
return tx.peerPushPaymentInitiations.get(pursePub);
|
||||||
|
});
|
||||||
|
if (!peerPushInitiation) {
|
||||||
|
throw Error("peer push payment not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const purseExpiration = peerPushInitiation.purseExpiration;
|
||||||
|
const hContractTerms = peerPushInitiation.contractTermsHash;
|
||||||
|
|
||||||
|
const purseSigResp = await ws.cryptoApi.signPurseCreation({
|
||||||
|
hContractTerms,
|
||||||
|
mergePub: peerPushInitiation.mergePub,
|
||||||
|
minAge: 0,
|
||||||
|
purseAmount: peerPushInitiation.amount,
|
||||||
|
purseExpiration,
|
||||||
|
pursePriv: peerPushInitiation.pursePriv,
|
||||||
|
});
|
||||||
|
|
||||||
|
const coins = await queryCoinInfosForSelection(
|
||||||
|
ws,
|
||||||
|
peerPushInitiation.coinSel,
|
||||||
|
);
|
||||||
|
|
||||||
|
const depositSigsResp = await ws.cryptoApi.signPurseDeposits({
|
||||||
|
exchangeBaseUrl: peerPushInitiation.exchangeBaseUrl,
|
||||||
|
pursePub: peerPushInitiation.pursePub,
|
||||||
|
coins,
|
||||||
|
});
|
||||||
|
|
||||||
|
const econtractResp = await ws.cryptoApi.encryptContractForMerge({
|
||||||
|
contractTerms: peerPushInitiation.contractTerms,
|
||||||
|
mergePriv: peerPushInitiation.mergePriv,
|
||||||
|
pursePriv: peerPushInitiation.pursePriv,
|
||||||
|
pursePub: peerPushInitiation.pursePub,
|
||||||
|
contractPriv: peerPushInitiation.contractPriv,
|
||||||
|
contractPub: peerPushInitiation.contractPub,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createPurseUrl = new URL(
|
||||||
|
`purses/${peerPushInitiation.pursePub}/create`,
|
||||||
|
peerPushInitiation.exchangeBaseUrl,
|
||||||
|
);
|
||||||
|
|
||||||
|
const httpResp = await ws.http.postJson(createPurseUrl.href, {
|
||||||
|
amount: peerPushInitiation.amount,
|
||||||
|
merge_pub: peerPushInitiation.mergePub,
|
||||||
|
purse_sig: purseSigResp.sig,
|
||||||
|
h_contract_terms: hContractTerms,
|
||||||
|
purse_expiration: purseExpiration,
|
||||||
|
deposits: depositSigsResp.deposits,
|
||||||
|
min_age: 0,
|
||||||
|
econtract: econtractResp.econtract,
|
||||||
|
});
|
||||||
|
|
||||||
|
const resp = await httpResp.json();
|
||||||
|
|
||||||
|
logger.info(`resp: ${j2s(resp)}`);
|
||||||
|
|
||||||
|
if (httpResp.status !== 200) {
|
||||||
|
throw Error("got error response from exchange");
|
||||||
|
}
|
||||||
|
|
||||||
|
await ws.db
|
||||||
|
.mktx((x) => [x.peerPushPaymentInitiations])
|
||||||
|
.runReadWrite(async (tx) => {
|
||||||
|
const ppi = await tx.peerPushPaymentInitiations.get(pursePub);
|
||||||
|
if (!ppi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ppi.status = PeerPushPaymentInitiationStatus.PurseCreated;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: OperationAttemptResultType.Finished,
|
||||||
|
result: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate sending a peer-to-peer push payment.
|
||||||
|
*/
|
||||||
export async function initiatePeerToPeerPush(
|
export async function initiatePeerToPeerPush(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
req: InitiatePeerPushPaymentRequest,
|
req: InitiatePeerPushPaymentRequest,
|
||||||
@ -305,13 +443,7 @@ export async function initiatePeerToPeerPush(
|
|||||||
|
|
||||||
const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
|
const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
|
||||||
|
|
||||||
const econtractResp = await ws.cryptoApi.encryptContractForMerge({
|
const contractKeyPair = await ws.cryptoApi.createEddsaKeypair({});
|
||||||
contractTerms,
|
|
||||||
mergePriv: mergePair.priv,
|
|
||||||
pursePriv: pursePair.priv,
|
|
||||||
pursePub: pursePair.pub,
|
|
||||||
});
|
|
||||||
|
|
||||||
const coinSelRes: SelectPeerCoinsResult = await ws.db
|
const coinSelRes: SelectPeerCoinsResult = await ws.db
|
||||||
.mktx((x) => [
|
.mktx((x) => [
|
||||||
x.exchanges,
|
x.exchanges,
|
||||||
@ -320,7 +452,6 @@ export async function initiatePeerToPeerPush(
|
|||||||
x.coinAvailability,
|
x.coinAvailability,
|
||||||
x.denominations,
|
x.denominations,
|
||||||
x.refreshGroups,
|
x.refreshGroups,
|
||||||
x.peerPullPaymentInitiations,
|
|
||||||
x.peerPushPaymentInitiations,
|
x.peerPushPaymentInitiations,
|
||||||
])
|
])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
@ -342,7 +473,8 @@ export async function initiatePeerToPeerPush(
|
|||||||
|
|
||||||
await tx.peerPushPaymentInitiations.add({
|
await tx.peerPushPaymentInitiations.add({
|
||||||
amount: Amounts.stringify(instructedAmount),
|
amount: Amounts.stringify(instructedAmount),
|
||||||
contractPriv: econtractResp.contractPriv,
|
contractPriv: contractKeyPair.priv,
|
||||||
|
contractPub: contractKeyPair.pub,
|
||||||
contractTermsHash: hContractTerms,
|
contractTermsHash: hContractTerms,
|
||||||
exchangeBaseUrl: sel.exchangeBaseUrl,
|
exchangeBaseUrl: sel.exchangeBaseUrl,
|
||||||
mergePriv: mergePair.priv,
|
mergePriv: mergePair.priv,
|
||||||
@ -351,8 +483,12 @@ export async function initiatePeerToPeerPush(
|
|||||||
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.Initiated,
|
||||||
status: PeerPushPaymentInitiationStatus.PurseCreated,
|
contractTerms: contractTerms,
|
||||||
|
coinSel: {
|
||||||
|
coinPubs: sel.coins.map((x) => x.coinPub),
|
||||||
|
contributions: sel.coins.map((x) => x.contribution),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await tx.contractTerms.put({
|
await tx.contractTerms.put({
|
||||||
@ -373,53 +509,22 @@ export async function initiatePeerToPeerPush(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const purseSigResp = await ws.cryptoApi.signPurseCreation({
|
await runOperationWithErrorReporting(
|
||||||
hContractTerms,
|
ws,
|
||||||
mergePub: mergePair.pub,
|
RetryTags.byPeerPushPaymentInitiationPursePub(pursePair.pub),
|
||||||
minAge: 0,
|
async () => {
|
||||||
purseAmount: Amounts.stringify(instructedAmount),
|
return await processPeerPushOutgoing(ws, pursePair.pub);
|
||||||
purseExpiration,
|
},
|
||||||
pursePriv: pursePair.priv,
|
|
||||||
});
|
|
||||||
|
|
||||||
const depositSigsResp = await ws.cryptoApi.signPurseDeposits({
|
|
||||||
exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
|
|
||||||
pursePub: pursePair.pub,
|
|
||||||
coins: coinSelRes.result.coins,
|
|
||||||
});
|
|
||||||
|
|
||||||
const createPurseUrl = new URL(
|
|
||||||
`purses/${pursePair.pub}/create`,
|
|
||||||
coinSelRes.result.exchangeBaseUrl,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const httpResp = await ws.http.postJson(createPurseUrl.href, {
|
|
||||||
amount: Amounts.stringify(instructedAmount),
|
|
||||||
merge_pub: mergePair.pub,
|
|
||||||
purse_sig: purseSigResp.sig,
|
|
||||||
h_contract_terms: hContractTerms,
|
|
||||||
purse_expiration: purseExpiration,
|
|
||||||
deposits: depositSigsResp.deposits,
|
|
||||||
min_age: 0,
|
|
||||||
econtract: econtractResp.econtract,
|
|
||||||
});
|
|
||||||
|
|
||||||
const resp = await httpResp.json();
|
|
||||||
|
|
||||||
logger.info(`resp: ${j2s(resp)}`);
|
|
||||||
|
|
||||||
if (httpResp.status !== 200) {
|
|
||||||
throw Error("got error response from exchange");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
contractPriv: econtractResp.contractPriv,
|
contractPriv: contractKeyPair.priv,
|
||||||
mergePriv: mergePair.priv,
|
mergePriv: mergePair.priv,
|
||||||
pursePub: pursePair.pub,
|
pursePub: pursePair.pub,
|
||||||
exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
|
exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
|
||||||
talerUri: constructPayPushUri({
|
talerUri: constructPayPushUri({
|
||||||
exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
|
exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
|
||||||
contractPriv: econtractResp.contractPriv,
|
contractPriv: contractKeyPair.priv,
|
||||||
}),
|
}),
|
||||||
transactionId: makeTransactionId(
|
transactionId: makeTransactionId(
|
||||||
TransactionType.PeerPushDebit,
|
TransactionType.PeerPushDebit,
|
||||||
|
@ -24,11 +24,7 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import {
|
import { TalerErrorDetail, AbsoluteTime } from "@gnu-taler/taler-util";
|
||||||
TalerErrorDetail,
|
|
||||||
AbsoluteTime,
|
|
||||||
TalerProtocolTimestamp,
|
|
||||||
} from "@gnu-taler/taler-util";
|
|
||||||
import { RetryInfo } from "./util/retries.js";
|
import { RetryInfo } from "./util/retries.js";
|
||||||
|
|
||||||
export enum PendingTaskType {
|
export enum PendingTaskType {
|
||||||
@ -41,6 +37,7 @@ export enum PendingTaskType {
|
|||||||
Withdraw = "withdraw",
|
Withdraw = "withdraw",
|
||||||
Deposit = "deposit",
|
Deposit = "deposit",
|
||||||
Backup = "backup",
|
Backup = "backup",
|
||||||
|
PeerPushOutgoing = "peer-push-outgoing",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,6 +54,7 @@ export type PendingTaskInfo = PendingTaskInfoCommon &
|
|||||||
| PendingRecoupTask
|
| PendingRecoupTask
|
||||||
| PendingDepositTask
|
| PendingDepositTask
|
||||||
| PendingBackupTask
|
| PendingBackupTask
|
||||||
|
| PendingPeerPushOutgoingTask
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface PendingBackupTask {
|
export interface PendingBackupTask {
|
||||||
@ -74,6 +72,14 @@ export interface PendingExchangeUpdateTask {
|
|||||||
lastError: TalerErrorDetail | undefined;
|
lastError: TalerErrorDetail | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wallet wants to send a peer push payment.
|
||||||
|
*/
|
||||||
|
export interface PendingPeerPushOutgoingTask {
|
||||||
|
type: PendingTaskType.PeerPushOutgoing;
|
||||||
|
pursePub: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The wallet should check whether coins from this exchange
|
* The wallet should check whether coins from this exchange
|
||||||
* need to be auto-refreshed.
|
* need to be auto-refreshed.
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
BackupProviderRecord,
|
BackupProviderRecord,
|
||||||
DepositGroupRecord,
|
DepositGroupRecord,
|
||||||
ExchangeRecord,
|
ExchangeRecord,
|
||||||
|
PeerPushPaymentInitiationRecord,
|
||||||
PurchaseRecord,
|
PurchaseRecord,
|
||||||
RecoupGroupRecord,
|
RecoupGroupRecord,
|
||||||
RefreshGroupRecord,
|
RefreshGroupRecord,
|
||||||
@ -200,9 +201,17 @@ export namespace RetryTags {
|
|||||||
export function forBackup(backupRecord: BackupProviderRecord): string {
|
export function forBackup(backupRecord: BackupProviderRecord): string {
|
||||||
return `${PendingTaskType.Backup}:${backupRecord.baseUrl}`;
|
return `${PendingTaskType.Backup}:${backupRecord.baseUrl}`;
|
||||||
}
|
}
|
||||||
|
export function forPeerPushPaymentInitiation(
|
||||||
|
ppi: PeerPushPaymentInitiationRecord,
|
||||||
|
): string {
|
||||||
|
return `${PendingTaskType.PeerPushOutgoing}:${ppi.pursePub}`;
|
||||||
|
}
|
||||||
export function byPaymentProposalId(proposalId: string): string {
|
export function byPaymentProposalId(proposalId: string): string {
|
||||||
return `${PendingTaskType.Purchase}:${proposalId}`;
|
return `${PendingTaskType.Purchase}:${proposalId}`;
|
||||||
}
|
}
|
||||||
|
export function byPeerPushPaymentInitiationPursePub(pursePub: string): string {
|
||||||
|
return `${PendingTaskType.PeerPushOutgoing}:${pursePub}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function scheduleRetryInTx(
|
export async function scheduleRetryInTx(
|
||||||
|
@ -198,6 +198,7 @@ import {
|
|||||||
initiatePeerToPeerPush,
|
initiatePeerToPeerPush,
|
||||||
preparePeerPullPayment,
|
preparePeerPullPayment,
|
||||||
preparePeerPushPayment,
|
preparePeerPushPayment,
|
||||||
|
processPeerPushOutgoing,
|
||||||
} from "./operations/pay-peer.js";
|
} from "./operations/pay-peer.js";
|
||||||
import { getPendingOperations } from "./operations/pending.js";
|
import { getPendingOperations } from "./operations/pending.js";
|
||||||
import {
|
import {
|
||||||
@ -317,6 +318,8 @@ async function callOperationHandler(
|
|||||||
}
|
}
|
||||||
case PendingTaskType.Backup:
|
case PendingTaskType.Backup:
|
||||||
return await processBackupForProvider(ws, pending.backupProviderBaseUrl);
|
return await processBackupForProvider(ws, pending.backupProviderBaseUrl);
|
||||||
|
case PendingTaskType.PeerPushOutgoing:
|
||||||
|
return await processPeerPushOutgoing(ws, pending.pursePub);
|
||||||
default:
|
default:
|
||||||
return assertUnreachable(pending);
|
return assertUnreachable(pending);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user