Skip to content

Commit

Permalink
Implement sign tx intent for Trezor signer
Browse files Browse the repository at this point in the history
  • Loading branch information
OBorce committed Dec 10, 2024
1 parent 1d766e5 commit 511bba3
Show file tree
Hide file tree
Showing 9 changed files with 755 additions and 606 deletions.
1,172 changes: 574 additions & 598 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions common/src/chain/transaction/signed_transaction_intent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ pub struct SignedTransactionIntent {
}

impl SignedTransactionIntent {
pub fn new_unchecked(signed_message: String, signatures: Vec<Vec<u8>>) -> Self {
Self {
signed_message,
signatures,
}
}

/// Create a signed intent given the id of the transaction and its input destinations.
///
/// Only PublicKeyHash and PublicKey destinations are supported by this function.
Expand Down
2 changes: 2 additions & 0 deletions wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ tempfile.workspace = true
[features]
trezor = ["dep:trezor-client", "wallet-types/trezor"]
trezor-emulator = []

default = ["trezor"]
68 changes: 68 additions & 0 deletions wallet/src/signer/software_signer/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,74 @@ fn sign_message(#[case] seed: Seed) {
res.verify_signature(&config, &destination, &message_challenge).unwrap();
}

#[rstest]
#[trace]
#[case(Seed::from_entropy())]
fn sign_transaction_intent(#[case] seed: Seed) {
use common::primitives::Idable;

let mut rng = make_seedable_rng(seed);

let config = Arc::new(create_regtest());
let db = Arc::new(Store::new(DefaultBackend::new_in_memory()).unwrap());
let mut db_tx = db.transaction_rw_unlocked(None).unwrap();

let master_key_chain = MasterKeyChain::new_from_mnemonic(
config.clone(),
&mut db_tx,
MNEMONIC,
None,
StoreSeedPhrase::DoNotStore,
)
.unwrap();

let key_chain = master_key_chain
.create_account_key_chain(&mut db_tx, DEFAULT_ACCOUNT_INDEX, LOOKAHEAD_SIZE)
.unwrap();
let mut account = Account::new(config.clone(), &mut db_tx, key_chain, None).unwrap();

let mut signer = SoftwareSigner::new(config.clone(), DEFAULT_ACCOUNT_INDEX);

let inputs: Vec<TxInput> = (0..rng.gen_range(1..5))
.map(|_| {
let source_id = if rng.gen_bool(0.5) {
Id::<Transaction>::new(H256::random_using(&mut rng)).into()
} else {
Id::<GenBlock>::new(H256::random_using(&mut rng)).into()
};
TxInput::from_utxo(source_id, rng.next_u32())
})
.collect();
let input_destinations: Vec<_> = (0..inputs.len())
.map(|_| account.get_new_address(&mut db_tx, ReceiveFunds).unwrap().1.into_object())
.collect();

let tx = Transaction::new(
0,
inputs,
vec![TxOutput::Transfer(
OutputValue::Coin(Amount::from_atoms(rng.gen())),
account.get_new_address(&mut db_tx, Change).unwrap().1.into_object(),
)],
)
.unwrap();

let intent: String = [rng.gen::<char>(), rng.gen::<char>(), rng.gen::<char>()].iter().collect();
let res = signer
.sign_transaction_intent(
&tx,
&input_destinations,
&intent,
account.key_chain(),
&db_tx,
)
.unwrap();

let expected_signed_message =
SignedTransactionIntent::get_message_to_sign(&intent, &tx.get_id());
res.verify(&config, &input_destinations, &expected_signed_message).unwrap();
}

#[rstest]
#[trace]
#[case(Seed::from_entropy())]
Expand Down
26 changes: 19 additions & 7 deletions wallet/src/signer/trezor_signer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use common::{
AccountCommand, AccountSpending, ChainConfig, Destination, OutPointSourceId,
SignedTransactionIntent, Transaction, TxInput, TxOutput,
},
primitives::H256,
primitives::{Idable, H256},
};
use crypto::key::{
extended::ExtendedPublicKey,
Expand Down Expand Up @@ -478,13 +478,25 @@ impl Signer for TrezorSigner {

fn sign_transaction_intent(
&mut self,
_transaction: &Transaction,
_input_destinations: &[Destination],
_intent: &str,
_key_chain: &impl AccountKeyChains,
_db_tx: &impl WalletStorageReadUnlocked,
transaction: &Transaction,
input_destinations: &[Destination],
intent: &str,
key_chain: &impl AccountKeyChains,
db_tx: &impl WalletStorageReadUnlocked,
) -> SignerResult<SignedTransactionIntent> {
unimplemented!("FIXME")
let tx_id = transaction.get_id();
let message_to_sign = SignedTransactionIntent::get_message_to_sign(intent, &tx_id);

let mut signatures = Vec::with_capacity(input_destinations.len());
for dest in input_destinations {
let sig = self.sign_challenge(message_to_sign.as_bytes(), dest, key_chain, db_tx)?;
signatures.push(sig.into_raw());
}

Ok(SignedTransactionIntent::new_unchecked(
message_to_sign,
signatures,
))
}
}

Expand Down
72 changes: 72 additions & 0 deletions wallet/src/signer/trezor_signer/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,78 @@ fn sign_message(#[case] seed: Seed) {
res.verify_signature(&chain_config, &destination, &message_challenge).unwrap();
}

#[rstest]
#[trace]
#[case(Seed::from_entropy())]
fn sign_transaction_intent(#[case] seed: Seed) {
use common::primitives::Idable;

let mut rng = make_seedable_rng(seed);

let config = Arc::new(create_regtest());
let db = Arc::new(Store::new(DefaultBackend::new_in_memory()).unwrap());
let mut db_tx = db.transaction_rw_unlocked(None).unwrap();

let master_key_chain = MasterKeyChain::new_from_mnemonic(
config.clone(),
&mut db_tx,
MNEMONIC,
None,
StoreSeedPhrase::DoNotStore,
)
.unwrap();

let key_chain = master_key_chain
.create_account_key_chain(&mut db_tx, DEFAULT_ACCOUNT_INDEX, LOOKAHEAD_SIZE)
.unwrap();
let mut account = Account::new(config.clone(), &mut db_tx, key_chain, None).unwrap();

let mut devices = find_devices(false);
assert!(!devices.is_empty());
let client = devices.pop().unwrap().connect().unwrap();

let mut signer = TrezorSigner::new(config.clone(), Arc::new(Mutex::new(client)));

let inputs: Vec<TxInput> = (0..rng.gen_range(1..5))
.map(|_| {
let source_id = if rng.gen_bool(0.5) {
Id::<Transaction>::new(H256::random_using(&mut rng)).into()
} else {
Id::<GenBlock>::new(H256::random_using(&mut rng)).into()
};
TxInput::from_utxo(source_id, rng.next_u32())
})
.collect();
let input_destinations: Vec<_> = (0..inputs.len())
.map(|_| account.get_new_address(&mut db_tx, ReceiveFunds).unwrap().1.into_object())
.collect();

let tx = Transaction::new(
0,
inputs,
vec![TxOutput::Transfer(
OutputValue::Coin(Amount::from_atoms(rng.gen())),
account.get_new_address(&mut db_tx, Change).unwrap().1.into_object(),
)],
)
.unwrap();

let intent: String = [rng.gen::<char>(), rng.gen::<char>(), rng.gen::<char>()].iter().collect();
let res = signer
.sign_transaction_intent(
&tx,
&input_destinations,
&intent,
account.key_chain(),
&db_tx,
)
.unwrap();

let expected_signed_message =
SignedTransactionIntent::get_message_to_sign(&intent, &tx.get_id());
res.verify(&config, &input_destinations, &expected_signed_message).unwrap();
}

#[rstest]
#[trace]
#[case(Seed::from_entropy())]
Expand Down
3 changes: 2 additions & 1 deletion wallet/src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1452,6 +1452,7 @@ where
intent: String,
current_fee_rate: FeeRate,
consolidate_fee_rate: FeeRate,
additional_utxo_infos: &BTreeMap<PoolOrTokenId, UtxoAdditionalInfo>,
) -> WalletResult<(SignedTransaction, SignedTransactionIntent)> {
let (signed_tx, input_destinations) = self.create_transaction_to_addresses_impl(
account_index,
Expand All @@ -1461,7 +1462,7 @@ where
current_fee_rate,
consolidate_fee_rate,
|send_request| send_request.destinations().to_owned(),
&BTreeMap::new(), // FIXME
additional_utxo_infos,
)?;

let signed_intent = self.for_account_rw_unlocked(
Expand Down
3 changes: 3 additions & 0 deletions wallet/wallet-controller/src/runtime_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,7 @@ impl<B: storage::Backend + 'static> RuntimeWallet<B> {
intent: String,
current_fee_rate: FeeRate,
consolidate_fee_rate: FeeRate,
additional_utxo_infos: &BTreeMap<PoolOrTokenId, UtxoAdditionalInfo>,
) -> WalletResult<(SignedTransaction, SignedTransactionIntent)> {
match self {
RuntimeWallet::Software(w) => w.create_transaction_to_addresses_with_intent(
Expand All @@ -1223,6 +1224,7 @@ impl<B: storage::Backend + 'static> RuntimeWallet<B> {
intent,
current_fee_rate,
consolidate_fee_rate,
additional_utxo_infos,
),
#[cfg(feature = "trezor")]
RuntimeWallet::Trezor(w) => w.create_transaction_to_addresses_with_intent(
Expand All @@ -1233,6 +1235,7 @@ impl<B: storage::Backend + 'static> RuntimeWallet<B> {
intent,
current_fee_rate,
consolidate_fee_rate,
additional_utxo_infos,
),
}
}
Expand Down
8 changes: 8 additions & 0 deletions wallet/wallet-controller/src/synced_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,13 @@ where
account_index: U31,
token_info: &UnconfirmedTokenInfo| {
token_info.check_can_be_used()?;
let additional_info = BTreeMap::from_iter([(
PoolOrTokenId::TokenId(token_info.token_id()),
UtxoAdditionalInfo::TokenInfo(TokenAdditionalInfo {
num_decimals: token_info.num_decimals(),
ticker: token_info.token_ticker().to_vec(),
}),
)]);
wallet.create_transaction_to_addresses_with_intent(
account_index,
[output],
Expand All @@ -1006,6 +1013,7 @@ where
intent,
current_fee_rate,
consolidate_fee_rate,
&additional_info,
)
},
)
Expand Down

0 comments on commit 511bba3

Please sign in to comment.