package bitcoin import ( "testing" "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" "github.com/tyler-smith/go-bip39" ) const ( testMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" ) func TestDeriveAddresses(t *testing.T) { seed := bip39.NewSeed(testMnemonic, "") addresses, err := DeriveAddresses(seed, 5) if err != nil { t.Fatalf("DeriveAddresses failed: %v", err) } // We expect 5 addresses per path * 3 paths = 15 addresses expectedCount := 5 * len(StandardPaths) if len(addresses) != expectedCount { t.Errorf("Expected %d addresses, got %d", expectedCount, len(addresses)) } // Test known addresses for the test mnemonic expectedAddresses := map[string]string{ "m/44'/0'/0'/0/0": "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA", "m/84'/0'/0'/0/0": "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", } addressMap := make(map[string]string) for _, addr := range addresses { addressMap[addr.Path] = addr.Address } for path, expectedAddr := range expectedAddresses { if actualAddr, exists := addressMap[path]; !exists { t.Errorf("Address for path %s not found", path) } else if actualAddr != expectedAddr { t.Errorf("Address mismatch for path %s: expected %s, got %s", path, expectedAddr, actualAddr) } } } func TestDerivePathAddresses(t *testing.T) { seed := bip39.NewSeed(testMnemonic, "") masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) if err != nil { t.Fatalf("Failed to create master key: %v", err) } tests := []struct { path DerivationPath addressIndex int expectedAddress string }{ { path: StandardPaths[0], // BIP44 addressIndex: 0, expectedAddress: "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA", }, { path: StandardPaths[1], // BIP49 addressIndex: 0, expectedAddress: "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf", }, { path: StandardPaths[2], // BIP84 addressIndex: 0, expectedAddress: "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", }, } for _, tt := range tests { t.Run(tt.path.Name, func(t *testing.T) { addresses, err := derivePathAddresses(masterKey, tt.path, 5) if err != nil { t.Fatalf("derivePathAddresses failed: %v", err) } if len(addresses) != 5 { t.Errorf("Expected 5 addresses, got %d", len(addresses)) } if addresses[tt.addressIndex].Address != tt.expectedAddress { t.Errorf("Address mismatch: expected %s, got %s", tt.expectedAddress, addresses[tt.addressIndex].Address) } }) } } func TestDeriveAddressesWithPassphrase(t *testing.T) { seed := bip39.NewSeed(testMnemonic, "TREZOR") addresses, err := DeriveAddresses(seed, 1) if err != nil { t.Fatalf("DeriveAddresses failed: %v", err) } // With passphrase, addresses should be different if len(addresses) == 0 { t.Error("Expected addresses to be generated") } // The first BIP44 address with "TREZOR" passphrase should be different addressMap := make(map[string]string) for _, addr := range addresses { addressMap[addr.Path] = addr.Address } // Should NOT match the address without passphrase if addr, exists := addressMap["m/44'/0'/0'/0/0"]; exists { if addr == "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA" { t.Error("Address should be different with passphrase") } } }