wallet-core: retries for peer pull payments
This commit is contained in:
parent
24694eae73
commit
1e378e4499
@ -60,6 +60,7 @@ import {
|
|||||||
hashCoinPub,
|
hashCoinPub,
|
||||||
hashDenomPub,
|
hashDenomPub,
|
||||||
hashTruncate32,
|
hashTruncate32,
|
||||||
|
j2s,
|
||||||
kdf,
|
kdf,
|
||||||
kdfKw,
|
kdfKw,
|
||||||
keyExchangeEcdhEddsa,
|
keyExchangeEcdhEddsa,
|
||||||
@ -447,11 +448,11 @@ export interface SignPurseCreationRequest {
|
|||||||
|
|
||||||
export interface SpendCoinDetails {
|
export interface SpendCoinDetails {
|
||||||
coinPub: string;
|
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 {
|
export interface SignPurseDepositsRequest {
|
||||||
@ -1453,7 +1454,6 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
|
|||||||
tci: TalerCryptoInterfaceR,
|
tci: TalerCryptoInterfaceR,
|
||||||
req: EncryptContractRequest,
|
req: EncryptContractRequest,
|
||||||
): Promise<EncryptContractResponse> {
|
): Promise<EncryptContractResponse> {
|
||||||
|
|
||||||
const enc = await encryptContractForMerge(
|
const enc = await encryptContractForMerge(
|
||||||
decodeCrock(req.pursePub),
|
decodeCrock(req.pursePub),
|
||||||
decodeCrock(req.contractPriv),
|
decodeCrock(req.contractPriv),
|
||||||
@ -1491,24 +1491,22 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
|
|||||||
tci: TalerCryptoInterfaceR,
|
tci: TalerCryptoInterfaceR,
|
||||||
req: EncryptContractForDepositRequest,
|
req: EncryptContractForDepositRequest,
|
||||||
): Promise<EncryptContractForDepositResponse> {
|
): Promise<EncryptContractForDepositResponse> {
|
||||||
const contractKeyPair = await this.createEddsaKeypair(tci, {});
|
|
||||||
const enc = await encryptContractForDeposit(
|
const enc = await encryptContractForDeposit(
|
||||||
decodeCrock(req.pursePub),
|
decodeCrock(req.pursePub),
|
||||||
decodeCrock(contractKeyPair.priv),
|
decodeCrock(req.contractPriv),
|
||||||
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 decryptContractForDeposit(
|
async decryptContractForDeposit(
|
||||||
|
@ -190,14 +190,15 @@ export interface EncryptContractResponse {
|
|||||||
export interface EncryptContractForDepositRequest {
|
export interface EncryptContractForDepositRequest {
|
||||||
contractTerms: any;
|
contractTerms: any;
|
||||||
|
|
||||||
|
contractPriv: string;
|
||||||
|
contractPub: string;
|
||||||
|
|
||||||
pursePub: string;
|
pursePub: string;
|
||||||
pursePriv: string;
|
pursePriv: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EncryptContractForDepositResponse {
|
export interface EncryptContractForDepositResponse {
|
||||||
econtract: EncryptedContract;
|
econtract: EncryptedContract;
|
||||||
|
|
||||||
contractPriv: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DecryptContractRequest {
|
export interface DecryptContractRequest {
|
||||||
|
@ -1780,6 +1780,18 @@ export interface PeerPullPaymentInitiationRecord {
|
|||||||
*/
|
*/
|
||||||
contractTermsHash: string;
|
contractTermsHash: string;
|
||||||
|
|
||||||
|
mergePub: string;
|
||||||
|
mergePriv: string;
|
||||||
|
|
||||||
|
contractPub: string;
|
||||||
|
contractPriv: string;
|
||||||
|
|
||||||
|
contractTerms: PeerContractTerms;
|
||||||
|
|
||||||
|
mergeTimestamp: TalerProtocolTimestamp;
|
||||||
|
|
||||||
|
mergeReserveRowId: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Status of the peer pull payment initiation.
|
* Status of the peer pull payment initiation.
|
||||||
*/
|
*/
|
||||||
|
@ -340,7 +340,7 @@ export async function preparePeerPushPayment(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function processPeerPushOutgoing(
|
export async function processPeerPushInitiation(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
pursePub: string,
|
pursePub: string,
|
||||||
): Promise<OperationAttemptResult> {
|
): Promise<OperationAttemptResult> {
|
||||||
@ -417,6 +417,7 @@ export async function processPeerPushOutgoing(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ppi.status = PeerPushPaymentInitiationStatus.PurseCreated;
|
ppi.status = PeerPushPaymentInitiationStatus.PurseCreated;
|
||||||
|
await tx.peerPushPaymentInitiations.put(ppi);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -428,7 +429,7 @@ export async function processPeerPushOutgoing(
|
|||||||
/**
|
/**
|
||||||
* Initiate sending a peer-to-peer push payment.
|
* Initiate sending a peer-to-peer push payment.
|
||||||
*/
|
*/
|
||||||
export async function initiatePeerToPeerPush(
|
export async function initiatePeerPushPayment(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
req: InitiatePeerPushPaymentRequest,
|
req: InitiatePeerPushPaymentRequest,
|
||||||
): Promise<InitiatePeerPushPaymentResponse> {
|
): Promise<InitiatePeerPushPaymentResponse> {
|
||||||
@ -513,7 +514,7 @@ export async function initiatePeerToPeerPush(
|
|||||||
ws,
|
ws,
|
||||||
RetryTags.byPeerPushPaymentInitiationPursePub(pursePair.pub),
|
RetryTags.byPeerPushPaymentInitiationPursePub(pursePair.pub),
|
||||||
async () => {
|
async () => {
|
||||||
return await processPeerPushOutgoing(ws, pursePair.pub);
|
return await processPeerPushInitiation(ws, pursePair.pub);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -935,6 +936,115 @@ export async function checkPeerPullPayment(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function processPeerPullInitiation(
|
||||||
|
ws: InternalWalletState,
|
||||||
|
pursePub: string,
|
||||||
|
): Promise<OperationAttemptResult> {
|
||||||
|
const pullIni = await ws.db
|
||||||
|
.mktx((x) => [x.peerPullPaymentInitiations])
|
||||||
|
.runReadOnly(async (tx) => {
|
||||||
|
return tx.peerPullPaymentInitiations.get(pursePub);
|
||||||
|
});
|
||||||
|
if (!pullIni) {
|
||||||
|
throw Error("peer pull payment initiation not found in database");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pullIni.status === OperationStatus.Finished) {
|
||||||
|
logger.warn("peer pull payment initiation is already finished");
|
||||||
|
return {
|
||||||
|
type: OperationAttemptResultType.Finished,
|
||||||
|
result: undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mergeReserve = await ws.db
|
||||||
|
.mktx((x) => [x.reserves])
|
||||||
|
.runReadOnly(async (tx) => {
|
||||||
|
return tx.reserves.get(pullIni.mergeReserveRowId);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!mergeReserve) {
|
||||||
|
throw Error("merge reserve for peer pull payment not found in database");
|
||||||
|
}
|
||||||
|
|
||||||
|
const purseFee = Amounts.stringify(Amounts.zeroOfAmount(pullIni.amount));
|
||||||
|
|
||||||
|
const reservePayto = talerPaytoFromExchangeReserve(
|
||||||
|
pullIni.exchangeBaseUrl,
|
||||||
|
mergeReserve.reservePub,
|
||||||
|
);
|
||||||
|
|
||||||
|
const econtractResp = await ws.cryptoApi.encryptContractForDeposit({
|
||||||
|
contractPriv: pullIni.contractPriv,
|
||||||
|
contractPub: pullIni.contractPub,
|
||||||
|
contractTerms: pullIni.contractTerms,
|
||||||
|
pursePriv: pullIni.pursePriv,
|
||||||
|
pursePub: pullIni.pursePub,
|
||||||
|
});
|
||||||
|
|
||||||
|
const purseExpiration = pullIni.contractTerms.purse_expiration;
|
||||||
|
const sigRes = await ws.cryptoApi.signReservePurseCreate({
|
||||||
|
contractTermsHash: pullIni.contractTermsHash,
|
||||||
|
flags: WalletAccountMergeFlags.CreateWithPurseFee,
|
||||||
|
mergePriv: pullIni.mergePriv,
|
||||||
|
mergeTimestamp: pullIni.mergeTimestamp,
|
||||||
|
purseAmount: pullIni.contractTerms.amount,
|
||||||
|
purseExpiration: purseExpiration,
|
||||||
|
purseFee: purseFee,
|
||||||
|
pursePriv: pullIni.pursePriv,
|
||||||
|
pursePub: pullIni.pursePub,
|
||||||
|
reservePayto,
|
||||||
|
reservePriv: mergeReserve.reservePriv,
|
||||||
|
});
|
||||||
|
|
||||||
|
const reservePurseReqBody: ExchangeReservePurseRequest = {
|
||||||
|
merge_sig: sigRes.mergeSig,
|
||||||
|
merge_timestamp: pullIni.mergeTimestamp,
|
||||||
|
h_contract_terms: pullIni.contractTermsHash,
|
||||||
|
merge_pub: pullIni.mergePub,
|
||||||
|
min_age: 0,
|
||||||
|
purse_expiration: purseExpiration,
|
||||||
|
purse_fee: purseFee,
|
||||||
|
purse_pub: pullIni.pursePub,
|
||||||
|
purse_sig: sigRes.purseSig,
|
||||||
|
purse_value: pullIni.contractTerms.amount,
|
||||||
|
reserve_sig: sigRes.accountSig,
|
||||||
|
econtract: econtractResp.econtract,
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.info(`reserve purse request: ${j2s(reservePurseReqBody)}`);
|
||||||
|
|
||||||
|
const reservePurseMergeUrl = new URL(
|
||||||
|
`reserves/${mergeReserve.reservePub}/purse`,
|
||||||
|
pullIni.exchangeBaseUrl,
|
||||||
|
);
|
||||||
|
|
||||||
|
const httpResp = await ws.http.postJson(
|
||||||
|
reservePurseMergeUrl.href,
|
||||||
|
reservePurseReqBody,
|
||||||
|
);
|
||||||
|
|
||||||
|
const resp = await readSuccessResponseJsonOrThrow(httpResp, codecForAny());
|
||||||
|
|
||||||
|
logger.info(`reserve merge response: ${j2s(resp)}`);
|
||||||
|
|
||||||
|
await ws.db
|
||||||
|
.mktx((x) => [x.peerPullPaymentInitiations])
|
||||||
|
.runReadWrite(async (tx) => {
|
||||||
|
const pi2 = await tx.peerPullPaymentInitiations.get(pursePub);
|
||||||
|
if (!pi2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pi2.status = OperationStatus.Finished;
|
||||||
|
await tx.peerPullPaymentInitiations.put(pi2);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: OperationAttemptResultType.Finished,
|
||||||
|
result: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function preparePeerPullPayment(
|
export async function preparePeerPullPayment(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
req: PreparePeerPullPaymentRequest,
|
req: PreparePeerPullPaymentRequest,
|
||||||
@ -967,39 +1077,14 @@ export async function initiatePeerPullPayment(
|
|||||||
const instructedAmount = Amounts.parseOrThrow(
|
const instructedAmount = Amounts.parseOrThrow(
|
||||||
req.partialContractTerms.amount,
|
req.partialContractTerms.amount,
|
||||||
);
|
);
|
||||||
const purseExpiration = req.partialContractTerms.purse_expiration;
|
|
||||||
const contractTerms = req.partialContractTerms;
|
const contractTerms = req.partialContractTerms;
|
||||||
|
|
||||||
const reservePayto = talerPaytoFromExchangeReserve(
|
|
||||||
req.exchangeBaseUrl,
|
|
||||||
mergeReserveInfo.reservePub,
|
|
||||||
);
|
|
||||||
|
|
||||||
const econtractResp = await ws.cryptoApi.encryptContractForDeposit({
|
|
||||||
contractTerms,
|
|
||||||
pursePriv: pursePair.priv,
|
|
||||||
pursePub: pursePair.pub,
|
|
||||||
});
|
|
||||||
|
|
||||||
const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
|
const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
|
||||||
|
|
||||||
const purseFee = Amounts.stringify(
|
const contractKeyPair = await ws.cryptoApi.createEddsaKeypair({});
|
||||||
Amounts.zeroOfCurrency(instructedAmount.currency),
|
|
||||||
);
|
|
||||||
|
|
||||||
const sigRes = await ws.cryptoApi.signReservePurseCreate({
|
const mergeReserveRowId = mergeReserveInfo.rowId;
|
||||||
contractTermsHash: hContractTerms,
|
checkDbInvariant(!!mergeReserveRowId);
|
||||||
flags: WalletAccountMergeFlags.CreateWithPurseFee,
|
|
||||||
mergePriv: mergePair.priv,
|
|
||||||
mergeTimestamp: mergeTimestamp,
|
|
||||||
purseAmount: req.partialContractTerms.amount,
|
|
||||||
purseExpiration: purseExpiration,
|
|
||||||
purseFee: purseFee,
|
|
||||||
pursePriv: pursePair.priv,
|
|
||||||
pursePub: pursePair.pub,
|
|
||||||
reservePayto,
|
|
||||||
reservePriv: mergeReserveInfo.reservePriv,
|
|
||||||
});
|
|
||||||
|
|
||||||
await ws.db
|
await ws.db
|
||||||
.mktx((x) => [x.peerPullPaymentInitiations, x.contractTerms])
|
.mktx((x) => [x.peerPullPaymentInitiations, x.contractTerms])
|
||||||
@ -1010,7 +1095,14 @@ export async function initiatePeerPullPayment(
|
|||||||
exchangeBaseUrl: req.exchangeBaseUrl,
|
exchangeBaseUrl: req.exchangeBaseUrl,
|
||||||
pursePriv: pursePair.priv,
|
pursePriv: pursePair.priv,
|
||||||
pursePub: pursePair.pub,
|
pursePub: pursePair.pub,
|
||||||
status: OperationStatus.Finished,
|
mergePriv: mergePair.priv,
|
||||||
|
mergePub: mergePair.pub,
|
||||||
|
status: OperationStatus.Pending,
|
||||||
|
contractTerms: contractTerms,
|
||||||
|
mergeTimestamp,
|
||||||
|
mergeReserveRowId: mergeReserveRowId,
|
||||||
|
contractPriv: contractKeyPair.priv,
|
||||||
|
contractPub: contractKeyPair.pub,
|
||||||
});
|
});
|
||||||
await tx.contractTerms.put({
|
await tx.contractTerms.put({
|
||||||
contractTermsRaw: contractTerms,
|
contractTermsRaw: contractTerms,
|
||||||
@ -1018,43 +1110,24 @@ export async function initiatePeerPullPayment(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const reservePurseReqBody: ExchangeReservePurseRequest = {
|
// FIXME: Should we somehow signal to the client
|
||||||
merge_sig: sigRes.mergeSig,
|
// whether purse creation has failed, or does the client/
|
||||||
merge_timestamp: mergeTimestamp,
|
// check this asynchronously from the transaction status?
|
||||||
h_contract_terms: hContractTerms,
|
|
||||||
merge_pub: mergePair.pub,
|
|
||||||
min_age: 0,
|
|
||||||
purse_expiration: purseExpiration,
|
|
||||||
purse_fee: purseFee,
|
|
||||||
purse_pub: pursePair.pub,
|
|
||||||
purse_sig: sigRes.purseSig,
|
|
||||||
purse_value: req.partialContractTerms.amount,
|
|
||||||
reserve_sig: sigRes.accountSig,
|
|
||||||
econtract: econtractResp.econtract,
|
|
||||||
};
|
|
||||||
|
|
||||||
logger.info(`reserve purse request: ${j2s(reservePurseReqBody)}`);
|
await runOperationWithErrorReporting(
|
||||||
|
ws,
|
||||||
const reservePurseMergeUrl = new URL(
|
RetryTags.byPeerPullPaymentInitiationPursePub(pursePair.pub),
|
||||||
`reserves/${mergeReserveInfo.reservePub}/purse`,
|
async () => {
|
||||||
req.exchangeBaseUrl,
|
return processPeerPullInitiation(ws, pursePair.pub);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const httpResp = await ws.http.postJson(
|
|
||||||
reservePurseMergeUrl.href,
|
|
||||||
reservePurseReqBody,
|
|
||||||
);
|
|
||||||
|
|
||||||
const resp = await readSuccessResponseJsonOrThrow(httpResp, codecForAny());
|
|
||||||
|
|
||||||
logger.info(`reserve merge response: ${j2s(resp)}`);
|
|
||||||
|
|
||||||
const wg = await internalCreateWithdrawalGroup(ws, {
|
const wg = await internalCreateWithdrawalGroup(ws, {
|
||||||
amount: instructedAmount,
|
amount: instructedAmount,
|
||||||
wgInfo: {
|
wgInfo: {
|
||||||
withdrawalType: WithdrawalRecordType.PeerPullCredit,
|
withdrawalType: WithdrawalRecordType.PeerPullCredit,
|
||||||
contractTerms,
|
contractTerms,
|
||||||
contractPriv: econtractResp.contractPriv,
|
contractPriv: contractKeyPair.priv,
|
||||||
},
|
},
|
||||||
exchangeBaseUrl: req.exchangeBaseUrl,
|
exchangeBaseUrl: req.exchangeBaseUrl,
|
||||||
reserveStatus: WithdrawalGroupStatus.QueryingStatus,
|
reserveStatus: WithdrawalGroupStatus.QueryingStatus,
|
||||||
@ -1067,7 +1140,7 @@ export async function initiatePeerPullPayment(
|
|||||||
return {
|
return {
|
||||||
talerUri: constructPayPullUri({
|
talerUri: constructPayPullUri({
|
||||||
exchangeBaseUrl: req.exchangeBaseUrl,
|
exchangeBaseUrl: req.exchangeBaseUrl,
|
||||||
contractPriv: econtractResp.contractPriv,
|
contractPriv: contractKeyPair.priv,
|
||||||
}),
|
}),
|
||||||
transactionId: makeTransactionId(
|
transactionId: makeTransactionId(
|
||||||
TransactionType.PeerPullCredit,
|
TransactionType.PeerPullCredit,
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
RefreshCoinStatus,
|
RefreshCoinStatus,
|
||||||
OperationStatus,
|
OperationStatus,
|
||||||
OperationStatusRange,
|
OperationStatusRange,
|
||||||
|
PeerPushPaymentInitiationStatus,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import {
|
import {
|
||||||
PendingOperationsResponse,
|
PendingOperationsResponse,
|
||||||
@ -341,6 +342,58 @@ async function gatherBackupPending(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function gatherPeerPullInitiationPending(
|
||||||
|
ws: InternalWalletState,
|
||||||
|
tx: GetReadOnlyAccess<{
|
||||||
|
peerPullPaymentInitiations: typeof WalletStoresV1.peerPullPaymentInitiations;
|
||||||
|
operationRetries: typeof WalletStoresV1.operationRetries;
|
||||||
|
}>,
|
||||||
|
now: AbsoluteTime,
|
||||||
|
resp: PendingOperationsResponse,
|
||||||
|
): Promise<void> {
|
||||||
|
await tx.peerPullPaymentInitiations.iter().forEachAsync(async (pi) => {
|
||||||
|
if (pi.status === OperationStatus.Finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const opId = RetryTags.forPeerPullPaymentInitiation(pi);
|
||||||
|
const retryRecord = await tx.operationRetries.get(opId);
|
||||||
|
const timestampDue = retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now();
|
||||||
|
resp.pendingOperations.push({
|
||||||
|
type: PendingTaskType.PeerPullInitiation,
|
||||||
|
...getPendingCommon(ws, opId, timestampDue),
|
||||||
|
givesLifeness: true,
|
||||||
|
retryInfo: retryRecord?.retryInfo,
|
||||||
|
pursePub: pi.pursePub,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function gatherPeerPushInitiationPending(
|
||||||
|
ws: InternalWalletState,
|
||||||
|
tx: GetReadOnlyAccess<{
|
||||||
|
peerPushPaymentInitiations: typeof WalletStoresV1.peerPushPaymentInitiations;
|
||||||
|
operationRetries: typeof WalletStoresV1.operationRetries;
|
||||||
|
}>,
|
||||||
|
now: AbsoluteTime,
|
||||||
|
resp: PendingOperationsResponse,
|
||||||
|
): Promise<void> {
|
||||||
|
await tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => {
|
||||||
|
if (pi.status === PeerPushPaymentInitiationStatus.PurseCreated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const opId = RetryTags.forPeerPushPaymentInitiation(pi);
|
||||||
|
const retryRecord = await tx.operationRetries.get(opId);
|
||||||
|
const timestampDue = retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now();
|
||||||
|
resp.pendingOperations.push({
|
||||||
|
type: PendingTaskType.PeerPushInitiation,
|
||||||
|
...getPendingCommon(ws, opId, timestampDue),
|
||||||
|
givesLifeness: true,
|
||||||
|
retryInfo: retryRecord?.retryInfo,
|
||||||
|
pursePub: pi.pursePub,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function getPendingOperations(
|
export async function getPendingOperations(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
): Promise<PendingOperationsResponse> {
|
): Promise<PendingOperationsResponse> {
|
||||||
@ -359,6 +412,8 @@ export async function getPendingOperations(
|
|||||||
x.depositGroups,
|
x.depositGroups,
|
||||||
x.recoupGroups,
|
x.recoupGroups,
|
||||||
x.operationRetries,
|
x.operationRetries,
|
||||||
|
x.peerPullPaymentInitiations,
|
||||||
|
x.peerPushPaymentInitiations,
|
||||||
])
|
])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const resp: PendingOperationsResponse = {
|
const resp: PendingOperationsResponse = {
|
||||||
@ -372,6 +427,8 @@ export async function getPendingOperations(
|
|||||||
await gatherPurchasePending(ws, tx, now, resp);
|
await gatherPurchasePending(ws, tx, now, resp);
|
||||||
await gatherRecoupPending(ws, tx, now, resp);
|
await gatherRecoupPending(ws, tx, now, resp);
|
||||||
await gatherBackupPending(ws, tx, now, resp);
|
await gatherBackupPending(ws, tx, now, resp);
|
||||||
|
await gatherPeerPushInitiationPending(ws, tx, now, resp);
|
||||||
|
await gatherPeerPullInitiationPending(ws, tx, now, resp);
|
||||||
return resp;
|
return resp;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,8 @@ export enum PendingTaskType {
|
|||||||
Withdraw = "withdraw",
|
Withdraw = "withdraw",
|
||||||
Deposit = "deposit",
|
Deposit = "deposit",
|
||||||
Backup = "backup",
|
Backup = "backup",
|
||||||
PeerPushOutgoing = "peer-push-outgoing",
|
PeerPushInitiation = "peer-push-initiation",
|
||||||
|
PeerPullInitiation = "peer-pull\-initiation",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +55,8 @@ export type PendingTaskInfo = PendingTaskInfoCommon &
|
|||||||
| PendingRecoupTask
|
| PendingRecoupTask
|
||||||
| PendingDepositTask
|
| PendingDepositTask
|
||||||
| PendingBackupTask
|
| PendingBackupTask
|
||||||
| PendingPeerPushOutgoingTask
|
| PendingPeerPushInitiationTask
|
||||||
|
| PendingPeerPullInitiationTask
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface PendingBackupTask {
|
export interface PendingBackupTask {
|
||||||
@ -75,8 +77,16 @@ export interface PendingExchangeUpdateTask {
|
|||||||
/**
|
/**
|
||||||
* The wallet wants to send a peer push payment.
|
* The wallet wants to send a peer push payment.
|
||||||
*/
|
*/
|
||||||
export interface PendingPeerPushOutgoingTask {
|
export interface PendingPeerPushInitiationTask {
|
||||||
type: PendingTaskType.PeerPushOutgoing;
|
type: PendingTaskType.PeerPushInitiation;
|
||||||
|
pursePub: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wallet wants to send a peer pull payment.
|
||||||
|
*/
|
||||||
|
export interface PendingPeerPullInitiationTask {
|
||||||
|
type: PendingTaskType.PeerPullInitiation;
|
||||||
pursePub: string;
|
pursePub: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
BackupProviderRecord,
|
BackupProviderRecord,
|
||||||
DepositGroupRecord,
|
DepositGroupRecord,
|
||||||
ExchangeRecord,
|
ExchangeRecord,
|
||||||
|
PeerPullPaymentInitiationRecord,
|
||||||
PeerPushPaymentInitiationRecord,
|
PeerPushPaymentInitiationRecord,
|
||||||
PurchaseRecord,
|
PurchaseRecord,
|
||||||
RecoupGroupRecord,
|
RecoupGroupRecord,
|
||||||
@ -204,13 +205,21 @@ export namespace RetryTags {
|
|||||||
export function forPeerPushPaymentInitiation(
|
export function forPeerPushPaymentInitiation(
|
||||||
ppi: PeerPushPaymentInitiationRecord,
|
ppi: PeerPushPaymentInitiationRecord,
|
||||||
): string {
|
): string {
|
||||||
return `${PendingTaskType.PeerPushOutgoing}:${ppi.pursePub}`;
|
return `${PendingTaskType.PeerPushInitiation}:${ppi.pursePub}`;
|
||||||
|
}
|
||||||
|
export function forPeerPullPaymentInitiation(
|
||||||
|
ppi: PeerPullPaymentInitiationRecord,
|
||||||
|
): string {
|
||||||
|
return `${PendingTaskType.PeerPullInitiation}:${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 {
|
export function byPeerPushPaymentInitiationPursePub(pursePub: string): string {
|
||||||
return `${PendingTaskType.PeerPushOutgoing}:${pursePub}`;
|
return `${PendingTaskType.PeerPushInitiation}:${pursePub}`;
|
||||||
|
}
|
||||||
|
export function byPeerPullPaymentInitiationPursePub(pursePub: string): string {
|
||||||
|
return `${PendingTaskType.PeerPullInitiation}:${pursePub}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,10 +195,11 @@ import {
|
|||||||
checkPeerPullPayment,
|
checkPeerPullPayment,
|
||||||
checkPeerPushPayment,
|
checkPeerPushPayment,
|
||||||
initiatePeerPullPayment,
|
initiatePeerPullPayment,
|
||||||
initiatePeerToPeerPush,
|
initiatePeerPushPayment,
|
||||||
preparePeerPullPayment,
|
preparePeerPullPayment,
|
||||||
preparePeerPushPayment,
|
preparePeerPushPayment,
|
||||||
processPeerPushOutgoing,
|
processPeerPullInitiation,
|
||||||
|
processPeerPushInitiation,
|
||||||
} from "./operations/pay-peer.js";
|
} from "./operations/pay-peer.js";
|
||||||
import { getPendingOperations } from "./operations/pending.js";
|
import { getPendingOperations } from "./operations/pending.js";
|
||||||
import {
|
import {
|
||||||
@ -318,8 +319,10 @@ async function callOperationHandler(
|
|||||||
}
|
}
|
||||||
case PendingTaskType.Backup:
|
case PendingTaskType.Backup:
|
||||||
return await processBackupForProvider(ws, pending.backupProviderBaseUrl);
|
return await processBackupForProvider(ws, pending.backupProviderBaseUrl);
|
||||||
case PendingTaskType.PeerPushOutgoing:
|
case PendingTaskType.PeerPushInitiation:
|
||||||
return await processPeerPushOutgoing(ws, pending.pursePub);
|
return await processPeerPushInitiation(ws, pending.pursePub);
|
||||||
|
case PendingTaskType.PeerPullInitiation:
|
||||||
|
return await processPeerPullInitiation(ws, pending.pursePub);
|
||||||
default:
|
default:
|
||||||
return assertUnreachable(pending);
|
return assertUnreachable(pending);
|
||||||
}
|
}
|
||||||
@ -1381,7 +1384,7 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
|
|||||||
}
|
}
|
||||||
case WalletApiOperation.InitiatePeerPushPayment: {
|
case WalletApiOperation.InitiatePeerPushPayment: {
|
||||||
const req = codecForInitiatePeerPushPaymentRequest().decode(payload);
|
const req = codecForInitiatePeerPushPaymentRequest().decode(payload);
|
||||||
return await initiatePeerToPeerPush(ws, req);
|
return await initiatePeerPushPayment(ws, req);
|
||||||
}
|
}
|
||||||
case WalletApiOperation.CheckPeerPushPayment: {
|
case WalletApiOperation.CheckPeerPushPayment: {
|
||||||
const req = codecForCheckPeerPushPaymentRequest().decode(payload);
|
const req = codecForCheckPeerPushPaymentRequest().decode(payload);
|
||||||
|
Loading…
Reference in New Issue
Block a user