Skip to content

Commit

Permalink
Merge pull request #20 from HathorNetwork/dev
Browse files Browse the repository at this point in the history
Release v0.2.0
  • Loading branch information
pedroferreira1 authored Apr 27, 2022
2 parents 37aa76b + 15b716f commit 04b9e69
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 35 deletions.
41 changes: 38 additions & 3 deletions hathorlib/base_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from hathorlib.conf import HathorSettings
from hathorlib.exceptions import InvalidOutputValue, WeightError
from hathorlib.scripts import P2PKH, MultiSig, parse_address_script
from hathorlib.scripts import P2PKH, DataScript, MultiSig, parse_address_script
from hathorlib.utils import int_to_bytes, unpack, unpack_len

settings = HathorSettings()
Expand Down Expand Up @@ -397,14 +397,38 @@ def is_nft_creation_standard(self) -> bool:
return False

def is_standard(self, std_max_output_script_size: int = settings.PUSHTX_MAX_OUTPUT_SCRIPT_SIZE,
only_standard_script_type: bool = True) -> bool:
only_standard_script_type: bool = True,
max_number_of_data_script_outputs: int = settings.MAX_DATA_SCRIPT_OUTPUTS) -> bool:
""" Return True is the transaction is standard
"""
# TODO in the future we should have a way to know which standard validation failed
# we could have an array of errors from args that we append an error object
# or a bool parameter "raise_on_non_standard", which will raise an error if it's non standard

# First we check if t's an NFT standard
# We could remove this because now that we are adding support
# for some data script outputs in a transaction, this would
# also be considered a standard but if we change our minds
# about the data scripts in the future we would need to remember
# to add NFT support back, so I'm just keeping this here
if self.is_nft_creation_standard():
return True

# We've discussed to allow any number of Data Script outputs but we decided to
# add some restrictions first. Because of that we are not making Data Script a
# standard script and we are handling it manually
number_of_data_script_outputs = 0
for output in self.outputs:
if not output.is_standard_script(std_max_output_script_size, only_standard_script_type):
# If not standard then we check if it's a data script with valid size
if output.is_script_size_valid(std_max_output_script_size) and output.is_data_script():
# Then we check if it already reached the maximum number of data script outputs
if number_of_data_script_outputs == max_number_of_data_script_outputs:
return False
else:
number_of_data_script_outputs += 1
continue

return False

return True
Expand Down Expand Up @@ -585,11 +609,22 @@ def to_json(self, *, decode_script: bool = False) -> Dict[str, Any]:
data['decoded'] = self.to_human_readable()
return data

def is_script_size_valid(self, max_output_script_size: int = settings.PUSHTX_MAX_OUTPUT_SCRIPT_SIZE) -> bool:
"""Return True if output script size is valid"""
if len(self.script) > max_output_script_size:
return False

return True

def is_data_script(self) -> bool:
"""Return True if output script is a DataScript"""
return DataScript.parse_script(self.script) is not None

def is_standard_script(self, std_max_output_script_size: int = settings.PUSHTX_MAX_OUTPUT_SCRIPT_SIZE,
only_standard_script_type: bool = True) -> bool:
"""Return True if this output has a standard script."""
# First check: script size limit
if len(self.script) > std_max_output_script_size:
if not self.is_script_size_valid(std_max_output_script_size):
return False

# Second check: output script type
Expand Down
3 changes: 3 additions & 0 deletions hathorlib/conf/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ class HathorSettings(NamedTuple):

# Maximum size of the tx output's script allowed for a tx to be standard
PUSHTX_MAX_OUTPUT_SCRIPT_SIZE: int = 256

# Maximum number of tx outputs of Data Script type
MAX_DATA_SCRIPT_OUTPUTS: int = 25
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

