Skip to content

Commit 58c1da7

Browse files
authored
Audit 302 (#123)
1 parent 48eb623 commit 58c1da7

File tree

15 files changed

+81
-50
lines changed

15 files changed

+81
-50
lines changed

docs/source/whatsnew/0.9.1.rst

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.. _whatsnew-v091:
2+
3+
*******************************
4+
What's New in neo-mamba 0.9.1
5+
*******************************
6+
7+
:Author: Erik van den Brink
8+
9+
v3.0.2 compatibility
10+
===================
11+
12+
This release fixes bugs after auditing neo-mamba vs neo-cli v3.0.2. The TestNet is audited up to block `189200`,
13+
whereas MainNet is audited up to block `127057`.
14+

docs/source/whatsnew/index.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ This chapter describes the most important changes between the neo-mamba versions
1818
0.6.rst
1919
0.7.rst
2020
0.8.rst
21-
0.9.rst
21+
0.9.rst
22+
0.9.1.rst

neo3/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from neo3.core import cryptography
88
from .singleton import _Singleton
99

10-
version = '0.9'
10+
version = '0.9.1'
1111

1212
core_logger = logging.getLogger('neo3.core')
1313
network_logger = logging.getLogger('neo3.network')

neo3/contracts/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ def validate_type(obj: object, type_: typing.Type):
6060
'syscall_name_to_int',
6161
'DesignationContract',
6262
'DesignateRole',
63-
'NameService',
6463
'LedgerContract',
6564
'NEF',
6665
'MethodToken',

neo3/contracts/applicationengine.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def __init__(self,
5151
self.nonce_data = self.script_container.hash().to_array()[:16]
5252
else:
5353
self.nonce_data = b'\x00' * 16
54-
if self.snapshot and self.snapshot.persisting_block:
54+
if self.snapshot is not None and self.snapshot.persisting_block is not None:
5555
nonce = self.snapshot.persisting_block.nonce
5656
nonce ^= int.from_bytes(self.nonce_data, "little", signed=False)
5757
self.nonce_data = nonce.to_bytes(16, "little")
@@ -268,7 +268,7 @@ def load_token(self, token_id: int) -> vm.ExecutionContext:
268268

269269
token = nef.tokens[token_id]
270270
if token.parameters_count > len(self.current_context.evaluation_stack):
271-
raise ValueError("Token count exceeds available paremeters on evaluation stack")
271+
raise ValueError("Token count exceeds available parameters on evaluation stack")
272272
args: List[vm.StackItem] = []
273273
for _ in range(token.parameters_count):
274274
args.append(self.pop())
@@ -432,7 +432,7 @@ def _native_to_stackitem(self, value, native_type) -> vm.StackItem:
432432
Convert native type to VM type
433433
434434
Note: order of checking matters.
435-
e.g. a Transaction should be treated as IInteropable, while its also ISerializable
435+
e.g. a Transaction should be treated as IInteroperable, while its also ISerializable
436436
"""
437437
if isinstance(value, vm.StackItem):
438438
return value
@@ -466,5 +466,15 @@ def _native_to_stackitem(self, value, native_type) -> vm.StackItem:
466466
return self._native_to_stackitem(value, native_type)
467467
else:
468468
raise ValueError # shouldn't be possible, but silences mypy
469+
elif native_type == list:
470+
arr = vm.ArrayStackItem(self.reference_counter)
471+
for item in value:
472+
arr.append(self._native_to_stackitem(item, type(item)))
473+
return arr
474+
elif native_type == tuple:
475+
_struct = vm.StructStackItem(self.reference_counter)
476+
_struct.append(self._native_to_stackitem(value[0], type(value[0])))
477+
_struct.append(self._native_to_stackitem(value[1], type(value[1])))
478+
return _struct
469479
else:
470480
return vm.StackItem.from_interface(value)

neo3/contracts/native/fungible.py

+11-19
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def balance_of(self, snapshot: storage.Snapshot, account: types.UInt160) -> vm.B
9696
9797
Note: The returned value is still in internal format. Divide the results by the contract's `decimals`
9898
"""
99-
storage_item = snapshot.storages.try_get(self.key_account + account)
99+
storage_item = snapshot.storages.try_get(self.key_account + account, read_only=True)
100100
if storage_item is None:
101101
return vm.BigInteger.zero()
102102
else:
@@ -641,17 +641,11 @@ def vote(self,
641641
return True
642642

643643
@register("getCandidates", contracts.CallFlags.READ_STATES, cpu_price=1 << 22)
644-
def get_candidates(self, engine: contracts.ApplicationEngine) -> None:
644+
def get_candidates(self, engine: contracts.ApplicationEngine) -> List[Tuple[cryptography.ECPoint, vm.BigInteger]]:
645645
"""
646646
Fetch all registered candidates, convert them to a StackItem and push them onto the evaluation stack.
647647
"""
648-
array = vm.ArrayStackItem(engine.reference_counter)
649-
for k, v in self._get_candidates(engine.snapshot):
650-
struct = vm.StructStackItem(engine.reference_counter)
651-
struct.append(vm.ByteStringStackItem(k.to_array()))
652-
struct.append(vm.IntegerStackItem(v))
653-
array.append(struct)
654-
engine.push(array)
648+
return self._get_candidates(engine.snapshot)
655649

656650
@register("getNextBlockValidators", contracts.CallFlags.READ_STATES, cpu_price=1 << 16)
657651
def get_next_block_validators(self, snapshot: storage.Snapshot) -> List[cryptography.ECPoint]:
@@ -915,16 +909,14 @@ def _compute_committee_members(self, snapshot: storage.Snapshot) -> Dict[cryptog
915909
def _get_candidates(self,
916910
snapshot: storage.Snapshot) -> \
917911
List[Tuple[cryptography.ECPoint, vm.BigInteger]]:
918-
if self._candidates_dirty:
919-
self._candidates = []
920-
for k, v in snapshot.storages.find(self.key_candidate.to_array()):
921-
candidate = _CandidateState.deserialize_from_bytes(v.value)
922-
if candidate.registered:
923-
# take of the CANDIDATE prefix
924-
point = cryptography.ECPoint.deserialize_from_bytes(k.key[1:])
925-
self._candidates.append((point, candidate.votes))
926-
self._candidates_dirty = False
927-
912+
self._candidates = []
913+
for k, v in snapshot.storages.find(self.key_candidate.to_array()):
914+
candidate = _CandidateState.deserialize_from_bytes(v.value)
915+
if candidate.registered:
916+
# take of the CANDIDATE prefix
917+
point = cryptography.ECPoint.deserialize_from_bytes(k.key[1:])
918+
self._candidates.append((point, candidate.votes))
919+
self._candidates_dirty = False
928920
return self._candidates
929921

930922
def _should_refresh_committee(self, height: int) -> bool:

neo3/contracts/native/ledger.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@ def get_block(self, snapshot: storage.Snapshot, index_or_hash: bytes) -> Optiona
3939
height = vm.BigInteger(index_or_hash)
4040
if height < 0 or height > 4294967295: # uint.MaxValue
4141
raise ValueError("Invalid height")
42-
block = snapshot.blocks.try_get_by_height(height, read_only=True)
42+
block = snapshot.blocks.try_get_by_height(int(height), read_only=True)
4343
elif len(index_or_hash) == types.UInt256._BYTE_LEN:
4444
block_hash = types.UInt256(index_or_hash)
4545
block = snapshot.blocks.try_get(block_hash, read_only=True)
4646
else:
4747
raise ValueError("Invalid data")
4848

49-
if block and not self._is_traceable_block(snapshot, block.index):
50-
block = None
49+
if block is None or not self._is_traceable_block(snapshot, block.index):
50+
return None
5151
return block.trim()
5252

5353
@register("getTransaction", contracts.CallFlags.READ_STATES, cpu_price=1 << 15)

neo3/core/cryptography/ecc.py

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ def __bool__(self):
3232
def __hash__(self):
3333
return hash(self.x + self.y)
3434

35+
def __deepcopy__(self, memodict={}):
36+
return ECPoint.deserialize_from_bytes(self.to_array(), self.curve, False)
37+
3538
def is_zero(self):
3639
return self.x == 0 and self.y == 0
3740

neo3/core/types/uint.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@ def __init__(self, num_bytes: int, data: Union[bytes, bytearray] = None) -> None
1818

1919
if data is None:
2020
self._data = bytes(num_bytes)
21-
2221
else:
23-
self._data = data
24-
25-
if len(self._data) != num_bytes:
26-
raise ValueError(f"Invalid UInt: data length {len(self._data)} != specified num_bytes {num_bytes}")
22+
if len(data) < num_bytes:
23+
raise ValueError(f"Invalid UInt: data length {len(data)} != specified num_bytes {num_bytes}")
24+
self._data = data[:num_bytes]
2725

2826
def __len__(self) -> int:
2927
""" Count of data bytes. """

neo3/network/payloads/block.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ def _serializable_init(cls):
302302
return cls(Header._serializable_init(), [])
303303

304304

305-
class TrimmedBlock(serialization.ISerializable):
305+
class TrimmedBlock(serialization.ISerializable, IInteroperable):
306306
"""
307307
A size reduced Block instance.
308308
@@ -350,6 +350,20 @@ def deserialize(self, reader: serialization.BinaryReader) -> None:
350350
self.header = reader.read_serializable(Header)
351351
self.hashes = reader.read_serializable_list(types.UInt256, max=0xFFFF)
352352

353+
def to_stack_item(self, reference_counter: vm.ReferenceCounter) -> vm.StackItem:
354+
array = vm.ArrayStackItem(reference_counter)
355+
array.append(vm.ByteStringStackItem(self.header.hash().to_array()))
356+
array.append(vm.IntegerStackItem(self.header.version))
357+
array.append(vm.ByteStringStackItem(self.header.prev_hash.to_array()))
358+
array.append(vm.ByteStringStackItem(self.header.merkle_root.to_array()))
359+
array.append(vm.IntegerStackItem(self.header.timestamp))
360+
array.append(vm.IntegerStackItem(self.header.nonce))
361+
array.append(vm.IntegerStackItem(self.header.index))
362+
array.append(vm.IntegerStackItem(self.header.primary_index))
363+
array.append(vm.ByteStringStackItem(self.header.next_consensus.to_array()))
364+
array.append(vm.IntegerStackItem(len(self.hashes)))
365+
return array
366+
353367
@classmethod
354368
def _serializable_init(cls):
355369
return cls(Header._serializable_init(), [])

neo3/storage/cache.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ def __init__(self, db):
170170
self._internal_get = self._db._internal_block_get
171171
self._internal_try_get = self._db._internal_block_try_get
172172
self._internal_all = self._db._internal_block_all
173+
self._internal_get_by_height = self._db._internal_block_get_by_height
173174

174175
def put(self, block: payloads.Block) -> None:
175176
"""
@@ -213,7 +214,7 @@ def get_by_height(self, height: int, read_only=False) -> payloads.Block:
213214
"""
214215
block_hash = self._height_hash_mapping.get(height, None)
215216
if block_hash is None:
216-
raise KeyError
217+
block_hash = self._internal_get_by_height(height).hash() # raises KeyError if block is not found
217218
return self.get(block_hash, read_only)
218219

219220
def try_get(self, hash: types.UInt256, read_only=False) -> Optional[payloads.Block]:
@@ -237,11 +238,12 @@ def try_get_by_height(self, height: int, read_only=False) -> Optional[payloads.B
237238
height: block index.
238239
read_only: set to True to safeguard against return value modifications being persisted when committing.
239240
"""
240-
block_hash = self._height_hash_mapping.get(height, None)
241-
if block_hash is None:
241+
try:
242+
block = self.get_by_height(height, read_only)
243+
except KeyError:
242244
return None
243245

244-
return self.try_get(block_hash, read_only)
246+
return self.try_get(block.hash(), read_only)
245247

246248
def delete(self, hash: types.UInt256) -> None:
247249
"""
@@ -280,8 +282,8 @@ def all(self) -> Iterator[payloads.Block]:
280282

281283

282284
class CachedContractAccess(CachedAccess):
283-
_gas_token_script_hash = types.UInt160.from_string("f61eebf573ea36593fd43aa150c055ad7906ab83")
284-
_neo_token_script_hash = types.UInt160.from_string("70e2301955bf1e74cbb31d18c2f96972abadb328")
285+
_gas_token_script_hash = types.UInt160.from_string("d2a4cff31913016155e38e474a2c06d08be276cf")
286+
_neo_token_script_hash = types.UInt160.from_string("ef4073a0f2b305a38ec4050e4d3d28bc40ea63f5")
285287

286288
def __init__(self, db):
287289
super(CachedContractAccess, self).__init__(db)

neo3/wallet/account.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import hashlib
55
import unicodedata
66
from typing import Optional, Dict, Any, List
7-
from Crypto.Cipher import AES
7+
from Crypto.Cipher import AES # type: ignore
88
from jsonschema import validate # type: ignore
99
from neo3 import settings, contracts, vm, wallet, storage
1010
from neo3.network import payloads

requirements.txt

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ base58==1.0.3
44
bitarray==1.0.1
55
coverage>=5.0.2
66
Events==0.3
7-
lz4==2.2.1
7+
lz4==3.1.3
88
neo3crypto==0.2
9-
neo3vm>=0.8.2
10-
neo3vm-stubs>=0.8.2
9+
neo3vm>=0.8.3
10+
neo3vm-stubs>=0.8.3
1111
mmh3==2.5.1
1212
mypy>=0.782
1313
mypy-extensions==0.4.3
@@ -18,5 +18,5 @@ Sphinx==3.5.2
1818
sphinx-autodoc-typehints==1.11.1
1919
pycryptodome==3.10.1
2020
pycodestyle==2.5.0
21-
pybiginteger>=1.2.3
22-
pybiginteger-stubs>=1.2.3
21+
pybiginteger>=1.2.4
22+
pybiginteger-stubs>=1.2.4

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def run(self):
7575
setup(
7676
name='neo-mamba',
7777
python_requires='==3.8.*',
78-
version='0.9',
78+
version='0.9.1',
7979
description="Python SDK for the NEO 3 blockchain",
8080
long_description=readme,
8181
long_description_content_type="text/x-rst",

tests/contracts/test_json.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,7 @@ def test_serialization_map(self):
228228
m[key3] = v3
229229
m[key2] = v2
230230
s = contracts.JSONSerializer.serialize(m, 999)
231-
# this is a known deviation. NEO preserved key order, we don't
232-
# but shouldn't matter as it gets deserialized to a map stackitem
233-
expected = r'{"test1":1,"test2":2,"test3":3}'
231+
expected = r'{"test1":1,"test3":3,"test2":2}'
234232
self.assertEqual(expected, s)
235233

236234
def test_serialization_array(self):

0 commit comments

Comments
 (0)