* cmd/clef, signer: refresh tutorial, fix noticed issues * cmd/clef, signer: support removing stored keys (delpw + rules) * cmd/clef: polishes + Geth integration in the tutorial
		
			
				
	
	
		
			178 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			178 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:])
 |