cmd/signer: rework json communication

This commit is contained in:
Martin Holst Swende 2017-12-16 10:38:21 +01:00
parent 92ba02a8d5
commit 2ccae4ffec
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
4 changed files with 74 additions and 20 deletions

View file

@ -404,17 +404,11 @@ Some snags and todos
checksum, since the addresses are common.Address, and not String. This should be changed upstream, checksum, since the addresses are common.Address, and not String. This should be changed upstream,
so that they are some more complex form with both common.Address and the original string (?) so that they are some more complex form with both common.Address and the original string (?)
* The JSON communication relies on an external library, which has not been vendored in. We could probably
reuse the jsoncodec by adding some fields for clientcodec, but it's a bit messy.
* The audit-log perhaps leave some things to be desired. I have not found a perfect way to save an audit log of events. * The audit-log perhaps leave some things to be desired. I have not found a perfect way to save an audit log of events.
* Some more fields should be added to calldata, e.g http-header `Origin`. * Some more fields should be added to calldata, e.g http-header `Origin`.
* The signer should also generate the identicon for the addresses, and pass to the UI as a base64 string.
* based on https://github.com/ethereum/blockies
* Maybe actually need to be in the UI, at least in UI:s allowing the user to change the address
* The signer should take a startup param "--no-change", for UI:s that do not contain the capability * The signer should take a startup param "--no-change", for UI:s that do not contain the capability
to perform changes to things, only approve/deny. Such a UI should be able to start the signer in to perform changes to things, only approve/deny. Such a UI should be able to start the signer in
a more secure mode by telling it that it only wants approve/deny capabilities. a more secure mode by telling it that it only wants approve/deny capabilities.
@ -438,4 +432,13 @@ invoking methods with the following info:
* The signer should pass the `Origin` header as call-info to the UI. As of right now, the way that info about the request is * The signer should pass the `Origin` header as call-info to the UI. As of right now, the way that info about the request is
put together is a bit of a hack into the http server. This could probably be greatly improved put together is a bit of a hack into the http server. This could probably be greatly improved
* The signer * Geth relay
- Geth should be started in `geth --external_signer localhost:8550`.
* Wallets / accounts. Add API methods for wallets.
* Rules. In the future, it should be possible to specify rules, e.g. "Allow sending up to 1 eth per day to contract Y". Two problems:
* There needs to be a very good structure around rules. Either a full language (lua/js) or a limited but flexible syntax based on e.g. json/yaml. Kind of like a firewall ruleset.
* This implies that the signer would remember passwords, which is very problematic. However, a good UI implementation will want these things,
and it would be better to implement it once in the signer, than having UI:s develop their own remember-password logic.

View file

