Files
keyfunc/internal/childmnemonic/childmnemonic_test.go
clawbot cc68529c5f
All checks were successful
check / check (push) Successful in 19s
The mnemonic command: child mnemonics (closes #4)
keyfunc mnemonic prints a child mnemonic derived from the main one with
BIP-85's own mnemonic application, in English, at 12, 18 or 24 words.
Its entropy is the BIP-85 entropy cut to the length the word count
needs, which is what that application asks for, so it does not go
through the generator the other key types read their bytes from. The
BIP-85 specification's own test vectors for the application are the
tests.

Two pieces the derivation package already had inside one function are
now named: the master key a mnemonic stands for, and the refusal of a
key index that has no hardened child. Both key types call them.

Model: opus-5
2026-09-07 15:43:10 +00:00

118 lines
3.3 KiB
Go

package childmnemonic_test
import (
"strings"
"testing"
"git.eeqj.de/sneak/keyfunc/internal/childmnemonic"
"git.eeqj.de/sneak/keyfunc/internal/derive"
"github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/stretchr/testify/require"
bip39 "github.com/tyler-smith/go-bip39"
)
// The lengths the tool offers, and one it does not.
const (
twelve = 12
eighteen = 18
twentyFour = 24
fifteen = 15
)
// specificationKey is the master key the BIP-85 specification gives
// every one of its test vectors for.
const specificationKey = "xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBq" +
"sx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb"
// The three English child mnemonics at key index 0 that the BIP-85
// specification gives for that master key.
const (
twelveWords = "girl mad pet galaxy egg matter matrix prison refuse " +
"sense ordinary nose"
eighteenWords = "near account window bike charge season chef number " +
"sketch tomorrow excuse sniff circle vital hockey outdoor " +
"supply token"
twentyFourWords = "puppy ocean match cereal symbol another shed " +
"magic wrap hammer bulb intact gadget divorce twin tonight " +
"reason outdoor destroy simple truth cigar social volcano"
)
// example returns the mnemonic every BIP-39 document uses to show its
// test vectors: eleven abandons and about.
func example() string {
return strings.Repeat("abandon ", 11) + "about"
}
func TestTheSpecificationTestVectors(t *testing.T) {
t.Parallel()
require.Equal(t, twelveWords, fromSpecificationKey(t, twelve))
require.Equal(t, eighteenWords, fromSpecificationKey(t, eighteen))
require.Equal(t, twentyFourWords, fromSpecificationKey(t, twentyFour))
}
func TestTheLongestChildMnemonicPassesItsOwnChecksum(t *testing.T) {
t.Parallel()
child := fromSpecificationKey(t, twentyFour)
require.Len(t, strings.Fields(child), twentyFour)
require.True(t, bip39.IsMnemonicValid(child))
}
func TestEachKeyIndexGivesItsOwnChildMnemonic(t *testing.T) {
t.Parallel()
master, err := derive.Master(example())
require.NoError(t, err)
first, err := childmnemonic.Derive(master, twelve, 0)
require.NoError(t, err)
require.True(t, bip39.IsMnemonicValid(first))
second, err := childmnemonic.Derive(master, twelve, 1)
require.NoError(t, err)
require.True(t, bip39.IsMnemonicValid(second))
require.NotEqual(t, first, second)
}
func TestALengthTheToolDoesNotOfferIsRefused(t *testing.T) {
t.Parallel()
master, err := hdkeychain.NewKeyFromString(specificationKey)
require.NoError(t, err)
_, err = childmnemonic.Derive(master, fifteen, 0)
require.ErrorIs(t, err, childmnemonic.ErrWordCount)
}
func TestAnIndexWithNoHardenedChildIsRefused(t *testing.T) {
t.Parallel()
master, err := hdkeychain.NewKeyFromString(specificationKey)
require.NoError(t, err)
_, err = childmnemonic.Derive(master, twelve, derive.MaxIndex+1)
require.ErrorIs(t, err, derive.ErrIndexTooLarge)
_, err = childmnemonic.Derive(master, twelve, derive.MaxIndex)
require.NoError(t, err)
}
// fromSpecificationKey derives the child mnemonic of that length at key
// index 0 from the master key the specification gives.
func fromSpecificationKey(t *testing.T, words uint32) string {
t.Helper()
master, err := hdkeychain.NewKeyFromString(specificationKey)
require.NoError(t, err)
child, err := childmnemonic.Derive(master, words, 0)
require.NoError(t, err)
return child
}