# Proper Self-Custody Wallet Solution for RinCoin ## ❌ The Problem You're absolutely right - the wallet file backup method is NOT user-friendly for self-custody: - Not a simple 12-word phrase - Large binary files - Can't easily write down or memorize - Not compatible with standard wallet UX ## ✅ The REAL Solution: BIP39 Mnemonic with Key Derivation **We need to implement BIP39 support in the web wallet layer, NOT rely on RinCoin's RPC!** ### How It Works: ``` User's 12 Words (BIP39) ↓ (in web wallet) Master Seed (BIP32) ↓ (derive keys) Private Keys for Addresses ↓ (import to RinCoin) RinCoin Wallet (via importprivkey) ``` ### Key Insight: **The web wallet handles BIP39/BIP32 derivation, then imports the derived keys into RinCoin!** ## 🎯 Implementation Architecture ### Layer 1: Web Wallet (BIP39 Support) The browser extension/web wallet handles: - BIP39 mnemonic generation and validation - BIP32 key derivation - Address generation - Key management ### Layer 2: RinCoin Node (Key Storage Only) The RinCoin node just stores imported keys: - Receives derived private keys via `importprivkey` - Tracks balances and transactions - Signs transactions when needed ## 🔧 Technical Implementation ### 1. Backup Flow (Generate Mnemonic) ```javascript // In Web Wallet import * as bip39 from 'bip39'; import { BIP32Factory } from 'bip32'; import * as bitcoin from 'bitcoinjs-lib'; // Generate new wallet with mnemonic async function createNewWallet() { // 1. Generate BIP39 mnemonic (12 or 24 words) const mnemonic = bip39.generateMnemonic(128); // 12 words // 2. Convert to seed const seed = await bip39.mnemonicToSeed(mnemonic); // 3. Create master key const root = bip32.fromSeed(seed); // 4. Derive keys for RinCoin (using Litecoin derivation path) // m/44'/2'/0'/0/0 (Litecoin path, which RinCoin is based on) const masterPath = "m/44'/2'/0'/0"; // 5. Generate first 20 addresses const addresses = []; for (let i = 0; i < 20; i++) { const child = root.derivePath(`${masterPath}/${i}`); const privateKey = child.toWIF(); const address = deriveRinCoinAddress(child.publicKey); addresses.push({ index: i, address: address, privateKey: privateKey }); } // 6. Import keys to RinCoin node for (const addr of addresses) { await importPrivateKeyToNode(addr.privateKey, addr.address); } // 7. Return mnemonic to user (SHOW ONCE, USER MUST WRITE DOWN) return { mnemonic: mnemonic, addresses: addresses.map(a => a.address) }; } // Import single key to RinCoin async function importPrivateKeyToNode(privateKey, label) { return await rpcCall('importprivkey', [privateKey, label, false]); } ``` ### 2. Restore Flow (From Mnemonic) ```javascript // In Web Wallet async function restoreWalletFromMnemonic(mnemonic) { // 1. Validate mnemonic if (!bip39.validateMnemonic(mnemonic)) { throw new Error('Invalid mnemonic phrase'); } // 2. Convert to seed const seed = await bip39.mnemonicToSeed(mnemonic); // 3. Create master key const root = bip32.fromSeed(seed); // 4. Create new wallet on node await rpcCall('createwallet', [walletName, false, false]); // 5. Derive and import keys with gap limit const masterPath = "m/44'/2'/0'/0"; let consecutiveUnused = 0; const GAP_LIMIT = 20; for (let i = 0; consecutiveUnused < GAP_LIMIT; i++) { const child = root.derivePath(`${masterPath}/${i}`); const privateKey = child.toWIF(); const address = deriveRinCoinAddress(child.publicKey); // Import key await importPrivateKeyToNode(privateKey, `addr_${i}`); // Check if address has been used (has transactions) const hasTransactions = await checkAddressUsage(address); if (hasTransactions) { consecutiveUnused = 0; // Reset counter } else { consecutiveUnused++; } } // 6. Rescan blockchain to find all transactions await rpcCall('rescanblockchain', []); // 7. Get balance const balance = await rpcCall('getbalance', []); return { success: true, balance: balance, message: 'Wallet restored successfully!' }; } ``` ### 3. Address Derivation for RinCoin ```javascript import * as bitcoin from 'bitcoinjs-lib'; // RinCoin uses Litecoin's address format function deriveRinCoinAddress(publicKey) { // Use Litecoin network parameters (RinCoin is Litecoin-based) const rincoinNetwork = { messagePrefix: '\x19RinCoin Signed Message:\n', bech32: 'rin', // For SegWit addresses (rin1q...) bip32: { public: 0x019da462, // Litecoin's public key version private: 0x019d9cfe // Litecoin's private key version }, pubKeyHash: 0x30, // Litecoin pubkey hash (for legacy addresses) scriptHash: 0x32, // Litecoin script hash wif: 0xb0 // Litecoin WIF }; // Generate SegWit address (rin1q...) const { address } = bitcoin.payments.p2wpkh({ pubkey: publicKey, network: rincoinNetwork }); return address; } ``` ## 📱 User Experience Design ### Creating New Wallet: ``` ┌─────────────────────────────────┐ │ Create RinCoin Wallet │ ├─────────────────────────────────┤ │ │ │ [Generate New Wallet] │ │ │ │ ↓ │ │ │ │ 🔐 Your Recovery Phrase: │ │ ┌───────────────────────────┐ │ │ │ witch collapse practice │ │ │ │ feed shame open despair │ │ │ │ creek road again ice least │ │ │ └───────────────────────────┘ │ │ │ │ ⚠️ WRITE THIS DOWN! │ │ These 12 words are the ONLY │ │ way to recover your wallet. │ │ │ │ ☐ I have written down my │ │ recovery phrase │ │ │ │ [Continue] ─────────────────→ │ └─────────────────────────────────┘ ``` ### Restoring Wallet: ``` ┌─────────────────────────────────┐ │ Restore RinCoin Wallet │ ├─────────────────────────────────┤ │ │ │ Enter your 12-word recovery │ │ phrase: │ │ │ │ ┌───────────────────────────┐ │ │ │ 1. witch 7. despair │ │ │ │ 2. collapse 8. creek │ │ │ │ 3. practice 9. road │ │ │ │ 4. feed 10. again │ │ │ │ 5. shame 11. ice │ │ │ │ 6. open 12. least │ │ │ └───────────────────────────┘ │ │ │ │ [Restore Wallet] │ │ │ │ ↓ │ │ │ │ 🔄 Restoring wallet... │ │ • Deriving keys... ✓ │ │ • Importing to node... ✓ │ │ • Scanning blockchain... ⏳ │ │ │ │ ✅ Restored! │ │ Balance: 1059.00 RIN │ └─────────────────────────────────┘ ``` ## 🔐 Security Features ### 1. Mnemonic Only (No Key Storage) ```javascript // NEVER store the mnemonic permanently! // Only derive keys when needed class SecureWallet { // User enters mnemonic each session async unlockWallet(mnemonic) { this.root = await deriveRootKey(mnemonic); // Keep in memory only } // Clear on logout lockWallet() { this.root = null; // Clear all key material from memory } // Derive key on-demand async getPrivateKey(index) { if (!this.root) throw new Error('Wallet locked'); return this.root.derivePath(`m/44'/2'/0'/0/${index}`); } } ``` ### 2. Optional Encryption ```javascript // For convenience, allow encrypted storage async function saveEncryptedMnemonic(mnemonic, password) { const encrypted = await encrypt(mnemonic, password); localStorage.setItem('encrypted_wallet', encrypted); } async function loadEncryptedMnemonic(password) { const encrypted = localStorage.getItem('encrypted_wallet'); return await decrypt(encrypted, password); } ``` ## 🎯 Complete Implementation Example ```javascript // rin-web-wallet.js import * as bip39 from 'bip39'; import { BIP32Factory } from 'bip32'; import * as ecc from 'tiny-secp256k1'; import * as bitcoin from 'bitcoinjs-lib'; const bip32 = BIP32Factory(ecc); class RinWebWallet { constructor(rpcUrl, rpcUser, rpcPassword) { this.rpcUrl = rpcUrl; this.rpcUser = rpcUser; this.rpcPassword = rpcPassword; } // Create new wallet with mnemonic async createWallet(walletName) { // Generate mnemonic const mnemonic = bip39.generateMnemonic(128); // 12 words // Create wallet on node await this.rpcCall('createwallet', [walletName, false, false]); // Import initial addresses await this.importAddressesFromMnemonic(mnemonic, walletName, 20); return { mnemonic: mnemonic, message: 'WRITE DOWN THESE 12 WORDS!' }; } // Restore wallet from mnemonic async restoreWallet(mnemonic, walletName) { // Validate if (!bip39.validateMnemonic(mnemonic)) { throw new Error('Invalid mnemonic'); } // Create wallet on node await this.rpcCall('createwallet', [walletName, false, false]); // Import addresses with gap limit await this.importAddressesFromMnemonic(mnemonic, walletName, 100); // Rescan blockchain await this.rpcCall('rescanblockchain', [], walletName); const balance = await this.rpcCall('getbalance', [], walletName); return { success: true, balance: balance }; } // Internal: Import addresses from mnemonic async importAddressesFromMnemonic(mnemonic, walletName, count) { const seed = await bip39.mnemonicToSeed(mnemonic); const root = bip32.fromSeed(seed); for (let i = 0; i < count; i++) { const child = root.derivePath(`m/44'/2'/0'/0/${i}`); const privateKey = child.toWIF(); // Import to node await this.rpcCall('importprivkey', [ privateKey, `address_${i}`, false // Don't rescan yet ], walletName); } } // RPC call helper async rpcCall(method, params = [], wallet = null) { const url = wallet ? `${this.rpcUrl}/wallet/${wallet}` : this.rpcUrl; const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Basic ' + btoa(`${this.rpcUser}:${this.rpcPassword}`) }, body: JSON.stringify({ jsonrpc: '2.0', id: method, method: method, params: params }) }); const data = await response.json(); if (data.error) throw new Error(data.error.message); return data.result; } } // Usage const wallet = new RinWebWallet( 'http://localhost:9556', 'rinrpc', '745ce784d5d537fc06105a1b935b7657903cfc71a5fb3b90' ); // Create new const { mnemonic } = await wallet.createWallet('my_wallet'); console.log('SAVE THESE WORDS:', mnemonic); // Restore await wallet.restoreWallet('witch collapse practice feed shame open despair creek road again ice least', 'restored_wallet'); ``` ## ✅ Benefits of This Approach 1. **✅ Standard BIP39** - Compatible with hardware wallets, other software 2. **✅ 12-word backup** - Easy to write down, memorize, store 3. **✅ Self-custody** - User controls the mnemonic 4. **✅ Cross-platform** - Restore on any device 5. **✅ Secure** - Industry-standard cryptography 6. **✅ User-friendly** - Matches MetaMask/Trust Wallet UX 7. **✅ Works with RinCoin** - Uses `importprivkey` for each address ## 🚀 Next Steps 1. Implement BIP39/BIP32 support in web wallet 2. Test key derivation with RinCoin addresses 3. Create beautiful UI for mnemonic generation/restore 4. Add encrypted local storage option 5. Implement proper gap limit scanning 6. Add address book and transaction history ## 📚 Required Libraries ```bash npm install bip39 bip32 tiny-secp256k1 bitcoinjs-lib ``` Or for Python backend: ```bash pip install mnemonic bip32utils bitcoinlib ``` --- **YES! You CAN create a secure, self-custody RinCoin wallet with easy mnemonic restore!** The key is implementing BIP39 support in your web wallet layer, not relying on RinCoin's limited RPC support. 🎉