Skip to content
This repository has been archived by the owner on Jan 24, 2024. It is now read-only.

Commit

Permalink
Merge pull request #67 from eosnewyork/release/2.1.x
Browse files Browse the repository at this point in the history
Release/2.1.x
  • Loading branch information
deckb committed Jun 29, 2021
2 parents ed55d65 + 0310e29 commit c29c3ca
Show file tree
Hide file tree
Showing 19 changed files with 844 additions and 572 deletions.
1 change: 0 additions & 1 deletion eospy/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

2 changes: 1 addition & 1 deletion eospy/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '2.0.1'
__version__ = '2.1.0'
271 changes: 134 additions & 137 deletions eospy/cleos.py

Large diffs are not rendered by default.

153 changes: 79 additions & 74 deletions eospy/command_line.py

Large diffs are not rendered by default.

35 changes: 18 additions & 17 deletions eospy/dynamic_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,43 @@
#
import requests

class DynamicUrl :
#def __init__(self, url='http://localhost:8888', version='v1', cache=None) :
def __init__(self, url='http://localhost:8888', version='v1', cache=None) :

class DynamicUrl:
# def __init__(self, url='http://localhost:8888', version='v1', cache=None) :
def __init__(self, url='http://localhost:8888', version='v1', cache=None):
self._cache = cache or []
self._baseurl = url
self._version = version

def __getattr__(self, name) :
def __getattr__(self, name):
return self._(name)

def __del__(self) :
def __del__(self):
pass

def _(self, name) :
return DynamicUrl(url=self._baseurl, version=self._version, cache=self._cache+[name])
def _(self, name):
return DynamicUrl(url=self._baseurl, version=self._version, cache=self._cache + [name])

def method(self) :
def method(self):
return self._cache

def create_url(self):
url_str = '{0}/{1}'.format(self._baseurl,self._version)
for obj in self.method() :
url_str = '{0}/{1}'.format(self._baseurl, self._version)
for obj in self.method():
url_str = '{0}/{1}'.format(url_str, obj)
return url_str

def get_url(self, url, params=None, json=None, timeout=30) :
def get_url(self, url, params=None, json=None, timeout=30):
# get request
r = requests.get(url,params=params, json=json, timeout=timeout)
r = requests.get(url, params=params, json=json, timeout=timeout)
r.raise_for_status()
return r.json()

def post_url(self, url, params=None, json=None, data=None, timeout=30) :
def post_url(self, url, params=None, json=None, data=None, timeout=30):
# post request
r = requests.post(url,params=params, json=json, data=data, timeout=timeout)
try :
r = requests.post(url, params=params, json=json, data=data, timeout=timeout)
try:
r.raise_for_status()
except :
except:
raise requests.exceptions.HTTPError('Error: {}'.format(r.json()))
return r.json()
11 changes: 10 additions & 1 deletion eospy/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,47 @@ class InvalidKeyFile(Exception):
''' Raised when the key file format is invalid '''
pass


class InvalidPermissionFormat(Exception):
''' Raised when the permission format is invalid'''
pass


class EOSKeyError(Exception):
''' Raised when there is an EOSKey error '''
pass


class EOSMsigInvalidProposal(Exception):
''' Raised when an invalid proposal is queried'''
pass


class EOSBufferInvalidType(Exception):
''' Raised when trying to encode/decode an invalid type '''
pass


class EOSInvalidSchema(Exception):
''' Raised when trying to process a schema '''
pass


class EOSUnknownObj(Exception):
''' Raised when an object is not found in the ABI '''
pass


class EOSAbiProcessingError(Exception):
''' Raised when the abi action cannot be processed '''
pass


class EOSSetSameCode(Exception):
''' Raised when the code would not change on a set'''
pass


class EOSSetSameAbi(Exception):
''' Raised when the abi would not change on a set'''
pass
pass
127 changes: 67 additions & 60 deletions eospy/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
import ecdsa
import re
from binascii import hexlify, unhexlify
from .utils import sha256, ripemd160, str_to_hex, hex_to_int
from .utils import sha256, ripemd160
from .signer import Signer
import hashlib
import time
import struct
import array

def get_curve(key_type) :
if key_type == 'R1' :
return ecdsa.NIST256p
return ecdsa.SECP256k1

def check_wif(key) :
if isinstance(key, str) :
try :
Expand All @@ -19,89 +23,93 @@ def check_wif(key) :
pass
return False

class EOSKey(Signer) :
def __init__(self, private_str='') :

