diff --git a/CHANGELOG b/CHANGELOG index 4f1d70b..4cdfaf7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +* 0.4.0 + - Handle hex inputs for int values in transaction dict + - Change signature for Web3 constructor to take provider instead of url + - Add script for importing private key from hex * 0.3.0 - Implement SQLAlchemy for db backend * 0.2.6 diff --git a/crypto_dev_signer/eth/transaction.py b/crypto_dev_signer/eth/transaction.py index 8c931ec..14df5f4 100644 --- a/crypto_dev_signer/eth/transaction.py +++ b/crypto_dev_signer/eth/transaction.py @@ -27,11 +27,35 @@ class EIP155Transaction: to = binascii.unhexlify(strip_hex_prefix(tx['to'])) data = binascii.unhexlify(strip_hex_prefix(tx['data'])) + gas_price = None + start_gas = None + value = None + + try: + gas_price = int(tx['gasPrice']) + except ValueError: + gas_price = int(tx['gasPrice'], 16) + + try: + start_gas = int(tx['gas']) + except ValueError: + start_gas = int(tx['gas'], 16) + + try: + value = int(tx['value']) + except ValueError: + value = int(tx['value'], 16) + + try: + nonce = int(nonce) + except ValueError: + nonce = int(nonce, 16) + self.nonce = nonce - self.gas_price = int(tx['gasPrice']) - self.start_gas = int(tx['gas']) + self.gas_price = gas_price + self.start_gas = start_gas self.to = to - self.value = int(tx['value']) + self.value = value self.data = data self.v = chainId self.r = b'' diff --git a/crypto_dev_signer/eth/web3ext/__init__.py b/crypto_dev_signer/eth/web3ext/__init__.py index 7dda657..f031592 100644 --- a/crypto_dev_signer/eth/web3ext/__init__.py +++ b/crypto_dev_signer/eth/web3ext/__init__.py @@ -17,13 +17,8 @@ def create_middleware(ipcpath): # overrides the original Web3 constructor -def Web3(blockchain_provider='ws://localhost:8546', ipcpath=None): - provider = None - if re.match(re_websocket, blockchain_provider) != None: - provider = WebsocketProvider(blockchain_provider) - elif re.match(re_http, blockchain_provider) != None: - provider = HTTPProvider(blockchain_provider) - +#def Web3(blockchain_provider='ws://localhost:8546', ipcpath=None): +def Web3(provider, ipcpath=None): w3 = Web3super(provider) if ipcpath != None: diff --git a/crypto_dev_signer/eth/web3ext/middleware.py b/crypto_dev_signer/eth/web3ext/middleware.py index d3cdb71..22b0df2 100644 --- a/crypto_dev_signer/eth/web3ext/middleware.py +++ b/crypto_dev_signer/eth/web3ext/middleware.py @@ -38,11 +38,11 @@ class PlatformMiddleware: # dict input comes as [{}] and fails if not passed on as an array @staticmethod def _translate_params(params): - if params.__class__.__name__ == 'tuple': - r = [] - for p in params: - r.append(p) - return r + #if params.__class__.__name__ == 'tuple': + # r = [] + # for p in params: + # r.append(p) + # return r if params.__class__.__name__ == 'list' and len(params) > 0: return params[0] @@ -64,7 +64,7 @@ class PlatformMiddleware: ipc_provider_workaround = s.connect(self.ipcaddr) logg.info('redirecting method {} params {} original params {}'.format(method, params, suspect_params)) - o = jsonrpc_request(method, params) + o = jsonrpc_request(method, params[0]) j = json.dumps(o) logg.debug('send {}'.format(j)) s.send(j.encode('utf-8')) @@ -81,8 +81,8 @@ class PlatformMiddleware: params = PlatformMiddleware._translate_params(suspect_params) s = socket.socket(family=socket.AF_UNIX, type=socket.SOCK_STREAM, proto=0) ipc_provider_workaround = s.connect(self.ipcaddr) - logg.info('redirecting method {} params {} original params {}'.format(method, params, suspect_params)) - o = jsonrpc_request(method, params) + logg.info('redirecting methodd {} params {} original params {}'.format(method, params, suspect_params)) + o = jsonrpc_request(method, params[0]) j = json.dumps(o) logg.debug('send {}'.format(j)) s.send(j.encode('utf-8')) diff --git a/crypto_dev_signer/keystore/postgres.py b/crypto_dev_signer/keystore/postgres.py index 95e1710..209f5fe 100644 --- a/crypto_dev_signer/keystore/postgres.py +++ b/crypto_dev_signer/keystore/postgres.py @@ -75,6 +75,7 @@ class ReferenceKeystore(Keystore): }, ) self.db_session.commit() + logg.info('added private key for address {}'.format(address_hex_clean)) return address_hex diff --git a/crypto_dev_signer/runnable/signer.py b/crypto_dev_signer/runnable/signer.py index f327ab6..e532237 100755 --- a/crypto_dev_signer/runnable/signer.py +++ b/crypto_dev_signer/runnable/signer.py @@ -97,6 +97,7 @@ def personal_new_account(p): def personal_sign_transaction(p): + logg.debug('got {} to sign'.format(p[0])) t = EIP155Transaction(p[0], p[0]['nonce'], 8995) z = signer.signTransaction(t, p[1]) raw_signed_tx = t.rlp_serialize() @@ -212,7 +213,6 @@ def init(): signer = ReferenceSigner(db) -#if __name__ == '__main__': def main(): init() arg = None @@ -226,3 +226,7 @@ def main(): (rpc_id, response) = process_input(arg) r = jsonrpc_ok(rpc_id, response) sys.stdout.write(json.dumps(r)) + + +if __name__ == '__main__': + main() diff --git a/scripts/import.py b/scripts/import.py new file mode 100644 index 0000000..da3ccc8 --- /dev/null +++ b/scripts/import.py @@ -0,0 +1,58 @@ +# standard imports +import os +import sys +import logging +import argparse + +# third-party imports +import confini + +# local imports +from crypto_dev_signer.keystore import ReferenceKeystore + +logging.basicConfig(level=logging.WARNING) +logg = logging.getLogger() + +config_dir = os.path.join('/usr/local/etc/cic-eth') + +db = None + + +argparser = argparse.ArgumentParser() +argparser.add_argument('-c', type=str, default=config_dir, help='config file') +argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration') +argparser.add_argument('-v', action='store_true', help='be verbose') +argparser.add_argument('-vv', action='store_true', help='be more verbose') +argparser.add_argument('private_key', type=str, help='private key to add, 0x hex format') +args = argparser.parse_args() + +if args.vv: + logging.getLogger().setLevel(logging.DEBUG) +elif args.v: + logging.getLogger().setLevel(logging.INFO) + +config = confini.Config(args.c, args.env_prefix) +config.process() +config.censor('PASSWORD', 'DATABASE') +config.censor('SECRET', 'SIGNER') +logg.debug('config loaded from {}:\n{}'.format(config_dir, config)) + +# connect to database +dsn = 'postgresql://{}:{}@{}:{}/{}'.format( + config.get('DATABASE_USER'), + config.get('DATABASE_PASSWORD'), + config.get('DATABASE_HOST'), + config.get('DATABASE_PORT'), + config.get('DATABASE_NAME'), + ) + +logg.info('using dsn {}'.format(dsn)) + + +if __name__ == '__main__': + kw = { + 'symmetric_key': bytes.fromhex(config.get('SIGNER_SECRET')), + } + r = ReferenceKeystore(dsn, **kw) + private_key_bytes = bytes.fromhex(args.private_key) + r.import_raw_key(private_key_bytes) diff --git a/setup.py b/setup.py index a255457..21eb384 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ f.close() setup( name="crypto-dev-signer", - version="0.3.0", + version="0.4.0", description="A signer and keystore daemon and library for cryptocurrency software development", author="Louis Holbrook", author_email="dev@holbrook.no", @@ -26,7 +26,7 @@ setup( 'pysha3', 'rlp', 'json-rpc', - 'confini==0.2.6', + 'confini==0.2.7', 'sqlalchemy==1.3.19', ], long_description=long_description, diff --git a/test/test_sign.py b/test/test_sign.py index c941f72..ba8d27b 100644 --- a/test/test_sign.py +++ b/test/test_sign.py @@ -12,15 +12,26 @@ logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() -tx = { +tx_ints = { + 'nonce': 0, 'from': "0xEB014f8c8B418Db6b45774c326A0E64C78914dC0", 'gasPrice': "20000000000", - 'gas': "22000", + 'gas': "21000", 'to': '0x3535353535353535353535353535353535353535', 'value': "1000", 'data': "deadbeef", } +tx_hexs = { + 'nonce': '0x0', + 'from': "0xEB014f8c8B418Db6b45774c326A0E64C78914dC0", + 'gasPrice': "0x4a817c800", + 'gas': "0x5208", + 'to': '0x3535353535353535353535353535353535353535', + 'value': "0x3e8", + 'data': "deadbeef", +} + class pkGetter: def __init__(self, pk): @@ -54,17 +65,24 @@ class TestSign(unittest.TestCase): # TODO: verify rlp tx output def test_serialize_transaction(self): - t = EIP155Transaction(tx, 0) + t = EIP155Transaction(tx_ints, 0) self.assertRegex(t.__class__.__name__, "Transaction") s = t.serialize() - self.assertEqual('{}'.format(s), "{'nonce': '0x0', 'gasPrice': '0x4a817c800', 'gas': '0x55f0', 'to': '0x3535353535353535353535353535353535353535', 'value': '0x3e8', 'data': '0xdeadbeef', 'v': '0x1', 'r': '', 's': ''}") + self.assertEqual('{}'.format(s), "{'nonce': '0x0', 'gasPrice': '0x4a817c800', 'gas': '0x5208', 'to': '0x3535353535353535353535353535353535353535', 'value': '0x3e8', 'data': '0xdeadbeef', 'v': '0x1', 'r': '', 's': ''}") r = t.rlp_serialize() - self.assertEqual(r.hex(), 'ea808504a817c8008255f09435353535353535353535353535353535353535358203e884deadbeef018080') + self.assertEqual(r.hex(), 'ea808504a817c8008252089435353535353535353535353535353535353535358203e884deadbeef018080') + + t = EIP155Transaction(tx_hexs, 0) + self.assertRegex(t.__class__.__name__, "Transaction") + s = t.serialize() + self.assertEqual('{}'.format(s), "{'nonce': '0x0', 'gasPrice': '0x4a817c800', 'gas': '0x5208', 'to': '0x3535353535353535353535353535353535353535', 'value': '0x3e8', 'data': '0xdeadbeef', 'v': '0x1', 'r': '', 's': ''}") + r = t.rlp_serialize() + self.assertEqual(r.hex(), 'ea808504a817c8008252089435353535353535353535353535353535353535358203e884deadbeef018080') def test_sign_transaction(self): - t = EIP155Transaction(tx, 461, 8995) + t = EIP155Transaction(tx_ints, 461, 8995) s = ReferenceSigner(self.pk_getter) z = s.signTransaction(t)