mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
feat: allow to edit the wallet mints and relays (with tests updated)
This commit is contained in:
parent
7c1297e865
commit
feff31f094
2 changed files with 91 additions and 19 deletions
|
|
@ -35,6 +35,9 @@ Deno.test('PUT /wallet must be successful', async () => {
|
||||||
'https://houston.mint.com', // duplicate on purpose
|
'https://houston.mint.com', // duplicate on purpose
|
||||||
'https://cuiaba.mint.com',
|
'https://cuiaba.mint.com',
|
||||||
],
|
],
|
||||||
|
relays: [
|
||||||
|
'wss://manager.com/relay',
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -65,7 +68,7 @@ Deno.test('PUT /wallet must be successful', async () => {
|
||||||
'https://cuiaba.mint.com',
|
'https://cuiaba.mint.com',
|
||||||
]);
|
]);
|
||||||
assertEquals(data.relays, [
|
assertEquals(data.relays, [
|
||||||
'ws://localhost:4036/relay',
|
'wss://manager.com/relay',
|
||||||
]);
|
]);
|
||||||
assertEquals(data.balance, 0);
|
assertEquals(data.balance, 0);
|
||||||
|
|
||||||
|
|
@ -79,7 +82,7 @@ Deno.test('PUT /wallet must be successful', async () => {
|
||||||
|
|
||||||
assertEquals(nutzap_p2pk, p2pk);
|
assertEquals(nutzap_p2pk, p2pk);
|
||||||
assertEquals([nutzap_info.tags.find(([name]) => name === 'relay')?.[1]!], [
|
assertEquals([nutzap_info.tags.find(([name]) => name === 'relay')?.[1]!], [
|
||||||
'ws://localhost:4036/relay',
|
'wss://manager.com/relay',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
mock.restore();
|
mock.restore();
|
||||||
|
|
@ -111,31 +114,87 @@ Deno.test('PUT /wallet must NOT be successful: wrong request body/schema', async
|
||||||
mock.restore();
|
mock.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('PUT /wallet must NOT be successful: wallet already exists', async () => {
|
Deno.test('PUT /wallet must be successful: edit wallet', async () => {
|
||||||
const mock = stub(globalThis, 'fetch', () => {
|
const mock = stub(globalThis, 'fetch', () => {
|
||||||
return Promise.resolve(new Response());
|
return Promise.resolve(new Response());
|
||||||
});
|
});
|
||||||
|
|
||||||
await using test = await createTestRoute();
|
await using test = await createTestRoute();
|
||||||
const { route, sk, relay } = test;
|
const { route, sk, relay, signer } = test;
|
||||||
|
|
||||||
await relay.event(genEvent({ kind: 17375 }, sk));
|
const pubkey = await signer.getPublicKey();
|
||||||
|
const privkey = bytesToString('hex', generateSecretKey());
|
||||||
|
const p2pk = getPublicKey(stringToBytes('hex', privkey));
|
||||||
|
|
||||||
|
// Wallet
|
||||||
|
await relay.event(genEvent({
|
||||||
|
kind: 17375,
|
||||||
|
content: await signer.nip44.encrypt(
|
||||||
|
pubkey,
|
||||||
|
JSON.stringify([
|
||||||
|
['privkey', privkey],
|
||||||
|
['mint', 'https://mint.soul.com'],
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
}, sk));
|
||||||
|
|
||||||
|
// Nutzap information
|
||||||
|
await relay.event(genEvent({
|
||||||
|
kind: 10019,
|
||||||
|
tags: [
|
||||||
|
['pubkey', p2pk],
|
||||||
|
['mint', 'https://mint.soul.com'],
|
||||||
|
['relay', 'ws://localhost:4036/relay'],
|
||||||
|
],
|
||||||
|
}, sk));
|
||||||
|
|
||||||
const response = await route.request('/wallet', {
|
const response = await route.request('/wallet', {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'authorization': `Bearer ${nip19.nsecEncode(sk)}`,
|
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
mints: ['https://mint.heart.com'],
|
mints: [
|
||||||
|
'https://new-vampire-mint.com',
|
||||||
|
'https://new-age-mint.com',
|
||||||
|
],
|
||||||
|
relays: [
|
||||||
|
'wss://law-of-the-universe/relay',
|
||||||
|
'wss://law-of-the-universe/relay',
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const body2 = await response.json();
|
const body = await response.json();
|
||||||
|
|
||||||
assertEquals(response.status, 400);
|
const data = walletSchema.parse(body);
|
||||||
assertEquals(body2, { error: 'You already have a wallet 😏' });
|
|
||||||
|
assertEquals(response.status, 200);
|
||||||
|
|
||||||
|
assertEquals(bytesToString('hex', sk) !== privkey, true);
|
||||||
|
|
||||||
|
assertEquals(data.pubkey_p2pk, p2pk);
|
||||||
|
assertEquals(data.mints, [
|
||||||
|
'https://new-vampire-mint.com',
|
||||||
|
'https://new-age-mint.com',
|
||||||
|
]);
|
||||||
|
assertEquals(data.relays, [
|
||||||
|
'wss://law-of-the-universe/relay',
|
||||||
|
]);
|
||||||
|
assertEquals(data.balance, 0);
|
||||||
|
|
||||||
|
const [nutzap_info] = await relay.query([{ authors: [pubkey], kinds: [10019] }]);
|
||||||
|
|
||||||
|
assertExists(nutzap_info);
|
||||||
|
assertEquals(nutzap_info.kind, 10019);
|
||||||
|
assertEquals(nutzap_info.tags.length, 4);
|
||||||
|
|
||||||
|
const nutzap_p2pk = nutzap_info.tags.find(([value]) => value === 'pubkey')?.[1]!;
|
||||||
|
|
||||||
|
assertEquals(nutzap_p2pk, p2pk);
|
||||||
|
assertEquals([nutzap_info.tags.find(([name]) => name === 'relay')?.[1]!], [
|
||||||
|
'wss://law-of-the-universe/relay',
|
||||||
|
]);
|
||||||
|
|
||||||
mock.restore();
|
mock.restore();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { userMiddleware } from '@ditto/mastoapi/middleware';
|
||||||
import { DittoRoute } from '@ditto/mastoapi/router';
|
import { DittoRoute } from '@ditto/mastoapi/router';
|
||||||
import { generateSecretKey, getPublicKey } from 'nostr-tools';
|
import { generateSecretKey, getPublicKey } from 'nostr-tools';
|
||||||
import { NostrEvent, NSchema as n } from '@nostrify/nostrify';
|
import { NostrEvent, NSchema as n } from '@nostrify/nostrify';
|
||||||
import { bytesToString } from '@scure/base';
|
import { bytesToString, stringToBytes } from '@scure/base';
|
||||||
import { logi } from '@soapbox/logi';
|
import { logi } from '@soapbox/logi';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
|
@ -159,6 +159,9 @@ const createWalletSchema = z.object({
|
||||||
mints: z.array(z.string().url()).nonempty().transform((val) => {
|
mints: z.array(z.string().url()).nonempty().transform((val) => {
|
||||||
return [...new Set(val)];
|
return [...new Set(val)];
|
||||||
}),
|
}),
|
||||||
|
relays: z.array(z.string().url()).transform((val) => {
|
||||||
|
return [...new Set(val)];
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -167,7 +170,7 @@ const createWalletSchema = z.object({
|
||||||
* https://github.com/nostr-protocol/nips/blob/master/61.md#nutzap-informational-event
|
* https://github.com/nostr-protocol/nips/blob/master/61.md#nutzap-informational-event
|
||||||
*/
|
*/
|
||||||
route.put('/wallet', userMiddleware({ enc: 'nip44' }), async (c) => {
|
route.put('/wallet', userMiddleware({ enc: 'nip44' }), async (c) => {
|
||||||
const { conf, user, relay, signal } = c.var;
|
const { user, relay, signal } = c.var;
|
||||||
|
|
||||||
const pubkey = await user.signer.getPublicKey();
|
const pubkey = await user.signer.getPublicKey();
|
||||||
const body = await parseBody(c.req.raw);
|
const body = await parseBody(c.req.raw);
|
||||||
|
|
@ -177,18 +180,28 @@ route.put('/wallet', userMiddleware({ enc: 'nip44' }), async (c) => {
|
||||||
return c.json({ error: 'Bad schema', schema: result.error }, 400);
|
return c.json({ error: 'Bad schema', schema: result.error }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { mints } = result.data;
|
const { mints, relays } = result.data;
|
||||||
|
let previousPrivkey: string | undefined;
|
||||||
|
|
||||||
const [event] = await relay.query([{ authors: [pubkey], kinds: [17375] }], { signal });
|
const [event] = await relay.query([{ authors: [pubkey], kinds: [17375] }], { signal });
|
||||||
if (event) {
|
if (event) {
|
||||||
return c.json({ error: 'You already have a wallet 😏' }, 400);
|
const walletContentSchema = z.string().array().min(2).array();
|
||||||
|
|
||||||
|
const { data: walletContent, success, error } = n.json().pipe(walletContentSchema).safeParse(
|
||||||
|
await user.signer.nip44.decrypt(pubkey, event.content),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return c.json({ error: 'Your wallet is in an invalid format', schema: error }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
previousPrivkey = walletContent.find(([name]) => name === 'privkey')?.[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
const walletContentTags: string[][] = [];
|
const walletContentTags: string[][] = [];
|
||||||
|
|
||||||
const sk = generateSecretKey();
|
const privkey = previousPrivkey ?? bytesToString('hex', generateSecretKey());
|
||||||
const privkey = bytesToString('hex', sk);
|
const p2pk = getPublicKey(stringToBytes('hex', privkey));
|
||||||
const p2pk = getPublicKey(sk);
|
|
||||||
|
|
||||||
walletContentTags.push(['privkey', privkey]);
|
walletContentTags.push(['privkey', privkey]);
|
||||||
|
|
||||||
|
|
@ -209,7 +222,7 @@ route.put('/wallet', userMiddleware({ enc: 'nip44' }), async (c) => {
|
||||||
kind: 10019,
|
kind: 10019,
|
||||||
tags: [
|
tags: [
|
||||||
...mints.map((mint) => ['mint', mint, 'sat']),
|
...mints.map((mint) => ['mint', mint, 'sat']),
|
||||||
['relay', conf.relay], // TODO: add more relays once things get more stable
|
...relays.map((relay) => ['relay', relay]),
|
||||||
['pubkey', p2pk],
|
['pubkey', p2pk],
|
||||||
],
|
],
|
||||||
}, c);
|
}, c);
|
||||||
|
|
@ -218,7 +231,7 @@ route.put('/wallet', userMiddleware({ enc: 'nip44' }), async (c) => {
|
||||||
const walletEntity: Wallet = {
|
const walletEntity: Wallet = {
|
||||||
pubkey_p2pk: p2pk,
|
pubkey_p2pk: p2pk,
|
||||||
mints,
|
mints,
|
||||||
relays: [conf.relay],
|
relays,
|
||||||
balance: 0, // Newly created wallet, balance is zero.
|
balance: 0, // Newly created wallet, balance is zero.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue