Skip to content

Commit

Permalink
Merge branch 'develop' into pyup-scheduled-update-2019-04-01
Browse files Browse the repository at this point in the history
  • Loading branch information
xeroc authored Apr 8, 2019
2 parents bff650d + dcb666c commit b438ff3
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 11 deletions.
6 changes: 3 additions & 3 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
685 Fabian Schuh <[email protected]>
699 Fabian Schuh <[email protected]>
154 Fabian Schuh <[email protected]>
76 Fabian Schuh <[email protected]>
77 Fabian Schuh <[email protected]>
24 pyup-bot <[email protected]>
20 gileadmcgee <[email protected]>
10 user name <[email protected]>
Expand All @@ -9,6 +9,7 @@
4 bitcrab <[email protected]>
3 Boombastic <[email protected]>
3 Holger Nahrstaedt <[email protected]>
3 Vladimir Kamarzin <[email protected]>
3 abitmore <[email protected]>
2 Nicolas Wack <[email protected]>
2 Scott Howard <[email protected]>
Expand All @@ -19,7 +20,6 @@
1 Best Buy #2 <[email protected]>
1 Holger Nahrstaedt <[email protected]>
1 Pavel Martynov <[email protected]>
1 Vladimir Kamarzin <[email protected]>
1 iHashFury <[email protected]>
1 jhtitor <[email protected]>
1 linouxis9 <[email protected]>
Expand Down
8 changes: 5 additions & 3 deletions grapheneapi/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ def setup_proxy(self, options):
self.proxy_user = options.pop("proxy_user", None)
self.proxy_pass = options.pop("proxy_pass", None)
self.proxy_rdns = False
log.info(
"Using proxy %s:%d %s" % (self.proxy_host, self.proxy_port, self.proxy_type)
)
if self.proxy_host:
log.info(
"Using proxy %s:%d %s"
% (self.proxy_host, self.proxy_port, self.proxy_type)
)

def get_proxy_url(self): # pragma: no cover
if not self.proxy_host:
Expand Down
187 changes: 186 additions & 1 deletion graphenecommon/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import re

from binascii import hexlify, unhexlify
from datetime import datetime

from graphenebase.ecdsa import sign_message, verify_message

Expand All @@ -19,7 +20,7 @@
log = logging.getLogger(__name__)


class Message(AbstractBlockchainInstanceProvider):
class MessageV1(AbstractBlockchainInstanceProvider):
""" Allow to sign and verify Messages that are sigend with a private key
"""

Expand Down Expand Up @@ -187,3 +188,187 @@ def verify(self, **kwargs):
self.plain_message = message

return True


class MessageV2(AbstractBlockchainInstanceProvider):
""" Allow to sign and verify Messages that are sigend with a private key
"""

def __init__(self, message, *args, **kwargs):
self.define_classes()
assert self.account_class
assert self.publickey_class

self.message = message
self.signed_by_account = None
self.signed_by_name = None
self.meta = None
self.plain_message = None

def sign(self, account=None, **kwargs):
""" Sign a message with an account's memo key
:param str account: (optional) the account that owns the bet
(defaults to ``default_account``)
:raises ValueError: If not account for signing is provided
:returns: the signed message encapsulated in a known format
"""
if not account:
if "default_account" in self.blockchain.config:
account = self.blockchain.config["default_account"]
if not account:
raise ValueError("You need to provide an account")

# Data for message
account = self.account_class(account, blockchain_instance=self.blockchain)

# wif key
wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
account["options"]["memo_key"]
)

payload = [
"from",
account["name"],
"key",
account["options"]["memo_key"],
"time",
str(datetime.utcnow()),
"text",
self.message,
]
enc_message = json.dumps(payload, separators=(",", ":"))

# signature
signature = hexlify(sign_message(enc_message, wif)).decode("ascii")

return dict(signed=enc_message, payload=payload, signature=signature)

def verify(self, **kwargs):
""" Verify a message with an account's memo key
:param str account: (optional) the account that owns the bet
(defaults to ``default_account``)
:returns: True if the message is verified successfully
:raises InvalidMessageSignature if the signature is not ok
"""
assert isinstance(self.message, dict), "Message must be dictionary"

payload = self.message.get("payload")
assert payload, "Missing payload"
payload_dict = {k[0]: k[1] for k in zip(payload[::2], payload[1::2])}
signature = self.message.get("signature")

account_name = payload_dict.get("from").strip()
memo_key = payload_dict.get("key").strip()

assert account_name, "Missing account name 'from'"
assert memo_key, "missing 'key'"

try:
self.publickey_class(memo_key, prefix=self.blockchain.prefix)
except Exception:
raise InvalidMemoKeyException("The memo key in the message is invalid")

# Load account from blockchain
try:
account = self.account_class(
account_name, blockchain_instance=self.blockchain
)
except AccountDoesNotExistsException:
raise AccountDoesNotExistsException(
"Could not find account {}. Are you connected to the right chain?".format(
account_name
)
)

# Test if memo key is the same as on the blockchain
if not account["options"]["memo_key"] == memo_key:
raise WrongMemoKey(
"Memo Key of account {} on the Blockchain ".format(account["name"])
+ "differs from memo key in the message: {} != {}".format(
account["options"]["memo_key"], memo_key
)
)

# Ensure payload and signed match
signed_target = json.dumps(self.message.get("payload"), separators=(",", ":"))
signed_actual = self.message.get("signed")
assert (
signed_target == signed_actual
), "payload doesn't match signed message: \n{}\n{}".format(
signed_target, signed_actual
)

# Reformat message
enc_message = self.message.get("signed")

# Verify Signature
pubkey = verify_message(enc_message, unhexlify(signature))

