cmd/signer: minor changes

This commit is contained in:
Martin Holst Swende 2017-11-30 14:59:06 +01:00
parent f88559e09f
commit 39d43a74b1
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
5 changed files with 57 additions and 56 deletions

View file

@ -11,13 +11,23 @@ synchronised with the chain or a particular Ethereum node that has no build in,
In its current form the signer is very limited and designed to work with Mist. It hasn't got a connection to an 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 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 or fetching additional information that can help the user to make a decision to sign a transaction or data.
the signer only supports password protected accounts. Support for hardware tokens such as Trezor and Legder is planned.
## Command line flags ## Command line flags
The signer accepts the following command line options: 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. --chainid value chain identifier (default: 1)
--loglevel value log level to emit to the screen (default: 4)
--keystore value Directory for the keystore (default: "/home/martin/.ethereum/keystore")
--networkid value Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby) (default: 1)
--lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength
--nousb Disables monitoring for and managing USB hardware wallets
--rpcaddr value HTTP-RPC server listening interface (default: "localhost")
--rpcport value HTTP-RPC server listening port (default: 8550)
--4bytedb value File containing 4byte-identifiers (default: "./4byte.json")
--help, -h show help
```
Example: Example:
``` ```
@ -25,7 +35,8 @@ signer -keystore /my/keystore -chainid 4
``` ```
## Communicating ## Communicating
The signer listens on stdin for incoming requests and sends responses on stdout. Messages are expected to follow the
The signer listens to HTTP requests on `rpcaddr`:`rpcport`. The messages are expected to be JSON
[jsonrpc 2.0 standard](http://www.jsonrpc.org/specification). [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 Some of these call can require user interaction. Clients must be aware that responses may be deplayed significanlty or
@ -50,7 +61,8 @@ The client is responsible for creating a backup of the keystore. If the keystore
lost accounts. lost accounts.
#### Arguments #### Arguments
- passphrase [string]: passphrase that is used to protect the private key that is stored within the keystore
None
#### Result #### Result
- address [string]: account address that is derived from the generated key - address [string]: account address that is derived from the generated key
@ -83,7 +95,8 @@ lost accounts.
List all accounts that this signer currently manages List all accounts that this signer currently manages
#### Arguments #### Arguments
none
None
#### Result #### Result
- array with account records: - array with account records:
@ -120,11 +133,10 @@ lost accounts.
### account_signTransaction ### account_signTransaction
#### Sign transactions #### Sign transactions
Signs a transactions and respons with the signed transaction in RLP encoded form. Signs a transactions and responds with the signed transaction in RLP encoded form.
#### Arguments #### Arguments
- from [address]: account to send the transaction from - from [address]: account to send the transaction from
- passphrase [string]: passphrase to unlock the from account
- Transaction object: - Transaction object:
- transaction.to [address]: receiver account - transaction.to [address]: receiver account
- gas [number]: maximum amount of gas to burn - gas [number]: maximum amount of gas to burn
@ -144,7 +156,6 @@ lost accounts.
"method": "account_signTransaction", "method": "account_signTransaction",
"params": [ "params": [
"0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
"my password",
{ {
"gas": "0x55555", "gas": "0x55555",
"gasPrice": "0x1234", "gasPrice": "0x1234",
@ -170,7 +181,6 @@ lost accounts.
#### Arguments #### Arguments
- account [address]: account to sign with - account [address]: account to sign with
- passphrase [string]: passphrase to unlock the account
- data [data]: data to sign - data [data]: data to sign
#### Result #### Result
@ -184,7 +194,6 @@ lost accounts.
"method": "account_sign", "method": "account_sign",
"params": [ "params": [
"0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
"my password",
"0xaabbccdd" "0xaabbccdd"
] ]
} }
@ -236,8 +245,6 @@ lost accounts.
#### Arguments #### Arguments
- account [object]: key in [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) (retrieved with account_export) - 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 #### Result
- imported key [object]: - imported key [object]:
@ -273,8 +280,6 @@ lost accounts.
"id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9", "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
"version": 3 "version": 3
}, },
"my password",
"my password"
] ]
} }
{ {

View file

@ -41,7 +41,6 @@ type SignerAPI struct {
am *accounts.Manager am *accounts.Manager
ui SignerUI ui SignerUI
abidb abiDb abidb abiDb
audit *auditlogger
} }
// Metadata about the request // Metadata about the request
@ -173,7 +172,7 @@ func (ui *HeadlessUI) ShowInfo(message string) {
// key that is generated when a new account is created. // key that is generated when a new account is created.
// noUSB disables USB support that is required to support hardware devices such as // noUSB disables USB support that is required to support hardware devices such as
// ledger and trezor. // ledger and trezor.
func NewSignerAPI(chainID int64, ksLocation string, noUSB bool, ui SignerUI, abidb *abiDb, auditlog string) *SignerAPI { func NewSignerAPI(chainID int64, ksLocation string, noUSB bool, ui SignerUI, abidb *abiDb) *SignerAPI {
var backends []accounts.Backend var backends []accounts.Backend
// support password based accounts // support password based accounts
@ -197,11 +196,7 @@ func NewSignerAPI(chainID int64, ksLocation string, noUSB bool, ui SignerUI, abi
log.Debug("Trezor support enabled") log.Debug("Trezor support enabled")
} }
} }
var al *auditlogger return &SignerAPI{big.NewInt(chainID), accounts.NewManager(backends...), ui, *abidb}
if auditlog != ""{
al = &auditlogger{auditlog}
}
return &SignerAPI{big.NewInt(chainID), accounts.NewManager(backends...), ui, *abidb, al}
} }
func metaData(ctx context.Context) Metadata { func metaData(ctx context.Context) Metadata {
@ -300,6 +295,7 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, from common.Address,
} }
signedTx, err := wallet.SignTxWithPassphrase(acc, result.pw, tx, api.chainID) signedTx, err := wallet.SignTxWithPassphrase(acc, result.pw, tx, api.chainID)
if err != nil { if err != nil {
api.ui.ShowError(err.Error())
return nil, err return nil, err
} }
return rlp.EncodeToBytes(signedTx) return rlp.EncodeToBytes(signedTx)
@ -337,6 +333,7 @@ func (api *SignerAPI) Sign(ctx context.Context, addr common.Address, data hexuti
// Assemble sign the data with the wallet // Assemble sign the data with the wallet
signature, err := wallet.SignHashWithPassphrase(account, res.pw, sighash) signature, err := wallet.SignHashWithPassphrase(account, res.pw, sighash)
if err != nil { if err != nil {
api.ui.ShowError(err.Error())
return nil, err return nil, err
} }
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
@ -414,7 +411,8 @@ func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.Raw
// Imports tries to import the given keyJSON in the local keystore. The keyJSON data is expected to be // 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 // 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. // 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) (Account, error) { func (api *SignerAPI) Import(ctx context.Context, string, keyJSON json.RawMessage) (Account, error) {
be := api.am.Backends(keystore.KeyStoreType) be := api.am.Backends(keystore.KeyStoreType)
if len(be) == 0 { if len(be) == 0 {
@ -424,10 +422,12 @@ func (api *SignerAPI) Import(ctx context.Context, keyJSON json.RawMessage) (Acco
ch := make(chan ImportResponse, 1) ch := make(chan ImportResponse, 1)
api.ui.ApproveImport(&ImportRequest{}, metaData(ctx), ch) api.ui.ApproveImport(&ImportRequest{}, metaData(ctx), ch)
if resp := <-ch; resp.approved { if resp := <-ch; resp.approved {
acc, err := be[0].(*keystore.KeyStore).Import(keyJSON, resp.oldPassword, resp.newPassword) acc, err := be[0].(*keystore.KeyStore).Import(keyJSON, resp.oldPassword, resp.newPassword)
if err != nil { if err != nil {
api.ui.ShowError(err.Error())
return Account{}, err return Account{}, err
} }

View file

@ -1,23 +0,0 @@
package main
import (
"os"
"github.com/ethereum/go-ethereum/log"
)
type auditlogger struct{
filename string
}
func (al auditlogger) append(text string) error{
f, err := os.OpenFile(al.filename, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
log.Crit("Failed to open audit log","err", err)
return err
}
defer f.Close()
if _, err = f.WriteString(text); err != nil {
log.Crit("Failed to write to audit log","err", err)
return err
}
}

View file

@ -54,21 +54,37 @@ func (ui *CommandlineUI) readString() string {
// readPassword reads a single line from stdin, trimming it from the trailing new // readPassword reads a single line from stdin, trimming it from the trailing new
// line and returns it. The input will not be echoed. // line and returns it. The input will not be echoed.
func (ui *CommandlineUI) readPassword() string { func (ui *CommandlineUI) readPassword() string {
fmt.Printf("Enter password to approve:\n")
fmt.Printf("> ") fmt.Printf("> ")
text, err := terminal.ReadPassword(int(os.Stdin.Fd())) text, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil { if err != nil {
log.Crit("Failed to read password", "err", err) log.Crit("Failed to read password", "err", err)
} }
fmt.Println() fmt.Println()
fmt.Println("-----------------------")
return string(text)
}
// readPassword reads a single line from stdin, trimming it from the trailing new
// line and returns it. The input will not be echoed.
func (ui *CommandlineUI) readPasswordText(inputstring string) string {
fmt.Printf("Enter %s:\n", inputstring)
fmt.Printf("> ")
text, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
log.Crit("Failed to read password", "err", err)
}
fmt.Println("-----------------------")
return string(text) return string(text)
} }
// confirm returns true if user enters 'Yes', otherwise false // confirm returns true if user enters 'Yes', otherwise false
func (ui *CommandlineUI) confirm() bool { func (ui *CommandlineUI) confirm() bool {
fmt.Printf("Type 'Yes' to approve\n") fmt.Printf("Type 'Yes' to approve:\n")
if ui.readString() == "Yes" { if ui.readString() == "Yes" {
return true return true
} }
fmt.Println("-----------------------")
return false return false
} }
@ -98,7 +114,7 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest, metadata Metadata, ch
showMetadata(metadata) showMetadata(metadata)
fmt.Printf("-------------------------------------------\n") fmt.Printf("-------------------------------------------\n")
ch <- SignTxResponse{request.transaction.Hash(), ui.confirm(), ""} ch <- SignTxResponse{request.transaction.Hash(), true ,ui.readPassword()}
} }
// ApproveSignData prompt the user for confirmation to request to sign data // ApproveSignData prompt the user for confirmation to request to sign data
@ -113,7 +129,7 @@ func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest, metadata Meta
fmt.Printf("message hash: %v\n", request.hash) fmt.Printf("message hash: %v\n", request.hash)
fmt.Printf("-------------------------------------------\n") fmt.Printf("-------------------------------------------\n")
showMetadata(metadata) showMetadata(metadata)
ch <- SignDataResponse{ui.confirm(), ""} ch <- SignDataResponse{true, ui.readPassword()}
} }
// ApproveExport prompt the user for confirmation to export encrypted account json // ApproveExport prompt the user for confirmation to export encrypted account json
@ -141,7 +157,11 @@ func (ui *CommandlineUI) ApproveImport(request *ImportRequest, metadata Metadata
fmt.Printf("A request has been made to import an encrypted keyfile\n") fmt.Printf("A request has been made to import an encrypted keyfile\n")
fmt.Printf("-------------------------------------------\n") fmt.Printf("-------------------------------------------\n")
showMetadata(metadata) showMetadata(metadata)
ch <- ImportResponse{ui.confirm(), "", ""} if ui.confirm(){
ch <- ImportResponse{true, ui.readPasswordText("Old password"), ui.readPasswordText("New password")}
}else{
ch <- ImportResponse{false, "", ""}
}
} }
// ApproveListing prompt the user for confirmation to list accounts // ApproveListing prompt the user for confirmation to list accounts
@ -177,17 +197,17 @@ func (ui *CommandlineUI) ApproveNewAccount(requst *NewAccountRequest, metadata M
fmt.Printf("Approving this operation means that a new account is created,\n") fmt.Printf("Approving this operation means that a new account is created,\n")
fmt.Printf("and the address show to the caller\n") fmt.Printf("and the address show to the caller\n")
showMetadata(metadata) showMetadata(metadata)
ch <- NewAccountResponse{ui.confirm(), ""} ch <- NewAccountResponse{ui.confirm(), ui.readPassword()}
} }
// ShowError displays error message to user // ShowError displays error message to user
func (ui *CommandlineUI) ShowError(message string) { func (ui *CommandlineUI) ShowError(message string) {
fmt.Printf("ERROR: %v", message) fmt.Printf("ERROR: %v\n", message)
} }
// ShowInfo displays info message to user // ShowInfo displays info message to user
func (ui *CommandlineUI) ShowInfo(message string) { func (ui *CommandlineUI) ShowInfo(message string) {
fmt.Printf("Info: %v", message) fmt.Printf("Info: %v\n", message)
} }

View file

@ -91,8 +91,7 @@ func main() {
c.Int64(utils.NetworkIdFlag.Name), c.Int64(utils.NetworkIdFlag.Name),
c.String("keystore"), c.String("keystore"),
c.Bool(utils.NoUSBFlag.Name), c.Bool(utils.NoUSBFlag.Name),
NewCommandlineUI(), db, NewCommandlineUI(), db)
c.String("auditlog"))
listener net.Listener listener net.Listener
//err error //err error
) )