diff --git a/funga/eth/cli/client.py b/funga/eth/cli/client.py new file mode 100644 index 0000000..34495aa --- /dev/null +++ b/funga/eth/cli/client.py @@ -0,0 +1,27 @@ +# standard imports +import urllib.request +import json + +# external imports +import jsonrpc_std.interface + + +class RPCHTTPClient: + + def __init__(self, host='localhost', port=8000): + self.host = host + self.port = port + self.url = 'http://{}:{}'.format(host, port) + + + def rpc_sign_tx(self, tx, request_id=None, passphrase=''): + data_src = jsonrpc_std.interface.jsonrpc_request('personal_signTransaction', request_id=request_id) + data_src['params'].append(tx) + data_src['params'].append(passphrase) + data = json.dumps(data_src) + req = urllib.request.Request(self.url, data=data.encode('utf-8')) + req.add_header('Accept', 'application/json') + req.add_header('Content-Type', 'application/json') + r = urllib.request.urlopen(req) + v = r.read() + return v.decode('utf-8') diff --git a/funga/eth/runnable/tx.py b/funga/eth/runnable/tx.py new file mode 100644 index 0000000..24bbfe0 --- /dev/null +++ b/funga/eth/runnable/tx.py @@ -0,0 +1,77 @@ +# standard imports +import os +import logging +import sys +import json +import argparse +import getpass + +# external impors +import coincurve +from hexathon import strip_0x + +# local imports +from funga.error import DecryptError +from funga.eth.keystore.dict import DictKeystore +#from funga.eth.signer import EIP155Signer +#from funga.eth.transaction import EIP155Transaction +from funga.eth.cli.client import RPCHTTPClient + +logging.basicConfig(level=logging.WARNING) +logg = logging.getLogger() + +argparser = argparse.ArgumentParser() +argparser.add_argument('-f', type=str, help='Keyfile to use for signing') +argparser.add_argument('-z', action='store_true', help='zero-length password') +argparser.add_argument('-v', action='store_true', help='be verbose') +argparser.add_argument('-0', dest='nonl', action='store_true', help='no newline at end of output') +argparser.add_argument('-a', '--signer-address', dest='signer_address', type=str, help='Ethereum address of signer') +argparser.add_argument('-i', '--chain-id', dest='chain_id', type=int, default=1, help='Ethereum address of signer') +argparser.add_argument('--value', type=int, default=0, help='Gas token unit value of transaction') +argparser.add_argument('--nonce', type=int, default=0, help='Transaction nonce') +argparser.add_argument('--fee-price', dest='fee_price', default=1, type=int, help='Gas price') +argparser.add_argument('--fee-limit', dest='fee_limit', default=21000, type=int, help='Gas limit') +argparser.add_argument('recipient_address', type=str, help='Recipient of transaction') +args = argparser.parse_args() + +if args.v: + logg.setLevel(logging.DEBUG) + +rpc_signer = RPCHTTPClient() + +def main(): +# passphrase = os.environ.get('WALLET_PASSPHRASE', os.environ.get('PASSPHRASE')) +# if args.z: +# passphrase = '' +# if passphrase == None: +# passphrase = getpass.getpass('decryption phrase: ') +# +# keystore = DictKeystore() +# address = keystore.import_keystore_file(args.f, password=passphrase) + + tx = { + 'from': args.signer_address, + 'to': args.recipient_address, + 'value': args.value, + 'data': '0x', + 'gasPrice': args.fee_price, + 'gas': args.fee_limit, + 'chainId': args.chain_id, + 'nonce': args.nonce, + } + r = rpc_signer.rpc_sign_tx(tx) + + #tx = EIP155Transaction(tx_src, tx_src['nonce'], tx_src['chainId']) + #print(tx) + + #signer = EIP155Signer(keystore) + #sig = signer.sign_ethereum_message(address, args.msg.encode('utf-8').hex(), password=passphrase) +# +# r = sig.hex() + if not args.nonl: + r += "\n" + sys.stdout.write(r) + + +if __name__ == '__main__': + main() diff --git a/man/eth-keyfile.1.groff b/man/eth-keyfile.1.groff new file mode 100644 index 0000000..d42e9c1 --- /dev/null +++ b/man/eth-keyfile.1.groff @@ -0,0 +1,34 @@ +.TH eth-keyfile 1 +.SH NAME +eth-keyfile /- Generate and parse Ethereum keyfiles +.SH SYNOPSIS +\fBeth-keyfile\fP [ -z ] [ -v ] [ -k \fIprivatekeyfile\fP ] +.P +\fBeth-keyfile\fP [ --private-key ] [ -z ] [ -v ] -d \fIkeyfile\fP +.SH DESCRIPTION +\fBeth-keyfile\fP lets you create and parse Ethereum keyfiles. +.SS OPTIONS +.TP +\fB-d \fIkeyfile\fP +Parse the given keyfile, outputting the Ethereum address. +.TP +\fB--private-key\fP +When parsing a keyfile, output the private key instead of the Ethereum address. +.TP +\fB-k \fIprivatekeyfile\fP\fP +When creating a keyfile, load private key from the filename given as argument. +.TP +\fB-v\fP +Turn on verbose logging. +.TP +\fB-z\fP +Do not use a passphrase. +.SS ENVIRONMENT VARIABLES +.TP +\fBPRIVATE_KEY\fP +Create a keyfile for the given private key. +.TP +\fBPASSPHRASE\fP +Create or parse a keyfile using the given passphrase. +.SS NOTES +The current implementation will only create and parse keyfiles encrypted with aes-128-ctr, and with the scrypt KDF. diff --git a/requirements.txt b/requirements.txt index dbb2cd6..2ec302c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -cryptography==3.2.1 +cryptography==3.3 pysha3==1.0.2 rlp==2.0.1 #rlp==3.0.0