Skip to content
This repository has been archived by the owner on Nov 15, 2021. It is now read-only.

Commit

Permalink
Merge pull request #13 from CityOfZion/development
Browse files Browse the repository at this point in the history
Merge CoZ Development into jseagrave21 Development
  • Loading branch information
jseagrave21 committed Sep 11, 2018
2 parents 9e66e9f + ea3422d commit 629fbd5
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 41 deletions.
30 changes: 14 additions & 16 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@ Changelog

All notable changes to this project are documented in this file.

[0.7.9-dev] in progress
-----------------------
- Gracefully handle network packet deserialization failures


[0.7.8-dev] 2018-xx-xx
----------------------
[0.7.8] 2018-09-06
------------------
- Prefix ``vin`` JSON output format to match C#
- Update ``neo-boa`` to v0.5.0 for Python 3.7 compatibility
- Update pexpect to 4.6.0 to be compatible with Python 3.7
- Update ``pexpect`` to 4.6.0 to be compatible with Python 3.7
- Accept incoming node connections, configurable via protocol config file setting (default: OFF)
- Fixes vulnerability to RPC invoke functionality that can send node into unclosed loop during 'test' invokes
- Fix issue with opening recently created wallets
- Fix import_blocks.py block hash caching issue
- Fix ``import_blocks.py`` block hash caching issue
- Update prompt.py: add ``account`` to help, update help, update standard completions, add ``config maxpeers`` functionality, update ``configure`` function arguments to behave as intended
- Add support for multiple requests in one transaction for JSON-RPC
- Update docs ``toctree`` so all pages are indexed & added instructions for contributing to docs


[0.7.7] 2018-08-23
Expand Down Expand Up @@ -222,10 +228,10 @@ All notable changes to this project are documented in this file.
- move ``prompt.py`` and other scripts to ``neo/bin``
- default chain data path is now in ``~/.neopython/Chains``. ``prompt.log`` and ``prompt.history`` files are also stored there
- the following console scripts are now on the ``venv`` path after running ``pip install neo-python`` or ``pip install -e .`` for github based installs:
- ``np-prompt``
- ``np-api-server``
- ``np-bootstrap``
- ``np-reencrypt-wallet``
- ``np-prompt``
- ``np-api-server``
- ``np-bootstrap``
- ``np-reencrypt-wallet``
- updated docs for Pypi changes


Expand Down Expand Up @@ -343,14 +349,6 @@ All notable changes to this project are documented in this file.
- lots of documentation
- various small bugfixes


[0.4.3] 2017-12-21
------------------

- updated ``neo-boa`` to ``0.2.1``
- added support for array ``REVERSE`` and ``APPEND`` VM opcodes


[0.4.3] 2017-12-21
------------------

Expand Down
19 changes: 19 additions & 0 deletions docs/Readme.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Contributing
============

Contributions are always welcome!

Guidelines
----------
When contributing please note the following:

1. NEO-Python Docs use the sphinx module to build https://neo-python.readthedocs.io/en/latest/. So, make sure every docucment you add uses reStructuredText (e.g. <yourfile>.rst).

2. After creating your document, be sure to update the ``toctree`` in index.rst. **Failing to do so will result in your document missing from the readthedocs website.**

3. Before submitting a pull request to add your new document, run ``make docs`` to verify your build is successful and includes no warnings. You will need to install the sphinx module and rtd theme:

::

pip install sphinx
pip install sphinx_rtd_theme
4 changes: 2 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ def __getattr__(cls, name):
# built documents.
#
# The short X.Y version.
version = __version__ = '0.7.8-dev'
version = __version__ = '0.7.9-dev'
# The full version, including alpha/beta/rc tags.
release = __version__ = '0.7.8-dev'
release = __version__ = '0.7.9-dev'


# The language for content autogenerated by Sphinx. Refer to documentation
Expand Down
5 changes: 4 additions & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,18 @@ neo-python - Python Node and SDK for the NEO blockchain


