cmd/signer: rename fromaccount, update pythonpoc with new json encoding format

This commit is contained in:
Martin Holst Swende 2017-12-22 23:47:14 +01:00
parent c55fd329ae
commit f743cdd437
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
4 changed files with 238 additions and 37 deletions

View file

@ -377,7 +377,175 @@ None
These methods needs to be implemented by a UI listener. These methods needs to be implemented by a UI listener.
still work in progress
By starting the signer with the switch `--stdio-ui-test`, the signer will invoke all known methods, and expect the UI to respond with
denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented.
See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'.
**work in progress**
### ApproveTx
Invoked when there's a transaction for approval.
#### Sample call
```json
{
"jsonrpc": "2.0",
"method": "ApproveTx",
"params": [{
"transaction": {
"to": "0xae967917c465db8578ca9024c205720b1a3651A9",
"gas": "0x333",
"gasPrice": "0x123",
"value": "0x10",
"data": "0xd7a5865800000000000000000000000000000000000000000000000000000000000000ff",
"nonce": "0x0"
},
"fromaccount": "0xAe967917c465db8578ca9024c205720b1a3651A9",
"call_info": "Warning! Could not validate ABI-data against calldata\nSupplied ABI spec does not contain method signature in data: 0xd7a58658",
"meta": {
"remote": "127.0.0.1:34572",
"local": "localhost:8550",
"scheme": "HTTP/1.1"
}
}],
"id": 1
}
```
### ApproveExport
Invoked when a request to export an account has been made.
#### Sample call
```json
{
"jsonrpc": "2.0",
"id": 7,
"method": "ApproveExport",
"params": [
{
"address": "0x0000000000000000000000000000000000000000",
"meta": {
"remote": "signer binary",
"local": "main",
"scheme": "in-proc"
}
}
]
}
```
### ApproveListing
Invoked when a request for account listing has been made.
#### Sample call
```json
{
"jsonrpc": "2.0",
"id": 5,
"method": "ApproveListing",
"params": [
{
"accounts": [
{
"type": "Account",
"url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42",
"address": "0x123409812340981234098123409812deadbeef42"
},
{
"type": "Account",
"url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42",
"address": "0xcafebabedeadbeef34098123409812deadbeef42"
}
],
"meta": {
"remote": "signer binary",
"local": "main",
"scheme": "in-proc"
}
}
]
}
```
### ApproveSignData
#### Sample call
```json
{
"jsonrpc": "2.0",
"id": 4,
"method": "ApproveSignData",
"params": [
{
"address": "0x123409812340981234098123409812deadbeef42",
"raw_data": "0x01020304",
"message": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004",
"hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310",
"meta": {
"remote": "signer binary",
"local": "main",
"scheme": "in-proc"
}
}
]
}
```
### ShowInfo
The UI should show the info to the user. Does not expect response.
#### Sample call
```json
{
"jsonrpc": "2.0",
"id": 9,
"method": "ShowInfo",
"params": [
{
"text": "Tests completed"
}
]
}
```
### ShowError
The UI should show the info to the user. Does not expect response.
```json
{
"jsonrpc": "2.0",
"id": 2,
"method": "ShowError",
"params": [
{
"text": "Testing 'ShowError'"
}
]
}
```
### Rules for UI apis ### Rules for UI apis
@ -398,6 +566,9 @@ A UI should conform to the following rules.
along with the UI. along with the UI.
## TODOs ## TODOs
Some snags and todos Some snags and todos

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"regexp" "regexp"
"reflect"
) )
type decodedArgument struct { type decodedArgument struct {
@ -82,18 +83,29 @@ func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) {
if method == nil { if method == nil {
return nil, fmt.Errorf("Supplied ABI spec does not contain method signature in data: 0x%x", sigdata) return nil, fmt.Errorf("Supplied ABI spec does not contain method signature in data: 0x%x", sigdata)
} }
var v interface{}
method.Inputs.Unpack(v, argdata)
ref := reflect.ValueOf(v)
values := make([]interface{}, ref.NumField())
for i := 0; i < ref.NumField(); i++ {
values[i] = ref.Field(i).Interface()
}
fmt.Println(values)
decoded := decodedCallData{signature: method.Sig(), name: method.Name} decoded := decodedCallData{signature: method.Sig(), name: method.Name}
/*
for n, argument := range method.Inputs { for n, argument := range method.Inputs {
value, err := abi.ToGoType(n*32, argument.Type, argdata)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to decode argument %d (signature %v): %v", n, method.Sig(), err) return nil, fmt.Errorf("Failed to decode argument %d (signature %v): %v", n, method.Sig(), err)
} else { } else {
decodedArg := decodedArgument{ decodedArg := decodedArgument{
soltype: argument, soltype: argument,
value: value, value: reflect.ValueOf(v,) ,
} }
decoded.inputs = append(decoded.inputs, decodedArg) decoded.inputs = append(decoded.inputs, decodedArg)
} }
@ -103,20 +115,19 @@ func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) {
// original data. If we didn't do that, it would e.g. be possible to stuff extra data into the arguments, which // original data. If we didn't do that, it would e.g. be possible to stuff extra data into the arguments, which
// is not detected by merely decoding the data. // is not detected by merely decoding the data.
var ( var (
gotypedArguments = make([]interface{}, len(decoded.inputs))
encoded []byte encoded []byte
) )
for i, arg := range decoded.inputs { encoded, err = method.Inputs.Pack(v)
gotypedArguments[i] = arg.value
}
encoded, err = abispec.Pack(method.Name, gotypedArguments...)
if !bytes.Equal(encoded, calldata) { if !bytes.Equal(encoded, calldata) {
exp := common.Bytes2Hex(encoded) exp := common.Bytes2Hex(encoded)
was := common.Bytes2Hex(calldata) was := common.Bytes2Hex(calldata)
return nil, fmt.Errorf("WARNING: Supplied data is stuffed with extra data. \nWant %s\nHave %s\nfor method %v", exp, was, method.Sig()) return nil, fmt.Errorf("WARNING: Supplied data is stuffed with extra data. \nWant %s\nHave %s\nfor method %v", exp, was, method.Sig())
} }
*/
return &decoded, nil return &decoded, nil
} }

