package bip39wordlist_test import ( "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "sneak.berlin/go/x/pkg/bip39wordlist" ) // vectorRepeats is how many times "abandon" appears in the standard BIP39 // test vector for all-zero 128-bit entropy, which ends with "about". const vectorRepeats = bip39wordlist.MinWordCount - 1 // vectorMnemonic returns the standard BIP39 test vector for all-zero // 128-bit entropy. func vectorMnemonic() string { return strings.Repeat("abandon ", vectorRepeats) + "about" } // messyVectorMnemonic returns vectorMnemonic with mixed case and irregular // whitespace; it must normalize to vectorMnemonic. func messyVectorMnemonic() string { return " Abandon ABANDON\t" + strings.Repeat("abandon ", 4) + "\n" + strings.Repeat("abandon ", 5) + "About \n" } func TestValidWordCount(t *testing.T) { t.Parallel() for _, n := range []int{12, 15, 18, 21, 24} { assert.True(t, bip39wordlist.ValidWordCount(n), "%d words", n) } for _, n := range []int{0, 1, 9, 11, 13, 14, 16, 23, 25, 27, 36} { assert.False(t, bip39wordlist.ValidWordCount(n), "%d words", n) } } func TestValidateWordlistAcceptsValidMnemonics(t *testing.T) { t.Parallel() require.NoError(t, bip39wordlist.ValidateWordlist(vectorMnemonic())) require.NoError(t, bip39wordlist.ValidateWordlist(messyVectorMnemonic())) } func TestValidateWordlistRejectsWrongWordCount(t *testing.T) { t.Parallel() err := bip39wordlist.ValidateWordlist("") require.ErrorIs(t, err, bip39wordlist.ErrInvalidWordCount) err = bip39wordlist.ValidateWordlist(vectorMnemonic() + " abandon") require.ErrorIs(t, err, bip39wordlist.ErrInvalidWordCount) assert.Contains(t, err.Error(), "got 13") } func TestValidateWordlistRejectsUnknownWord(t *testing.T) { t.Parallel() words := strings.Fields(vectorMnemonic()) words[4] = "bogus" err := bip39wordlist.ValidateWordlist(strings.Join(words, " ")) require.ErrorIs(t, err, bip39wordlist.ErrUnknownWord) assert.Contains(t, err.Error(), `"bogus" at position 5`) } func TestValidateWordlistRejectsBadChecksum(t *testing.T) { t.Parallel() // Twelve times "abandon" has valid words and count but the checksum // bits in the last word are wrong. allAbandon := strings.Repeat("abandon ", bip39wordlist.MinWordCount) err := bip39wordlist.ValidateWordlist(allAbandon) require.ErrorIs(t, err, bip39wordlist.ErrInvalidChecksum) } func TestNormalizeWordlistCanonicalizes(t *testing.T) { t.Parallel() normalized, err := bip39wordlist.NormalizeWordlist(messyVectorMnemonic()) require.NoError(t, err) assert.Equal(t, vectorMnemonic(), normalized) normalized, err = bip39wordlist.NormalizeWordlist(vectorMnemonic()) require.NoError(t, err) assert.Equal(t, vectorMnemonic(), normalized) } func TestNormalizeWordlistRefusesInvalid(t *testing.T) { t.Parallel() normalized, err := bip39wordlist.NormalizeWordlist("Abandon Bogus about") require.ErrorIs(t, err, bip39wordlist.ErrInvalidWordCount) assert.Empty(t, normalized) normalized, err = bip39wordlist.NormalizeWordlist( strings.Repeat("abandon ", bip39wordlist.MinWordCount), ) require.ErrorIs(t, err, bip39wordlist.ErrInvalidChecksum) assert.Empty(t, normalized) } func TestGenerateMnemonicProducesValidMnemonics(t *testing.T) { t.Parallel() for _, wordCount := range []int{12, 15, 18, 21, 24} { mnemonic, err := bip39wordlist.GenerateMnemonic(wordCount) require.NoError(t, err) require.NoError(t, bip39wordlist.ValidateWordlist(mnemonic)) assert.Len(t, strings.Fields(mnemonic), wordCount) normalized, err := bip39wordlist.NormalizeWordlist(mnemonic) require.NoError(t, err) assert.Equal(t, mnemonic, normalized, "generated mnemonic is canonical") } } func TestGenerateMnemonicIsRandom(t *testing.T) { t.Parallel() first, err := bip39wordlist.GenerateMnemonic(bip39wordlist.MaxWordCount) require.NoError(t, err) second, err := bip39wordlist.GenerateMnemonic(bip39wordlist.MaxWordCount) require.NoError(t, err) assert.NotEqual(t, first, second) } func TestGenerateMnemonicRejectsInvalidWordCount(t *testing.T) { t.Parallel() for _, wordCount := range []int{0, 11, 13, 25} { mnemonic, err := bip39wordlist.GenerateMnemonic(wordCount) require.ErrorIs(t, err, bip39wordlist.ErrInvalidWordCount) assert.Empty(t, mnemonic) } }