cmd/signer: Made lowercase json-definitions, added UI-signer test functionality

This commit is contained in:
Martin Holst Swende 2017-12-06 14:40:46 +01:00
parent d0c0b9bacd
commit cd84631194
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
5 changed files with 187 additions and 61 deletions

View file

@ -45,70 +45,73 @@ type SignerAPI struct {
// Metadata about the request
type Metadata struct {
remote string
local string
scheme string
Remote string `json:"remote"`
Local string `json:"local"`
Scheme string `json:"scheme"`
}
// types for the requests/response types
type (
// SignTxRequest contains info about a Transaction to sign
SignTxRequest struct {
Transaction TransactionArg
From common.Address
Callinfo fmt.Stringer
Meta Metadata
Transaction TransactionArg `json:"transaction"`
From common.Address `json:"fromaccount"`
Callinfo fmt.Stringer `json:"call_info"`
Meta Metadata `json:"meta"`
}
// SignTxResponse result from SignTxRequest
SignTxResponse struct {
//The UI may make changes to the TX
Transaction TransactionArg
From common.Address
Approved bool
Password string
Approved bool `json:"approved"`
Password string `json:"password"`
}
// ExportRequest info about query to export accounts
ExportRequest struct {
Address common.Address
Meta Metadata
Address common.Address `json:"address"`
Meta Metadata `json:"meta"`
}
// ExportResponse response to export-request
ExportResponse struct {
Approved bool
Approved bool `json:"approved"`
}
// ImportRequest info about request to import an Account
ImportRequest struct {
Meta Metadata
Meta Metadata `json:"meta"`
}
ImportResponse struct {
Approved bool
OldPassword string
NewPassword string
Approved bool `json:"approved"`
OldPassword string `json:"old_password"`
NewPassword string `json:"new_password"`
}
SignDataRequest struct {
Address common.Address
Rawdata hexutil.Bytes
Message string
Hash hexutil.Bytes
Meta Metadata
Address common.Address `json:"address"`
Rawdata hexutil.Bytes `json:"raw_data"`
Message string `json:"message"`
Hash hexutil.Bytes `json:"hash"`
Meta Metadata `json:"meta"`
}
SignDataResponse struct {
Approved bool
Approved bool `json:"approved"`
Password string
}
NewAccountRequest struct {
Meta Metadata
Meta Metadata `json:"meta"`
}
NewAccountResponse struct {
Approved bool
Password string
Approved bool `json:"approved"`
Password string `json:"password"`
}
ListRequest struct {
Accounts []Account
Meta Metadata
Accounts []Account `json:"accounts"`
Meta Metadata `json:"meta"`
}
ListResponse struct {
Accounts []Account
Accounts []Account `json:"accounts"`
}
Message struct {
Message string `json:"message"`
}
)
@ -186,13 +189,13 @@ func metaData(ctx context.Context) Metadata {
m := Metadata{"NA", "NA", "NA"}
if v := ctx.Value("remote"); v != nil {
m.remote = v.(string)
m.Remote = v.(string)
}
if v := ctx.Value("scheme"); v != nil {
m.scheme = v.(string)
m.Scheme = v.(string)
}
if v := ctx.Value("local"); v != nil {
m.local = v.(string)
m.Local = v.(string)
}
return m
}
@ -463,7 +466,7 @@ 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
// 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, string, keyJSON json.RawMessage) (Account, error) {
func (api *SignerAPI) Import(ctx context.Context, keyJSON json.RawMessage) (Account, error) {
be := api.am.Backends(keystore.KeyStoreType)

View file

@ -90,7 +90,7 @@ func (ui *CommandlineUI) confirm() bool {
}
func showMetadata(metadata Metadata) {
fmt.Printf("Request info:\n\t%v -> %v -> %v\n", metadata.remote, metadata.scheme, metadata.local)
fmt.Printf("Request info:\n\t%v -> %v -> %v\n", metadata.Remote, metadata.Scheme, metadata.Local)
}
// ApproveTx prompt the user for confirmation to request to sign Transaction
@ -100,7 +100,7 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro
weival := request.Transaction.Value
fmt.Printf("--------- Transaction request-------------\n")
fmt.Printf("to: %v\n", request.Transaction.To.Hex())
fmt.Printf("to: %v\n", request.Transaction.To)
fmt.Printf("from: %v\n", request.From.Hex())
fmt.Printf("value: %v wei\n", weival)
if len(request.Transaction.Data) > 0 {
@ -125,7 +125,7 @@ func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest) (SignDataResp
fmt.Printf("-------- Sign data request--------------\n")
fmt.Printf("Account: %x\n", request.Address)
fmt.Printf("message: \n%v\n", request.Message)
fmt.Printf("message: \n%q\n", request.Message)
fmt.Printf("raw data: \n%v\n", request.Rawdata)
fmt.Printf("message hash: %v\n", request.Hash)
fmt.Printf("-------------------------------------------\n")
@ -154,7 +154,7 @@ func (ui *CommandlineUI) ApproveImport(request *ImportRequest) (ImportResponse,
ui.mu.Lock()
defer ui.mu.Unlock()
fmt.Printf("-------- Export Account request--------------\n")
fmt.Printf("-------- Import Account request--------------\n")
fmt.Printf("A request has been made to import an encrypted keyfile\n")
fmt.Printf("-------------------------------------------\n")
showMetadata(request.Meta)

View file

@ -24,7 +24,10 @@ import (
"os"
"path/filepath"
"context"
"encoding/json"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
@ -84,6 +87,10 @@ func main() {
"This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user " +
"interface, and can be used when the signer is started by an external process.",
},
cli.BoolFlag{
Name: "stdio-ui-test",
Usage: "Mechanism to test interface between signer and UI. Requires 'stdio-ui'.",
},
}
app.Action = func(c *cli.Context) error {
@ -136,6 +143,7 @@ func main() {
if err = server.RegisterName("account", api); err != nil {
utils.Fatalf("Could not register signer API: %v", err)
}
//server.ListServices()
// Import from file
if rfile := c.String("requestfile"); rfile != "" {
@ -151,13 +159,63 @@ func main() {
log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint))
cors := []string{"*"}
if c.Bool("stdio-ui-test") {
log.Info("Performing UI test")
go testExternalUI(api)
}
rpc.NewHTTPServer(cors, server).Serve(listener)
return nil
}
app.Run(os.Args)
}
func testExternalUI(api *SignerAPI) {
ctx := context.WithValue(context.Background(), "remote", "signer binary")
ctx = context.WithValue(ctx, "scheme", "in-proc")
ctx = context.WithValue(ctx, "local", "main")
errs := make([]string, 0)
api.ui.ShowInfo("Testing 'ShowInfo'")
api.ui.ShowError("Testing 'ShowError'")
checkErr:= func(method string, err error){
if err != nil && err != ErrRequestDenied{
errs = append(errs, fmt.Sprintf("%v: %v", method, err.Error()))
}
}
var err error
_, err = api.SignTransaction(ctx, common.Address{}, TransactionArg{}, nil);
checkErr("SignTransaction", err)
_, err = api.Sign(ctx, common.Address{}, common.Hex2Bytes("01020304"))
checkErr("Sign", err)
_, err =api.List(ctx)
checkErr("List", err)
_, err = api.New(ctx)
checkErr("New", err)
_, err = api.Export(ctx, common.Address{})
checkErr("Export", err)
_, err = api.Import(ctx, json.RawMessage{})
checkErr("Import", err)
api.ui.ShowInfo("Tests completed")
if len(errs) > 0 {
log.Error("Got errors")
for _, e := range errs {
log.Error(e)
}
}else{
log.Info("No errors")
}
}
// Create Account
// curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_new","params":["test"],"id":67}' localhost:8550

View file

@ -34,60 +34,117 @@ class PipeTransport(ServerTransport):
def receive_message(self):
data = self.input.readline()
print("IN ->\n{}".format( data))
#print(">> {}".format( data))
return None, urlparse.unquote(data)
def send_reply(self, context, reply):
print("OUT <-\n{}".format( reply))
#print("<< {}".format( reply))
self.output.write(reply)
self.output.write("\n")
dispatcher = RPCDispatcher()
@dispatcher.public
def ApproveTx(Transaction = None, From = None, Callinfo = None, Meta = None):
def ApproveTx(transaction = None, fromaccount = None, call_info = None, meta = None):
"""
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}
:param transaction: transaction info
:param call_info: info abou the call, e.g. if ABI info could not be
:param meta: metadata about the request, e.g. where the call comes from
:return:
"""
return {
"Approved" : True,
"Transaction" : Transaction,
"From" : From,
"Password" : None,
"approved" : False,
"transaction" : None,
"fromaccount" : fromaccount,
"password" : None,
}
@dispatcher.public
def ApproveSignData():
return {"Approved": False,
"Password" : None}
def ApproveSignData(address=None, raw_data = None, message = None, hash = None, meta = None):
""" Example request
{"jsonrpc":"2.0","method":"ApproveSignData","params":{"address":"0x0000000000000000000000000000000000000000","raw_data":"0x01020304","message":"\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004","hash":"0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310","meta":{"remote":"signer binary","local":"main","scheme":"in-proc"}},"id":3}
"""
return {"approved": False,
"password" : None}
@dispatcher.public
def ApproveExport():
return {"Approved" : False}
def ApproveExport(address = None, meta = None):
""" Example request
{"jsonrpc":"2.0","method":"ApproveExport","params":{"address":"0x0000000000000000000000000000000000000000","meta":{"remote":"signer binary","local":"main","scheme":"in-proc"}},"id":5}
"""
return {"approved" : False}
@dispatcher.public
def ApproveImport():
return {"Approved" : False, "OldPassword": "", "NewPassword": ""}
def ApproveImport(meta = None):
""" Example request
{"jsonrpc":"2.0","method":"ApproveImport","params":{"Meta":{}},"id":4}
"""
return {"approved" : False, "old_password": "", "new_password": ""}
@dispatcher.public
def ApproveListing():
return []
def ApproveListing(accounts=None, meta = None):
""" Example request
{"jsonrpc":"2.0","method":"ApproveListing","params":{"accounts":[{"type":"Account","url":"keystore:///home/user/ethereum/keystore/file","address":"0x010101010101010010101010101abcdef0001337"}],"Meta":{}},"id":2}
"""
return {'accounts': []}
@dispatcher.public
def ApproveNewAccount():
return {"Approved": False, "Password": ""}
def ApproveNewAccount(meta = None):
"""
Example request
{"jsonrpc":"2.0","method":"ApproveNewAccount","params":{"meta":{"remote":"signer binary","local":"main","scheme":"in-proc"}},"id":5}
:return:
"""
return {"approved": False, "password": ""}
@dispatcher.public
def ShowError(text = ""):
sys.err.println("Error: %s", text)
def ShowError(message = ""):
"""
Example request:
{"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowError'"},"id":1}
:param text: to show
:return: nothing
"""
sys.stderr.write("Error: {}\n".format( message))
return
@dispatcher.public
def ShowInfo(text = ""):
sys.err.println("Info: %s", text)
def ShowInfo(message = ""):
"""
Example request
{"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowInfo'"},"id":0}
:param text: to display
:return:nothing
"""
sys.stdout.write("Info: {}\n".format( message))
return
def main():
def main(args):
cmd = ["./signer", "--stdio-ui"]
if len(args) > 0 and args[0] == "test":
cmd.extend(["--stdio-ui-test"])
print("cmd: {}".format(" ".join(cmd)))
# line buffered
p = subprocess.Popen(["./signer", "--stdio-ui"], bufsize=1, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p = subprocess.Popen(cmd, bufsize=1, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
transport = PipeTransport(p.stdout, p.stdin)
rpc_server = RPCServer(
transport,
@ -97,4 +154,4 @@ def main():
rpc_server.serve_forever()
if __name__ == '__main__':
main()
main(sys.argv[1:])

View file

@ -109,9 +109,17 @@ func (ui StdIOUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountRespo
}
func (ui StdIOUI) ShowError(message string) {
err := ui.dispatch("ShowError", &Message{message}, nil)
if err != nil {
log.Info("Error calling 'ShowError'", "exc", err.Error(),"msg", message)
}
}
func (ui StdIOUI) ShowInfo(message string) {
err := ui.dispatch("ShowInfo", Message{message}, nil)
if err != nil {
log.Info("Error calling 'ShowInfo'", "exc", err.Error(), "msg", message)
}
}
type rwc struct {