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
35 changes: 8 additions & 27 deletions hathor/consensus/block_consensus.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,11 @@ def update_consensus(self, block: Block) -> None:
assert self.context.nc_events is None
self.context.nc_events = []
self.update_voided_info(block)
if self._settings.ENABLE_NANO_CONTRACTS:

if self._should_execute_nano(block):
self.execute_nano_contracts(block)
else:
self._nc_initialize_empty(block)

def _nc_initialize_empty(self, block: Block) -> None:
"""Initialize a block with an empty contract trie."""
Expand Down Expand Up @@ -149,29 +152,10 @@ def _should_execute_nano(self, block: Block) -> bool:
vertex=parent,
feature=Feature.NANO_CONTRACTS,
)
is_active_on_block = self.feature_service.is_feature_active(
vertex=block,
feature=Feature.NANO_CONTRACTS,
)
match is_active_on_parent, is_active_on_block:
case False, False:
# Nano is not active, nothing to execute.
return False
case False, True:
# This is the first active block, so we initialize it and return False
# because there's nothing to execute on the first active block.
self._nc_initialize_empty(block)
return False
case True, False: # pragma: no cover
raise AssertionError('unreachable')
case True, True:
# All set, proceed with execution.
return True
case _: # pragma: no cover
raise AssertionError('unreachable')

case NanoContractsSetting.DISABLED: # pragma: no cover
raise AssertionError('unreachable')
return is_active_on_parent

case NanoContractsSetting.DISABLED:
return False

case _: # pragma: no cover
assert_never(self._settings.ENABLE_NANO_CONTRACTS)
Expand All @@ -190,9 +174,6 @@ def _nc_execute_calls(self, block: Block, *, is_reorg: bool) -> None:
self._nc_initialize_empty(block)
return

if not self._should_execute_nano(block):
return

meta = block.get_metadata()
assert not meta.voided_by
assert meta.nc_block_root_id is None
Expand Down
49 changes: 44 additions & 5 deletions tests/nanocontracts/test_nano_feature_activation.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@


class MyBluprint(Blueprint):
a: int

@public
def initialize(self, ctx: Context) -> None:
pass
self.a = 123

@public
def nop(self, ctx: Context) -> None:
pass
self.a = 456


class TestNanoFeatureActivation(unittest.TestCase):
Expand Down Expand Up @@ -74,12 +76,16 @@ def setUp(self) -> None:
assert self.manager.tx_storage.nc_catalog is not None
self.manager.tx_storage.nc_catalog.blueprints[self.blueprint_id] = MyBluprint

empty_block_storage = self.manager.consensus_algorithm.nc_storage_factory.get_empty_block_storage()
empty_block_storage.commit()
self.empty_root_id = empty_block_storage.get_root_id()

def test_activation(self) -> None:
private_key = unittest.OCB_TEST_PRIVKEY.hex()
password = unittest.OCB_TEST_PASSWORD.hex()
artifacts = self.dag_builder.build_from_str(f'''
blockchain genesis b[1..13]
blockchain b10 a[11..12]
blockchain b10 a[11..13]
b10 < dummy < b11

nc1.nc_id = "{self.blueprint_id.hex()}"
Expand All @@ -96,10 +102,13 @@ def test_activation(self) -> None:

a11.weight = 10
b13 < a11

nc1 <-- a13
ocb1 <-- a13
''')

