commit b46ed3a9e6407b2b6f345596e4add609340c147f
parent 3fb5745f9800be47211c7a7f4e1cfb0ce5cf72d2
Author: nolash <dev@holbrook.no>
Date: Wed, 17 Mar 2021 15:34:51 +0100
Remove eth_keys, web3 dependencies
Diffstat:
15 files changed, 122 insertions(+), 75 deletions(-)
diff --git a/config/config.ini b/config/config.ini
@@ -1,3 +1,3 @@
[signer]
secret = deadbeef
-socket_path = /tmp/crypto-dev-signer/jsonrpc.ipc
+socket_path = ipc:///tmp/crypto-dev-signer/jsonrpc.ipc
diff --git a/crypto_dev_signer/__init__.py b/crypto_dev_signer/__init__.py
@@ -1,4 +0,0 @@
-import crypto_dev_signer.eth.signer
-import crypto_dev_signer.eth.web3ext
-import crypto_dev_signer.eth.transaction
-import crypto_dev_signer.common
diff --git a/crypto_dev_signer/eth/signer/defaultsigner.py b/crypto_dev_signer/eth/signer/defaultsigner.py
@@ -1,11 +1,15 @@
+# standard imports
import logging
-import sha3
-from eth_keys import KeyAPI
-from eth_keys.backends import NativeECCBackend
+# external imports
+import sha3
+import coincurve
+#from eth_keys import KeyAPI
+#from eth_keys.backends import NativeECCBackend
-keys = KeyAPI(NativeECCBackend)
-logg = logging.getLogger(__name__)
+#keys = KeyAPI(NativeECCBackend)
+#logg = logging.getLogger(__name__)
+logg = logging.getLogger()
class Signer:
@@ -31,9 +35,9 @@ class ReferenceSigner(Signer):
s = tx.rlp_serialize()
h = sha3.keccak_256()
h.update(s)
- g = h.digest()
- k = keys.PrivateKey(self.keyGetter.get(tx.sender, password))
- z = keys.ecdsa_sign(message_hash=g, private_key=k)
+ message_to_sign = h.digest()
+ z = self.sign(tx.sender, message_to_sign, password)
+
vnum = int.from_bytes(tx.v, 'big')
v = (vnum * 2) + 35 + z[64]
byts = ((v.bit_length()-1)/8)+1
@@ -55,17 +59,30 @@ class ReferenceSigner(Signer):
def signEthereumMessage(self, address, message, password=None):
- #msg = b'\x19Ethereum Signed Message:\n{}{}'.format(len(message), message)
- k = keys.PrivateKey(self.keyGetter.get(address, password))
+
+ #k = keys.PrivateKey(self.keyGetter.get(address, password))
#z = keys.ecdsa_sign(message_hash=g, private_key=k)
- z = None
if type(message).__name__ == 'str':
logg.debug('signing message in "str" format: {}'.format(message))
- z = k.sign_msg(bytes.fromhex(message))
+ #z = k.sign_msg(bytes.fromhex(message))
+ message = bytes.fromhex(message)
elif type(message).__name__ == 'bytes':
logg.debug('signing message in "bytes" format: {}'.format(message.hex()))
- z = k.sign_msg(message)
+ #z = k.sign_msg(message)
else:
+ logg.debug('unhandled format {}'.format(type(message).__name__))
raise ValueError('message must be type str or bytes, received {}'.format(type(message).__name__))
+
+ ethereumed_message_header = b'\x19' + 'Ethereum Signed Message:\n{}'.format(len(message)).encode('utf-8')
+ h = sha3.keccak_256()
+ h.update(ethereumed_message_header + message)
+ message_to_sign = h.digest()
+
+ z = self.sign(address, message_to_sign, password)
return z
+
+ def sign(self, address, message, password=None):
+ pk = coincurve.PrivateKey(secret=self.keyGetter.get(address, password))
+ z = pk.sign(hasher=None, message=message)
+ return z
diff --git a/crypto_dev_signer/keystore/__init__.py b/crypto_dev_signer/keystore/__init__.py
@@ -1,8 +1,8 @@
# third-party imports
-from eth_keys import KeyAPI
-from eth_keys.backends import NativeECCBackend
+#from eth_keys import KeyAPI
+#from eth_keys.backends import NativeECCBackend
-keyapi = KeyAPI(NativeECCBackend)
+#keyapi = KeyAPI(NativeECCBackend)
-from .postgres import ReferenceKeystore
-from .dict import DictKeystore
+#from .postgres import ReferenceKeystore
+#from .dict import DictKeystore
diff --git a/crypto_dev_signer/keystore/dict.py b/crypto_dev_signer/keystore/dict.py
@@ -1,11 +1,14 @@
# standard imports
import logging
+# external imports
+from hexathon import strip_0x
+
# local imports
-from . import keyapi
+#from . import keyapi
from .interface import Keystore
from crypto_dev_signer.error import UnknownAccountError
-from crypto_dev_signer.common import strip_hex_prefix
+from crypto_dev_signer.encoding import private_key_to_address
logg = logging.getLogger()
@@ -25,9 +28,13 @@ class DictKeystore(Keystore):
raise UnknownAccountError(address)
+ def list(self):
+ return list(self.keys.keys())
+
+
def import_key(self, pk, password=None):
- pubk = keyapi.private_key_to_public_key(pk)
- address_hex = pubk.to_checksum_address()
- address_hex_clean = strip_hex_prefix(address_hex)
- self.keys[address_hex_clean] = pk.to_bytes()
+ address_hex = private_key_to_address(pk)
+ address_hex_clean = strip_0x(address_hex)
+ self.keys[address_hex_clean] = pk.secret
+ logg.debug('added key {}'.format(address_hex))
return address_hex
diff --git a/crypto_dev_signer/keystore/interface.py b/crypto_dev_signer/keystore/interface.py
@@ -1,12 +1,13 @@
# standard imports
import os
-
-# third-party imports
-import eth_keyfile
+import json
+import logging
# local imports
-from . import keyapi
+from crypto_dev_signer.keystore import keyfile
+import coincurve
+logg = logging.getLogger(__name__)
class Keystore:
@@ -14,13 +15,17 @@ class Keystore:
raise NotImplementedError
+ def list(self):
+ raise NotImplementedError
+
+
def new(self, password=None):
b = os.urandom(32)
return self.import_raw_key(b, password)
def import_raw_key(self, b, password=None):
- pk = keyapi.PrivateKey(b)
+ pk = coincurve.PrivateKey(secret=b)
return self.import_key(pk, password)
@@ -30,9 +35,16 @@ class Keystore:
def import_keystore_data(self, keystore_content, password=''):
#private_key = w3.eth.account.decrypt(keystore_content, password)
- private_key = eth_keyfile.decode_keyfile_json(keystore_content, password.encode('utf-8'))
+ if type(keystore_content).__name__ == 'str':
+ keystore_content = json.loads(keystore_content)
+ elif type(keystore_content).__name__ == 'bytes':
+ logg.debug('bytes {}'.format(keystore_content))
+ keystore_content = json.loads(keystore_content.decode('utf-8'))
+ private_key = keyfile.from_dict(keystore_content, password.encode('utf-8'))
return self.import_raw_key(private_key, password)
def import_keystore_file(self, keystore_file, password=''):
- keystore_content = eth_keyfile.load_keyfile(keystore_file)
- return self.import_keystore_data(keystore_content, password)
+ private_key = keyfile.from_file(keystore_file)
+ #return self.import_keystore_data(keystore_content, password)
+ return self.import_raw_key(private_key, password)
+ #return kes
diff --git a/crypto_dev_signer/keystore/postgres.py b/crypto_dev_signer/keystore/postgres.py
@@ -2,7 +2,7 @@
import logging
import base64
-# third-party imports
+# external imports
from cryptography.fernet import Fernet
#import psycopg2
#from psycopg2 import sql
@@ -10,12 +10,13 @@ from cryptography.fernet import Fernet
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
import sha3
+from hexathon import strip_0x
# local imports
from .interface import Keystore
-from crypto_dev_signer.common import strip_hex_prefix
-from . import keyapi
+#from . import keyapi
from crypto_dev_signer.error import UnknownAccountError
+from crypto_dev_signer.encoding import private_key_to_address
logg = logging.getLogger(__file__)
@@ -53,7 +54,7 @@ class ReferenceKeystore(Keystore):
def get(self, address, password=None):
- safe_address = strip_hex_prefix(address)
+ safe_address = strip_0x(address)
s = text('SELECT key_ciphertext FROM ethereum WHERE wallet_address_hex = :a')
r = self.db_session.execute(s, {
'a': safe_address,
@@ -69,10 +70,10 @@ class ReferenceKeystore(Keystore):
def import_key(self, pk, password=None):
- pubk = keyapi.private_key_to_public_key(pk)
- address_hex = pubk.to_checksum_address()
- address_hex_clean = strip_hex_prefix(address_hex)
- c = self._encrypt(pk.to_bytes(), password)
+ address_hex = private_key_to_address(pk)
+ address_hex_clean = strip_0x(address_hex)
+
+ c = self._encrypt(pk.secret, password)
s = text('INSERT INTO ethereum (wallet_address_hex, key_ciphertext) VALUES (:a, :c)') #%s, %s)')
self.db_session.execute(s, {
'a': address_hex_clean,
diff --git a/crypto_dev_signer/runnable/keyfile.py b/crypto_dev_signer/runnable/keyfile.py
@@ -0,0 +1,8 @@
+# standard imports
+import logging
+import sys
+
+# local imports
+from crypto_dev_signer.keystore.keyfile import parse_file
+
+print(from_file(sys.argv[1]).hex())
diff --git a/crypto_dev_signer/runnable/signer.py b/crypto_dev_signer/runnable/signer.py
@@ -1,4 +1,5 @@
# standard imports
+import re
import os
import sys
import stat
@@ -6,6 +7,7 @@ import socket
import json
import logging
import argparse
+from urllib.parse import urlparse
# third-party imports
import confini
@@ -51,9 +53,9 @@ logg.debug('config loaded from {}:\n{}'.format(config_dir, config))
if args.i:
chainId = args.i
if args.s:
- socket_path = args.s
+ socket_url = urlparse(args.s)
elif config.get('SIGNER_SOCKET_PATH'):
- socket_path = config.get('SIGNER_SOCKET_PATH')
+ socket_url = urlparse(config.get('SIGNER_SOCKET_PATH'))
# connect to database
@@ -68,6 +70,8 @@ dsn = 'postgresql://{}:{}@{}:{}/{}'.format(
logg.info('using dsn {}'.format(dsn))
logg.info('using socket {}'.format(socket_path))
+re_http = r'^http'
+re_unix = r'^ipc'
class MissingSecretError(BaseException):
@@ -114,7 +118,7 @@ def personal_sign_transaction(p):
# TODO: temporary workaround for platform, since personal_signTransaction is missing from web3.py
def eth_signTransaction(tx):
- return personal_sign_transaction([tx, ''])
+ return personal_sign_transaction([tx[0], ''])
def eth_sign(p):
@@ -249,13 +253,16 @@ def main():
arg = json.loads(sys.argv[1])
except:
logg.info('no json rpc command detected, starting socket server')
- socket_spec = socket_path.split(':')
- if len(socket_spec) == 2:
+ scheme = 'ipc'
+ if socket_url.scheme != '':
+ scheme = socket_url.scheme
+ if re.match(re_http, socket_url.scheme):
+ socket_spec = socket_url.netloc.split(':')
host = socket_spec[0]
port = int(socket_spec[1])
start_server_tcp((host, port))
else:
- start_server_unix(socket_path)
+ start_server_unix(socket_url.path)
sys.exit(0)
(rpc_id, response) = process_input(arg)
diff --git a/requirements.txt b/requirements.txt
@@ -1,9 +1,10 @@
-web3==5.12.2
psycopg2==2.8.6
-cryptography==3.2.1
-eth-keys==0.3.3
+#cryptography==3.2.1
pysha3==1.0.2
-rlp==2.0.1
+simple-rlp==0.1.2
json-rpc==1.13.0
confini~=0.3.6a1
sqlalchemy==1.3.20
+coincurve==15.0.0
+pycrypto==2.6.1
+hexathon==0.0.1a3
diff --git a/setup.py b/setup.py
@@ -24,7 +24,7 @@ f.close()
setup(
name="crypto-dev-signer",
- version="0.4.13rc4",
+ version="0.4.13rc6",
description="A signer and keystore daemon and library for cryptocurrency software development",
author="Louis Holbrook",
author_email="dev@holbrook.no",
diff --git a/test/test_helper.py b/test/test_helper.py
@@ -3,14 +3,12 @@ import unittest
import logging
import os
-# third-party imports
-import web3
-
# local imports
-from crypto_dev_signer.keystore import DictKeystore
+from crypto_dev_signer.keystore.dict import DictKeystore
from crypto_dev_signer.eth.signer import ReferenceSigner
from crypto_dev_signer.helper import TxExecutor
-from crypto_dev_signer.eth.helper import EthTxExecutor
+#from crypto_dev_signer.eth.helper import EthTxExecutor
+from crypto_dev_signer.encoding import to_checksum_address
logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
@@ -42,7 +40,7 @@ class MockEthTxBackend:
def builder_two(self, tx):
tx['value'] = 10243
- tx['to'] = web3.Web3.toChecksumAddress('0x' + os.urandom(20).hex())
+ tx['to'] = to_checksum_address('0x' + os.urandom(20).hex())
tx['data'] = ''
if tx.get('feePrice') != None:
tx['gasPrice'] = tx['feePrice']
@@ -78,15 +76,15 @@ class TestHelper(unittest.TestCase):
executor.sign_and_send([backend.builder_two])
- def test_eth_helper(self):
- backend = MockEthTxBackend()
- w3 = web3.Web3(web3.Web3.HTTPProvider('http://localhost:8545'))
- executor = EthTxExecutor(w3, self.address_hex, self.signer, 1337)
-
- tx_ish = {'from': self.address_hex}
- #executor.sign_and_send([backend.builder, backend.builder_two])
- with self.assertRaises(ValueError):
- executor.sign_and_send([backend.builder_two])
+# def test_eth_helper(self):
+# backend = MockEthTxBackend()
+# w3 = web3.Web3(web3.Web3.HTTPProvider('http://localhost:8545'))
+# executor = EthTxExecutor(w3, self.address_hex, self.signer, 1337)
+#
+# tx_ish = {'from': self.address_hex}
+# #executor.sign_and_send([backend.builder, backend.builder_two])
+# with self.assertRaises(ValueError):
+# executor.sign_and_send([backend.builder_two])
if __name__ == '__main__':
diff --git a/test/test_keystore_dict.py b/test/test_keystore_dict.py
@@ -7,7 +7,7 @@ import base64
import os
# local imports
-from crypto_dev_signer.keystore import DictKeystore
+from crypto_dev_signer.keystore.dict import DictKeystore
from crypto_dev_signer.error import UnknownAccountError
from crypto_dev_signer.eth.signer import ReferenceSigner
diff --git a/test/test_keystore_reference.py b/test/test_keystore_reference.py
@@ -12,7 +12,7 @@ from psycopg2 import sql
from cryptography.fernet import Fernet, InvalidToken
# local imports
-from crypto_dev_signer.keystore import ReferenceKeystore
+from crypto_dev_signer.keystore.postgres import ReferenceKeystore
from crypto_dev_signer.error import UnknownAccountError
logging.basicConfig(level=logging.DEBUG)
diff --git a/test/test_sign.py b/test/test_sign.py
@@ -68,14 +68,14 @@ class TestSign(unittest.TestCase):
t = EIP155Transaction(tx_ints, 0)
self.assertRegex(t.__class__.__name__, "Transaction")
s = t.serialize()
- self.assertEqual('{}'.format(s), "{'nonce': '', 'gasPrice': '0x04a817c800', 'gas': '0x5208', 'to': '0x3535353535353535353535353535353535353535', 'value': '0x03e8', 'data': '0xdeadbeef', 'v': '0x01', 'r': '', 's': ''}")
+ self.assertEqual('{}'.format(s), "{'nonce': '0x00', 'gasPrice': '0x04a817c800', 'gas': '0x5208', 'to': '0x3535353535353535353535353535353535353535', 'value': '0x03e8', 'data': '0xdeadbeef', 'v': '0x01', 'r': '', 's': ''}")
r = t.rlp_serialize()
self.assertEqual(r.hex(), 'ea808504a817c8008252089435353535353535353535353535353535353535358203e884deadbeef018080')
t = EIP155Transaction(tx_hexs, 0)
self.assertRegex(t.__class__.__name__, "Transaction")
s = t.serialize()
- self.assertEqual('{}'.format(s), "{'nonce': '', 'gasPrice': '0x04a817c800', 'gas': '0x5208', 'to': '0x3535353535353535353535353535353535353535', 'value': '0x03e8', 'data': '0xdeadbeef', 'v': '0x01', 'r': '', 's': ''}")
+ self.assertEqual('{}'.format(s), "{'nonce': '0x00', 'gasPrice': '0x04a817c800', 'gas': '0x5208', 'to': '0x3535353535353535353535353535353535353535', 'value': '0x03e8', 'data': '0xdeadbeef', 'v': '0x01', 'r': '', 's': ''}")
r = t.rlp_serialize()
self.assertEqual(r.hex(), 'ea808504a817c8008252089435353535353535353535353535353535353535358203e884deadbeef018080')