// SPDX-License-Identifier: MIT // Litecoin.watch recovery study. Public synthetic fixtures only; NEVER FUND THEM. // npm install --ignore-scripts --save-exact @trustwallet/wallet-core@4.8.4 // node trust-recovery.cjs output.json const fs=require('node:fs'),crypto=require('node:crypto'); const {initWasm}=require('@trustwallet/wallet-core'); (async()=>{ const c=await initWasm(),coin=c.CoinType.litecoin; const words=Array(11).fill('abandon').concat('about').join(' '),rows=[],checks=[],bridges=[]; function check(id,condition,detail){if(!condition)throw Error(id);checks.push({id,passed:true,detail});} check('bip39-valid',c.Mnemonic.isValid(words),'Public 128-bit BIP39 zero-entropy fixture accepted.'); check('bip39-invalid-checksum',!c.Mnemonic.isValid(Array(12).fill('abandon').join(' ')),'Same dictionary words with the wrong checksum rejected by validation.'); function derive(w,path,kind){ const k=w.getKey(coin,path),p=k.getPublicKeySecp256k1(true); const a=kind==='p2pkh'?c.BitcoinAddress.createWithPublicKey(p,c.CoinTypeExt.p2pkhPrefix(coin)):c.SegwitAddress.createWithPublicKey(c.HRP.litecoin,p); const result={address:a.description(),publicKey:Buffer.from(p.data()).toString('hex')};a.delete();p.delete();k.delete();return result; } for(const passphrase of ['', 'recovery-lab', 'recovery-lab-typo']){ const w=c.HDWallet.createWithMnemonic(words,passphrase),restored=c.HDWallet.createWithMnemonic(words,passphrase); for(const [purpose,kind] of [[44,'p2pkh'],[84,'p2wpkh']]){ for(const [account,change,index] of [[0,0,0],[0,0,1],[0,0,19],[0,0,20],[0,1,0],[0,1,1],[1,0,0]]){ const path=`m/${purpose}'/2'/${account}'/${change}/${index}`,a=derive(w,path,kind),b=derive(restored,path,kind); if(a.address!==b.address||a.publicKey!==b.publicKey)throw Error('restore mismatch'); rows.push({passphrase,path,kind,...a,restoredAddress:b.address}); } if(passphrase==='')bridges.push({kind,purpose,accountPath:`m/${purpose}'/2'/0'`,xprv:w.getExtendedPrivateKey(purpose===44?c.Purpose.bip44:c.Purpose.bip84,coin,purpose===44?c.HDVersion.xprv:c.HDVersion.zprv),xpub:w.getExtendedPublicKey(purpose===44?c.Purpose.bip44:c.Purpose.bip84,coin,purpose===44?c.HDVersion.xpub:c.HDVersion.zpub)}); } w.delete();restored.delete(); } check('restore-42-addresses',rows.length===42&&rows.every(r=>r.address===r.restoredAddress),'42 derivations reproduce exactly after a fresh mnemonic restore; this is a fixed fixture set, not a success-rate estimate.'); const at=(pass,path)=>rows.find(r=>r.passphrase===pass&&r.path===path).address; const path="m/84'/2'/0'/0/0"; check('passphrase-changes-wallet',at('',path)!==at('recovery-lab',path),'An added BIP39 passphrase changes the derived address.'); check('passphrase-typo-valid-different',at('recovery-lab',path)!==at('recovery-lab-typo',path),'A typo is accepted as another passphrase and derives a different valid address; no password-error signal.'); check('account-changes-address',at('',path)!==at('',"m/84'/2'/1'/0/0"),'A different account produces a different address.'); check('change-branch-differs',at('',path)!==at('',"m/84'/2'/0'/1/0"),'Receive and change branches are distinct.'); check('index20-exists',!!at('',"m/84'/2'/0'/0/20"),'Index 20 derives successfully. This is not a test of an app network gap scan.'); const result={schema:1,testedAt:new Date().toISOString(),implementation:'Trust Wallet Core WebAssembly',version:require('@trustwallet/wallet-core/package.json').version,node:process.version,platform:process.platform,scope:'Offline BIP39 derivation and fresh HDWallet restoration in the engine. No Trust Wallet mobile/extension UI, chain history, balance discovery or mainnet spend test.',publicFixtureWarning:'All words and derived keys in this experiment are public synthetic test material. Never use or fund them.',mnemonic:words,checks,rows,bridges}; fs.writeFileSync(process.argv[2],JSON.stringify(result,null,2)+'\n');console.log(JSON.stringify({checks:checks.length,addresses:rows.length,version:result.version})); })().catch(e=>{console.error(e);process.exitCode=1;});