b3, b4, b7, b8, b11, b12, b13, a11 = artifacts.get_typed_vertices(
('b3', 'b4', 'b7', 'b8', 'b11', 'b12', 'b13', 'a11'),
b3, b4, b7, b8, b11, b12, b13, a11, a12, a13 = artifacts.get_typed_vertices(
('b3', 'b4', 'b7', 'b8', 'b11', 'b12', 'b13', 'a11', 'a12', 'a13'),
Block,
)
nc1, ocb1 = artifacts.get_typed_vertices(('nc1', 'ocb1'), Transaction)
Expand All @@ -126,6 +135,8 @@ def test_activation(self) -> None:
artifacts.propagate_with(self.manager, up_to='b11')
assert self.feature_service.get_state(block=b11, feature=Feature.NANO_CONTRACTS) is FeatureState.LOCKED_IN

assert b11.get_metadata().nc_block_root_id == self.empty_root_id

# At this point, the feature is not active, so the nc txs are rejected on the mempool.
msg = 'full validation failed: Header `NanoHeader` not supported by `Transaction`'
with pytest.raises(InvalidNewTransaction, match=msg):
Expand All @@ -142,6 +153,9 @@ def test_activation(self) -> None:
artifacts.propagate_with(self.manager, up_to='b12')
assert self.feature_service.get_state(block=b12, feature=Feature.NANO_CONTRACTS) is FeatureState.ACTIVE

assert b11.get_metadata().nc_block_root_id == self.empty_root_id
assert b12.get_metadata().nc_block_root_id == self.empty_root_id

# Now, the nc txs are accepted on the mempool.
artifacts.propagate_with(self.manager, up_to='nc1')
assert nc1.get_metadata().validation.is_valid()
Expand All @@ -154,6 +168,10 @@ def test_activation(self) -> None:
artifacts.propagate_with(self.manager, up_to='b13')
assert nc1.get_metadata().nc_execution is NCExecutionState.SUCCESS

assert b11.get_metadata().nc_block_root_id == self.empty_root_id
assert b12.get_metadata().nc_block_root_id == self.empty_root_id
assert b13.get_metadata().nc_block_root_id not in (self.empty_root_id, None)

artifacts.propagate_with(self.manager, up_to='a11')
assert a11.get_metadata().validation.is_valid()
assert a11.get_metadata().voided_by is None
Expand All @@ -162,6 +180,11 @@ def test_activation(self) -> None:
assert ocb1.get_metadata().validation.is_invalid()
assert ocb1.get_metadata().validation.is_invalid()

assert b11.get_metadata().nc_block_root_id == self.empty_root_id
assert b12.get_metadata().nc_block_root_id == self.empty_root_id
assert b13.get_metadata().nc_block_root_id not in (self.empty_root_id, None)
assert a11.get_metadata().nc_block_root_id == self.empty_root_id

# The nc txs are removed from the mempool.
assert not self.manager.tx_storage.transaction_exists(b13.hash)
assert not self.manager.tx_storage.transaction_exists(nc1.hash)
Expand All @@ -171,6 +194,13 @@ def test_activation(self) -> None:

# The nc txs are re-accepted on the mempool.
artifacts.propagate_with(self.manager, up_to='a12')
assert self.feature_service.get_state(block=a12, feature=Feature.NANO_CONTRACTS) is FeatureState.ACTIVE

assert b11.get_metadata().nc_block_root_id == self.empty_root_id
assert b12.get_metadata().nc_block_root_id == self.empty_root_id
assert b13.get_metadata().nc_block_root_id not in (self.empty_root_id, None)
assert a11.get_metadata().nc_block_root_id == self.empty_root_id
assert a12.get_metadata().nc_block_root_id == self.empty_root_id

nc1._metadata = None
self.vertex_handler.on_new_relayed_vertex(nc1)
Expand All @@ -185,3 +215,12 @@ def test_activation(self) -> None:
assert ocb1.get_metadata().voided_by is None
assert self.manager.tx_storage.transaction_exists(ocb1.hash)
assert ocb1 in list(self.manager.tx_storage.iter_mempool_tips_from_best_index())

artifacts.propagate_with(self.manager, up_to='a13')

assert b11.get_metadata().nc_block_root_id == self.empty_root_id
assert b12.get_metadata().nc_block_root_id == self.empty_root_id
assert b13.get_metadata().nc_block_root_id not in (self.empty_root_id, None)
assert a11.get_metadata().nc_block_root_id == self.empty_root_id
assert a12.get_metadata().nc_block_root_id == self.empty_root_id
assert a13.get_metadata().nc_block_root_id not in (self.empty_root_id, None)
Loading