.. toctree::
:maxdepth: 2
:maxdepth: 3

overview
install
installwindows
Seedlist
basicusage
prompt
settings-and-logging
neo/Core/TX/Transaction
neo/SmartContract/smartcontracts
neo/SmartContract/application-vs-verification
data-types
contribute
license
Expand Down
2 changes: 1 addition & 1 deletion docs/source/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ The solution probably is
Install from PyPi
================
=================
The easiest way to install ``neo-python`` on your machine is to download it and install from PyPi using ``pip``. First, we recommend you to create a virtual environment in order to isolate this installation from your system directories and then install it as you normally would do:
Expand Down
14 changes: 14 additions & 0 deletions neo/Network/NeoNode.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ def HandlePeerInfoReceived(self, payload):
"""Process response of `self.RequestPeerInfo`."""
addrs = IOHelper.AsSerializableWithType(payload, 'neo.Network.Payloads.AddrPayload.AddrPayload')

if not addrs:
return

for index, nawt in enumerate(addrs.NetworkAddressesWithTime):
self.leader.RemoteNodePeerReceived(nawt.Address, nawt.Port, index)

Expand Down Expand Up @@ -383,6 +386,9 @@ def HandleVersion(self, payload):
"""Process the response of `self.RequestVersion`."""
self.Version = IOHelper.AsSerializableWithType(payload, "neo.Network.Payloads.VersionPayload.VersionPayload")

if not self.Version:
return

if self.incoming_client:
if self.Version.Nonce == self.nodeid:
self.Disconnect()
Expand All @@ -409,6 +415,8 @@ def HandleInvMessage(self, payload):
return

inventory = IOHelper.AsSerializableWithType(payload, 'neo.Network.Payloads.InvPayload.InvPayload')
if not inventory:
return

if inventory.Type == InventoryType.BlockInt:

Expand Down Expand Up @@ -471,6 +479,8 @@ def HandleBlockReceived(self, inventory):
inventory (neo.Network.Inventory):
"""
block = IOHelper.AsSerializableWithType(inventory, 'neo.Core.Block.Block')
if not block:
return

blockhash = block.Hash.ToBytes()
if blockhash in BC.Default().BlockRequests:
Expand Down Expand Up @@ -523,6 +533,8 @@ def HandleGetDataMessageReceived(self, payload):
payload (neo.Network.Inventory):
"""
inventory = IOHelper.AsSerializableWithType(payload, 'neo.Network.Payloads.InvPayload.InvPayload')
if not inventory:
return

for hash in inventory.Hashes:
hash = hash.encode('utf-8')
Expand Down Expand Up @@ -564,6 +576,8 @@ def HandleGetBlocksMessageReceived(self, payload):
return

inventory = IOHelper.AsSerializableWithType(payload, 'neo.Network.Payloads.GetBlocksPayload.GetBlocksPayload')
if not inventory:
return

blockchain = BC.Default()
hash = inventory.HashStart[0]
Expand Down
2 changes: 1 addition & 1 deletion neo/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.7.8-dev'
__version__ = '0.7.9-dev'
48 changes: 30 additions & 18 deletions neo/api/JSONRPC/JsonRpcApi.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,9 @@ def __init__(self, port, wallet=None):
self.port = port
self.wallet = wallet

#
# JSON-RPC API Route
#
@app.route('/')
@json_response
@cors_header
def home(self, request):
# {"jsonrpc": "2.0", "id": 5, "method": "getblockcount", "params": []}
body = None
request_id = None
def get_data(self, body: dict):

try:
body = json.loads(request.content.read().decode("utf-8"))
request_id = body["id"] if body and "id" in body else None

if "jsonrpc" not in body or body["jsonrpc"] != "2.0":
Expand All @@ -112,17 +102,42 @@ def home(self, request):
"result": result
}

except JSONDecodeError as e:
error = JsonRpcError.parseError()
return self.get_custom_error_payload(request_id, error.code, error.message)

