1use massa_time::MassaTime;
30
31use crate::mips::MIP_0002_EXECUTION_VERSION;
32use crate::versioning::{MipComponent, MipStore};
33
34pub fn sig_chain_id_for_slot(
46 mip_store: &MipStore,
47 chain_id: u64,
48 slot_ts: MassaTime,
49) -> Option<u64> {
50 if mip_store.get_latest_component_version_at(&MipComponent::Execution, slot_ts)
51 >= MIP_0002_EXECUTION_VERSION
52 {
53 Some(chain_id)
54 } else {
55 None
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use std::collections::BTreeMap;
63
64 use massa_hash::Hash;
65 use massa_models::block_header::{BlockHeader, BlockHeaderSerializer};
66 use massa_models::block_id::BlockId;
67 use massa_models::config::{CHAINID, MIP_STORE_STATS_BLOCK_CONSIDERED};
68 use massa_models::denunciation::Denunciation;
69 use massa_models::endorsement::{Endorsement, EndorsementSerializer};
70 use massa_models::secure_share::{Id, SecureShareContent};
71 use massa_models::slot::Slot;
72 use massa_signature::KeyPair;
73 use num::rational::Ratio;
74
75 use crate::test_helpers::versioning_helpers::advance_state_until;
76 use crate::versioning::{ComponentState, MipComponent, MipInfo, MipStatsConfig, MipStore};
77
78 fn store_with_execution_v2_active() -> (MipStore, MassaTime, MassaTime) {
81 let start = MassaTime::from_millis(10_000);
82 let timeout = MassaTime::from_millis(100_000);
83 let activation_delay = MassaTime::from_millis(5_000);
84 let mi = MipInfo {
85 name: "MIP-0002-BugFix".to_string(),
86 version: 2,
87 components: BTreeMap::from([(MipComponent::Execution, MIP_0002_EXECUTION_VERSION)]),
88 start,
89 timeout,
90 activation_delay,
91 };
92 let ms = advance_state_until(ComponentState::active(MassaTime::from_millis(0)), &mi);
93 let stats = MipStatsConfig {
94 block_count_considered: MIP_STORE_STATS_BLOCK_CONSIDERED,
95 warn_announced_version_ratio: Ratio::new_raw(30, 100),
96 };
97 let store = MipStore::try_from(([(mi, ms)], stats)).expect("mip store");
98
99 let before = start.saturating_sub(MassaTime::from_millis(1));
100 let active_at = (0..400)
101 .map(|i| start.saturating_add(MassaTime::from_millis(i * 100)))
102 .find(|ts| {
103 store.get_latest_component_version_at(&MipComponent::Execution, *ts)
104 >= MIP_0002_EXECUTION_VERSION
105 })
106 .expect("Execution v2 should become Active after start");
107 (store, before, active_at)
108 }
109
110 fn make_endorsement(
111 keypair: &KeyPair,
112 slot: Slot,
113 tag: &str,
114 chain_id: u64,
115 sig_chain_id: Option<u64>,
116 ) -> massa_models::endorsement::SecureShareEndorsement {
117 Endorsement::new_verifiable(
118 Endorsement {
119 slot,
120 index: 0,
121 endorsed_block: BlockId::new(Hash::compute_from(tag.as_bytes())),
122 },
123 EndorsementSerializer::new(),
124 keypair,
125 chain_id,
126 sig_chain_id,
127 )
128 .unwrap()
129 }
130
131 fn make_header(
132 keypair: &KeyPair,
133 slot: Slot,
134 tag: &str,
135 chain_id: u64,
136 sig_chain_id: Option<u64>,
137 ) -> massa_models::block_header::SecuredHeader {
138 BlockHeader::new_verifiable(
139 BlockHeader {
140 current_version: 0,
141 announced_version: None,
142 slot,
143 parents: Vec::new(),
144 operation_merkle_root: Hash::compute_from(tag.as_bytes()),
145 endorsements: Vec::new(),
146 denunciations: Vec::new(),
147 },
148 BlockHeaderSerializer::new(),
149 keypair,
150 chain_id,
151 sig_chain_id,
152 )
153 .unwrap()
154 }
155
156 #[test]
157 fn test_sig_chain_id_for_slot_flips_at_execution_v2_active() {
158 let chain = *CHAINID;
159 let (store, before, active_at) = store_with_execution_v2_active();
160
161 assert_eq!(sig_chain_id_for_slot(&store, chain, before), None);
162 assert_eq!(sig_chain_id_for_slot(&store, chain, active_at), Some(chain));
163 }
164
165 #[test]
169 fn test_mip_store_migration_endorsement_and_header_no_partition() {
170 let chain = *CHAINID;
171 let other_chain = chain.wrapping_add(1);
172 let (store, before, active_at) = store_with_execution_v2_active();
173 let keypair = KeyPair::generate(0).unwrap();
174 let slot = Slot::new(3, 7);
175
176 let pre = sig_chain_id_for_slot(&store, chain, before);
178 assert_eq!(pre, None);
179
180 let e1 = make_endorsement(&keypair, slot, "e_pre_1", chain, pre);
181 let e2 = make_endorsement(&keypair, slot, "e_pre_2", chain, pre);
182 let de_e_pre = Denunciation::try_from((&e1, &e2, pre)).unwrap();
183 assert!(de_e_pre.is_valid_with_chain(pre));
184 assert!(!de_e_pre.is_valid_with_chain(Some(chain)));
185
186 let h1 = make_header(&keypair, slot, "h_pre_1", chain, pre);
187 let h2 = make_header(&keypair, slot, "h_pre_2", chain, pre);
188 let de_h_pre = Denunciation::try_from((&h1, &h2, pre)).unwrap();
189 assert!(de_h_pre.is_valid_with_chain(pre));
190 assert!(!de_h_pre.is_valid_with_chain(Some(chain)));
191
192 let post = sig_chain_id_for_slot(&store, chain, active_at);
194 assert_eq!(post, Some(chain));
195
196 let e3 = make_endorsement(&keypair, slot, "e_post_1", chain, post);
197 let e4 = make_endorsement(&keypair, slot, "e_post_2", chain, post);
198 let de_e_post = Denunciation::try_from((&e3, &e4, post)).unwrap();
199 assert!(de_e_post.is_valid_with_chain(post));
200 assert!(!de_e_post.is_valid_with_chain(None));
201
202 let h3 = make_header(&keypair, slot, "h_post_1", chain, post);
203 let h4 = make_header(&keypair, slot, "h_post_2", chain, post);
204 let de_h_post = Denunciation::try_from((&h3, &h4, post)).unwrap();
205 assert!(de_h_post.is_valid_with_chain(post));
206 assert!(!de_h_post.is_valid_with_chain(None));
207
208 let e_other = make_endorsement(&keypair, slot, "e_other", other_chain, Some(other_chain));
210 assert!(Denunciation::try_from((&e3, &e_other, post)).is_err());
211 let h_other = make_header(&keypair, slot, "h_other", other_chain, Some(other_chain));
212 assert!(Denunciation::try_from((&h3, &h_other, post)).is_err());
213 }
214}