diff options
author | Florian Dold <florian@dold.me> | 2022-08-23 11:29:45 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2022-08-23 20:35:11 +0200 |
commit | f3ff5a72257dda27cab555f8b8d921d45bfc3e4b (patch) | |
tree | 708494a3aa2346fead51931fe4bfc41cc0249659 /packages/taler-wallet-core/src/operations | |
parent | 4ca38113abee2c0ca17b26aa55cf6a0ecafe49c9 (diff) |
peer-to-peer pull payments MVP
p2p pull wip
Diffstat (limited to 'packages/taler-wallet-core/src/operations')
-rw-r--r-- | packages/taler-wallet-core/src/operations/peer-to-peer.ts | 164 |
1 files changed, 146 insertions, 18 deletions
diff --git a/packages/taler-wallet-core/src/operations/peer-to-peer.ts b/packages/taler-wallet-core/src/operations/peer-to-peer.ts index 4d2f2bb5f..eca319a29 100644 --- a/packages/taler-wallet-core/src/operations/peer-to-peer.ts +++ b/packages/taler-wallet-core/src/operations/peer-to-peer.ts @@ -37,7 +37,10 @@ import { eddsaGetPublic, encodeCrock, ExchangePurseMergeRequest, + ExchangeReservePurseRequest, getRandomBytes, + InitiatePeerPullPaymentRequest, + InitiatePeerPullPaymentResponse, InitiatePeerPushPaymentRequest, InitiatePeerPushPaymentResponse, j2s, @@ -370,24 +373,12 @@ export function talerPaytoFromExchangeReserve( return `payto://${proto}/${url.host}${url.pathname}${reservePub}`; } -export async function acceptPeerPushPayment( +async function getMergeReserveInfo( ws: InternalWalletState, - req: AcceptPeerPushPaymentRequest, -) { - const peerInc = await ws.db - .mktx((x) => ({ peerPushPaymentIncoming: x.peerPushPaymentIncoming })) - .runReadOnly(async (tx) => { - return tx.peerPushPaymentIncoming.get(req.peerPushPaymentIncomingId); - }); - - if (!peerInc) { - throw Error( - `can't accept unknown incoming p2p push payment (${req.peerPushPaymentIncomingId})`, - ); - } - - const amount = Amounts.parseOrThrow(peerInc.contractTerms.amount); - + req: { + exchangeBaseUrl: string; + }, +): Promise<MergeReserveInfo> { // We have to eagerly create the key pair outside of the transaction, // due to the async crypto API. const newReservePair = await ws.cryptoApi.createEddsaKeypair({}); @@ -398,7 +389,7 @@ export async function acceptPeerPushPayment( withdrawalGroups: x.withdrawalGroups, })) .runReadWrite(async (tx) => { - const ex = await tx.exchanges.get(peerInc.exchangeBaseUrl); + const ex = await tx.exchanges.get(req.exchangeBaseUrl); checkDbInvariant(!!ex); if (ex.currentMergeReserveInfo) { return ex.currentMergeReserveInfo; @@ -411,6 +402,31 @@ export async function acceptPeerPushPayment( return ex.currentMergeReserveInfo; }); + return mergeReserveInfo; +} + +export async function acceptPeerPushPayment( + ws: InternalWalletState, + req: AcceptPeerPushPaymentRequest, +) { + const peerInc = await ws.db + .mktx((x) => ({ peerPushPaymentIncoming: x.peerPushPaymentIncoming })) + .runReadOnly(async (tx) => { + return tx.peerPushPaymentIncoming.get(req.peerPushPaymentIncomingId); + }); + + if (!peerInc) { + throw Error( + `can't accept unknown incoming p2p push payment (${req.peerPushPaymentIncomingId})`, + ); + } + + const amount = Amounts.parseOrThrow(peerInc.contractTerms.amount); + + const mergeReserveInfo = await getMergeReserveInfo(ws, { + exchangeBaseUrl: peerInc.exchangeBaseUrl, + }); + const mergeTimestamp = TalerProtocolTimestamp.now(); const reservePayto = talerPaytoFromExchangeReserve( @@ -461,3 +477,115 @@ export async function acceptPeerPushPayment( }, }); } + +export async function initiatePeerRequestForPay( + ws: InternalWalletState, + req: InitiatePeerPullPaymentRequest, +): Promise<InitiatePeerPullPaymentResponse> { + const mergeReserveInfo = await getMergeReserveInfo(ws, { + exchangeBaseUrl: req.exchangeBaseUrl, + }); + + const mergeTimestamp = TalerProtocolTimestamp.now(); + + const pursePair = await ws.cryptoApi.createEddsaKeypair({}); + const mergePair = await ws.cryptoApi.createEddsaKeypair({}); + + const purseExpiration: TalerProtocolTimestamp = AbsoluteTime.toTimestamp( + AbsoluteTime.addDuration( + AbsoluteTime.now(), + Duration.fromSpec({ days: 2 }), + ), + ); + + const reservePayto = talerPaytoFromExchangeReserve( + req.exchangeBaseUrl, + mergeReserveInfo.reservePub, + ); + + const contractTerms = { + ...req.partialContractTerms, + amount: req.amount, + purse_expiration: purseExpiration, + }; + + const econtractResp = await ws.cryptoApi.encryptContractForDeposit({ + contractTerms, + pursePriv: pursePair.priv, + pursePub: pursePair.pub, + }); + + const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms); + + const purseFee = Amounts.stringify( + Amounts.getZero(Amounts.parseOrThrow(req.amount).currency), + ); + + const sigRes = await ws.cryptoApi.signReservePurseCreate({ + contractTermsHash: hContractTerms, + flags: WalletAccountMergeFlags.CreateWithPurseFee, + mergePriv: mergePair.priv, + mergeTimestamp: mergeTimestamp, + purseAmount: req.amount, + purseExpiration: purseExpiration, + purseFee: purseFee, + pursePriv: pursePair.priv, + pursePub: pursePair.pub, + reservePayto, + reservePriv: mergeReserveInfo.reservePriv, + }); + + await ws.db + .mktx((x) => ({ + peerPullPaymentInitiation: x.peerPullPaymentInitiation, + })) + .runReadWrite(async (tx) => { + await tx.peerPullPaymentInitiation.put({ + amount: req.amount, + contractTerms, + exchangeBaseUrl: req.exchangeBaseUrl, + pursePriv: pursePair.priv, + pursePub: pursePair.pub, + }); + }); + + const reservePurseReqBody: ExchangeReservePurseRequest = { + merge_sig: sigRes.mergeSig, + merge_timestamp: mergeTimestamp, + 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.amount, + reserve_sig: sigRes.accountSig, + econtract: econtractResp.econtract, + }; + + logger.info(`reserve purse request: ${j2s(reservePurseReqBody)}`); + + const reservePurseMergeUrl = new URL( + `reserves/${mergeReserveInfo.reservePub}/purse`, + req.exchangeBaseUrl, + ); + + const httpResp = await ws.http.postJson( + reservePurseMergeUrl.href, + reservePurseReqBody, + ); + + const resp = await readSuccessResponseJsonOrThrow(httpResp, codecForAny()); + + logger.info(`reserve merge response: ${j2s(resp)}`); + + // FIXME: Now create a withdrawal operation! + + return { + talerUri: constructPayPushUri({ + exchangeBaseUrl: req.exchangeBaseUrl, + contractPriv: econtractResp.contractPriv, + }), + }; +} |