Skip to content

Commit a011430

Browse files
committed
Add psbt example for sign and finalize
Same example as posted in discussions
1 parent 8f86992 commit a011430

File tree

3 files changed

+207
-0
lines changed

3 files changed

+207
-0
lines changed

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,7 @@ required-features = ["std"]
6060
[[example]]
6161
name = "taproot"
6262
required-features = ["compiler","std"]
63+
64+
[[example]]
65+
name = "psbt_sign_finalize"
66+
required-features = ["std"]

contrib/test.sh

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ then
6868
cargo run --example psbt
6969
cargo run --example xpub_descriptors
7070
cargo run --example taproot --features=compiler
71+
cargo run --example psbt_sign_finalize
7172
fi
7273

7374
if [ "$DO_NO_STD" = true ]

examples/psbt_sign_finalize.rs

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
use std::collections::BTreeMap;
2+
use std::fmt;
3+
use std::str::FromStr;
4+
5+
use bitcoin::consensus::serialize;
6+
use bitcoin::util::sighash::SighashCache;
7+
use bitcoin::{PackedLockTime, PrivateKey};
8+
use bitcoind::bitcoincore_rpc::jsonrpc::base64;
9+
use bitcoind::bitcoincore_rpc::RawTx;
10+
use miniscript::bitcoin::consensus::encode::deserialize;
11+
use miniscript::bitcoin::hashes::hex::FromHex;
12+
use miniscript::bitcoin::util::psbt;
13+
use miniscript::bitcoin::util::psbt::PartiallySignedTransaction as Psbt;
14+
use miniscript::bitcoin::{
15+
self, secp256k1, Address, Network, OutPoint, Script, Sequence, Transaction, TxIn, TxOut,
16+
};
17+
use miniscript::psbt::{PsbtExt, PsbtInputExt};
18+
use miniscript::Descriptor;
19+
20+
// note use of re-exported dependencies
21+
22+
#[derive(Debug, PartialEq)]
23+
pub enum DescError {
24+
/// PSBT was not able to finalize
25+
PsbtFinalizeError,
26+
/// Problem with address computation
27+
AddressComputationError,
28+
/// Error while parsing the descriptor
29+
DescParseError,
30+
}
31+
32+
impl fmt::Display for DescError {
33+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34+
match *self {
35+
DescError::PsbtFinalizeError => f.write_str("PSBT was not able to finalize"),
36+
DescError::AddressComputationError => f.write_str("Problem with address computation"),
37+
DescError::DescParseError => f.write_str("Not able to parse the descriptor"),
38+
}
39+
}
40+
}
41+
42+
fn main() {
43+
let secp256k1 = secp256k1::Secp256k1::new();
44+
45+
let s = "wsh(t:or_c(pk(027a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de),v:thresh(1,pkh(032d672a1a91cc39d154d366cd231983661b0785c7f27bc338447565844f4a6813),a:pkh(03417129311ed34c242c012cd0a3e0b9bca0065f742d0dfb63c78083ea6a02d4d9),a:pkh(025a687659658baeabdfc415164528065be7bcaade19342241941e556557f01e28))))#7hut9ukn";
46+
let bridge_descriptor = Descriptor::from_str(&s).unwrap();
47+
//let bridge_descriptor = Descriptor::<bitcoin::PublicKey>::from_str(&s).expect("parse descriptor string");
48+
assert!(bridge_descriptor.sanity_check().is_ok());
49+
println!(
50+
"Bridge pubkey script: {}",
51+
bridge_descriptor.script_pubkey()
52+
);
53+
println!(
54+
"Bridge address: {}",
55+
bridge_descriptor.address(Network::Regtest).unwrap()
56+
);
57+
println!(
58+
"Weight for witness satisfaction cost {}",
59+
bridge_descriptor.max_satisfaction_weight().unwrap()
60+
);
61+
62+
let master_private_key_str = "cQhdvB3McbBJdx78VSSumqoHQiSXs75qwLptqwxSQBNBMDxafvaw";
63+
let _master_private_key =
64+
PrivateKey::from_str(master_private_key_str).expect("Can't create private key");
65+
println!(
66+
"Master public key: {}",
67+
_master_private_key.public_key(&secp256k1)
68+
);
69+
70+
let backup1_private_key_str = "cWA34TkfWyHa3d4Vb2jNQvsWJGAHdCTNH73Rht7kAz6vQJcassky";
71+
let backup1_private =
72+
PrivateKey::from_str(backup1_private_key_str).expect("Can't create private key");
73+
74+
println!(
75+
"Backup1 public key: {}",
76+
backup1_private.public_key(&secp256k1)
77+
);
78+
79+
let backup2_private_key_str = "cPJFWUKk8sdL7pcDKrmNiWUyqgovimmhaaZ8WwsByDaJ45qLREkh";
80+
let backup2_private =
81+
PrivateKey::from_str(backup2_private_key_str).expect("Can't create private key");
82+
83+
println!(
84+
"Backup2 public key: {}",
85+
backup2_private.public_key(&secp256k1)
86+
);
87+
88+
let backup3_private_key_str = "cT5cH9UVm81W5QAf5KABXb23RKNSMbMzMx85y6R2mF42L94YwKX6";
89+
let _backup3_private =
90+
PrivateKey::from_str(backup3_private_key_str).expect("Can't create private key");
91+
92+
println!(
93+
"Backup3 public key: {}",
94+
_backup3_private.public_key(&secp256k1)
95+
);
96+
97+
let spend_tx = Transaction {
98+
version: 2,
99+
lock_time: PackedLockTime(5000),
100+
input: vec![],
101+
output: vec![],
102+
};
103+
104+
// Spend one input and spend one output for simplicity.
105+
let mut psbt = Psbt {
106+
unsigned_tx: spend_tx,
107+
unknown: BTreeMap::new(),
108+
proprietary: BTreeMap::new(),
109+
xpub: BTreeMap::new(),
110+
version: 0,
111+
inputs: vec![],
112+
outputs: vec![],
113+
};
114+
115+
let hex_tx = "020000000001018ff27041f3d738f5f84fd5ee62f1c5b36afebfb15f6da0c9d1382ddd0eaaa23c0000000000feffffff02b3884703010000001600142ca3b4e53f17991582d47b15a053b3201891df5200e1f50500000000220020c0ebf552acd2a6f5dee4e067daaef17b3521e283aeaa44a475278617e3d2238a0247304402207b820860a9d425833f729775880b0ed59dd12b64b9a3d1ab677e27e4d6b370700220576003163f8420fe0b9dc8df726cff22cbc191104a2d4ae4f9dfedb087fcec72012103817e1da42a7701df4db94db8576f0e3605f3ab3701608b7e56f92321e4d8999100000000";
116+
let depo_tx: Transaction = deserialize(&Vec::<u8>::from_hex(hex_tx).unwrap()).unwrap();
117+
118+
let receiver = Address::from_str("bcrt1qsdks5za4t6sevaph6tz9ddfjzvhkdkxe9tfrcy").unwrap();
119+
120+
let amount = 100000000;
121+
122+
let (outpoint, witness_utxo) = get_vout(&depo_tx, bridge_descriptor.script_pubkey());
123+
124+
let mut txin = TxIn::default();
125+
txin.previous_output = outpoint;
126+
127+
txin.sequence = Sequence::from_height(26); //Sequence::MAX; //
128+
psbt.unsigned_tx.input.push(txin);
129+
130+
psbt.unsigned_tx.output.push(TxOut {
131+
script_pubkey: receiver.script_pubkey(),
132+
value: amount / 5 - 500,
133+
});
134+
135+
psbt.unsigned_tx.output.push(TxOut {
136+
script_pubkey: bridge_descriptor.script_pubkey(),
137+
value: amount * 4 / 5,
138+
});
139+
140+
// Generating signatures & witness data
141+
142+
let mut input = psbt::Input::default();
143+
input
144+
.update_with_descriptor_unchecked(&bridge_descriptor)
145+
.unwrap();
146+
147+
input.witness_utxo = Some(witness_utxo.clone());
148+
psbt.inputs.push(input);
149+
psbt.outputs.push(psbt::Output::default());
150+
151+
let mut sighash_cache = SighashCache::new(&psbt.unsigned_tx);
152+
153+
let msg = psbt
154+
.sighash_msg(0, &mut sighash_cache, None)
155+
.unwrap()
156+
.to_secp_msg();
157+
158+
// Fixme: Take a parameter
159+
let hash_ty = bitcoin::EcdsaSighashType::All;
160+
161+
let sk1 = backup1_private.inner;
162+
let sk2 = backup2_private.inner;
163+
164+
// Finally construct the signature and add to psbt
165+
let sig1 = secp256k1.sign_ecdsa(&msg, &sk1);
166+
let pk1 = backup1_private.public_key(&secp256k1);
167+
assert!(secp256k1.verify_ecdsa(&msg, &sig1, &pk1.inner).is_ok());
168+
169+
// Second key just in case
170+
let sig2 = secp256k1.sign_ecdsa(&msg, &sk2);
171+
let pk2 = backup2_private.public_key(&secp256k1);
172+
assert!(secp256k1.verify_ecdsa(&msg, &sig2, &pk2.inner).is_ok());
173+
174+
psbt.inputs[0].partial_sigs.insert(
175+
pk1,
176+
bitcoin::EcdsaSig {
177+
sig: sig1,
178+
hash_ty: hash_ty,
179+
},
180+
);
181+
182+
println!("{:#?}", psbt);
183+
184+
let serialized = serialize(&psbt);
185+
println!("{}", base64::encode(&serialized));
186+
187+
psbt.finalize_mut(&secp256k1).unwrap();
188+
println!("{:#?}", psbt);
189+
190+
let tx = psbt.extract_tx();
191+
println!("{}", tx.raw_hex());
192+
}
193+
194+
// Find the Outpoint by spk
195+
fn get_vout(tx: &Transaction, spk: Script) -> (OutPoint, TxOut) {
196+
for (i, txout) in tx.clone().output.into_iter().enumerate() {
197+
if spk == txout.script_pubkey {
198+
return (OutPoint::new(tx.txid(), i as u32), txout);
199+
}
200+
}
201+
panic!("Only call get vout on functions which have the expected outpoint");
202+
}

0 commit comments

Comments
 (0)