except JsonRpcError as e:
return self.get_custom_error_payload(request_id, e.code, e.message)

except Exception as e:
error = JsonRpcError.internalError(str(e))
return self.get_custom_error_payload(request_id, error.code, error.message)

#
# JSON-RPC API Route
#
@app.route('/')
@json_response
@cors_header
def home(self, request):
# {"jsonrpc": "2.0", "id": 5, "method": "getblockcount", "params": []}
# or multiple requests in 1 transaction
# [{"jsonrpc": "2.0", "id": 1, "method": "getblock", "params": [10], {"jsonrpc": "2.0", "id": 2, "method": "getblock", "params": [10,1]}
request_id = None

try:
content = json.loads(request.content.read().decode("utf-8"))

# test if it's a multi-request message
if isinstance(content, list):
result = []
for body in content:
result.append(self.get_data(body))
return result

# otherwise it's a single request
return self.get_data(content)

except JSONDecodeError as e:
error = JsonRpcError.parseError()
return self.get_custom_error_payload(request_id, error.code, error.message)

def json_rpc_method_handler(self, method, params):

if method == "getaccountstate":
Expand Down Expand Up @@ -239,9 +254,6 @@ def json_rpc_method_handler(self, method, params):
result = NodeLeader.Instance().Relay(transaction)
return result

elif method == "submitblock":
raise NotImplementedError()

elif method == "validateaddress":
return self.validateaddress(params)

Expand Down
62 changes: 62 additions & 0 deletions neo/api/JSONRPC/test_json_rpc_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,12 @@ def test_validate_address(self):
res = json.loads(self.app.home(mock_req))
self.assertTrue(res["result"]["isvalid"])

# address with invalid checksum
req = self._gen_rpc_req("validateaddress", params=["AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2x"])
mock_req = mock_request(json.dumps(req).encode("utf-8"))
res = json.loads(self.app.home(mock_req))
self.assertFalse(res["result"]["isvalid"])

# example from docs.neo.org
req = self._gen_rpc_req("validateaddress", params=["152f1muMCNa7goXYhYAQC61hxEgGacmncB"])
mock_req = mock_request(json.dumps(req).encode("utf-8"))
Expand Down Expand Up @@ -537,6 +543,23 @@ def test_getbalance_neo_with_wallet(self):
self.app.wallet = None
os.remove(test_wallet_path)

def test_getbalance_invalid_params(self):
test_wallet_path = os.path.join(mkdtemp(), "getbalance.db3")
self.app.wallet = UserWallet.Create(
test_wallet_path,
to_aes_key('awesomepassword')
)

# max param length should be one, we provide 2
req = self._gen_rpc_req("getbalance", params=[1, 2])
mock_req = mock_request(json.dumps(req).encode("utf-8"))
res = json.loads(self.app.home(mock_req))

error = res.get('error', {})

self.assertEqual(error.get('code', None), -400)
self.assertEqual(error.get('message', None), "Params should contain 1 id.")

