From ea91e9334fa12e4eeec57fbab20dfc89905e1e53 Mon Sep 17 00:00:00 2001 From: Egge Date: Tue, 3 Dec 2024 20:03:13 +0100 Subject: [PATCH] more refactoring + cleanup more refactoring more refactoring --- src/CashuWallet.ts | 154 ++++------------------------- src/model/types/wallet/blinding.ts | 8 -- src/model/types/wallet/payloads.ts | 37 +++---- src/utils.ts | 11 --- test/integration.test.ts | 2 - test/wallet.test.ts | 4 +- 6 files changed, 39 insertions(+), 177 deletions(-) delete mode 100644 src/model/types/wallet/blinding.ts diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index 66750ef8..66cc8578 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -1,4 +1,3 @@ -import { bytesToHex, hexToBytes } from '@noble/hashes/utils'; import { CashuMint } from './CashuMint.js'; import { type MeltPayload, @@ -11,33 +10,28 @@ import { type MintQuotePayload, type MeltQuotePayload, type SendResponse, - type SerializedBlindedMessage, type Token, - SerializedBlindedSignature, GetInfoResponse, OutputAmounts, ProofState, MintQuoteResponse, MintQuoteState, MeltQuoteState, - SerializedDLEQ + SwapTransaction } from './model/types/index.js'; import { getDecodedToken, splitAmount, sumProofs, getKeepAmounts, - numberToHexPadded64, hasValidDleq, stripDleq } from './utils.js'; import { hashToCurve, pointFromHex } from '@cashu/crypto/modules/common'; -import { constructProofFromPromise, serializeProof } from '@cashu/crypto/modules/client'; +import { serializeProof } from '@cashu/crypto/modules/client'; import { getSignedProofs } from '@cashu/crypto/modules/client/NUT11'; -import { type Proof as NUT11Proof, DLEQ } from '@cashu/crypto/modules/common/index'; +import { type Proof as NUT11Proof } from '@cashu/crypto/modules/common/index'; import { SubscriptionCanceller } from './model/types/wallet/websocket.js'; -import { verifyDLEQProof_reblind } from '@cashu/crypto/modules/client/NUT12'; -import { SwapTransaction } from './model/types/wallet/blinding.js'; import { BlindingData } from './model/BlindingData.js'; /** * The default number of proofs per denomination to keep in a wallet. @@ -571,7 +565,7 @@ class CashuWallet { options?.customBlindingData ); const { signatures } = await this.mint.swap(swapTransaction.payload); - const swapProofs = this.constructProofs(signatures, swapTransaction.blindingData, keyset); + const swapProofs = swapTransaction.blindingData.map((d, i) => d.toProof(signatures[i], keyset)); const splitProofsToKeep: Array = []; const splitProofsToSend: Array = []; swapProofs.forEach((p, i) => { @@ -620,7 +614,7 @@ class CashuWallet { const validData = blindingData.filter((d) => outputs.some((o) => d.blindedMessage.B_ === o.B_)); return { - proofs: this.constructProofs(promises, validData, keys) + proofs: validData.map((d, i) => d.toProof(promises[i], keys)) }; } @@ -683,19 +677,19 @@ class CashuWallet { }; } - const blindingData = this.createRandomBlindedMessages( + const blindingData = this.createBlindedMessages( amount, keyset, - options?.outputAmounts?.keepAmounts, options?.counter, - options?.pubkey + options?.pubkey, + options?.outputAmounts?.keepAmounts ); const mintPayload: MintPayload = { outputs: blindingData.map((d) => d.blindedMessage), quote: quote }; const { signatures } = await this.mint.mint(mintPayload); - return this.constructProofs(signatures, blindingData, keyset); + return blindingData.map((d, i) => d.toProof(signatures[i], keyset)); } /** @@ -769,13 +763,9 @@ class CashuWallet { outputs: blindingData.map((d) => d.blindedMessage) }; const meltResponse = await this.mint.melt(meltPayload); - let change: Array = []; - if (meltResponse.change) { - change = this.constructProofs(meltResponse.change, blindingData, keys); - } return { quote: meltResponse, - change: change + change: meltResponse.change?.map((s, i) => blindingData[i].toProof(s, keys)) ?? [] }; } @@ -815,53 +805,28 @@ class CashuWallet { if (customBlindingData?.keep) { keepBlindingData = customBlindingData.keep; - } else if (this._seed && counter) { - keepBlindingData = BlindingData.createDeterministicData( - keepAmount, - this._seed, - counter, - keyset, - outputAmounts?.keepAmounts - ); - counter = counter + keepBlindingData.length; - } else if (pubkey) { - keepBlindingData = BlindingData.createP2PKData( - pubkey, - keepAmount, - keyset, - outputAmounts?.keepAmounts - ); } else { - keepBlindingData = BlindingData.createRandomData( + keepBlindingData = this.createBlindedMessages( keepAmount, keyset, + counter, + pubkey, outputAmounts?.keepAmounts ); } + if (customBlindingData?.send) { sendBlindingData = customBlindingData.send; - } else if (counter || counter === 0) { - if (!this._seed) { - throw new Error('cannot create deterministic messages without seed'); - } - sendBlindingData = BlindingData.createDeterministicData( + } else { + sendBlindingData = this.createBlindedMessages( amount, - this._seed, - counter, keyset, - outputAmounts?.sendAmounts - ); - counter = counter + sendBlindingData.length; - } else if (pubkey) { - sendBlindingData = BlindingData.createP2PKData( + counter, pubkey, - amount, - keyset, outputAmounts?.sendAmounts ); - } else { - sendBlindingData = BlindingData.createRandomData(amount, keyset, outputAmounts?.sendAmounts); } + if (privkey) { proofsToSend = getSignedProofs( proofsToSend.map((p: Proof) => { @@ -1067,30 +1032,6 @@ class CashuWallet { }; } - /** - * Creates blinded messages for a given amount - * @param amount amount to create blinded messages for - * @param split optional preference for splitting proofs into specific amounts. overrides amount param - * @param keyksetId? override the keysetId derived from the current mintKeys with a custom one. This should be a keyset that was fetched from the `/keysets` endpoint - * @param counter? optionally set counter to derive secret deterministically. CashuWallet class must be initialized with seed phrase to take effect - * @param pubkey? optionally locks ecash to pubkey. Will not be deterministic, even if counter is set! - * @returns blinded messages, secrets, rs, and amounts - */ - private createRandomBlindedMessages( - amount: number, - keyset: MintKeys, - split?: Array, - counter?: number, - pubkey?: string - ): Array<{ - blindingFactor: bigint; - secret: Uint8Array; - blindedMessage: SerializedBlindedMessage; - }> { - const amounts = splitAmount(amount, keyset.keys, split); - return this.createBlindedMessages(amount, keyset, counter, pubkey, amounts); - } - /** * Creates blinded messages for a according to @param amounts * @param amount array of amounts to create blinded messages for @@ -1138,11 +1079,7 @@ class CashuWallet { amount: number, keyset: MintKeys, counter?: number - ): Array<{ - blindingFactor: bigint; - secret: Uint8Array; - blindedMessage: SerializedBlindedMessage; - }> { + ): Array { let count = Math.ceil(Math.log2(amount)) || 1; //Prevent count from being -Infinity if (count < 0) { @@ -1151,59 +1088,6 @@ class CashuWallet { const amounts = count ? Array(count).fill(1) : []; return this.createBlindedMessages(amount, keyset, counter, undefined, amounts); } - - /** - * construct proofs from @params promises, @params rs, @params secrets, and @params keyset - * @param promises array of serialized blinded signatures - * @param rs arrays of binding factors - * @param secrets array of secrets - * @param keyset mint keyset - * @returns array of serialized proofs - */ - private constructProofs( - promises: Array, - blindingData: Array<{ - blindingFactor: bigint; - secret: Uint8Array; - blindedMessage: SerializedBlindedMessage; - }>, - keyset: MintKeys - ): Array { - return promises.map((p: SerializedBlindedSignature, i: number) => { - const dleq = - p.dleq == undefined - ? undefined - : ({ - s: hexToBytes(p.dleq.s), - e: hexToBytes(p.dleq.e), - r: blindingData[i].blindingFactor - } as DLEQ); - const blindSignature = { - id: p.id, - amount: p.amount, - C_: pointFromHex(p.C_), - dleq: dleq - }; - const r = blindingData[i].blindingFactor; - const secret = blindingData[i].secret; - const A = pointFromHex(keyset.keys[p.amount]); - const proof = constructProofFromPromise(blindSignature, r, secret, A); - const serializedProof = { - ...serializeProof(proof), - ...(dleq && { - dleqValid: verifyDLEQProof_reblind(secret, dleq, proof.C, A) - }), - ...(dleq && { - dleq: { - s: bytesToHex(dleq.s), - e: bytesToHex(dleq.e), - r: numberToHexPadded64(dleq.r ?? BigInt(0)) - } as SerializedDLEQ - }) - } as Proof; - return serializedProof; - }); - } } export { CashuWallet }; diff --git a/src/model/types/wallet/blinding.ts b/src/model/types/wallet/blinding.ts deleted file mode 100644 index 7d2db17e..00000000 --- a/src/model/types/wallet/blinding.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { SwapPayload } from '.'; -import { BlindingData } from '../../BlindingData'; - -export type SwapTransaction = { - payload: SwapPayload; - blindingData: Array; - keepVector: Array; -}; diff --git a/src/model/types/wallet/payloads.ts b/src/model/types/wallet/payloads.ts index 2ab98720..016181a8 100644 --- a/src/model/types/wallet/payloads.ts +++ b/src/model/types/wallet/payloads.ts @@ -1,23 +1,6 @@ +import { BlindingData } from '../../BlindingData'; import { Proof } from './index'; -/** - * Data that the library needs to hold in memory while it awaits the blinded signatures for the mint. It is later used for unblinding the signatures. - */ -export type BlindingData = { - /** - * Blinded messages sent to the mint for signing. - */ - blindedMessages: Array; - /** - * secrets, kept client side for constructing proofs later. - */ - secrets: Array; - /** - * Blinding factor used for blinding messages and unblinding signatures after they are received from the mint. - */ - blindingFactors: Array; -}; - /** * Payload that needs to be sent to the mint when melting. Includes Return for overpaid fees */ @@ -113,3 +96,21 @@ export type SerializedBlindedMessage = { */ id: string; }; + +/** + * includes all data required to swap inputs for outputs and construct proofs from them. + */ +export type SwapTransaction = { + /** + * payload that will be sent to the mint for a swap + */ + payload: SwapPayload; + /** + * blinding data required to construct proofs + */ + blindingData: Array; + /** + * list of booleans to determine which proofs to keep + */ + keepVector: Array; +}; diff --git a/src/utils.ts b/src/utils.ts index 335787eb..0d92b7ad 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -5,7 +5,6 @@ import { encodeUint8toBase64Url } from './base64.js'; import { - BlindingData, DeprecatedToken, Keys, MintKeys, @@ -71,16 +70,6 @@ export function splitAmount( return split.sort((a, b) => (order === 'desc' ? b - a : a - b)); } -export function mergeBlindingData(...data: Array): BlindingData { - const mergedData: BlindingData = { blindedMessages: [], blindingFactors: [], secrets: [] }; - data.forEach((d) => { - mergedData.secrets.push(...d.secrets); - mergedData.blindingFactors.push(...d.blindingFactors); - mergedData.blindedMessages.push(...d.blindedMessages); - }); - return mergedData; -} - /** * Creates a list of amounts to keep based on the proofs we have and the proofs we want to reach. * @param proofsWeHave complete set of proofs stored (from current mint) diff --git a/test/integration.test.ts b/test/integration.test.ts index c99f3b5c..cb0d5c3c 100644 --- a/test/integration.test.ts +++ b/test/integration.test.ts @@ -14,11 +14,9 @@ import { import ws from 'ws'; import { injectWebSocketImpl } from '../src/ws.js'; import { - deriveKeysetId, getEncodedToken, getEncodedTokenV4, hexToNumber, - mergeBlindingData, numberToHexPadded64, sumProofs } from '../src/utils.js'; diff --git a/test/wallet.test.ts b/test/wallet.test.ts index 09bb63a9..add7e41a 100644 --- a/test/wallet.test.ts +++ b/test/wallet.test.ts @@ -8,11 +8,9 @@ import { MintQuoteResponse, MintQuoteState } from '../src/model/types/index.js'; -import { getDecodedToken, mergeBlindingData, splitAmount } from '../src/utils.js'; -import { Proof } from '@cashu/crypto/modules/common'; +import { getDecodedToken } from '../src/utils.js'; import { Server, WebSocket } from 'mock-socket'; import { injectWebSocketImpl } from '../src/ws.js'; -import { BlindingData } from '../src/model/BlindingData.js'; injectWebSocketImpl(WebSocket);