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}