From d531223d11f3798d190935cde20d657d02496cc6 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 19 Feb 2022 16:57:08 +0100 Subject: [PATCH 1/3] cmd/clef: fixups to the python clef poc --- cmd/clef/pythonsigner.py | 182 +++++++++++++++++++++++++-------------- 1 file changed, 116 insertions(+), 66 deletions(-) diff --git a/cmd/clef/pythonsigner.py b/cmd/clef/pythonsigner.py index 315aabd73fdb..d26277647335 100644 --- a/cmd/clef/pythonsigner.py +++ b/cmd/clef/pythonsigner.py @@ -37,40 +37,36 @@ def receive_message(self): return None, urlparse.unquote(data) def send_reply(self, context, reply): + reply = str(reply, "utf-8") print("<< {}".format( reply)) - self.output.write(reply) - self.output.write("\n") + self.output.write("{}\n".format(reply)) + +def sanitize(txt, limit=100): + return txt[:limit].encode("unicode_escape").decode("utf-8") + +def metaString(meta): + """ + "meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""} + """ + return """ + Request context: + {} -> {} -> {} + Additional HTTP header data, provided by the external caller: + User-Agent: {} + Origin: {} +""".format( meta.get('remote', ""), meta.get('scheme',''), meta.get('local', ''), + sanitize(meta.get("User-Agent"), 200), sanitize(meta.get("Origin"),100)) class StdIOHandler(): def __init__(self): pass @public - def ApproveTx(self,req): + 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 - } + + {"jsonrpc":"2.0","id":20,"method":"ui_approveTx","params":[{"transaction":{"from":"0xDEADbEeF000000000000000000000000DeaDbeEf","to":"0xDEADbEeF000000000000000000000000DeaDbeEf","gas":"0x3e8","gasPrice":"0x5","maxFeePerGas":null,"maxPriorityFeePerGas":null,"value":"0x6","nonce":"0x1","data":"0x"},"call_info":null,"meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""}}]} :param transaction: transaction info :param call_info: info abou the call, e.g. if ABI info could not be @@ -78,83 +74,136 @@ def ApproveTx(self,req): :return: """ transaction = req.get('transaction') - _from = req.get('from') - call_info = req.get('call_info') - meta = req.get('meta') + _from = transaction.get('from', "") + to = transaction.get('to', "") + sys.stdout.write("""Sign transaction request: + {} + + From: {} + To: {} + + Auto-rejecting request +""".format(metaString(req.get('meta',{})), _from, to )) - return { - "approved" : False, - #"transaction" : transaction, - # "from" : _from, -# "password" : None, - } + return {"approved" : False} @public - def ApproveSignData(self, req): + def approveSignData(self, req): """ Example request + {"jsonrpc":"2.0","id":8,"method":"ui_approveSignData","params":[{"content_type":"application/x-clique-header","address":"0x0011223344556677889900112233445566778899","raw_data":"+QIRoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIIFOYIFOYIFOoIFOoIFOppFeHRyYSBkYXRhIEV4dHJhIGRhdGEgRXh0cqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAA==","messages":[{"name":"Clique header","value":"clique header 1337 [0x44381ab449d77774874aca34634cb53bc21bd22aef2d3d4cf40e51176cb585ec]","type":"clique"}],"call_info":null,"hash":"0xa47ab61438a12a06c81420e308c2b7aae44e9cd837a5df70dd021421c0f58643","meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""}}]} """ + contentType = req.get('content_type') + address = req.get('address') + rawData = req.get('raw_data') + contentType = req.get('content_type') + hash = req.get('hash') + meta = req.get('meta', {}) + sys.stdout.write("""Sign data request: + {} + + Content-type: {} + Address: {} + Hash: {} + + Auto-rejecting request +""".format(metaString(meta), contentType, address, hash )) + return {"approved": False, "password" : None} @public - def ApproveExport(self, req): + def approveNewAccount(self, req): """ Example request - + {"jsonrpc":"2.0","id":25,"method":"ui_approveNewAccount","params":[{"meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""}}]} """ - return {"approved" : False} + meta = req.get('meta', {}) + sys.stdout.write("""Create new account request: + {} + + Auto-rejecting request +""".format(metaString(meta) )) + return {"approved": False} @public - def ApproveImport(self, req): - """ Example request + def showError(self,req): + """ + Example request + {"jsonrpc":"2.0","method":"ui_showError","params":[{"text":"If you see this message, enter 'yes' to the next question"}]} + :param message: to display + :return:nothing """ - return { "approved" : False, "old_password": "", "new_password": ""} + text = req.get('text') + sys.stdout.write("Error: {}\n".format(text) ) + sys.stdout.write("Press enter to continue\n") + input() + return @public - def ApproveListing(self, req): - """ Example request + def showInfo(self,req): + """ + Example request + {"jsonrpc":"2.0","method":"ui_showInfo","params":[{"text":"If you see this message, enter 'yes' to next question"}]} + :param message: to display + :return:nothing """ - return {'accounts': []} + text = req.get('text') + sys.stdout.write("Info: {}\n".format(text) ) + sys.stdout.write("Press enter to continue\n") + input() + return @public - def ApproveNewAccount(self, req): + def onSignerStartup(self,req): """ Example request - - :return: + {"jsonrpc":"2.0", + "method":"ui_onSignerStartup", + "params":[{"info":{"extapi_http":"n/a","extapi_ipc":"/home/user/.clef/clef.ipc","extapi_version":"6.1.0","intapi_version":"7.0.1"}}]} """ - return {"approved": False, - #"password": "" - } + info = req.get('info') + http = info.get('extapi_http') + ipc = info.get('extapi_ipc') + extVer = info.get('extapi_version') + intVer = info.get('intapi_version') + sys.stdout.write(""" + Ext api url:{} + Int api ipc: {} + Ext api ver: {} + Int api ver: {} +""".format(http, ipc, extVer, intVer)) @public - def ShowError(self,message = {}): + def approveListing(self,req): """ - Example request: - - {"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowError'"},"id":1} - - :param message: to show - :return: nothing + {"jsonrpc":"2.0","id":23,"method":"ui_approveListing","params":[{"accounts":[{"address":... """ - if 'text' in message.keys(): - sys.stderr.write("Error: {}\n".format( message['text'])) - return + accounts = req.get('accounts',[]) + addrs = [x.get("address") for x in accounts] + + sys.stdout.write("\n## Account listing request\n\tDo you want to allow listing the following accounts?\n\t-{}\n\n->" + .format( "\n\t-".join(addrs))) + sys.stdout.write("Auto-answering No\n") + return {} @public - def ShowInfo(self,message = {}): + def onInputRequired(self,req): """ Example request - {"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowInfo'"},"id":0} + {"jsonrpc":"2.0","id":1,"method":"ui_onInputRequired","params":[{"title":"Master Password","prompt":"Please enter the password to decrypt the master seed","isPassword":true}]} :param message: to display :return:nothing """ + title = req.get('title') + isPassword = req.get("isPassword") + prompt = req.get('prompt') + sys.stdout.write("\n## {}\n\t{}\n\n> ".format( title, prompt)) + if not isPassword: + return { "text": input()} - if 'text' in message.keys(): - sys.stdout.write("Error: {}\n".format( message['text'])) - return + return "" def main(args): cmd = ["clef", "--stdio-ui"] @@ -162,7 +211,8 @@ def main(args): cmd.extend(["--stdio-ui-test"]) print("cmd: {}".format(" ".join(cmd))) dispatcher = RPCDispatcher() - dispatcher.register_instance(StdIOHandler(), '') + dispatcher.register_instance(StdIOHandler(), 'ui_') + # line buffered p = subprocess.Popen(cmd, bufsize=1, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) From dc6fd487fdc3f2736e288bddc50db6cce40494b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Vysok=C3=BD?= Date: Mon, 2 May 2022 13:42:12 +0200 Subject: [PATCH 2/3] Pyclef pythonsigner.py upgrades (#29) * black auto-formatter applied * file to define python requirements * flake8 applied, long json lines left untouched * ignore flake on long docstrings * unused variable * indent output message text strings --- cmd/clef/pythonsigner.py | 317 ++++++++++++++++++++++++-------------- cmd/clef/requirements.txt | 1 + 2 files changed, 202 insertions(+), 116 deletions(-) create mode 100644 cmd/clef/requirements.txt diff --git a/cmd/clef/pythonsigner.py b/cmd/clef/pythonsigner.py index d26277647335..9892580f49c4 100644 --- a/cmd/clef/pythonsigner.py +++ b/cmd/clef/pythonsigner.py @@ -1,68 +1,90 @@ -import os,sys, subprocess +import sys +import subprocess + from tinyrpc.transports import ServerTransport from tinyrpc.protocols.jsonrpc import JSONRPCProtocol -from tinyrpc.dispatch import public,RPCDispatcher +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. +""" +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. + +To make this work install all the requirements: -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.""" + pip install -r requirements.txt +""" try: import urllib.parse as urlparse except ImportError: import urllib as urlparse + class StdIOTransport(ServerTransport): - """ Uses std input/output for RPC """ + """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 """ + """Uses std a pipe for RPC""" - def __init__(self,input, output): + def __init__(self, input, output): self.input = input self.output = output def receive_message(self): data = self.input.readline() - print(">> {}".format( data)) + print(">> {}".format(data)) return None, urlparse.unquote(data) def send_reply(self, context, reply): reply = str(reply, "utf-8") - print("<< {}".format( reply)) + print("<< {}".format(reply)) self.output.write("{}\n".format(reply)) + def sanitize(txt, limit=100): return txt[:limit].encode("unicode_escape").decode("utf-8") + def metaString(meta): """ "meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""} - """ - return """ - Request context: - {} -> {} -> {} - Additional HTTP header data, provided by the external caller: - User-Agent: {} - Origin: {} -""".format( meta.get('remote', ""), meta.get('scheme',''), meta.get('local', ''), - sanitize(meta.get("User-Agent"), 200), sanitize(meta.get("Origin"),100)) - -class StdIOHandler(): + """ # noqa: E501 + message = ( + "\tRequest context:\n" + "\t\t{remote} -> {scheme} -> {local}\n" + "\tAdditional HTTP header data, provided by the external caller:\n" + "\t\tUser-Agent: {user_agent}\n" + "\t\tOrigin: {origin}\n" + ) + return message.format( + remote=meta.get("remote", ""), + scheme=meta.get("scheme", ""), + local=meta.get("local", ""), + user_agent=sanitize(meta.get("User-Agent"), 200), + origin=sanitize(meta.get("Origin"), 100), + ) + + +class StdIOHandler: def __init__(self): pass @public - def approveTx(self,req): + def approveTx(self, req): """ Example request: @@ -72,156 +94,219 @@ def approveTx(self,req): :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 = transaction.get('from', "") - to = transaction.get('to', "") - sys.stdout.write("""Sign transaction request: - {} - - From: {} - To: {} - - Auto-rejecting request -""".format(metaString(req.get('meta',{})), _from, to )) - - return {"approved" : False} + """ # noqa: E501 + message = ( + "Sign transaction request:\n" + "\t{meta_string}\n" + "\n" + "\tFrom: {from_}\n" + "\tTo: {to}\n" + "\n" + "\tAuto-rejecting request" + ) + meta = req.get("meta", {}) + transaction = req.get("transaction") + sys.stdout.write( + message.format( + meta_string=metaString(meta), + from_=transaction.get("from", ""), + to=transaction.get("to", ""), + ) + ) + return { + "approved": False, + } @public def approveSignData(self, req): - """ Example request + """ + Example request: {"jsonrpc":"2.0","id":8,"method":"ui_approveSignData","params":[{"content_type":"application/x-clique-header","address":"0x0011223344556677889900112233445566778899","raw_data":"+QIRoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIIFOYIFOYIFOoIFOoIFOppFeHRyYSBkYXRhIEV4dHJhIGRhdGEgRXh0cqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAA==","messages":[{"name":"Clique header","value":"clique header 1337 [0x44381ab449d77774874aca34634cb53bc21bd22aef2d3d4cf40e51176cb585ec]","type":"clique"}],"call_info":null,"hash":"0xa47ab61438a12a06c81420e308c2b7aae44e9cd837a5df70dd021421c0f58643","meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""}}]} - """ - contentType = req.get('content_type') - address = req.get('address') - rawData = req.get('raw_data') - contentType = req.get('content_type') - hash = req.get('hash') - meta = req.get('meta', {}) - sys.stdout.write("""Sign data request: - {} - - Content-type: {} - Address: {} - Hash: {} - - Auto-rejecting request -""".format(metaString(meta), contentType, address, hash )) - - return {"approved": False, "password" : None} + """ # noqa: E501 + message = ( + "Sign data request:\n" + "\t{meta_string}\n" + "\n" + "\tContent-type: {content_type}\n" + "\tAddress: {address}\n" + "\tHash: {hash_}\n" + "\n" + "\tAuto-rejecting request\n" + ) + meta = req.get("meta", {}) + sys.stdout.write( + message.format( + meta_string=metaString(meta), + content_type=req.get("content_type"), + address=req.get("address"), + hash_=req.get("hash"), + ) + ) + + return { + "approved": False, + "password": None, + } @public def approveNewAccount(self, req): - """ Example request - {"jsonrpc":"2.0","id":25,"method":"ui_approveNewAccount","params":[{"meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""}}]} """ - meta = req.get('meta', {}) - sys.stdout.write("""Create new account request: - {} - - Auto-rejecting request -""".format(metaString(meta) )) - return {"approved": False} + Example request: + + {"jsonrpc":"2.0","id":25,"method":"ui_approveNewAccount","params":[{"meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""}}]} + """ # noqa: E501 + message = ( + "Create new account request:\n" + "\t{meta_string}\n" + "\n" + "\tAuto-rejecting request\n" + ) + meta = req.get("meta", {}) + sys.stdout.write(message.format(meta_string=metaString(meta))) + return { + "approved": False, + } @public - def showError(self,req): + def showError(self, req): """ - Example request + Example request: + {"jsonrpc":"2.0","method":"ui_showError","params":[{"text":"If you see this message, enter 'yes' to the next question"}]} :param message: to display :return:nothing - """ - text = req.get('text') - sys.stdout.write("Error: {}\n".format(text) ) - sys.stdout.write("Press enter to continue\n") + """ # noqa: E501 + message = ( + "Error: {text}\n" + "Press enter to continue\n" + ) + text = req.get("text") + sys.stdout.write(message.format(text)) input() return @public - def showInfo(self,req): + def showInfo(self, req): """ - Example request + Example request: + {"jsonrpc":"2.0","method":"ui_showInfo","params":[{"text":"If you see this message, enter 'yes' to next question"}]} :param message: to display :return:nothing - """ - text = req.get('text') - sys.stdout.write("Info: {}\n".format(text) ) - sys.stdout.write("Press enter to continue\n") + """ # noqa: E501 + message = ( + "Info: {text}\n" + "Press enter to continue\n" + ) + text = req.get("text") + sys.stdout.write(message.format(text)) input() return @public - def onSignerStartup(self,req): - """ - Example request - {"jsonrpc":"2.0", - "method":"ui_onSignerStartup", - "params":[{"info":{"extapi_http":"n/a","extapi_ipc":"/home/user/.clef/clef.ipc","extapi_version":"6.1.0","intapi_version":"7.0.1"}}]} + def onSignerStartup(self, req): """ - info = req.get('info') - http = info.get('extapi_http') - ipc = info.get('extapi_ipc') - extVer = info.get('extapi_version') - intVer = info.get('intapi_version') - sys.stdout.write(""" - Ext api url:{} - Int api ipc: {} - Ext api ver: {} - Int api ver: {} -""".format(http, ipc, extVer, intVer)) + Example request: + + {"jsonrpc":"2.0", "method":"ui_onSignerStartup", "params":[{"info":{"extapi_http":"n/a","extapi_ipc":"/home/user/.clef/clef.ipc","extapi_version":"6.1.0","intapi_version":"7.0.1"}}]} + """ # noqa: E501 + message = ( + "\n" + "\t\tExt api url: {extapi_http}\n" + "\t\tInt api ipc: {extapi_ipc}\n" + "\t\tExt api ver: {extapi_version}\n" + "\t\tInt api ver: {intapi_version}\n" + ) + info = req.get("info") + sys.stdout.write( + message.format( + extapi_http=info.get("extapi_http"), + extapi_ipc=info.get("extapi_ipc"), + extapi_version=info.get("extapi_version"), + intapi_version=info.get("intapi_version"), + ) + ) @public - def approveListing(self,req): - """ - {"jsonrpc":"2.0","id":23,"method":"ui_approveListing","params":[{"accounts":[{"address":... + def approveListing(self, req): """ - accounts = req.get('accounts',[]) - addrs = [x.get("address") for x in accounts] + Example request: - sys.stdout.write("\n## Account listing request\n\tDo you want to allow listing the following accounts?\n\t-{}\n\n->" - .format( "\n\t-".join(addrs))) - sys.stdout.write("Auto-answering No\n") + {"jsonrpc":"2.0","id":23,"method":"ui_approveListing","params":[{"accounts":[{"address":... + """ # noqa: E501 + message = ( + "\n" + "## Account listing request\n" + "\tDo you want to allow listing the following accounts?\n" + "\t-{addrs}\n" + "\n" + "->Auto-answering No\n" + ) + accounts = req.get("accounts", []) + addrs = [x.get("address") for x in accounts] + sys.stdout.write( + message.format( + addrs="\n\t-".join(addrs) + ) + ) return {} @public - def onInputRequired(self,req): + def onInputRequired(self, req): """ - Example request + Example request: + {"jsonrpc":"2.0","id":1,"method":"ui_onInputRequired","params":[{"title":"Master Password","prompt":"Please enter the password to decrypt the master seed","isPassword":true}]} :param message: to display :return:nothing - """ - title = req.get('title') + """ # noqa: E501 + message = ( + "\n" + "## {title}\n" + "\t{prompt}\n" + "\n" + "> " + ) + sys.stdout.write( + message.format( + title=req.get("title") + prompt=req.get("prompt") + ) + ) isPassword = req.get("isPassword") - prompt = req.get('prompt') - sys.stdout.write("\n## {}\n\t{}\n\n> ".format( title, prompt)) if not isPassword: - return { "text": input()} + return {"text": input()} 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(), 'ui_') + dispatcher.register_instance(StdIOHandler(), "ui_") # line buffered - p = subprocess.Popen(cmd, 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, + ) rpc_server = RPCServer( - PipeTransport(p.stdout, p.stdin), - JSONRPCProtocol(), - dispatcher + PipeTransport(p.stdout, p.stdin), JSONRPCProtocol(), dispatcher ) rpc_server.serve_forever() -if __name__ == '__main__': + +if __name__ == "__main__": main(sys.argv[1:]) diff --git a/cmd/clef/requirements.txt b/cmd/clef/requirements.txt new file mode 100644 index 000000000000..5381862e306d --- /dev/null +++ b/cmd/clef/requirements.txt @@ -0,0 +1 @@ +tinyrpc==1.1.4 From 5de8791c69e128d9b053d9de445ed9bc3882c734 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 2 May 2022 13:55:13 +0200 Subject: [PATCH 3/3] cmd/clef: python poc fixups --- cmd/clef/pythonsigner.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/cmd/clef/pythonsigner.py b/cmd/clef/pythonsigner.py index 9892580f49c4..b9ea1e406a89 100644 --- a/cmd/clef/pythonsigner.py +++ b/cmd/clef/pythonsigner.py @@ -179,11 +179,11 @@ def showError(self, req): :return:nothing """ # noqa: E501 message = ( - "Error: {text}\n" + "## Error\n{text}\n" "Press enter to continue\n" ) text = req.get("text") - sys.stdout.write(message.format(text)) + sys.stdout.write(message.format(text=text)) input() return @@ -198,11 +198,11 @@ def showInfo(self, req): :return:nothing """ # noqa: E501 message = ( - "Info: {text}\n" + "## Info\n{text}\n" "Press enter to continue\n" ) text = req.get("text") - sys.stdout.write(message.format(text)) + sys.stdout.write(message.format(text=text)) input() return @@ -240,16 +240,19 @@ def approveListing(self, req): message = ( "\n" "## Account listing request\n" + "\t{meta_string}\n" "\tDo you want to allow listing the following accounts?\n" "\t-{addrs}\n" "\n" "->Auto-answering No\n" ) + meta = req.get("meta", {}) accounts = req.get("accounts", []) addrs = [x.get("address") for x in accounts] sys.stdout.write( message.format( - addrs="\n\t-".join(addrs) + addrs="\n\t-".join(addrs), + meta_string=metaString(meta) ) ) return {} @@ -273,7 +276,7 @@ def onInputRequired(self, req): ) sys.stdout.write( message.format( - title=req.get("title") + title=req.get("title"), prompt=req.get("prompt") ) )