blvm_consensus/
version_bits.rs1use blvm_spec_lock::spec_locked;
7
8use crate::types::BlockHeader;
9
10pub const LOCK_IN_PERIOD: u32 = 2016;
12
13pub const ACTIVATION_THRESHOLD: u32 = 1916;
15
16#[derive(Debug, Clone, Copy)]
18pub struct Bip9Deployment {
19 pub bit: u8,
21 pub start_time: u64,
23 pub timeout: u64,
25}
26
27#[spec_locked("5.4.9", "Bip54DeploymentMainnet")]
33#[blvm_spec_lock::ensures(result.bit == 15)]
34pub fn bip54_deployment_mainnet() -> Bip9Deployment {
35 Bip9Deployment {
36 bit: 15,
37 start_time: 0,
38 timeout: u64::MAX,
39 }
40}
41
42#[spec_locked("5.4.9", "Bip54DeploymentTestnet")]
47#[blvm_spec_lock::ensures(result.bit == 15)]
48pub fn bip54_deployment_testnet() -> Bip9Deployment {
49 Bip9Deployment {
50 bit: 15,
51 start_time: 0,
52 timeout: u64::MAX,
53 }
54}
55
56#[spec_locked("5.4.9", "Bip54DeploymentRegtest")]
61#[blvm_spec_lock::ensures(result.bit == 15)]
62pub fn bip54_deployment_regtest() -> Bip9Deployment {
63 Bip9Deployment {
64 bit: 15,
65 start_time: 0,
66 timeout: u64::MAX,
67 }
68}
69
70#[spec_locked("5.4.9", "Bip54DeploymentForNetwork")]
75#[blvm_spec_lock::ensures(result.bit == 15)]
76pub fn bip54_deployment_for_network(network: &crate::types::Network) -> Bip9Deployment {
77 match network {
78 crate::types::Network::Mainnet => bip54_deployment_mainnet(),
79 crate::types::Network::Testnet => bip54_deployment_testnet(),
80 crate::types::Network::Regtest => bip54_deployment_regtest(),
81 }
82}
83
84pub fn activation_height_from_headers<H: AsRef<BlockHeader>>(
104 headers: &[H],
105 current_height: u64,
106 current_time: u64,
107 deployment: &Bip9Deployment,
108) -> Option<u64> {
109 if deployment.start_time >= deployment.timeout {
110 return None;
111 }
112 if current_time < deployment.start_time || current_time >= deployment.timeout {
113 return None;
114 }
115 if headers.len() < LOCK_IN_PERIOD as usize {
116 return None;
117 }
118
119 let mut count = 0u32;
120 for h in headers.iter().take(LOCK_IN_PERIOD as usize) {
121 let v = h.as_ref().version as u32;
122 if ((v >> deployment.bit) & 1) != 0 {
123 count += 1;
124 }
125 }
126 if count < ACTIVATION_THRESHOLD {
127 return None;
128 }
129
130 let period_end = current_height.saturating_sub(1);
135 let period_index = period_end / LOCK_IN_PERIOD as u64;
136 let activation_height = (period_index + 2).checked_mul(LOCK_IN_PERIOD as u64)?;
138
139 Some(activation_height)
140}
141
142#[inline]
145pub fn merge_bip54_activation_candidate(
146 previous: Option<u64>,
147 candidate: Option<u64>,
148) -> Option<u64> {
149 match (previous, candidate) {
150 (Some(a), Some(b)) => Some(a.min(b)),
151 (Some(a), None) => Some(a),
152 (None, Some(b)) => Some(b),
153 (None, None) => None,
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use crate::types::BlockHeader;
161
162 fn header(version: i64) -> BlockHeader {
163 BlockHeader {
164 version,
165 prev_block_hash: [0u8; 32],
166 merkle_root: [0u8; 32],
167 timestamp: 0,
168 bits: 0x1d00ffff,
169 nonce: 0,
170 }
171 }
172
173 #[test]
174 fn disabled_deployment_returns_none() {
175 let dep = Bip9Deployment {
176 bit: 0,
177 start_time: 100,
178 timeout: 100,
179 };
180 let headers: Vec<BlockHeader> = (0..2016).map(|_| header(1)).collect();
181 assert!(activation_height_from_headers(&headers, 4032, 150, &dep).is_none());
182 }
183
184 #[test]
185 fn active_after_lockin() {
186 let dep = Bip9Deployment {
187 bit: 0,
188 start_time: 0,
189 timeout: u64::MAX,
190 };
191 let headers: Vec<BlockHeader> = (0..2016).map(|_| header(1)).collect();
193 assert_eq!(
195 activation_height_from_headers(&headers, 4032, 1, &dep),
196 Some(6048)
197 );
198 assert_eq!(
200 activation_height_from_headers(&headers, 6048, 1, &dep),
201 Some(8064)
202 );
203 assert_eq!(
204 merge_bip54_activation_candidate(
205 activation_height_from_headers(&headers, 4032, 1, &dep),
206 activation_height_from_headers(&headers, 6048, 1, &dep),
207 ),
208 Some(6048)
209 );
210 }
211
212 #[test]
213 fn not_active_before_activation_height() {
214 let dep = Bip9Deployment {
215 bit: 0,
216 start_time: 0,
217 timeout: u64::MAX,
218 };
219 let headers: Vec<BlockHeader> = (0..2016).map(|_| header(1)).collect();
220 let act = activation_height_from_headers(&headers, 4031, 1, &dep);
221 assert_eq!(act, Some(6048));
222 assert!(
223 !crate::bip_validation::is_bip54_active_at(4031, crate::types::Network::Mainnet, act),
224 "override height must not activate BIP54 before that height"
225 );
226 }
227
228 #[test]
229 fn huge_current_height_does_not_panic() {
230 let dep = Bip9Deployment {
231 bit: 0,
232 start_time: 0,
233 timeout: u64::MAX,
234 };
235 let headers: Vec<BlockHeader> = (0..2016).map(|_| header(1)).collect();
236 assert!(activation_height_from_headers(&headers, u64::MAX, 1, &dep).is_none());
237 }
238}