View file

@ -26,6 +26,8 @@ import (
"bytes" "bytes"
"reflect"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/accounts/usbwallet" "github.com/ethereum/go-ethereum/accounts/usbwallet"
@ -56,7 +58,7 @@ type (
// SignTxRequest contains info about a Transaction to sign // SignTxRequest contains info about a Transaction to sign
SignTxRequest struct { SignTxRequest struct {
Transaction TransactionArg `json:"transaction"` Transaction TransactionArg `json:"transaction"`
From common.MixedcaseAddress `json:"fromaccount"` From common.MixedcaseAddress `json:"from"`
Callinfo string `json:"call_info"` Callinfo string `json:"call_info"`
Meta Metadata `json:"meta"` Meta Metadata `json:"meta"`
} }
@ -64,7 +66,7 @@ type (
SignTxResponse struct { SignTxResponse struct {
//The UI may make changes to the TX //The UI may make changes to the TX
Transaction TransactionArg `json:"transaction"` Transaction TransactionArg `json:"transaction"`
From common.MixedcaseAddress `json:"fromaccount"` From common.MixedcaseAddress `json:"from"`
Approved bool `json:"approved"` Approved bool `json:"approved"`
Password string `json:"password"` Password string `json:"password"`
} }
@ -260,7 +262,7 @@ func logDiff(original *SignTxRequest, new *SignTxResponse) bool {
modified = true modified = true
log.Info("Sender-account changed by UI", "was", f0, "is", f1) log.Info("Sender-account changed by UI", "was", f0, "is", f1)
} }
if t0, t1 := original.Transaction.To, new.Transaction.To; t0 != t1 { if t0, t1 := original.Transaction.To, new.Transaction.To; !reflect.DeepEqual(t0, t1) {
log.Info("Recipient-account changed by UI", "was", t0, "is", t1) log.Info("Recipient-account changed by UI", "was", t0, "is", t1)
modified = true modified = true
} }

View file

@ -48,71 +48,87 @@ class StdIOHandler():
pass pass
@public @public
def ApproveTx(self,transaction = None, fromaccount = None, call_info = None, meta = None): def ApproveTx(self,req):
""" """
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": "0xae967917c465db8578ca9024c205720b1a3651A9",
"gas": "0x333",
"gasPrice": "0x123",
"value": "0x10",
"data": "0xd7a5865800000000000000000000000000000000000000000000000000000000000000ff",
"nonce": "0x0"
},
"from": "0xAe967917c465db8578ca9024c205720b1a3651A9",
"call_info": "Warning! Could not validate ABI-data against calldata\nSupplied ABI spec does not contain method signature in data: 0xd7a58658",
"meta": {
"remote": "127.0.0.1:34572",
"local": "localhost:8550",
"scheme": "HTTP/1.1"
}
}],
"id": 1
}
: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
:param meta: metadata about the request, e.g. where the call comes from :param meta: metadata about the request, e.g. where the call comes from
:return: :return:
""" """
transaction = req.get('transaction')
_from = req.get('from')
call_info = req.get('call_info')
meta = req.get('meta')
return { return {
"approved" : False, "approved" : False,
"transaction" : None, "transaction" : transaction,
#"fromaccount" : fromaccount, "from" : _from,
"password" : None, # "password" : None,
} }
@public @public
def ApproveSignData(self,address=None, raw_data = None, message = None, hash = None, meta = None): def ApproveSignData(self, req):
""" Example request """ 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, return {"approved": False, "password" : None}
"password" : None}
@public @public
def ApproveExport(self,address = None, meta = None): def ApproveExport(self, req):
""" Example request """ Example request
{"jsonrpc":"2.0","method":"ApproveExport","params":{"address":"0x0000000000000000000000000000000000000000","meta":{"remote":"signer binary","local":"main","scheme":"in-proc"}},"id":5}
""" """
return {"approved" : False} return {"approved" : False}
@public @public
def ApproveImport(self,meta = None): def ApproveImport(self, req):
""" Example request """ Example request
{"jsonrpc":"2.0","method":"ApproveImport","params":{"Meta":{}},"id":4}
""" """
return { "approved" : False, "old_password": "", "new_password": ""} return { "approved" : False, "old_password": "", "new_password": ""}
@public @public
def ApproveListing(self,accounts=None, meta = None): def ApproveListing(self, req):
""" Example request """ 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': []} return {'accounts': []}
@public @public
def ApproveNewAccount(self,meta = None): def ApproveNewAccount(self, req):
""" """
Example request Example request
{"jsonrpc":"2.0","method":"ApproveNewAccount","params":{"meta":{"remote":"signer binary","local":"main","scheme":"in-proc"}},"id":5}
:return: :return:
""" """
return {"approved": False, "password": ""} return {"approved": False,
#"password": ""
}
@public @public
def ShowError(self,message = {}): def ShowError(self,message = {}):
@ -137,6 +153,7 @@ class StdIOHandler():
:param message: to display :param message: to display
:return:nothing :return:nothing
""" """
if 'text' in message.keys(): if 'text' in message.keys():
sys.stdout.write("Error: {}\n".format( message['text'])) sys.stdout.write("Error: {}\n".format( message['text']))
return return