package bitcoin import ( "fmt" "sort" "time" "btcphrasechecker/types" ) // Analyzer implements the ChainAnalyzer interface for Bitcoin type Analyzer struct { addressCount int } // NewAnalyzer creates a new Bitcoin analyzer func NewAnalyzer(addressCount int) *Analyzer { return &Analyzer{ addressCount: addressCount, } } // DeriveAddresses derives Bitcoin addresses from seed func (a *Analyzer) DeriveAddresses(seed []byte, count int) ([]types.AddressInfo, error) { return DeriveAddresses(seed, count) } // GetAddressInfo fetches address information func (a *Analyzer) GetAddressInfo(address string) (balance, txCount uint64, received, sent uint64, err error) { return GetAddressInfo(address) } // GetTransactions fetches transactions for an address func (a *Analyzer) GetTransactions(address string) ([]types.Transaction, error) { return GetTransactions(address) } // GetChain returns the chain type func (a *Analyzer) GetChain() types.Chain { return types.ChainBitcoin } // AnalyzeWallet performs comprehensive wallet analysis func AnalyzeWallet(seed []byte, addressCount int, verbose bool) (*types.WalletSummary, error) { summary := &types.WalletSummary{ ActiveAddresses: make([]types.AddressInfo, 0), TransactionHistory: make([]types.TransactionDetail, 0), } // Derive all addresses addresses, err := DeriveAddresses(seed, addressCount) if err != nil { return nil, err } if verbose { fmt.Println("Bitcoin Wallet Analysis") fmt.Println("======================") fmt.Println() } // Check each derivation path pathStats := make(map[string]struct { count int received uint64 sent uint64 }) for _, pathInfo := range StandardPaths { if verbose { fmt.Printf("Checking %s: %s - %s\n", pathInfo.Name, pathInfo.Path, pathInfo.Desc) fmt.Println("-----------------------------------------------------------") } pathAddresses := filterByPath(addresses, pathInfo.Path) for _, addr := range pathAddresses { balance, txCount, received, sent, err := GetAddressInfo(addr.Address) if err != nil { continue } addr.Balance = balance addr.TxCount = int(txCount) addr.Received = received addr.Sent = sent if txCount > 0 { if verbose { fmt.Printf(" %s\n", addr.Address) fmt.Printf(" Path: %s\n", addr.Path) fmt.Printf(" Balance: %.8f BTC\n", float64(balance)/100000000.0) fmt.Printf(" Received: %.8f BTC\n", float64(received)/100000000.0) fmt.Printf(" Sent: %.8f BTC\n", float64(sent)/100000000.0) fmt.Printf(" Transactions: %d\n", txCount) } summary.TotalBalance += balance summary.TotalReceived += received summary.TotalSent += sent summary.TotalTxCount += int(txCount) summary.ActiveAddresses = append(summary.ActiveAddresses, addr) // Update path stats ps := pathStats[pathInfo.Path] ps.count++ ps.received += received ps.sent += sent pathStats[pathInfo.Path] = ps } // Rate limiting time.Sleep(100 * time.Millisecond) } if verbose { fmt.Println() } } return summary, nil } // FetchTransactionHistory fetches and processes all transactions for active addresses func FetchTransactionHistory(summary *types.WalletSummary) error { var allTransactions []types.TransactionDetail receiveTxMap := make(map[string]bool) sendTxMap := make(map[string]bool) for _, addr := range summary.ActiveAddresses { if addr.TxCount == 0 { continue } txs, err := GetTransactions(addr.Address) if err != nil { continue } for _, tx := range txs { txType := "self" amount := uint64(0) if tx.Received > 0 && tx.Sent == 0 { txType = "received" amount = tx.Received receiveTxMap[tx.TxID] = true } else if tx.Sent > 0 && tx.Received == 0 { txType = "sent" amount = tx.Sent sendTxMap[tx.TxID] = true } else if tx.Received > 0 && tx.Sent > 0 { txType = "self" amount = tx.Received } detail := types.TransactionDetail{ TxID: tx.TxID, Time: tx.Time, BlockHeight: tx.BlockHeight, Confirmed: tx.Confirmed, Type: txType, Amount: amount, Address: addr.Address, } allTransactions = append(allTransactions, detail) } // Rate limiting time.Sleep(100 * time.Millisecond) } // Sort by time (oldest first) sort.Slice(allTransactions, func(i, j int) bool { return allTransactions[i].Time.Before(allTransactions[j].Time) }) // Calculate running balance balance := uint64(0) for i := range allTransactions { tx := &allTransactions[i] if tx.Type == "received" { balance += tx.Amount } else if tx.Type == "sent" { if balance >= tx.Amount { balance -= tx.Amount } } tx.Balance = balance } summary.TransactionHistory = allTransactions summary.ReceiveTxCount = len(receiveTxMap) summary.SendTxCount = len(sendTxMap) return nil } // filterByPath filters addresses by derivation path prefix func filterByPath(addresses []types.AddressInfo, pathPrefix string) []types.AddressInfo { var filtered []types.AddressInfo for _, addr := range addresses { if len(addr.Path) >= len(pathPrefix) && addr.Path[:len(pathPrefix)] == pathPrefix { filtered = append(filtered, addr) } } return filtered }