All checks were successful
check / check (push) Successful in 19s
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
118 lines
3.3 KiB
Go
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
|
|
}
|