commit
81e72147ef
@ -0,0 +1,48 @@ |
||||
const KeyringController = require('eth-keyring-controller') |
||||
|
||||
const seedPhraseVerifier = { |
||||
|
||||
// Verifies if the seed words can restore the accounts.
|
||||
//
|
||||
// The seed words can recreate the primary keyring and the accounts belonging to it.
|
||||
// The created accounts in the primary keyring are always the same.
|
||||
// The keyring always creates the accounts in the same sequence.
|
||||
verifyAccounts (createdAccounts, seedWords) { |
||||
|
||||
return new Promise((resolve, reject) => { |
||||
|
||||
if (!createdAccounts || createdAccounts.length < 1) { |
||||
return reject(new Error('No created accounts defined.')) |
||||
} |
||||
|
||||
const keyringController = new KeyringController({}) |
||||
const Keyring = keyringController.getKeyringClassForType('HD Key Tree') |
||||
const opts = { |
||||
mnemonic: seedWords, |
||||
numberOfAccounts: createdAccounts.length, |
||||
} |
||||
|
||||
const keyring = new Keyring(opts) |
||||
keyring.getAccounts() |
||||
.then((restoredAccounts) => { |
||||
|
||||
log.debug('Created accounts: ' + JSON.stringify(createdAccounts)) |
||||
log.debug('Restored accounts: ' + JSON.stringify(restoredAccounts)) |
||||
|
||||
if (restoredAccounts.length !== createdAccounts.length) { |
||||
// this should not happen...
|
||||
return reject(new Error('Wrong number of accounts')) |
||||
} |
||||
|
||||
for (let i = 0; i < restoredAccounts.length; i++) { |
||||
if (restoredAccounts[i].toLowerCase() !== createdAccounts[i].toLowerCase()) { |
||||
return reject(new Error('Not identical accounts! Original: ' + createdAccounts[i] + ', Restored: ' + restoredAccounts[i])) |
||||
} |
||||
} |
||||
return resolve() |
||||
}) |
||||
}) |
||||
}, |
||||
} |
||||
|
||||
module.exports = seedPhraseVerifier |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,129 +1,103 @@ |
||||
const assert = require('assert') |
||||
const sinon = require('sinon') |
||||
const clone = require('clone') |
||||
const nock = require('nock') |
||||
const MetaMaskController = require('../../app/scripts/metamask-controller') |
||||
const blacklistJSON = require('../stub/blacklist') |
||||
const firstTimeState = require('../../app/scripts/first-time-state') |
||||
const BN = require('ethereumjs-util').BN |
||||
const GWEI_BN = new BN('1000000000') |
||||
|
||||
describe('MetaMaskController', function () { |
||||
const noop = () => {} |
||||
const metamaskController = new MetaMaskController({ |
||||
showUnconfirmedMessage: noop, |
||||
unlockAccountMessage: noop, |
||||
showUnapprovedTx: noop, |
||||
platform: {}, |
||||
encryptor: { |
||||
encrypt: function(password, object) { |
||||
this.object = object |
||||
return Promise.resolve() |
||||
}, |
||||
decrypt: function () { |
||||
return Promise.resolve(this.object) |
||||
} |
||||
}, |
||||
// initial state
|
||||
initState: clone(firstTimeState), |
||||
}) |
||||
let metamaskController |
||||
const sandbox = sinon.sandbox.create() |
||||
const noop = () => { } |
||||
|
||||
beforeEach(function () { |
||||
// sinon allows stubbing methods that are easily verified
|
||||
this.sinon = sinon.sandbox.create() |
||||
|
||||
nock('https://api.infura.io') |
||||
.persist() |
||||
.get('/v2/blacklist') |
||||
.reply(200, blacklistJSON) |
||||
|
||||
nock('https://api.infura.io') |
||||
.persist() |
||||
.get(/.*/) |
||||
.reply(200) |
||||
|
||||
metamaskController = new MetaMaskController({ |
||||
showUnapprovedTx: noop, |
||||
encryptor: { |
||||
encrypt: function (password, object) { |
||||
this.object = object |
||||
return Promise.resolve() |
||||
}, |
||||
decrypt: function () { |
||||
return Promise.resolve(this.object) |
||||
}, |
||||
}, |
||||
initState: clone(firstTimeState), |
||||
}) |
||||
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndKeychain') |
||||
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndRestore') |
||||
}) |
||||
|
||||
afterEach(function () { |
||||
// sinon requires cleanup otherwise it will overwrite context
|
||||
this.sinon.restore() |
||||
nock.cleanAll() |
||||
sandbox.restore() |
||||
}) |
||||
|
||||
describe('Metamask Controller', function () { |
||||
assert(metamaskController) |
||||
|
||||
beforeEach(function () { |
||||
sinon.spy(metamaskController.keyringController, 'createNewVaultAndKeychain') |
||||
sinon.spy(metamaskController.keyringController, 'createNewVaultAndRestore') |
||||
}) |
||||
|
||||
afterEach(function () { |
||||
metamaskController.keyringController.createNewVaultAndKeychain.restore() |
||||
metamaskController.keyringController.createNewVaultAndRestore.restore() |
||||
}) |
||||
|
||||
describe('#getGasPrice', function () { |
||||
it('gives the 50th percentile lowest accepted gas price from recentBlocksController', async function () { |
||||
const realRecentBlocksController = metamaskController.recentBlocksController |
||||
metamaskController.recentBlocksController = { |
||||
store: { |
||||
getState: () => { |
||||
return { |
||||
recentBlocks: [ |
||||
{ gasPrices: [ '0x3b9aca00', '0x174876e800'] }, |
||||
{ gasPrices: [ '0x3b9aca00', '0x174876e800'] }, |
||||
{ gasPrices: [ '0x174876e800', '0x174876e800' ]}, |
||||
{ gasPrices: [ '0x174876e800', '0x174876e800' ]}, |
||||
] |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
const gasPrice = metamaskController.getGasPrice() |
||||
assert.equal(gasPrice, '0x3b9aca00', 'accurately estimates 50th percentile accepted gas price') |
||||
|
||||
metamaskController.recentBlocksController = realRecentBlocksController |
||||
}) |
||||
|
||||
it('gives the 1 gwei price if no blocks have been seen.', async function () { |
||||
const realRecentBlocksController = metamaskController.recentBlocksController |
||||
metamaskController.recentBlocksController = { |
||||
store: { |
||||
getState: () => { |
||||
return { |
||||
recentBlocks: [] |
||||
} |
||||
describe('#getGasPrice', function () { |
||||
it('gives the 50th percentile lowest accepted gas price from recentBlocksController', async function () { |
||||
const realRecentBlocksController = metamaskController.recentBlocksController |
||||
metamaskController.recentBlocksController = { |
||||
store: { |
||||
getState: () => { |
||||
return { |
||||
recentBlocks: [ |
||||
{ gasPrices: [ '0x3b9aca00', '0x174876e800'] }, |
||||
{ gasPrices: [ '0x3b9aca00', '0x174876e800'] }, |
||||
{ gasPrices: [ '0x174876e800', '0x174876e800' ]}, |
||||
{ gasPrices: [ '0x174876e800', '0x174876e800' ]}, |
||||
], |
||||
} |
||||
} |
||||
} |
||||
|
||||
const gasPrice = metamaskController.getGasPrice() |
||||
assert.equal(gasPrice, '0x' + GWEI_BN.toString(16), 'defaults to 1 gwei') |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
metamaskController.recentBlocksController = realRecentBlocksController |
||||
}) |
||||
const gasPrice = metamaskController.getGasPrice() |
||||
assert.equal(gasPrice, '0x3b9aca00', 'accurately estimates 50th percentile accepted gas price') |
||||
|
||||
metamaskController.recentBlocksController = realRecentBlocksController |
||||
}) |
||||
}) |
||||
|
||||
describe('#createNewVaultAndKeychain', function () { |
||||
it('can only create new vault on keyringController once', async function () { |
||||
const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity') |
||||
|
||||
describe('#createNewVaultAndKeychain', function () { |
||||
it('can only create new vault on keyringController once', async function () { |
||||
const selectStub = sandbox.stub(metamaskController, 'selectFirstIdentity') |
||||
|
||||
const password = 'a-fake-password' |
||||
const password = 'a-fake-password' |
||||
|
||||
const first = await metamaskController.createNewVaultAndKeychain(password) |
||||
const second = await metamaskController.createNewVaultAndKeychain(password) |
||||
await metamaskController.createNewVaultAndKeychain(password) |
||||
await metamaskController.createNewVaultAndKeychain(password) |
||||
|
||||
assert(metamaskController.keyringController.createNewVaultAndKeychain.calledOnce) |
||||
assert(metamaskController.keyringController.createNewVaultAndKeychain.calledOnce) |
||||
|
||||
selectStub.reset() |
||||
}) |
||||
selectStub.reset() |
||||
}) |
||||
}) |
||||
|
||||
describe('#createNewVaultAndRestore', function () { |
||||
it('should be able to call newVaultAndRestore despite a mistake.', async function () { |
||||
|
||||
const password = 'what-what-what' |
||||
const wrongSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadiu' |
||||
const rightSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium' |
||||
await metamaskController.createNewVaultAndRestore(password, wrongSeed) |
||||
.catch((e) => { |
||||
return |
||||
}) |
||||
await metamaskController.createNewVaultAndRestore(password, rightSeed) |
||||
|
||||
describe('#createNewVaultAndRestore', function () { |
||||
it('should be able to call newVaultAndRestore despite a mistake.', async function () { |
||||
// const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity')
|
||||
|
||||
const password = 'what-what-what' |
||||
const wrongSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadiu' |
||||
const rightSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium' |
||||
const first = await metamaskController.createNewVaultAndRestore(password, wrongSeed) |
||||
.catch((e) => { |
||||
return |
||||
}) |
||||
const second = await metamaskController.createNewVaultAndRestore(password, rightSeed) |
||||
|
||||
assert(metamaskController.keyringController.createNewVaultAndRestore.calledTwice) |
||||
}) |
||||
assert(metamaskController.keyringController.createNewVaultAndRestore.calledTwice) |
||||
}) |
||||
}) |
||||
}) |
||||
|
@ -0,0 +1,133 @@ |
||||
const assert = require('assert') |
||||
const clone = require('clone') |
||||
const KeyringController = require('eth-keyring-controller') |
||||
const firstTimeState = require('../../app/scripts/first-time-state') |
||||
const seedPhraseVerifier = require('../../app/scripts/lib/seed-phrase-verifier') |
||||
const mockEncryptor = require('../lib/mock-encryptor') |
||||
|
||||
describe('SeedPhraseVerifier', function () { |
||||
|
||||
describe('verifyAccounts', function () { |
||||
|
||||
let password = 'passw0rd1' |
||||
let hdKeyTree = 'HD Key Tree' |
||||
|
||||
let keyringController |
||||
let vault |
||||
let primaryKeyring |
||||
|
||||
beforeEach(async function () { |
||||
keyringController = new KeyringController({ |
||||
initState: clone(firstTimeState), |
||||
encryptor: mockEncryptor, |
||||
}) |
||||
|
||||
assert(keyringController) |
||||
|
||||
vault = await keyringController.createNewVaultAndKeychain(password) |
||||
primaryKeyring = keyringController.getKeyringsByType(hdKeyTree)[0] |
||||
}) |
||||
|
||||
it('should be able to verify created account with seed words', async function () { |
||||
|
||||
let createdAccounts = await primaryKeyring.getAccounts() |
||||
assert.equal(createdAccounts.length, 1) |
||||
|
||||
let serialized = await primaryKeyring.serialize() |
||||
let seedWords = serialized.mnemonic |
||||
assert.notEqual(seedWords.length, 0) |
||||
|
||||
let result = await seedPhraseVerifier.verifyAccounts(createdAccounts, seedWords) |
||||
}) |
||||
|
||||
it('should be able to verify created account (upper case) with seed words', async function () { |
||||
|
||||
let createdAccounts = await primaryKeyring.getAccounts() |
||||
assert.equal(createdAccounts.length, 1) |
||||
|
||||
let upperCaseAccounts = [createdAccounts[0].toUpperCase()] |
||||
|
||||
let serialized = await primaryKeyring.serialize() |
||||
let seedWords = serialized.mnemonic |
||||
assert.notEqual(seedWords.length, 0) |
||||
|
||||
let result = await seedPhraseVerifier.verifyAccounts(upperCaseAccounts, seedWords) |
||||
}) |
||||
|
||||
it('should be able to verify created account (lower case) with seed words', async function () { |
||||
|
||||
let createdAccounts = await primaryKeyring.getAccounts() |
||||
assert.equal(createdAccounts.length, 1) |
||||
let lowerCaseAccounts = [createdAccounts[0].toLowerCase()] |
||||
|
||||
let serialized = await primaryKeyring.serialize() |
||||
let seedWords = serialized.mnemonic |
||||
assert.notEqual(seedWords.length, 0) |
||||
|
||||
let result = await seedPhraseVerifier.verifyAccounts(lowerCaseAccounts, seedWords) |
||||
}) |
||||
|
||||
it('should return error with good but different seed words', async function () { |
||||
|
||||
let createdAccounts = await primaryKeyring.getAccounts() |
||||
assert.equal(createdAccounts.length, 1) |
||||
|
||||
let serialized = await primaryKeyring.serialize() |
||||
let seedWords = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium' |
||||
|
||||
try {
|
||||
let result = await seedPhraseVerifier.verifyAccounts(createdAccounts, seedWords) |
||||
assert.fail("Should reject") |
||||
} catch (err) { |
||||
assert.ok(err.message.indexOf('Not identical accounts!') >= 0, 'Wrong error message') |
||||
} |
||||
}) |
||||
|
||||
it('should return error with undefined existing accounts', async function () { |
||||
|
||||
let createdAccounts = await primaryKeyring.getAccounts() |
||||
assert.equal(createdAccounts.length, 1) |
||||
|
||||
let serialized = await primaryKeyring.serialize() |
||||
let seedWords = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium' |
||||
|
||||
try {
|
||||
let result = await seedPhraseVerifier.verifyAccounts(undefined, seedWords) |
||||
assert.fail("Should reject") |
||||
} catch (err) { |
||||
assert.equal(err.message, 'No created accounts defined.') |
||||
} |
||||
}) |
||||
|
||||
it('should return error with empty accounts array', async function () { |
||||
|
||||
let createdAccounts = await primaryKeyring.getAccounts() |
||||
assert.equal(createdAccounts.length, 1) |
||||
|
||||
let serialized = await primaryKeyring.serialize() |
||||
let seedWords = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium' |
||||
|
||||
try {
|
||||
let result = await seedPhraseVerifier.verifyAccounts([], seedWords) |
||||
assert.fail("Should reject") |
||||
} catch (err) { |
||||
assert.equal(err.message, 'No created accounts defined.') |
||||
} |
||||
}) |
||||
|
||||
it('should be able to verify more than one created account with seed words', async function () { |
||||
|
||||
const keyState = await keyringController.addNewAccount(primaryKeyring) |
||||
const keyState2 = await keyringController.addNewAccount(primaryKeyring) |
||||
|
||||
let createdAccounts = await primaryKeyring.getAccounts() |
||||
assert.equal(createdAccounts.length, 3) |
||||
|
||||
let serialized = await primaryKeyring.serialize() |
||||
let seedWords = serialized.mnemonic |
||||
assert.notEqual(seedWords.length, 0) |
||||
|
||||
let result = await seedPhraseVerifier.verifyAccounts(createdAccounts, seedWords) |
||||
}) |
||||
}) |
||||
}) |
Loading…
Reference in new issue