diff --git a/test/functional/test_framework/wallet_cli_controller.py b/test/functional/test_framework/wallet_cli_controller.py index 78a800fc38..d46bbeccf3 100644 --- a/test/functional/test_framework/wallet_cli_controller.py +++ b/test/functional/test_framework/wallet_cli_controller.py @@ -22,7 +22,7 @@ from dataclasses import dataclass from tempfile import NamedTemporaryFile -from typing import Optional, List +from typing import Optional, List, Tuple ONE_MB = 2**20 READ_TIMEOUT_SEC = 30 @@ -176,13 +176,12 @@ async def issue_new_token(self, amount_to_issue: str, number_of_decimals: int, metadata_uri: str, - destination_address: str) -> Optional[str]: - output = await self._write_command(f"issuenewtoken {token_ticker} {amount_to_issue} {number_of_decimals} {metadata_uri} {destination_address}\n") + destination_address: str) -> Tuple[Optional[str], Optional[str]]: + output = await self._write_command(f'issuenewtoken "{token_ticker}" "{amount_to_issue}" "{number_of_decimals}" "{metadata_uri}" {destination_address}\n') if output.startswith("A new token has been issued with ID"): - return output[output.find(':')+2:] + return output[output.find(':')+2:], None - self.log.error(f"err: {output}") - return None + return None, output async def issue_new_nft(self, destination_address: str, diff --git a/test/functional/wallet_delegations.py b/test/functional/wallet_delegations.py index 5e21884c33..462c2eb90f 100644 --- a/test/functional/wallet_delegations.py +++ b/test/functional/wallet_delegations.py @@ -305,7 +305,7 @@ async def async_test(self): # Submit a valid transaction output = { - 'Transfer': [ { 'Coin': 50_000_000_000_000_000 }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], + 'Transfer': [ { 'Coin': 50_000 * MLT_COIN }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], } encoded_tx, tx_id = make_tx([reward_input(tip_id)], [output], 0) self.log.debug(f"Encoded transaction {tx_id}: {encoded_tx}") diff --git a/test/functional/wallet_get_address_usage.py b/test/functional/wallet_get_address_usage.py index 374dbbbfea..56a166b7bc 100644 --- a/test/functional/wallet_get_address_usage.py +++ b/test/functional/wallet_get_address_usage.py @@ -25,7 +25,7 @@ """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.mintlayer import (make_tx, reward_input, tx_input) +from test_framework.mintlayer import (MLT_COIN, make_tx, reward_input, tx_input) from test_framework.util import assert_raises_rpc_error from test_framework.mintlayer import mintlayer_hash, block_input_data_obj from test_framework.wallet_cli_controller import WalletCliController @@ -98,7 +98,7 @@ async def async_test(self): # Submit a valid transaction output = { - 'Transfer': [ { 'Coin': 1_000_000_000_000 }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], + 'Transfer': [ { 'Coin': 10 * MLT_COIN }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], } encoded_tx, tx_id = make_tx([reward_input(tip_id)], [output], 0) diff --git a/test/functional/wallet_nfts.py b/test/functional/wallet_nfts.py index dabaa5733a..a6b7f753ab 100644 --- a/test/functional/wallet_nfts.py +++ b/test/functional/wallet_nfts.py @@ -28,7 +28,7 @@ """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.mintlayer import (make_tx, reward_input, tx_input) +from test_framework.mintlayer import (make_tx, reward_input, tx_input, MLT_COIN) from test_framework.util import assert_raises_rpc_error from test_framework.mintlayer import mintlayer_hash, block_input_data_obj from test_framework.wallet_cli_controller import WalletCliController @@ -90,7 +90,7 @@ async def async_test(self): # Submit a valid transaction output = { - 'Transfer': [ { 'Coin': 100_000_000_000_000 }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], + 'Transfer': [ { 'Coin': 1000 * MLT_COIN }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], } encoded_tx, tx_id = make_tx([reward_input(tip_id)], [output], 0) diff --git a/test/functional/wallet_recover_accounts.py b/test/functional/wallet_recover_accounts.py index 4170dd80f9..6c1001e55b 100644 --- a/test/functional/wallet_recover_accounts.py +++ b/test/functional/wallet_recover_accounts.py @@ -29,7 +29,7 @@ """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.mintlayer import (make_tx, reward_input, tx_input) +from test_framework.mintlayer import (make_tx, reward_input, tx_input, MLT_COIN) from test_framework.util import assert_raises_rpc_error from test_framework.mintlayer import mintlayer_hash, block_input_data_obj from test_framework.wallet_cli_controller import DEFAULT_ACCOUNT_INDEX, WalletCliController @@ -77,6 +77,9 @@ async def async_test(self): # new wallet async with WalletCliController(node, self.config, self.log) as wallet: + invalid_mnemonic = "asd asd dwa" + assert "Invalid mnemonic:" in await wallet.recover_wallet(invalid_mnemonic) + await wallet.create_wallet() # check it is on genesis @@ -93,7 +96,7 @@ async def async_test(self): # Submit a valid transaction output = { - 'Transfer': [ { 'Coin': 1_000_000_000_000 }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], + 'Transfer': [ { 'Coin': 10 * MLT_COIN }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], } encoded_tx, tx_id = make_tx([reward_input(tip_id)], [output], 0) diff --git a/test/functional/wallet_select_utxos.py b/test/functional/wallet_select_utxos.py index f9e6c44c00..75e606eeca 100644 --- a/test/functional/wallet_select_utxos.py +++ b/test/functional/wallet_select_utxos.py @@ -30,7 +30,7 @@ from test_framework.mintlayer import (make_tx, reward_input, tx_input) from test_framework.util import assert_raises_rpc_error from test_framework.mintlayer import mintlayer_hash, block_input_data_obj -from test_framework.wallet_cli_controller import WalletCliController +from test_framework.wallet_cli_controller import UtxoOutpoint, WalletCliController import asyncio import sys @@ -124,6 +124,15 @@ def make_output(pub_key_bytes): assert len(utxos) == num_utxos address = await wallet.new_address() + + # try to select one and send more than it has it should fail + selected_utxos = utxos[:1] + output = await wallet.send_to_address(address, 11, selected_utxos) + assert "Controller error: Wallet error: Coin selection error: Not enough funds" in output + # check that we didn't spent any utxos + assert utxos == await wallet.list_utxos() + + # select the first 3 and check that they will be spent selected_utxos = utxos[:3] not_selected_utxos = utxos[3:] await wallet.send_to_address(address, 1, selected_utxos) @@ -146,6 +155,17 @@ def make_output(pub_key_bytes): # check not-selected utxos are still present assert all(not_selected_utxo in new_utxos for not_selected_utxo in not_selected_utxos) + # try to select already spent utxo + assert "Selected UTXO is already consumed" in await wallet.send_to_address(address, 11, selected_utxos) + + # try to select unknown utxo + unknown_utxo_id = "0" * len(selected_utxos[0].id) + unknown_utxo = UtxoOutpoint(unknown_utxo_id, 1) + assert "Cannot find UTXO" in await wallet.send_to_address(address, 11, [unknown_utxo]) + + # check that we didn't spent any utxos + assert new_utxos == await wallet.list_utxos() + if __name__ == '__main__': diff --git a/test/functional/wallet_submit_tx.py b/test/functional/wallet_submit_tx.py index e3f1c6d6cb..ea027dc342 100644 --- a/test/functional/wallet_submit_tx.py +++ b/test/functional/wallet_submit_tx.py @@ -25,7 +25,7 @@ """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.mintlayer import (make_tx, reward_input, tx_input) +from test_framework.mintlayer import (make_tx, reward_input, tx_input, MLT_COIN) from test_framework.util import assert_raises_rpc_error from test_framework.mintlayer import mintlayer_hash, block_input_data_obj from test_framework.wallet_cli_controller import WalletCliController @@ -89,7 +89,7 @@ async def async_test(self): # Submit a valid transaction output = { - 'Transfer': [ { 'Coin': 1_000_000_000_000 }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], + 'Transfer': [ { 'Coin': 10 * MLT_COIN }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], } encoded_tx, tx_id = make_tx([reward_input(tip_id)], [output], 0) diff --git a/test/functional/wallet_tokens.py b/test/functional/wallet_tokens.py index 78c2ec7fc7..e6fe5dfda9 100644 --- a/test/functional/wallet_tokens.py +++ b/test/functional/wallet_tokens.py @@ -28,7 +28,7 @@ """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.mintlayer import (make_tx, reward_input, tx_input) +from test_framework.mintlayer import (make_tx, reward_input, tx_input, MLT_COIN) from test_framework.util import assert_raises_rpc_error from test_framework.mintlayer import mintlayer_hash, block_input_data_obj from test_framework.wallet_cli_controller import WalletCliController @@ -90,7 +90,7 @@ async def async_test(self): # Submit a valid transaction output = { - 'Transfer': [ { 'Coin': 100_000_000_000_000 }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], + 'Transfer': [ { 'Coin': 101 * MLT_COIN }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ], } encoded_tx, tx_id = make_tx([reward_input(tip_id)], [output], 0) @@ -108,11 +108,38 @@ async def async_test(self): assert await wallet.get_best_block_height() == '1' assert await wallet.get_best_block() == block_id - assert "Coins amount: 1000" in await wallet.get_balance() + assert "Coins amount: 101" in await wallet.get_balance() address = await wallet.new_address() - token_id = await wallet.issue_new_token("XXX", "10000", 2, "http://uri", address) + + # invalid ticker + # > max len + token_id, err = await wallet.issue_new_token("asdddd", "10000", 2, "http://uri", address) + assert token_id is None + assert err is not None + assert "Invalid ticker length" in err + # non alphanumeric + token_id, err = await wallet.issue_new_token("asd#", "10000", 2, "http://uri", address) + assert token_id is None + assert err is not None + assert "Invalid character in token ticker" in err + + # invalid url + token_id, err = await wallet.issue_new_token("XXX", "10000", 2, "123 123", address) + assert token_id is None + assert err is not None + assert "Incorrect metadata URI" in err + + # invalid num decimals + token_id, err = await wallet.issue_new_token("XXX", "10000", 99, "http://uri", address) + assert token_id is None + assert err is not None + assert "Too many decimals" in err + + # issue a valid token + token_id, err = await wallet.issue_new_token("XXX", "10000", 2, "http://uri", address) assert token_id is not None + assert err is None self.log.info(f"new token id: {token_id}") self.generate_block() @@ -135,6 +162,12 @@ async def async_test(self): # check the new balance assert f"{token_id} amount: 9989.99" in await wallet.get_balance() + # try to issue a new token, should fail with not enough coins + token_id, err = await wallet.issue_new_token("XXX", "10000", 2, "http://uri", address) + assert token_id is None + assert err is not None + assert "Not enough funds" in err + if __name__ == '__main__': WalletTokens().main() diff --git a/wallet/wallet-cli-lib/Cargo.toml b/wallet/wallet-cli-lib/Cargo.toml index fd5cf543dc..cbb815347f 100644 --- a/wallet/wallet-cli-lib/Cargo.toml +++ b/wallet/wallet-cli-lib/Cargo.toml @@ -8,6 +8,7 @@ rust-version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +chainstate = { path = "../../chainstate" } common = { path = "../../common" } consensus = { path = "../../consensus" } crypto = { path = "../../crypto" } @@ -36,7 +37,6 @@ prettytable-rs = "0.10" [dev-dependencies] blockprod = { path = "../../blockprod" } -chainstate = { path = "../../chainstate" } chainstate-storage = { path = "../../chainstate/storage" } crypto = { path = "../../crypto" } mempool = { path = "../../mempool" } diff --git a/wallet/wallet-cli-lib/src/commands/mod.rs b/wallet/wallet-cli-lib/src/commands/mod.rs index 906c79dc34..e1941e196f 100644 --- a/wallet/wallet-cli-lib/src/commands/mod.rs +++ b/wallet/wallet-cli-lib/src/commands/mod.rs @@ -17,6 +17,7 @@ mod helper_types; use std::{path::PathBuf, str::FromStr, sync::Arc}; +use chainstate::TokenIssuanceError; use clap::Parser; use common::{ address::Address, @@ -29,10 +30,13 @@ use common::{ use crypto::key::{hdkd::u31::U31, PublicKey}; use p2p_types::{bannable_address::BannableAddress, ip_or_socket_address::IpOrSocketAddress}; use serialization::{hex::HexEncode, hex_encoded::HexEncoded}; -use wallet::{account::Currency, version::get_version, wallet_events::WalletEventsNoOp}; +use utils::ensure; +use wallet::{ + account::Currency, version::get_version, wallet_events::WalletEventsNoOp, WalletError, +}; use wallet_controller::{ - read::ReadOnlyController, synced_controller::SyncedController, NodeInterface, NodeRpcClient, - PeerId, DEFAULT_ACCOUNT_INDEX, + read::ReadOnlyController, synced_controller::SyncedController, ControllerError, NodeInterface, + NodeRpcClient, PeerId, DEFAULT_ACCOUNT_INDEX, }; use crate::{errors::WalletCliError, CliController}; @@ -474,6 +478,7 @@ impl CommandHandler { .await .map_err(WalletCliError::Controller) } + fn get_readonly_controller( &mut self, ) -> Result, WalletCliError> { @@ -784,6 +789,13 @@ impl CommandHandler { metadata_uri, destination_address, } => { + ensure!( + number_of_decimals <= chain_config.token_max_dec_count(), + WalletCliError::Controller(ControllerError::WalletError( + WalletError::TokenIssuance(TokenIssuanceError::IssueErrorTooManyDecimals), + )) + ); + let amount_to_issue = parse_token_amount(number_of_decimals, &amount_to_issue)?; let destination_address = parse_address(chain_config, &destination_address)?;