2022-09-05 16:14:43 -04:00
|
|
|
|
|
|
|
import assert from "assert";
|
|
|
|
|
|
|
|
import { sha256 } from "../crypto/index.js";
|
|
|
|
import { toUtf8Bytes } from "../utils/utf8.js";
|
|
|
|
|
|
|
|
import { wordlists } from "../wordlists/wordlists.js";
|
|
|
|
|
|
|
|
import { Mnemonic } from "../index.js";
|
|
|
|
|
|
|
|
import { loadTests } from "./utils.js";
|
|
|
|
import type { TestCaseMnemonic } from "./types.js";
|
|
|
|
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
function fromHex(hex: string): string {
|
|
|
|
const data = Buffer.from(hex.substring(2), "hex");
|
|
|
|
return decoder.decode(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
function repeat(text: string, length: number): Array<string> {
|
|
|
|
const result = [ ];
|
|
|
|
while (result.length < length) { result.push(text); }
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
describe("Tests Mnemonics", function() {
|
|
|
|
const tests = loadTests<TestCaseMnemonic>("mnemonics");
|
|
|
|
|
|
|
|
function runTest(phrase: string, mnemonic: Mnemonic, test: TestCaseMnemonic): void {
|
|
|
|
assert.ok(Mnemonic.isValidMnemonic(phrase, mnemonic.wordlist), "isValidMnemonic");
|
|
|
|
if (test.locale === "en") {
|
|
|
|
assert.ok(Mnemonic.isValidMnemonic(phrase), "isValidMnemonic (default)");
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.equal(mnemonic.wordlist.locale, test.locale, "locale");
|
|
|
|
|
|
|
|
assert.equal(mnemonic.entropy, test.entropy, "entropy");
|
|
|
|
assert.equal(mnemonic.computeSeed(), test.seed, "seed");
|
|
|
|
assert.equal(sha256(toUtf8Bytes(phrase)), test.phraseHash, "phraseHash")
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const test of tests) {
|
|
|
|
const wordlist = wordlists[test.locale];
|
|
|
|
|
|
|
|
it(`computes mnemonic from phrase: ${ test.name }`, function() {
|
|
|
|
if (wordlist == null) {
|
|
|
|
this.skip();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const phrase = fromHex(test.phrase);
|
|
|
|
const password = fromHex(test.password);
|
|
|
|
const mnemonic = Mnemonic.fromPhrase(phrase, password, wordlist);
|
|
|
|
runTest(phrase, mnemonic, test);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const test of tests) {
|
|
|
|
const wordlist = wordlists[test.locale];
|
|
|
|
|
|
|
|
it(`computes mnemonic from entropy: ${ test.name }`, function() {
|
|
|
|
if (wordlist == null) {
|
|
|
|
this.skip();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const phrase = fromHex(test.phrase);
|
|
|
|
const password = fromHex(test.password);
|
|
|
|
const mnemonic = Mnemonic.fromEntropy(test.entropy, password, wordlist);
|
|
|
|
runTest(phrase, mnemonic, test);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("Tests Bad Mnemonics Fail", function() {
|
|
|
|
|
|
|
|
const badLengths = [
|
|
|
|
repeat("abandon", 9), // 9 words; too short
|
|
|
|
repeat("abandon", 16), // 16 words; not congruent to 0 mod 3
|
|
|
|
repeat("abandon", 27), // 27 words; too long
|
|
|
|
];
|
|
|
|
|
|
|
|
for (const _phrase of badLengths) {
|
|
|
|
const phrase = _phrase.join(" ");
|
2022-11-27 21:59:20 -05:00
|
|
|
it(`correctly fails on invalid mnemonic length: ${ _phrase.length }`, function() {
|
2022-09-05 16:14:43 -04:00
|
|
|
assert.ok(!Mnemonic.isValidMnemonic(phrase));
|
|
|
|
assert.throws(function() {
|
|
|
|
Mnemonic.fromPhrase(phrase);
|
|
|
|
}, function(error: any) {
|
|
|
|
return (error.code === "INVALID_ARGUMENT" &&
|
|
|
|
error.message.match(/^invalid mnemonic length/) &&
|
|
|
|
error.argument === "mnemonic" &&
|
|
|
|
error.value === "[ REDACTED ]");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-11-27 21:59:20 -05:00
|
|
|
it("correctly fails on invalid mnemonic word", function() {
|
2022-09-05 16:14:43 -04:00
|
|
|
const phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon wagmi";
|
|
|
|
assert.ok(!Mnemonic.isValidMnemonic(phrase));
|
|
|
|
assert.throws(function() {
|
|
|
|
Mnemonic.fromPhrase(phrase);
|
|
|
|
}, function(error: any) {
|
|
|
|
return (error.code === "INVALID_ARGUMENT" &&
|
|
|
|
error.message.match(/^invalid mnemonic word at index 11/) &&
|
|
|
|
error.argument === "mnemonic" &&
|
|
|
|
error.value === "[ REDACTED ]");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-11-27 21:59:20 -05:00
|
|
|
it("correctly fails on invalid mnemonic checksum", function() {
|
2022-09-05 16:14:43 -04:00
|
|
|
const phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon";
|
|
|
|
assert.ok(!Mnemonic.isValidMnemonic(phrase));
|
|
|
|
assert.throws(function() {
|
|
|
|
Mnemonic.fromPhrase(phrase);
|
|
|
|
}, function(error: any) {
|
|
|
|
return (error.code === "INVALID_ARGUMENT" &&
|
|
|
|
error.message.match(/^invalid mnemonic checksum/) &&
|
|
|
|
error.argument === "mnemonic" &&
|
|
|
|
error.value === "[ REDACTED ]");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const badEntropyLengths = [
|
|
|
|
repeat("42", 12), //12 bytes; too short
|
|
|
|
repeat("42", 15), // 16 bytes; not congruent to 0 mod 4
|
|
|
|
repeat("42", 36), // 36 bytes; too long
|
|
|
|
];
|
|
|
|
|
|
|
|
for (const _entropy of badEntropyLengths) {
|
|
|
|
const entropy = "0x" + _entropy.join("");
|
2022-11-27 21:59:20 -05:00
|
|
|
it(`correctly fails on invalid entropy length: ${ _entropy.length }`, function() {
|
2022-09-05 16:14:43 -04:00
|
|
|
assert.throws(function() {
|
|
|
|
Mnemonic.fromEntropy(entropy);
|
|
|
|
}, function(error: any) {
|
|
|
|
return (error.code === "INVALID_ARGUMENT" &&
|
|
|
|
error.message.match(/^invalid entropy size/) &&
|
|
|
|
error.argument === "entropy" &&
|
|
|
|
error.value === "[ REDACTED ]");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
})
|