Skip to content

Commit e0880cf

Browse files
benma's agentbenma
authored andcommitted
examples: add OP_RETURN example
1 parent dff44c2 commit e0880cf

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ required-features = ["usb", "tokio/rt", "tokio/macros"]
7575
name = "btc_sign_psbt"
7676
required-features = ["usb", "tokio/rt", "tokio/macros"]
7777

78+
[[example]]
79+
name = "btc_sign_op_return"
80+
required-features = ["usb", "tokio/rt", "tokio/macros"]
81+
7882
[[example]]
7983
name = "btc_sign_msg"
8084
required-features = ["usb", "tokio/rt", "tokio/macros"]

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ example-btc-signtx:
1010
cargo run --example btc_signtx --features=usb,tokio/rt,tokio/macros
1111
example-btc-psbt:
1212
cargo run --example btc_sign_psbt --features=usb,tokio/rt,tokio/macros
13+
example-btc-sign-op-return:
14+
cargo run --example btc_sign_op_return --features=usb,tokio/rt,tokio/macros
1315
example-btc-sign-msg:
1416
cargo run --example btc_sign_msg --features=usb,tokio/rt,tokio/macros
1517
example-btc-miniscript:

examples/btc_sign_op_return.rs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
use std::str::FromStr;
2+
3+
use bitbox_api::{pb, Keypath};
4+
5+
use bitcoin::{
6+
bip32::{ChildNumber, DerivationPath, Fingerprint, Xpub},
7+
blockdata::script::Builder,
8+
opcodes::all,
9+
psbt::Psbt,
10+
secp256k1, transaction, Amount, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut,
11+
Witness,
12+
};
13+
use semver::VersionReq;
14+
15+
async fn connect_bitbox() -> bitbox_api::PairedBitBox<bitbox_api::runtime::TokioRuntime> {
16+
let noise_config = Box::new(bitbox_api::NoiseConfigNoCache {});
17+
let device = bitbox_api::BitBox::<bitbox_api::runtime::TokioRuntime>::from_hid_device(
18+
bitbox_api::usb::get_any_bitbox02().unwrap(),
19+
noise_config,
20+
)
21+
.await
22+
.unwrap();
23+
let pairing = device.unlock_and_pair().await.unwrap();
24+
if let Some(pairing_code) = pairing.get_pairing_code().as_ref() {
25+
println!("Pairing code\n{pairing_code}");
26+
}
27+
pairing.wait_confirm().await.unwrap()
28+
}
29+
30+
async fn build_op_return_psbt(
31+
paired: &bitbox_api::PairedBitBox<bitbox_api::runtime::TokioRuntime>,
32+
) -> Psbt {
33+
let coin = pb::BtcCoin::Tbtc;
34+
let secp = secp256k1::Secp256k1::new();
35+
36+
let fingerprint_hex = paired.root_fingerprint().await.unwrap();
37+
let fingerprint = Fingerprint::from_str(&fingerprint_hex).unwrap();
38+
let account_path: DerivationPath = "m/84'/1'/0'".parse().unwrap();
39+
let input_path: DerivationPath = "m/84'/1'/0'/0/5".parse().unwrap();
40+
let change_path: DerivationPath = "m/84'/1'/0'/1/0".parse().unwrap();
41+
42+
let account_keypath = Keypath::from(&account_path);
43+
let account_xpub = paired
44+
.btc_xpub(
45+
coin,
46+
&account_keypath,
47+
pb::btc_pub_request::XPubType::Tpub,
48+
false,
49+
)
50+
.await
51+
.unwrap();
52+
53+
let account_xpub = Xpub::from_str(&account_xpub).unwrap();
54+
55+
let input_pub = account_xpub
56+
.derive_pub(
57+
&secp,
58+
&[
59+
ChildNumber::from_normal_idx(0).unwrap(),
60+
ChildNumber::from_normal_idx(5).unwrap(),
61+
],
62+
)
63+
.unwrap()
64+
.to_pub();
65+
let change_pub = account_xpub
66+
.derive_pub(
67+
&secp,
68+
&[
69+
ChildNumber::from_normal_idx(1).unwrap(),
70+
ChildNumber::from_normal_idx(0).unwrap(),
71+
],
72+
)
73+
.unwrap()
74+
.to_pub();
75+
76+
let prev_tx = Transaction {
77+
version: transaction::Version::TWO,
78+
lock_time: bitcoin::absolute::LockTime::ZERO,
79+
input: vec![TxIn {
80+
previous_output: "3131313131313131313131313131313131313131313131313131313131313131:0"
81+
.parse()
82+
.unwrap(),
83+
script_sig: ScriptBuf::new(),
84+
sequence: Sequence(0xFFFFFFFF),
85+
witness: Witness::default(),
86+
}],
87+
output: vec![TxOut {
88+
value: Amount::from_sat(50_000_000),
89+
script_pubkey: ScriptBuf::new_p2wpkh(&input_pub.wpubkey_hash()),
90+
}],
91+
};
92+
93+
let op_return_data = b"hello world";
94+
let op_return_script = Builder::new()
95+
.push_opcode(all::OP_RETURN)
96+
.push_slice(op_return_data)
97+
.into_script();
98+
99+
let tx = Transaction {
100+
version: transaction::Version::TWO,
101+
lock_time: bitcoin::absolute::LockTime::ZERO,
102+
input: vec![TxIn {
103+
previous_output: OutPoint {
104+
txid: prev_tx.compute_txid(),
105+
vout: 0,
106+
},
107+
script_sig: ScriptBuf::new(),
108+
sequence: Sequence(0xFFFFFFFF),
109+
witness: Witness::default(),
110+
}],
111+
output: vec![
112+
TxOut {
113+
value: Amount::from_sat(49_000_000),
114+
script_pubkey: ScriptBuf::new_p2wpkh(&change_pub.wpubkey_hash()),
115+
},
116+
TxOut {
117+
value: Amount::from_sat(0),
118+
script_pubkey: op_return_script,
119+
},
120+
],
121+
};
122+
123+
let mut psbt = Psbt::from_unsigned_tx(tx).unwrap();
124+
psbt.inputs[0].non_witness_utxo = Some(prev_tx.clone());
125+
psbt.inputs[0].witness_utxo = Some(prev_tx.output[0].clone());
126+
psbt.inputs[0]
127+
.bip32_derivation
128+
.insert(input_pub.0, (fingerprint, input_path.clone()));
129+
130+
psbt.outputs[0]
131+
.bip32_derivation
132+
.insert(change_pub.0, (fingerprint, change_path.clone()));
133+
134+
psbt
135+
}
136+
137+
#[tokio::main(flavor = "current_thread")]
138+
async fn main() {
139+
let paired = connect_bitbox().await;
140+
141+
let firmware_version = paired.version();
142+
if !VersionReq::parse(">=9.24.0")
143+
.unwrap()
144+
.matches(firmware_version)
145+
{
146+
eprintln!(
147+
"Connected firmware {firmware_version} does not support OP_RETURN outputs (requires >=9.24.0)."
148+
);
149+
return;
150+
}
151+
152+
let mut psbt = build_op_return_psbt(&paired).await;
153+
154+
paired
155+
.btc_sign_psbt(
156+
pb::BtcCoin::Tbtc,
157+
&mut psbt,
158+
None,
159+
pb::btc_sign_init_request::FormatUnit::Default,
160+
)
161+
.await
162+
.unwrap();
163+
}

0 commit comments

Comments
 (0)