1use serde::{Deserialize, Serialize};
4use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
5
6#[cfg(feature = "production")]
7use rustc_hash::FxHashMap;
8#[cfg(feature = "production")]
9use smallvec::SmallVec;
10#[cfg(not(feature = "production"))]
11use std::collections::HashMap;
12
13#[cfg(feature = "production")]
15pub use smallvec;
16
17#[cfg(feature = "production")]
19#[macro_export]
20macro_rules! tx_inputs {
21 ($($item:expr),* $(,)?) => {
22 {
23 $crate::smallvec::SmallVec::from_vec(vec![$($item),*])
24 }
25 };
26}
27
28#[cfg(not(feature = "production"))]
29#[macro_export]
30macro_rules! tx_inputs {
31 ($($item:expr),* $(,)?) => {
32 vec![$($item),*]
33 };
34}
35
36#[cfg(feature = "production")]
37#[macro_export]
38macro_rules! tx_outputs {
39 ($($item:expr),* $(,)?) => {
40 {
41 $crate::smallvec::SmallVec::from_vec(vec![$($item),*])
42 }
43 };
44}
45
46#[cfg(not(feature = "production"))]
47#[macro_export]
48macro_rules! tx_outputs {
49 ($($item:expr),* $(,)?) => {
50 vec![$($item),*]
51 };
52}
53
54pub type Hash = [u8; 32];
56
57pub type ByteString = Vec<u8>;
59
60pub type Witness = Vec<ByteString>;
65
66const SHARED_BYTE_INLINE_CAP: usize = 25;
74
75pub static SBS_SHARED_LIVE: std::sync::atomic::AtomicI64 = std::sync::atomic::AtomicI64::new(0);
79pub static SBS_SHARED_TOTAL: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
81
82enum SharedRepr {
83 Inline {
84 len: u8,
85 data: [u8; SHARED_BYTE_INLINE_CAP],
86 },
87 Shared(std::sync::Arc<[u8]>),
88}
89
90#[derive(Clone)]
93pub struct SharedByteString(SharedRepr);
94
95impl std::fmt::Debug for SharedByteString {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 f.debug_tuple("SharedByteString")
98 .field(&self.as_slice())
99 .finish()
100 }
101}
102
103impl PartialEq for SharedByteString {
104 #[inline]
105 fn eq(&self, other: &Self) -> bool {
106 self.as_slice() == other.as_slice()
107 }
108}
109
110impl Eq for SharedByteString {}
111
112impl std::hash::Hash for SharedByteString {
113 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
114 self.as_slice().hash(state);
115 }
116}
117
118impl SharedByteString {
119 #[inline]
120 fn as_slice(&self) -> &[u8] {
121 match &self.0 {
122 SharedRepr::Inline { len, data } => &data[..*len as usize],
123 SharedRepr::Shared(a) => a,
124 }
125 }
126
127 #[inline]
128 fn from_bytes(v: &[u8]) -> Self {
129 if v.len() <= SHARED_BYTE_INLINE_CAP {
130 let mut data = [0u8; SHARED_BYTE_INLINE_CAP];
131 data[..v.len()].copy_from_slice(v);
132 Self(SharedRepr::Inline {
133 len: v.len() as u8,
134 data,
135 })
136 } else {
137 SBS_SHARED_LIVE.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
138 SBS_SHARED_TOTAL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
139 Self(SharedRepr::Shared(std::sync::Arc::from(v)))
140 }
141 }
142}
143
144impl Clone for SharedRepr {
149 fn clone(&self) -> Self {
150 match self {
151 SharedRepr::Inline { len, data } => SharedRepr::Inline {
152 len: *len,
153 data: *data,
154 },
155 SharedRepr::Shared(a) => {
156 SBS_SHARED_LIVE.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
157 SharedRepr::Shared(std::sync::Arc::clone(a))
158 }
159 }
160 }
161}
162
163impl Drop for SharedRepr {
164 fn drop(&mut self) {
165 if matches!(self, SharedRepr::Shared(_)) {
166 SBS_SHARED_LIVE.fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
167 }
168 }
169}
170
171impl std::ops::Deref for SharedByteString {
172 type Target = [u8];
173 #[inline]
174 fn deref(&self) -> &[u8] {
175 self.as_slice()
176 }
177}
178
179impl Serialize for SharedByteString {
180 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
181 self.as_slice().serialize(s)
182 }
183}
184
185impl<'de> Deserialize<'de> for SharedByteString {
186 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
187 let v: Vec<u8> = Deserialize::deserialize(d)?;
188 Ok(Self::from_bytes(&v))
189 }
190}
191
192impl From<ByteString> for SharedByteString {
193 #[inline]
194 fn from(v: ByteString) -> Self {
195 Self::from_bytes(v.as_slice())
196 }
197}
198
199impl From<&[u8]> for SharedByteString {
200 #[inline]
201 fn from(v: &[u8]) -> Self {
202 Self::from_bytes(v)
203 }
204}
205
206impl Default for SharedByteString {
207 #[inline]
208 fn default() -> Self {
209 Self(SharedRepr::Inline {
210 len: 0,
211 data: [0u8; SHARED_BYTE_INLINE_CAP],
212 })
213 }
214}
215
216impl AsRef<[u8]> for SharedByteString {
217 #[inline]
218 fn as_ref(&self) -> &[u8] {
219 self.as_slice()
220 }
221}
222
223impl SharedByteString {
224 #[inline]
226 pub fn as_arc(&self) -> std::sync::Arc<[u8]> {
227 match &self.0 {
228 SharedRepr::Shared(a) => std::sync::Arc::clone(a),
229 SharedRepr::Inline { len, data } => {
230 std::sync::Arc::from(data[..*len as usize].to_vec().into_boxed_slice())
231 }
232 }
233 }
234}
235
236pub type Natural = u64;
238
239pub type Integer = i64;
241
242#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
246pub enum Network {
247 Mainnet,
249 Testnet,
251 Testnet4,
253 Regtest,
255 Signet,
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq)]
264pub struct TimeContext {
265 pub network_time: u64,
268 pub median_time_past: u64,
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Eq)]
280pub struct Bip54BoundaryTimestamps {
281 pub timestamp_n_minus_1: u64,
283 pub timestamp_n_minus_2015: u64,
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
292pub enum ForkId {
293 Bip30,
295 Bip16,
297 Bip34,
299 Bip66,
301 Bip65,
303 Bip112,
305 Bip147,
307 SegWit,
309 Taproot,
311 Ctv,
313 Csfs,
315 Bip54,
317}
318
319impl Network {
320 pub fn from_env() -> Self {
328 match std::env::var("BITCOIN_NETWORK").as_deref() {
329 Ok("testnet") => Network::Testnet,
330 Ok("testnet4") => Network::Testnet4,
331 Ok("regtest") => Network::Regtest,
332 Ok("signet") => Network::Signet,
333 _ => Network::Mainnet,
334 }
335 }
336
337 pub fn hrp(&self) -> &'static str {
341 match self {
342 Network::Mainnet => "bc",
343 Network::Testnet | Network::Testnet4 | Network::Signet => "tb",
344 Network::Regtest => "bcrt",
345 }
346 }
347}
348
349#[repr(transparent)]
354#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
355pub struct BlockHeight(pub u64);
356
357impl BlockHeight {
358 #[inline(always)]
360 pub fn new(height: u64) -> Self {
361 BlockHeight(height)
362 }
363
364 #[inline(always)]
366 pub fn as_u64(self) -> u64 {
367 self.0
368 }
369}
370
371impl From<u64> for BlockHeight {
372 #[inline(always)]
373 fn from(height: u64) -> Self {
374 BlockHeight(height)
375 }
376}
377
378impl From<BlockHeight> for u64 {
379 #[inline(always)]
380 fn from(height: BlockHeight) -> Self {
381 height.0
382 }
383}
384
385impl std::ops::Deref for BlockHeight {
386 type Target = u64;
387
388 #[inline(always)]
389 fn deref(&self) -> &Self::Target {
390 &self.0
391 }
392}
393
394#[repr(transparent)]
399#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
400pub struct BlockHash(pub Hash);
401
402impl BlockHash {
403 #[inline(always)]
405 pub fn new(hash: Hash) -> Self {
406 BlockHash(hash)
407 }
408
409 #[inline(always)]
411 pub fn as_hash(self) -> Hash {
412 self.0
413 }
414
415 #[inline(always)]
417 pub fn as_hash_ref(&self) -> &Hash {
418 &self.0
419 }
420}
421
422impl From<Hash> for BlockHash {
423 #[inline(always)]
424 fn from(hash: Hash) -> Self {
425 BlockHash(hash)
426 }
427}
428
429impl From<BlockHash> for Hash {
430 #[inline(always)]
431 fn from(hash: BlockHash) -> Self {
432 hash.0
433 }
434}
435
436impl std::ops::Deref for BlockHash {
437 type Target = Hash;
438
439 #[inline(always)]
440 fn deref(&self) -> &Self::Target {
441 &self.0
442 }
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
449pub struct OutPoint {
450 pub hash: Hash,
451 pub index: u32,
452}
453
454#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
459pub struct TransactionInput {
460 pub prevout: OutPoint, pub sequence: Natural, pub script_sig: ByteString, }
464
465#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
467pub struct TransactionOutput {
468 pub value: Integer,
469 pub script_pubkey: ByteString,
470}
471
472#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
477pub struct Transaction {
478 pub version: Natural,
479 #[cfg(feature = "production")]
480 pub inputs: SmallVec<[TransactionInput; 2]>,
481 #[cfg(not(feature = "production"))]
482 pub inputs: Vec<TransactionInput>,
483 #[cfg(feature = "production")]
484 pub outputs: SmallVec<[TransactionOutput; 2]>,
485 #[cfg(not(feature = "production"))]
486 pub outputs: Vec<TransactionOutput>,
487 pub lock_time: Natural,
488}
489
490#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
495pub struct BlockHeader {
496 pub version: Integer,
497 pub prev_block_hash: Hash,
498 pub merkle_root: Hash,
499 pub timestamp: Natural,
500 pub bits: Natural,
501 pub nonce: Natural,
502}
503
504impl std::convert::AsRef<BlockHeader> for BlockHeader {
505 #[inline]
506 fn as_ref(&self) -> &BlockHeader {
507 self
508 }
509}
510
511pub static ARC_BLOCK_CREATED: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
515
516pub static ARC_BLOCKHEADER_CREATED: std::sync::atomic::AtomicU64 =
519 std::sync::atomic::AtomicU64::new(0);
520
521pub static BLOCK_LIVE: AtomicI64 = AtomicI64::new(0);
526
527pub static LARGE_TX_SLICE_LIVE: AtomicI64 = AtomicI64::new(0);
531
532pub static LARGE_TX_SLICE_BYTES: AtomicU64 = AtomicU64::new(0);
534
535pub const TX_SLICE_LARGE_MIN: usize = 786_432;
537pub const TX_SLICE_LARGE_MAX: usize = 1_572_864;
538
539#[inline]
540pub fn tx_slice_heap_bytes(n_tx: usize) -> usize {
541 n_tx.saturating_mul(std::mem::size_of::<Transaction>())
542}
543
544#[inline]
545fn note_block_new(n_tx: usize) {
546 BLOCK_LIVE.fetch_add(1, Ordering::Relaxed);
547 let b = tx_slice_heap_bytes(n_tx);
548 if (TX_SLICE_LARGE_MIN..=TX_SLICE_LARGE_MAX).contains(&b) {
549 LARGE_TX_SLICE_LIVE.fetch_add(1, Ordering::Relaxed);
550 LARGE_TX_SLICE_BYTES.fetch_add(b as u64, Ordering::Relaxed);
551 }
552}
553
554#[inline]
555fn note_block_drop(n_tx: usize) {
556 BLOCK_LIVE
560 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |v| {
561 Some(if v > 0 { v - 1 } else { 0 })
562 })
563 .ok();
564 let b = tx_slice_heap_bytes(n_tx);
565 if (TX_SLICE_LARGE_MIN..=TX_SLICE_LARGE_MAX).contains(&b) {
566 LARGE_TX_SLICE_LIVE
567 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |v| {
568 Some(if v > 0 { v - 1 } else { 0 })
569 })
570 .ok();
571 LARGE_TX_SLICE_BYTES
572 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |v| {
573 Some(v.saturating_sub(b as u64))
574 })
575 .ok();
576 }
577}
578
579#[derive(Debug, PartialEq, Eq, Serialize)]
585pub struct Block {
586 pub header: BlockHeader,
587 pub transactions: Box<[Transaction]>,
588}
589
590impl Block {
591 pub fn from_parts(header: BlockHeader, transactions: Box<[Transaction]>) -> Self {
594 note_block_new(transactions.len());
595 Self {
596 header,
597 transactions,
598 }
599 }
600}
601
602impl Clone for Block {
603 fn clone(&self) -> Self {
604 Self::from_parts(self.header.clone(), self.transactions.clone())
605 }
606}
607
608impl<'de> Deserialize<'de> for Block {
609 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
610 #[derive(Deserialize)]
611 struct BlockDe {
612 header: BlockHeader,
613 transactions: Box<[Transaction]>,
614 }
615 let raw = BlockDe::deserialize(deserializer)?;
616 note_block_new(raw.transactions.len());
617 Ok(Self {
618 header: raw.header,
619 transactions: raw.transactions,
620 })
621 }
622}
623
624impl Drop for Block {
625 fn drop(&mut self) {
626 note_block_drop(self.transactions.len());
627 }
628}
629
630#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
632pub struct UTXO {
633 pub value: Integer,
634 pub script_pubkey: SharedByteString,
635 pub height: Natural,
636 pub is_coinbase: bool,
639}
640
641#[cfg(feature = "production")]
646pub type UtxoSet = FxHashMap<OutPoint, std::sync::Arc<UTXO>>;
647
648#[cfg(not(feature = "production"))]
649pub type UtxoSet = HashMap<OutPoint, std::sync::Arc<UTXO>>;
650
651#[inline]
655pub fn utxo_set_with_capacity(n: usize) -> UtxoSet {
656 #[cfg(feature = "production")]
657 {
658 FxHashMap::with_capacity_and_hasher(n, Default::default())
659 }
660 #[cfg(not(feature = "production"))]
661 {
662 HashMap::with_capacity(n)
663 }
664}
665
666#[inline]
668pub fn utxo_set_insert(set: &mut UtxoSet, op: OutPoint, u: UTXO) {
669 use std::sync::Arc;
670 set.insert(op, Arc::new(u));
671}
672
673#[must_use = "Validation result must be checked - ignoring may cause consensus violations"]
678#[derive(Debug, Clone, PartialEq, Eq)]
679pub enum ValidationResult {
680 Valid,
681 Invalid(String),
682}
683
684#[derive(Debug, Clone)]
686pub struct ScriptContext {
687 pub script_sig: ByteString,
688 pub script_pubkey: ByteString,
689 pub witness: Option<ByteString>,
690 pub flags: u32,
691}
692
693#[derive(Debug, Clone)]
695pub struct BlockContext {
696 pub height: Natural,
697 pub prev_headers: Vec<BlockHeader>,
698 pub utxo_set: UtxoSet,
699}
700
701#[cfg(test)]
702mod m1_block_live_tests {
703 use super::*;
704
705 #[test]
706 fn dest_ba_1mib_classes_are_tx_slice_widths() {
707 let tsz = std::mem::size_of::<Transaction>();
708 assert_eq!(tx_slice_heap_bytes(4096), 4096 * tsz);
709 assert_eq!(tx_slice_heap_bytes(5120), 5120 * tsz);
710 if tsz == 256 {
712 assert_eq!(tx_slice_heap_bytes(3072), 786_432);
713 assert_eq!(tx_slice_heap_bytes(4096), 1_048_576);
714 assert_eq!(tx_slice_heap_bytes(5120), 1_310_720);
715 assert_eq!(tx_slice_heap_bytes(6144), 1_572_864);
716 assert!((TX_SLICE_LARGE_MIN..=TX_SLICE_LARGE_MAX).contains(&tx_slice_heap_bytes(4096)));
717 assert!((TX_SLICE_LARGE_MIN..=TX_SLICE_LARGE_MAX).contains(&tx_slice_heap_bytes(5120)));
718 }
719 }
720
721 #[test]
722 fn serde_notes_block_live() {
723 let literal = Block {
724 header: BlockHeader::default(),
725 transactions: Vec::new().into_boxed_slice(),
726 };
727 let json = serde_json::to_string(&literal).expect("ser");
728 drop(literal);
729 let before = BLOCK_LIVE.load(Ordering::Relaxed);
730 let decoded: Block = serde_json::from_str(&json).expect("de");
731 assert_eq!(BLOCK_LIVE.load(Ordering::Relaxed), before + 1);
732 drop(decoded);
733 assert_eq!(BLOCK_LIVE.load(Ordering::Relaxed), before);
734 }
735
736 #[test]
737 fn wire_decode_notes_block_live() {
738 use crate::serialization::block::{deserialize_block_with_witnesses, serialize_block};
739 let tx = Transaction {
740 version: 1,
741 inputs: crate::tx_inputs![TransactionInput {
742 prevout: OutPoint {
743 hash: [1; 32],
744 index: 0
745 },
746 script_sig: vec![0x51],
747 sequence: 0xffffffff,
748 }],
749 outputs: crate::tx_outputs![TransactionOutput {
750 value: 50_0000_0000,
751 script_pubkey: vec![0x51],
752 }],
753 lock_time: 0,
754 };
755 let built = Block::from_parts(BlockHeader::default(), vec![tx].into_boxed_slice());
756 let wire = serialize_block(&built);
757 drop(built);
758 let before = BLOCK_LIVE.load(Ordering::Relaxed);
759 let (decoded, _) = deserialize_block_with_witnesses(&wire).expect("wire decode");
760 assert_eq!(BLOCK_LIVE.load(Ordering::Relaxed), before + 1);
761 drop(decoded);
762 assert_eq!(BLOCK_LIVE.load(Ordering::Relaxed), before);
763 }
764
765 #[test]
766 fn drop_floors_live_at_zero() {
767 let before = BLOCK_LIVE.load(Ordering::Relaxed);
768 {
769 let _literal = Block {
770 header: BlockHeader::default(),
771 transactions: Vec::new().into_boxed_slice(),
772 };
773 }
774 assert_eq!(BLOCK_LIVE.load(Ordering::Relaxed), before);
775 }
776}