cashubtc / cashu-ts

A TypeScript library for building Cashu wallets
MIT License
58 stars 34 forks source link

Add `CashuWallet.checkProofsStates()` #199

Closed callebtc closed 3 weeks ago

callebtc commented 3 weeks ago

Add checkProofsStates method to CashuWallet to get the spent enums from the mint, should retire checkProofsSpent which throws most information away.

I would prefer getting rid of checkProofsSpent entirely. If you agree, please delete it or let me know.

Edit: we're getting rid of checkProofsSpent.

Egge21M commented 3 weeks ago

Can we be sure that a mint will ALWAYS return the state in the same order as it received the proofs? This is not explicitly defined in NUT07 and if the mint does actually not do that, we need to take care of this in order to avoid lost nuts.

callebtc commented 3 weeks ago

Can we be sure that a mint will ALWAYS return the state in the same order as it received the proofs? This is not explicitly defined in NUT07 and if the mint does actually not do that, we need to take care of this in order to avoid lost nuts.

Agreed, good point. Fixed in code and fixed in the spec: https://github.com/cashubtc/nuts/pull/181

Egge21M commented 3 weeks ago

Thanks for updating! What do you think about this instead?

async checkProofsStates(proofs: Array<Proof>): Promise<Array<ProofState>> {
    const enc = new TextEncoder();
    const Ys = proofs.map((p: Proof) => hashToCurve(enc.encode(p.secret)).toHex(true));
    // TODO: Replace this with a value from the info endpoint of the mint eventually
    const BATCH_SIZE = 100;
    const states: Array<ProofState> = [];
    for (let i = 0; i < Ys.length; i += BATCH_SIZE) {
        const YsSlice = Ys.slice(i, i + BATCH_SIZE);
        const { states: batchStates } = await this.mint.check({
            Ys: YsSlice
        });
        const stateMap: { [y: string]: ProofState } = {};
        batchStates.forEach((s) => {
            stateMap[s.Y] = s;
        });
        for (let j = 0; j < YsSlice.length; j++) {
            const state = stateMap[YsSlice[j]];
            if (!state) {
                throw new Error('Could not find state for proof with Y: ' + YsSlice[j]);
            }
        states.push(state);
        }
    }
    return states;
}

By introducing a map for the returned states, we can check whether a state is present in constant time. Before the worst case scenario was a quadratic + running time

callebtc commented 3 weeks ago

By introducing a map for the returned states, we can check whether a state is present in constant time. Before the worst case scenario was a quadratic + running time

sure go ahead and push and merge it