diff --git a/test/functional/test_framework/wallet_cli_controller.py b/test/functional/test_framework/wallet_cli_controller.py index 477c42c75..c5677aa06 100644 --- a/test/functional/test_framework/wallet_cli_controller.py +++ b/test/functional/test_framework/wallet_cli_controller.py @@ -357,6 +357,9 @@ async def unfreeze_token(self, token_id: str) -> str: async def change_token_authority(self, token_id: str, new_authority: str) -> str: return await self._write_command(f"token-change-authority {token_id} {new_authority}\n") + async def change_token_metadata_uri(self, token_id: str, new_metadata_uri: str) -> str: + return await self._write_command(f"token-change-metadata-uri {token_id} {new_metadata_uri}\n") + async def issue_new_nft(self, destination_address: str, media_hash: str, diff --git a/test/functional/test_framework/wallet_rpc_controller.py b/test/functional/test_framework/wallet_rpc_controller.py index f349c992e..92a7a5627 100644 --- a/test/functional/test_framework/wallet_rpc_controller.py +++ b/test/functional/test_framework/wallet_rpc_controller.py @@ -318,6 +318,9 @@ async def unfreeze_token(self, token_id: str) -> str: async def change_token_authority(self, token_id: str, new_authority: str) -> str: return self._write_command("token_change_authority", [self.account, token_id, new_authority, {'in_top_x_mb': 5}])['result'] + async def change_token_metadata_uri(self, token_id: str, new_metadata_uri: str) -> str: + return self._write_command("token_change_metadata_uri", [self.account, token_id, new_metadata_uri, {'in_top_x_mb': 5}])['result'] + async def issue_new_nft(self, destination_address: str, media_hash: str, diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index babb7f78f..cff0280aa 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -150,6 +150,8 @@ class UnicodeOnWindowsError(ValueError): 'wallet_tokens_transfer_from_multisig_addr.py', 'wallet_tokens_transfer_from_multisig_addr_rpc.py', 'wallet_tokens_change_authority.py', + 'wallet_tokens_change_metadata_uri.py', + 'wallet_tokens_change_metadata_uri_rpc.py', 'wallet_tokens_change_supply.py', 'wallet_nfts.py', 'wallet_decommission_genesis.py', diff --git a/test/functional/wallet_tokens_change_metadata_uri.py b/test/functional/wallet_tokens_change_metadata_uri.py new file mode 100644 index 000000000..6b382d3b0 --- /dev/null +++ b/test/functional/wallet_tokens_change_metadata_uri.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023 RBB S.r.l +# Copyright (c) 2017-2021 The Bitcoin Core developers +# opensource@mintlayer.org +# SPDX-License-Identifier: MIT +# Licensed under the MIT License; +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Wallet tokens change metadata uri test + +Check that: +* We can create a new wallet, +* get an address +* send coins to the wallet's address +* sync the wallet with the node +* check balance +* issue new token +* check metadata uri +* change metadata uri +* check that metadata uri is changed +""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.mintlayer import (make_tx, reward_input, ATOMS_PER_COIN) +from test_framework.util import assert_in, assert_equal +from test_framework.mintlayer import block_input_data_obj +from test_framework.wallet_cli_controller import WalletCliController + +import asyncio +import sys +import random +import string + +class WalletTokens(BitcoinTestFramework): + + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 1 + self.extra_args = [[ + "--blockprod-min-peers-to-produce-blocks=0", + ]] + + def setup_network(self): + self.setup_nodes() + self.sync_all(self.nodes[0:1]) + + def generate_block(self): + node = self.nodes[0] + + block_input_data = { "PoW": { "reward_destination": "AnyoneCanSpend" } } + block_input_data = block_input_data_obj.encode(block_input_data).to_hex()[2:] + + # create a new block, taking transactions from mempool + block = node.blockprod_generate_block(block_input_data, [], [], "FillSpaceFromMempool") + node.chainstate_submit_block(block) + block_id = node.chainstate_best_block_id() + + # Wait for mempool to sync + self.wait_until(lambda: node.mempool_local_best_block_id() == block_id, timeout = 5) + + return block_id + + def run_test(self): + if 'win32' in sys.platform: + asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) + asyncio.run(self.async_test()) + + async def async_test(self): + node = self.nodes[0] + + # new wallet + async with WalletCliController(node, self.config, self.log) as wallet: + await wallet.create_wallet() + + # check it is on genesis + assert_equal('0', await wallet.get_best_block_height()) + + # new address + pub_key_bytes = await wallet.new_public_key() + assert_equal(len(pub_key_bytes), 33) + + # Get chain tip + tip_id = node.chainstate_best_block_id() + self.log.debug(f'Tip: {tip_id}') + + # Submit a valid transaction + output = { + 'Transfer': [ { 'Coin': 1001 * ATOMS_PER_COIN }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], + } + encoded_tx, tx_id = make_tx([reward_input(tip_id)], [output], 0) + + node.mempool_submit_transaction(encoded_tx, {}) + assert node.mempool_contains_tx(tx_id) + + block_id = self.generate_block() # Block 1 + assert not node.mempool_contains_tx(tx_id) + + # sync the wallet + assert_in("Success", await wallet.sync()) + + # check wallet best block if it is synced + assert_equal(await wallet.get_best_block_height(), '1') + assert_equal(await wallet.get_best_block(), block_id) + assert_in("Coins amount: 1001", await wallet.get_balance()) + + # issue a valid token + address = await wallet.new_address() + metadata_uri = "http://uri" + token_id, err = await wallet.issue_new_token("XXX", 2, metadata_uri, address, token_supply='lockable') + assert token_id is not None + assert err is None + self.log.info(f"new token id: {token_id}") + + self.generate_block() + assert_in("Success", await wallet.sync()) + + token_info = node.chainstate_token_info(token_id) + assert_equal(metadata_uri, token_info['content']['metadata_uri']['text']); + + new_metadata_uri = bytes([random.randint(0, 255) for _ in range(random.randint(1, 128))]).hex() + assert_in("The transaction was submitted successfully", await wallet.change_token_metadata_uri(token_id, new_metadata_uri)) + + self.generate_block() + assert_in("Success", await wallet.sync()) + + token_info = node.chainstate_token_info(token_id) + print(token_info) + assert_equal(new_metadata_uri, token_info['content']['metadata_uri']['hex']); + assert token_info['content']['metadata_uri']['text'] is not metadata_uri + +if __name__ == '__main__': + WalletTokens().main() diff --git a/test/functional/wallet_tokens_change_metadata_uri_rpc.py b/test/functional/wallet_tokens_change_metadata_uri_rpc.py new file mode 100644 index 000000000..5b50bc681 --- /dev/null +++ b/test/functional/wallet_tokens_change_metadata_uri_rpc.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023 RBB S.r.l +# Copyright (c) 2017-2021 The Bitcoin Core developers +# opensource@mintlayer.org +# SPDX-License-Identifier: MIT +# Licensed under the MIT License; +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Wallet tokens change metadta uri test""" + +from wallet_tokens_change_metadata_uri import WalletTokens +from test_framework.wallet_rpc_controller import WalletRpcController + + +class WalletTokensRpc(WalletTokens): + def set_test_params(self): + super().set_test_params() + self.wallet_controller = WalletRpcController + + def run_test(self): + super().run_test() + + +if __name__ == '__main__': + WalletTokensRpc().main() diff --git a/wallet/src/account/mod.rs b/wallet/src/account/mod.rs index 83c3a4ff3..5370225b3 100644 --- a/wallet/src/account/mod.rs +++ b/wallet/src/account/mod.rs @@ -2239,8 +2239,14 @@ fn group_preselected_inputs( .ok_or(WalletError::OutputAmountOverflow)?, )?; } - // TODO(metadata) - AccountCommand::ChangeTokenMetadataUri(_, _) => unimplemented!(), + AccountCommand::ChangeTokenMetadataUri(token_id, _) => { + update_preselected_inputs( + currency_grouper::Currency::Token(*token_id), + Amount::ZERO, + (*fee + chain_config.token_change_metadata_uri_fee()) + .ok_or(WalletError::OutputAmountOverflow)?, + )?; + } // TODO(orders) AccountCommand::ConcludeOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(),