Skip to content

Commit

Permalink
cleaned CashuAuthWallet
Browse files Browse the repository at this point in the history
  • Loading branch information
Egge21M committed Feb 4, 2025
1 parent 23cb3de commit 36edb59
Showing 1 changed file with 5 additions and 162 deletions.
167 changes: 5 additions & 162 deletions src/CashuAuthWallet.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,12 @@
import { bytesToHex, randomBytes } from '@noble/hashes/utils';
import { CashuAuthMint } from './CashuAuthMint.js';
import { BlindedMessage } from './model/BlindedMessage.js';
import {
type MintKeys,
type MintKeyset,
type Proof,
type SerializedBlindedMessage,
SerializedBlindedSignature,
GetInfoResponse,
OutputAmounts,
BlindingData,
BlindAuthMintPayload
} from './model/types/index.js';
import { bytesToNumber, splitAmount, getKeepAmounts } from './utils.js';
import { pointFromHex } from '@cashu/crypto/modules/common';
import {
blindMessage,
constructProofFromPromise,
serializeProof
} from '@cashu/crypto/modules/client';
import { deriveBlindingFactor, deriveSecret } from '@cashu/crypto/modules/client/NUT09';
import { createP2PKsecret } from '@cashu/crypto/modules/client/NUT11';
import { type Proof as NUT11Proof } from '@cashu/crypto/modules/common/index';

/**
* The default number of proofs per denomination to keep in a wallet.
*/
const DEFAULT_DENOMINATION_TARGET = 3;

/**
* The default unit for the wallet, if not specified in constructor.
*/
const DEFAULT_UNIT = 'auth';
import { OutputData } from './model/OutputData.js';

/**
* Class that represents a Cashu wallet.
Expand All @@ -41,10 +16,8 @@ class CashuAuthWallet {
private _keys: Map<string, MintKeys> = new Map();
private _keysetId: string | undefined;
private _keysets: Array<MintKeyset> = [];
private _seed: Uint8Array | undefined = undefined;
private _unit = DEFAULT_UNIT;
private _unit = 'auth';
private _mintInfo: GetInfoResponse | undefined = undefined;
private _denominationTarget = DEFAULT_DENOMINATION_TARGET;

mint: CashuAuthMint;

Expand All @@ -61,12 +34,9 @@ class CashuAuthWallet {
constructor(
mint: CashuAuthMint,
options?: {
unit?: string;
keys?: Array<MintKeys> | MintKeys;
keysets?: Array<MintKeyset>;
mintInfo?: GetInfoResponse;
bip39seed?: Uint8Array;
denominationTarget?: number;
}
) {
this.mint = mint;
Expand All @@ -77,24 +47,9 @@ class CashuAuthWallet {
keys = options?.keys;
}
if (keys) keys.forEach((key: MintKeys) => this._keys.set(key.id, key));
if (options?.unit) this._unit = options?.unit;
if (options?.keysets) this._keysets = options.keysets;
if (options?.denominationTarget) {
this._denominationTarget = options.denominationTarget;
}

if (options?.bip39seed) {
if (options.bip39seed instanceof Uint8Array) {
this._seed = options.bip39seed;
return;
}
throw new Error('bip39seed must be a valid UInt8Array');
}
}

get unit(): string {
return this._unit;
}
get keys(): Map<string, MintKeys> {
return this._keys;
}
Expand Down Expand Up @@ -224,128 +179,16 @@ class CashuAuthWallet {
clearAuthToken: string,
options?: {
keysetId?: string;
outputAmounts?: OutputAmounts;
proofsWeHave?: Array<Proof>;
counter?: number;
pubkey?: string;
}
): Promise<Array<Proof>> {
const keyset = await this.getKeys(options?.keysetId);
if (!options?.outputAmounts && options?.proofsWeHave) {
options.outputAmounts = {
keepAmounts: getKeepAmounts(
options.proofsWeHave,
amount,
keyset.keys,
this._denominationTarget
),
sendAmounts: []
};
}
const outputData = OutputData.createRandomData(amount, keyset);

const { blindedMessages, secrets, blindingFactors } = this.createRandomBlindedMessages(
amount,
keyset,
options?.outputAmounts?.keepAmounts,
options?.counter,
options?.pubkey
);
const mintPayload: BlindAuthMintPayload = {
outputs: blindedMessages
outputs: outputData.map((d) => d.blindedMessage)
};
const { signatures } = await this.mint.mint(mintPayload, clearAuthToken);
return this.constructProofs(signatures, blindingFactors, secrets, keyset);
}
/**
* 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. CashuAuthWallet 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<number>,
counter?: number,
pubkey?: string
): BlindingData & { amounts: Array<number> } {
const amounts = splitAmount(amount, keyset.keys, split);
return this.createBlindedMessages(amounts, keyset.id, counter, pubkey);
}

/**
* Creates blinded messages for a according to @param amounts
* @param amount array of amounts to create blinded messages for
* @param counter? optionally set counter to derive secret deterministically. CashuAuthWallet class must be initialized with seed phrase to take effect
* @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 pubkey? optionally locks ecash to pubkey. Will not be deterministic, even if counter is set!
* @returns blinded messages, secrets, rs, and amounts
*/
private createBlindedMessages(
amounts: Array<number>,
keysetId: string,
counter?: number,
pubkey?: string
): BlindingData & { amounts: Array<number> } {
// if we atempt to create deterministic messages without a _seed, abort.
if (counter != undefined && !this._seed) {
throw new Error(
'Cannot create deterministic messages without seed. Instantiate CashuAuthWallet with a bip39seed, or omit counter param.'
);
}
const blindedMessages: Array<SerializedBlindedMessage> = [];
const secrets: Array<Uint8Array> = [];
const blindingFactors: Array<bigint> = [];
for (let i = 0; i < amounts.length; i++) {
let deterministicR = undefined;
let secretBytes = undefined;
if (pubkey) {
secretBytes = createP2PKsecret(pubkey);
} else if (this._seed && counter != undefined) {
secretBytes = deriveSecret(this._seed, keysetId, counter + i);
deterministicR = bytesToNumber(deriveBlindingFactor(this._seed, keysetId, counter + i));
} else {
secretBytes = randomBytes(32);
}
if (!pubkey) {
const secretHex = bytesToHex(secretBytes);
secretBytes = new TextEncoder().encode(secretHex);
}
secrets.push(secretBytes);
const { B_, r } = blindMessage(secretBytes, deterministicR);
blindingFactors.push(r);
const blindedMessage = new BlindedMessage(amounts[i], B_, keysetId);
blindedMessages.push(blindedMessage.getSerializedBlindedMessage());
}
return { blindedMessages, secrets, blindingFactors, 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<SerializedBlindedSignature>,
rs: Array<bigint>,
secrets: Array<Uint8Array>,
keyset: MintKeys
): Array<Proof> {
return promises
.map((p: SerializedBlindedSignature, i: number) => {
const blindSignature = { id: p.id, amount: p.amount, C_: pointFromHex(p.C_) };
const r = rs[i];
const secret = secrets[i];
const A = pointFromHex(keyset.keys[p.amount]);
return constructProofFromPromise(blindSignature, r, secret, A);
})
.map((p: NUT11Proof) => serializeProof(p) as Proof);
return outputData.map((d, i) => d.toProof(signatures[i], keyset));
}
}

Expand Down

0 comments on commit 36edb59

Please sign in to comment.