@ -49,7 +49,7 @@ def ApproveTx(transaction = None, fromaccount = None, call_info = None, meta = N
""" """
Example request: Example request:
{"jsonrpc":"2.0","method":"ApproveTx","params":{"transaction":{"to":null,"gas":null,"gasPrice":null,"value":null,"data":"0x","nonce":null},"from":"0x0000000000000000000000000000000000000000","call_info":null,"meta":{"remote":"signer binary","local":"main","scheme":"in-proc"}},"id":2} {"jsonrpc":"2.0","method":"ApproveTx","params":{"transaction":{"to":null,"gas":null,"gasPrice":null,"value":null,"data":"0x","nonce":null},"fromaccount":"0x0000000000000000000000000000000000000000","call_info":null,"meta":{"remote":"signer binary","local":"main","scheme":"in-proc"}},"id":2}
:param transaction: transaction info :param transaction: transaction info
:param call_info: info abou the call, e.g. if ABI info could not be :param call_info: info abou the call, e.g. if ABI info could not be
@ -59,7 +59,7 @@ def ApproveTx(transaction = None, fromaccount = None, call_info = None, meta = N
return { return {
"approved" : False, "approved" : False,
"transaction" : None, "transaction" : None,
"fromaccount" : fromaccount, # "fromaccount" : fromaccount,
"password" : None, "password" : None,
} }

View file

@ -18,26 +18,32 @@
package main package main
import ( import (
"bufio"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/powerman/rpc-codec/jsonrpc2"
"github.com/ethereum/go-ethereum/rpc"
"io" "io"
"os" "os"
"sync" "sync"
"context"
) )
type StdIOUI struct { type StdIOUI struct {
client *jsonrpc2.Client //client *jsonrpc2.Client
client rpc.Client
// codec rpc.ClientCodec // codec rpc.ClientCodec
mu sync.Mutex mu sync.Mutex
} }
func NewStdIOUI() *StdIOUI { func NewStdIOUI() *StdIOUI {
in, out := bufio.NewReader(os.Stdin), os.Stdout // in, out := bufio.NewReader(os.Stdin), os.Stdout
client, err := rpc.DialContext(context.Background(), "stdio://")
if err != nil {
log.Crit("Could not create stdio client", "err", err)
}
return &StdIOUI{client: *client}
//return &StdIOUI{client: jsonrpc2.NewClient(&rwc{in, out})}
//codec := rpc2.NewJSONCodec()
return &StdIOUI{client: jsonrpc2.NewClient(&rwc{in, out})}
//return &StdIOUI{}
} }
func (ui StdIOUI) dispatch(serviceMethod string, args interface{}, reply interface{}) error { func (ui StdIOUI) dispatch(serviceMethod string, args interface{}, reply interface{}) error {
@ -50,10 +56,12 @@ func (ui StdIOUI) dispatch(serviceMethod string, args interface{}, reply interfa
// in, out := bufio.NewReader(os.Stdin), os.Stdout // in, out := bufio.NewReader(os.Stdin), os.Stdout
// codec := jsonrpc.NewClientCodec(&rwc{in, out}) // codec := jsonrpc.NewClientCodec(&rwc{in, out})
// c := rpc.NewClientWithCodec(codec) // c := rpc.NewClientWithCodec(codec)
// return c.Call(serviceMethod, args, &reply) // return c.Call(serviceMethod, args, &reply)
log.Info("Writing to client")
err := ui.client.Call(serviceMethod, args, &reply) err := ui.client.Call(&reply, serviceMethod, args)
log.Info("Writing to client done")
// err := ui.client.Call(serviceMethod, args, &reply)
if err != nil { if err != nil {
log.Info("Error", "exc", err.Error()) log.Info("Error", "exc", err.Error())
} }

View file

@ -33,6 +33,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"os"
) )
var ( var (
@ -171,6 +172,8 @@ func DialContext(ctx context.Context, rawurl string) (*Client, error) {
return DialHTTP(rawurl) return DialHTTP(rawurl)
case "ws", "wss": case "ws", "wss":
return DialWebsocket(ctx, rawurl, "") return DialWebsocket(ctx, rawurl, "")
case "stdio":
return DialStdIO(ctx)
case "": case "":
return DialIPC(ctx, rawurl) return DialIPC(ctx, rawurl)
default: default:
@ -178,6 +181,46 @@ func DialContext(ctx context.Context, rawurl string) (*Client, error) {
} }
} }
type StdIOConn struct{}
func (io StdIOConn) Read(b []byte) (n int, err error) {
return os.Stdin.Read(b)
}
func (io StdIOConn) Write(b []byte) (n int, err error) {
return os.Stdout.Write(b)
}
func (io StdIOConn) Close() error {
return nil
}
func (io StdIOConn) LocalAddr() net.Addr {
return &net.UnixAddr{Name: "stdio", Net: "stdio"}
}
func (io StdIOConn) RemoteAddr() net.Addr {
return &net.UnixAddr{Name: "stdio", Net: "stdio"}
}
func (io StdIOConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (io StdIOConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (io StdIOConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func DialStdIO(ctx context.Context) (*Client, error) {
return newClient(ctx, func(_ context.Context) (net.Conn, error) {
return StdIOConn{}, nil
})
}
func newClient(initctx context.Context, connectFunc func(context.Context) (net.Conn, error)) (*Client, error) { func newClient(initctx context.Context, connectFunc func(context.Context) (net.Conn, error)) (*Client, error) {
conn, err := connectFunc(initctx) conn, err := connectFunc(initctx)
if err != nil { if err != nil {