btc_transaction_utils/lib.rs
1// Copyright 2018 The Exonum Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! BTC transaction utils is a small library that will help to create multisig addresses
16//! and sign a some types of segwit transactions.
17//!
18//! By using this library you can make the following things:
19//!
20//! - [Create][redeem-script] a redeem script and a corresponding multisig address (3 of 4).
21//! - [Sign][p2wpk] the `P2WPK` inputs.
22//! - [Sign][p2wsh] the `P2WSH` inputs.
23//!
24//! # Examples
25//!
26//! ## Create a redeem script and a corresponding multisig address (3 of 4).
27//!
28//! ```
29//! use bitcoin::network::constants::Network;
30//! use btc_transaction_utils::multisig::RedeemScriptBuilder;
31//! use btc_transaction_utils::test_data::secp_gen_keypair;
32//! use btc_transaction_utils::p2wsh;
33//!
34//! // Generate four key pairs.
35//! let keypairs = (0..4)
36//! .map(|_| secp_gen_keypair(Network::Testnet))
37//! .collect::<Vec<_>>();
38//! // Create a corresponding redeem script.
39//! let public_keys = keypairs.iter().map(|keypair| keypair.0);
40//! let script = RedeemScriptBuilder::with_public_keys(public_keys)
41//! .quorum(3)
42//! .to_script()
43//! .unwrap();
44//! // Create a corresponding testnet address for the given redeem script.
45//! let address = p2wsh::address(&script, Network::Testnet);
46//! println!("{}", address.to_string());
47//! ```
48//!
49//! ## Sign P2WPK input
50//!
51//! ```
52//! use bitcoin::{
53//! blockdata::opcodes::all::OP_RETURN,
54//! blockdata::script::{Builder, Script},
55//! blockdata::transaction::{OutPoint, Transaction, TxIn, TxOut},
56//! network::constants::Network
57//! };
58//! use btc_transaction_utils::{
59//! p2wpk,
60//! test_data::{secp_gen_keypair_with_rng, btc_tx_from_hex},
61//! TxInRef
62//! };
63//! use rand::prelude::*;
64//!
65//! // Take a transaction with the unspent P2WPK output.
66//! let prev_tx = btc_tx_from_hex(
67//! "02000000000101beccab33bc72bfc81b63fdec8a4a9a4719e4418bdb7b20e47b0\
68//! 2074dc42f2d800000000017160014f3b1b3819c1290cd5d675c1319dc7d9d98d5\
69//! 71bcfeffffff02dceffa0200000000160014368c6b7c38f0ff0839bf78d77544d\
70//! a96cb685bf28096980000000000160014284175e336fa10865fb4d1351c9e18e7\
71//! 30f5d6f90247304402207c893c85d75e2230dde04f5a1e2c83c4f0b7d93213372\
72//! 746eb2227b068260d840220705484b6ec70a8fc0d1f80c3a98079602595351b7a\
73//! 9bca7caddb9a6adb0a3440012103150514f05f3e3f40c7b404b16f8a09c2c71ba\
74//! d3ba8da5dd1e411a7069cc080a004b91300",
75//! );
76//! // Take the corresponding key pair.
77//! let mut rng = thread_rng();
78//! let keypair = secp_gen_keypair_with_rng(&mut rng, Network::Testnet);
79//! // Create an unsigned transaction
80//! let mut transaction = Transaction {
81//! version: 2,
82//! lock_time: 0,
83//! input: vec![
84//! TxIn {
85//! previous_output: OutPoint {
86//! txid: prev_tx.txid(),
87//! vout: 1,
88//! },
89//! script_sig: Script::default(),
90//! sequence: 0xFFFFFFFF,
91//! witness: Vec::default(),
92//! },
93//! ],
94//! output: vec![
95//! TxOut {
96//! value: 0,
97//! script_pubkey: Builder::new()
98//! .push_opcode(OP_RETURN)
99//! .push_slice(b"Hello Exonum!")
100//! .into_script(),
101//! },
102//! ],
103//! };
104//! // Create a signature for the given input.
105//! let mut signer = p2wpk::InputSigner::new(keypair.0, Network::Testnet);
106//! let signature = signer
107//! .sign_input(TxInRef::new(&transaction, 0), &prev_tx, &keypair.1.key)
108//! .unwrap();
109//! // Finalize the transaction.
110//! signer.spend_input(&mut transaction.input[0], signature);
111//! ```
112//!
113//! ## Sign P2WSH input
114//!
115//! ```
116//! use bitcoin::{
117//! blockdata::opcodes::all::OP_RETURN,
118//! blockdata::script::{Builder, Script},
119//! blockdata::transaction::{OutPoint, Transaction, TxIn, TxOut},
120//! network::constants::Network
121//! };
122//! use btc_transaction_utils::{
123//! multisig::RedeemScriptBuilder,
124//! p2wsh,
125//! test_data::{secp_gen_keypair_with_rng, btc_tx_from_hex},
126//! TxInRef
127//! };
128//! use rand::prelude::*;
129//!
130//! // Take a transaction with the unspent P2WSH output.
131//! let prev_tx = btc_tx_from_hex(
132//! "02000000000101f8c16000cc59f9505046303944d42a6c264a322f80b46bb4361\
133//! 15b6e306ba9950000000000feffffff02f07dc81600000000160014f65eb9d72a\
134//! 8475dd8e26f4fa748796e211aa8869102700000000000022002001fb25c3db04c\
135//! a5580da43a7d38dd994650d9aa6d6ee075b4578388deed338ed0247304402206b\
136//! 5f211cd7f9b89e80c734b61113c33f437ba153e7ba6bc275eed857e54fcb26022\
137//! 0038562e88b805f0cdfd4873ab3579d52268babe6af9c49086c00343187cdf28a\
138//! 012103979dff5cd9045f4b6fa454d2bc5357586a85d4789123df45f83522963d9\
139//! 4e3217fb91300",
140//! );
141//! // Take the corresponding key pairs and the redeem script.
142//! let total_count = 18;
143//! let quorum = 12;
144//! let mut rng = thread_rng();
145//! let keypairs = (0..total_count)
146//! .into_iter()
147//! .map(|_| secp_gen_keypair_with_rng(&mut rng, Network::Testnet))
148//! .collect::<Vec<_>>();
149//! let public_keys = keypairs.iter().map(|keypair| keypair.0);
150//! let redeem_script = RedeemScriptBuilder::with_public_keys(public_keys)
151//! .quorum(quorum)
152//! .to_script()
153//! .unwrap();
154//! // Create an unsigned transaction.
155//! let mut transaction = Transaction {
156//! version: 2,
157//! lock_time: 0,
158//! input: vec![
159//! TxIn {
160//! previous_output: OutPoint {
161//! txid: prev_tx.txid(),
162//! vout: 1,
163//! },
164//! script_sig: Script::default(),
165//! sequence: 0xFFFFFFFF,
166//! witness: Vec::default(),
167//! },
168//! ],
169//! output: vec![
170//! TxOut {
171//! value: 0,
172//! script_pubkey: Builder::new()
173//! .push_opcode(OP_RETURN)
174//! .push_slice(b"Hello Exonum with multisig!")
175//! .into_script(),
176//! },
177//! ],
178//! };
179//! // Create signatures for the given input.
180//! let mut signer = p2wsh::InputSigner::new(redeem_script.clone());
181//! let signatures = keypairs[0..quorum]
182//! .iter()
183//! .map(|keypair| {
184//! let txin = TxInRef::new(&transaction, 0);
185//! signer.sign_input(txin, &prev_tx, &keypair.1.key).unwrap()
186//! })
187//! .collect::<Vec<_>>();
188//! // Finalize the transaction.
189//! signer.spend_input(&mut transaction.input[0], signatures);
190//! ```
191//!
192//! [redeem-script]: #create-a-redeem-script-and-a-corresponding-multisig-address-3-of-4
193//! [p2wpk]: #sign-p2wpk-input
194//! [p2wsh]: #sign-p2wsh-input
195
196#![deny(
197 missing_debug_implementations,
198 missing_docs,
199 unsafe_code,
200 bare_trait_objects
201)]
202
203#[macro_use]
204mod macros;
205mod sign;
206
207use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut};
208
209pub mod multisig;
210pub mod p2wpk;
211pub mod p2wsh;
212pub mod test_data;
213
214pub(crate) use bitcoin_hashes::{hash160::Hash as Hash160, sha256d::Hash as Sha256dHash, Hash};
215pub use sign::{InputSignature, InputSignatureRef};
216
217/// A borrowed reference to a transaction input.
218#[derive(Debug, Copy, Clone)]
219pub struct TxInRef<'a> {
220 transaction: &'a Transaction,
221 index: usize,
222}
223
224impl<'a> TxInRef<'a> {
225 /// Constructs a reference to the input with the given index of the given transaction.
226 pub fn new(transaction: &'a Transaction, index: usize) -> TxInRef<'a> {
227 assert!(transaction.input.len() > index);
228 TxInRef { transaction, index }
229 }
230
231 /// Returns a reference to the borrowed transaction.
232 pub fn transaction(&self) -> &Transaction {
233 self.transaction
234 }
235
236 /// Returns a reference to the input.
237 pub fn input(&self) -> &TxIn {
238 &self.transaction.input[self.index]
239 }
240
241 /// Returns the index of input.
242 pub fn index(&self) -> usize {
243 self.index
244 }
245}
246
247impl<'a> AsRef<TxIn> for TxInRef<'a> {
248 fn as_ref(&self) -> &TxIn {
249 self.input()
250 }
251}
252
253/// An auxiliary enumeration that helps to get the balance of the previous
254/// unspent transaction output.
255#[derive(Debug, Copy, Clone)]
256pub enum UnspentTxOutValue<'a> {
257 /// The output balance.
258 Balance(u64),
259 /// A reference to the transaction with the required unspent output.
260 PrevTx(&'a Transaction),
261 /// A reference to the transaction output to be spent.
262 PrevOut(&'a TxOut),
263}
264
265impl<'a> UnspentTxOutValue<'a> {
266 /// Returns the output balance value.
267 pub fn balance(self, txin: TxInRef) -> u64 {
268 match self {
269 UnspentTxOutValue::Balance(value) => value,
270 UnspentTxOutValue::PrevTx(prev_tx) => {
271 prev_tx.output[txin.input().previous_output.vout as usize].value
272 }
273 UnspentTxOutValue::PrevOut(out) => out.value,
274 }
275 }
276}
277
278impl<'a> From<u64> for UnspentTxOutValue<'a> {
279 fn from(balance: u64) -> UnspentTxOutValue<'a> {
280 UnspentTxOutValue::Balance(balance)
281 }
282}
283
284impl<'a> From<&'a Transaction> for UnspentTxOutValue<'a> {
285 fn from(tx_ref: &'a Transaction) -> UnspentTxOutValue {
286 UnspentTxOutValue::PrevTx(tx_ref)
287 }
288}
289
290impl<'a> From<&'a TxOut> for UnspentTxOutValue<'a> {
291 fn from(tx_out: &'a TxOut) -> UnspentTxOutValue<'a> {
292 UnspentTxOutValue::PrevOut(tx_out)
293 }
294}