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