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
26 changes: 16 additions & 10 deletions hathor/nanocontracts/blueprint_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,17 @@ def create_deposit_token(
amount: int,
mint_authority: bool = True,
melt_authority: bool = True,
*,
salt: bytes = b'',
) -> TokenUid:
"""Create a new deposit-based token."""
return self.__runner.syscall_create_child_deposit_token(
token_name,
token_symbol,
amount,
mint_authority,
melt_authority
salt=salt,
token_name=token_name,
token_symbol=token_symbol,
amount=amount,
mint_authority=mint_authority,
melt_authority=melt_authority,
)

# XXX: temporary alias
Expand All @@ -270,14 +273,17 @@ def create_fee_token(
amount: int,
mint_authority: bool = True,
melt_authority: bool = True,
*,
salt: bytes = b'',
) -> TokenUid:
"""Create a new fee-based token."""
return self.__runner.syscall_create_child_fee_token(
token_name,
token_symbol,
amount,
mint_authority,
melt_authority
salt=salt,
token_name=token_name,
token_symbol=token_symbol,
amount=amount,
mint_authority=mint_authority,
melt_authority=melt_authority,
)

@final
Expand Down
6 changes: 5 additions & 1 deletion hathor/nanocontracts/runner/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,8 @@ def _create_blueprint_instance(self, blueprint_id: BlueprintId, changes_tracker:
@_forbid_syscall_from_view('create_deposit_token')
def syscall_create_child_deposit_token(
self,
*,
salt: bytes,
token_name: str,
token_symbol: str,
amount: int,
Expand All @@ -1037,7 +1039,7 @@ def syscall_create_child_deposit_token(
last_call_record = self.get_current_call_record()
parent_id = last_call_record.contract_id
cleaned_token_symbol = clean_token_string(token_symbol)
token_id = derive_child_token_id(parent_id, cleaned_token_symbol)
token_id = derive_child_token_id(parent_id, cleaned_token_symbol, salt=salt)

token_amount = amount
htr_amount = get_deposit_token_deposit_amount(self._settings, token_amount)
Expand Down Expand Up @@ -1071,6 +1073,8 @@ def syscall_create_child_deposit_token(
@_forbid_syscall_from_view('create_fee_token')
def syscall_create_child_fee_token(
self,
*,
salt: bytes,
token_name: str,
token_symbol: str,
amount: int,
Expand Down
3 changes: 2 additions & 1 deletion hathor/nanocontracts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,12 @@ def derive_child_contract_id(parent_id: ContractId, salt: bytes, blueprint_id: B
return ContractId(VertexId(h.digest()))


def derive_child_token_id(parent_id: ContractId, token_symbol: str) -> TokenUid:
def derive_child_token_id(parent_id: ContractId, token_symbol: str, *, salt: bytes = b'') -> TokenUid:
"""Derive the token id for a token created by a (parent) contract."""
h = hashlib.sha256()
h.update(CHILD_TOKEN_ID_PREFIX)
h.update(parent_id)
h.update(salt)
h.update(token_symbol.encode('utf-8'))
return TokenUid(VertexId(h.digest()))

Expand Down
48 changes: 34 additions & 14 deletions tests/nanocontracts/test_token_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ def withdraw(self, ctx: Context) -> None:
def create_deposit_token(
self,
ctx: Context,
salt: bytes,
token_name: str,
token_symbol: str,
amount: int,
mint_authority: bool,
melt_authority: bool,
) -> None:
self.syscall.create_deposit_token(token_name, token_symbol, amount, mint_authority, melt_authority)
self.syscall.create_deposit_token(token_name, token_symbol, amount, mint_authority, melt_authority, salt=salt)


class NCNanoContractTestCase(unittest.TestCase):
Expand Down Expand Up @@ -164,6 +165,8 @@ def test_token_creation_by_vertex(self) -> None:

def test_token_creation_by_contract(self) -> None:
token_symbol = 'TKA'
salt0 = b'\0'
salt1 = b'\1'

dag_builder = TestDAGBuilder.from_manager(self.manager)
vertices = dag_builder.build_from_str(f'''
Expand All @@ -174,23 +177,28 @@ def test_token_creation_by_contract(self) -> None:
tx1.nc_method = initialize()

tx2.nc_id = tx1
tx2.nc_method = create_deposit_token("MyToken", "{token_symbol}", 100, false, false)
tx2.nc_method = create_deposit_token("{salt0.hex()}", "MyToken", "{token_symbol}", 100, false, false)
tx2.nc_deposit = 3 HTR

tx3.nc_id = tx1
tx3.nc_method = create_deposit_token("MyToken (2)", "{token_symbol}", 50, true, false)
tx3.nc_method = create_deposit_token("{salt0.hex()}", "MyToken (2)", "{token_symbol}", 50, true, false)
tx3.nc_deposit = 1 HTR

tx2 < tx3
tx4.nc_id = tx1
tx4.nc_method = create_deposit_token("{salt1.hex()}", "MyToken", "{token_symbol}", 30, true, false)
tx4.nc_deposit = 1 HTR

tx2 < tx3 < tx4

b31 --> tx1
b31 --> tx2
b32 --> tx3
b32 --> tx4
''')

vertices.propagate_with(self.manager)

tx1, tx2, tx3 = vertices.get_typed_vertices(['tx1', 'tx2', 'tx3'], Transaction)
tx1, tx2, tx3, tx4 = vertices.get_typed_vertices(['tx1', 'tx2', 'tx3', 'tx4'], Transaction)
b31, b32 = vertices.get_typed_vertices(['b31', 'b32'], Block)

# Uncomment for debugging:
Expand All @@ -207,6 +215,9 @@ def test_token_creation_by_contract(self) -> None:
assert tx3.get_metadata().voided_by == {tx3.hash, NC_EXECUTION_FAIL_ID}
assert tx3.get_metadata().nc_execution is NCExecutionState.FAILURE

assert tx4.get_metadata().voided_by is None
assert tx4.get_metadata().nc_execution is NCExecutionState.SUCCESS

assert b31.get_metadata().voided_by is None
assert b32.get_metadata().voided_by is None

Expand All @@ -217,26 +228,35 @@ def test_token_creation_by_contract(self) -> None:
reason='NCTokenAlreadyExists',
)

child_token_id = derive_child_token_id(ContractId(VertexId(tx1.hash)), token_symbol)
child_token_balance_key = BalanceKey(nc_id=tx1.hash, token_uid=child_token_id)
child_token_id0 = derive_child_token_id(ContractId(VertexId(tx1.hash)), token_symbol, salt=salt0)
child_token_id1 = derive_child_token_id(ContractId(VertexId(tx1.hash)), token_symbol, salt=salt1)

child_token_balance_key0 = BalanceKey(nc_id=tx1.hash, token_uid=child_token_id0)
child_token_balance_key1 = BalanceKey(nc_id=tx1.hash, token_uid=child_token_id1)
htr_balance_key = BalanceKey(nc_id=tx1.hash, token_uid=settings.HATHOR_TOKEN_UID)

block_storage = self.manager.get_nc_block_storage(b31)
expected_token_info = TokenDescription(
token_id=child_token_id,
block_storage = self.manager.get_nc_block_storage(b32)
assert block_storage.get_token_description(child_token_id0) == TokenDescription(
token_id=child_token_id0,
token_name='MyToken',
token_symbol=token_symbol,
)
assert block_storage.get_token_description(child_token_id1) == TokenDescription(
token_id=child_token_id1,
token_name='MyToken',
token_symbol=token_symbol,
)
assert block_storage.get_token_description(child_token_id) == expected_token_info

nc_storage = block_storage.get_contract_storage(tx1.hash)
assert nc_storage.get_all_balances() == {
child_token_balance_key: Balance(value=100, can_mint=False, can_melt=False),
child_token_balance_key0: Balance(value=100, can_mint=False, can_melt=False),
child_token_balance_key1: Balance(value=30, can_mint=True, can_melt=False),
htr_balance_key: Balance(value=2, can_mint=False, can_melt=False),
}

tokens_index = self.manager.tx_storage.indexes.tokens
assert tokens_index.get_token_info(settings.HATHOR_TOKEN_UID).get_total() == (
settings.GENESIS_TOKENS + 40 * settings.INITIAL_TOKENS_PER_BLOCK - 1
settings.GENESIS_TOKENS + 40 * settings.INITIAL_TOKENS_PER_BLOCK - 2
)
assert tokens_index.get_token_info(child_token_id).get_total() == 100
assert tokens_index.get_token_info(child_token_id0).get_total() == 100
assert tokens_index.get_token_info(child_token_id1).get_total() == 30
Loading