-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathtest-keplr-path.cjs
More file actions
138 lines (114 loc) · 4.18 KB
/
test-keplr-path.cjs
File metadata and controls
138 lines (114 loc) · 4.18 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
const { hmac } = require('@noble/hashes/hmac');
const { sha512 } = require('@noble/hashes/sha512');
const * as bip39 = require('bip39');
const * as secp256k1 = require('@noble/secp256k1');
const { bech32 } = require('bech32');
const { sha256 } = require('@noble/hashes/sha256');
const { ripemd160 } = require('@noble/hashes/ripemd160');
// hash160 = RIPEMD160(SHA256(data))
function hash160(data) {
return ripemd160(sha256(data));
}
// Bech32 encoding helper
function convertBits(data, fromBits, toBits, pad) {
let acc = 0;
let bits = 0;
const result = [];
const maxv = (1 << toBits) - 1;
for (const value of data) {
acc = (acc << fromBits) | value;
bits += fromBits;
while (bits >= toBits) {
bits -= toBits;
result.push((acc >> bits) & maxv);
}
}
if (pad && bits > 0) {
result.push((acc << (toBits - bits)) & maxv);
}
return result;
}
// Bech32 encode
function bech32Encode(hrp, data) {
return bech32.encode(hrp, data);
}
// Parse BIP32 path
function parsePath(path) {
const parts = path.split('/').slice(1);
return parts.map(part => {
const hardened = part.endsWith("'") || part.endsWith('h');
const index = parseInt(part.replace(/['h]$/, ''), 10);
return { index, hardened };
});
}
// BIP32 derive child key
function deriveChild(parentKey, parentChainCode, index, hardened) {
const data = new Uint8Array(37);
if (hardened) {
data[0] = 0x00;
data.set(parentKey, 1);
new DataView(data.buffer).setUint32(33, index + 0x80000000, false);
} else {
const pubKey = secp256k1.getPublicKey(parentKey, true);
data.set(pubKey, 0);
new DataView(data.buffer).setUint32(33, index, false);
}
const I = hmac(sha512, parentChainCode, data);
const IL = I.slice(0, 32);
const IR = I.slice(32);
// Add parent key to derived key (mod n)
const keyBigInt = (BigInt('0x' + Buffer.from(IL).toString('hex')) +
BigInt('0x' + Buffer.from(parentKey).toString('hex'))) %
BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141');
const newKey = new Uint8Array(32);
const keyHex = keyBigInt.toString(16).padStart(64, '0');
for (let i = 0; i < 32; i++) {
newKey[i] = parseInt(keyHex.substr(i * 2, 2), 16);
}
return { key: newKey, chainCode: IR };
}
// Derive from seed with path
function deriveFromSeed(seed, path) {
const I = hmac(sha512, Buffer.from('Bitcoin seed'), seed);
let key = I.slice(0, 32);
let chainCode = I.slice(32);
const components = parsePath(path);
for (const { index, hardened } of components) {
const derived = deriveChild(key, chainCode, index, hardened);
key = derived.key;
chainCode = derived.chainCode;
}
return { privateKey: key, publicKey: secp256k1.getPublicKey(key, true) };
}
// Generate bc1 address from public key
function getBitcoinAddress(publicKey, hrp = 'bc') {
const pubKeyHash = hash160(publicKey);
const words = convertBits(pubKeyHash, 8, 5, true);
return bech32Encode(hrp, [0, ...words]);
}
async function main() {
// Replace with your test mnemonic
const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
console.log("Testing derivation paths with standard test mnemonic\n");
const seedBuffer = await bip39.mnemonicToSeed(mnemonic);
const seed = new Uint8Array(seedBuffer);
console.log("=== BIP84 Standard: Account index in third position ===");
console.log("m/84'/0'/ACCOUNT'/0/0\n");
for (let account = 0; account < 3; account++) {
const path = `m/84'/0'/${account}'/0/0`;
const { publicKey } = deriveFromSeed(seed, path);
const address = getBitcoinAddress(publicKey, 'bc');
console.log(`Account ${account}: ${path}`);
console.log(` Address: ${address}\n`);
}
console.log("=== Alternative: Address index in last position ===");
console.log("m/84'/0'/0'/0/ADDRESS\n");
for (let addrIdx = 0; addrIdx < 3; addrIdx++) {
const path = `m/84'/0'/0'/0/${addrIdx}`;
const { publicKey } = deriveFromSeed(seed, path);
const address = getBitcoinAddress(publicKey, 'bc');
console.log(`Address ${addrIdx}: ${path}`);
console.log(` Address: ${address}\n`);
}
}
main().catch(console.error);