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 if *network == crate::types::Network::Mainnet {
79 bip54_deployment_mainnet()
80 } else if *network == crate::types::Network::Testnet {
81 bip54_deployment_testnet()
82 } else {
83 bip54_deployment_regtest()
84 }
85}
86
87#[spec_locked("5.4.9", "ActivationHeightFromVersionBits")]
107pub fn activation_height_from_headers<H: AsRef<BlockHeader>>(
108 headers: &[H],
109 current_height: u64,
110 current_time: u64,
111 deployment: &Bip9Deployment,
112) -> Option<u64> {
113 if deployment.start_time >= deployment.timeout {
114 return None;
115 }
116 if current_time < deployment.start_time || current_time >= deployment.timeout {
117 return None;
118 }
119 if headers.len() < LOCK_IN_PERIOD as usize {
120 return None;
121 }
122
123 let mut count = 0u32;
124 for h in headers.iter().take(LOCK_IN_PERIOD as usize) {
125 let v = h.as_ref().version as u32;
126 if ((v >> deployment.bit) & 1) != 0 {
127 count += 1;
128 }
129 }
130 if count < ACTIVATION_THRESHOLD {
131 return None;
132 }
133
134 let period_end = current_height.saturating_sub(1);
139 let period_index = period_end / LOCK_IN_PERIOD as u64;
140 let activation_height = (period_index + 2).checked_mul(LOCK_IN_PERIOD as u64)?;
142
143 Some(activation_height)
144}
145
146#[inline]
149pub fn merge_bip54_activation_candidate(
150 previous: Option<u64>,
151 candidate: Option<u64>,
152) -> Option<u64> {
153 match (previous, candidate) {
154 (Some(a), Some(b)) => Some(a.min(b)),
155 (Some(a), None) => Some(a),
156 (None, Some(b)) => Some(b),
157 (None, None) => None,
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use crate::types::BlockHeader;
165
166 fn header(version: i64) -> BlockHeader {
167 BlockHeader {
168 version,
169 prev_block_hash: [0u8; 32],
170 merkle_root: [0u8; 32],
171 timestamp: 0,
172 bits: 0x1d00ffff,
173 nonce: 0,
174 }
175 }
176
177 #[test]
178 fn disabled_deployment_returns_none() {
179 let dep = Bip9Deployment {
180 bit: 0,
181 start_time: 100,
182 timeout: 100,
183 };
184 let headers: Vec<BlockHeader> = (0..2016).map(|_| header(1)).collect();
185 assert!(activation_height_from_headers(&headers, 4032, 150, &dep).is_none());
186 }
187
188 #[test]
189 fn active_after_lockin() {
190 let dep = Bip9Deployment {
191 bit: 0,
192 start_time: 0,
193 timeout: u64::MAX,
194 };
195 let headers: Vec<BlockHeader> = (0..2016).map(|_| header(1)).collect();
197 assert_eq!(
199 activation_height_from_headers(&headers, 4032, 1, &dep),
200 Some(6048)
201 );
202 assert_eq!(
204 activation_height_from_headers(&headers, 6048, 1, &dep),
205 Some(8064)
206 );
207 assert_eq!(
208 merge_bip54_activation_candidate(
209 activation_height_from_headers(&headers, 4032, 1, &dep),
210 activation_height_from_headers(&headers, 6048, 1, &dep),
211 ),
212 Some(6048)
213 );
214 }
215
216 #[test]
217 fn not_active_before_activation_height() {
218 let dep = Bip9Deployment {
219 bit: 0,
220 start_time: 0,
221 timeout: u64::MAX,
222 };
223 let headers: Vec<BlockHeader> = (0..2016).map(|_| header(1)).collect();
224 let act = activation_height_from_headers(&headers, 4031, 1, &dep);
225 assert_eq!(act, Some(6048));
226 assert!(
227 !crate::bip_validation::is_bip54_active_at(4031, crate::types::Network::Mainnet, act),
228 "override height must not activate BIP54 before that height"
229 );
230 }
231
232 #[test]
233 fn huge_current_height_does_not_panic() {
234 let dep = Bip9Deployment {
235 bit: 0,
236 start_time: 0,
237 timeout: u64::MAX,
238 };
239 let headers: Vec<BlockHeader> = (0..2016).map(|_| header(1)).collect();
240 assert!(activation_height_from_headers(&headers, u64::MAX, 1, &dep).is_none());
241 }
242}