Packages: core (BIP39/32/44/49/84/86 derivation, WIF), keystore (PBKDF2 + AES-GCM), psbt, p2p (BIP157/158 SPV daemon, Dandelion++ broadcast with relay-echo verification), blockbook, rpc, electrum, net, bns, web-ui (React/Vite), mobile (Expo). ops/ holds the nginx vhost, systemd unit and push.sh deploy script for digibyte.x/web.
91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
// Recovery demo — takes a seed phrase and prints candidate addresses at
|
|
// every standard DGB derivation path across the first 5 accounts and
|
|
// first 20 addresses per account. Meant for offline verification against
|
|
// a block explorer or Ian Coleman; the actual wallet's "scan all paths"
|
|
// import flow will run the same shape of derivation and then check each
|
|
// address for balance via the P2P+BIP158 client.
|
|
|
|
import {
|
|
seedFromPhrase,
|
|
validateSeedPhrase,
|
|
rootFromSeed,
|
|
accountNode,
|
|
addressNode,
|
|
p2pkhAddress,
|
|
p2shP2wpkhAddress,
|
|
p2shP2wpkhAddressesBoth,
|
|
p2wpkhAddress,
|
|
p2trAddress,
|
|
PURPOSE_LABEL,
|
|
derivationPath,
|
|
digibyteLegacyP2SH,
|
|
type Purpose,
|
|
} from '../src/index.js';
|
|
|
|
const args = process.argv.slice(2);
|
|
if (args.length === 0) {
|
|
console.error('Usage: npm run recover -- "word1 word2 ... word12"');
|
|
console.error('');
|
|
console.error('WARNING: never paste a real seed here. Generate a throwaway seed with');
|
|
console.error('`npm run demo` and use that. Terminal history persists.');
|
|
process.exit(1);
|
|
}
|
|
|
|
const phrase = args.join(' ').trim();
|
|
const wordCount = phrase.split(/\s+/).length;
|
|
const validCounts = [12, 15, 18, 21, 24];
|
|
|
|
if (!validCounts.includes(wordCount)) {
|
|
console.error(`Got ${wordCount} words. BIP39 requires exactly one of: ${validCounts.join(', ')}.`);
|
|
console.error('');
|
|
console.error('Common cause: pasting placeholder text along with the real words.');
|
|
console.error('Quote just the seed phrase itself, with no extra words before or after.');
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!validateSeedPhrase(phrase)) {
|
|
console.error(`Got ${wordCount} words at a valid length, but the BIP39 checksum failed.`);
|
|
console.error('Check for typos or non-BIP39 wordlist words.');
|
|
process.exit(1);
|
|
}
|
|
|
|
const seed = await seedFromPhrase(phrase);
|
|
const root = rootFromSeed(seed);
|
|
|
|
const ACCOUNTS = 5;
|
|
const ADDRESSES = 20;
|
|
|
|
console.log(`\n=== Recovery Scan ===`);
|
|
console.log(`Seed: ${phrase.split(/\s+/).length}-word BIP39`);
|
|
console.log(`Accounts: 0..${ACCOUNTS - 1}`);
|
|
console.log(`Addresses: 0..${ADDRESSES - 1} per account, external chain only\n`);
|
|
|
|
function scan(
|
|
label: string,
|
|
purpose: Purpose,
|
|
derive: (n: ReturnType<typeof addressNode>) => string,
|
|
): void {
|
|
console.log(`── ${label}`);
|
|
for (let a = 0; a < ACCOUNTS; a++) {
|
|
const acct = accountNode(root, purpose, a);
|
|
for (let i = 0; i < ADDRESSES; i++) {
|
|
const key = addressNode(acct, 0, i);
|
|
console.log(` ${derivationPath(purpose, a, 0, i).padEnd(24)} ${derive(key)}`);
|
|
}
|
|
}
|
|
console.log();
|
|
}
|
|
|
|
scan(PURPOSE_LABEL[44], 44, p2pkhAddress);
|
|
scan(`${PURPOSE_LABEL[49]} (modern "S..." — DGB Core default)`, 49, p2shP2wpkhAddress);
|
|
scan(
|
|
`${PURPOSE_LABEL[49]} (legacy "3..." — Ian Coleman / older tools)`,
|
|
49,
|
|
(n) => p2shP2wpkhAddressesBoth(n).legacy,
|
|
);
|
|
scan(PURPOSE_LABEL[84], 84, p2wpkhAddress);
|
|
scan(PURPOSE_LABEL[86], 86, p2trAddress);
|
|
|
|
console.log('Note: BIP49 shown twice on purpose. Same underlying key, two');
|
|
console.log('valid DGB P2SH version bytes. Any recovery scan must check both.');
|
|
console.log(`(legacyP2SH network for reference: scriptHash=0x${digibyteLegacyP2SH.scriptHash.toString(16)})\n`);
|