forked from JSKitty/scc-web3
-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathparsed_secret.js
More file actions
141 lines (137 loc) · 5 KB
/
parsed_secret.js
File metadata and controls
141 lines (137 loc) · 5 KB
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
140
141
import { cleanAndVerifySeedPhrase } from './wallet.js';
import { parseWIF, verifyWIF } from './encoding.js';
import { cChainParams } from './chain_params.js';
import { LegacyMasterKey, HdMasterKey } from './masterkey.js';
import { PIVXShield } from 'pivx-shield';
import { mnemonicToSeed } from 'bip39';
import { base64_to_buf, decrypt } from './aes-gcm.js';
import { isBase64 } from './misc.js';
export class ParsedSecret {
/**
* @type {import('../masterkey.js').MasterKey} masterkey - Masterkey object derived from the secret
*/
masterKey;
/**
* @type {PIVXShield} shield - Shield object associated with the secret. Only provided if the secret contains a seed
*/
shield;
seed;
constructor(masterKey, shield = null, seed = null) {
this.masterKey = masterKey;
this.shield = shield;
this.seed = seed;
}
/**
* @param {Uint8Array} seed
*/
static async createParsedSecretBySeed(seed) {
const pivxShield = await PIVXShield.create({
seed,
// hardcoded value considering the last checkpoint, this is good both for mainnet and testnet
// TODO: take the wallet creation height in input from users
blockHeight: cChainParams.current.defaultStartingShieldBlock,
coinType: cChainParams.current.BIP44_TYPE,
// TODO: Change account index once account system is made
accountIndex: 0,
loadSaplingData: false,
});
return new ParsedSecret(
new HdMasterKey({
seed,
}),
pivxShield,
seed
);
}
/**
* Parses whatever the secret is to a MasterKey
* @param {string|number[]|Uint8Array} secret
* @returns {Promise<ParsedSecret?>}
*/
static async parse(secret, password = '', advancedMode) {
const rules = [
{
test: (s) => Array.isArray(s) || s instanceof Uint8Array,
f: (s) => new ParsedSecret(new LegacyMasterKey({ pkBytes: s })),
},
{
test: (s) => isBase64(s) && s.length >= 128,
f: async (s, p) => ParsedSecret.parse(await decrypt(s, p)),
},
{
// If it's base64, and the length is 88 (64 decoded), it's likely a seed
test: (s) => isBase64(s) && s.length === 88,
f: (s) =>
ParsedSecret.createParsedSecretBySeed(base64_to_buf(s)),
},
{
test: (s) => s.startsWith('xprv'),
f: (s) => new ParsedSecret(new HdMasterKey({ xpriv: s })),
},
{
test: (s) => s.startsWith('xpub'),
f: (s) => new ParsedSecret(new HdMasterKey({ xpub: s })),
},
{
test: (s) =>
cChainParams.current.PUBKEY_PREFIX.includes(s[0]) &&
s.length === 34,
f: (s) => new ParsedSecret(new LegacyMasterKey({ address: s })),
},
{
test: (s) => verifyWIF(s),
f: (s) => ParsedSecret.parse(parseWIF(s)),
},
{
test: (s) => s.includes(' '),
f: async (s) => {
const { ok, msg, phrase } = await cleanAndVerifySeedPhrase(
s,
advancedMode
);
if (!ok) throw new Error(msg);
const seed = await mnemonicToSeed(phrase, password);
return await ParsedSecret.createParsedSecretBySeed(seed);
},
},
{
test: (s) => {
try {
const obj = JSON.parse(s);
return !!obj.mk;
} catch (_) {
return false;
}
},
f: async (s) => {
const obj = JSON.parse(s);
const mk = (await ParsedSecret.parse(obj.mk)).masterKey;
let shield;
try {
if (obj.shield)
shield = await PIVXShield.create({
extendedSpendingKey: obj.shield,
blockHeight: 4_200_000,
coinType: cChainParams.current.BIP44_TYPE,
accountIndex: 0,
loadSaplingData: false,
});
} catch (_) {}
return new ParsedSecret(mk, shield);
},
},
];
for (const rule of rules) {
let test;
try {
test = rule.test(secret, password);
} catch (e) {
test = false;
}
if (test) {
return await rule.f(secret, password);
}
}
return null;
}
}