Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion hathor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
TxOutputScript,
VertexId,
)
from hathor.nanocontracts.utils import sha3, verify_ecdsa
from hathor.nanocontracts.utils import json_dumps, sha3, verify_ecdsa
from hathor.version import __version__

__all__ = [
Expand Down Expand Up @@ -73,5 +73,6 @@
'VertexId',
'sha3',
'verify_ecdsa',
'json_dumps',
'__version__',
]
1 change: 1 addition & 0 deletions hathor/nanocontracts/allowed_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,6 @@
NCParsedArgs=hathor.NCParsedArgs,
sha3=hathor.sha3,
verify_ecdsa=hathor.verify_ecdsa,
json_dumps=hathor.json_dumps,
),
}
2 changes: 2 additions & 0 deletions hathor/nanocontracts/nc_exec_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ def error(self, message: str, **kwargs: Any) -> None:

def __emit_event__(self, data: bytes) -> None:
"""Emit a custom event from a Nano Contract."""
if not isinstance(data, bytes):
raise NCFail(f'event data must be of type `bytes`, found `{type(data).__name__}`')
if len(data) > MAX_EVENT_SIZE:
raise NCFail(f'event data cannot be larger than {MAX_EVENT_SIZE} bytes, is {len(data)}')
self.__events__.append(NCEvent(nc_id=self.__nc_id__, data=data))
Expand Down
31 changes: 30 additions & 1 deletion hathor/nanocontracts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import hashlib
from types import ModuleType
from typing import Callable
from typing import Any, Callable

from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes
Expand Down Expand Up @@ -169,3 +169,32 @@ def verify_ecdsa(public_key: bytes, data: bytes, signature: bytes) -> bool:
return True
except InvalidSignature:
return False


def json_dumps(
obj: object,
*,
ensure_ascii: bool = True,
indent: int | str | None = None,
separators: tuple[str, str] | None = (',', ':'),
sort_keys: bool = False,
) -> str:
"""
Serialize obj as a JSON. Arguments are a subset of Python's `json.dumps`.
It automatically converts `bytes`-like values to their hex representation.
"""
import json

def dump_bytes(data: Any) -> str:
if isinstance(data, bytes):
return data.hex()
raise TypeError(f'Object of type {type(data).__name__} is not JSON serializable')

return json.dumps(
obj,
ensure_ascii=ensure_ascii,
indent=indent,
separators=separators,
sort_keys=sort_keys,
default=dump_bytes,
)
1 change: 1 addition & 0 deletions hathor_tests/nanocontracts/test_exposed_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
'hathor.SignedData.some_new_attribute',
'hathor.export.some_new_attribute',
'hathor.fallback.some_new_attribute',
'hathor.json_dumps.some_new_attribute',
'hathor.public.some_new_attribute',
'hathor.sha3.some_new_attribute',
'hathor.verify_ecdsa.some_new_attribute',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec

from hathor import ContractId
from hathor.crypto.util import get_public_key_bytes_compressed
from hathor.nanocontracts import Blueprint, Context, NCFail, public, utils as nc_utils, view
from hathor_tests.nanocontracts.blueprints.unittest import BlueprintTestCase
Expand All @@ -37,8 +38,13 @@ def test_sha3(self, data: bytes) -> bytes:
def test_verify_ecdsa(self, public_key: bytes, data: bytes, signature: bytes) -> bool:
return nc_utils.verify_ecdsa(public_key, data, signature)

@view
def test_json_dumps(self) -> str:
obj = dict(a=[1, 2, 3], b=123, c='abc', d=ContractId(b'\x01' * 32))
return nc_utils.json_dumps(obj)


class TestCryptoFunctions(BlueprintTestCase):
class TestUtilsFunctions(BlueprintTestCase):
def setUp(self) -> None:
super().setUp()

Expand Down Expand Up @@ -75,3 +81,10 @@ def test_verify_ecdsa_success(self) -> None:
signature = private_key.sign(data, ec.ECDSA(hashes.SHA256()))

assert self.runner.call_view_method(self.contract_id, 'test_verify_ecdsa', public_key, data, signature)

def test_json_dumps(self) -> None:
result = self.runner.call_view_method(self.contract_id, 'test_json_dumps')

assert result == (
'{"a":[1,2,3],"b":123,"c":"abc","d":"0101010101010101010101010101010101010101010101010101010101010101"}'
)