ethereumjs / keythereum

Create, import and export Ethereum keys
MIT License
609 stars 163 forks source link

recover very slow #52

Closed rustyx closed 6 years ago

rustyx commented 6 years ago

The recover function of keythereum runs more than 7 times slower than unlockAccount in geth.

// setup code omitted
(async()=>{
  const tm1 = Date.now();
  await web3.eth.personal.unlockAccount(acct, passwd);
  const tm2 = Date.now();
  console.log(tm2 - tm1);
  var keyObject = keythereum.importFromFile(acct, datadir);
  const tm3 = Date.now();
  var privateKey = keythereum.recover(passwd, keyObject);
  const tm4 = Date.now();
  console.log(tm4 - tm3);
})()

Prints

766
5697

Why is that? Can it be improved?

I have to use geth-generated accounts, and specifying fewer hashing rounds is not an option.

tinybike commented 6 years ago

@rustyx are you running this in Nodejs or the browser? I suspect the difference is just that Go is faster than JS (because it compiles to native machine code), but I admit I've never compared them directly.

Note: if your concern is tying up the main thread, I suggest using the asynchronous version of recover by providing a callback function. (Adding promise support to keythereum is on the to-do list, but I haven't had time to add it as of yet...)

tinybike commented 6 years ago

Quick follow-up thought. As I understand it, web3.eth.personal.unlockAccount sends a personal_unlockAccount JSON RPC request to geth, which will be nearly instant if you're using the geth console, but will require an RPC request round-trip otherwise. By contrast, keythereum.recover is purely local JS code. So, if you have a local, trusted geth instance that has the personal RPC namespace available, you may be better off using personal_unlockAccount. In other circumstances, recover may be more convenient.

rustyx commented 6 years ago

First a reply to the last comment: if I didn't need the private key itself for off-chain custom encryption, I would of course just use the geth RPC API.

Now, there is no "tying up the main thread" as in Node there is just one thread; running a 6-second KDF loop blocks the entire Node server, no matter how much "asynchronously" it is invoked.

The problem is that the pure-JavaScript scrypt is slow. Node.js has a native scrypt which I measured to be 8 times faster. But it won't work in a browser any more.

To that end I've forked this project and made it Node-only as I personally don't see any need to run it in a browser. At the same time made it promise-aware with ES6.

Are there people running keythereum inside a browser? If so, for what purpose?

tinybike commented 6 years ago

Yes, I've seen keythereum used in the browser. I know Augur's middleware used it at one point (although not currently) for basic key management tasks; e.g., button in the UI to import a keyfile, then keythereum decrypts the private key, etc.

Glad to see you've made a more performant (and promise-aware) Node-only version! I think if you added a browser field to the package.json, your version could be merged with the browser-compatible version in this repo:

"browser": {
  "scrypt": false  
}

And in index.js:

var scrypt = process.browser ? require("./lib/scrypt") : require("scrypt");