You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/rust/trezor-client/examples/sign_tx.rs

120 lines
4.6 KiB

use std::io::{self, Write};
use bitcoin::{
bip32, blockdata::script::Builder, consensus::encode::Decodable, network::constants::Network,
psbt, Address, Sequence, Transaction, TxIn, TxOut,
};
use trezor_client::{Error, SignTxProgress, TrezorMessage, TrezorResponse};
fn handle_interaction<T, R: TrezorMessage>(resp: TrezorResponse<T, R>) -> T {
match resp {
TrezorResponse::Ok(res) => res,
// assering ok() returns the failure error
TrezorResponse::Failure(_) => resp.ok().unwrap(),
TrezorResponse::ButtonRequest(req) => handle_interaction(req.ack().unwrap()),
TrezorResponse::PinMatrixRequest(req) => {
println!("Enter PIN");
let mut pin = String::new();
if io::stdin().read_line(&mut pin).unwrap() != 5 {
println!("must enter pin, received: {}", pin);
}
// trim newline
handle_interaction(req.ack_pin(pin[..4].to_owned()).unwrap())
}
TrezorResponse::PassphraseRequest(req) => {
println!("Enter passphrase");
let mut pass = String::new();
io::stdin().read_line(&mut pass).unwrap();
// trim newline
handle_interaction(req.ack_passphrase(pass[..pass.len() - 1].to_owned()).unwrap())
}
}
}
fn tx_progress(
psbt: &mut psbt::PartiallySignedTransaction,
progress: SignTxProgress,
raw_tx: &mut Vec<u8>,
) -> Result<(), Error> {
if let Some(part) = progress.get_serialized_tx_part() {
raw_tx.write_all(part).unwrap();
}
if !progress.finished() {
let progress = handle_interaction(progress.ack_psbt(psbt, Network::Testnet).unwrap());
tx_progress(psbt, progress, raw_tx)
} else {
Ok(())
}
}
fn main() {
tracing_subscriber::fmt().with_max_level(tracing::Level::TRACE).init();
// init with debugging
let mut trezor = trezor_client::unique(false).unwrap();
trezor.init_device(None).unwrap();
let pubkey = handle_interaction(
trezor
.get_public_key(
&vec![
bip32::ChildNumber::from_hardened_idx(0).unwrap(),
bip32::ChildNumber::from_hardened_idx(0).unwrap(),
bip32::ChildNumber::from_hardened_idx(1).unwrap(),
]
.into(),
trezor_client::protos::InputScriptType::SPENDADDRESS,
Network::Testnet,
true,
)
.unwrap(),
);
let addr = Address::p2pkh(&pubkey.to_pub(), Network::Testnet);
println!("address: {}", addr);
let mut psbt = psbt::PartiallySignedTransaction {
unsigned_tx: Transaction {
version: 1,
lock_time: bitcoin::absolute::LockTime::from_consensus(0),
input: vec![TxIn {
previous_output: "c5bdb27907b78ce03f94e4bf2e94f7a39697b9074b79470019e3dbc76a10ecb6:0".parse().unwrap(),
sequence: Sequence(0xffffffff),
script_sig: Builder::new().into_script(),
witness: Default::default(),
}],
output: vec![TxOut {
value: 14245301,
script_pubkey: addr.script_pubkey(),
}],
},
inputs: vec![psbt::Input {
non_witness_utxo: Some(Transaction::consensus_decode(&mut &hex::decode("020000000001011eb5a3e65946f88b00d67b321e5fd980b32a2316fb1fc9b712baa6a1033a04e30100000017160014f0f81ee77d552b4c81497451d1abf5c22ce8e352feffffff02b55dd900000000001976a9142c3cf5686f47c1de9cc90b4255cc2a1ef8c01b3188acfb0391ae6800000017a914a3a79e37ad366d9bf9471b28a9a8f64b50de0c968702483045022100c0aa7b262967fc2803c8a9f38f26682edba7cafb7d4870ebdc116040ad5338b502205dfebd08e993af2e6aa3118a438ad70ed9f6e09bc6abfd21f8f2957af936bc070121031f4e69fcf110bb31f019321834c0948b5487f2782489f370f66dc20f7ac767ca8bf81500").unwrap()[..]).unwrap()),
..Default::default()
}],
outputs: vec![
psbt::Output {
..Default::default()
},
],
proprietary: Default::default(),
unknown: Default::default(),
version: 0,
xpub: Default::default(),
};
println!("psbt before: {:?}", psbt);
println!("unsigned txid: {}", psbt.unsigned_tx.txid());
println!(
"unsigned tx: {}",
hex::encode(bitcoin::consensus::encode::serialize(&psbt.unsigned_tx))
);
let mut raw_tx = Vec::new();
let progress = handle_interaction(trezor.sign_tx(&psbt, Network::Testnet).unwrap());
tx_progress(&mut psbt, progress, &mut raw_tx).unwrap();
println!("signed tx: {}", hex::encode(raw_tx));
}