Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/wallet generic signer #1783

Open
wants to merge 45 commits into
base: master
Choose a base branch
from
Open

Conversation

OBorce
Copy link
Contributor

@OBorce OBorce commented Jun 18, 2024

  • add a generic signer provider for creating a software or a hardware signer
  • make account key chain generic to allow using it with and without a VRF keychain
  • add an implementation for the trezor signer
  • copy auto generated trezor client to communicate with a connected trezor device
  • add new hardware-wallet option when creating and opening a wallet in the CLI and RPC wallet commands

@OBorce OBorce force-pushed the refactor/wallet-generic-signer branch from e1f4596 to 37b71b9 Compare June 20, 2024 04:25
@OBorce OBorce force-pushed the refactor/wallet-generic-signer branch 6 times, most recently from 626b7d1 to fb2af0d Compare August 1, 2024 07:47
@OBorce OBorce marked this pull request as ready for review August 1, 2024 12:04
do_checks.sh Outdated Show resolved Hide resolved
@OBorce OBorce force-pushed the refactor/wallet-generic-signer branch from 726bb45 to 855cfce Compare August 6, 2024 10:54
build-tools/codecheck/codecheck.py Outdated Show resolved Hide resolved
crypto/src/key/secp256k1/extended_keys.rs Outdated Show resolved Hide resolved
crypto/src/key/extended.rs Outdated Show resolved Hide resolved
}
#[cfg(feature = "trezor")]
(WalletType::Trezor, _) => {
let client = make_cold_wallet_rpc_client(Arc::clone(&self.chain_config));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why cold mode? I thought that's the whole point that hardware wallet is both convenient and secure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's make it an error? Not sure if there is a use case for Trezor + Cold

Cargo.toml Show resolved Hide resolved
wallet/src/signer/trezor_signer/mod.rs Outdated Show resolved Hide resolved
wallet/Cargo.toml Outdated Show resolved Hide resolved
wallet/wallet-cli-commands/src/helper_types.rs Outdated Show resolved Hide resolved
wallet/wallet-cli-commands/src/command_handler/mod.rs Outdated Show resolved Hide resolved
wallet/wallet-controller/src/lib.rs Outdated Show resolved Hide resolved
wallet/wallet-controller/src/lib.rs Outdated Show resolved Hide resolved
@OBorce OBorce force-pushed the refactor/wallet-generic-signer branch 2 times, most recently from 1cd8110 to c23f38d Compare August 13, 2024 23:23
wallet/src/signer/trezor_signer/mod.rs Outdated Show resolved Hide resolved
wallet/src/signer/trezor_signer/mod.rs Show resolved Hide resolved
wallet/src/signer/trezor_signer/mod.rs Outdated Show resolved Hide resolved
wallet/src/signer/trezor_signer/tests.rs Show resolved Hide resolved
wallet/src/signer/trezor_signer/mod.rs Outdated Show resolved Hide resolved

/// Create a wallet using a connected hardware wallet. Only the public keys will be kept in
/// the software wallet
#[arg(long, conflicts_with_all(["mnemonic", "passphrase"]))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it also conflicts with whether_to_store_seed_phrase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whether_to_store_seed_phrase was a required parameter I am not sure if we should break backwards compatibility or not, that is why I left it alone, and just check that it is set to false.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should thenwhether_to_store_seed_phrase be marked with required_unless_present("hardware_wallet") or something like that? So people won't have to type it every time for hardware.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it also conflicts with whether_to_store_seed_phrase

I guess this comment is still relevant.

Also, I suggest updating descriptions for all options that don't work with "--hardware_wallet" so that they explicitly mention this.

wallet/wallet-cli-commands/src/lib.rs Show resolved Hide resolved
wallet/wallet-controller/src/types/mod.rs Outdated Show resolved Hide resolved
wallet/wallet-cli-commands/src/lib.rs Show resolved Hide resolved
wallet/src/signer/trezor_signer/mod.rs Outdated Show resolved Hide resolved
@OBorce OBorce force-pushed the refactor/wallet-generic-signer branch from c23f38d to 4824da8 Compare August 30, 2024 16:09
@OBorce OBorce force-pushed the refactor/wallet-generic-signer branch 4 times, most recently from 6c3d058 to 037f258 Compare September 17, 2024 19:42
wallet/wallet-rpc-lib/src/rpc/types.rs Outdated Show resolved Hide resolved
wallet/wallet-rpc-lib/src/service/worker.rs Outdated Show resolved Hide resolved
wallet/types/src/wallet_type.rs Outdated Show resolved Hide resolved
wallet/wallet-cli-commands/src/command_handler/mod.rs Outdated Show resolved Hide resolved
wallet/src/signer/trezor_signer/tests.rs Outdated Show resolved Hide resolved
Destination::AnyoneCanSpend,
),
TxOutput::DataDeposit(vec![1, 2, 3]),
TxOutput::Htlc(OutputValue::Coin(burn_amount), Box::new(hash_lock)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there also should be a case for spending Htlc utxo with a secret

wallet/src/signer/trezor_signer/tests.rs Outdated Show resolved Hide resolved
wallet/src/signer/trezor_signer/tests.rs Show resolved Hide resolved
assert!(!devices.is_empty());
let client = devices.pop().unwrap().connect().unwrap();

let mut signer = TrezorSigner::new(chain_config.clone(), Arc::new(Mutex::new(client)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. There should be some negative cases: where trezor failed to sign inputs with unknown keys or something like that.
  2. How does device become aware of private keys? I see that at the beginning of the test a key chain is created from mnemonic, but how trezor is related to that I don't understand (like initialising it with test mnemonic of something)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok I keep forgetting that this is manual test. So basically you have to recover a wallet from test mnemonic right?

@OBorce OBorce force-pushed the refactor/wallet-generic-signer branch from fd48654 to 511bba3 Compare December 10, 2024 16:56
@OBorce OBorce force-pushed the refactor/wallet-generic-signer branch from 3b76519 to 46cf688 Compare December 11, 2024 10:35
Comment on lines +183 to +194
TxOutput::ProduceBlockFromStake(_, pool_id) => {
let staker_balance = rpc_client
.get_staker_balance(*pool_id)
.await
.map_err(ControllerError::NodeCallError)?
.ok_or(WalletError::UnknownPoolId(*pool_id))?;
Ok(UtxoWithAdditionalInfo::new(
utxo,
Some(UtxoAdditionalInfo::PoolInfo { staker_balance }),
))
}
TxOutput::IssueNft(_, _, _)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this somewhat scary. E.g. we're signing a raw transaction, which was created who knows when, and then here we're obtaining the pool balance which now may be different from the one that it had when the tx was created (e.g. due to a reorg). So we're basically producing potentially incorrect data and rely on it being ignored (because, as I can see, it's not used in places where this function is called).
Which makes me think that "dynamic" data, like pool balances (as opposed to "static" data, like token decimals) shouldn't be in UtxoWithAdditionalInfo.
But then it'd make sense to move the static data out of it as well, because e.g. it'd be more natural for token decimals to be stored in a single map and passed alongside PartiallySignedTransaction (or maybe it can still be a part of PartiallySignedTransaction, but not a part of individual utxos).
I wonder whether we should reconsider this whole approach with UtxoWithAdditionalInfo. @azarovh what do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure there is a way around it because we have to display what is being signed and a tx has to have an output with specific decommissioned amount.

Copy link
Contributor Author

@OBorce OBorce Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is that the same dynamic data is also used on tx creation and once you submit the tx to the node to validate the tx. So if it is wrong now it will also be wrong on submittion.

~~The only issue I see is if the user's node itself creates 2 blocks which cause a reorg. They create the tx while on the first block and then a reorg happens with the other block which can have more or less fees so the tx might be rejected if the new balance is less or silently accepted and the excess coins going to fees. ~~ actually the block id will be different and the utxo will not be the same

Comment on lines +1129 to +1132
let device = find_devices(false)
.into_iter()
.find(|device| device.model == Model::Trezor)
.ok_or(TrezorError::NoDeviceFound)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Regarding 'Model' variants - why ignore TrezorEmulator? Also, TrezorLegacy is the Model One as I can see, so we should support it too.
  2. What if multiple devices are connected? I think it's not a good idea to select one randomly. Probably the easiest solution is to refuse to proceed asking the user to leave only one device connected. But ideally we should allow to select the device.

@ImplOfAnImpl
Copy link
Contributor

Some additional thoughts:

  1. I don't think you ever check the device for Capability_Mintlayer. If so, we'll be getting bug reports like "nothing works", only to find out later that the user didn't install our firmware.
  2. It's better to show to the user the name of the device that is being used, to avoid confusion (the label field of message Features).
  3. It may even make sense to store the device name in the wallet file itself, so that when there is a mismatch, we could tell the user what device must be used with this wallet file.
    (But since the label can be changed any time, we'll probably have to update it every time after we successfully connect to the proper device).
  4. We should probably also store the device_id in the wallet file, so that when we detect public keys mismatch, we can tell the user whether the device itself is wrong or the entered passphrase.
    The tricky thing is that device_id is not hardcoded in the device and instead is randomly generated on device setup. So if the user resets the device and restores the same seed phrase, the id will be different. So we'll still have to check the keys first and then if the device_id doesn't match, tell the user that the device seems to be different.

@ImplOfAnImpl
Copy link
Contributor

Some minor issues:

  1. The wallet file is created before a device is successfully discovered. So, if there are no connected devices, the user ends up with a useless wallet file.
  2. The error dialogs in node-gui are messed up. This is the one when no device can be found:
    node-gui-no-trezor-device
    and this is when a file with the same name already exists:
    node-gui-file-exists
    Btw, when I press "ok, the previous dialog is still shown and has to be closed manually:
    node-gui-old-dialog-still-shown
    This happens for the software wallet too.
    Also, shouldn't we overwrite the existing file instead of failing? Because the user has already confirmed overwriting when selecting the file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants