From 9ad2a1f912813904d35d30ba9e3dddcd775296b2 Mon Sep 17 00:00:00 2001 From: Tim Coulter Date: Thu, 24 Mar 2016 11:54:15 -0700 Subject: [PATCH 1/2] Use HD wallet for account generation. --- bin/testrpc | 43 +++++++++++++++++++++++++++++++++++++++++-- lib/blockchain.js | 16 +++++++++++++++- lib/manager.js | 4 ++-- lib/server.js | 41 ----------------------------------------- package.json | 2 ++ test/requests.js | 2 +- 6 files changed, 61 insertions(+), 47 deletions(-) diff --git a/bin/testrpc b/bin/testrpc index 9ce93a8fe..8b8cea787 100755 --- a/bin/testrpc +++ b/bin/testrpc @@ -1,6 +1,7 @@ #!/usr/bin/env node var argv = require('yargs').argv; var TestRPC = require('..'); +var pkg = require("../package.json"); function parseAccounts(accounts) { function splitAccount(account) { @@ -24,10 +25,48 @@ if (argv.d || argv.deterministic) { argv.s = "TestRPC is awesome!"; } -TestRPC.startServer(console, { - port: argv.p || argv.port, +var options = { + port: argv.p || argv.port || 8545, debug: argv.d || argv.debug, seed: argv.s || argv.seed, total_accounts: argv.a || argv.accounts, accounts: parseAccounts(argv.account) +} + +var server = TestRPC.server(console, options); + +console.log("EthereumJS TestRPC v" + pkg.version); + +server.listen(options.port, function(err, blockchain) { + if (err) { + console.log(err); + return; + } + console.log(""); + console.log("Available Accounts"); + console.log("=================="); + + var accounts = blockchain.accounts; + var addresses = Object.keys(accounts); + + addresses.forEach(function(address) { + console.log(address); + }); + + console.log(""); + console.log("Private Keys"); + console.log("=================="); + + addresses.forEach(function(address) { + console.log(accounts[address].secretKey.toString("hex")); + }); + + console.log(""); + console.log("HD Wallet"); + console.log("=================="); + console.log("Mnemonic: " + blockchain.mnemonic); + console.log("Base HD Path: " + blockchain.wallet_hdpath + "{account_index}") + + console.log(""); + console.log("Listening on localhost:" + options.port); }); diff --git a/lib/blockchain.js b/lib/blockchain.js index 86a3e1718..ed5b22df1 100644 --- a/lib/blockchain.js +++ b/lib/blockchain.js @@ -6,6 +6,8 @@ var FakeTransaction = require('ethereumjs-tx/fake.js'); var utils = require('ethereumjs-util'); var seedrandom = require('seedrandom'); var random = require('./random'); +var bip39 = require('bip39') +var hdkey = require('ethereumjs-wallet/hdkey') Blockchain = function(logger, options) { this.stateTrie = new Trie(); @@ -25,6 +27,9 @@ Blockchain = function(logger, options) { this.snapshots = []; this.logger = logger || console; this.rng = seedrandom(options.seed); + this.mnemonic = this.mnemonic || bip39.entropyToMnemonic(random.randomBytes(16, this.rng)); + this.wallet = hdkey.fromMasterSeed(bip39.mnemonicToSeed(this.mnemonic)); + this.wallet_hdpath = "m/44'/60'/0'/0/"; if (options.debug == true) { this.vm.on('step', function(info){ @@ -56,7 +61,16 @@ Blockchain.prototype.toHex = function(val) { Blockchain.prototype.addAccount = function(opts, callback) { var self = this; - var secretKey = opts.secretKey || random.randomBytes(32, this.rng); + var secretKey; + + if (opts.secretKey) { + this.secretKey = opts.secretKey; + } else { + var index = Object.keys(this.accounts).length; + var account = this.wallet.derivePath(this.wallet_hdpath + index) // index is a number + secretKey = account.getWallet().getPrivateKey() // Buffer + } + var publicKey = utils.privateToPublic(new Buffer(secretKey)); var address = utils.pubToAddress(new Buffer(publicKey)); diff --git a/lib/manager.js b/lib/manager.js index 9d4c0a8e8..060a30136 100644 --- a/lib/manager.js +++ b/lib/manager.js @@ -28,7 +28,7 @@ Manager.prototype.initialize = function(callback) { }); } else { // Add 10 accounts, for testing purposes. - async.times(this.total_accounts, function(n, next) { + async.timesSeries(this.total_accounts, function(n, next) { self.blockchain.addAccount({}, next); }, function() { self.initialized = true; @@ -43,7 +43,7 @@ Manager.prototype.waitForInitialization = function(callback) { self.waitForInitialization(callback); }, 100); } else { - callback(null, this.blockchain.accounts); + callback(null, this.blockchain); } } diff --git a/lib/server.js b/lib/server.js index 6b5723168..d130ad677 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,5 +1,4 @@ var Manager = require('./manager.js'); -var pkg = require("../package.json"); var http = require("http"); var ProviderEngine = require("web3-provider-engine"); @@ -190,46 +189,6 @@ Server = { throw new Error("Synchronous requests are not supported."); } }; - }, - - startServer: function(logger, options, callback) { - var self = this; - var port = options.port; - - if (port == null) { - port = 8545; - } - - if (logger == null) { - logger = console; - } - - var server = this.server(logger, options); - - logger.log("EthereumJS TestRPC v" + pkg.version); - - server.listen(port, function(err, accounts) { - if (err) { - logger.log(err); - return; - } - logger.log(""); - logger.log("Available Accounts"); - logger.log("=================="); - - accounts = Object.keys(accounts); - - for (var i = 0; i < accounts.length; i++) { - logger.log(accounts[i]); - } - - logger.log(""); - logger.log("Listening on localhost:" + port); - - if (callback) { - callback(); - } - }); } } diff --git a/package.json b/package.json index 727e10d23..fb0bf089f 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,13 @@ "dependencies": { "async": "^1.5.0", "bignumber.js": "^2.1.4", + "bip39": "^2.2.0", "ethereumjs-account": "^2.0.2", "ethereumjs-block": "^1.2.2", "ethereumjs-tx": "^1.1.0", "ethereumjs-util": "^4.0.1", "ethereumjs-vm": "^1.2.1", + "ethereumjs-wallet": "^0.5.0", "merkle-patricia-tree": "2.1.2", "seedrandom": "^2.4.2", "shelljs": "^0.6.0", diff --git a/test/requests.js b/test/requests.js index cbdd9e692..79fba41b9 100644 --- a/test/requests.js +++ b/test/requests.js @@ -399,7 +399,7 @@ var tests = function(web3) { web3.eth.sendTransaction(tx_data, function(err, result) { if (err) { - assert.equal(err.message.indexOf("Error: could not unlock signer account"), 0); + assert.notEqual(err.message.indexOf("could not unlock signer account"), -1); done(); } else { assert.fail("Should have received an error") From 8c093a5587262e232cbeda413975134bbb45a238 Mon Sep 17 00:00:00 2001 From: Tim Coulter Date: Thu, 24 Mar 2016 11:57:07 -0700 Subject: [PATCH 2/2] Allow the -m/--mnemonic option. --- bin/testrpc | 1 + lib/blockchain.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/testrpc b/bin/testrpc index 8b8cea787..27af841f2 100755 --- a/bin/testrpc +++ b/bin/testrpc @@ -29,6 +29,7 @@ var options = { port: argv.p || argv.port || 8545, debug: argv.d || argv.debug, seed: argv.s || argv.seed, + mnemonic: argv.m || argv.mnemonic, total_accounts: argv.a || argv.accounts, accounts: parseAccounts(argv.account) } diff --git a/lib/blockchain.js b/lib/blockchain.js index ed5b22df1..ddc8f890e 100644 --- a/lib/blockchain.js +++ b/lib/blockchain.js @@ -27,7 +27,7 @@ Blockchain = function(logger, options) { this.snapshots = []; this.logger = logger || console; this.rng = seedrandom(options.seed); - this.mnemonic = this.mnemonic || bip39.entropyToMnemonic(random.randomBytes(16, this.rng)); + this.mnemonic = options.mnemonic || bip39.entropyToMnemonic(random.randomBytes(16, this.rng)); this.wallet = hdkey.fromMasterSeed(bip39.mnemonicToSeed(this.mnemonic)); this.wallet_hdpath = "m/44'/60'/0'/0/";