latest versions
This commit is contained in:
209
btcphrasechecker/bitcoin/analyzer.go
Normal file
209
btcphrasechecker/bitcoin/analyzer.go
Normal file
@@ -0,0 +1,209 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user