1use blvm_spec_lock::spec_locked;
8
9use crate::constants::*;
10use crate::types::{ForkId, Network};
11
12pub trait IsForkActive {
17 fn is_fork_active(&self, fork: ForkId, height: u64) -> bool;
19}
20
21#[derive(Debug, Clone)]
26pub struct ForkActivationTable {
27 pub bip30_deactivation: u64,
29 pub bip16: u64,
31 pub bip34: u64,
32 pub bip66: u64,
33 pub bip65: u64,
34 pub bip112: u64,
35 pub bip147: u64,
36 pub segwit: u64,
37 pub taproot: u64,
38 pub ctv: u64,
39 pub csfs: u64,
40 pub bip54: u64,
41}
42
43impl IsForkActive for ForkActivationTable {
44 #[inline]
45 fn is_fork_active(&self, fork: ForkId, height: u64) -> bool {
46 match fork {
47 ForkId::Bip30 => height <= self.bip30_deactivation,
48 ForkId::Bip16 => height >= self.bip16,
49 ForkId::Bip34 => height >= self.bip34,
50 ForkId::Bip66 => height >= self.bip66,
51 ForkId::Bip65 => height >= self.bip65,
52 ForkId::Bip112 => height >= self.bip112,
53 ForkId::Bip147 => height >= self.bip147,
54 ForkId::SegWit => height >= self.segwit,
55 ForkId::Taproot => height >= self.taproot,
56 ForkId::Ctv => self.ctv != u64::MAX && height >= self.ctv,
57 ForkId::Csfs => self.csfs != u64::MAX && height >= self.csfs,
58 ForkId::Bip54 => height >= self.bip54,
59 }
60 }
61}
62
63impl ForkActivationTable {
64 pub fn from_network(network: Network) -> Self {
66 Self::from_network_and_bip54_override(network, None)
67 }
68
69 pub fn from_network_and_bip54_override(
71 network: Network,
72 bip54_activation_override: Option<u64>,
73 ) -> Self {
74 let (
75 bip30_deactivation,
76 bip16,
77 bip34,
78 bip66,
79 bip65,
80 bip112,
81 bip147,
82 segwit,
83 taproot,
84 ctv,
85 csfs,
86 ) = match network {
87 Network::Mainnet => (
88 BIP30_DEACTIVATION_MAINNET,
89 BIP16_P2SH_ACTIVATION_MAINNET,
90 BIP34_ACTIVATION_MAINNET,
91 BIP66_ACTIVATION_MAINNET,
92 BIP65_ACTIVATION_MAINNET,
93 BIP112_CSV_ACTIVATION_MAINNET,
94 BIP147_ACTIVATION_MAINNET,
95 SEGWIT_ACTIVATION_MAINNET,
96 TAPROOT_ACTIVATION_MAINNET,
97 if CTV_ACTIVATION_MAINNET == 0 {
98 u64::MAX
99 } else {
100 CTV_ACTIVATION_MAINNET
101 },
102 if CSFS_ACTIVATION_MAINNET == 0 {
103 u64::MAX
104 } else {
105 CSFS_ACTIVATION_MAINNET
106 },
107 ),
108 Network::Testnet => (
109 BIP30_DEACTIVATION_TESTNET,
110 BIP16_P2SH_ACTIVATION_TESTNET,
111 BIP34_ACTIVATION_TESTNET,
112 BIP66_ACTIVATION_TESTNET,
113 BIP65_ACTIVATION_TESTNET,
114 BIP112_CSV_ACTIVATION_TESTNET,
115 BIP147_ACTIVATION_TESTNET,
116 SEGWIT_ACTIVATION_TESTNET,
117 TAPROOT_ACTIVATION_TESTNET,
118 if CTV_ACTIVATION_TESTNET == 0 {
119 u64::MAX
120 } else {
121 CTV_ACTIVATION_TESTNET
122 },
123 if CSFS_ACTIVATION_TESTNET == 0 {
124 u64::MAX
125 } else {
126 CSFS_ACTIVATION_TESTNET
127 },
128 ),
129 Network::Regtest => (
130 BIP30_DEACTIVATION_REGTEST,
131 BIP16_P2SH_ACTIVATION_REGTEST,
132 BIP34_ACTIVATION_REGTEST,
133 BIP66_ACTIVATION_REGTEST,
134 0,
135 BIP112_CSV_ACTIVATION_REGTEST,
136 0,
137 0,
138 0,
139 CTV_ACTIVATION_REGTEST,
140 CSFS_ACTIVATION_REGTEST,
141 ),
142 };
143
144 let bip54 = bip54_activation_override.unwrap_or(match network {
145 Network::Mainnet => BIP54_ACTIVATION_MAINNET,
146 Network::Testnet => BIP54_ACTIVATION_TESTNET,
147 Network::Regtest => BIP54_ACTIVATION_REGTEST,
148 });
149
150 Self {
151 bip30_deactivation,
152 bip16,
153 bip34,
154 bip66,
155 bip65,
156 bip112,
157 bip147,
158 segwit,
159 taproot,
160 ctv,
161 csfs,
162 bip54,
163 }
164 }
165}
166
167#[spec_locked("11.2", "TaprootActivationHeight")]
169#[blvm_spec_lock::ensures(result == 0 || result == 709632 || result == 2011968)]
170#[inline]
171pub fn taproot_activation_height(network: Network) -> u64 {
172 match network {
173 Network::Mainnet => TAPROOT_ACTIVATION_MAINNET,
174 Network::Testnet => TAPROOT_ACTIVATION_TESTNET,
175 Network::Regtest => 0,
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::types::Network;
183
184 #[test]
185 fn fork_table_testnet_matches_primitives() {
186 let t = ForkActivationTable::from_network(Network::Testnet);
187 assert_eq!(t.bip65, BIP65_ACTIVATION_TESTNET);
188 assert_eq!(t.bip112, BIP112_CSV_ACTIVATION_TESTNET);
189 assert_eq!(t.bip147, BIP147_ACTIVATION_TESTNET);
190 assert_eq!(t.segwit, SEGWIT_ACTIVATION_TESTNET);
191 assert_eq!(t.taproot, TAPROOT_ACTIVATION_TESTNET);
192 }
193
194 #[test]
195 fn taproot_activation_height_matches_table() {
196 for net in [Network::Mainnet, Network::Testnet, Network::Regtest] {
197 let h = taproot_activation_height(net);
198 let t = ForkActivationTable::from_network(net).taproot;
199 assert_eq!(h, t, "{net:?}");
200 }
201 }
202}