pallet_bitcoin/
types.rs

1use bitcoin::locktime::absolute;
2use codec::{Decode, DecodeWithMemTracking, Encode};
3use scale_info::TypeInfo;
4use sp_std::vec::Vec;
5pub use subcoin_runtime_primitives::{OutPoint, Txid};
6
7/// An absolute lock time value, representing either a block height or a UNIX timestamp (seconds
8/// since epoch).
9#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq, DecodeWithMemTracking)]
10pub enum LockTime {
11    /// A block height lock time value.
12    Height(u32),
13    /// A UNIX timestamp lock time value.
14    ///
15    /// A UNIX timestamp, seconds since epoch, guaranteed to always contain a valid time value.
16    Time(u32),
17}
18
19impl From<absolute::LockTime> for LockTime {
20    fn from(lock_time: absolute::LockTime) -> Self {
21        match lock_time {
22            absolute::LockTime::Blocks(n) => Self::Height(n.to_consensus_u32()),
23            absolute::LockTime::Seconds(n) => Self::Time(n.to_consensus_u32()),
24        }
25    }
26}
27
28impl From<LockTime> for absolute::LockTime {
29    fn from(val: LockTime) -> Self {
30        match val {
31            LockTime::Height(n) => Self::Blocks(
32                absolute::Height::from_consensus(n).expect("Invalid height in LockTime"),
33            ),
34            LockTime::Time(n) => {
35                Self::Seconds(absolute::Time::from_consensus(n).expect("Invalid time in LockTime"))
36            }
37        }
38    }
39}
40
41/// The Witness is the data used to unlock bitcoin since the [segwit upgrade].
42///
43/// Can be logically seen as an array of bytestrings, i.e. `Vec<Vec<u8>>`, and it is serialized on the wire
44/// in that format.
45///
46/// [segwit upgrade]: <https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki>
47#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq)]
48pub struct Witness {
49    /// Contains the witness `Vec<Vec<u8>>` serialization without the initial varint indicating the
50    /// number of elements (which is stored in `witness_elements`).
51    content: Vec<u8>,
52    /// The number of elements in the witness.
53    ///
54    /// Stored separately (instead of as a VarInt in the initial part of content) so that methods
55    /// like [`Witness::push`] don't have to shift the entire array.
56    // usize
57    witness_elements: u64,
58    /// This is the valid index pointing to the beginning of the index area. This area is 4 *
59    /// stack_size bytes at the end of the content vector which stores the indices of each item.
60    indices_start: u64,
61}
62
63/// Regular bitcoin transaction input.
64///
65/// Structurely same with [`bitcoin::TxIn`].
66#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq, DecodeWithMemTracking)]
67pub struct RegularTxIn {
68    /// The reference to the previous output that is being used as an input.
69    pub previous_output: OutPoint,
70    /// The unlocking script (scriptSig) that pushes values onto the stack,
71    /// enabling the referenced output's script to be satisfied.
72    pub unlocking_script: Vec<u8>,
73    /// The sequence number, which suggests to miners which of two
74    /// conflicting transactions should be preferred, or 0xFFFFFFFF
75    /// to ignore this feature. This is generally never used since
76    /// the miner behavior cannot be enforced.
77    pub sequence: u32,
78    /// Witness data: an array of byte-arrays.
79    /// Note that this field is *not* (de)serialized with the rest of the TxIn in
80    /// Encodable/Decodable, as it is (de)serialized at the end of the full
81    /// Transaction. It *is* (de)serialized with the rest of the TxIn in other
82    /// (de)serialization routines.
83    pub witness: Vec<Vec<u8>>,
84}
85
86/// Bitcoin transaction input.
87///
88/// This type is a wrapper around [`bitcoin::TxIn`], designed to provide a user-friendly representation
89/// of transaction inputs (`TxIn`) in polkadot.js.org. It handles both coinbase (block reward)
90/// transactions and standard transactions.
91#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq, DecodeWithMemTracking)]
92pub enum TxIn {
93    /// Input from a coinbase transaction, which does not reference any previous output.
94    Coinbase {
95        /// Arbitrary data used in the coinbase transaction, such as extra nonce or miner-specific information.
96        data: Vec<u8>,
97        /// Sequence.
98        ///
99        /// Note that the value of sequence is not always Sequence::MAX, see https://www.blockchain.com/explorer/transactions/btc/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429
100        sequence: u32,
101    },
102    /// Input from a regular transaction, which references a previous output (`OutPoint`).
103    Regular(RegularTxIn),
104}
105
106impl From<bitcoin::TxIn> for TxIn {
107    fn from(txin: bitcoin::TxIn) -> Self {
108        if txin.previous_output.is_null() {
109            Self::Coinbase {
110                data: txin.script_sig.into_bytes(),
111                sequence: txin.sequence.0,
112            }
113        } else {
114            Self::Regular(RegularTxIn {
115                previous_output: txin.previous_output.into(),
116                unlocking_script: txin.script_sig.into_bytes(),
117                sequence: txin.sequence.0,
118                witness: txin.witness.to_vec(),
119            })
120        }
121    }
122}
123
124impl From<TxIn> for bitcoin::TxIn {
125    fn from(val: TxIn) -> Self {
126        match val {
127            TxIn::Coinbase { data, sequence } => bitcoin::TxIn {
128                previous_output: bitcoin::OutPoint::null(),
129                script_sig: bitcoin::ScriptBuf::from_bytes(data),
130                sequence: bitcoin::Sequence(sequence),
131                witness: bitcoin::Witness::new(),
132            },
133            TxIn::Regular(RegularTxIn {
134                previous_output,
135                unlocking_script,
136                sequence,
137                witness,
138            }) => bitcoin::TxIn {
139                previous_output: previous_output.into(),
140                script_sig: bitcoin::ScriptBuf::from_bytes(unlocking_script),
141                sequence: bitcoin::Sequence(sequence),
142                witness: witness.into(),
143            },
144        }
145    }
146}
147
148/// Bitcoin transaction output.
149#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq, DecodeWithMemTracking)]
150pub struct TxOut {
151    /// The value of the output, in satoshis.
152    pub value: u64,
153    /// The script which must be satisfied for the output to be spent.
154    pub script_pubkey: Vec<u8>,
155}
156
157/// Bitcoin transaction.
158#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq, DecodeWithMemTracking)]
159pub struct Transaction {
160    /// The protocol version, is currently expected to be 1 or 2 (BIP 68).
161    pub version: i32,
162    /// Block height or timestamp. Transaction cannot be included in a block until this height/time.
163    pub lock_time: LockTime,
164    /// List of transaction inputs.
165    pub input: Vec<TxIn>,
166    /// List of transaction outputs.
167    pub output: Vec<TxOut>,
168}
169
170impl From<Transaction> for bitcoin::Transaction {
171    fn from(val: Transaction) -> Self {
172        let Transaction {
173            version,
174            lock_time,
175            input,
176            output,
177        } = val;
178
179        bitcoin::Transaction {
180            version: bitcoin::transaction::Version(version),
181            lock_time: lock_time.into(),
182            input: input.into_iter().map(Into::into).collect(),
183            output: output
184                .into_iter()
185                .map(|txout| bitcoin::TxOut {
186                    value: bitcoin::Amount::from_sat(txout.value),
187                    script_pubkey: bitcoin::ScriptBuf::from_bytes(txout.script_pubkey),
188                })
189                .collect(),
190        }
191    }
192}
193
194impl From<bitcoin::Transaction> for Transaction {
195    fn from(btc_tx: bitcoin::Transaction) -> Self {
196        Self {
197            version: btc_tx.version.0,
198            lock_time: btc_tx.lock_time.into(),
199            input: btc_tx.input.into_iter().map(Into::into).collect(),
200            output: btc_tx
201                .output
202                .into_iter()
203                .map(|txout| TxOut {
204                    value: txout.value.to_sat(),
205                    script_pubkey: txout.script_pubkey.into_bytes(),
206                })
207                .collect(),
208        }
209    }
210}