class EOSKey(Signer):
def __init__(self, private_str=''):
''' '''
if private_str :
if private_str:
private_key, format, key_type = self._parse_key(private_str)
self._sk = ecdsa.SigningKey.from_string(unhexlify(private_key), curve=ecdsa.SECP256k1)
self._key_type = key_type
self._curve = get_curve(key_type)
self._sk = ecdsa.SigningKey.from_string(unhexlify(private_key), curve=self._curve)
else :
prng = self._create_entropy()
self._sk = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1, entropy=prng)
self._key_type = 'K1'
self._curve = get_curve(self._key_type)
self._sk = ecdsa.SigningKey.generate(curve=self._curve, entropy=prng)
self._vk = self._sk.get_verifying_key()

def __str__(self) :
def __str__(self):
return self.to_public()
def _parse_key(self, private_str) :

def _parse_key(self, private_str):
''' '''
match = re.search('^PVT_([A-Za-z0-9]+)_([A-Za-z0-9]+)$', private_str)
if not match :
# legacy WIF - format
if not match:
# legacy WIF - format
version_key = self._check_decode(private_str, 'sha256x2')
# ensure first 2 chars == 0x80
version = int(version_key[0:2],16)
if not version == 0x80 :
version = int(version_key[0:2], 16)
if not version == 0x80:
raise ValueError('Expected version 0x80, instead got {0}', version)
private_key = version_key[2:]
key_type = 'K1'
format = 'WIF'
else :
else:
key_type, key_string = match.groups()
private_key = self._check_decode(key_string, key_type)
format = 'PVT'
return (private_key, format, key_type)

def _create_entropy(self) :
def _create_entropy(self):
''' '''
ba = bytearray(os.urandom(32))
seed = sha256(ba)
return ecdsa.util.PRNG(seed)

def _check_encode(self, key_buffer, key_type=None) :
def _check_encode(self, key_buffer, key_type=None):
''' '''
if isinstance(key_buffer, bytes) :
if isinstance(key_buffer, bytes):
key_buffer = key_buffer.decode()
check = key_buffer
if key_type == 'sha256x2' :
if key_type == 'sha256x2':
first_sha = sha256(unhexlify(check))
chksum = sha256(unhexlify(first_sha))[:8]
else :
if key_type :
check += hexlify(bytearray(key_type,'utf-8')).decode()
else:
if key_type:
check += hexlify(bytearray(key_type, 'utf-8')).decode()
chksum = ripemd160(unhexlify(check))[:8]
return base58.b58encode(unhexlify(key_buffer+chksum))
def _check_decode(self, key_string, key_type=None) :
return base58.b58encode(unhexlify(key_buffer + chksum))

def _check_decode(self, key_string, key_type=None):
''' '''
buffer = hexlify(base58.b58decode(key_string)).decode()
chksum = buffer[-8:]
key = buffer[:-8]
if key_type == 'sha256x2' :
if key_type == 'sha256x2':
# legacy
first_sha = sha256(unhexlify(key))
newChk = sha256(unhexlify(first_sha))[:8]
else :
else:
check = key
if key_type :
if key_type:
check += hexlify(bytearray(key_type, 'utf-8')).decode()
newChk = ripemd160(unhexlify(check))[:8]
#print('newChk: '+newChk)
if chksum != newChk :
if chksum != newChk:
raise ValueError('checksums do not match: {0} != {1}'.format(chksum, newChk))
return key


