pythonsigner.py 5.06 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
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
78
        :return:
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
        """
        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):
160
    cmd = ["clef", "--stdio-ui"]
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
    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:])