UNPKG

happyucjs-lightwallet

Version:
435 lines (346 loc) 14.2 kB
var expect = require('chai').expect var keyStore = require('../lib/keystore') var upgrade = require('../lib/upgrade') var fixtures = require('./fixtures/keystore') var Promise = require('bluebird') var defaultHdPathString = "m/0'/0'/0'"; // Test with 100 private keys var addrprivkeyvector = require('./fixtures/addrprivkey100.json') // Test with 10000 private keys - takes about 40 seconds to run // var addrprivkeyvector = require('./fixtures/addrprivkey10000.json') var Transaction = require('happyucjs-tx'); var createVaultProm = Promise.promisify(keyStore.createVault) describe("Keystore", function() { describe("createVault constructor", function() { it('accepts a variety of options', function(done) { var fixture = fixtures.valid[0]; keyStore.createVault({ password: fixture.password, seedPhrase: fixture.mnSeed, salt: fixture.salt, hdPathString: fixture.hdPathString }, function(err, ks) { expect(ks.encSeed).to.not.equal(undefined); var decryptedPaddedSeed = keyStore._decryptString(ks.encSeed, Uint8Array.from(fixtures.valid[0].pwDerivedKey)); // Check padding expect(decryptedPaddedSeed.length).to.equal(120); expect(decryptedPaddedSeed.trim()).to.equal(fixtures.valid[0].mnSeed); done(); }); }); it('generates a random salt for key generation', function(done) { this.timeout(10000); var fixture = fixtures.valid[0]; keyStore.createVault({ password: fixture.password, seedPhrase: fixture.mnSeed, hdPathString: fixture.hdPathString }, function(err, ks) { var salt0 = ks.salt; expect(ks.salt).to.not.equal(undefined); ks.keyFromPassword(fixture.password, function(err, derivedKey) { var decryptedPaddedSeed = keyStore._decryptString(ks.encSeed, derivedKey); // Check padding expect(decryptedPaddedSeed.length).to.equal(120); expect(decryptedPaddedSeed.trim()).to.equal(fixtures.valid[0].mnSeed); keyStore.createVault({ password: fixture.password, seedPhrase: fixture.mnSeed, hdPathString: fixture.hdPathString }, function(err, ks1) { var salt1 = ks1.salt; expect(salt0).to.not.equal(salt1); done(); }) }); }); }); }); // Can't directly test the encrypt/decrypt functions // since salt and iv is used. describe("_encryptString _decryptString", function() { fixtures.valid.forEach(function (f) { it('encrypts the seed then returns same seed decrypted ' + '"' + f.mnSeed.substring(0,25) + '..."', function (done) { var encryptedString = keyStore._encryptString(f.mnSeed, Uint8Array.from(f.pwDerivedKey)) var decryptedString = keyStore._decryptString(encryptedString, Uint8Array.from(f.pwDerivedKey)) expect(decryptedString).to.equal(f.mnSeed) done(); }) }) }); describe("_encryptKey _decryptKey", function() { fixtures.valid.forEach(function (f) { it('encrypts the key then returns same key decrypted ' + '"' + f.privKeyHex.substring(0,15) + '..."', function (done) { var encryptedKey = keyStore._encryptKey(f.privKeyHex, Uint8Array.from(f.pwDerivedKey)) var decryptedKey = keyStore._decryptKey(encryptedKey, Uint8Array.from(f.pwDerivedKey)) expect(decryptedKey).to.equal(f.privKeyHex) done(); }) }) }); describe("deriveKeyFromPassword", function () { it("derives a key correctly from the password", function(done) { var derKeyProm = Promise.promisify(keyStore.deriveKeyFromPasswordAndSalt); var promArray = []; fixtures.valid.forEach(function (f) { promArray.push(derKeyProm(f.password, f.salt)); }) Promise.all(promArray).then(function(derived) { for(var i=0; i<derived.length; i++) { expect(derived[i]).to.deep.equal(Uint8Array.from(fixtures.valid[i].pwDerivedKey)) } done(); }) }) it('Checks if a derived key is correct or not', function (done) { var derKey = Uint8Array.from(fixtures.valid[0].pwDerivedKey) var derKey1 = Uint8Array.from(fixtures.valid[1].pwDerivedKey) var fixture = fixtures.valid[0] keyStore.createVault({ password: fixture.password, seedPhrase: fixture.mnSeed, salt: fixture.salt, hdPathString: fixture.hdPathString }, function (err, ks) { var isDerKeyCorrect = ks.isDerivedKeyCorrect(derKey); expect(isDerKeyCorrect).to.equal(true); var isDerKey1Correct = ks.isDerivedKeyCorrect(derKey1); expect(isDerKey1Correct).to.equal(false); done(); }) }) }) describe("_computeAddressFromPrivKey", function() { fixtures.valid.forEach(function (f) { it('generates valid address from private key ' + '"' + f.privKeyHex.substring(0,15) + '..."', function (done) { var address = '0x' + keyStore._computeAddressFromPrivKey(f.privKeyHex) expect(address).to.equal(f.address) done(); }) }) addrprivkeyvector.forEach(function (f) { it('generates valid address from private key ' + '"' + f.key.substring(0,15) + '..."', function (done) { var address = keyStore._computeAddressFromPrivKey(f.key) expect(address).to.equal(f.addr) done(); }) }) }); describe("serialize deserialize", function() { it("serializes empty keystore and returns same non-empty keystore when deserialized ", function(done) { var fixture = fixtures.valid[0] keyStore.createVault({ password: fixture.password, seedPhrase: fixture.mnSeed, salt: fixture.salt, hdPathString: fixture.hdPathString }, function (err, origKS) { var serKS = origKS.serialize() var deserKS = keyStore.deserialize(serKS) // Retains all attributes properly expect(deserKS).to.deep.equal(origKS) done(); }) }) it("serializes non-empty keystore and returns same non-empty keystore when deserialized ", function(done) { var fixture = fixtures.valid[0] keyStore.createVault({ password: fixture.password, seedPhrase: fixture.mnSeed, salt: fixture.salt, hdPathString: fixture.hdPathString }, function (err, origKS) { //Add Keys origKS.generateNewAddress(Uint8Array.from(fixtures.valid[0].pwDerivedKey), 20) var serKS = origKS.serialize() var deserKS = keyStore.deserialize(serKS) // Retains all attributes properly expect(deserKS).to.deep.equal(origKS) done(); }) }) }); describe("generateNewAddress", function() { var N = fixtures.valid.length; it("returns a new address, next in hd wallet", function(done) { this.timeout(10000); var promArray = []; fixtures.valid.forEach(function (fixture) { promArray.push(createVaultProm({ password: fixture.password, seedPhrase: fixture.mnSeed, salt: fixture.salt, hdPathString: fixture.hdPathString })) }) Promise.all(promArray).then(function(keystores) { for (var i=0; i<N; i++) { var ks = keystores[i] var numAddresses = fixtures.valid[i].hdIndex+1; ks.generateNewAddress(Uint8Array.from(fixtures.valid[i].pwDerivedKey), numAddresses); var addresses = ks.getAddresses(); var addr = addresses[addresses.length-1]; var priv = ks.exportPrivateKey(addr, Uint8Array.from(fixtures.valid[i].pwDerivedKey)); expect(addr).to.equal(fixtures.valid[i].address); expect(priv).to.equal(fixtures.valid[i].privKeyHex); } done(); }).catch(done) }); }); describe("getAddresses", function() { it("returns the object's address attribute", function(done) { var fixture = fixtures.valid[0] keyStore.createVault({ password: fixture.password, seedPhrase: fixture.mnSeed, salt: fixture.salt, hdPathString: fixture.hdPathString }, function (err, ks) { var add0x = function (addr) {return ('0x' + addr)} var pwKey = Uint8Array.from(fixture.pwDerivedKey) expect(ks.addresses.length).to.equal(0) expect(ks.getAddresses()).to.deep.equal(ks.addresses) ks.generateNewAddress(pwKey) expect(ks.addresses.length).to.equal(1) expect(ks.getAddresses()).to.deep.equal(ks.addresses.map(add0x)) ks.generateNewAddress(pwKey, 5) expect(ks.addresses.length).to.equal(6) expect(ks.getAddresses()).to.deep.equal(ks.addresses.map(add0x)) done(); }) }); }); describe("Seed functions", function() { it('returns the unencrypted seed', function(done) { var fixture = fixtures.valid[0] keyStore.createVault({ password: fixture.password, seedPhrase: fixture.mnSeed, salt: fixture.salt, hdPathString: fixture.hdPathString }, function (err, ks) { var pwKey = Uint8Array.from(fixtures.valid[0].pwDerivedKey) expect(ks.getSeed(pwKey)).to.equal(fixture.mnSeed) done(); }) }); it('checks if seed is valid', function(done) { var isValid = keyStore.isSeedValid(fixtures.valid[0].mnSeed) expect(isValid).to.equal(true); isValid = keyStore.isSeedValid(fixtures.invalid[0].mnSeed) expect(isValid).to.equal(false); done(); }); it('concatenates and hashes entropy sources', function(done) { var N = fixtures.sha256Test.length; for (var i=0; i<N; i++) { var ent0 = new Buffer(fixtures.sha256Test[i].ent0); var ent1 = new Buffer(fixtures.sha256Test[i].ent1); var outputString = keyStore._concatAndSha256(ent0, ent1).toString('hex'); expect(outputString).to.equal(fixtures.sha256Test[i].targetHash); } done(); }) }); describe("exportPrivateKey", function() { it('exports the private key corresponding to an address', function(done) { var fixture = fixtures.valid[0] keyStore.createVault({ password: fixture.password, seedPhrase: fixture.mnSeed, salt: fixture.salt, hdPathString: fixture.hdPathString }, function (err, ks) { var pw = Uint8Array.from(fixtures.valid[0].pwDerivedKey) ks.generateNewAddress(pw, 2) var addr = ks.getAddresses(); var exportedPriv0 = ks.exportPrivateKey(addr[0], pw) var exportedPriv1 = ks.exportPrivateKey(addr[1], pw) var addrFromExported0 = '0x' + keyStore._computeAddressFromPrivKey(exportedPriv0) var addrFromExported1 = '0x' + keyStore._computeAddressFromPrivKey(exportedPriv1) expect(addrFromExported0).to.equal(addr[0]) expect(addrFromExported1).to.equal(addr[1]) done(); }) }); }); describe("hooked webu-provider", function() { it('implements hasAddress() correctly', function(done) { var fixture = fixtures.valid[0] keyStore.createVault({ password: fixture.password, seedPhrase: fixture.mnSeed, salt: fixture.salt, hdPathString: fixture.hdPathString }, function (err, ks) { var pw = Uint8Array.from(fixtures.valid[0].pwDerivedKey) ks.generateNewAddress(pw, 5) var addr = ks.getAddresses(); for (var i=0; i<addr.length; i++) { ks.hasAddress(addr[i], function (err, hasAddr) { expect(hasAddr).to.equal(true) }) ks.hasAddress(addr[i], function (err, hasAddr) { expect(hasAddr).to.equal(true) }) } ks.hasAddress('abcdef0123456', function (err, hasAddr) { expect(hasAddr).to.equal(false) }) ks.hasAddress('0xabcdef0123456', function (err, hasAddr) { expect(hasAddr).to.equal(false) }) done(); }) }); it('implements signTransaction correctly', function(done) { var fixture = fixtures.valid[1] keyStore.createVault({ password: fixture.password, seedPhrase: fixture.mnSeed, salt: fixture.salt, hdPathString: fixture.hdPathString }, function (err, ks) { ks.keyFromPassword(fixture.password, function(err, pwDerivedKey) { ks.generateNewAddress(pwDerivedKey, 1) var addr = ks.getAddresses()[0] // console.log(addr); // Trivial passwordProvider ks.passwordProvider = function(callback) {callback(null, fixture.password)} var txParams = fixture.webuTxParams // console.log('txParams',txParams); ks.signTransaction(txParams, function (err, signedTx) { // console.log('err' ,err); // console.log('signedTx',signedTx.slice(2)); expect(signedTx.slice(2)).to.equal(fixture.rawSignedTx) done(); }); }) }); }); }); describe('upgrade old serialized keystore', function () { it('upgrades a keystore older than version 2', function (done) { this.timeout(10000); var oldKS = require('./fixtures/lightwallet.json') var oldSerialized = JSON.stringify(oldKS); upgrade(oldSerialized, 'test', function(err, upgradedKeystore) { var newKS = keyStore.deserialize(upgradedKeystore); expect(newKS.addresses).to.deep.equal(oldKS.addresses); done(); }) }) it('upgrades a version 2 keystore', function (done) { this.timeout(10000); var oldKS = require('./fixtures/lightwalletv2.json') var oldSerialized = JSON.stringify(oldKS); // console.log(upgrade) upgrade(oldSerialized, 'PHveKjhQ&8dwWEdhu]q6', function(err, upgradedKeystore) { var newKS = keyStore.deserialize(upgradedKeystore); expect(newKS.addresses).to.deep.equal(oldKS.ksData[newKS.hdPathString].addresses); done(); }) }) }) });