taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit ae990ccbf08988014a8fad2d80eb2b529896c102
parent ef2f1c0d0628c50c6f768bc552655119b59f7687
Author: Florian Dold <florian@dold.me>
Date:   Fri, 22 Aug 2025 23:47:57 +0200

wallet-core: allow faking p2p push transactions

Diffstat:
Mpackages/taler-wallet-core/src/dev-experiments.ts | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 121 insertions(+), 2 deletions(-)

diff --git a/packages/taler-wallet-core/src/dev-experiments.ts b/packages/taler-wallet-core/src/dev-experiments.ts @@ -34,6 +34,7 @@ import { Logger, MerchantContractTermsV0, MerchantContractVersion, + PeerContractTerms, RefreshReason, TalerPreciseTimestamp, encodeCrock, @@ -50,15 +51,20 @@ import { PendingTaskType, constructTaskIdentifier } from "./common.js"; import { DenomLossEventRecord, DenomLossStatus, + PeerPushCreditStatus, + PeerPushDebitStatus, PurchaseStatus, RefreshGroupRecord, RefreshOperationStatus, WithdrawalGroupStatus, WithdrawalRecordType, timestampPreciseToDb, + timestampProtocolToDb, } from "./db.js"; import { DenomLossTransactionContext } from "./exchanges.js"; import { PayMerchantTransactionContext } from "./pay-merchant.js"; +import { PeerPushCreditTransactionContext } from "./pay-peer-push-credit.js"; +import { PeerPushDebitTransactionContext } from "./pay-peer-push-debit.js"; import { RefreshTransactionContext } from "./refresh.js"; import { rematerializeTransactions } from "./transactions.js"; import { DevExperimentState, WalletExecutionContext } from "./wallet.js"; @@ -286,8 +292,6 @@ export async function applyDevExperiment( } const merchantName = parsedUri.query?.get("merchantName") ?? "Test Merchant"; - const noncePair = await wex.ws.cryptoApi.createEddsaKeypair({}); - const merchantPub = encodeCrock(getRandomBytes(32)); const exchangeBaseUrl = parsedUri.query?.get("exchangeBaseUrl") ?? "https://exchange.demo.taler.net/"; @@ -306,6 +310,8 @@ export async function applyDevExperiment( timestamp = AbsoluteTime.now(); } const orderId = encodeCrock(randomBytes(8)); + const noncePair = await wex.ws.cryptoApi.createEddsaKeypair({}); + const merchantPub = encodeCrock(getRandomBytes(32)); const ct: MerchantContractTermsV0 = { amount: Amounts.stringify(amountEffective), exchanges: [ @@ -380,6 +386,119 @@ export async function applyDevExperiment( }); break; } + case "peer-push-debit": { + const pursePair = await wex.cryptoApi.createEddsaKeypair({}); + const pursePub = pursePair.pub; + const ctx = new PeerPushDebitTransactionContext(wex, pursePub); + const summary = parsedUri.query?.get("summary") ?? "Test"; + const amountEffectiveStr = parsedUri.query?.get("amountEffective"); + if (!amountEffectiveStr) { + throw Error("missing amountEffective option"); + } + const exchangeBaseUrl = + parsedUri.query?.get("exchangeBaseUrl") ?? + "https://exchange.demo.taler.net/"; + const amountEffective = Amounts.parseOrThrow(amountEffectiveStr); + const trelStr = parsedUri.query?.get("tRel") ?? undefined; + let timestamp: AbsoluteTime; + if (trelStr) { + timestamp = AbsoluteTime.subtractDuraction( + AbsoluteTime.now(), + Duration.fromPrettyString(trelStr), + ); + } else { + timestamp = AbsoluteTime.now(); + } + const ct: PeerContractTerms = { + amount: Amounts.stringify(amountEffective), + purse_expiration: AbsoluteTime.toProtocolTimestamp(timestamp), + summary, + }; + const contractTermsHash = ContractTermsUtil.hashContractTerms(ct); + const contractPair = await wex.cryptoApi.createEddsaKeypair({}); + const mergePair = await wex.cryptoApi.createEddsaKeypair({}); + await wex.db.runAllStoresReadWriteTx({}, async (tx) => { + await tx.contractTerms.put({ + contractTermsRaw: ct, + h: contractTermsHash, + }); + await tx.peerPushDebit.put({ + amount: Amounts.stringify(amountEffective), + contractEncNonce: encodeCrock(randomBytes(32)), + contractPriv: contractPair.priv, + contractPub: contractPair.pub, + contractTermsHash: contractTermsHash, + exchangeBaseUrl, + mergePriv: mergePair.priv, + mergePub: mergePair.pub, + purseExpiration: timestampProtocolToDb(ct.purse_expiration), + pursePriv: pursePair.priv, + pursePub: pursePair.pub, + status: PeerPushDebitStatus.Done, + timestampCreated: timestampPreciseToDb( + AbsoluteTime.toPreciseTimestamp(timestamp), + ), + totalCost: Amounts.stringify(amountEffective), + }); + await ctx.updateTransactionMeta(tx); + }); + break; + } + case "peer-push-credit": { + const pursePair = await wex.cryptoApi.createEddsaKeypair({}); + const pursePub = pursePair.pub; + const peerPushCreditId = encodeCrock(randomBytes(32)); + const ctx = new PeerPushCreditTransactionContext(wex, pursePub); + const summary = parsedUri.query?.get("summary") ?? "Test"; + const amountEffectiveStr = parsedUri.query?.get("amountEffective"); + if (!amountEffectiveStr) { + throw Error("missing amountEffective option"); + } + const exchangeBaseUrl = + parsedUri.query?.get("exchangeBaseUrl") ?? + "https://exchange.demo.taler.net/"; + const amountEffective = Amounts.parseOrThrow(amountEffectiveStr); + const trelStr = parsedUri.query?.get("tRel") ?? undefined; + let timestamp: AbsoluteTime; + if (trelStr) { + timestamp = AbsoluteTime.subtractDuraction( + AbsoluteTime.now(), + Duration.fromPrettyString(trelStr), + ); + } else { + timestamp = AbsoluteTime.now(); + } + const ct: PeerContractTerms = { + amount: Amounts.stringify(amountEffective), + purse_expiration: AbsoluteTime.toProtocolTimestamp(timestamp), + summary, + }; + const contractTermsHash = ContractTermsUtil.hashContractTerms(ct); + const contractPair = await wex.cryptoApi.createEddsaKeypair({}); + const mergePair = await wex.cryptoApi.createEddsaKeypair({}); + await wex.db.runAllStoresReadWriteTx({}, async (tx) => { + await tx.contractTerms.put({ + contractTermsRaw: ct, + h: contractTermsHash, + }); + await tx.peerPushCredit.put({ + contractPriv: contractPair.priv, + contractTermsHash, + currency: Amounts.currencyOf(amountEffective), + estimatedAmountEffective: Amounts.stringify(amountEffective), + exchangeBaseUrl, + mergePriv: mergePair.priv, + peerPushCreditId, + pursePub, + status: PeerPushCreditStatus.Done, + timestamp: timestampPreciseToDb( + AbsoluteTime.toPreciseTimestamp(timestamp), + ), + withdrawalGroupId: undefined, + }); + }); + break; + } default: { throw Error("transaction type not supported"); }