Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api cleanup #16

Merged
merged 2 commits into from
Sep 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 30 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ pip install ethereum-keys
## QuickStart

```python
>>> from eth_keys import KeyAPI
>>> keys = KeyAPI()
>>> from eth_keys import keys
>>> pk = keys.PrivateKey(b'\x01' * 32)
>>> signature = pk.sign(b'a message')
>>> signature = pk.sign_msg(b'a message')
>>> pk
'0x0101010101010101010101010101010101010101010101010101010101010101'
>>> pk.public_key
Expand All @@ -36,11 +35,18 @@ True

### `KeyAPI(backend=None)`

The `KeyAPI` object is the primary API for interacting with the `ethereum-keys` libary. The object takes a single optional argument in it's constructor which designates what backend will be used for eliptical curve cryptography operations. The built-in backends are:
The `KeyAPI` object is the primary API for interacting with the `ethereum-keys`
libary. The object takes a single optional argument in it's constructor which
designates what backend will be used for eliptical curve cryptography
operations. The built-in backends are:

* `eth_keys.backends.NativeECCBackend` (**default**): A pure python implementation of the ECC operations.
* `eth_keys.backends.NativeECCBackend` A pure python implementation of the ECC operations.
* `eth_keys.backends.CoinCurveECCBackend`: Uses the [`coincurve`](https://github.com/ofek/coincurve) library for ECC operations.

By default, `ethereum-keys` will *try* to use the `CoinCurveECCBackend`,
falling back to the `NativeECCBackend` if the `coincurve` library is not
available.

> Note: The `coincurve` library is not automatically installed with `ethereum-keys` and must be installed separately.

The `backend` argument can be given in any of the following forms.
Expand All @@ -60,6 +66,16 @@ The `backend` argument can be given in any of the following forms.
>>> keys = KeyAPI('eth_keys.backends.CoinCurveECCBackend')
```

The backend can also be configured using the environment variable
`ECC_BACKEND_CLASS` which should be set to the dot-separated python import path
to the desired backend.

```python
>>> import os
>>> os.environ['ECC_BACKEND_CLASS'] = 'eth_keys.backends.CoinCurveECCBackend'
```


### `KeyAPI.ecdsa_sign(message_hash, private_key) -> Signature`

This method returns a signature for the given `message_hash`, signed by the
Expand Down Expand Up @@ -129,7 +145,7 @@ given `private_key`.
* `private_key` may either be a byte string of length 32 or an instance of the `KeyAPI.PrivateKey` class.


#### `PublicKey.recover_msg(message, signature) -> PublicKey`
#### `PublicKey.recover_from_msg(message, signature) -> PublicKey`

This `classmethod` returns a new `PublicKey` instance computed from the
provided `message` and `signature`.
Expand All @@ -138,9 +154,9 @@ provided `message` and `signature`.
* `signature` **must** be an instance of `KeyAPI.Signature`


#### `PublicKey.recover_msg_hash(message_hash, signature) -> PublicKey`
#### `PublicKey.recover_from_msg_hash(message_hash, signature) -> PublicKey`

Same as `PublicKey.recover_msg` except that `message_hash` should be the Keccak
Same as `PublicKey.recover_from_msg` except that `message_hash` should be the Keccak
hash of the `message`.


Expand Down Expand Up @@ -183,15 +199,15 @@ The following methods and properties are available
This *property* holds the `PublicKey` instance coresponding to this private key.


#### `PrivateKey.sign(message) -> Signature`
#### `PrivateKey.sign_msg(message) -> Signature`

This method returns a signature for the given `message` in the form of a
`Signature` instance

* `message` **must** be a byte string.


#### `PrivateKey.sign_hash(message_hash) -> Signature`
#### `PrivateKey.sign_msg_hash(message_hash) -> Signature`

