@bsv/sdk
Version:
BSV Blockchain Software Development Kit
176 lines (153 loc) • 5.72 kB
text/typescript
import Mnemonic from '../Mnemonic'
import { wordList as enWordList } from '../bip-39-wordlist-en'
import Random from '../../primitives/Random'
import vectors from './Mnemonic.vectors'
import HD from '../HD'
import { toBase58Check, toHex, toArray } from '../../primitives/utils'
describe('Mnemonic', function () {
it('should initialize the class', () => {
expect(Mnemonic).toBeDefined()
expect(new Mnemonic()).toBeDefined()
})
it('should have a wordlist of length 2048', () => {
expect(enWordList.value.length).toEqual(2048)
})
it('should handle this community-derived test vector', () => {
// There was a bug in Copay and bip32jp about deriving addresses with Mnemonic
// and bip44. This confirms we are handling the situation correctly and
// derive the correct value.
//
// More information here:
// https://github.com/iancoleman/bip39/issues/58
const seed = Mnemonic.fromString(
'fruit wave dwarf banana earth journey tattoo true farm silk olive fence'
).toSeed('banana')
let bip32 = HD.fromSeed(seed)
bip32 = bip32.derive("m/44'/0'/0'/0/0")
const pkh = bip32.pubKey.toHash() as number[]
const addr = toBase58Check(pkh)
expect(addr).toEqual('17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F')
})
it('should generate a mnemonic phrase that passes the check', () => {
let mnemonic: string
// should be able to make a mnemonic with or without the default wordlist
let m = new Mnemonic().fromRandom(128)
expect(m.check()).toEqual(true)
m = new Mnemonic().fromRandom(128)
expect(m.check()).toEqual(true)
const entropy = Array(32)
entropy.fill(0)
m = new Mnemonic().fromEntropy(entropy)
expect(m.check()).toEqual(true)
mnemonic = m.mnemonic
// mnemonics with extra whitespace do not pass the check
m = new Mnemonic().fromString(mnemonic + ' ')
expect(m.check()).toEqual(false)
// mnemonics with a word replaced do not pass the check
const words = mnemonic.split(' ')
expect(words[words.length - 1]).not.toEqual('zoo')
words[words.length - 1] = 'zoo'
mnemonic = words.join(' ')
expect(new Mnemonic().fromString(mnemonic).check()).toEqual(false)
})
describe('#toBinary', () => {
it('should convert to a binary array', () => {
const m = new Mnemonic().fromRandom()
expect(m.seed).toBeDefined()
expect(m.mnemonic).toBeDefined()
const buf = m.toBinary()
expect(buf.length).toBeGreaterThan(512 / 8 + 1 + 1)
})
})
describe('#fromBinary', () => {
it('should convert from a binary array', () => {
const mA = new Mnemonic().fromRandom()
const mB = new Mnemonic().fromBinary(mA.toBinary())
expect(mA.mnemonic).toEqual(mB.mnemonic)
expect(toHex(mA.seed)).toEqual(toHex(mB.seed))
})
})
describe('#fromRandom', () => {
it('should throw an error if bits is too low', () => {
expect(() => {
new Mnemonic().fromRandom(127)
}).toThrow('bits must be multiple of 32')
})
it('should throw an error if bits is not a multiple of 32', () => {
expect(() => {
new Mnemonic().fromRandom(256 - 1)
}).toThrow('bits must be multiple of 32')
})
})
describe('@fromRandom', () => {
it('should throw an error if bits is too low', () => {
expect(() => {
Mnemonic.fromRandom(127)
}).toThrow('bits must be multiple of 32')
})
it('should throw an error if bits is not a multiple of 32', () => {
expect(() => {
Mnemonic.fromRandom(256 - 1)
}).toThrow('bits must be multiple of 32')
})
})
describe('#fromEntropy', () => {
it('should build from entropy', () => {
const m = new Mnemonic().fromEntropy(Random(32))
expect(m).toBeDefined()
})
})
describe('@fromEntropy', () => {
it('should build from entropy', () => {
const m = Mnemonic.fromEntropy(Random(32))
expect(m).toBeDefined()
})
})
describe('#entropy2Mnemonic', () => {
it('should throw an error if you do not use enough entropy', () => {
const buf = Buffer.alloc(128 / 8 - 1)
buf.fill(0)
expect(() => {
new Mnemonic().entropy2Mnemonic([...buf])
}).toThrow('Entropy is less than 128 bits. It must be 128 bits or more.')
})
})
describe('#fromString', () => {
it('should throw an error in invalid mnemonic', () => {
expect(() => {
new Mnemonic().fromString('invalid mnemonic').toSeed()
}).toThrow(
'Mnemonic does not pass the check - was the mnemonic typed incorrectly? Are there extra spaces?'
)
})
})
describe('@isValid', () => {
it('should know this is valid', () => {
const isValid = Mnemonic.isValid(
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
'TREZOR'
)
expect(isValid).toBe(true)
})
it('should know this is invalid', () => {
const isValid = Mnemonic.isValid(
'abandonnnnnnn abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
'TREZOR'
)
expect(isValid).toEqual(false)
})
})
describe('vectors', () => {
vectors.english.forEach((vector, v) => {
it('should pass english test vector ' + v.toString(), () => {
const entropy = toArray(vector.entropy, 'hex')
const m = new Mnemonic().fromEntropy(entropy)
expect(m.toString()).toEqual(vector.mnemonic)
expect(m.check()).toEqual(true)
const seed = m.toSeed(vector.passphrase)
expect(toHex(seed)).toEqual(vector.seed)
expect(HD.fromSeed(seed).toString()).toEqual(vector.bip32_xprv)
})
})
})
})