diff --git a/README.md b/README.md index 85e27038c..cbe039755 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,11 @@ The RPC methods currently implemented are: * `net_version` * `miner_start` * `miner_stop` +* `personal_listAccounts` +* `personal_lockAccount` +* `personal_newAccount` +* `personal_unlockAccount` +* `personal_sendTransaction` * `rpc_modules` * `web3_clientVersion` * `web3_sha3` diff --git a/lib/statemanager.js b/lib/statemanager.js index 35419eec7..24c7cc0fe 100644 --- a/lib/statemanager.js +++ b/lib/statemanager.js @@ -7,6 +7,7 @@ var FakeTransaction = require('ethereumjs-tx/fake.js'); var utils = require('ethereumjs-util'); var seedrandom = require('seedrandom'); var bip39 = require('bip39'); +var wallet = require('ethereumjs-wallet'); var hdkey = require('ethereumjs-wallet/hdkey'); var async = require("async"); var BlockchainDouble = require("./blockchain_double.js"); @@ -36,6 +37,8 @@ StateManager = function(options) { this.accounts = {}; this.secure = !!options.secure; + this.account_passwords = {} + this.personal_accounts = {} this.total_accounts = options.total_accounts || 10; this.coinbase = null; @@ -145,7 +148,9 @@ StateManager.prototype.createAccount = function(opts) { var secretKey; var balance; - if (opts.secretKey) { + if (opts.generate) { + secretKey = wallet.generate().getPrivateKey(); + } else if (opts.secretKey) { secretKey = utils.toBuffer(to.hex(opts.secretKey)); } else { var acct = this.wallet.derivePath(this.wallet_hdpath + opts.index) // index is a number diff --git a/lib/subproviders/geth_api_double.js b/lib/subproviders/geth_api_double.js index f8080be40..c16cf2355 100644 --- a/lib/subproviders/geth_api_double.js +++ b/lib/subproviders/geth_api_double.js @@ -298,7 +298,61 @@ GethApiDouble.prototype.miner_stop = function(callback) { GethApiDouble.prototype.rpc_modules = function(callback) { // returns the availible api modules and versions - callback(null, {"eth":"1.0","net":"1.0","rpc":"1.0","web3":"1.0","evm":"1.0"}); + callback(null, {"eth":"1.0","net":"1.0","rpc":"1.0","web3":"1.0","evm":"1.0","personal":"1.0"}); +}; + +GethApiDouble.prototype.personal_listAccounts = function(callback) { + callback(null, Object.keys(this.state.personal_accounts)); +}; + +GethApiDouble.prototype.personal_newAccount = function(password, callback) { + var account = this.state.createAccount({ generate: true }); + this.state.accounts[account.address] = account; + this.state.personal_accounts[account.address] = true; + this.state.account_passwords[account.address] = password; + callback(null, account.address); +}; + +GethApiDouble.prototype.personal_lockAccount = function(address, callback) { + var account = this.state.personal_accounts[address]; + if (account !== true) { + return callback("Account not found") + } + this.state.unlocked_accounts[address.toLowerCase()] = false; + callback(null, true); +}; + +GethApiDouble.prototype.personal_unlockAccount = function(address, password, duration, callback) { + // FIXME handle duration + var account = this.state.personal_accounts[address]; + if (account !== true) { + return callback("Account not found") + } + if (this.state.account_passwords[address.toLowerCase()] !== password) { + return callback("Invalid password") + } + this.state.unlocked_accounts[address.toLowerCase()] = true; + callback(null, true); +}; + +GethApiDouble.prototype.personal_sendTransaction = function(tx_data, password, callback) { + if (tx_data.from == null) { + callback("Sender not found"); + return; + } + + var from = utils.addHexPrefix(tx_data.from).toLowerCase(); + + var self = this; + self.personal_unlockAccount(from, password, null, function(err) { + if (err) { + return callback(err) + } + self.state.queueTransaction("eth_sendTransaction", tx_data, function(err, ret) { + self.state.unlocked_accounts[from] = false; + callback(err, ret); + }); + }); }; /* Functions for testing purposes only. */ diff --git a/package.json b/package.json index 44693758b..de59d86bb 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "seedrandom": "~2.4.2", "shelljs": "~0.6.0", "solc": "0.4.6", - "web3": "~0.16.0", + "web3": "~0.18.2", "web3-provider-engine": "~8.1.0", "yargs": "~3.29.0" }, diff --git a/test/requests.js b/test/requests.js index 27743e3b8..237938d78 100644 --- a/test/requests.js +++ b/test/requests.js @@ -52,13 +52,18 @@ var contract = { var tests = function(web3) { var accounts; + var personalAccount; before(function(done) { web3.eth.getAccounts(function(err, accs) { if (err) return done(err); accounts = accs; - done(); + + web3.personal.newAccount("password", function(err, result) { + personalAccount = result; + done(); + }); }); }); @@ -838,6 +843,60 @@ var tests = function(web3) { }); }); }); + + describe("personal_newAccount", function() { + it("should return the new address", function(done) { + web3.personal.newAccount("password", function(err, result) { + if (err) return done(err); + assert.notEqual(result.match("0x[0-9a-f]{39}"), null, "Invalid address received"); + done(); + }); + }); + }); + + describe("personal_listAccounts", function() { + it("should return more than 0 accounts", function(done) { + web3.personal.getListAccounts(function(err, result) { + if (err) return done(err); + assert.equal(result.length, 2); + done(); + }); + }); + }); + + describe("personal_unlockAccount", function() { + it("should unlock account", function(done) { + web3.personal.unlockAccount(personalAccount, "password", function(err, result) { + if (err) return done(err); + assert.equal(result, true); + done(); + }); + }); + }); + + describe("personal_lockAccount", function() { + it("should lock account", function(done) { + web3.personal.lockAccount(personalAccount, function(err, result) { + if (err) return done(err); + assert.equal(result, true); + done(); + }); + }); + }); + + describe("personal_sendTransaction", function() { + it("should send transaction", function(done) { + web3.personal.sendTransaction({ + from: personalAccount, + to: personalAccount, + value: 1 + }, "password", function(err, result) { + // NOTE: this is an Error class thrown by the state + assert.notEqual(err.message.match("sender doesn't have enough funds to send tx."), null); + done(); + }); + }); + }); }; var logger = {