Same as `PrivateKey.sign` except that `message_hash` should be the Keccak
hash of the `message`.
Expand Down Expand Up @@ -244,17 +260,17 @@ Same as `Signature.verify_msg` except that `message_hash` should be the Keccak
hash of the `message`.


#### `Signature.recover_msg(message) -> PublicKey`
#### `Signature.recover_public_key_from_msg(message) -> PublicKey`

This method returns a `PublicKey` instance recovered from the signature.

* `message`: **must** be a byte string.


#### `Signature.recover_msg_hash(message_hash) -> PublicKey`
#### `Signature.recover_public_key_from_msg_hash(message_hash) -> PublicKey`

Same as `Signature.recover_msg` except that `message_hash` should be the Keccak
hash of the `message`.
Same as `Signature.recover_public_key_from_msg` except that `message_hash`
should be the Keccak hash of the `message`.


### Exceptions
Expand Down
5 changes: 4 additions & 1 deletion eth_keys/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from __future__ import absolute_import

from .main import KeyAPI # noqa: F401
from .main import ( # noqa: F401
KeyAPI,
lazy_key_api as keys,
)
13 changes: 10 additions & 3 deletions eth_keys/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,25 @@
)

from .base import BaseECCBackend # noqa: F401
from .coincurve import CoinCurveECCBackend # noqa: F401
from .coincurve import ( # noqa: F401
CoinCurveECCBackend,
is_coincurve_available,
)
from .native import NativeECCBackend # noqa: F401


DEFAULT_ECC_BACKEND = 'eth_keys.backends.native.NativeECCBackend'
def get_default_backend_class():
if is_coincurve_available():
return 'eth_keys.backends.CoinCurveECCBackend'
else:
return 'eth_keys.backends.NativeECCBackend'


def get_backend_class(import_path=None):
if import_path is None:
import_path = os.environ.get(
'ECC_BACKEND_CLASS',
DEFAULT_ECC_BACKEND,
get_default_backend_class(),
)
return import_string(import_path)

Expand Down
9 changes: 9 additions & 0 deletions eth_keys/backends/coincurve.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
from .base import BaseECCBackend


def is_coincurve_available():
try:
import coincurve # noqa: F401
except ImportError:
return False
else:
return True


class CoinCurveECCBackend(BaseECCBackend):
def __init__(self):
try:
Expand Down
18 changes: 9 additions & 9 deletions eth_keys/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ def from_private(cls, private_key):
return cls.get_backend().private_key_to_public_key(private_key)

@classmethod
def recover_msg(cls, message, signature):
def recover_from_msg(cls, message, signature):
message_hash = keccak(message)
return cls.recover_msg_hash(message_hash, signature)
return cls.recover_from_msg_hash(message_hash, signature)

@classmethod
def recover_msg_hash(cls, message_hash, signature):
def recover_from_msg_hash(cls, message_hash, signature):
return cls.get_backend().ecdsa_recover(message_hash, signature)

def verify_msg(self, message, signature):
Expand Down Expand Up @@ -168,11 +168,11 @@ def __init__(self, private_key_bytes):

self.public_key = self.backend.private_key_to_public_key(self)

def sign(self, message):
def sign_msg(self, message):
message_hash = keccak(message)
return self.sign_hash(message_hash)
return self.sign_msg_hash(message_hash)

def sign_hash(self, message_hash):
def sign_msg_hash(self, message_hash):
return self.backend.ecdsa_sign(message_hash, self)


Expand Down Expand Up @@ -295,11 +295,11 @@ def verify_msg(self, message, public_key):
def verify_msg_hash(self, message_hash, public_key):
return self.backend.ecdsa_verify(message_hash, self, public_key)

def recover_msg(self, message):
def recover_public_key_from_msg(self, message):
message_hash = keccak(message)
return self.recover_msg_hash(message_hash)
return self.recover_public_key_from_msg_hash(message_hash)

def recover_msg_hash(self, message_hash):
def recover_public_key_from_msg_hash(self, message_hash):
return self.backend.ecdsa_recover(message_hash, self)

