status-im/status-go

View on GitHub
extkeys/mnemonic_test.go

Summary

Maintainability
A
0 mins
Test Coverage
package extkeys

import (
    "encoding/json"
    "fmt"
    "os"
    "testing"
)

type VectorsFile struct {
    Data    map[string][][6]string
    vectors []*Vector
}

type Vector struct {
    language, password, input, mnemonic, seed, xprv string
}

func TestNewMnemonic(t *testing.T) {
    m1 := NewMnemonic()
    if m1.salt != defaultSalt {
        t.Errorf("expected default salt, got: %q", m1.salt)
    }
}

func TestMnemonic_WordList(t *testing.T) {
    m := NewMnemonic()
    _, err := m.WordList(EnglishLanguage)
    if err != nil {
        t.Errorf("expected WordList to return WordList without errors, got: %s", err)
    }

    indexes := []Language{-1, Language(len(m.wordLists))}
    for _, index := range indexes {
        _, err := m.WordList(index)
        if err == nil {
            t.Errorf("expected WordList to return an error with index %d", index)
        }
    }
}

// TestMnemonicPhrase
func TestMnemonicPhrase(t *testing.T) {

    mnemonic := NewMnemonic()

    // test strength validation
    strengths := []EntropyStrength{127, 129, 257}
    for _, s := range strengths {
        _, err := mnemonic.MnemonicPhrase(s, EnglishLanguage)
        if err != ErrInvalidEntropyStrength {
            t.Errorf("Entropy strength '%d' should be invalid", s)
        }
    }

    // test mnemonic generation
    t.Log("Test mnemonic generation:")
    for _, language := range mnemonic.AvailableLanguages() {
        phrase, err := mnemonic.MnemonicPhrase(EntropyStrength128, language)
        t.Logf("Mnemonic (%s): %s", Languages[language], phrase)

        if err != nil {
            t.Errorf("Test failed: could not create seed: %s", err)
        }

        if !mnemonic.ValidMnemonic(phrase, language) {
            t.Error("Seed is not valid Mnenomic")
        }
    }

    // run against test vectors
    vectorsFile, err := LoadVectorsFile("mnemonic_vectors.json")
    if err != nil {
        t.Error(err)
    }

    t.Log("Test against pre-computed seed vectors:")
    stats := map[string]int{}
    for _, vector := range vectorsFile.vectors {
        stats[vector.language]++
        mnemonic := NewMnemonic()
        seed := mnemonic.MnemonicSeed(vector.mnemonic, vector.password)
        if fmt.Sprintf("%x", seed) != vector.seed {
            t.Errorf("Test failed (%s): incorrect seed (%x) generated (expected: %s)", vector.language, seed, vector.seed)
            return
        }
        //t.Logf("Test passed: correct seed (%x) generated (expected: %s)", seed, vector.seed)

        rootKey, err := NewMaster(seed)
        if err != nil {
            t.Error(err)
        }

        if rootKey.String() != vector.xprv {
            t.Errorf("Test failed (%s): incorrect xprv (%s) generated (expected: %s)", vector.language, rootKey, vector.xprv)
        }
    }
    for language, count := range stats {
        t.Logf("[%s]: %d tests completed", language, count)
    }
}

func LoadVectorsFile(path string) (*VectorsFile, error) {
    fp, err := os.Open(path)
    if err != nil {
        return nil, fmt.Errorf("Test failed: cannot open vectors file: %s", err)
    }

    var vectorsFile VectorsFile
    if err := json.NewDecoder(fp).Decode(&vectorsFile); err != nil {
        return nil, fmt.Errorf("Test failed: cannot parse vectors file: %s", err)
    }

    // load data into Vector structs
    for language, data := range vectorsFile.Data {
        for _, item := range data {
            vectorsFile.vectors = append(vectorsFile.vectors, &Vector{language, item[0], item[1], item[2], item[3], item[4]})
        }
    }

    return &vectorsFile, nil
}

func (v *Vector) String() string {
    return fmt.Sprintf("{password: %s, input: %s, mnemonic: %s, seed: %s, xprv: %s}",
        v.password, v.input, v.mnemonic, v.seed, v.xprv)
}