def test_getbalance_token_with_wallet(self):
test_wallet_path = os.path.join(mkdtemp(), "getbalance.db3")
self.app.wallet = UserWallet.Create(
Expand Down Expand Up @@ -614,3 +637,42 @@ def test_getnewaddress_with_wallet(self):
self.app.wallet.Close()
self.app.wallet = None
os.remove(test_wallet_path)

def test_valid_multirequest(self):
raw_block_request = {"jsonrpc": "2.0", "method": "getblock", "params": [1], "id": 1}
verbose_block_request = {"jsonrpc": "2.0", "method": "getblock", "params": [1, 1], "id": 2}

multi_request = json.dumps([raw_block_request, verbose_block_request])
mock_req = mock_request(multi_request.encode())
res = json.loads(self.app.home(mock_req))

self.assertEqual(type(res), list)
self.assertEqual(len(res), 2)
expected_raw_block = '00000000ef1f8f66a16fba100ed760f4ac6aa5a0d0bb8f4a0e92705b106761ef181718b3d0765298ceb5f57de7d2b0dab00ed25be4134706ada2d90adb8b7e3aba323a8e1abd125901000000d11f7a289214bdaff3812db982f3b0089a21a278988efeec6a027b2501fd450140884037dd265cb5f5a54802f53c2c8593b31d5b8a9c0bad4c7e366b153d878989d168080ac36b930036a9eb966b48c70bb41792e698fa021116f27c09643563b840e83ab14404d964a91dbac45f5460e88ad57196b1779478e3475334af8c1b49cd9f0213257895c60b5b92a4800eb32d785cbb39ae1f022528943909fd37deba63403677848bf98cc9dbd8fbfd7f2e4f34471866ea82ca6bffbf0f778b6931483700c17829b4bd066eb04983d3aac0bd46b9c8d03a73a8e714d3119de93cd9522e314054d16853b22014190063f77d9edf6fbccefcf71fffd1234f688823b4e429ae5fa639d0a664c842fbdfcb4d6e21f39d81c23563b92cffa09696d93c95bc4893a6401a43071d00d3e854f7f1f321afa7d5301d36f2195dc1e2643463f34ae637d2b02ae0eb11d4256c507a4f8304cea6396a7fce640f50acb301c2f6336d27717e84f155210209e7fd41dfb5c2f8dc72eb30358ac100ea8c72da18847befe06eade68cebfcb9210327da12b5c40200e9f65569476bbff2218da4f32548ff43b6387ec1416a231ee821034ff5ceeac41acf22cd5ed2da17a6df4dd8358fcb2bfb1a43208ad0feaab2746b21026ce35b29147ad09e4afe4ec4a7319095f08198fa8babbe3c56e970b143528d2221038dddc06ce687677a53d54f096d2591ba2302068cf123c1f2d75c2dddc542557921039dafd8571a641058ccc832c5e2111ea39b09c0bde36050914384f7a48bce9bf92102d02b1873a0863cd042cc717da31cea0d7cf9db32b74d4c72c01b0011503e2e2257ae010000d11f7a2800000000'
self.assertEqual(res[0]['result'], expected_raw_block)
expected_verbose_hash = '0x0012f8566567a9d7ddf25acb5cf98286c9703297de675d01ba73fbfe6bcb841c'
self.assertEqual(res[1]['result']['hash'], expected_verbose_hash)

def test_multirequest_with_1_invalid_request(self):
"""
We provide 2 requests, first one invalid and should return and error, second one valid and should still come up with correct results
"""
# block request of invalid block, should fail
raw_block_request = {"jsonrpc": "2.0", "method": "getblock", "params": [10000000000], "id": 1}
verbose_block_request = {"jsonrpc": "2.0", "method": "getblock", "params": [1, 1], "id": 2}

multi_request = json.dumps([raw_block_request, verbose_block_request])
mock_req = mock_request(multi_request.encode())
res = json.loads(self.app.home(mock_req))

self.assertEqual(type(res), list)
self.assertEqual(len(res), 2)

# test for errors in first invalid request
error = res[0].get('error', {})
self.assertEqual(error.get('code', None), -100)
self.assertEqual(error.get('message', None), "Unknown block")

# test for success in second valid request
expected_verbose_hash = '0x0012f8566567a9d7ddf25acb5cf98286c9703297de675d01ba73fbfe6bcb841c'
self.assertEqual(res[1]['result']['hash'], expected_verbose_hash)
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.7.8-dev
current_version = 0.7.9-dev
commit = True
tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
setup(
name='neo-python',
python_requires='>=3.6',
version='0.7.8-dev',
version='0.7.9-dev',
description="Python Node and SDK for the NEO blockchain",
long_description=readme,
author="Thomas Saunders",
Expand Down

0 comments on commit 629fbd5

Please sign in to comment.