1use core::slice;
23use std::fmt::{self, Debug, Display, Formatter, LowerHex};
24use std::iter::Sum;
25use std::num::ParseIntError;
26use std::ops::{Div, Rem};
27use std::str::FromStr;
28
29use amplify::hex::{self, FromHex, ToHex};
30use amplify::{ByteArray, Bytes32StrRev, Wrapper};
31use commit_verify::{DigestExt, Sha256};
32
33use crate::{
34 ConsensusDecode, ConsensusDecodeError, ConsensusEncode, LockTime, NonStandardValue,
35 ScriptPubkey, SeqNo, SigScript, VarIntArray, Witness, Wtxid, LIB_NAME_BITCOIN,
36};
37
38#[derive(Wrapper, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From)]
39#[wrapper(AsSlice)]
40#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
41#[strict_type(lib = LIB_NAME_BITCOIN)]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
43#[wrapper(BorrowSlice, Index, RangeOps, Debug, Hex, Display, FromStr)]
44pub struct Txid(
46 #[from]
47 #[from([u8; 32])]
48 Bytes32StrRev,
49);
50
51impl From<Txid> for [u8; 32] {
52 fn from(txid: Txid) -> Self { txid.to_byte_array() }
53}
54
55impl Txid {
56 #[inline]
57 pub const fn coinbase() -> Self { Self(Bytes32StrRev::zero()) }
58 #[inline]
59 pub fn is_coinbase(&self) -> bool { self.to_byte_array() == [0u8; 32] }
60}
61
62#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display, From)]
63#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
64#[strict_type(lib = LIB_NAME_BITCOIN)]
65#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
66#[display(inner)]
67pub struct Vout(u32);
69
70impl Vout {
71 pub const fn from_u32(u: u32) -> Self { Vout(u) }
72 #[inline]
73 pub const fn into_u32(self) -> u32 { self.0 }
74 #[inline]
75 pub const fn into_usize(self) -> usize { self.0 as usize }
76 #[inline]
77 pub const fn to_u32(&self) -> u32 { self.0 }
78 #[inline]
79 pub const fn to_usize(&self) -> usize { self.0 as usize }
80}
81
82impl FromStr for Vout {
83 type Err = ParseIntError;
84
85 #[inline]
86 fn from_str(s: &str) -> Result<Self, Self::Err> { s.parse().map(Self) }
87}
88
89#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display)]
90#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
91#[strict_type(lib = LIB_NAME_BITCOIN)]
92#[display("{txid}:{vout}")]
93pub struct Outpoint {
94 pub txid: Txid,
95 pub vout: Vout,
96}
97
98impl Outpoint {
99 #[inline]
100 pub fn new(txid: Txid, vout: impl Into<Vout>) -> Self {
101 Self {
102 txid,
103 vout: vout.into(),
104 }
105 }
106
107 #[inline]
108 pub const fn coinbase() -> Self {
109 Self {
110 txid: Txid::coinbase(),
111 vout: Vout::from_u32(0),
112 }
113 }
114
115 #[inline]
116 pub fn vout_u32(self) -> u32 { self.vout.into_u32() }
117
118 #[inline]
119 pub fn vout_usize(self) -> usize { self.vout.into_usize() }
120
121 #[inline]
122 pub fn is_coinbase(&self) -> bool { self.txid.is_coinbase() && self.vout.into_u32() == 0 }
123}
124
125#[derive(Clone, Eq, PartialEq, Debug, Display, From, Error)]
126#[display(doc_comments)]
127pub enum OutpointParseError {
128 MalformedSeparator(String),
131
132 #[from]
134 InvalidVout(ParseIntError),
135
136 #[from]
138 InvalidTxid(hex::Error),
139}
140
141impl FromStr for Outpoint {
142 type Err = OutpointParseError;
143
144 fn from_str(s: &str) -> Result<Self, Self::Err> {
145 let (txid, vout) = s
146 .split_once(':')
147 .ok_or_else(|| OutpointParseError::MalformedSeparator(s.to_owned()))?;
148 Ok(Outpoint::new(txid.parse()?, Vout::from_str(vout)?))
149 }
150}
151
152#[cfg(feature = "serde")]
153mod _serde_outpoint {
154 use serde::de::{SeqAccess, Visitor};
155 use serde::ser::SerializeTuple;
156 use serde::{Deserialize, Deserializer, Serialize, Serializer};
157
158 use super::*;
159
160 impl Serialize for Outpoint {
161 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
162 where S: Serializer {
163 if serializer.is_human_readable() {
164 serializer.serialize_str(&self.to_string())
165 } else {
166 let mut ser = serializer.serialize_tuple(2)?;
167 ser.serialize_element(&self.txid)?;
168 ser.serialize_element(&self.vout)?;
169 ser.end()
170 }
171 }
172 }
173
174 impl<'de> Deserialize<'de> for Outpoint {
175 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
176 where D: Deserializer<'de> {
177 use serde::de::Error;
178 if deserializer.is_human_readable() {
179 String::deserialize(deserializer).and_then(|string| {
180 Self::from_str(&string)
181 .map_err(|_| D::Error::custom("wrong outpoint string representation"))
182 })
183 } else {
184 struct OutpointVisitor;
185
186 impl<'de> Visitor<'de> for OutpointVisitor {
187 type Value = Outpoint;
188
189 fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
190 write!(formatter, "a transaction outpoint")
191 }
192
193 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
194 where A: SeqAccess<'de> {
195 let mut outpoint = Outpoint::coinbase();
196 outpoint.txid =
197 seq.next_element()?.ok_or_else(|| Error::invalid_length(0, &self))?;
198 outpoint.vout =
199 seq.next_element()?.ok_or_else(|| Error::invalid_length(1, &self))?;
200 Ok(outpoint)
201 }
202 }
203
204 deserializer.deserialize_tuple(2, OutpointVisitor)
205 }
206 }
207 }
208}
209
210#[derive(Clone, Eq, PartialEq, Hash, Debug)]
211#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
212#[strict_type(lib = LIB_NAME_BITCOIN)]
213#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
214pub struct TxIn {
215 pub prev_output: Outpoint,
216 pub sig_script: SigScript,
217 pub sequence: SeqNo,
218 pub witness: Witness,
219}
220
221#[derive(
222 Wrapper, WrapperMut, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, From, Default
223)]
224#[wrapper(Add, Sub, Mul, Div, FromStr)]
225#[wrapper_mut(MathAssign)]
226#[derive(StrictType, StrictEncode, StrictDecode)]
227#[strict_type(lib = LIB_NAME_BITCOIN)]
228#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
229pub struct Sats(
230 #[from]
231 #[from(u32)]
232 #[from(u16)]
233 #[from(u8)]
234 pub u64,
235);
236
237impl Sats {
238 pub const ZERO: Self = Sats(0);
239 #[allow(clippy::inconsistent_digit_grouping)]
240 pub const BTC: Self = Sats(1_000_000_00);
241 pub const MAX: Self = Sats(u64::MAX);
242
243 pub const fn from_btc(btc: u32) -> Self { Self(btc as u64 * Self::BTC.0) }
244 pub fn from_sats(sats: impl Into<u64>) -> Self { Self(sats.into()) }
245
246 pub const fn is_zero(&self) -> bool { self.0 == 0 }
247 pub const fn is_non_zero(&self) -> bool { self.0 != 0 }
248
249 pub const fn btc_round(&self) -> u64 {
250 if self.0 == 0 {
251 return 0;
252 }
253 let inc = 2 * self.sats_rem() / Self::BTC.0;
254 self.0 / Self::BTC.0 + inc
255 }
256
257 pub const fn btc_ceil(&self) -> u64 {
258 if self.0 == 0 {
259 return 0;
260 }
261 let inc = if self.sats_rem() > 0 { 1 } else { 0 };
262 self.0 / Self::BTC.0 + inc
263 }
264
265 pub const fn btc_floor(&self) -> u64 {
266 if self.0 == 0 {
267 return 0;
268 }
269 self.0 / Self::BTC.0
270 }
271
272 pub const fn sats(&self) -> u64 { self.0 }
273
274 pub fn sats_i64(&self) -> i64 {
275 i64::try_from(self.0).expect("amount of sats exceeds total bitcoin supply")
276 }
277
278 pub const fn sats_rem(&self) -> u64 { self.0 % Self::BTC.0 }
279
280 pub const fn btc_sats(&self) -> (u64, u64) { (self.btc_floor(), self.sats_rem()) }
281
282 #[must_use]
283 pub fn checked_add(&self, other: impl Into<Self>) -> Option<Self> {
284 self.0.checked_add(other.into().0).map(Self)
285 }
286 #[must_use]
287 pub fn checked_sub(&self, other: impl Into<Self>) -> Option<Self> {
288 self.0.checked_sub(other.into().0).map(Self)
289 }
290
291 #[must_use]
292 pub fn checked_add_assign(&mut self, other: impl Into<Self>) -> Option<Self> {
293 *self = Self(self.0.checked_add(other.into().0)?);
294 Some(*self)
295 }
296
297 #[must_use]
298 pub fn checked_sub_assign(&mut self, other: impl Into<Self>) -> Option<Self> {
299 *self = Self(self.0.checked_sub(other.into().0)?);
300 Some(*self)
301 }
302
303 #[must_use]
304 pub fn saturating_add(&self, other: impl Into<Self>) -> Self {
305 self.0.saturating_add(other.into().0).into()
306 }
307
308 #[must_use]
309 pub fn saturating_sub(&self, other: impl Into<Self>) -> Self {
310 self.0.saturating_sub(other.into().0).into()
311 }
312
313 pub fn saturating_add_assign(&mut self, other: impl Into<Self>) {
314 *self = self.0.saturating_add(other.into().0).into();
315 }
316 pub fn saturating_sub_assign(&mut self, other: impl Into<Self>) {
317 *self = self.0.saturating_sub(other.into().0).into();
318 }
319}
320
321impl PartialEq<u64> for Sats {
322 fn eq(&self, other: &u64) -> bool { self.0.eq(other) }
323}
324
325impl Sum for Sats {
326 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
327 iter.fold(Sats::ZERO, |sum, value| sum.saturating_add(value))
328 }
329}
330
331impl Sum<u64> for Sats {
332 fn sum<I: Iterator<Item = u64>>(iter: I) -> Self {
333 iter.fold(Sats::ZERO, |sum, value| sum.saturating_add(value))
334 }
335}
336
337impl Div<usize> for Sats {
338 type Output = Sats;
339 fn div(self, rhs: usize) -> Self::Output { Sats(self.0 / rhs as u64) }
340}
341
342impl Rem<usize> for Sats {
343 type Output = Sats;
344 fn rem(self, rhs: usize) -> Self::Output { Sats(self.0 % rhs as u64) }
345}
346
347impl Display for Sats {
348 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Display::fmt(&self.0, f) }
349}
350
351#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)]
352#[derive(StrictType, StrictEncode, StrictDecode)]
353#[strict_type(lib = LIB_NAME_BITCOIN)]
354#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
355pub struct TxOut {
356 pub value: Sats,
357 pub script_pubkey: ScriptPubkey,
358}
359
360impl TxOut {
361 pub fn new(script_pubkey: impl Into<ScriptPubkey>, value: impl Into<Sats>) -> Self {
362 TxOut {
363 script_pubkey: script_pubkey.into(),
364 value: value.into(),
365 }
366 }
367}
368
369#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
370#[derive(StrictType, StrictEncode, StrictDecode)]
371#[strict_type(lib = LIB_NAME_BITCOIN)]
372#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
373pub struct TxVer(i32);
374
375impl Default for TxVer {
376 fn default() -> Self { TxVer(2) }
377}
378
379impl TxVer {
380 pub const V1: Self = TxVer(1);
382 pub const V2: Self = TxVer(2);
384
385 #[inline]
386 pub const fn from_consensus_i32(ver: i32) -> Self { TxVer(ver) }
387
388 pub const fn try_from_standard(ver: i32) -> Result<Self, NonStandardValue<i32>> {
389 let ver = TxVer::from_consensus_i32(ver);
390 if !ver.is_standard() {
391 Err(NonStandardValue::with(ver.0, "TxVer"))
392 } else {
393 Ok(ver)
394 }
395 }
396
397 #[inline]
398 pub const fn is_standard(self) -> bool { self.0 <= TxVer::V2.0 }
399
400 #[inline]
401 pub const fn to_consensus_i32(&self) -> i32 { self.0 }
402}
403
404#[derive(Clone, Eq, PartialEq, Hash, Debug, Display)]
405#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
406#[strict_type(lib = LIB_NAME_BITCOIN)]
407#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
408#[display(LowerHex)]
409pub struct Tx {
410 pub version: TxVer,
411 pub inputs: VarIntArray<TxIn>,
412 pub outputs: VarIntArray<TxOut>,
413 pub lock_time: LockTime,
414}
415
416impl LowerHex for Tx {
417 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
418 f.write_str(&self.consensus_serialize().to_hex())
419 }
420}
421
422#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)]
423#[display(inner)]
424pub enum BlockDataParseError {
425 #[from]
426 Hex(hex::Error),
427 #[from]
428 Consensus(ConsensusDecodeError),
429}
430
431impl FromStr for Tx {
432 type Err = BlockDataParseError;
433
434 fn from_str(s: &str) -> Result<Self, Self::Err> {
435 let data = Vec::<u8>::from_hex(s)?;
436 Tx::consensus_deserialize(data).map_err(BlockDataParseError::from)
437 }
438}
439
440impl Tx {
441 #[inline]
442 pub fn inputs(&self) -> slice::Iter<TxIn> { self.inputs.iter() }
443
444 #[inline]
445 pub fn outputs(&self) -> slice::Iter<TxOut> { self.outputs.iter() }
446
447 #[inline]
448 pub fn is_segwit(&self) -> bool { self.inputs().any(|txin| !txin.witness.is_empty()) }
449
450 #[inline]
451 pub fn to_unsigned_tx(&self) -> Tx {
452 let mut tx = self.clone();
453 for input in &mut tx.inputs {
454 input.sig_script = SigScript::empty();
455 input.witness = empty!();
456 }
457 tx
458 }
459
460 pub fn ntxid(&self) -> [u8; 32] { self.to_unsigned_tx().txid().to_byte_array() }
465
466 pub fn txid(&self) -> Txid {
473 let mut enc = Sha256::default();
474 self.version.consensus_encode(&mut enc).expect("engines don't error");
475 self.inputs.consensus_encode(&mut enc).expect("engines don't error");
476 self.outputs.consensus_encode(&mut enc).expect("engines don't error");
477 self.lock_time.consensus_encode(&mut enc).expect("engines don't error");
478 let mut double = Sha256::default();
479 double.input_raw(&enc.finish());
480 Txid::from_byte_array(double.finish())
481 }
482
483 pub fn wtxid(&self) -> Wtxid {
490 let mut enc = Sha256::default();
491 self.consensus_encode(&mut enc).expect("engines don't error");
492 let mut double = Sha256::default();
493 double.input_raw(&enc.finish());
494 Wtxid::from_byte_array(double.finish())
495 }
496}
497
498#[cfg(test)]
499mod test {
500 #![cfg_attr(coverage_nightly, coverage(off))]
501
502 use super::*;
503
504 #[test]
505 fn txid_byteorder() {
506 let hex = "ed9f6388c0360c1861d331a0388d5a54815dd720cc67fa783c348217a0e943ca";
507 let from_str = Txid::from_str(hex).unwrap();
508 let from_hex = Txid::from_hex(hex).unwrap();
509 assert_eq!(from_str, from_hex);
510 assert_eq!(from_str.to_string(), from_str.to_hex());
511 assert_eq!(from_str.to_string(), hex);
512 assert_eq!(format!("{from_str:x}"), hex);
513 assert_eq!(from_str[0], 0xca);
514 }
515
516 #[test]
517 fn sats() {
518 assert_eq!(Sats(0).0, 0);
519 assert_eq!(Sats(0).btc_round(), 0);
520 assert_eq!(Sats(0).btc_ceil(), 0);
521 assert_eq!(Sats(0).btc_floor(), 0);
522 assert_eq!(Sats(0).sats(), 0);
523 assert_eq!(Sats(0).sats_rem(), 0);
524
525 assert_eq!(Sats(1000).0, 1000);
526 assert_eq!(Sats(1000).btc_round(), 0);
527 assert_eq!(Sats(1000).btc_ceil(), 1);
528 assert_eq!(Sats(1000).btc_floor(), 0);
529 assert_eq!(Sats(1000).sats(), 1000);
530 assert_eq!(Sats(1000).sats_rem(), 1000);
531
532 assert_eq!(Sats(49_999_999).btc_round(), 0);
533 assert_eq!(Sats(49_999_999).btc_ceil(), 1);
534 assert_eq!(Sats(49_999_999).btc_floor(), 0);
535 assert_eq!(Sats(50_000_000).0, 50_000_000);
536 assert_eq!(Sats(50_000_000).btc_round(), 1);
537 assert_eq!(Sats(50_000_000).btc_ceil(), 1);
538 assert_eq!(Sats(50_000_000).btc_floor(), 0);
539 assert_eq!(Sats(50_000_000).sats(), 50_000_000);
540 assert_eq!(Sats(50_000_000).sats_rem(), 50_000_000);
541
542 assert_eq!(Sats(99_999_999).btc_round(), 1);
543 assert_eq!(Sats(99_999_999).btc_ceil(), 1);
544 assert_eq!(Sats(99_999_999).btc_floor(), 0);
545 assert_eq!(Sats(100_000_000), Sats::from_btc(1));
546 assert_eq!(Sats(100_000_000).0, 100_000_000);
547 assert_eq!(Sats(100_000_000).btc_round(), 1);
548 assert_eq!(Sats(100_000_000).btc_ceil(), 1);
549 assert_eq!(Sats(100_000_000).btc_floor(), 1);
550 assert_eq!(Sats(100_000_000).sats(), 100_000_000);
551 assert_eq!(Sats(100_000_000).sats_rem(), 0);
552 assert_eq!(Sats(100_000_001).sats(), 100_000_001);
553 assert_eq!(Sats(100_000_001).sats_rem(), 1);
554 assert_eq!(Sats(110_000_000).sats(), 110_000_000);
555 assert_eq!(Sats(110_000_000).sats_rem(), 10_000_000);
556 }
557
558 #[test]
559 fn nonsegwit_transaction() {
560 let tx =
561 "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c49\
562 3046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7\
563 f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506e\
564 fdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b\
565 3839e2bbf32d826a1e222031fd888ac00000000";
566 let realtx = Tx::from_str(tx).unwrap();
567
568 assert_eq!(&realtx.to_string(), tx);
569 assert_eq!(&realtx.to_hex(), tx);
570 assert_eq!(&format!("{realtx:x}"), tx);
571
572 assert_eq!(realtx.version, TxVer::V1);
576 assert_eq!(realtx.inputs.len(), 1);
577 assert_eq!(
580 format!("{:x}", realtx.inputs[0].prev_output.txid),
581 "ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string()
582 );
583 assert_eq!(realtx.inputs[0].prev_output.vout, Vout::from_u32(1));
584 assert_eq!(realtx.outputs.len(), 1);
585 assert_eq!(realtx.lock_time, LockTime::ZERO);
586
587 assert_eq!(
588 format!("{:x}", realtx.txid()),
589 "a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string()
590 );
591 assert_eq!(
592 format!("{:x}", realtx.wtxid()),
593 "a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string()
594 );
595 }
602
603 #[test]
604 fn segwit_transaction() {
605 let tx =
606 "02000000000101595895ea20179de87052b4046dfe6fd515860505d6511a9004cf12a1f93cac7c01000000\
607 00ffffffff01deb807000000000017a9140f3444e271620c736808aa7b33e370bd87cb5a078702483045022\
608 100fb60dad8df4af2841adc0346638c16d0b8035f5e3f3753b88db122e70c79f9370220756e6633b17fd271\
609 0e626347d28d60b0a2d6cbb41de51740644b9fb3ba7751040121028fa937ca8cba2197a37c007176ed89410\
610 55d3bcb8627d085e94553e62f057dcc00000000";
611 let realtx = Tx::from_str(tx).unwrap();
612
613 assert_eq!(&realtx.to_string(), tx);
614 assert_eq!(&realtx.to_hex(), tx);
615 assert_eq!(&format!("{realtx:x}"), tx);
616
617 assert_eq!(realtx.version, TxVer::V2);
621 assert_eq!(realtx.inputs.len(), 1);
622 assert_eq!(
625 format!("{:x}", realtx.inputs[0].prev_output.txid),
626 "7cac3cf9a112cf04901a51d605058615d56ffe6d04b45270e89d1720ea955859".to_string()
627 );
628 assert_eq!(realtx.inputs[0].prev_output.vout, Vout::from_u32(1));
629 assert_eq!(realtx.outputs.len(), 1);
630 assert_eq!(realtx.lock_time, LockTime::ZERO);
631
632 assert_eq!(
633 format!("{:x}", realtx.txid()),
634 "f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string()
635 );
636 assert_eq!(
637 format!("{:x}", realtx.wtxid()),
638 "80b7d8a82d5d5bf92905b06f2014dd699e03837ca172e3a59d51426ebbe3e7f5".to_string()
639 );
640
641 }
657}