def _recover_key(self, digest, signature, i) :
def _recover_key(self, digest, signature, i):
''' Recover the public key from the sig
http://www.secg.org/sec1-v2.pdf
'''
curve = ecdsa.SECP256k1.curve
G = ecdsa.SECP256k1.generator
order = ecdsa.SECP256k1.order
curve = self._curve.curve
G = self._curve.generator
order = self._curve.order
yp = (i %2)
r, s = ecdsa.util.sigdecode_string(signature, order)
x = r + (i // 2 ) * order
x = r + (i // 2) * order
alpha = ((x * x * x) + (curve.a() * x) + curve.b()) % curve.p()
beta = ecdsa.numbertheory.square_root_mod_prime(alpha, curve.p())
y = beta if (beta - yp) % 2 == 0 else curve.p() - beta
Expand All @@ -111,21 +119,21 @@ def _recover_key(self, digest, signature, i) :
# compute Q
Q = ecdsa.numbertheory.inverse_mod(r, order) * (s * R + (-e % order) * G)
# verify message
if not ecdsa.VerifyingKey.from_public_point(Q, curve=ecdsa.SECP256k1).verify_digest(signature, digest,
if not ecdsa.VerifyingKey.from_public_point(Q, curve=self._curve).verify_digest(signature, digest,
sigdecode=ecdsa.util.sigdecode_string) :
return None
return ecdsa.VerifyingKey.from_public_point(Q, curve=ecdsa.SECP256k1)
return ecdsa.VerifyingKey.from_public_point(Q, curve=self._curve)

def _recovery_pubkey_param(self, digest, signature) :
''' Use to derive a number that will allow for the easy recovery
of the public key from the signature
'''
for i in range(0,4) :
for i in range(0, 4):
p = self._recover_key(digest, signature, i)
if (p.to_string() == self._vk.to_string()) :
if (p.to_string() == self._vk.to_string()):
return i

def _compress_pubkey(self) :
def _compress_pubkey(self):
''' '''
order = self._sk.curve.generator.order()
p = self._vk.pubkey.point
Expand All @@ -142,12 +150,12 @@ def _is_canonical(self, sig):
t4 = not (sig[33] == 0 and ((sig[34] & 0x80) == 0))
return t1 and t2 and t3 and t4

def to_public(self) :
def to_public(self):
''' '''
cmp = self._compress_pubkey()
return 'EOS' + self._check_encode(cmp).decode()
def to_wif(self) :

def to_wif(self):
''' '''
pri_key = '80' + hexlify(self._sk.to_string()).decode()
return self._check_encode(pri_key, 'sha256x2').decode()
Expand All @@ -157,25 +165,25 @@ def sign_string(self, data, encoding="utf-8"):
digest = sha256(bytearray(data, encoding))
return self.sign(digest)

def sign(self, digest) :
def sign(self, digest):
''' '''
cnt = 0
# convert digest to hex string
digest = unhexlify(digest)
if len(digest) != 32 :
if len(digest) != 32:
raise ValueError("32 byte buffer required")
while 1 :
while 1:
# get deterministic k
if cnt:
sha_digest = hashlib.sha256(digest + bytearray(cnt)).digest()
else :
else:
sha_digest = hashlib.sha256(digest).digest()
k = ecdsa.rfc6979.generate_k( self._sk.curve.generator.order(),
self._sk.privkey.secret_multiplier,
hashlib.sha256,
# hashlib.sha256(digest + struct.pack('d', time.time())).digest() # use time to randomize
sha_digest
)
k = ecdsa.rfc6979.generate_k(self._sk.curve.generator.order(),
self._sk.privkey.secret_multiplier,
hashlib.sha256,
# hashlib.sha256(digest + struct.pack('d', time.time())).digest() # use time to randomize
sha_digest
)
# sign the message
sigder = self._sk.sign_digest(digest, sigencode=ecdsa.util.sigencode_der, k=k)

Expand All @@ -187,7 +195,7 @@ def sign(self, digest) :
# ensure signature is canonical
lenR = sigder[3]
lenS = sigder[5 + lenR]

if lenR == 32 and lenS == 32:
# derive recover parameter
i = self._recovery_pubkey_param(digest, sig)
Expand All @@ -199,19 +207,19 @@ def sign(self, digest) :
break
# if self._is_canonical(sigstr):
# break
cnt +=1
cnt += 1
if not cnt % 10 :
print('Still searching for a signature. Tried {} times.'.format(cnt))
# encode
return 'SIG_K1_' + self._check_encode(hexlify(sigstr), 'K1').decode()
return 'SIG_' + self._key_type + '_' + self._check_encode(hexlify(sigstr), self._key_type).decode()

def verify(self, encoded_sig, digest) :
def verify(self, encoded_sig, digest):
''' '''
# remove SIG_ prefix
encoded_sig = encoded_sig[4:]
# remove curve prefix
curvePre = encoded_sig[:3].strip('_')
if curvePre != 'K1' :
if curvePre != self._key_type :
raise TypeError('Unsupported curve prefix {}'.format(curvePre))

decoded_sig = self._check_decode(encoded_sig[3:], curvePre)
Expand All @@ -221,11 +229,10 @@ def verify(self, encoded_sig, digest) :
sig = decoded_sig[2:]
# verify sig by recovering the key and comparing to self._vk
# p = self._recover_key(unhexlify(digest), unhexlify(sig), recover_param)
#return self._vk.verify_digest(unhexlify(sig), unhexlify(digest), sigdecode=ecdsa.util.sigdecode_string)
# return self._vk.verify_digest(unhexlify(sig), unhexlify(digest), sigdecode=ecdsa.util.sigdecode_string)
# return p.to_string() == self._vk.to_string()
try:
self._vk.verify_digest(unhexlify(sig), unhexlify(digest), sigdecode=ecdsa.util.sigdecode_string)
except ecdsa.keys.BadSignatureError:
return False
return True

Loading

0 comments on commit c29c3ca

Please sign in to comment.