560305f601
- uses newer version of go-ethereum required for go1.11
180 lines
5.1 KiB
Python
180 lines
5.1 KiB
Python
import os,sys, subprocess
|
|
from tinyrpc.transports import ServerTransport
|
|
from tinyrpc.protocols.jsonrpc import JSONRPCProtocol
|
|
from tinyrpc.dispatch import public,RPCDispatcher
|
|
from tinyrpc.server import RPCServer
|
|
|
|
""" This is a POC example of how to write a custom UI for Clef. The UI starts the
|
|
clef process with the '--stdio-ui' option, and communicates with clef using standard input / output.
|
|
|
|
The standard input/output is a relatively secure way to communicate, as it does not require opening any ports
|
|
or IPC files. Needless to say, it does not protect against memory inspection mechanisms where an attacker
|
|
can access process memory."""
|
|
|
|
try:
|
|
import urllib.parse as urlparse
|
|
except ImportError:
|
|
import urllib as urlparse
|
|
|
|
class StdIOTransport(ServerTransport):
|
|
""" Uses std input/output for RPC """
|
|
def receive_message(self):
|
|
return None, urlparse.unquote(sys.stdin.readline())
|
|
|
|
def send_reply(self, context, reply):
|
|
print(reply)
|
|
|
|
class PipeTransport(ServerTransport):
|
|
""" Uses std a pipe for RPC """
|
|
|
|
def __init__(self,input, output):
|
|
self.input = input
|
|
self.output = output
|
|
|
|
def receive_message(self):
|
|
data = self.input.readline()
|
|
print(">> {}".format( data))
|
|
return None, urlparse.unquote(data)
|
|
|
|
def send_reply(self, context, reply):
|
|
print("<< {}".format( reply))
|
|
self.output.write(reply)
|
|
self.output.write("\n")
|
|
|
|
class StdIOHandler():
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
@public
|
|
def ApproveTx(self,req):
|
|
"""
|
|
Example request:
|
|
{
|
|
"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 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:
|
|
"""
|
|
transaction = req.get('transaction')
|
|
_from = req.get('from')
|
|
call_info = req.get('call_info')
|
|
meta = req.get('meta')
|
|
|
|
return {
|
|
"approved" : False,
|
|
#"transaction" : transaction,
|
|
# "from" : _from,
|
|
# "password" : None,
|
|
}
|
|
|
|
@public
|
|
def ApproveSignData(self, req):
|
|
""" Example request
|
|
|
|
"""
|
|
return {"approved": False, "password" : None}
|
|
|
|
@public
|
|
def ApproveExport(self, req):
|
|
""" Example request
|
|
|
|
"""
|
|
return {"approved" : False}
|
|
|
|
@public
|
|
def ApproveImport(self, req):
|
|
""" Example request
|
|
|
|
"""
|
|
return { "approved" : False, "old_password": "", "new_password": ""}
|
|
|
|
@public
|
|
def ApproveListing(self, req):
|
|
""" Example request
|
|
|
|
"""
|
|
return {'accounts': []}
|
|
|
|
@public
|
|
def ApproveNewAccount(self, req):
|
|
"""
|
|
Example request
|
|
|
|
:return:
|
|
"""
|
|
return {"approved": False,
|
|
#"password": ""
|
|
}
|
|
|
|
@public
|
|
def ShowError(self,message = {}):
|
|
"""
|
|
Example request:
|
|
|
|
{"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowError'"},"id":1}
|
|
|
|
:param message: to show
|
|
:return: nothing
|
|
"""
|
|
if 'text' in message.keys():
|
|
sys.stderr.write("Error: {}\n".format( message['text']))
|
|
return
|
|
|
|
@public
|
|
def ShowInfo(self,message = {}):
|
|
"""
|
|
Example request
|
|
{"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowInfo'"},"id":0}
|
|
|
|
:param message: to display
|
|
:return:nothing
|
|
"""
|
|
|
|
if 'text' in message.keys():
|
|
sys.stdout.write("Error: {}\n".format( message['text']))
|
|
return
|
|
|
|
def main(args):
|
|
|
|
cmd = ["./clef", "--stdio-ui"]
|
|
if len(args) > 0 and args[0] == "test":
|
|
cmd.extend(["--stdio-ui-test"])
|
|
print("cmd: {}".format(" ".join(cmd)))
|
|
dispatcher = RPCDispatcher()
|
|
dispatcher.register_instance(StdIOHandler(), '')
|
|
# line buffered
|
|
p = subprocess.Popen(cmd, bufsize=1, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
|
|
rpc_server = RPCServer(
|
|
PipeTransport(p.stdout, p.stdin),
|
|
JSONRPCProtocol(),
|
|
dispatcher
|
|
)
|
|
rpc_server.serve_forever()
|
|
|
|
if __name__ == '__main__':
|
|
main(sys.argv[1:])
|