def __index__(self):
Expand Down
20 changes: 19 additions & 1 deletion eth_keys/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class KeyAPI(object):

def __init__(self, backend=None):
if backend is None:
backend = get_backend()
pass
elif isinstance(backend, BaseECCBackend):
pass
elif isinstance(backend, type) and issubclass(backend, BaseECCBackend):
Expand All @@ -48,6 +48,19 @@ def __init__(self, backend=None):

self.backend = backend

_backend = None

@property
def backend(self):
if self._backend is None:
return get_backend()
else:
return self._backend

@backend.setter
def backend(self, value):
self._backend = value

#
# Proxy method calls to the backends
#
Expand Down Expand Up @@ -102,3 +115,8 @@ def private_key_to_public_key(self, private_key):
"an instance of `eth_keys.datatypes.PublicKey`"
)
return public_key


# This creates an easy to import backend which will lazily fetch whatever
# backend has been configured at runtime (as opposed to import or instantiation time).
lazy_key_api = KeyAPI(backend=None)
16 changes: 8 additions & 8 deletions tests/core/test_key_and_signature_datastructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,22 @@ def private_key(ecc_backend):


def test_signing_from_private_key_obj(ecc_backend, private_key):
signature = private_key.sign(MSG)
signature = private_key.sign_msg(MSG)

assert ecc_backend.ecdsa_verify(MSGHASH, signature, private_key.public_key)


def test_hash_signing_from_private_key_obj(ecc_backend, private_key):
signature = private_key.sign_hash(MSGHASH)
signature = private_key.sign_msg_hash(MSGHASH)

assert ecc_backend.ecdsa_verify(MSGHASH, signature, private_key.public_key)


def test_recover_from_public_key_class(ecc_backend, private_key):
signature = ecc_backend.ecdsa_sign(MSGHASH, private_key)
public_key = ecc_backend.PublicKey.recover_msg_hash(MSGHASH, signature)
public_key = ecc_backend.PublicKey.recover_from_msg_hash(MSGHASH, signature)

assert public_key == ecc_backend.PublicKey.recover_msg(MSG, signature)
assert public_key == ecc_backend.PublicKey.recover_from_msg(MSG, signature)
assert public_key == private_key.public_key


Expand Down Expand Up @@ -84,9 +84,9 @@ def test_verify_from_signature_obj(ecc_backend, private_key):

def test_recover_from_signature_obj(ecc_backend, private_key):
signature = ecc_backend.ecdsa_sign(MSGHASH, private_key)
public_key = signature.recover_msg_hash(MSGHASH)
public_key = signature.recover_public_key_from_msg_hash(MSGHASH)

assert public_key == signature.recover_msg(MSG)
assert public_key == signature.recover_public_key_from_msg(MSG)
assert public_key == private_key.public_key


Expand All @@ -110,7 +110,7 @@ def test_to_canonical_address_from_public_key(private_key):

def test_hex_conversion(private_key):
public_key = private_key.public_key
signature = private_key.sign(b'message')
signature = private_key.sign_msg(b'message')

assert hex(public_key) == encode_hex(bytes(public_key))
assert hex(private_key) == encode_hex(bytes(private_key))
Expand All @@ -123,7 +123,7 @@ def test_hex_conversion(private_key):

def test_bytes_conversion(private_key):
public_key = private_key.public_key
signature = private_key.sign(b'message')
signature = private_key.sign_msg(b'message')

assert bytes(public_key) == public_key._raw_key
assert bytes(private_key) == private_key._raw_key
Expand Down
2 changes: 1 addition & 1 deletion tests/core/test_key_api_proxy_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def public_key(private_key):

@pytest.fixture
def signature(private_key):
return private_key.sign_hash(MSGHASH)
return private_key.sign_msg_hash(MSGHASH)


def test_proxied_backend_properties(key_api, ecc_backend):
Expand Down