1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
import { db, g, s } from "$lib/db";
import { findLastUsedIndex, parseDescriptor } from "$lib/esplora";
import { l, warn } from "$lib/logging";
import {
createBalanceAccount,
createCreditAccounts,
tbSetBalance,
tbSetPending,
tbSetCredit,
tbMultiplyForMicrosats,
} from "$lib/tb";
async function migrate(id) {
const k = id?.replace(/\s/g, "").toLowerCase();
let user = await g(`user:${k}`);
if (typeof user === "string") user = await g(`user:${user}`);
return user;
}
export const migrateAccounts = async () => {
const keys = await db.keys("account:*");
let migrated = 0;
for (const key of keys) {
try {
if (key.endsWith(":invoices")) continue;
const keyType = await db.type(key);
if (keyType !== "string") continue;
const account = await g(key);
if (!account || !account.id) continue;
if (account.pubkey || account.type === "ark" || !account.seed) continue;
if (!account.descriptors?.length) continue;
const parsed = parseDescriptor(account.descriptors[0].desc);
if (!parsed) continue;
account.pubkey = parsed.pubkey;
account.fingerprint = parsed.fingerprint;
try {
account.nextIndex = await findLastUsedIndex(
parsed.pubkey,
parsed.fingerprint,
);
} catch (e) {
warn("account migration index scan skipped", key, e.message);
account.nextIndex = account.nextIndex || 0;
}
await s(`account:${account.id}`, account);
migrated++;
} catch (e) {
warn("failed to migrate account", key, e.message);
}
}
return migrated;
};
export const migrateBalancesToTB = async () => {
const migrated = await db.get("tb:migrated");
if (migrated) return 0;
let count = 0;
// Migrate balance:* keys
for await (const k of db.scanIterator({ MATCH: "balance:*" })) {
try {
const id = k.split(":")[1];
const balance = Number.parseInt((await db.get(k)) || "0");
await createBalanceAccount(id);
if (balance > 0) await tbSetBalance(id, balance);
count++;
} catch (e) {
warn("failed to migrate balance", k, e.message);
}
}
// Migrate pending:* keys
for await (const k of db.scanIterator({ MATCH: "pending:*" })) {
try {
const id = k.split(":")[1];
const pending = Number.parseInt((await db.get(k)) || "0");
if (pending > 0) await tbSetPending(id, pending);
} catch (e) {
warn("failed to migrate pending", k, e.message);
}
}
// Migrate credit:*:* keys
for await (const k of db.scanIterator({ MATCH: "credit:*:*" })) {
try {
const parts = k.split(":");
const type = parts[1];
const uid = parts[2];
const amount = Number.parseInt((await db.get(k)) || "0");
await createCreditAccounts(uid);
if (amount > 0) await tbSetCredit(uid, type, amount);
} catch (e) {
warn("failed to migrate credit", k, e.message);
}
}
await db.set("tb:migrated", Date.now().toString());
l(`Migrated ${count} balances to TigerBeetle`);
return count;
};
export const migrateToMicrosats = async () => {
const migrated = await db.get("tb:microsats");
if (migrated) return 0;
let count = 0;
// Iterate through all users and multiply their TB balances
for await (const k of db.scanIterator({ MATCH: "user:*" })) {
try {
const raw = await db.get(k);
if (!raw) continue;
// Skip user alias keys (they just contain another key reference)
if (!raw.startsWith("{")) continue;
const user = JSON.parse(raw);
if (!user.id) continue;
const multiplied = await tbMultiplyForMicrosats(user.id);
if (multiplied > 0) count++;
} catch (e) {
warn("microsats migration error", k, e.message);
}
}
await db.set("tb:microsats", Date.now().toString());
l(`Migrated ${count} users to microsats`);
return count;
};
export default migrate;
|