Skip to content

Commit

Permalink
raw token receive
Browse files Browse the repository at this point in the history
  • Loading branch information
Egge21M committed Jan 17, 2024
1 parent 808d1d3 commit f94fcc5
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 65 deletions.
31 changes: 23 additions & 8 deletions src/CashuWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
SerializedBlindedMessage,
SerializedBlindedSignature,
SplitPayload,
Token,
TokenEntry
} from './model/types/index.js';
import {
Expand Down Expand Up @@ -143,12 +144,20 @@ class CashuWallet {
* @param preference optional preference for splitting proofs into specific amounts
* @returns New token with newly created proofs, token entries that had errors, and newKeys if they have changed
*/
async receive(encodedToken: string, preference?: Array<AmountPreference>): Promise<ReceiveResponse> {
const { token } = cleanToken(getDecodedToken(encodedToken));
async receive(
token: string | Token,
preference?: Array<AmountPreference>
): Promise<ReceiveResponse> {
let decodedToken: Array<TokenEntry>;
if (typeof token === 'string') {
decodedToken = cleanToken(getDecodedToken(token)).token;
} else {
decodedToken = token.token;
}
const tokenEntries: Array<TokenEntry> = [];
const tokenEntriesWithError: Array<TokenEntry> = [];
let newKeys: MintKeys | undefined;
for (const tokenEntry of token) {
for (const tokenEntry of decodedToken) {
if (!tokenEntry?.proofs?.length) {
continue;
}
Expand Down Expand Up @@ -184,14 +193,17 @@ class CashuWallet {
* @param preference optional preference for splitting proofs into specific amounts.
* @returns New token entry with newly created proofs, proofs that had errors, and newKeys if they have changed
*/
async receiveTokenEntry(tokenEntry: TokenEntry, preference?: Array<AmountPreference>): Promise<ReceiveTokenEntryResponse> {
async receiveTokenEntry(
tokenEntry: TokenEntry,
preference?: Array<AmountPreference>
): Promise<ReceiveTokenEntryResponse> {
const proofsWithError: Array<Proof> = [];
const proofs: Array<Proof> = [];
let newKeys: MintKeys | undefined;
try {
const amount = tokenEntry.proofs.reduce((total, curr) => total + curr.amount, 0);
if (!preference) {
preference = getDefaultAmountPreference(amount)
preference = getDefaultAmountPreference(amount);
}
const { payload, blindedMessages } = this.createSplitPayload(
amount,
Expand Down Expand Up @@ -245,7 +257,7 @@ class CashuWallet {
proofs.forEach((proof) => {
if (amountAvailable >= amount) {
proofsToKeep.push(proof);
return
return;
}
amountAvailable = amountAvailable + proof.amount;
proofsToSend.push(proof);
Expand All @@ -256,7 +268,11 @@ class CashuWallet {
}
if (amount < amountAvailable || preference) {
const { amountKeep, amountSend } = this.splitReceive(amount, amountAvailable);
const { payload, blindedMessages } = this.createSplitPayload(amountSend, proofsToSend, preference);
const { payload, blindedMessages } = this.createSplitPayload(
amountSend,
proofsToSend,
preference
);
const { promises } = await this.mint.split(payload);
const proofs = dhke.constructProofs(
promises,
Expand All @@ -273,7 +289,6 @@ class CashuWallet {
amountKeepCounter += proof.amount;
splitProofsToKeep.push(proof);
return;

}
splitProofsToSend.push(proof);
});
Expand Down
149 changes: 92 additions & 57 deletions test/wallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { decode } from '@gandlaf21/bolt11-decode';
import nock from 'nock';
import { CashuMint } from '../src/CashuMint.js';
import { CashuWallet } from '../src/CashuWallet.js';
import { cleanToken, getDecodedToken } from '../src/utils.js';

const dummyKeysResp = { 1: '02f970b6ee058705c0dddc4313721cffb7efd3d142d96ea8e01d31c2b2ff09f181' };
const mintUrl = 'https://legend.lnbits.com/cashu/api/v1/4gr9Xcmz3XEkUNwiBiQGoC';
Expand Down Expand Up @@ -33,67 +34,101 @@ describe('test fees', () => {
describe('receive', () => {
const tokenInput =
'eyJwcm9vZnMiOlt7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifV0sIm1pbnRzIjpbeyJ1cmwiOiJodHRwczovL2xlZ2VuZC5sbmJpdHMuY29tL2Nhc2h1L2FwaS92MS80Z3I5WGNtejNYRWtVTndpQmlRR29DIiwiaWRzIjpbIi91WUIvNndXbllrVSJdfV19';
test('test receive', async () => {
nock(mintUrl)
.post('/split')
.reply(200, {
promises: [
{
id: 'z32vUtKgNCm1',
amount: 1,
C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422'
}
]
});
const wallet = new CashuWallet(mint);

const { token: t, tokensWithErrors } = await wallet.receive(tokenInput);

expect(t.token).toHaveLength(1);
expect(t.token[0].proofs).toHaveLength(1);
expect(t.token[0]).toMatchObject({
proofs: [{ amount: 1, id: 'z32vUtKgNCm1' }],
mint: 'https://legend.lnbits.com/cashu/api/v1/4gr9Xcmz3XEkUNwiBiQGoC'
test('test receive encoded token', async () => {
nock(mintUrl)
.post('/split')
.reply(200, {
promises: [
{
id: 'z32vUtKgNCm1',
amount: 1,
C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422'
}
]
});
expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true);
expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true);
expect(tokensWithErrors).toBe(undefined);
const wallet = new CashuWallet(mint);

const { token: t, tokensWithErrors } = await wallet.receive(tokenInput);

expect(t.token).toHaveLength(1);
expect(t.token[0].proofs).toHaveLength(1);
expect(t.token[0]).toMatchObject({
proofs: [{ amount: 1, id: 'z32vUtKgNCm1' }],
mint: 'https://legend.lnbits.com/cashu/api/v1/4gr9Xcmz3XEkUNwiBiQGoC'
});
test('test receive custom split', async () => {
nock(mintUrl)
.post('/split')
.reply(200, {
promises: [
{
id: 'z32vUtKgNCm1',
amount: 1,
C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422'
},
{
id: 'z32vUtKgNCm1',
amount: 1,
C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422'
},
{
id: 'z32vUtKgNCm1',
amount: 1,
C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422'
}
]
});
const wallet = new CashuWallet(mint);
const token3sat = 'eyJwcm9vZnMiOlt7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifSx7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifSx7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifV0sIm1pbnRzIjpbeyJ1cmwiOiJodHRwczovL2xlZ2VuZC5sbmJpdHMuY29tL2Nhc2h1L2FwaS92MS80Z3I5WGNtejNYRWtVTndpQmlRR29DIiwiaWRzIjpbIi91WUIvNndXbllrVSJdfV19'
const { token: t, tokensWithErrors } = await wallet.receive(token3sat, [{amount:1, count:3}]);

expect(t.token).toHaveLength(1);
expect(t.token[0].proofs).toHaveLength(3);
expect(t.token[0]).toMatchObject({
proofs: [{ amount: 1, id: 'z32vUtKgNCm1' },{ amount: 1, id: 'z32vUtKgNCm1' },{ amount: 1, id: 'z32vUtKgNCm1' }],
expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true);
expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true);
expect(tokensWithErrors).toBe(undefined);
});
test('test receive raw token', async () => {
const decodedInput = cleanToken(getDecodedToken(tokenInput));
nock(mintUrl)
.post('/split')
.reply(200, {
promises: [
{
id: 'z32vUtKgNCm1',
amount: 1,
C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422'
}
]
});
expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true);
expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true);
expect(tokensWithErrors).toBe(undefined);
const wallet = new CashuWallet(mint);

const { token: t, tokensWithErrors } = await wallet.receive(decodedInput);

expect(t.token).toHaveLength(1);
expect(t.token[0].proofs).toHaveLength(1);
expect(t.token[0]).toMatchObject({
proofs: [{ amount: 1, id: 'z32vUtKgNCm1' }],
mint: 'https://legend.lnbits.com/cashu/api/v1/4gr9Xcmz3XEkUNwiBiQGoC'
});
expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true);
expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true);
expect(tokensWithErrors).toBe(undefined);
});
test('test receive custom split', async () => {
nock(mintUrl)
.post('/split')
.reply(200, {
promises: [
{
id: 'z32vUtKgNCm1',
amount: 1,
C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422'
},
{
id: 'z32vUtKgNCm1',
amount: 1,
C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422'
},
{
id: 'z32vUtKgNCm1',
amount: 1,
C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422'
}
]
});
const wallet = new CashuWallet(mint);
const token3sat =
'eyJwcm9vZnMiOlt7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifSx7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifSx7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifV0sIm1pbnRzIjpbeyJ1cmwiOiJodHRwczovL2xlZ2VuZC5sbmJpdHMuY29tL2Nhc2h1L2FwaS92MS80Z3I5WGNtejNYRWtVTndpQmlRR29DIiwiaWRzIjpbIi91WUIvNndXbllrVSJdfV19';
const { token: t, tokensWithErrors } = await wallet.receive(token3sat, [
{ amount: 1, count: 3 }
]);

expect(t.token).toHaveLength(1);
expect(t.token[0].proofs).toHaveLength(3);
expect(t.token[0]).toMatchObject({
proofs: [
{ amount: 1, id: 'z32vUtKgNCm1' },
{ amount: 1, id: 'z32vUtKgNCm1' },
{ amount: 1, id: 'z32vUtKgNCm1' }
]
});
expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true);
expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true);
expect(tokensWithErrors).toBe(undefined);
});
test('test receive tokens already spent', async () => {
const msg = 'tokens already spent. Secret: oEpEuViVHUV2vQH81INUbq++Yv2w3u5H0LhaqXJKeR0=';
nock(mintUrl).post('/split').reply(200, { detail: msg });
Expand Down

0 comments on commit f94fcc5

Please sign in to comment.