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 Network::Signet => (
143 BIP30_DEACTIVATION_REGTEST,
144 BIP16_P2SH_ACTIVATION_REGTEST,
145 1,
146 1,
147 1,
148 1,
149 1,
150 1,
151 1,
152 u64::MAX,
153 u64::MAX,
154 ),
155 };
156
157 let bip54 = bip54_activation_override.unwrap_or(match network {
158 Network::Mainnet => BIP54_ACTIVATION_MAINNET,
159 Network::Testnet => BIP54_ACTIVATION_TESTNET,
160 Network::Regtest => BIP54_ACTIVATION_REGTEST,
161 Network::Signet => u64::MAX,
162 });
163
164 Self {
165 bip30_deactivation,
166 bip16,
167 bip34,
168 bip66,
169 bip65,
170 bip112,
171 bip147,
172 segwit,
173 taproot,
174 ctv,
175 csfs,
176 bip54,
177 }
178 }
179}
180
181#[spec_locked("11.2", "TaprootActivationHeight")]
183#[blvm_spec_lock::ensures(result == 0 || result == 1 || result == 709632 || result == 2011968)]
184#[inline]
185pub fn taproot_activation_height(network: Network) -> u64 {
186 if network == Network::Mainnet {
188 TAPROOT_ACTIVATION_MAINNET
189 } else if network == Network::Testnet {
190 TAPROOT_ACTIVATION_TESTNET
191 } else if network == Network::Signet {
192 1
193 } else {
194 0
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201 use crate::types::Network;
202
203 #[test]
204 fn fork_table_testnet_matches_primitives() {
205 let t = ForkActivationTable::from_network(Network::Testnet);
206 assert_eq!(t.bip65, BIP65_ACTIVATION_TESTNET);
207 assert_eq!(t.bip112, BIP112_CSV_ACTIVATION_TESTNET);
208 assert_eq!(t.bip147, BIP147_ACTIVATION_TESTNET);
209 assert_eq!(t.segwit, SEGWIT_ACTIVATION_TESTNET);
210 assert_eq!(t.taproot, TAPROOT_ACTIVATION_TESTNET);
211 }
212
213 #[test]
214 fn taproot_activation_height_matches_table() {
215 for net in [
216 Network::Mainnet,
217 Network::Testnet,
218 Network::Regtest,
219 Network::Signet,
220 ] {
221 let h = taproot_activation_height(net);
222 let t = ForkActivationTable::from_network(net).taproot;
223 assert_eq!(h, t, "{net:?}");
224 }
225 }
226}