diff options
-rw-r--r-- | packages/taler-util/src/talerCrypto.ts | 6 | ||||
-rw-r--r-- | packages/taler-util/src/talerTypes.ts | 7 | ||||
-rw-r--r-- | packages/taler-util/src/walletTypes.ts | 2 | ||||
-rw-r--r-- | packages/taler-wallet-cli/src/index.ts | 2 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/util/coinSelection.test.ts | 42 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/wallet.ts | 5 |
6 files changed, 59 insertions, 5 deletions
diff --git a/packages/taler-util/src/talerCrypto.ts b/packages/taler-util/src/talerCrypto.ts index 228dc3269..e27e329a9 100644 --- a/packages/taler-util/src/talerCrypto.ts +++ b/packages/taler-util/src/talerCrypto.ts @@ -664,7 +664,6 @@ export function hashDenomPub(pub: DenominationPubKey): Uint8Array { const hashInputBuf = new ArrayBuffer(pubBuf.length + 4 + 4); const uint8ArrayBuf = new Uint8Array(hashInputBuf); const dv = new DataView(hashInputBuf); - logger.info("age_mask", pub.age_mask); dv.setUint32(0, pub.age_mask ?? 0); dv.setUint32(4, DenomKeyType.toIntTag(pub.cipher)); uint8ArrayBuf.set(pubBuf, 8); @@ -680,8 +679,7 @@ export function hashDenomPub(pub: DenominationPubKey): Uint8Array { return nacl.hash(uint8ArrayBuf); } else { throw Error( - `unsupported cipher (${ - (pub as DenominationPubKey).cipher + `unsupported cipher (${(pub as DenominationPubKey).cipher }), unable to hash`, ); } @@ -785,7 +783,7 @@ export enum TalerSignaturePurpose { export class SignaturePurposeBuilder { private chunks: Uint8Array[] = []; - constructor(private purposeNum: number) {} + constructor(private purposeNum: number) { } put(bytes: Uint8Array): SignaturePurposeBuilder { this.chunks.push(Uint8Array.from(bytes)); diff --git a/packages/taler-util/src/talerTypes.ts b/packages/taler-util/src/talerTypes.ts index ffc1f5160..b21c6caec 100644 --- a/packages/taler-util/src/talerTypes.ts +++ b/packages/taler-util/src/talerTypes.ts @@ -47,7 +47,7 @@ import { } from "./time.js"; import { codecForAmountString } from "./amounts.js"; import { strcmp } from "./helpers.js"; -import { Edx25519PublicKey } from "./talerCrypto.js"; +import { AgeCommitmentProof, Edx25519PublicKey } from "./talerCrypto.js"; /** * Denomination as found in the /keys response from the exchange. @@ -954,6 +954,11 @@ export interface CoinDumpJson { * Suspended coins are not considered for payments. */ coin_suspended: boolean; + + /** + * Information about the age restriction + */ + ageCommitmentProof: AgeCommitmentProof | undefined; }>; } diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts index d8696377a..a8946fbbb 100644 --- a/packages/taler-util/src/walletTypes.ts +++ b/packages/taler-util/src/walletTypes.ts @@ -738,6 +738,7 @@ export const codecForGetExchangeTosRequest = (): Codec<GetExchangeTosRequest> => export interface AcceptManualWithdrawalRequest { exchangeBaseUrl: string; amount: string; + restrictAge?: number, } export const codecForAcceptManualWithdrawalRequet = @@ -745,6 +746,7 @@ export const codecForAcceptManualWithdrawalRequet = buildCodecForObject<AcceptManualWithdrawalRequest>() .property("exchangeBaseUrl", codecForString()) .property("amount", codecForString()) + .property("restrictAge", codecOptional(codecForNumber())) .build("AcceptManualWithdrawalRequest"); export interface GetWithdrawalDetailsForAmountRequest { diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts index a4c99902c..5ba6e4bf2 100644 --- a/packages/taler-wallet-cli/src/index.ts +++ b/packages/taler-wallet-cli/src/index.ts @@ -775,6 +775,7 @@ advancedCli .requiredOption("amount", ["--amount"], clk.STRING, { help: "Amount to withdraw", }) + .maybeOption("restrictAge", ["--restrict-age"], clk.INT) .action(async (args) => { await withWallet(args, async (wallet) => { const exchangeBaseUrl = args.withdrawManually.exchange; @@ -796,6 +797,7 @@ advancedCli { amount, exchangeBaseUrl, + restrictAge: parseInt(String(args.withdrawManually.restrictAge), 10), }, ); const reservePub = resp.reservePub; diff --git a/packages/taler-wallet-core/src/util/coinSelection.test.ts b/packages/taler-wallet-core/src/util/coinSelection.test.ts index dc64a57dc..ca7b76eb5 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.test.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.test.ts @@ -43,6 +43,20 @@ function fakeAci(current: string, feeDeposit: string): AvailableCoinInfo { }; } +function fakeAciWithAgeRestriction(current: string, feeDeposit: string): AvailableCoinInfo { + return { + availableAmount: a(current), + coinPub: "foobar", + denomPub: { + cipher: DenomKeyType.Rsa, + rsa_public_key: "foobar", + age_mask: 2446657, + }, + feeDeposit: a(feeDeposit), + exchangeBaseUrl: "https://example.com/", + }; +} + test("it should be able to pay if merchant takes the fees", (t) => { const acis: AvailableCoinInfo[] = [ fakeAci("EUR:1.0", "EUR:0.1"), @@ -267,3 +281,31 @@ test("coin selection 9", (t) => { ); t.pass(); }); + + +test("it should be able to use unrestricted coins for age restricted contract", (t) => { + const acis: AvailableCoinInfo[] = [ + fakeAciWithAgeRestriction("EUR:1.0", "EUR:0.2"), + fakeAciWithAgeRestriction("EUR:0.2", "EUR:0.2"), + ]; + const res = selectPayCoins({ + candidates: { + candidateCoins: acis, + wireFeesPerExchange: {}, + }, + contractTermsAmount: a("EUR:1.2"), + depositFeeLimit: a("EUR:0.4"), + wireFeeLimit: a("EUR:0"), + wireFeeAmortization: 1, + requiredMinimumAge: 13 + }); + if (!res) { + t.fail(); + return; + } + t.true(res.coinContributions.length === 2); + t.true( + Amounts.cmp(Amounts.sum(res.coinContributions).amount, "EUR:1.2") === 0, + ); + t.pass(); +}); diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index fb61ae0dc..053a0763b 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -454,11 +454,14 @@ async function acceptManualWithdrawal( ws: InternalWalletState, exchangeBaseUrl: string, amount: AmountJson, + restrictAge?: number, + ): Promise<AcceptManualWithdrawalResult> { try { const resp = await createReserve(ws, { amount, exchange: exchangeBaseUrl, + restrictAge }); const exchangePaytoUris = await ws.db .mktx((x) => ({ @@ -690,6 +693,7 @@ async function dumpCoins(ws: InternalWalletState): Promise<CoinDumpJson> { remaining_value: Amounts.stringify(c.currentAmount), withdrawal_reserve_pub: withdrawalReservePub, coin_suspended: c.suspended, + ageCommitmentProof: c.ageCommitmentProof, }); } }); @@ -801,6 +805,7 @@ async function dispatchRequestInternal( ws, req.exchangeBaseUrl, Amounts.parseOrThrow(req.amount), + req.restrictAge ); return res; } |