diff --git a/accounts/usbwallet/hub.go b/accounts/usbwallet/hub.go
index 61fc98ccc8..640320bc91 100644
--- a/accounts/usbwallet/hub.go
+++ b/accounts/usbwallet/hub.go
@@ -127,7 +127,7 @@ func (hub *Hub) refreshWallets() {
// breaking the Ledger protocol if that is waiting for user confirmation. This
// is a bug acknowledged at Ledger, but it won't be fixed on old devices so we
// need to prevent concurrent comms ourselves. The more elegant solution would
- // be to ditch enumeration in favor of hutplug events, but that don't work yet
+ // be to ditch enumeration in favor of hotplug events, but that don't work yet
// on Windows so if we need to hack it anyway, this is more elegant for now.
hub.commsLock.Lock()
if hub.commsPend > 0 { // A confirmation is pending, don't refresh
diff --git a/accounts/usbwallet/wallet.go b/accounts/usbwallet/wallet.go
index 8b3b5a5224..6cef6e0fb0 100644
--- a/accounts/usbwallet/wallet.go
+++ b/accounts/usbwallet/wallet.go
@@ -99,7 +99,7 @@ type wallet struct {
//
// As such, a hardware wallet needs two locks to function correctly. A state
// lock can be used to protect the wallet's software-side internal state, which
- // must not be held exlusively during hardware communication. A communication
+ // must not be held exclusively during hardware communication. A communication
// lock can be used to achieve exclusive access to the device itself, this one
// however should allow "skipping" waiting for operations that might want to
// use the device, but can live without too (e.g. account self-derivation).
diff --git a/cmd/signer/README.md b/cmd/signer/README.md
new file mode 100644
index 0000000000..bd43f8acd1
--- /dev/null
+++ b/cmd/signer/README.md
@@ -0,0 +1,342 @@
+**Signer API**
+----
+The signer utility can be used to sign transactions and data and is meant as a replacement for geth's account management.
+This allows DApp's not to depend on geth's account management. When a DApp wants to sign data it can send the data to
+the signer, the signer will than provide the user with context and asks the user for permission to sign the data. If
+the users grants the signing request the signer will send the signature back to the DApp.
+
+This setup allows a DApp to connect to a remote Ethereum node and send transactions that are locally signed. This can
+help in situations when a DApp is connected to a remote node because a local Ethereum node is not available, not
+synchronised with the chain or a particular Ethereum node that has no build in, or limited account management.
+
+In its current form the signer is very limited and designed to work with Mist. It hasn't got a connection to an
+Ethereum node. This restriction imposed many limitations such as the lack of ability to keep track of nonces, balances
+or fetching additional information that can help the user to make a decision to sign a transaction or data. Currently
+the signer only supports password protected accounts. Support for hardware tokens such as Trezor and Legder is planned.
+
+## Command line flags
+The signer accepts the following command line options:
+- keystore, the directory where the password protected keystore stores keyfiles. The default directory is within geth's datadir. It is OS dependand, use `signer -h` to see where the location is on your system.
+- chainid, the chain identifier. Default value is the Ethereum mainnet. See of a list of chain identifiers that are used https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md.
+
+Example:
+```
+signer -keystore /my/keystore -chainid 4
+```
+
+## Communicating
+The signer listens on stdin for incoming requests and sends responses on stdout. Messages are expected to follow the
+[jsonrpc 2.0 standard](http://www.jsonrpc.org/specification).
+
+Some of these call can require user interaction. Clients must be aware that responses may be deplayed significanlty or
+may never be received if a users decideds to ignore the confirmation request.
+
+## API
+
+### Encoding
+- number: positive integers that are hex encoded
+- data: hex encoded data
+- string: ASCII string
+
+All hex encoded values must be prefixed with `0x`.
+
+## Methods
+
+### account_new
+
+#### Create new password protected account
+The signer will generate a new private key, encrypts it according to [web3 keystore spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) and stores it in the keystore directory.
+The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving
+lost accounts.
+
+#### Arguments
+ - passphrase [string]: passphrase that is used to protect the private key that is stored within the keystore
+
+#### Result
+ - address [string]: account address that is derived from the generated key
+ - url [string]: location of the keyfile
+
+#### Sample call
+```
+{
+ "id": 0,
+ "jsonrpc": "2.0",
+ "method": "account_new",
+ "params": [
+ "my password"
+ ]
+}
+
+{
+ "id": 0,
+ "jsonrpc": "2.0",
+ "result": {
+ "address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133",
+ "url": "keystore:///my/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133"
+ }
+}
+```
+
+### account_list
+
+#### List available accounts
+ List all accounts that this signer currently manages
+
+#### Arguments
+ none
+
+#### Result
+ - array with account records:
+ - account.address [string]: account address that is derived from the generated key
+ - account.type [string]: type of the
+ - account.url [string]: location of the account
+
+#### Sample call
+```
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "account_list"
+}
+
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "result": [
+ {
+ "address": "0xafb2f771f58513609765698f65d3f2f0224a956f",
+ "type": "account",
+ "url": "keystore:///tmp/keystore/UTC--2017-08-24T07-26-47.162109726Z--afb2f771f58513609765698f65d3f2f0224a956f"
+ },
+ {
+ "address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133",
+ "type": "account",
+ "url": "keystore:///tmp/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133"
+ }
+ ]
+}
+```
+
+### account_signTransaction
+
+#### Sign transactions
+ Signs a transactions and respons with the signed transaction in RLP encoded form.
+
+#### Arguments
+ - from [address]: account to send the transaction from
+ - passphrase [string]: passphrase to unlock the from account
+ - Transaction object:
+ - transaction.to [address]: receiver account
+ - gas [number]: maximum amount of gas to burn
+ - gasPrice [number]: gas price
+ - value [number:optional]: amount of Wei to send with the transaction
+ - data [data:optional]: input data
+ - transaction.nonce [number]: account nonce
+
+#### Result
+ - signed transaction in RLP encoded form [data]
+
+#### Sample call
+```
+{
+ "id": 2,
+ "jsonrpc": "2.0",
+ "method": "account_signTransaction",
+ "params": [
+ "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
+ "my password",
+ {
+ "gas": "0x55555",
+ "gasPrice": "0x1234",
+ "input": "0xabcd",
+ "nonce": "0x0",
+ "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
+ "value": "0x1234"
+ }
+ ]
+}
+
+{
+ "id": 2,
+ "jsonrpc": "2.0",
+ "result": "0xf86480821234830555559407a565b7ed7d7a678680a4c162885bedbb695fe0821234802ea028f9ebeff90732eae45692a11c4ca2ef7f631a0a25bf8763d093e770c4ec464aa01fae77b24617913e718b989be78bc1aabb2fed3f2d4e3b93bd36759f1b5b4904"
+}
+```
+
+### account_sign
+
+#### Sign data
+ Signs a chunk of data and returns the calculated signature.
+
+#### Arguments
+ - account [address]: account to sign with
+ - passphrase [string]: passphrase to unlock the account
+ - data [data]: data to sign
+
+#### Result
+ - calculated signature [data]
+
+#### Sample call
+```
+{
+ "id": 3,
+ "jsonrpc": "2.0",
+ "method": "account_sign",
+ "params": [
+ "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
+ "my password",
+ "0xaabbccdd"
+ ]
+}
+
+{
+ "id": 3,
+ "jsonrpc": "2.0",
+ "result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
+}
+```
+
+### account_ecRecover
+
+#### Recover address
+ Derive the address from the account that was used to sign data from the data and signature.
+
+#### Arguments
+ - data [data]: data that was signed
+ - signature [data]: the signature to verify
+
+#### Result
+ - derived account [address]
+
+#### Sample call
+```
+{
+ "id": 4,
+ "jsonrpc": "2.0",
+ "method": "account_ecRecover",
+ "params": [
+ "0xaabbccdd",
+ "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
+ ]
+}
+
+{
+ "id": 4,
+ "jsonrpc": "2.0",
+ "result": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db"
+}
+
+```
+
+### account_import
+
+#### Import account
+ Import a private key into the keystore. The imported key is expected to be encrypted according to the web3 keystore
+ format.
+
+#### Arguments
+ - account [object]: key in [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) (retrieved with account_export)
+ - passphrase [string]: password to decrypt the given account
+ - newPassphrase [string]: password to encrypt the imported key in the keystore with
+
+#### Result
+ - imported key [object]:
+ - key.address [address]: address of the imported key
+ - key.type [string]: type of the account
+ - key.url [string]: key URL
+
+#### Sample call
+```
+{
+ "id": 6,
+ "jsonrpc": "2.0",
+ "method": "account_import",
+ "params": [
+ {
+ "address": "c7412fc59930fd90099c917a50e5f11d0934b2f5",
+ "crypto": {
+ "cipher": "aes-128-ctr",
+ "cipherparams": {
+ "iv": "401c39a7c7af0388491c3d3ecb39f532"
+ },
+ "ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204",
+ "kdf": "scrypt",
+ "kdfparams": {
+ "dklen": 32,
+ "n": 262144,
+ "p": 1,
+ "r": 8,
+ "salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a"
+ },
+ "mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806"
+ },
+ "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
+ "version": 3
+ },
+ "my password",
+ "my password"
+ ]
+}
+{
+ "id": 6,
+ "jsonrpc": "2.0",
+ "result": {
+ "address": "0xc7412fc59930fd90099c917a50e5f11d0934b2f5",
+ "type": "account",
+ "url": "keystore:///tmp/keystore/UTC--2017-08-24T11-00-42.032024108Z--c7412fc59930fd90099c917a50e5f11d0934b2f5"
+ }
+}
+```
+
+### account_export
+
+#### Export account from keystore
+ Export a private key from the keystore. The exported private key is encrypted with the original passphrase. When the
+ key is imported later this passphrase is required.
+
+#### Arguments
+ - account [address]: export private key that is associated with this account
+
+#### Result
+ - exported key, see [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) for
+ more information
+
+#### Sample call
+```
+{
+ "id": 5,
+ "jsonrpc": "2.0",
+ "method": "account_export",
+ "params": [
+ "0xc7412fc59930fd90099c917a50e5f11d0934b2f5"
+ ]
+}
+{
+ "id": 5,
+ "jsonrpc": "2.0",
+ "result": {
+ "address": "c7412fc59930fd90099c917a50e5f11d0934b2f5",
+ "crypto": {
+ "cipher": "aes-128-ctr",
+ "cipherparams": {
+ "iv": "401c39a7c7af0388491c3d3ecb39f532"
+ },
+ "ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204",
+ "kdf": "scrypt",
+ "kdfparams": {
+ "dklen": 32,
+ "n": 262144,
+ "p": 1,
+ "r": 8,
+ "salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a"
+ },
+ "mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806"
+ },
+ "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
+ "version": 3
+ }
+}
+```
+
+
+
diff --git a/cmd/signer/api.go b/cmd/signer/api.go
new file mode 100644
index 0000000000..66c09f1d05
--- /dev/null
+++ b/cmd/signer/api.go
@@ -0,0 +1,229 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "fmt"
+
+ "errors"
+
+ "context"
+
+ "math/big"
+
+ "io/ioutil"
+
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
+ "github.com/ethereum/go-ethereum/accounts/usbwallet"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+ "encoding/json"
+)
+
+type SignerAPI struct {
+ chainID *big.Int
+ am *accounts.Manager
+}
+
+// NewSignerAPI creates a new API that can be used for account management.
+// ksLocation specifies the directory where to store the password protected private
+// key that is generated when a new account is created.
+// noUSB disables USB support that is required to support hardware devices such as
+// ledger and trezor.
+func NewSignerAPI(chainID int64, ksLocation string, noUSB bool) *SignerAPI {
+ var backends []accounts.Backend
+
+ // support password based accounts
+ if len(ksLocation) > 0 {
+ backends = append(backends, keystore.NewKeyStore(ksLocation, keystore.StandardScryptN, keystore.StandardScryptP))
+ }
+
+ if !noUSB {
+ // Start a USB hub for Ledger hardware wallets
+ if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
+ log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err))
+ } else {
+ backends = append(backends, ledgerhub)
+ log.Debug("Ledger support enabled")
+ }
+ // Start a USB hub for Trezor hardware wallets
+ if trezorhub, err := usbwallet.NewTrezorHub(); err != nil {
+ log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err))
+ } else {
+ backends = append(backends, trezorhub)
+ log.Debug("Trezor support enabled")
+ }
+ }
+
+ return &SignerAPI{big.NewInt(chainID), accounts.NewManager(backends...)}
+}
+
+// List returns the set of wallet this signer manages. Each wallet can contain
+// multiple accounts.
+func (api *SignerAPI) List(ctx context.Context) []Account {
+ var accounts []Account
+ for _, wallet := range api.am.Wallets() {
+ for _, acc := range wallet.Accounts() {
+ acc := Account{Typ: "account", URL: wallet.URL(), Address: acc.Address}
+ accounts = append(accounts, acc)
+ }
+ }
+ return accounts
+}
+
+// New creates a new password protected account. The private key is protected with
+// the given password. Users are responsible to backup the private key that is stored
+// in the keystore location thas was specified when this API was created.
+func (api *SignerAPI) New(ctx context.Context, passphrase string) (accounts.Account, error) {
+ be := api.am.Backends(keystore.KeyStoreType)
+ if len(be) == 0 {
+ return accounts.Account{}, errors.New("password based accounts not supported")
+ }
+ acc, err := be[0].(*keystore.KeyStore).NewAccount(passphrase)
+ return acc, err
+}
+
+// SignTransaction signs the given transaction and returns it in an RLP encoded form
+// that can be posted to `eth_sendRawTransaction`.
+func (api *SignerAPI) SignTransaction(ctx context.Context, from common.Address, passwd string, args TransactionArg) (hexutil.Bytes, error) {
+ acc := accounts.Account{Address: from}
+
+ wallet, err := api.am.Find(acc)
+ if err != nil {
+ return nil, err
+ }
+
+ var tx *types.Transaction
+ if args.To == nil {
+ tx = types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), args.Data)
+ } else {
+ tx = types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), args.Data)
+ }
+
+ signedTx, err := wallet.SignTxWithPassphrase(acc, passwd, tx, api.chainID)
+ if err != nil {
+ return nil, err
+ }
+
+ return rlp.EncodeToBytes(signedTx)
+}
+
+// Sign calculates an Ethereum ECDSA signature for:
+// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message))
+//
+// Note, the produced signature conforms to the secp256k1 curve R, S and V values,
+// where the V value will be 27 or 28 for legacy reasons.
+//
+// The key used to calculate the signature is decrypted with the given password.
+//
+// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
+func (api *SignerAPI) Sign(ctx context.Context, addr common.Address, passwd string, data hexutil.Bytes) (hexutil.Bytes, error) {
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: addr}
+
+ wallet, err := api.am.Find(account)
+ if err != nil {
+ return nil, err
+ }
+ // Assemble sign the data with the wallet
+ signature, err := wallet.SignHashWithPassphrase(account, passwd, signHash(data))
+ if err != nil {
+ return nil, err
+ }
+ signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
+ return signature, nil
+}
+
+// EcRecover returns the address for the account that was used to create the signature.
+// Note, this function is compatible with eth_sign and personal_sign. As such it recovers
+// the address of:
+// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message})
+// addr = ecrecover(hash, signature)
+//
+// Note, the signature must conform to the secp256k1 curve R, S and V values, where
+// the V value must be be 27 or 28 for legacy reasons.
+//
+// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover
+func (api *SignerAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) {
+ if len(sig) != 65 {
+ return common.Address{}, fmt.Errorf("signature must be 65 bytes long")
+ }
+ if sig[64] != 27 && sig[64] != 28 {
+ return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
+ }
+ sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1
+
+ rpk, err := crypto.Ecrecover(signHash(data), sig)
+ if err != nil {
+ return common.Address{}, err
+ }
+ pubKey := crypto.ToECDSAPub(rpk)
+ recoveredAddr := crypto.PubkeyToAddress(*pubKey)
+ return recoveredAddr, nil
+}
+
+// signHash is a helper function that calculates a hash for the given message that can be
+// safely used to calculate a signature from.
+//
+// The hash is calulcated as
+// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
+//
+// This gives context to the signed message and prevents signing of transactions.
+func signHash(data []byte) []byte {
+ msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
+ return crypto.Keccak256([]byte(msg))
+}
+
+// Export returns encrypted private key associated with the given address in web3 keystore format.
+func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) {
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: addr}
+
+ wallet, err := api.am.Find(account)
+ if err != nil {
+ return nil, err
+ }
+
+ url := wallet.URL()
+ if url.Scheme != keystore.KeyStoreScheme {
+ return nil, fmt.Errorf("account is not a password protected account")
+ }
+
+ return ioutil.ReadFile(url.Path)
+}
+
+// Imports tries to import the given keyJSON in the local keystore. The keyJSON data is expected to be
+// in web3 keystore format. It will decrypt the keyJSON with the given passphrase and on successful
+// decryption it will encrypt the key with the given newPassphrase and store it in the keystore.
+func (api *SignerAPI) Import(ctx context.Context, keyJSON json.RawMessage, passphrase, newPassphrase string) (Account, error) {
+ be := api.am.Backends(keystore.KeyStoreType)
+ if len(be) == 0 {
+ return Account{}, errors.New("password based accounts not supported")
+ }
+
+ acc, err := be[0].(*keystore.KeyStore).Import(keyJSON, passphrase, newPassphrase)
+ if err != nil {
+ return Account{}, err
+ }
+
+ return Account{Typ: "account", URL: acc.URL, Address: acc.Address}, nil
+}
\ No newline at end of file
diff --git a/cmd/signer/demo.js b/cmd/signer/demo.js
new file mode 100644
index 0000000000..921a35bbbe
--- /dev/null
+++ b/cmd/signer/demo.js
@@ -0,0 +1,141 @@
+/* Demonstrates the following signer methods
+
+- account_new: generate new password protected account
+ 1. password: string
+
+ returns account object with address and URL
+
+- account_list: listing of accounts
+ no args
+
+ returns array with accounts
+
+- account_signTransaction: sign transaction and get tx in RLP encoded form back
+ 1. from: address
+ 2. passwd: string
+ 3. transaction: object
+
+ returns signed transaction in RLP form (can be used with eth_sendRawTransaction)
+
+- account_sign: calculate signature
+ 1. from: address
+ 2. passwd: string
+ 3. data: hex string
+
+ returns signature
+
+- account_ecRecover: derive address from signature
+ 1. data: hex string
+ 2. signature: hex string
+
+ returns address
+ */
+
+var spawn = require('child_process').spawn;
+
+// by default the signer used the keystore for the mainnet, in this case it is pointed to a non-standard location.
+// also it accepts the chainid, by default it uses the chainid for the mainnet.
+const signer = spawn('./signer', ['-keystore', '/tmp/keystore', '-chainid', 5]);
+const passwd = 'my password';
+var createdAccountAddress = '0x';
+var signData = '0xaabbccdd';
+var signSignature = '0x';
+var keystoreKeyData = '0x';
+
+var currentRequest = -1;
+function nextRequest() {
+ currentRequest++;
+ var req = null;
+
+ if (currentRequest < 7) {
+ req = {
+ id: currentRequest,
+ jsonrpc: "2.0"
+ };
+
+ switch (currentRequest) {
+ case 0:
+ req.method = 'account_new';
+ req.params = [passwd];
+ break
+ case 1:
+ req.method = 'account_list';
+ break;
+ case 2:
+ req.method = 'account_signTransaction';
+ req.params = [createdAccountAddress, passwd, {
+ nonce: "0x0",
+ gasPrice: "0x1234",
+ gas: "0x55555",
+ value: "0x1234",
+ input: "0xabcd",
+ to: "0x07a565b7ed7d7a678680a4c162885bedbb695fe0"
+ }];
+ break;
+ case 3:
+ req.method = 'account_sign';
+ req.params = [createdAccountAddress, passwd, signData];
+ break;
+ case 4:
+ req.method = 'account_ecRecover';
+ req.params = [signData, signSignature];
+ break;
+ case 5:
+ req.method = 'account_export';
+ req.params = [createdAccountAddress]
+ break;
+ case 6:
+ req.method = 'account_import';
+ req.params = [keystoreKeyData, passwd, passwd];
+ break;
+ }
+ }
+
+ return req;
+}
+signer.stdout.on('data', (data) => {
+ console.log(`${data}`);
+
+ response = JSON.parse(`${data}`);
+
+ switch (response.id) {
+ case 0:
+ createdAccountAddress = response.result.address;
+ break;
+ case 3:
+ signSignature = response.result;
+ break
+ case 4:
+ if (createdAccountAddress !== response.result) {
+ console.error("expected address", createdAccountAddress, "got", response.result);
+ } else {
+ //console.log("Address recovered correct");
+ }
+ break;
+ case 5:
+ keystoreKeyData = response.result;
+ break;
+ }
+
+ var req = nextRequest();
+ if (req !== null) {
+ req = JSON.stringify(req);
+ console.log(req);
+ signer.stdin.write(req);
+ } else {
+ signer.kill();
+ }
+});
+
+signer.stderr.on('data', (data) => {
+ console.log(`stderr: ${data}`);
+});
+
+signer.on('close', (code) => {
+ //console.log(`signer process exited with code ${code}`);
+});
+
+// kickstart request cycle
+req = JSON.stringify(nextRequest());
+console.log(req);
+signer.stdin.write(req);
diff --git a/cmd/signer/main.go b/cmd/signer/main.go
new file mode 100644
index 0000000000..803e9e8ef5
--- /dev/null
+++ b/cmd/signer/main.go
@@ -0,0 +1,68 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+// signer is a utility that can be used so sign transactions and
+// arbitrary data.
+package main
+
+import (
+ "bufio"
+ "flag"
+ "io"
+ "os"
+ "path/filepath"
+
+ "github.com/ethereum/go-ethereum/cmd/utils"
+ "github.com/ethereum/go-ethereum/node"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rpc"
+)
+
+var (
+ ksLocation = flag.String("keystore", filepath.Join(node.DefaultDataDir(), "keystore"), "Directory for the keystore")
+ chainID = flag.Int64("chainid", params.MainnetChainConfig.ChainId.Int64(), "chain identifier")
+)
+
+func main() {
+ flag.Parse()
+
+ var (
+ server = rpc.NewServer()
+ api = NewSignerAPI(*chainID, *ksLocation, true)
+ )
+
+ // register signer API with server
+ if err := server.RegisterName("account", api); err != nil {
+ utils.Fatalf("Could not register signer API: %v", err)
+ }
+
+ // start server with in-/output connected to stdin/stdout
+ in, out := bufio.NewReader(os.Stdin), os.Stdout
+ codec := rpc.NewJSONCodec(&rwc{in, out})
+ server.ServeCodec(codec, rpc.OptionMethodInvocation|rpc.OptionSubscriptions)
+}
+
+type rwc struct {
+ io.Reader
+ io.Writer
+}
+
+func (r *rwc) Close() error {
+ if err := os.Stdin.Close(); err != nil {
+ return err
+ }
+ return os.Stdout.Close()
+}
diff --git a/cmd/signer/test.js b/cmd/signer/test.js
new file mode 100644
index 0000000000..4f50f1834b
--- /dev/null
+++ b/cmd/signer/test.js
@@ -0,0 +1,24 @@
+const net = require('net');
+const path = require('path');
+
+let conn = net.connect(path.join('\\\\?\\pipe', 'ethereum-signer'))
+
+const req = {
+ id: 1234,
+ jsonrpc: '2.0',
+ //method: 'account_list',
+ method: 'account_signTransaction',
+ params: ['0xaabbccddaabbccddaabbccddaabbccddaabbccdd', {
+ to: '0x0011223344556677889900112233445566778899',
+ value: '0x123450000',
+ data: '0xabcdef',
+ gas: '0x12345',
+ gasPrice: '0x67890'
+ }]
+};
+
+conn.on('data', (data) => {
+ console.log(data.toString());
+});
+
+conn.write(JSON.stringify(req));
diff --git a/cmd/signer/types.go b/cmd/signer/types.go
new file mode 100644
index 0000000000..926a6241d5
--- /dev/null
+++ b/cmd/signer/types.go
@@ -0,0 +1,39 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+type Account struct {
+ Typ string `json:"type"`
+ URL accounts.URL `json:"url"`
+ Address common.Address `json:"address"`
+}
+
+// TransactionArg represents a transaction for the signer.
+type TransactionArg struct {
+ To *common.Address `json:"to"`
+ Gas *hexutil.Big `json:"gas"`
+ GasPrice *hexutil.Big `json:"gasPrice"`
+ Value *hexutil.Big `json:"value"`
+ Data hexutil.Bytes `json:"data"`
+ Nonce *hexutil.Uint64 `json:"nonce"`
+}