From 79ebc070e1bacaad717b4e8009005e5d5f1a5c57 Mon Sep 17 00:00:00 2001 From: Egge Date: Thu, 19 Dec 2024 14:07:57 +0100 Subject: [PATCH] renamed BlindingData to OutputData --- src/CashuWallet.ts | 141 +++++++++---------- src/model/{BlindingData.ts => OutputData.ts} | 22 +-- src/model/types/index.ts | 16 +-- src/model/types/wallet/payloads.ts | 4 +- test/integration.test.ts | 22 +-- test/wallet.test.ts | 8 +- 6 files changed, 102 insertions(+), 111 deletions(-) rename src/model/{BlindingData.ts => OutputData.ts} (90%) diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index 55812526..6a0ff10e 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -54,11 +54,11 @@ import { sumProofs } from './utils.js'; import { - BlindingData, - BlindingDataFactory, - BlindingDataLike, - isBlindingDataFactory -} from './model/BlindingData.js'; + OutputData, + OutputDataFactory, + OutputDataLike, + isOutputDataFactory +} from './model/OutputData.js'; /** * The default number of proofs per denomination to keep in a wallet. @@ -82,7 +82,7 @@ class CashuWallet { private _unit = DEFAULT_UNIT; private _mintInfo: MintInfo | undefined = undefined; private _denominationTarget = DEFAULT_DENOMINATION_TARGET; - private _keepFactory: BlindingDataFactory | undefined; + private _keepFactory: OutputDataFactory | undefined; mint: CashuMint; @@ -105,7 +105,7 @@ class CashuWallet { mintInfo?: GetInfoResponse; bip39seed?: Uint8Array; denominationTarget?: number; - keepFactory?: BlindingDataFactory; + keepFactory?: OutputDataFactory; } ) { this.mint = mint; @@ -269,7 +269,7 @@ class CashuWallet { * @returns New token with newly created proofs, token entries that had errors */ async receive(token: string | Token, options?: ReceiveOptions): Promise> { - const { requireDleq, keysetId, outputAmounts, counter, pubkey, privkey, blindingData, p2pk } = + const { requireDleq, keysetId, outputAmounts, counter, pubkey, privkey, outputData, p2pk } = options || {}; if (typeof token === 'string') { @@ -282,12 +282,11 @@ class CashuWallet { } } const amount = sumProofs(token.proofs) - this.getFeesForProofs(token.proofs); - let newBlindingData: { send: Array | BlindingDataFactory } | undefined = - undefined; - if (blindingData) { - newBlindingData = { send: blindingData }; + let newOutputData: { send: Array | OutputDataFactory } | undefined = undefined; + if (outputData) { + newOutputData = { send: outputData }; } else if (this._keepFactory) { - newBlindingData = { send: this._keepFactory }; + newOutputData = { send: this._keepFactory }; } const swapTransaction = this.createSwapPayload( amount, @@ -297,11 +296,11 @@ class CashuWallet { counter, pubkey, privkey, - newBlindingData, + newOutputData, p2pk ); const { signatures } = await this.mint.swap(swapTransaction.payload); - const proofs = swapTransaction.blindingData.map((d, i) => d.toProof(signatures[i], keys)); + const proofs = swapTransaction.outputData.map((d, i) => d.toProof(signatures[i], keys)); const orderedProofs: Array = []; swapTransaction.sortedIndices.forEach((s, o) => { orderedProofs[s] = proofs[o]; @@ -326,7 +325,7 @@ class CashuWallet { outputAmounts, pubkey, privkey, - blindingData + outputData } = options || {}; if (includeDleq) { proofs = proofs.filter((p: Proof) => p.dleq != undefined); @@ -347,7 +346,7 @@ class CashuWallet { pubkey || privkey || keysetId || - blindingData) // these options require a swap + outputData) // these options require a swap ) { // we need to swap // input selection, needs fees because of the swap @@ -489,7 +488,7 @@ class CashuWallet { */ async swap(amount: number, proofs: Array, options?: SwapOptions): Promise { let { outputAmounts } = options || {}; - const { includeFees, keysetId, counter, pubkey, privkey, proofsWeHave, blindingData, p2pk } = + const { includeFees, keysetId, counter, pubkey, privkey, proofsWeHave, outputData, p2pk } = options || {}; const keyset = await this.getKeys(keysetId); @@ -549,8 +548,8 @@ class CashuWallet { sendAmounts: sendAmounts }; - const customKeepBlindingData = blindingData?.keep || this._keepFactory; - const customSendBlindingData = blindingData?.send; + const keepOutputData = outputData?.keep || this._keepFactory; + const sendOutputData = outputData?.send; const swapTransaction = this.createSwapPayload( amountToSend, @@ -560,11 +559,11 @@ class CashuWallet { counter, pubkey, privkey, - { keep: customKeepBlindingData, send: customSendBlindingData }, + { keep: keepOutputData, send: sendOutputData }, p2pk ); const { signatures } = await this.mint.swap(swapTransaction.payload); - const swapProofs = swapTransaction.blindingData.map((d, i) => d.toProof(signatures[i], keyset)); + const swapProofs = swapTransaction.outputData.map((d, i) => d.toProof(signatures[i], keyset)); const splitProofsToKeep: Array = []; const splitProofsToSend: Array = []; const reorderedKeepVector = Array(swapTransaction.keepVector.length); @@ -604,7 +603,7 @@ class CashuWallet { } // create blank amounts for unknown restore amounts const amounts = Array(count).fill(1); - const blindingData = BlindingData.createDeterministicData( + const outputData = OutputData.createDeterministicData( amounts.length, this._seed, start, @@ -613,10 +612,10 @@ class CashuWallet { ); const { outputs, promises } = await this.mint.restore({ - outputs: blindingData.map((d) => d.blindedMessage) + outputs: outputData.map((d) => d.blindedMessage) }); - const validData = blindingData.filter((d) => outputs.some((o) => d.blindedMessage.B_ === o.B_)); + const validData = outputData.filter((d) => outputs.some((o) => d.blindedMessage.B_ === o.B_)); return { proofs: validData.map((d, i) => d.toProof(promises[i], keys)) }; @@ -669,9 +668,9 @@ class CashuWallet { }; } - let newBlindingData: Array = []; + let newBlindingData: Array = []; if (blindingData) { - if (isBlindingDataFactory(blindingData)) { + if (isOutputDataFactory(blindingData)) { const amounts = splitAmount(amount, keyset.keys, outputAmounts?.keepAmounts); for (let i = 0; i < amounts.length; i++) { newBlindingData.push(blindingData(amounts[i], keyset)); @@ -685,7 +684,7 @@ class CashuWallet { newBlindingData.push(this._keepFactory(amounts[i], keyset)); } } else { - newBlindingData = this.createBlindedMessages( + newBlindingData = this.createOutputData( amount, keyset, counter, @@ -741,7 +740,7 @@ class CashuWallet { ): Promise { const { keysetId, counter, privkey } = options || {}; const keys = await this.getKeys(keysetId); - const blindingData = this.createBlankOutputs( + const outputData = this.createBlankOutputs( sumProofs(proofsToSend) - meltQuote.amount, keys, counter, @@ -766,12 +765,12 @@ class CashuWallet { const meltPayload: MeltPayload = { quote: meltQuote.quote, inputs: proofsToSend, - outputs: blindingData.map((d) => d.blindedMessage) + outputs: outputData.map((d) => d.blindedMessage) }; const meltResponse = await this.mint.melt(meltPayload); return { quote: meltResponse, - change: meltResponse.change?.map((s, i) => blindingData[i].toProof(s, keys)) ?? [] + change: meltResponse.change?.map((s, i) => outputData[i].toProof(s, keys)) ?? [] }; } @@ -793,9 +792,9 @@ class CashuWallet { counter?: number, pubkey?: string, privkey?: string, - customBlindingData?: { - keep?: Array | BlindingDataFactory; - send?: Array | BlindingDataFactory; + customOutputData?: { + keep?: Array | OutputDataFactory; + send?: Array | OutputDataFactory; }, p2pk?: { pubkey: string; locktime?: number; refundKeys?: Array } ): SwapTransaction { @@ -807,21 +806,21 @@ class CashuWallet { ); } const keepAmount = totalAmount - amount - this.getFeesForProofs(proofsToSend); - let keepBlindingData: Array = []; - let sendBlindingData: Array = []; + let keepOutputData: Array = []; + let sendOutputData: Array = []; - if (customBlindingData?.keep) { - if (isBlindingDataFactory(customBlindingData.keep)) { - const factory = customBlindingData.keep; + if (customOutputData?.keep) { + if (isOutputDataFactory(customOutputData.keep)) { + const factory = customOutputData.keep; const amounts = splitAmount(keepAmount, keyset.keys); amounts.forEach((a) => { - keepBlindingData.push(factory(a, keyset)); + keepOutputData.push(factory(a, keyset)); }); } else { - keepBlindingData = customBlindingData.keep; + keepOutputData = customOutputData.keep; } } else { - keepBlindingData = this.createBlindedMessages( + keepOutputData = this.createOutputData( keepAmount, keyset, counter, @@ -832,18 +831,18 @@ class CashuWallet { ); } - if (customBlindingData?.send) { - if (isBlindingDataFactory(customBlindingData.send)) { - const factory = customBlindingData.send; + if (customOutputData?.send) { + if (isOutputDataFactory(customOutputData.send)) { + const factory = customOutputData.send; const amounts = splitAmount(amount, keyset.keys); amounts.forEach((a) => { - sendBlindingData.push(factory(a, keyset)); + sendOutputData.push(factory(a, keyset)); }); } else { - sendBlindingData = customBlindingData.send; + sendOutputData = customOutputData.send; } } else { - sendBlindingData = this.createBlindedMessages( + sendOutputData = this.createOutputData( amount, keyset, counter, @@ -869,7 +868,7 @@ class CashuWallet { proofsToSend = stripDleq(proofsToSend); - const mergedBlindingData = [...keepBlindingData, ...sendBlindingData]; + const mergedBlindingData = [...keepOutputData, ...sendOutputData]; const indices = mergedBlindingData .map((_, i) => i) .sort( @@ -877,19 +876,19 @@ class CashuWallet { mergedBlindingData[a].blindedMessage.amount - mergedBlindingData[b].blindedMessage.amount ); const keepVector = [ - ...Array(keepBlindingData.length).fill(true), - ...Array(sendBlindingData.length).fill(false) + ...Array(keepOutputData.length).fill(true), + ...Array(sendOutputData.length).fill(false) ]; - const sortedBlindingData = indices.map((i) => mergedBlindingData[i]); + const sortedOutputData = indices.map((i) => mergedBlindingData[i]); const sortedKeepVector = indices.map((i) => keepVector[i]); return { payload: { inputs: proofsToSend, - outputs: sortedBlindingData.map((d) => d.blindedMessage) + outputs: sortedOutputData.map((d) => d.blindedMessage) }, - blindingData: sortedBlindingData, + outputData: sortedOutputData, keepVector: sortedKeepVector, sortedIndices: indices }; @@ -1067,23 +1066,23 @@ class CashuWallet { * @param pubkey? optionally locks ecash to pubkey. Will not be deterministic, even if counter is set! * @returns blinded messages, secrets, rs, and amounts */ - private createBlindedMessages( + private createOutputData( amount: number, keyset: MintKeys, counter?: number, pubkey?: string, outputAmounts?: Array, p2pk?: { pubkey: string; locktime?: number; refundKeys?: Array }, - factory?: BlindingDataFactory - ): Array { - let blindingData: Array; + factory?: OutputDataFactory + ): Array { + let outputData: Array; if (pubkey) { - blindingData = BlindingData.createP2PKData({ pubkey }, amount, keyset, outputAmounts); + outputData = OutputData.createP2PKData({ pubkey }, amount, keyset, outputAmounts); } else if (counter || counter === 0) { if (!this._seed) { throw new Error('cannot create deterministic messages without seed'); } - blindingData = BlindingData.createDeterministicData( + outputData = OutputData.createDeterministicData( amount, this._seed, counter, @@ -1091,14 +1090,14 @@ class CashuWallet { outputAmounts ); } else if (p2pk) { - blindingData = BlindingData.createP2PKData(p2pk, amount, keyset); + outputData = OutputData.createP2PKData(p2pk, amount, keyset); } else if (factory) { const amounts = splitAmount(amount, keyset.keys); - blindingData = amounts.map((a) => factory(a, keyset)); + outputData = amounts.map((a) => factory(a, keyset)); } else { - blindingData = BlindingData.createRandomData(amount, keyset, outputAmounts); + outputData = OutputData.createRandomData(amount, keyset, outputAmounts); } - return blindingData; + return outputData; } /** @@ -1113,23 +1112,15 @@ class CashuWallet { amount: number, keyset: MintKeys, counter?: number, - factory?: BlindingDataFactory - ): Array { + factory?: OutputDataFactory + ): Array { let count = Math.ceil(Math.log2(amount)) || 1; //Prevent count from being -Infinity if (count < 0) { count = 0; } const amounts = count ? Array(count).fill(1) : []; - return this.createBlindedMessages( - amount, - keyset, - counter, - undefined, - amounts, - undefined, - factory - ); + return this.createOutputData(amount, keyset, counter, undefined, amounts, undefined, factory); } } diff --git a/src/model/BlindingData.ts b/src/model/OutputData.ts similarity index 90% rename from src/model/BlindingData.ts rename to src/model/OutputData.ts index f19824bc..6cd44e0b 100644 --- a/src/model/BlindingData.ts +++ b/src/model/OutputData.ts @@ -17,7 +17,7 @@ import { verifyDLEQProof_reblind } from '@cashu/crypto/modules/client/NUT12'; import { bytesToNumber, numberToHexPadded64, splitAmount } from '../utils'; import { deriveBlindingFactor, deriveSecret } from '@cashu/crypto/modules/client/NUT09'; -export interface BlindingDataLike { +export interface OutputDataLike { blindedMessage: SerializedBlindedMessage; blindingFactor: bigint; secret: Uint8Array; @@ -25,15 +25,15 @@ export interface BlindingDataLike { toProof: (signature: SerializedBlindedSignature, keyset: MintKeys) => Proof; } -export type BlindingDataFactory = (amount: number, keys: MintKeys) => BlindingDataLike; +export type OutputDataFactory = (amount: number, keys: MintKeys) => OutputDataLike; -export function isBlindingDataFactory( - value: Array | BlindingDataFactory -): value is BlindingDataFactory { +export function isOutputDataFactory( + value: Array | OutputDataFactory +): value is OutputDataFactory { return typeof value === 'function'; } -export class BlindingData implements BlindingDataLike { +export class OutputData implements OutputDataLike { blindedMessage: SerializedBlindedMessage; blindingFactor: bigint; secret: Uint8Array; @@ -113,7 +113,7 @@ export class BlindingData implements BlindingDataLike { const parsed = JSON.stringify(newSecret); const secretBytes = new TextEncoder().encode(parsed); const { r, B_ } = blindMessage(secretBytes); - return new BlindingData( + return new OutputData( new BlindedMessage(amount, B_, keysetId).getSerializedBlindedMessage(), r, secretBytes @@ -129,7 +129,7 @@ export class BlindingData implements BlindingDataLike { const randomHex = bytesToHex(randomBytes(32)); const secretBytes = new TextEncoder().encode(randomHex); const { r, B_ } = blindMessage(secretBytes); - return new BlindingData( + return new OutputData( new BlindedMessage(amount, B_, keysetId).getSerializedBlindedMessage(), r, secretBytes @@ -142,9 +142,9 @@ export class BlindingData implements BlindingDataLike { counter: number, keyset: MintKeys, customSplit?: Array - ): Array { + ): Array { const amounts = splitAmount(amount, keyset.keys, customSplit); - const data: Array = []; + const data: Array = []; for (let i = 0; i < amounts.length; i++) { data.push(this.createSingleDeterministicData(amount, seed, counter + i, keyset.id)); } @@ -160,7 +160,7 @@ export class BlindingData implements BlindingDataLike { const secretBytes = deriveSecret(seed, keysetId, counter); const deterministicR = bytesToNumber(deriveBlindingFactor(seed, keysetId, counter)); const { r, B_ } = blindMessage(secretBytes, deterministicR); - return new BlindingData( + return new OutputData( new BlindedMessage(amount, B_, keysetId).getSerializedBlindedMessage(), r, secretBytes diff --git a/src/model/types/index.ts b/src/model/types/index.ts index bce9c8c6..eaab9f41 100644 --- a/src/model/types/index.ts +++ b/src/model/types/index.ts @@ -1,4 +1,4 @@ -import { BlindingDataFactory, BlindingDataLike } from '../BlindingData'; +import { OutputDataFactory, OutputDataLike } from '../OutputData'; import { Proof } from './wallet/index'; export * from './mint/index'; @@ -27,7 +27,7 @@ export type ReceiveOptions = { pubkey?: string; privkey?: string; requireDleq?: boolean; - blindingData?: Array | BlindingDataFactory; + outputData?: Array | OutputDataFactory; p2pk?: { pubkey: string; locktime?: number; refundKeys?: Array }; }; @@ -53,9 +53,9 @@ export type SendOptions = { offline?: boolean; includeFees?: boolean; includeDleq?: boolean; - blindingData?: { - send?: Array | BlindingDataFactory; - keep?: Array | BlindingDataFactory; + outputData?: { + send?: Array | OutputDataFactory; + keep?: Array | OutputDataFactory; }; p2pk?: { pubkey: string; locktime?: number; refundKeys?: Array }; }; @@ -81,8 +81,8 @@ export type SwapOptions = { keysetId?: string; includeFees?: boolean; blindingData?: { - send?: Array | BlindingDataFactory; - keep?: Array | BlindingDataFactory; + send?: Array | OutputDataFactory; + keep?: Array | OutputDataFactory; }; p2pk?: { pubkey: string; locktime?: number; refundKeys?: Array }; }; @@ -105,7 +105,7 @@ export type MintProofOptions = { proofsWeHave?: Array; counter?: number; pubkey?: string; - blindingData?: Array | BlindingDataFactory; + blindingData?: Array | OutputDataFactory; p2pk?: { pubkey: string; locktime?: number; refundKeys?: Array }; }; diff --git a/src/model/types/wallet/payloads.ts b/src/model/types/wallet/payloads.ts index ef3300ec..0c1e0660 100644 --- a/src/model/types/wallet/payloads.ts +++ b/src/model/types/wallet/payloads.ts @@ -1,4 +1,4 @@ -import { BlindingData } from '../../BlindingData'; +import { OutputData } from '../../OutputData'; import { Proof } from './index'; /** @@ -108,7 +108,7 @@ export type SwapTransaction = { /** * blinding data required to construct proofs */ - blindingData: Array; + outputData: Array; /** * list of booleans to determine which proofs to keep */ diff --git a/test/integration.test.ts b/test/integration.test.ts index a0cdb991..c7edc0b1 100644 --- a/test/integration.test.ts +++ b/test/integration.test.ts @@ -25,7 +25,7 @@ import { splitAmount, sumProofs } from '../src/utils.js'; -import { BlindingData, BlindingDataFactory } from '../src/model/BlindingData.js'; +import { OutputData, OutputDataFactory } from '../src/model/OutputData.js'; import { randomBytes } from '@noble/hashes/utils'; dns.setDefaultResultOrder('ipv4first'); @@ -494,7 +494,7 @@ describe('Custom Outputs', () => { test('Default keepFactory', async () => { // First we create a keep factory, this is a function that will be used to construct all outputs that we "keep" function p2pkFactory(a: number, k: MintKeys) { - return BlindingData.createSingleP2PKData(hexPk, a, k.id); + return OutputData.createSingleP2PKData(hexPk, a, k.id); } const mint = new CashuMint(mintUrl); // We then pass out factory to the CashuWallet constructor @@ -534,15 +534,15 @@ describe('Custom Outputs', () => { // Just to receive them and lock them again, but this time overwriting the default factory const newProofs = await wallet.receive( { proofs: unlockedProofs.send, mint: mintUrl }, - { blindingData: (a, k) => BlindingData.createSingleP2PKData('testKey', a, k.id) } + { outputData: (a, k) => OutputData.createSingleP2PKData('testKey', a, k.id) } ); // Our factory also applies to the receive method, so we expect all received proofs to be locked expectProofsSecretToEqual(newProofs, 'testKey'); }, 15000); test('Manual Factory Mint', async () => { - function createFactory(pubkey: string): BlindingDataFactory { + function createFactory(pubkey: string): OutputDataFactory { function inner(a: number, k: MintKeys) { - return BlindingData.createSingleP2PKData(pubkey, a, k.id); + return OutputData.createSingleP2PKData(pubkey, a, k.id); } return inner; } @@ -558,9 +558,9 @@ describe('Custom Outputs', () => { expectProofsSecretToEqual(proofs, 'mintTest'); }); test('Manual Factory Send', async () => { - function createFactory(pubkey: string): BlindingDataFactory { + function createFactory(pubkey: string): OutputDataFactory { function inner(a: number, k: MintKeys) { - return BlindingData.createSingleP2PKData(pubkey, a, k.id); + return OutputData.createSingleP2PKData(pubkey, a, k.id); } return inner; } @@ -573,7 +573,7 @@ describe('Custom Outputs', () => { const proofs = await wallet.mintProofs(21, quote.quote); const amount = sumProofs(proofs) - wallet.getFeesForProofs(proofs); const { send, keep } = await wallet.send(amount, proofs, { - blindingData: { send: createFactory('send'), keep: createFactory('keep') } + outputData: { send: createFactory('send'), keep: createFactory('keep') } }); expectProofsSecretToEqual(send, 'send'); expectProofsSecretToEqual(keep, 'keep'); @@ -586,10 +586,10 @@ describe('Custom Outputs', () => { const quote = await wallet.createMintQuote(40); await new Promise((res) => setTimeout(res, 1000)); const proofs = await wallet.mintProofs(40, quote.quote); - const data1 = BlindingData.createP2PKData({ pubkey: 'key1' }, 10, keys); - const data2 = BlindingData.createP2PKData({ pubkey: 'key2' }, 10, keys); + const data1 = OutputData.createP2PKData({ pubkey: 'key1' }, 10, keys); + const data2 = OutputData.createP2PKData({ pubkey: 'key2' }, 10, keys); const { keep, send } = await wallet.send(20, proofs, { - blindingData: { send: [...data1, ...data2] } + outputData: { send: [...data1, ...data2] } }); const key1Sends = send.slice(0, data1.length); const key2Sends = send.slice(data1.length); diff --git a/test/wallet.test.ts b/test/wallet.test.ts index 350ff34f..3518ff4e 100644 --- a/test/wallet.test.ts +++ b/test/wallet.test.ts @@ -15,7 +15,7 @@ import { getDecodedToken } from '../src/utils.js'; import { Server, WebSocket } from 'mock-socket'; import { injectWebSocketImpl } from '../src/ws.js'; import { MintInfo } from '../src/model/MintInfo.js'; -import { BlindingData } from '../src/model/BlindingData.js'; +import { OutputData } from '../src/model/OutputData.js'; injectWebSocketImpl(WebSocket); @@ -741,7 +741,7 @@ describe('P2PK BlindingData', () => { test('Create BlindingData locked to pk with locktime and single refund key', async () => { const wallet = new CashuWallet(mint); const keys = await wallet.getKeys(); - const data = BlindingData.createP2PKData( + const data = OutputData.createP2PKData( { pubkey: 'thisisatest', locktime: 212, refundKeys: ['iamarefund'] }, 21, keys @@ -758,7 +758,7 @@ describe('P2PK BlindingData', () => { test('Create BlindingData locked to pk with locktime and multiple refund keys', async () => { const wallet = new CashuWallet(mint); const keys = await wallet.getKeys(); - const data = BlindingData.createP2PKData( + const data = OutputData.createP2PKData( { pubkey: 'thisisatest', locktime: 212, refundKeys: ['iamarefund', 'asecondrefund'] }, 21, keys @@ -775,7 +775,7 @@ describe('P2PK BlindingData', () => { test('Create BlindingData locked to pk without locktime and no refund keys', async () => { const wallet = new CashuWallet(mint); const keys = await wallet.getKeys(); - const data = BlindingData.createP2PKData({ pubkey: 'thisisatest' }, 21, keys); + const data = OutputData.createP2PKData({ pubkey: 'thisisatest' }, 21, keys); const decoder = new TextDecoder(); const allSecrets = data.map((d) => JSON.parse(decoder.decode(d.secret))); allSecrets.forEach((s) => {