Files
xo-stack/packages/primitives/tests/mnemonic.spec.ts

357 lines
9.0 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { Bytes } from '../src/bytes';
import { Mnemonic, MnemonicRaw } from '../src/mnemonic';
describe('Success cases', () => {
// Simple array of cases
const testCases: Array<{
fromPhrase: string;
fromWords: Array<string>;
passphrase?: string;
raw: MnemonicRaw;
toEntropy: Uint8Array;
toSeed: Uint8Array;
toPhrase: string;
toPhraseWithoutPassphrase: string;
toWords: Array<string>;
toWordsWithoutPassphrase: Array<string>;
}> = [
// No pass-phrase
{
fromPhrase:
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
fromWords: [
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'about',
],
raw: {
words: [
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'about',
],
},
toEntropy: Bytes.fromHex(
'00000000000000000000000000000000',
).toUint8Array(),
toSeed: Bytes.fromHex(
'5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4',
).toUint8Array(),
toPhrase:
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
toPhraseWithoutPassphrase:
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
toWords: [
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'about',
],
toWordsWithoutPassphrase: [
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'about',
],
},
// Passphrase
{
fromPhrase:
'legal winner thank year wave sausage worth useful legal winner thank yellow Libauth',
fromWords: [
'legal',
'winner',
'thank',
'year',
'wave',
'sausage',
'worth',
'useful',
'legal',
'winner',
'thank',
'yellow',
'Libauth',
],
passphrase: 'Libauth',
raw: {
words: [
'legal',
'winner',
'thank',
'year',
'wave',
'sausage',
'worth',
'useful',
'legal',
'winner',
'thank',
'yellow',
],
passphrase: 'Libauth',
},
toEntropy: Bytes.fromHex(
'7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f',
).toUint8Array(),
toSeed: Bytes.fromHex(
'0199495e71f9e7f40b52febf82dcb1a6c342e9178a5e8cfea4204cf6b2071e89345ab83e8a5f0ea3ae59025e5e2e8dd2c965752541912f28ead8a33ff7dd11dd',
).toUint8Array(),
toPhrase:
'legal winner thank year wave sausage worth useful legal winner thank yellow Libauth',
toPhraseWithoutPassphrase:
'legal winner thank year wave sausage worth useful legal winner thank yellow',
toWords: [
'legal',
'winner',
'thank',
'year',
'wave',
'sausage',
'worth',
'useful',
'legal',
'winner',
'thank',
'yellow',
'Libauth',
],
toWordsWithoutPassphrase: [
'legal',
'winner',
'thank',
'year',
'wave',
'sausage',
'worth',
'useful',
'legal',
'winner',
'thank',
'yellow',
],
},
];
it.each(testCases)('from(string)', ({ fromPhrase: phrase, raw }) => {
const mnemonic = Mnemonic.from(phrase);
expect(mnemonic.toRaw()).toStrictEqual(raw);
});
it.each(testCases)('from(words)', ({ fromWords, raw }) => {
const mnemonic = Mnemonic.from(fromWords);
expect(mnemonic.toRaw()).toStrictEqual(raw);
});
it.each(testCases)('fromPhrase', ({ fromPhrase, raw }) => {
const mnemonic = Mnemonic.fromPhrase(fromPhrase);
expect(mnemonic.toRaw()).toStrictEqual(raw);
});
it.each(testCases)('fromWords', ({ fromWords, raw }) => {
const mnemonic = Mnemonic.fromWords(fromWords);
expect(mnemonic.toRaw()).toStrictEqual(raw);
});
it.each(testCases)('fromWords', ({ fromWords, raw }) => {
const mnemonic = Mnemonic.fromWords(fromWords);
expect(mnemonic.toRaw()).toStrictEqual(raw);
});
it.each(testCases)('toSeed', ({ toSeed, raw }) => {
const mnemonic = Mnemonic.fromRaw(raw);
expect(mnemonic.toSeed()).toStrictEqual(toSeed);
});
it.each(testCases)('toPhrase', ({ toPhrase, raw }) => {
const mnemonic = Mnemonic.fromRaw(raw);
expect(mnemonic.toPhrase()).toStrictEqual(toPhrase);
});
it.each(testCases)(
'toPhrase(withoutPassphrase)',
({ toPhraseWithoutPassphrase, raw }) => {
const mnemonic = Mnemonic.fromRaw(raw);
expect(mnemonic.toPhrase(false)).toStrictEqual(toPhraseWithoutPassphrase);
},
);
it.each(testCases)('toWords', ({ toWords, raw }) => {
const mnemonic = Mnemonic.fromRaw(raw);
expect(mnemonic.toWords()).toStrictEqual(toWords);
});
it.each(testCases)(
'toWords(withoutPassphrase)',
({ toWordsWithoutPassphrase, _passphrase, raw }) => {
const mnemonic = Mnemonic.fromRaw(raw);
expect(mnemonic.toWords(false)).toStrictEqual(toWordsWithoutPassphrase);
},
);
it.each(testCases)('getPassphrase', ({ passphrase, raw }) => {
const mnemonic = Mnemonic.fromRaw(raw);
expect(mnemonic.getPassphrase()).toStrictEqual(passphrase);
});
});
describe('Generate Cases', () => {
it('generateRandom should return instance of Mnemonic', () => {
const mnemonic = Mnemonic.generateRandom();
expect(mnemonic).toBeInstanceOf(Mnemonic);
});
it('generateRandom should return instance of Mnemonic with passphrase', () => {
const mnemonic = Mnemonic.generateRandom('GeneralProtocols!');
expect(mnemonic).toBeInstanceOf(Mnemonic);
expect(mnemonic.getPassphrase()).toBe('GeneralProtocols!');
});
});
describe('Validations', () => {
it('isValid should succeed if valid mnemonic', () => {
const isValid = Mnemonic.isValid(
'goose just fan kidney barrel album wave crowd movie flame cousin exhaust',
);
expect(isValid).toBe(true);
});
it('isValid should fail if invalid mnemonic', () => {
// 11 words.
const isValid = Mnemonic.isValid(
'goose just fan kidney barrel album wave crowd movie flame cousin',
);
expect(isValid).toBe(false);
});
it('isValidWord should succeed if word is in word list', () => {
const isValid = Mnemonic.isValidWord('goose');
expect(isValid).toBe(true);
});
it('isValidWord should fail if word is not in word list', () => {
const isValid = Mnemonic.isValidWord('fnord');
expect(isValid).toBe(false);
});
});
describe('Fail cases', () => {
it('fromPhrase should fail if invalid number of words', () => {
expect(() => {
// 11 words.
Mnemonic.fromPhrase(
'goose just fan kidney barrel album wave crowd movie flame cousin',
);
}).toThrow();
});
it('generateRandom should fail if invalid wordList contains invalid number of words', () => {
expect(() => {
// 11 words.
Mnemonic.generateRandom('GeneralProtocols!', ['one', 'two', 'three']);
}).toThrow();
});
it('fromWords should fail if invalid number of words', () => {
expect(() => {
// 11 words.
Mnemonic.fromWords([
'goose',
'just',
'fan',
'kidney',
'barrel',
'album',
'wave',
'crowd',
'movie',
'flame',
'cousin',
]);
}).toThrow();
});
it('fromPhrase should fail if word not in dictionary', () => {
expect(() => {
// Invalid word "fnord".
Mnemonic.fromPhrase(
'fnord just fan kidney barrel album wave crowd movie flame cousin exhaust',
);
}).toThrow();
});
it('fromWord should fail if word not in dictionary', () => {
expect(() => {
// Invalid word "fnord".
Mnemonic.fromWords([
'fnord',
'just',
'fan',
'kidney',
'barrel',
'album',
'wave',
'crowd',
'movie',
'flame',
'cousin',
'exhaust',
]);
}).toThrow();
});
it('fromWord should fail if checksum is invalid', () => {
expect(() => {
// Invalid word "fnord".
Mnemonic.fromWords([
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'abandon',
'banana',
]);
}).toThrow();
});
});