1use super::*;
16
17impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
18 pub fn prepare_advance_to_next_quorum_block(
20 &self,
21 subdag: Subdag<N>,
22 transmissions: IndexMap<TransmissionID<N>, Transmission<N>>,
23 ) -> Result<Block<N>> {
24 let previous_block = self.latest_block();
26
27 let (ratifications, solutions, transactions) = decouple_transmissions(transmissions.into_iter())?;
29 ensure!(ratifications.is_empty(), "Ratifications are currently unsupported from the memory pool");
31 let (header, ratifications, solutions, transactions, aborted_transaction_ids) =
33 self.construct_block_template(&previous_block, Some(&subdag), ratifications, solutions, transactions)?;
34
35 Block::new_quorum(
37 previous_block.hash(),
38 header,
39 subdag,
40 ratifications,
41 solutions,
42 transactions,
43 aborted_transaction_ids,
44 )
45 }
46
47 pub fn prepare_advance_to_next_beacon_block<R: Rng + CryptoRng>(
49 &self,
50 private_key: &PrivateKey<N>,
51 candidate_ratifications: Vec<Ratify<N>>,
52 candidate_solutions: Vec<ProverSolution<N>>,
53 candidate_transactions: Vec<Transaction<N>>,
54 rng: &mut R,
55 ) -> Result<Block<N>> {
56 ensure!(candidate_ratifications.is_empty(), "Ratifications are currently unsupported from the memory pool");
58
59 let previous_block = self.latest_block();
61
62 let (header, ratifications, solutions, transactions, aborted_transaction_ids) = self.construct_block_template(
64 &previous_block,
65 None,
66 candidate_ratifications,
67 candidate_solutions,
68 candidate_transactions,
69 )?;
70
71 Block::new_beacon(
73 private_key,
74 previous_block.hash(),
75 header,
76 ratifications,
77 solutions,
78 transactions,
79 aborted_transaction_ids,
80 rng,
81 )
82 }
83
84 pub fn advance_to_next_block(&self, block: &Block<N>) -> Result<()> {
86 let mut current_block = self.current_block.write();
88 self.vm.add_next_block(block)?;
90 *current_block = block.clone();
92 drop(current_block);
94
95 if let Ok(current_committee) = self.vm.finalize_store().committee_store().current_committee() {
97 *self.current_committee.write() = Some(current_committee);
98 }
99
100 if block.height() % N::NUM_BLOCKS_PER_EPOCH == 0 || self.current_epoch_challenge.read().is_none() {
102 self.current_epoch_challenge.write().clone_from(&self.get_epoch_challenge(block.height()).ok());
104 }
105
106 Ok(())
107 }
108}
109
110impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
111 #[allow(clippy::type_complexity)]
113 fn construct_block_template(
114 &self,
115 previous_block: &Block<N>,
116 subdag: Option<&Subdag<N>>,
117 candidate_ratifications: Vec<Ratify<N>>,
118 candidate_solutions: Vec<ProverSolution<N>>,
119 candidate_transactions: Vec<Transaction<N>>,
120 ) -> Result<(Header<N>, Ratifications<N>, Option<CoinbaseSolution<N>>, Transactions<N>, Vec<N::TransactionID>)>
121 {
122 let (solutions, solutions_root, combined_proof_target) = match candidate_solutions.is_empty() {
124 true => (None, Field::<N>::zero(), 0u128),
125 false => {
126 let coinbase_verifying_key = self.coinbase_puzzle.coinbase_verifying_key();
128 let latest_epoch_challenge = self.latest_epoch_challenge()?;
130 let verification_results: Vec<_> = cfg_into_iter!(candidate_solutions)
133 .map(|solution| {
134 (
135 solution,
136 solution
137 .verify(coinbase_verifying_key, &latest_epoch_challenge, self.latest_proof_target())
138 .unwrap_or(false),
139 )
140 })
141 .collect();
142 let mut valid_candidate_solutions = Vec::with_capacity(N::MAX_SOLUTIONS);
144 let mut aborted_candidate_solutions = Vec::new();
145 for (solution, is_valid) in verification_results.into_iter() {
146 if is_valid && valid_candidate_solutions.len() < N::MAX_SOLUTIONS {
147 valid_candidate_solutions.push(solution);
148 } else {
149 aborted_candidate_solutions.push(solution);
150 }
151 }
152
153 match valid_candidate_solutions.is_empty() {
155 true => (None, Field::<N>::zero(), 0u128),
156 false => {
157 let solutions = CoinbaseSolution::new(valid_candidate_solutions)?;
159 let solutions_root = solutions.to_accumulator_point()?;
161 let combined_proof_target = solutions.to_combined_proof_target()?;
163 (Some(solutions), solutions_root, combined_proof_target)
165 }
166 }
167 }
168 };
169
170 let latest_state_root = self.latest_state_root();
172 let latest_cumulative_proof_target = previous_block.cumulative_proof_target();
174 let latest_coinbase_target = previous_block.coinbase_target();
176
177 let next_round = match subdag {
179 Some(subdag) => subdag.anchor_round(),
180 None => previous_block.round().saturating_add(1),
181 };
182 let next_height = previous_block.height().saturating_add(1);
184 let next_timestamp = match subdag {
186 Some(subdag) => subdag.timestamp(),
187 None => OffsetDateTime::now_utc().unix_timestamp(),
188 };
189 let next_cumulative_weight = previous_block.cumulative_weight().saturating_add(combined_proof_target);
191 let next_cumulative_proof_target = latest_cumulative_proof_target.saturating_add(combined_proof_target);
193 let is_coinbase_target_reached = next_cumulative_proof_target >= latest_coinbase_target as u128;
195 let next_cumulative_proof_target = match is_coinbase_target_reached {
197 true => 0,
198 false => next_cumulative_proof_target,
199 };
200 let next_coinbase_target = coinbase_target(
202 previous_block.last_coinbase_target(),
203 previous_block.last_coinbase_timestamp(),
204 next_timestamp,
205 N::ANCHOR_TIME,
206 N::NUM_BLOCKS_PER_EPOCH,
207 N::GENESIS_COINBASE_TARGET,
208 )?;
209 let next_proof_target = proof_target(next_coinbase_target, N::GENESIS_PROOF_TARGET);
211
212 let (next_last_coinbase_target, next_last_coinbase_timestamp) = match is_coinbase_target_reached {
214 true => (next_coinbase_target, next_timestamp),
215 false => (previous_block.last_coinbase_target(), previous_block.last_coinbase_timestamp()),
216 };
217
218 let coinbase_reward = coinbase_reward(
220 next_height,
221 N::STARTING_SUPPLY,
222 N::ANCHOR_HEIGHT,
223 N::BLOCK_TIME,
224 combined_proof_target,
225 u64::try_from(latest_cumulative_proof_target)?,
226 latest_coinbase_target,
227 )?;
228
229 let state = FinalizeGlobalState::new::<N>(
231 next_round,
232 next_height,
233 next_cumulative_weight,
234 next_cumulative_proof_target,
235 previous_block.hash(),
236 )?;
237 let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) = self.vm.speculate(
239 state,
240 Some(coinbase_reward),
241 candidate_ratifications,
242 solutions.as_ref(),
243 candidate_transactions.iter(),
244 )?;
245
246 let ratifications_root = ratifications.to_ratifications_root()?;
248
249 let subdag_root = match subdag {
251 Some(subdag) => subdag.to_subdag_root()?,
252 None => Field::zero(),
253 };
254
255 let metadata = Metadata::new(
257 N::ID,
258 next_round,
259 next_height,
260 next_cumulative_weight,
261 next_cumulative_proof_target,
262 next_coinbase_target,
263 next_proof_target,
264 next_last_coinbase_target,
265 next_last_coinbase_timestamp,
266 next_timestamp,
267 )?;
268
269 let header = Header::from(
271 latest_state_root,
272 transactions.to_transactions_root()?,
273 transactions.to_finalize_root(ratified_finalize_operations)?,
274 ratifications_root,
275 solutions_root,
276 subdag_root,
277 metadata,
278 )?;
279
280 Ok((header, ratifications, solutions, transactions, aborted_transaction_ids))
282 }
283}