├── tests ├── __init__.py ├── test_signature.py ├── test_loader.py ├── test_peer.py ├── test_multisignature.py ├── test_transaction.py ├── test_account.py ├── test_block.py ├── test_delegate.py └── test_transport.py ├── pythark ├── __init__.py ├── signature.py ├── peer.py ├── loader.py ├── multisignature.py ├── transaction.py ├── account.py ├── block.py ├── delegate.py ├── transport.py └── api.py ├── .travis.yml ├── requirements.txt ├── LICENSE ├── setup.py ├── CHANGELOG.md ├── .gitignore └── README.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pythark/__init__.py: -------------------------------------------------------------------------------- 1 | from .api import API 2 | from .block import Block 3 | from .account import Account 4 | from .delegate import Delegate 5 | from .loader import Loader 6 | from .multisignature import MultiSignature 7 | from .peer import Peer 8 | from .signature import Signature 9 | from .transaction import Transaction 10 | from .transport import Transport 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.3" 4 | - "3.4" 5 | - "3.5" 6 | - "3.6" 7 | install: 8 | - sudo apt-get install python3-dev libusb-1.0-0-dev libudev-dev 9 | - pip install https://github.com/ArkEcosystem/arky/archive/aip11.zip 10 | - pip install -r requirements.txt 11 | script: 12 | - python -m unittest discover 13 | os: 14 | - linux -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Arky==1.0 2 | base58==0.2.5 3 | certifi==2017.11.5 4 | cffi==1.11.2 5 | chardet==3.0.4 6 | docopt==0.6.2 7 | ecdsa==0.13 8 | ECPy==0.8.2 9 | future==0.16.0 10 | hidapi==0.7.99.post21 11 | idna==2.6 12 | ledgerblue==0.1.16 13 | logzero==1.3.1 14 | olefile==0.44 15 | Pillow==4.3.0 16 | pkginfo==1.4.1 17 | protobuf==3.5.0.post1 18 | pycparser==2.18 19 | pycrypto==2.6.1 20 | PyNaCl==1.2.1 21 | pytz==2017.3 22 | requests==2.18.4 23 | requests-toolbelt==0.8.0 24 | retrying==1.3.3 25 | six==1.11.0 26 | tqdm==4.19.5 27 | twine==1.9.1 28 | urllib3==1.22 29 | -------------------------------------------------------------------------------- /tests/test_signature.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from pythark import Signature 3 | 4 | 5 | class TestSignature(unittest.TestCase): 6 | def test_get_signature_fee(self): 7 | signature = Signature() 8 | resp = signature.get_signature_fee() 9 | self.assertEqual(resp["success"], True) 10 | 11 | def test_get_address_signature_fee(self): 12 | signature = Signature() 13 | resp = signature.get_address_signature_fee("Aasu14aTs9ipZdy1FMv7ay1Vqn3jPskA8t") 14 | self.assertEqual(resp["success"], True) 15 | 16 | 17 | if __name__ == '__main__': 18 | unittest.main() -------------------------------------------------------------------------------- /tests/test_loader.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from pythark import Loader 3 | 4 | 5 | class TestLoader(unittest.TestCase): 6 | def test_get_status(self): 7 | loader = Loader() 8 | resp = loader.get_status() 9 | self.assertEqual(resp["success"], True) 10 | 11 | def test_get_sync(self): 12 | loader = Loader() 13 | resp = loader.get_sync() 14 | self.assertEqual(resp["success"], True) 15 | 16 | def test_autoconfigure(self): 17 | loader = Loader() 18 | resp = loader.autoconfigure() 19 | self.assertEqual(resp["success"], True) 20 | 21 | if __name__ == '__main__': 22 | unittest.main() -------------------------------------------------------------------------------- /tests/test_peer.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from pythark import Peer 3 | 4 | 5 | class TestPeer(unittest.TestCase): 6 | def test_get_peer(self): 7 | peer = Peer() 8 | resp = peer.get_peer("45.76.30.14", 4001) 9 | self.assertEqual(resp["success"], True) 10 | 11 | def test_get_peers(self): 12 | peer = Peer() 13 | resp = peer.get_peers() 14 | self.assertEqual(resp["success"], True) 15 | 16 | def test_get_peer_version(self): 17 | peer = Peer() 18 | resp = peer.get_peer_version() 19 | self.assertEqual(resp["success"], True) 20 | 21 | 22 | if __name__ == '__main__': 23 | unittest.main() -------------------------------------------------------------------------------- /pythark/signature.py: -------------------------------------------------------------------------------- 1 | from .api import API 2 | 3 | 4 | class Signature(API): 5 | """ 6 | Operations for Signatures. 7 | """ 8 | 9 | def get_signature_fee(self): 10 | """ Get the fee for a signature. 11 | 12 | :return: 13 | """ 14 | resp = self.get("api/signatures/fee") 15 | return resp.json() 16 | 17 | def get_address_signature_fee(self, address): 18 | """ Get the fee for a signature on a specified address. 19 | 20 | :param address: A valid Ark address. 21 | :return: 22 | """ 23 | resp = self.get("api/signatures/fee", address=address) 24 | return resp.json() 25 | -------------------------------------------------------------------------------- /tests/test_multisignature.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from pythark import MultiSignature 3 | 4 | 5 | class TestMultiSignature(unittest.TestCase): 6 | def test_get_pending(self): 7 | multisig = MultiSignature() 8 | resp = multisig.get_pending("02c7455bebeadde04728441e0f57f82f972155c088252bf7c1365eb0dc84fbf5de") 9 | self.assertEqual(resp["success"], True) 10 | 11 | #def test_get_accounts(self): 12 | # # Throw a TypeError: NetworkError when attempting to fetch resource. even in the Swagger API. 13 | # multisig = MultiSignature() 14 | # resp = multisig.get_accounts("02ff171adaef486b7db9fc160b28433d20cf43163d56fd28fee72145f0d5219a4b ") 15 | # self.assertEqual(resp["success"], True) 16 | 17 | 18 | if __name__ == '__main__': 19 | unittest.main() -------------------------------------------------------------------------------- /pythark/peer.py: -------------------------------------------------------------------------------- 1 | from .api import API 2 | 3 | 4 | class Peer(API): 5 | """ 6 | Operations for Peers. 7 | """ 8 | 9 | def get_peer(self, ip, port): 10 | """ Get a single peer. 11 | 12 | :param ip: Valid IP of the peer. 13 | :param port: Valid port of the peer. 14 | :return: 15 | """ 16 | resp = self.get("api/peers/get", ip=ip, port=port) 17 | return resp.json() 18 | 19 | def get_peers(self): 20 | """ Get all peers. 21 | 22 | :return: 23 | """ 24 | resp = self.get("api/peers") 25 | return resp.json() 26 | 27 | def get_peer_version(self): 28 | """ Get the peer version. 29 | 30 | :return: 31 | """ 32 | resp = self.get("api/peers/version") 33 | return resp.json() 34 | -------------------------------------------------------------------------------- /pythark/loader.py: -------------------------------------------------------------------------------- 1 | from .api import API 2 | 3 | 4 | class Loader(API): 5 | """ 6 | Operations for Loaders. 7 | """ 8 | # def __init__(self): 9 | # self.api = API() 10 | 11 | def get_status(self): 12 | """ Get the blockchain status. 13 | 14 | :return: 15 | """ 16 | resp = self.get("api/loader/status") 17 | return resp.json() 18 | 19 | def get_sync(self): 20 | """ Get the synchronisation status of the client. 21 | 22 | :return: 23 | """ 24 | resp = self.get("api/loader/status/sync") 25 | return resp.json() 26 | 27 | def autoconfigure(self): 28 | """ Auto-configure the client Loader. 29 | 30 | :return: 31 | """ 32 | resp = self.get("api/loader/autoconfigure") 33 | return resp.json() -------------------------------------------------------------------------------- /pythark/multisignature.py: -------------------------------------------------------------------------------- 1 | from .api import API 2 | 3 | 4 | class MultiSignature(API): 5 | """ 6 | Operations for MultiSignatures. 7 | """ 8 | 9 | def get_pending(self, publicKey): 10 | """ Get pending multi signatures transactions. 11 | 12 | :param publicKey: A valid Ark publicKey. 13 | :return: 14 | """ 15 | resp = self.get("api/multisignatures/pending", publicKey=publicKey) 16 | return resp.json() 17 | 18 | def get_accounts(self, publicKey): 19 | # TypeError: NetworkError when attempting to fetch resource. 20 | """ Get a list of accounts. 21 | 22 | :param publicKey: A valid Ark publicKey. 23 | :return: 24 | """ 25 | resp = self.get("api/multisignatures/accounts", publicKey=publicKey) 26 | return resp.json() 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017 Jolan Beer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /tests/test_transaction.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from pythark import Transaction 3 | 4 | 5 | class TestTransaction(unittest.TestCase): 6 | def test_get_transaction(self): 7 | transaction = Transaction() 8 | resp = transaction.get_transaction("a38dc6b9e6679be706d5b39eef7dd0a7a10011e63da7623082106d90834e23e1") 9 | self.assertEqual(resp["success"], True) 10 | 11 | def test_get_transactions(self): 12 | transaction = Transaction() 13 | resp = transaction.get_transactions(limit=5) 14 | self.assertEqual(resp["success"], True) 15 | 16 | def test_get_unconfirmed_transaction(self): 17 | # Need a valid unconfirmed transaction ID, which I can't find, so a little workaround. 18 | transaction = Transaction() 19 | resp = transaction.get_unconfirmed_transaction("a4ee8418827a4cd16a83c01d6623a46149fa1eb7cadb9b9cf0073861e23c8a50") 20 | self.assertEqual(resp["success"], False) 21 | 22 | def test_get_unconfirmed_transactions(self): 23 | transaction = Transaction() 24 | resp = transaction.get_unconfirmed_transactions() 25 | self.assertEqual(resp["success"], True) 26 | 27 | 28 | if __name__ == '__main__': 29 | unittest.main() -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import setup 3 | 4 | 5 | # Utility function to read the README file. 6 | # Used for the long_description. It's nice, because now 1) we have a top level 7 | # README file and 2) it's easier to type in the README file than to put a raw 8 | # string in below ... 9 | def read(fname): 10 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 11 | 12 | setup( 13 | name="Pythark", 14 | version="0.1.6", 15 | description="Ark API Wrapper", 16 | long_description=read("README.md"), 17 | author="Highjhacker", 18 | author_email="jolanbeer@gmail.com", 19 | keywords="Ark python wrapper api", 20 | license="MIT", 21 | packages=["pythark", "tests"], 22 | url="https://github.com/Highjhacker/pythark", 23 | install_requires=[ 24 | 'requests', 25 | 'logzero', 26 | 'retrying' 27 | ], 28 | classifiers=[ 29 | "Development Status :: 4 - Beta", 30 | # Indicate who your project is intended for 31 | "Intended Audience :: Developers", 32 | "Topic :: Software Development :: Build Tools", 33 | "License :: OSI Approved :: MIT License" 34 | ], 35 | python_requires=">=3", 36 | zip_safe=False 37 | ) 38 | -------------------------------------------------------------------------------- /pythark/transaction.py: -------------------------------------------------------------------------------- 1 | from .api import API 2 | 3 | 4 | class Transaction(API): 5 | """ 6 | Operations for Transactions. 7 | """ 8 | 9 | def get_transaction(self, id): 10 | """ Get a single transaction. 11 | 12 | :param id: A valid Transaction id. 13 | :return: 14 | """ 15 | resp = self.get("api/transactions/get", id=id) 16 | return resp.json() 17 | 18 | def get_transactions(self, **kwargs): 19 | """ Get all transactions. 20 | 21 | :param kwargs: Optionnal parameters. blockId, limit, orderBy, offset, senderPublicKey, vendorField, ownerPublicKey, 22 | ownerAddress, senderId, recipientId, amount, fee 23 | :return: 24 | """ 25 | resp = self.get("api/transactions", **kwargs) 26 | return resp.json() 27 | 28 | def get_unconfirmed_transaction(self, id): 29 | """ Get a single unconfirmed transaction. 30 | 31 | :param id: A valid Transaction id. 32 | :return: 33 | """ 34 | resp = self.get("api/transactions/unconfirmed/get", id=id) 35 | return resp.json() 36 | 37 | def get_unconfirmed_transactions(self, **kwargs): 38 | """ Get all unconfirmed transactions. 39 | 40 | :param kwargs: Optionnal parameters. senderPublicKey, address 41 | :return: 42 | """ 43 | resp = self.get("api/transactions/unconfirmed", **kwargs) 44 | return resp.json() -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## Released 5 | 6 | ## [0.1.6] - 2017-02-07 7 | ### Added 8 | - Added the Kapu main network. 9 | - Fix for get_common_blocks. 10 | 11 | 12 | ## [0.1.5] - 2017-12-18 13 | ### Added 14 | - Added the retrying package in the dependencies of Pythark 15 | - Added a new catch on the get function (ReadTimeout) 16 | - Added the parameter "timeout" inside the except.TimeOut block in api.py 17 | 18 | ### Changed 19 | - Changed the network from "dev" to "dark" to be in compliance with Arky. 20 | 21 | ## [0.1.4] - 2017-12-13 22 | ### Added 23 | - Updated the docs for the network usage 24 | - Added the "retrying" package 25 | - Added a retry decorator to the get function 26 | - Added a new catch on the get function 27 | ### Changed 28 | 29 | 30 | 31 | ## [0.1.3] - 2017-12-10 32 | ### Added 33 | - Can now handle others network (Kapu, Dev) 34 | - If the request time out, a fallback url will be generated and used 35 | 36 | ### Changed 37 | 38 | ### Removed 39 | 40 | ## [0.1.2] - 2017-12-10 41 | ### Added 42 | - Try catch block for the Arky import, just in case. 43 | - Added a logger for the Transport class. 44 | 45 | ### Changed 46 | - Updated the readme. 47 | - Updated the .travis file. 48 | 49 | ## [0.1.1] - 2017-11-17 50 | 51 | ### Added 52 | - Pythark can now create a new transaction. 53 | - Testing for the post_new_transaction function. 54 | - Added Travis for better testings. 55 | 56 | ### Changed 57 | 58 | 59 | ### Removed 60 | - Removed the file main.py 61 | -------------------------------------------------------------------------------- /tests/test_account.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from pythark import Account 3 | 4 | 5 | class TestAccount(unittest.TestCase): 6 | def test_getBalance(self): 7 | account = Account() 8 | resp = account.get_balance("ANwjGUcVbLXpqbBUWbjUBQWkr4MWVDuJu9") 9 | self.assertEqual(resp["success"], True) 10 | 11 | def test_getBalanceDevNetwork(self): 12 | account = Account("dev") 13 | resp = account.get_balance("DU8i1HWoaaatLKzoA9FJPvX34FdNPzFkYr") 14 | self.assertEqual(resp["success"], True) 15 | 16 | def test_getBalanceKapuMainNetwork(self): 17 | account = Account("kapu") 18 | resp = account.get_balance("KUQc9hNoG4o81t1gwkYTapPqJrxp8Zxf9Y") 19 | self.assertEqual(resp["success"], True) 20 | 21 | def test_getPublicKey(self): 22 | account = Account() 23 | resp = account.get_public_key("ANwjGUcVbLXpqbBUWbjUBQWkr4MWVDuJu9") 24 | self.assertEqual(resp["success"], True) 25 | 26 | def test_get_delegate_fee(self): 27 | account = Account() 28 | resp = account.get_delegate_fee() 29 | self.assertEqual(resp["success"], True) 30 | 31 | def test_get_delegates(self): 32 | account = Account() 33 | resp = account.get_delegates("ANwjGUcVbLXpqbBUWbjUBQWkr4MWVDuJu9", limit=10) 34 | self.assertEqual(resp["success"], True) 35 | 36 | def test_get_accounts(self): 37 | account = Account() 38 | resp = account.get_accounts("ANwjGUcVbLXpqbBUWbjUBQWkr4MWVDuJu9") 39 | self.assertEqual(resp["success"], True) 40 | 41 | def test_get_top_accounts(self): 42 | account = Account() 43 | resp = account.get_top_accounts(limit=10) 44 | self.assertEqual(resp["success"], True) 45 | 46 | 47 | if __name__ == '__main__': 48 | unittest.main() -------------------------------------------------------------------------------- /pythark/account.py: -------------------------------------------------------------------------------- 1 | from .api import API 2 | 3 | 4 | class Account(API): 5 | """ 6 | Operations for Accounts. 7 | """ 8 | 9 | def get_balance(self, address): 10 | """ Get the balance of an account. 11 | 12 | :param address: A valid Ark address. 13 | :return: 14 | """ 15 | resp = self.get("api/accounts/getBalance", address=address) 16 | return resp.json() 17 | 18 | def get_public_key(self, address): 19 | """ Get the public key of an account. 20 | 21 | :param address: A valid Ark address. 22 | :return: 23 | """ 24 | resp = self.get("api/accounts/getPublickey", address=address) 25 | return resp.json() 26 | 27 | def get_delegate_fee(self): 28 | """ Get the delegate fee of an account. 29 | 30 | :return: 31 | """ 32 | resp = self.get("api/accounts/delegates/fee") 33 | return resp.json() 34 | 35 | def get_delegates(self, address, **kwargs): 36 | """ Get the delegates of an account. 37 | 38 | :param address: A valid Ark address. 39 | :param kwargs: Optionnal parameters. orderBy, limit, offset 40 | :return: 41 | """ 42 | resp = self.get("api/accounts/delegates", address=address, **kwargs) 43 | return resp.json() 44 | 45 | def get_accounts(self, address): 46 | """ Get account information of an address. 47 | 48 | :param address: A valid Ark address. 49 | :return: 50 | """ 51 | resp = self.get("api/accounts", address=address) 52 | return resp.json() 53 | 54 | def get_top_accounts(self, **kwargs): 55 | """ Get a list of top account 56 | 57 | :param kwargs: Optionnal parameters. limit, offset 58 | :return: 59 | """ 60 | resp = self.get("api/accounts/top", **kwargs) 61 | return resp.json() 62 | -------------------------------------------------------------------------------- /tests/test_block.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from pythark import Block 3 | 4 | 5 | class TestBlock(unittest.TestCase): 6 | def test_get_block(self): 7 | block = Block() 8 | resp = block.get_block("570934191207974498") 9 | self.assertEqual(resp["success"], True) 10 | 11 | def test_get_blocks(self): 12 | block = Block() 13 | resp = block.get_blocks(limit=5) 14 | self.assertEqual(resp["success"], True) 15 | 16 | def test_get_height(self): 17 | block = Block() 18 | resp = block.get_height() 19 | self.assertEqual(resp["success"], True) 20 | 21 | def test_get_epoch(self): 22 | block = Block() 23 | resp = block.get_epoch() 24 | self.assertEqual(resp["success"], True) 25 | 26 | def test_get_nethash(self): 27 | block = Block() 28 | resp = block.get_nethash() 29 | self.assertEqual(resp["success"], True) 30 | 31 | def test_get_fee(self): 32 | block = Block() 33 | resp = block.get_fee() 34 | self.assertEqual(resp["success"], True) 35 | 36 | def test_get_fees(self): 37 | block = Block() 38 | resp = block.get_fees() 39 | self.assertEqual(resp["success"], True) 40 | 41 | def test_get_milestone(self): 42 | block = Block() 43 | resp = block.get_milestone() 44 | self.assertEqual(resp["success"], True) 45 | 46 | def test_get_reward(self): 47 | block = Block() 48 | resp = block.get_reward() 49 | self.assertEqual(resp["success"], True) 50 | 51 | def test_get_supply(self): 52 | block = Block() 53 | resp = block.get_supply() 54 | self.assertEqual(resp["success"], True) 55 | 56 | def test_get_status(self): 57 | block = Block() 58 | resp = block.get_status() 59 | self.assertEqual(resp["success"], True) 60 | 61 | if __name__ == '__main__': 62 | unittest.main() -------------------------------------------------------------------------------- /tests/test_delegate.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from pythark import Delegate 3 | 4 | 5 | class TestDelegate(unittest.TestCase): 6 | def test_get_delegates_count(self): 7 | delegate = Delegate() 8 | resp = delegate.get_delegates_count("ANwjGUcVbLXpqbBUWbjUBQWkr4MWVDuJu9") 9 | self.assertEqual(resp["success"], True) 10 | 11 | def test_search_delegates(self): 12 | delegate = Delegate() 13 | resp = delegate.search_delegates(query="dr", limit=2) 14 | self.assertEqual(resp["success"], True) 15 | 16 | def test_get_voters(self): 17 | delegate = Delegate() 18 | resp = delegate.get_voters("031641ff081b93279b669f7771b3fbe48ade13eadb6d5fd85bdd025655e349f008") 19 | self.assertEqual(resp["success"], True) 20 | 21 | def test_get_delegate(self): 22 | delegate = Delegate() 23 | resp = delegate.get_delegate("jarunik") 24 | self.assertEqual(resp["success"], True) 25 | 26 | def test_get_delegate_public_key(self): 27 | delegate = Delegate() 28 | resp = delegate.get_delegate_publickey("031641ff081b93279b669f7771b3fbe48ade13eadb6d5fd85bdd025655e349f008") 29 | self.assertEqual(resp["success"], True) 30 | 31 | def test_get_delegates(self): 32 | delegate = Delegate() 33 | resp = delegate.get_delegates(limit=5, orderBy="username") 34 | self.assertEqual(resp["success"], True) 35 | 36 | def test_get_delegate_fee(self): 37 | delegate = Delegate() 38 | resp = delegate.get_delegate_fee("Aasu14aTs9ipZdy1FMv7ay1Vqn3jPskA8t") 39 | self.assertEqual(resp["success"], True) 40 | 41 | def test_get_forged_by_account(self): 42 | delegate = Delegate() 43 | resp = delegate.get_forged_by_account("02c7455bebeadde04728441e0f57f82f972155c088252bf7c1365eb0dc84fbf5de") 44 | self.assertEqual(resp["success"], True) 45 | 46 | def test_get_next_forgers(self): 47 | delegate = Delegate() 48 | resp = delegate.get_next_forgers("ANwjGUcVbLXpqbBUWbjUBQWkr4MWVDuJu9") 49 | self.assertEqual(resp["success"], True) 50 | 51 | if __name__ == '__main__': 52 | unittest.main() -------------------------------------------------------------------------------- /tests/test_transport.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from pythark import Transport 3 | 4 | 5 | class TestTransport(unittest.TestCase): 6 | def test_get_peers(self): 7 | transport = Transport() 8 | resp = transport.get_peers() 9 | self.assertEqual(resp["success"], True) 10 | 11 | def test_get_common_blocks(self): 12 | transport = Transport() 13 | resp = transport.get_common_blocks("5807533976636630922, 7191952529633383827") 14 | self.assertEqual(resp["success"], True) 15 | 16 | def test_get_blocks(self): 17 | transport = Transport() 18 | resp = transport.get_blocks("AJbmGnDAx9y91MQCDApyaqZhn6fBvYX9iJ") 19 | self.assertEqual(resp["success"], True) 20 | 21 | #def test_get_block(self): 22 | # # Doesn't work, even the curl from Swagger, need to be checked. 23 | # transport = Transport() 24 | # resp = transport.get_block("AJbmGnDAx9y91MQCDApyaqZhn6fBvYX9iJ") 25 | # self.assertEqual(resp["success"], True) 26 | 27 | def test_get_transactions(self): 28 | transport = Transport() 29 | resp = transport.get_transactions() 30 | self.assertEqual(resp["success"], True) 31 | 32 | def test_post_new_transaction(self): 33 | # Tested and approved. 34 | transport = Transport() 35 | resp = transport.post_transaction("dark", "DDvQqwqPXKd5P8dLAroFsnKR5Q3tKUtvnp", 1000000, "meat feed erupt toe crow treat rhythm dot angry marriage bicycle path") 36 | self.assertEqual(resp["success"], False) 37 | 38 | def test_get_transactions_from_ids(self): 39 | transport = Transport() 40 | resp = transport.get_transactions_from_ids("e9f1ff96ccaf9ebcadb0e1c0827c606a71a88c258c6a3ec1a880be000996dd25") 41 | self.assertEqual(resp["success"], True) 42 | 43 | def test_get_height(self): 44 | transport = Transport() 45 | resp = transport.get_height() 46 | self.assertEqual(resp["success"], True) 47 | 48 | def test_get_status(self): 49 | transport = Transport() 50 | resp = transport.get_status() 51 | self.assertEqual(resp["success"], True) 52 | 53 | 54 | if __name__ == '__main__': 55 | unittest.main() -------------------------------------------------------------------------------- /pythark/block.py: -------------------------------------------------------------------------------- 1 | from .api import API 2 | 3 | 4 | class Block(API): 5 | """ 6 | Operations for Blocks. 7 | """ 8 | 9 | def get_block(self, id): 10 | """ Get block by id. 11 | 12 | :param id: Valid Block ID. 13 | :return: 14 | """ 15 | resp = self.get("api/blocks/get", id=id) 16 | return resp.json() 17 | 18 | def get_blocks(self, **kwargs): 19 | """ Get all blocks. 20 | 21 | :param kwargs: Optionnal parameters. limit, orderBy, offset, generatorPublicKey, totalAmount, totalFee, reward, 22 | previousBlock, height 23 | :return: 24 | """ 25 | resp = self.get("api/blocks", **kwargs) 26 | return resp.json() 27 | 28 | def get_height(self): 29 | """ Get the blockchain height. 30 | 31 | :return: 32 | """ 33 | resp = self.get("api/blocks/getHeight") 34 | return resp.json() 35 | 36 | def get_epoch(self): 37 | """ Get the blockchain epoch. 38 | 39 | :return: 40 | """ 41 | resp = self.get("api/blocks/getEpoch") 42 | return resp.json() 43 | 44 | def get_nethash(self): 45 | """ Get the blockchain nethash. 46 | 47 | :return: 48 | """ 49 | resp = self.get("api/blocks/getNethash") 50 | return resp.json() 51 | 52 | def get_fee(self): 53 | """ Get the transaction fee for sending "normal" transactions. 54 | 55 | :return: 56 | """ 57 | resp = self.get("api/blocks/getFee") 58 | return resp.json() 59 | 60 | def get_fees(self): 61 | """ Get the network fees. 62 | 63 | :return: 64 | """ 65 | resp = self.get("api/blocks/getFees") 66 | return resp.json() 67 | 68 | def get_milestone(self): 69 | """ Get the blockchain milestone. 70 | 71 | :return: 72 | """ 73 | resp = self.get("api/blocks/getMilestone") 74 | return resp.json() 75 | 76 | def get_reward(self): 77 | """ Get the blockchain reward. 78 | 79 | :return: 80 | """ 81 | resp = self.get("api/blocks/getReward") 82 | return resp.json() 83 | 84 | def get_supply(self): 85 | """ Get the blockchain supply. 86 | 87 | :return: 88 | """ 89 | resp = self.get("api/blocks/getSupply") 90 | return resp.json() 91 | 92 | def get_status(self): 93 | """ Get the blockchain status. 94 | 95 | :return: 96 | """ 97 | resp = self.get("api/blocks/getStatus") 98 | return resp.json() 99 | -------------------------------------------------------------------------------- /pythark/delegate.py: -------------------------------------------------------------------------------- 1 | from .api import API 2 | 3 | 4 | class Delegate(API): 5 | """ 6 | Operations for Delegates. 7 | """ 8 | 9 | def get_delegates_count(self, address): 10 | """ Get the count of delegates. 11 | 12 | :param address: A valid Ark address. 13 | :return: 14 | """ 15 | resp = self.get("api/delegates/count", address=address) 16 | return resp.json() 17 | 18 | def search_delegates(self, query, **kwargs): 19 | """ Search for specific delegates. 20 | 21 | :param query: The name we want to search. 22 | :param kwargs: Optionnal parameters. limit 23 | :return: 24 | """ 25 | resp = self.get("api/delegates/search", q=query, **kwargs) 26 | return resp.json() 27 | 28 | def get_voters(self, publicKey): 29 | """ Get a list of voters for a delegate. 30 | 31 | :param publicKey: A valid Ark publicKey. 32 | :return: 33 | """ 34 | resp = self.get("api/delegates/voters", publicKey=publicKey) 35 | return resp.json() 36 | 37 | def get_delegate(self, username): 38 | """ Get a single delegate. 39 | 40 | :param username: The delegate's name. 41 | :return: 42 | """ 43 | resp = self.get("api/delegates/get", username=username) 44 | return resp.json() 45 | 46 | def get_delegate_publickey(self, publicKey): 47 | """ Get a single delegate. 48 | 49 | :param publicKey: A valid Ark publicKey. 50 | :return: 51 | """ 52 | resp = self.get("api/delegates/get", publicKey=publicKey) 53 | return resp.json() 54 | 55 | def get_delegates(self, **kwargs): 56 | """ Get all delegates. 57 | 58 | :param kwargs: Optionnal parameters. orderBy, limit, offset. 59 | :return: 60 | """ 61 | resp = self.get("api/delegates", **kwargs) 62 | return resp.json() 63 | 64 | def get_delegate_fee(self, address): 65 | """ Get the delegate fee. 66 | 67 | :param address: 68 | :return: 69 | """ 70 | resp = self.get("api/delegates/fee", address=address) 71 | return resp.json() 72 | 73 | def get_forged_by_account(self, generatorPublicKey): 74 | """ Get the amount of ARKs forged by an account. 75 | 76 | :param generatorPublicKey: A valid Ark generatorPublicKey. 77 | :return: 78 | """ 79 | resp = self.get("api/delegates/forging/getForgedByAccount", generatorPublicKey=generatorPublicKey) 80 | return resp.json() 81 | 82 | def get_next_forgers(self, address): 83 | """ Get the next forgers. 84 | 85 | :param address: A valid Ark address. 86 | :return: 87 | """ 88 | resp = self.get("api/delegates/getNextForgers", address=address) 89 | return resp.json() 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | .static_storage/ 56 | .media/ 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 107 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 108 | 109 | # User-specific stuff: 110 | .idea/**/workspace.xml 111 | .idea/**/tasks.xml 112 | .idea/dictionaries 113 | 114 | # Sensitive or high-churn files: 115 | .idea/**/dataSources/ 116 | .idea/**/dataSources.ids 117 | .idea/**/dataSources.xml 118 | .idea/**/dataSources.local.xml 119 | .idea/**/sqlDataSources.xml 120 | .idea/**/dynamic.xml 121 | .idea/**/uiDesigner.xml 122 | 123 | # Gradle: 124 | .idea/**/gradle.xml 125 | .idea/**/libraries 126 | 127 | # CMake 128 | cmake-build-debug/ 129 | 130 | # Mongo Explorer plugin: 131 | .idea/**/mongoSettings.xml 132 | 133 | ## File-based project format: 134 | *.iws 135 | 136 | ## Plugin-specific files: 137 | 138 | # IntelliJ 139 | out/ 140 | 141 | # mpeltonen/sbt-idea plugin 142 | .idea_modules/ 143 | 144 | # JIRA plugin 145 | atlassian-ide-plugin.xml 146 | 147 | # Cursive Clojure plugin 148 | .idea/replstate.xml 149 | 150 | # Crashlytics plugin (for Android Studio and IntelliJ) 151 | com_crashlytics_export_strings.xml 152 | crashlytics.properties 153 | crashlytics-build.properties 154 | fabric.properties 155 | 156 | # Personal notes 157 | notes.txt 158 | 159 | # Pypi 160 | .pypirc -------------------------------------------------------------------------------- /pythark/transport.py: -------------------------------------------------------------------------------- 1 | from .api import API 2 | from logzero import logger 3 | 4 | try: 5 | import arky 6 | from arky import rest 7 | except ImportError as error: 8 | logger.info("You don't have Arky installed, you can't post a new transaction without this module.") 9 | logger.info(error) 10 | 11 | 12 | class Transport(API): 13 | """ 14 | Operations for Transports. 15 | """ 16 | 17 | def get_peers(self): 18 | """ Get a list of peers. 19 | 20 | :return: 21 | """ 22 | resp = self.get("peer/list") 23 | return resp.json() 24 | 25 | def get_common_blocks(self, ids): 26 | """ Get a list of blocks by ids 27 | 28 | :param ids: List of Block ids. 29 | :return: 30 | """ 31 | resp = self.get("peer/blocks/common", ids=','.join(ids)) 32 | return resp.json() 33 | 34 | def get_blocks(self, address): 35 | """ Get all blocks. 36 | 37 | :param address: A valid Ark address. 38 | :return: 39 | """ 40 | resp = self.get("peer/blocks", address=address) 41 | return resp.json() 42 | 43 | def get_block(self, address): 44 | # Doesn't work, even the curl from Swagger, need to be checked. 45 | """ Get a single block. 46 | 47 | :param address: A valid Ark address. 48 | :return: 49 | """ 50 | resp = self.get("peer/block", address=address) 51 | return resp.json() 52 | 53 | def get_transactions(self): 54 | """ Get a list of transactions. 55 | 56 | :return: 57 | """ 58 | resp = self.get("peer/transactions") 59 | return resp.json() 60 | 61 | def post_transaction(self, network, recipientId, amount, secret, vendorField="", secondSecret=""): 62 | """ Post a new transaction. 63 | 64 | :param network: The network we want to use (ark, dark, ...) 65 | :param recipientId: A valid Ark address. 66 | :param amount: Amount of currency we want to transfer. 67 | :param secret: BIP39 seedpass. 68 | :param vendorField: Optionnal vendorField. 69 | :param secondSecret: Optionnal BIP39 second seedpass. 70 | :return: 71 | """ 72 | rest.use(network) 73 | 74 | return arky.core.sendTransaction(recipientId=recipientId, amount=amount, vendorField=vendorField, secret=secret, 75 | secondSecret=secondSecret) 76 | 77 | def post_transaction_bis(self, recipientId, amount, secret, secondSecret="", vendorField=None): 78 | transactions = [] 79 | 80 | resp = self.post("peer/transactions", recipientId=recipientId, amount=amount, secret=secret, secondSecret=secondSecret, vendorField=vendorField) 81 | return resp.json() 82 | 83 | def get_transactions_from_ids(self, ids): 84 | """ Get a list of transactions by ids. 85 | 86 | :param ids: A list of valid Transaction id 87 | :return: 88 | """ 89 | resp = self.get("peer/transactionsFromIds", ids=','.join(ids)) 90 | return resp.json() 91 | 92 | def get_height(self): 93 | """ Get the blockchain height. 94 | 95 | :return: 96 | """ 97 | resp = self.get("peer/height") 98 | return resp.json() 99 | 100 | def get_status(self): 101 | """ Get the blockchain status. 102 | 103 | :return: 104 | """ 105 | resp = self.get("peer/status") 106 | return resp.json() -------------------------------------------------------------------------------- /pythark/api.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import random 3 | import json 4 | from retrying import retry 5 | 6 | BASE_URL = "https://api.arknode.net/" # "http://37.59.129.164:4001/" 7 | BASE_URL_DEV = "http://167.114.29.52:4002/" 8 | BASE_URL_KAPUMAIN = "https://walletapi.kapu.one/" 9 | 10 | FALLBACKS_MAIN_ADDRESSES = [] 11 | FALLBACKS_DEV_ADDRESSES = [] 12 | FALLBACKS_KAPU_ADDRESSES = [] 13 | 14 | 15 | class API: 16 | """ 17 | Class allowing us to interact with the API. 18 | """ 19 | def __init__(self, network="main"): 20 | self.network = network 21 | 22 | @retry(stop_max_attempt_number=10, wait_fixed=10000) 23 | def get(self, endpoint, **kwargs): 24 | """ Do a HTTP get request to a specified endpoint (with optionnal parameters). 25 | 26 | :param endpoint: The endpoint we want to reach. 27 | :param kwargs: Optionnal parameters of the query. 28 | :return: Request response if HTTP code is equal to 200. 29 | """ 30 | payload = {name: kwargs[name] for name in kwargs if kwargs[name] is not None} 31 | try: 32 | if self.network == "main": 33 | headers_main = get_main_network_headers() 34 | r = requests.get("{0}{1}".format(BASE_URL, endpoint), headers=headers_main, params=payload, timeout=10) 35 | if self.network == "dark" or self.network == "dev": 36 | headers_dev = get_dev_network_headers() 37 | r = requests.get("{0}{1}".format(BASE_URL_DEV, endpoint), headers=headers_dev, params=payload, timeout=10) 38 | if self.network == "kapu": 39 | headers_kapu_main = get_kapu_main_network_headers() 40 | r = requests.get("{0}{1}".format(BASE_URL_KAPUMAIN, endpoint), headers=headers_kapu_main, params=payload, timeout=10) 41 | if r.status_code == 200: 42 | return r 43 | except requests.exceptions.Timeout: 44 | if self.network == "dark" or self.network == "dev": 45 | populate_fallback(FALLBACKS_DEV_ADDRESSES, self.network) 46 | url = select_random_ip(FALLBACKS_DEV_ADDRESSES) + "/" 47 | r = requests.get("{0}{1}".format(url, endpoint), headers=headers_dev, params=payload, timeout=10) 48 | elif self.network == "kapu": 49 | populate_fallback(FALLBACKS_KAPU_ADDRESSES, self.network) 50 | url = select_random_ip(FALLBACKS_KAPU_ADDRESSES) + "/" 51 | r = requests.get("{0}{1}".format(url, endpoint), headers=headers_kapu_main, params=payload, timeout=10) 52 | else: 53 | populate_fallback(FALLBACKS_MAIN_ADDRESSES, self.network) 54 | url = select_random_ip(FALLBACKS_MAIN_ADDRESSES) + "/" 55 | r = requests.get("{0}{1}".format(url, endpoint), headers=headers_main, params=payload, timeout=10) 56 | if r.status_code == 200: 57 | return r 58 | except requests.exceptions.ConnectionError as e: 59 | print("Connection error : ", e) 60 | except requests.exceptions.ReadTimeout as e: 61 | print("ReadTimeOut error : ", e) 62 | 63 | def post(self, endpoint, **kwargs): 64 | payload = {name: kwargs[name] for name in kwargs if kwargs[name] is not None} 65 | r = requests.post("{0}{1}".format(BASE_URL, endpoint), headers=get_dev_network_headers(), params=payload) 66 | return r 67 | 68 | 69 | def get_main_network_headers(): 70 | headers = { 71 | "nethash": "6e84d08bd299ed97c212c886c98a57e36545c8f5d645ca7eeae63a8bd62d8988", 72 | "version": "1.0.1", 73 | "port": "4001" 74 | } 75 | return headers 76 | 77 | 78 | def get_dev_network_headers(): 79 | headers = { 80 | "nethash": "578e820911f24e039733b45e4882b73e301f813a0d2c31330dafda84534ffa23", 81 | "version": "1.1.1", 82 | "port": "4002" 83 | } 84 | return headers 85 | 86 | 87 | def get_kapu_main_network_headers(): 88 | headers = { 89 | "nethash": "6e84d08bd299ed97c212c886c98a57e36545c8f5d645ca7eeae63a8bd62d8988", 90 | "version": "1.0.1", 91 | "port": "4001" 92 | } 93 | return headers 94 | 95 | 96 | def populate_fallback(fallback, network): 97 | from . import Peer 98 | if network == 'dev': 99 | p = Peer("dev") 100 | elif network == 'kapu': 101 | p = Peer("kapu") 102 | else: 103 | p = Peer() 104 | r = p.get_peers() 105 | for peer in r["peers"]: 106 | if peer["delay"] <= 50: 107 | fallback.append(peer) 108 | return fallback 109 | 110 | 111 | def select_random_ip(fallback): 112 | rand = random.choice(fallback) 113 | return rand['ip'] 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Imgur](https://i.imgur.com/ysh3akS.png) 2 | 3 | [![HitCount](http://hits.dwyl.io/Highjhacker/pyrark.svg)](http://hits.dwyl.io/Highjhacker/pyrark) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Build Status](https://travis-ci.org/Highjhacker/pythark.svg?branch=master)](https://travis-ci.org/Highjhacker/pythark) 4 | 5 | # Pythark 6 | 7 | Ark API Wrapper in Python. 8 | 9 | ## Built with 10 | - [Python](https://www.python.org/) 11 | - [Requests](http://docs.python-requests.org/en/master/) 12 | 13 | ## Installation 14 | 15 | Pythark uses Arky to create a new transaction, if you want to use this feature 16 | you will need to install Arky too. 17 | 18 | Since Arky can works with the nano s ledger now, you need to install these dependencies : 19 | 20 | ```shell 21 | sudo apt-get install python3-dev libusb-1.0-0-dev libudev-dev 22 | ``` 23 | 24 | ```shell 25 | pip install pythark 26 | pip install https://github.com/ArkEcosystem/arky/archive/aip11.zip 27 | ``` 28 | 29 | ## Application Example 30 | 31 | - [PytharkFlask](https://github.com/Highjhacker/PytharkFlask) - Example of a web application using Flask and Pythark 32 | - [PytharkCLI](https://github.com/Highjhacker/PytharkCLI) - Example of a CLI application using Click and Pythark 33 | ## Usage 34 | 35 | ### Network 36 | Since the version 0.1.3, Pythark can now interact with others network than the main one. If you want 37 | to query on the devnet for example, you will need to specify it like this : 38 | 39 | ```python 40 | from pythark import Peer 41 | # It's not mandatory to specify the network, by default the main network will be used. 42 | # So : peer = Peer() is still correct. 43 | peer = Peer("dev") # or peer = Peer(network="dev") 44 | print(peer.get_peers()) 45 | 46 | >>> {'success': True, 'peers': [{'ip': '167.114.29.62', 'port': 4002, 'version': '1.1.0', 'errors': 0, 'os': 'linux4.4.0-79-generic', 'height': 2056284, 'status': 'OK', 'delay': 33}, 47 | }... 48 | ``` 49 | 50 | You can use this with all the Pythark functions. 51 | 52 | The currently available networks are the following : main, dev, dark, kapu. 53 | 54 | 55 | ### Account 56 | 57 | ```python 58 | from pythark import Account 59 | acc = Account() 60 | print(acc.get_balance("ANwjGUcVbLXpqbBUWbjUBQWkr4MWVDuJu9")) 61 | 62 | >>> {'success': True, 'balance': '51795878544', 'unconfirmedBalance': '51795878544'} 63 | 64 | # If you want to query a balance on the devnet 65 | from pythark import Account 66 | acc = Account("dev") 67 | print(acc.get_balance("DMEvkeU7pNnH5eVDz63GVK6A4CThCmdcpk")) 68 | 69 | >>> {'success': True, 'balance': '4688266611418', 'unconfirmedBalance': '4688266611418'} 70 | ``` 71 | 72 | ### Block 73 | 74 | ```python 75 | from pythark import Block 76 | b = Block() 77 | print(b.get_block("570934191207974498")) 78 | 79 | >>> {'success': True, 'block': {'id': '570934191207974498', 'version': 0, 'timestamp': 19174464, 'height': 2376065, 'previousBlock': '7483598217382372212', 'numberOfTransactions': 50, 'totalAmount': 15830360775, 'totalFee': 500000000, 'reward': 200000000, 'payloadLength': 1600, 'payloadHash': '04c497e303c9aaa16db51e52b139e87ec19666f7a0e0fb14804ba0dcf0a15932', 'generatorPublicKey': '034682a4c4d2c8c0bc5f966dd422a83d2b433e212ef1f334f82cc3fe4676240933', 'generatorId': 'AdBSvLKPp6pMp5ZDsxkgjFu6KeCokncSMk', 'blockSignature': '304402201eb4097e7de1f2601e82333c040acac6df6458b7d59ec2370904fca42729243b022043d7ee08bf7007c06ec1119d12aa0ffe2895769f05c34fabc39f1c478a882049', 'confirmations': 158928, 'totalForged': '700000000'}} 80 | 81 | # If you want to query a block on the dev network : 82 | 83 | from pythark import Block 84 | b = Block("dev") 85 | print(b.get_block("5927359504701109797")) 86 | 87 | >>> {'success': True, 'block': {'id': '5927359504701109797', 'version': 0, 'timestamp': 23094024, 'height': 2076244, 'previousBlock': '17513022799527103654', 'numberOfTransactions': 4, 'totalAmount': 3320058873, 'totalFee': 40000000, 'reward': 200000000, 'payloadLength': 128, 'payloadHash': '5ff2e3c58a2fe4c3d7c5327ab811d039943e9444dab865853070def0d9f60e1c', 'generatorPublicKey': '0284a88da69cc04439633217c6961d2800df0f7dff7f85b9803848ee02d0743f1d', 'generatorId': 'DRkVSeW5e2zh9v7R5msdLc26fo8axFALGT', 'blockSignature': '3045022100f8e7b6bab48264b77c8f398ff6312a72d4f8698de0328a5a2d0840b481cef3ce02202fb011c0b5883117adf2ab729f7db460abd12e44e275de50547a21bd4e82d3a8', 'confirmations': 18, 'totalForged': '240000000'}} 88 | ``` 89 | 90 | ### Delegate 91 | 92 | ```python 93 | from pythark import Delegate 94 | d = Delegate() 95 | print(d.search_delegates("dr")) 96 | 97 | >>> {'success': True, 'delegates': [{'username': 'dr10', 'address': 'ANwjGUcVbLXpqbBUWbjUBQWkr4MWVDuJu9', 'publicKey': '031641ff081b93279b669f7771b3fbe48ade13eadb6d5fd85bdd025655e349f008', 'vote': '147614629879279', 'producedblocks': 30607, 'missedblocks': 190}, {'username': 'drusilla', 'address': 'AGzLMjoUiLbccC4YpaDsMRwHaoUwCoorQG', 'publicKey': '038dfc041c7e609f254b2cf38de4b55e02dff9e743497f5cf6b67d49d8e44978ce', 'vote': '0', 'producedblocks': 0, 'missedblocks': 0}]} 98 | 99 | # If you want to search for a delegate on the dev network : 100 | 101 | from pythark import Delegate 102 | d = Delegate("dev") 103 | print(d.search_delegates("d", limit=1)) 104 | 105 | >>> {'success': True, 'delegates': [{'username': 'arksidious', 'address': 'DJ4z35JF61d8zkA5B9soUAhg9mYHyLJr2C', 'publicKey': '02ec3f1b7d79d022b5a62a5af97218afd751db2210d1729309cd792c7a4fe92b2e', 'vote': '0', 'producedblocks': 10272, 'missedblocks': 42}]} 106 | ``` 107 | 108 | ### Loader 109 | 110 | ```python 111 | from pythark import Loader 112 | l = Loader() 113 | print(l.get_status()) 114 | 115 | >>> {'success': True, 'loaded': False, 'now': 2286032, 'blocksCount': 0} 116 | 117 | # If you want to get the status on the dev network : 118 | 119 | from pythark import Loader 120 | l = Loader("dev") 121 | print(l.get_status()) 122 | 123 | {'success': True, 'loaded': False, 'now': 1952955, 'blocksCount': 0} 124 | ``` 125 | 126 | ### MultiSignature 127 | 128 | ```python 129 | from pythark import MultiSignature 130 | m = MultiSignature() 131 | print(m.get_pending("02c7455bebeadde04728441e0f57f82f972155c088252bf7c1365eb0dc84fbf5de")) 132 | 133 | >>> {'success': True, 'transactions': []} 134 | 135 | # If you want to get the pending multi sig on the dev network : 136 | 137 | from pythark import MultiSignature 138 | m = MultiSignature("dev") 139 | print(m.get_pending("026f777ed892898a7c834e4cd9ce7b4c33bf90d2c91a9e67ddaa28de6d60d18ab1")) 140 | 141 | >>> {'success': True, 'transactions': []} 142 | ``` 143 | 144 | ### Peer 145 | 146 | ```python 147 | from pythark import Peer 148 | p = Peer() 149 | print(p.get_peer("78.229.106.139", 4001)) 150 | 151 | >>> {'success': True, 'peer': {'ip': '78.229.106.139', 'port': 4001, 'version': '1.0.1', 'errors': 0, 'os': 'linux4.4.0-92-generic', 'height': 2535012, 'status': 'OK', 'delay': 221}} 152 | 153 | # If you want to get a peer on the dev network : 154 | 155 | from pythark import Peer 156 | p = Peer("dev") 157 | print(p.get_peer("204.10.184.228", 4002)) 158 | 159 | >>> {'success': True, 'peer': {'ip': '204.10.184.228', 'port': 4002, 'version': '1.1.1', 'errors': 0, 'os': 'linux4.4.0-98-generic', 'height': 2076293, 'status': 'OK', 'delay': 117}} 160 | ``` 161 | 162 | ### Signature 163 | 164 | ```python 165 | from pythark import Signature 166 | s = Signature() 167 | print(s.get_signature_fee()) 168 | 169 | >>> {'success': True, 'fee': 500000000} 170 | 171 | # Get signature fee on the dev network : 172 | 173 | from pythark import Signature 174 | s = Signature("dev") 175 | print(s.get_signature_fee()) 176 | 177 | >>> {'success': True, 'fee': 500000000} 178 | ``` 179 | 180 | ### Transaction 181 | 182 | ```python 183 | from pythark import Transaction 184 | t = Transaction() 185 | print(t.get_transactions(limit=5, orderBy="timestamp")) 186 | 187 | >>> {'success': True, 'transactions': [{'id': 'b2ef0adc90e3cf4af5d221350d79c2f2712378e0ef5a71244eecaca4afdc7140', 'blockid': '4195226696324437309', 'type': 0, 'timestamp': -1980252, 'amount': 7350732799999, 'fee': 10000000, 'vendorField': 'Ark', 'senderId': 'AQKk9BwUZjM5fsjYCpreZJ4Ltatrt6ZJBE', 'recipientId': 'AXGVkwNJ3p5ruPJrEGEcwcaSz3THw69Eni', 'senderPublicKey': '0367b6eeef79462803cecff4692f06df379803d055941fb1f0c976097fa054aa03', 'signature': '3044022023eb7496803968e2f0e63d9eb7b0885adc3138ad7582e91ab83eae6a0d0afbcf02207f9d0f3a83179c408b819791dc007e3d5e3f266da81ba57aece6524586be3172', 'asset': {}, 'confirmations': 2533357}, {'id': '44d9d0a3093232b9368a24af90577741df8340b93732db23b90d44f6590d3e42', 'blockid': '4366553906931540162', 'type': 0, 'timestamp': 0, 'amount': 0, 'fee': 0, 'senderId': 'AewxfHQobSc49a4radHp74JZCGP8LRe4xA', 'recipientId': 'AU9BgcsCBDCkzPyY9EZXqiwukYq4Kor4oX', 'senderPublicKey': '0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3', 'signature': '3045022100ed57f27cabdb01f5398b30e63e3372735ee428e17e95de675c37586b6d1a5c12022062a0040ed189a4adac6c3d105e05180f7c74e8c68ca9912b3c60286c2226f3fa', 'asset': {}, 'confirmations': 2535055}, {'id': '512f1aa00538b24a3ba55d65519d34cea83d753f5b2cebfd7004d5c0eaa7177a', 'blockid': '4366553906931540162', 'type': 0, 'timestamp': 0, 'amount': 0, 'fee': 0, 'senderId': 'AewxfHQobSc49a4radHp74JZCGP8LRe4xA', 'recipientId': 'AeLpRK8rFVtBeyBVqBtdQpWDfLzaiNujKr', 'senderPublicKey': '0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3', 'signature': '3044022018618cfd5dd1024c0dd7677fdbddcaa6977b57f832eca130583d36480dfa452302202c067556fd93899fb0d18ea28e6f0276a778099cdde3d97d3bb8733dff965a59', 'asset': {}, 'confirmations': 2535055}, {'id': '8bb3997878a6a359f1418cf25f31c84f660e5e6897ebd6d07549ff6a4374a44d', 'blockid': '4366553906931540162', 'type': 0, 'timestamp': 0, 'amount': 0, 'fee': 0, 'senderId': 'AewxfHQobSc49a4radHp74JZCGP8LRe4xA', 'recipientId': 'ARagsXvdeTHYghaQgJkwbdSkPLZ73qdMkR', 'senderPublicKey': '0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3', 'signature': '3044022021e056a123b4a6c30e3f30dd68ff56f4cc1a994222cf27ff5b48434947e45f300220424cbc671a54a019cc655d02b2313a324702908a4a05c86bac4ac83029bb01ef', 'asset': {}, 'confirmations': 2535055}, {'id': '30cb724924823c689058c25243d1c213b9cdb8c157eff26ee9c89fc1e705fedd', 'blockid': '4366553906931540162', 'type': 0, 'timestamp': 0, 'amount': 0, 'fee': 0, 'senderId': 'AewxfHQobSc49a4radHp74JZCGP8LRe4xA', 'recipientId': 'AT9xWcPQ8hGYuXZ8aWE57VJFohyX1TTLkH', 'senderPublicKey': '0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3', 'signature': '3045022100fd0ab0bee79152978d8d5835e2d244fa159e4957f48d602c65e35e2383c0d14a022036380dac439784075befef7f7b14734f9ed782e4be5ac7f2f4c49985b08fdce9', 'asset': {}, 'confirmations': 2535055}], 'count': '340315'} 188 | 189 | # Get the transactions on the dev network : 190 | 191 | from pythark import Transaction 192 | t = Transaction("dev") 193 | print(t.get_transactions(limit=5, orderBy="timestamp")) 194 | 195 | >>> {'success': True, 'transactions': [{'id': 'e40ce11cab82736da1cc91191716f3c1f446ca7b6a9f4f93b7120ef105ba06e8', 'blockid': '13149578060728881902', 'type': 0, 'timestamp': 0, 'amount': 12500000000000000, 'fee': 0, 'senderId': 'DUFeXjJmYt1mWY3auywA1EQSqfCv5kYYfP', 'recipientId': 'DGihocTkwDygiFvmg6aG8jThYTic47GzU9', 'senderPublicKey': '03cb7bca143376721d0e9e3f3ccb0dc2e7e8470c06e630c3cef73f03e309b558ad', 'signature': '3044022016ecdf3039e69514c7d75861b22fc076496b61c07a1fcf793dc4f5c76fa0532b0220579c4c0c9d13720f9db5d9df29ed8ceab0adc266c6c160d612d4894dc5867eb1', 'asset': {}, 'confirmations': 2076306}, {'id': 'eb0146ac79afc228f0474a5ae1c4771970ae7880450b998c401029f522cd8a21', 'blockid': '13149578060728881902', 'type': 2, 'timestamp': 0, 'amount': 0, 'fee': 0, 'senderId': 'DNL81CT6WNG1PHjobBmLvKwLV3UUscBymB', 'senderPublicKey': '03e5b39a83e6c7c952c5908089d4524bb8dda93acc2b2b953247e43dc4fe9aa3d1', 'signature': '3045022100e3e38811778023e6f17fefd447f179d45ab92c398c7cfb1e34e2f6e1b167c95a022070c36439ecec0fc3c43850070f29515910435d389e059579878d61b5ff2ea337', 'asset': {'delegate': {'username': 'genesis_1', 'publicKey': '03e5b39a83e6c7c952c5908089d4524bb8dda93acc2b2b953247e43dc4fe9aa3d1'}}, 'confirmations': 2076306}, {'id': 'c9c554056b3428951633a7059dd64dfcbd776fef7f4a156ea362b37ee6ce74c7', 'blockid': '13149578060728881902', 'type': 2, 'timestamp': 0, 'amount': 0, 'fee': 0, 'senderId': 'DG9LYv5rqX67wuGvGVa9is5k1r86LKCVTA', 'senderPublicKey': '031137050d5fed0b5229b150257da2ac9c135efdf4bcb382b0ad0c197d7be458f4', 'signature': '30440220124baaa04491287d0abbf5a167c9b0f5ac95c22b196f42ff3d275cc9a213c2fd02206e6ebada85f67063e642dbcde6b956f8c99c05f4b9c55f1551d3eebba6375043', 'asset': {'delegate': {'username': 'genesis_3', 'publicKey': '031137050d5fed0b5229b150257da2ac9c135efdf4bcb382b0ad0c197d7be458f4'}}, 'confirmations': 2076306}, {'id': 'c82ccaa16be0e3c7ff4a53e2807968b71a0d88115223c3af2eb320f32449ac32', 'blockid': '13149578060728881902', 'type': 2, 'timestamp': 0, 'amount': 0, 'fee': 0, 'senderId': 'DMSwarrHg5N9ZAZ6nsqPuUjyAU6gdRAM9d', 'senderPublicKey': '037def83d085778d7767a182a179f345207953441089081f5bc13f86d3891308aa', 'signature': '3045022100900cea3c2df393414899c9d74db57d89c9f311c70d08b974d0fd4a98bfae2fc902204a2aa51a1ec71da27c26afc033de6bd2d15978813c120c95e1a4dafca75ce876', 'asset': {'delegate': {'username': 'genesis_4', 'publicKey': '037def83d085778d7767a182a179f345207953441089081f5bc13f86d3891308aa'}}, 'confirmations': 2076306}, {'id': 'ee6a19fff622ab4e6e96d159396de56d6034b4b18a9cf5c99efcf4e61b28e15a', 'blockid': '13149578060728881902', 'type': 2, 'timestamp': 0, 'amount': 0, 'fee': 0, 'senderId': 'DFcYHfCwhGWcBNy6cp48wy5SfXbQmfBYgT', 'senderPublicKey': '033f28ad2e9b897d46f1e67c7c52070e9ca46b04c0679ebb21fb236719e38aade3', 'signature': '30440220285188d8900cd3cffccf5e1de305b18856451dd04d2ed21165dffe9a7ce4afc1022009457be6bfe536971697105d47ad1f829738a5cacdb27a23c5d1e8a8dddf3ebd', 'asset': {'delegate': {'username': 'genesis_5', 'publicKey': '033f28ad2e9b897d46f1e67c7c52070e9ca46b04c0679ebb21fb236719e38aade3'}}, 'confirmations': 2076306}], 'count': '142386'} 196 | ``` 197 | 198 | ### Transport 199 | 200 | ```python 201 | from pythark import Transport 202 | t = Transport() 203 | print(t.get_status()) 204 | 205 | >>> {'success': True, 'height': 2535061, 'forgingAllowed': True, 'currentSlot': 2560155, 'header': {'id': '17084042248047495221', 'height': 2535061, 'version': 0, 'totalAmount': 0, 'totalFee': 0, 'reward': 200000000, 'payloadHash': 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 'payloadLength': 0, 'timestamp': 20481240, 'numberOfTransactions': 0, 'previousBlock': '9903476536476021910', 'generatorPublicKey': '0354319db3f22fb8d4588a09ebbb3e91631cbc2202ba58c69149b75c1a47eb7686', 'blockSignature': '3045022100d7988e19980767d259072d4884f359f95d5ca99bc99d909f70b55b1eadde5921022000b8eb45266a1ad7943d18abe45e5487da680677272a26f7ede78c63a0d545bb'}} 206 | 207 | # Get the status on the dev network : 208 | 209 | from pythark import Transport 210 | t = Transport("dev") 211 | print(t.get_status()) 212 | 213 | >>> {'success': True, 'height': 2076312, 'forgingAllowed': True, 'currentSlot': 2886837, 'header': {'id': '8062806100428564762', 'height': 2076312, 'version': 0, 'totalAmount': 0, 'totalFee': 0, 'reward': 200000000, 'payloadHash': 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 'payloadLength': 0, 'timestamp': 23094688, 'numberOfTransactions': 0, 'previousBlock': '3568461414597517092', 'generatorPublicKey': '02dc13fcb190bcfbe9e7ecfc6269635ed2c497a75bab471f2b15c1a99897da97b3', 'blockSignature': '304402202168ab17061e91b15193d4acbdcbf73c4a12a5380161b3359f9abfed9dc24f6702201435e6f13da3b46109c1c1621b147939c74fa5b61b348c86202fb0cf87528878'}} 214 | ``` 215 | 216 | Creating a new transaction : 217 | 218 | ```python 219 | from pythark import Transport 220 | transport = Transport() 221 | resp = transport.post_transaction( 222 | "dark", # Network 223 | "DDvQqwqPXKd5P8dLAroFsnKR5Q3tKUtvnp", # RecipientAddress 224 | 1000000, # Amount 225 | "firstPassphrase", # First passphrase, mandatory 226 | "vendorField", # Vendor field, optionnal 227 | "secondPassphrase") # Second passphrase, optionnal 228 | ``` 229 | 230 | ## TODOS 231 | 232 | - [x] Core code. 233 | - [x] Write documentation. 234 | - [ ] Basic docs written, need to polish. 235 | - [x] Unit testing. 236 | - [ ] Check if it can be better. 237 | - [x] Package it. 238 | - [ ] Seems OK right now, distributed on PyPi, but have to be sure it's OK for everyone on 239 | X python version and differents OS. 240 | - [x] Travis. 241 | - [ ] Missing support for python 3.2. 242 | - [ ] OSX Support ? 243 | - [ ] Windows support ? 244 | - [ ] Better errors handling for the models methods. 245 | - [x] Sample flask app. 246 | - [x] Sample CLI app. 247 | - [ ] Integrate it to the DiscArk bot. 248 | - [x] Allow to post a new transaction. 249 | - [x] Allow to specify a network to use (so we can query on the devnet, mainnet, ..) 250 | - [ ] Functionnal but can be better. 251 | 252 | ## Authors 253 | 254 | - Jolan Beer - Highjhacker 255 | 256 | ## License 257 | 258 | pythark is under MIT license. See the [LICENSE file](https://github.com/Highjhacker/pythark/blob/master/LICENSE) for more informations. 259 | --------------------------------------------------------------------------------