# Verify pubky
pk = self.publickey_class(
hexlify(pubkey).decode("ascii"), prefix=self.blockchain.prefix
)
if format(pk, self.blockchain.prefix) != memo_key:
raise InvalidMessageSignature("The signature doesn't match the memo key")

self.signed_by_account = account
self.signed_by_name = account["name"]
self.plain_message = payload_dict.get("text")

return True


class Message(MessageV1, MessageV2):
supported_formats = (MessageV1, MessageV2)
valid_exceptions = (
AccountDoesNotExistsException,
InvalidMessageSignature,
WrongMemoKey,
InvalidMemoKeyException,
)

def __init__(self, *args, **kwargs):
for _format in self.supported_formats:
try:
_format.__init__(self, *args, **kwargs)
return
except self.valid_exceptions as e:
raise e
except Exception as e:
log.warning(
"{}: Couldn't init: {}: {}".format(
_format.__name__, e.__class__.__name__, str(e)
)
)

def verify(self, **kwargs):
for _format in self.supported_formats:
try:
return _format.verify(self, **kwargs)
except self.valid_exceptions as e:
raise e
except Exception as e:
log.warning(
"{}: Couldn't verify: {}: {}".format(
_format.__name__, e.__class__.__name__, str(e)
)
)
raise ValueError("No Decoder accepted the message")

def sign(self, *args, **kwargs):
for _format in self.supported_formats:
try:
return _format.sign(self, *args, **kwargs)
except self.valid_exceptions as e:
raise e
except Exception as e:
log.warning(
"{}: Couldn't sign: {}: {}".format(
_format.__name__, e.__class__.__name__, str(e)
)
)
raise ValueError("No Decoder accepted the message")
1 change: 1 addition & 0 deletions requirements-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ dateutils==0.6.6
# Unit testing
pytest==4.4.0
pytest-mock==1.10.3
pytest-benchmark==3.2.2
coverage==4.5.3
mock==2.0.0

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ pylibscrypt==1.8.0
pycryptodome==3.8.0
appdirs==1.4.3
scrypt==0.8.13
secp256k1==0.13.2
# secp256k1==0.13.2
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from setuptools import setup

VERSION = "1.1.14"
VERSION = "1.1.16"
URL = "https://github.com/xeroc/python-graphenelib"

setup(
Expand Down
20 changes: 19 additions & 1 deletion tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@
from graphenecommon.committee import Committee as GCommittee
from graphenecommon.block import Block as GBlock, BlockHeader as GBlockHeader
from graphenecommon.blockchain import Blockchain as GBLockchain
from graphenecommon.message import Message as GMessage
from graphenecommon.message import (
Message as GMessage,
MessageV1 as GMessageV1,
MessageV2 as GMessageV2,
)
from graphenecommon.blockchainobject import ObjectCache, BlockchainObject
from graphenecommon.price import Price as GPrice
from graphenecommon.wallet import Wallet as GWallet
Expand Down Expand Up @@ -258,6 +262,20 @@ def define_classes(self):
self.publickey_class = PublicKey


@BlockchainInstance.inject
class MessageV1(GMessageV1):
def define_classes(self):
self.account_class = Account
self.publickey_class = PublicKey


@BlockchainInstance.inject
class MessageV2(GMessageV2):
def define_classes(self):
self.account_class = Account
self.publickey_class = PublicKey


@BlockchainInstance.inject
class Price(GPrice):
def define_classes(self):
Expand Down
48 changes: 48 additions & 0 deletions tests/test_benchmark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
import time
from .fixtures import (
Base58,
base58decode,
base58encode,
ripemd160,
base58CheckEncode,
base58CheckDecode,
gphBase58CheckEncode,
gphBase58CheckDecode,
)

wif_base58 = "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ"
wif_hex = "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d"
wif_hex_raw = "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d"
wif_gphbase85 = "6Mcb23muAxyXaSMhmB6B1mqkvLdWhtuFZmnZsxDczHRv4mL6Y"


def benchmark_base58():
return (base58decode(wif_base58), base58encode(wif_hex))


def benchmark_base58check():
return (base58CheckDecode(wif_base58), base58CheckEncode(0x80, wif_hex_raw))


def benchmark_gphbase58check():
return (gphBase58CheckDecode(wif_gphbase85), gphBase58CheckEncode(wif_hex_raw))


def test_base58(benchmark):
a, b = benchmark(benchmark_base58)
assert a == wif_hex
assert b == wif_base58


def test_base58check(benchmark):
a, b = benchmark(benchmark_base58check)
print(a, b)
assert a == wif_hex_raw
assert b == wif_base58


def test_gphbase58check(benchmark):
a, b = benchmark(benchmark_gphbase58check)
assert a == wif_hex_raw
assert b == wif_gphbase85
20 changes: 19 additions & 1 deletion tests/test_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import unittest
from .fixtures import fixture_data, Message
from .fixtures import fixture_data, Message, MessageV1, MessageV2
from pprint import pprint
from graphenecommon.exceptions import InvalidMessageSignature


Expand Down Expand Up @@ -40,3 +41,20 @@ def test_verify_message(self):
"2034f601e175a25cf9f60a828650301f57c9efab53929b6a82fb413feb8a786fcb3ba4238dd8bece03aee38526ee363324d43944d4a3f9dc624fbe53ef5f0c9a5e\n"
"-----END GRAPHENE SIGNED MESSAGE-----"
).verify()

def test_v2_enc(self):
m = MessageV2("foobar")
c = m.sign(account="init0")
v = MessageV2(c)
v.verify()

def test_v2andv1_enc(self):
m = MessageV2("foobar")
c = m.sign(account="init0")
v = Message(c)
v.verify()

m = MessageV1("foobar")
c = m.sign(account="init0")
v = Message(c)
v.verify()

0 comments on commit b438ff3

Please sign in to comment.