[tool.poetry]
name = "hathorlib"
version = "0.1.2"
version = "0.2.0"
description = "Hathor Network base objects library"
authors = ["Hathor Team <[email protected]>"]
license = "Apache-2.0"
Expand Down
77 changes: 77 additions & 0 deletions tests/test_data_script.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
Copyright (c) Hathor Labs and its affiliates.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
"""

import unittest

from hathorlib.base_transaction import TxOutput, tx_or_block_from_bytes
from hathorlib.conf import HathorSettings
from hathorlib.scripts import DataScript

settings = HathorSettings()


class HathorDataScriptTestCase(unittest.TestCase):
def test_script_data(self):
# Create NFT script data test
data = 'nft data test'
obj_data = DataScript(data)
human = obj_data.to_human_readable()
self.assertEqual(human['type'], 'Data')
self.assertEqual(human['data'], data)

script = obj_data.get_script()

parsed_obj = DataScript.parse_script(script)
self.assertEqual(parsed_obj.data, data)

# Parse output script from real NFT
data = bytes.fromhex('00020103000023117762f80fad7c28eea89e793036e8e5855038eee4deea02c53d7513e700006a473045022'
'100eab17bbadcd5297695847c7e81a9d9c8b7995b9816a8cb2db4f68721eef22d44022043e8b9498a557cd2'
'f8f4e957241cc78fee4daf0e149de5b9529048ee1ca0140e2103e42187c715fbdd129ef40bf9c6c9c63a6e0'
'd72d478d121fa23c6078fa5049457000000010000060454455354ac0000012c01001976a91495b3e7b7559a'
'2b1ffa6c337fc6aeff74e963796588ac0000000281001976a914e7b6fadc93b5553781d73ac908134c0bbc5'
'14e6b88ac01065465737474740354535440200000218def416127d5800200d9741624399388d196e5e40959'
'5e65a1803764ee078f34ebb2bda63ff6a63a001a2603c9a5947233dedb1160e9468e95563e76945ae58d829'
'118e17e668dc900000053')
tx = tx_or_block_from_bytes(data)
nft_script = DataScript.parse_script(tx.outputs[0].script)
self.assertEqual(nft_script.data, 'TEST')

self.assertFalse(tx.outputs[0].is_standard_script())
self.assertTrue(tx.outputs[1].is_standard_script())

def test_tx_with_script_data(self):
# Parse output script from real test tx
# This tx has a data script output and it's not an NFT creation tx
data = bytes.fromhex('0001010202000041a564f1d090bbf23f7f370eee970ded2270aa2ff59e4632deb2a746d28500ff62bcebf5d'
'f2827d98f6f3113c1226d555d5cafc77b914e4411698c3382e503006a47304502205a984dab561ff8f97a4f'
'c09d889f844de4fb66b32edc19e77bd84e58fa91bd61022100ef6bfa2e6c8b7f8eb41561b9b012b60fc41a3'
'9742cea74c4e0152be3ff98cbc421026f9b6b0b5d3badb218999d865b47ca70dc052920ca663d13eecf3176'
'2ed308ee003d11dacb7449dc7caf081223cfefb571e3ae4ec60da8eb74a201d516f3f3da01006a473045022'
'05a984dab561ff8f97a4fc09d889f844de4fb66b32edc19e77bd84e58fa91bd61022100ef6bfa2e6c8b7f8e'
'b41561b9b012b60fc41a39742cea74c4e0152be3ff98cbc421026f9b6b0b5d3badb218999d865b47ca70dc0'
'52920ca663d13eecf31762ed308ee000000010000464468747470733a2f2f697066732e696f2f697066732f'
'516d586656704d6b52463475674254666a5361367a566f6e6d4b4a31466f6e43717434774d39354b5453463'
'756622fac0000000101001976a914aa8de9f415b80986c8827580d267ff963cca41e688ac40200000000000'
'00620bdc9702003d11dacb7449dc7caf081223cfefb571e3ae4ec60da8eb74a201d516f3f3da004aa11e1d1'
'bc4d2c7b26e4f1b42b6da66b2add6bd562e8f1f59ec25b005e7a20000001a')
tx = tx_or_block_from_bytes(data)
self.assertTrue(tx.is_standard())

# Now we will add outputs until the max number of outputs
number_of_data_script_outputs = 1

while number_of_data_script_outputs < settings.MAX_DATA_SCRIPT_OUTPUTS:
new_output = TxOutput(1, tx.outputs[0].script, 0)
tx.outputs.append(new_output)
self.assertTrue(tx.is_standard())
number_of_data_script_outputs += 1

# If we add one more, then it should become non standard
new_output = TxOutput(1, tx.outputs[0].script, 0)
tx.outputs.append(new_output)
self.assertFalse(tx.is_standard())
31 changes: 0 additions & 31 deletions tests/test_nft.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import unittest

from hathorlib.base_transaction import TxOutput, tx_or_block_from_bytes
from hathorlib.scripts import DataScript


class HathorNFTTestCase(unittest.TestCase):
Expand Down Expand Up @@ -58,33 +57,3 @@ def test_is_nft(self):
new_output = TxOutput(1, tx4.outputs[0].script, 0)
tx4.outputs = [tx4.outputs[0], new_output] + tx4.outputs[1:]
self.assertFalse(tx4.is_nft_creation_standard())
self.assertFalse(tx4.is_standard())

def test_script_data(self):
# Create NFT script data test
data = 'nft data test'
obj_data = DataScript(data)
human = obj_data.to_human_readable()
self.assertEqual(human['type'], 'Data')
self.assertEqual(human['data'], data)

script = obj_data.get_script()

parsed_obj = DataScript.parse_script(script)
self.assertEqual(parsed_obj.data, data)

# Parse output script from real NFT
data = bytes.fromhex('00020103000023117762f80fad7c28eea89e793036e8e5855038eee4deea02c53d7513e700006a473045022'
'100eab17bbadcd5297695847c7e81a9d9c8b7995b9816a8cb2db4f68721eef22d44022043e8b9498a557cd2'
'f8f4e957241cc78fee4daf0e149de5b9529048ee1ca0140e2103e42187c715fbdd129ef40bf9c6c9c63a6e0'
'd72d478d121fa23c6078fa5049457000000010000060454455354ac0000012c01001976a91495b3e7b7559a'
'2b1ffa6c337fc6aeff74e963796588ac0000000281001976a914e7b6fadc93b5553781d73ac908134c0bbc5'
'14e6b88ac01065465737474740354535440200000218def416127d5800200d9741624399388d196e5e40959'
'5e65a1803764ee078f34ebb2bda63ff6a63a001a2603c9a5947233dedb1160e9468e95563e76945ae58d829'
'118e17e668dc900000053')
tx = tx_or_block_from_bytes(data)
nft_script = DataScript.parse_script(tx.outputs[0].script)
self.assertEqual(nft_script.data, 'TEST')

self.assertFalse(tx.outputs[0].is_standard_script())
self.assertTrue(tx.outputs[1].is_standard_script())

0 comments on commit 04b9e69

Please sign in to comment.