From 57deb867656c5715cb8bb6c55e329697b4670700 Mon Sep 17 00:00:00 2001 From: Maciej Janiszewski Date: Fri, 10 Apr 2020 13:16:20 +0200 Subject: [PATCH] Bump to 1.0.2 (#7) * Cleanup naming, add examples to readme * Bump ecdsa --- README.md | 75 ++++++++++++++++++++++++++++++++++++ aioeos/__init__.py | 37 ++++++++++++++++++ aioeos/account.py | 18 ++++----- aioeos/keys.py | 19 +++------ aioeos/rpc.py | 4 +- docs/source/changelog.rst | 11 ++++++ docs/source/introduction.rst | 14 +++---- poetry.lock | 39 +++++++++++-------- pyproject.toml | 4 +- tests/conftest.py | 8 ++-- tests/test_account.py | 24 +++++++----- tests/test_contracts.py | 3 +- tests/test_keys.py | 12 +++--- tests/test_real_rpc.py | 2 +- tests/test_rpc.py | 4 +- 15 files changed, 198 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 27b7206..626108b 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,81 @@ Library is available on PyPi, you can simply install it using `pip`. $ pip install aioeos ``` +## Usage + +### Importing a private key + +``` +from aioeos import EosAccount + +account = EosAccount(private_key='your key') +``` + +### Transferring funds + +``` +from aioeos import EosJsonRpc, EosTransaction +from aioeos.contracts import eosio_token + + +rpc = EosJsonRpc(url='http://127.0.0.1:8888') +block = await rpc.get_head_block() + +transaction = EosTransaction( + ref_block_num=block['block_num'] & 65535, + ref_block_prefix=block['ref_block_prefix'], + actions=[ + eosio_token.transfer( + from_addr=account.name, + to_addr='mysecondacc1', + quantity='1.0000 EOS', + authorization=[account.authorization('active')] + ) + ] +) +await rpc.sign_and_push_transaction(transaction, keys=[account.key]) +``` + +### Creating a new account + +``` +from aioeos import EosJsonRpc, EosTransaction, EosAuthority +from aioeos.contracts import eosio + + +main_account = EosAccount(name='mainaccount1', private_key='private key') +new_account = EosAccount(name='mysecondacc1') +owner = EosAuthority( + threshold=1, + keys=[new_account.key.to_key_weight(1)] +) + +rpc = EosJsonRpc(url='http://127.0.0.1:8888') +block = await rpc.get_head_block() + +await rpc.sign_and_push_transaction( + EosTransaction( + ref_block_num=block['block_num'] & 65535, + ref_block_prefix=block['ref_block_prefix'], + actions=[ + eosio.newaccount( + main_account.name, + new_account.name, + owner=owner, + authorization=[main_account.authorization('active')] + ), + eosio.buyrambytes( + main_account.name, + new_account.name, + 2048, + authorization=[main_account.authorization('active')] + ) + ], + ), + keys=[main_account.key] +) +``` + ## Documentation Docs and usage examples are available [here](https://aioeos.readthedocs.io/en/latest). diff --git a/aioeos/__init__.py b/aioeos/__init__.py index e69de29..e31277c 100644 --- a/aioeos/__init__.py +++ b/aioeos/__init__.py @@ -0,0 +1,37 @@ +from . import serializer, exceptions # noqa +from .account import EosAccount # noqa +from .keys import EosKey # noqa +from .rpc import EosJsonRpc # noqa +from .types import ( + UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, VarUInt, Float32, + Float64, TimePointSec, TimePoint, Name, AbiBytes, BaseAbiObject, + is_abi_object, EosPermissionLevel, EosKeyWeight, EosPermissionLevelWeight, + EosWaitWeight, EosAuthority, AbiActionPayload, EosAction, EosTransaction +) # noqa + +__all__ = [ + 'serializer', + 'exceptions', + + # Account + 'EosAccount', + + # Keys + 'EosKey', + + # RPC + 'EosJsonRpc', + + # types + # base ABI types + 'UInt8', 'UInt16', 'UInt32', 'UInt64', 'Int8', 'Int16', 'Int32', 'Int64', + 'VarUInt', 'Float32', 'Float64', 'TimePointSec', 'TimePoint', 'Name', + 'AbiBytes', 'BaseAbiObject', 'is_abi_object', + + # authority + 'EosPermissionLevel', 'EosKeyWeight', 'EosPermissionLevelWeight', + 'EosWaitWeight', 'EosAuthority', + + # transaction + 'AbiActionPayload', 'EosAction', 'EosTransaction' +] diff --git a/aioeos/account.py b/aioeos/account.py index 80c66b9..1c5afb7 100644 --- a/aioeos/account.py +++ b/aioeos/account.py @@ -1,10 +1,10 @@ from typing import Optional -from aioeos.keys import EOSKey +from aioeos.keys import EosKey from aioeos.types import EosPermissionLevel, EosPermissionLevelWeight -class EOSAccount: +class EosAccount: """ Describes account on EOS blockchain. Contrary to other blockchains such as Bitcoin or Ethereum, public key is not an address. An account can have @@ -17,12 +17,12 @@ class EOSAccount: :param name: name of the account, - Please provide key in one of the formats - EOSKey instance, private key in - WIF format and public key. Only one of these is accepted. If no key is - provided, a new one will be generated. + Please provide key in one of the formats - EosKey instance, private key in + WIF or PVT format and public key. Only one of these is accepted. If no key + is provided, a new one will be generated. - ;param key: EOSKey instance, - :param private_key: private key in wif format, + ;param key: EosKey instance, + :param private_key: private key in WIF or PVT format, :param public_key: public key """ @@ -30,7 +30,7 @@ def __init__( self, name: str, *, - key: Optional[EOSKey] = None, + key: Optional[EosKey] = None, private_key: str = '', public_key: str = '', ): @@ -47,7 +47,7 @@ def __init__( self.name = name self.key = ( - key or EOSKey(public_key=public_key, private_key=private_key) + key or EosKey(public_key=public_key, private_key=private_key) ) def authorization(self, permission: str) -> EosPermissionLevel: diff --git a/aioeos/keys.py b/aioeos/keys.py index 5660100..fafb1de 100644 --- a/aioeos/keys.py +++ b/aioeos/keys.py @@ -8,14 +8,14 @@ from aioeos.types import EosKeyWeight -class EOSKey: +class EosKey: """ - EOSKey instance. + EosKey instance. Depends on which kwargs are given, this works in a different way: - No kwargs - generates a new private key - Only private_key - public key is being derived from private key - - Only public_key - EOSKey instance has no private key + - Only public_key - EosKey instance has no private key """ def __init__(self, *, private_key: str = None, public_key: str = None): @@ -143,17 +143,10 @@ def _recovery_pubkey_param(self, digest, signature): if p.to_string() == self._vk.to_string(): return i - def _compress_pubkey(self): - order = self._vk.curve.generator.order() - point = self._vk.pubkey.point - return ( - bytes([2 + (point.y() & 1)]) - + ecdsa.util.number_to_string(point.x(), order) - ) - def to_public(self): """Returns compressed, base58 encoded public key prefixed with EOS""" - return f'EOS{self._check_encode(self._compress_pubkey())}' + compressed = self._vk.to_string(encoding='compressed') + return f'EOS{self._check_encode(compressed)}' def to_wif(self): """Converts private key to legacy WIF format""" @@ -230,5 +223,5 @@ def to_key_weight(self, weight: int) -> EosKeyWeight: return EosKeyWeight(key=self.to_public(), weight=weight) def __eq__(self, other) -> bool: - assert isinstance(other, EOSKey), 'Can compare only to EOSKey instance' + assert isinstance(other, EosKey), 'Can compare only to EosKey instance' return self.to_public() == other.to_public() diff --git a/aioeos/rpc.py b/aioeos/rpc.py index c5c4c7d..9e7db35 100644 --- a/aioeos/rpc.py +++ b/aioeos/rpc.py @@ -6,7 +6,7 @@ from aiohttp import ClientSession from aioeos import exceptions, serializer -from aioeos.keys import EOSKey +from aioeos.keys import EosKey from aioeos.types import EosTransaction, is_abi_object @@ -206,7 +206,7 @@ async def sign_and_push_transaction( transaction: EosTransaction, *, context_free_bytes: bytes = bytes(32), - keys: List[EOSKey] = [] + keys: List[EosKey] = [] ): for action in transaction.actions: if isinstance(action.data, dict): diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 458608f..67a0914 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -1,6 +1,17 @@ Changelog ========= +1.0.2 (10.04.2020) +------------------ + +- Tested library against RPC node, +- EosAccount API, +- Support using EOS ABI objects as action payloads, +- Signing support in RPC client, +- Cached chain ID, +- Simplified Getting Started guide, +- EOSKey renamed to EosKey + 1.0.1 (03.04.2020) ------------------ diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index f2df4e1..e4f6faa 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -110,11 +110,10 @@ account. :: - from aioeos.account import EOSAccount + from aioeos import EosAccount, EosTransaction from aioeos.contracts import eosio_token - from aioeos.types import EosTransaction - test_account = EOSAccount( + test_account = EosAccount( name='eostest12345', private_key='5JeaxignXEg3mGwvgmwxG6w6wHcRp9ooPw81KjrP2ah6TWSECDN' ) @@ -135,7 +134,7 @@ blockchain, we need to ask our RPC node to convert it for us. :: - from aioeos.rpc import EosJsonRpc + from aioeos import EosJsonRpc rpc = EosJsonRpc(url='http://127.0.0.1:8888') @@ -184,14 +183,11 @@ Complete example code:: import asyncio - from aioeos.account import EOSAccount + from aioeos import EosAccount, EosJsonRpc, EosTransaction from aioeos.contracts import eosio_token - from aioeos.rpc import EosJsonRpc - from aioeos.types import EosTransaction - async def example(): - test_account = EOSAccount( + test_account = EosAccount( name='eostest12345', private_key='5JeaxignXEg3mGwvgmwxG6w6wHcRp9ooPw81KjrP2ah6TWSECDN' ) diff --git a/poetry.lock b/poetry.lock index 0a9a7e1..8931f4d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -91,7 +91,7 @@ description = "Python package for providing Mozilla's CA Bundle." name = "certifi" optional = false python-versions = "*" -version = "2019.11.28" +version = "2020.4.5.1" [[package]] category = "main" @@ -134,8 +134,15 @@ category = "main" description = "ECDSA cryptographic signature library (pure python)" name = "ecdsa" optional = false -python-versions = "*" -version = "0.13.3" +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "0.15" + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] [[package]] category = "dev" @@ -327,7 +334,7 @@ description = "Python parsing module" name = "pyparsing" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.4.6" +version = "2.4.7" [[package]] category = "dev" @@ -423,7 +430,7 @@ security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] [[package]] -category = "dev" +category = "main" description = "Python 2 and 3 compatibility utilities" name = "six" optional = false @@ -580,7 +587,7 @@ description = "Backported and Experimental Type Hints for Python 3.5+" name = "typing-extensions" optional = false python-versions = "*" -version = "3.7.4.1" +version = "3.7.4.2" [[package]] category = "dev" @@ -629,7 +636,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "1513a41927f800b0fab11b7055942ec7691bec72e1a2b0bf8635ed08479eb8ec" +content-hash = "ad4bc1c5e28daa0ab28cc28d63691e79f850c04b6aa5fa5ff37bf02971bea25e" python-versions = "^3.7" [metadata.files] @@ -676,8 +683,8 @@ base58 = [ {file = "base58-2.0.0.tar.gz", hash = "sha256:c83584a8b917dc52dd634307137f2ad2721a9efb4f1de32fc7eaaaf87844177e"}, ] certifi = [ - {file = "certifi-2019.11.28-py2.py3-none-any.whl", hash = "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3"}, - {file = "certifi-2019.11.28.tar.gz", hash = "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"}, + {file = "certifi-2020.4.5.1-py2.py3-none-any.whl", hash = "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304"}, + {file = "certifi-2020.4.5.1.tar.gz", hash = "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"}, ] chardet = [ {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, @@ -725,8 +732,8 @@ docutils = [ {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, ] ecdsa = [ - {file = "ecdsa-0.13.3-py2.py3-none-any.whl", hash = "sha256:9814e700890991abeceeb2242586024d4758c8fc18445b194a49bd62d85861db"}, - {file = "ecdsa-0.13.3.tar.gz", hash = "sha256:163c80b064a763ea733870feb96f9dd9b92216cfcacd374837af18e4e8ec3d4d"}, + {file = "ecdsa-0.15-py2.py3-none-any.whl", hash = "sha256:867ec9cf6df0b03addc8ef66b56359643cb5d0c1dc329df76ba7ecfe256c8061"}, + {file = "ecdsa-0.15.tar.gz", hash = "sha256:8f12ac317f8a1318efa75757ef0a651abe12e51fc1af8838fb91079445227277"}, ] entrypoints = [ {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, @@ -859,8 +866,8 @@ pygments = [ {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"}, ] pyparsing = [ - {file = "pyparsing-2.4.6-py2.py3-none-any.whl", hash = "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"}, - {file = "pyparsing-2.4.6.tar.gz", hash = "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f"}, + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytest = [ {file = "pytest-5.4.1-py3-none-any.whl", hash = "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172"}, @@ -954,9 +961,9 @@ typed-ast = [ {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, ] typing-extensions = [ - {file = "typing_extensions-3.7.4.1-py2-none-any.whl", hash = "sha256:910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d"}, - {file = "typing_extensions-3.7.4.1-py3-none-any.whl", hash = "sha256:cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575"}, - {file = "typing_extensions-3.7.4.1.tar.gz", hash = "sha256:091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2"}, + {file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"}, + {file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"}, + {file = "typing_extensions-3.7.4.2.tar.gz", hash = "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae"}, ] urllib3 = [ {file = "urllib3-1.25.8-py2.py3-none-any.whl", hash = "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc"}, diff --git a/pyproject.toml b/pyproject.toml index aebe2b7..4d70355 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "aioeos" -version = "1.0.1" +version = "1.0.2" description = "Async library for interacting with EOS.io blockchain" authors = ["Maciej Janiszewski "] license = "MIT" @@ -14,7 +14,7 @@ keywords = ["blockchain", "eos", "async", "eosio", "cryptocurrency"] python = "^3.7" aiohttp = "^3.3.1" base58 = "2.0.0" -ecdsa = "0.13.3" +ecdsa = "^0.15" [tool.poetry.dev-dependencies] flake8 = "^3.7.9" diff --git a/tests/conftest.py b/tests/conftest.py index 106d82d..d0d680a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,10 +2,8 @@ import pytest -from aioeos.account import EOSAccount +from aioeos import EosAccount, EosJsonRpc, EosTransaction, EosAuthority from aioeos.contracts import eosio -from aioeos.rpc import EosJsonRpc -from aioeos.types import EosAuthority, EosTransaction @pytest.fixture @@ -15,7 +13,7 @@ def rpc(): @pytest.fixture def main_account(): - return EOSAccount( + return EosAccount( name='eostest12345', private_key='5JeaxignXEg3mGwvgmwxG6w6wHcRp9ooPw81KjrP2ah6TWSECDN' ) @@ -27,7 +25,7 @@ async def second_account(loop, main_account, rpc): random.choice('12345abcdefghijklmnopqrstuvwxyz') for i in range(12) ) - account = EOSAccount(name=name) + account = EosAccount(name=name) block = await rpc.get_head_block() owner = EosAuthority( diff --git a/tests/test_account.py b/tests/test_account.py index af8e68b..35e77d1 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -1,28 +1,32 @@ import pytest -from aioeos.account import EOSAccount -from aioeos.keys import EOSKey +from aioeos import EosAccount, EosKey def test_new_account(): - account_1 = EOSAccount(name='account1') - account_2 = EOSAccount(name='account2') + account_1 = EosAccount(name='account1') + account_2 = EosAccount(name='account2') assert account_1.key != account_2.key def test_only_one_key(): - key = EOSKey() + key = EosKey() # this is fine - EOSAccount(name='account', key=key) + EosAccount(name='account', key=key) + + # this is also fine + EosAccount(name='account', private_key=key.to_wif()) + EosAccount(name='account', private_key=key.to_pvt()) + EosAccount(name='account', public_key=key.to_public()) # this isn't with pytest.raises(AssertionError): - EOSAccount(name='account', key=key, public_key=key.to_public()) + EosAccount(name='account', key=key, public_key=key.to_public()) # absolutely wrong with pytest.raises(AssertionError): - EOSAccount( + EosAccount( name='account', key=key, public_key=key.to_public(), @@ -31,14 +35,14 @@ def test_only_one_key(): def test_account_authorization(): - account = EOSAccount(name='account') + account = EosAccount(name='account') authorization = account.authorization('active') assert authorization.actor == account.name assert authorization.permission == 'active' def test_account_permission_level_weight(): - account = EOSAccount(name='account') + account = EosAccount(name='account') authorization = account.authorization('active') permission_level_weight = account.permission_level_weight('active', 3) assert permission_level_weight.permission == authorization diff --git a/tests/test_contracts.py b/tests/test_contracts.py index 539505a..028ce68 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -1,4 +1,5 @@ -from aioeos.types import EosAction, EosAuthority +from aioeos import EosAction +from aioeos.types import EosAuthority from aioeos.contracts import eosio, eosio_token diff --git a/tests/test_keys.py b/tests/test_keys.py index 77db53c..eff21b4 100644 --- a/tests/test_keys.py +++ b/tests/test_keys.py @@ -1,11 +1,11 @@ -from aioeos.keys import EOSKey +from aioeos import EosKey def test_eos_key_creating(): """Checks if we can generate a key and sign something with it""" # Both keys should be different - key1 = EOSKey() - key2 = EOSKey() + key1 = EosKey() + key2 = EosKey() # sign an example string with key 1 example_string = 'Lorem ipsum dolor sit amet amet.'.encode('utf-8') @@ -16,7 +16,7 @@ def test_eos_key_creating(): assert not key2.verify(signature, example_string) # We should be able to convert the key to wif format and load it - key3 = EOSKey(private_key=key1.to_wif()) + key3 = EosKey(private_key=key1.to_wif()) assert key3.verify(signature, example_string) @@ -30,10 +30,10 @@ def test_eos_key_restore(): pvt_public = 'EOS859gxfnXyUriMgUeThh1fWv3oqcpLFyHa3TfFYC4PK2HqhToVM' # should return the same values as the original ones - wif_key = EOSKey(private_key=wif_private) + wif_key = EosKey(private_key=wif_private) assert wif_key.to_public() == wif_public assert wif_key.to_wif() == wif_private - pvt_key = EOSKey(private_key=pvt_private) + pvt_key = EosKey(private_key=pvt_private) assert pvt_key.to_public() == pvt_public assert pvt_key.to_pvt() == pvt_private diff --git a/tests/test_real_rpc.py b/tests/test_real_rpc.py index 644c18a..e792d33 100644 --- a/tests/test_real_rpc.py +++ b/tests/test_real_rpc.py @@ -2,8 +2,8 @@ import pytest +from aioeos import EosTransaction from aioeos.contracts import eosio_token -from aioeos.types import EosTransaction @pytest.mark.skipif( diff --git a/tests/test_rpc.py b/tests/test_rpc.py index 2e79d52..6121ab8 100644 --- a/tests/test_rpc.py +++ b/tests/test_rpc.py @@ -7,8 +7,8 @@ import pytest from yarl import URL -from aioeos import exceptions -from aioeos.types import BaseAbiObject, EosAction, EosTransaction, UInt8 +from aioeos import exceptions, EosAction, EosTransaction +from aioeos.types import BaseAbiObject, UInt8 @pytest.fixture