1use std::borrow::Borrow;
2use std::collections::BTreeMap;
3use std::fmt;
4use std::sync::Arc;
5
6use anyhow::Result;
7pub(crate) use processed_upto::{ProcessedUptoInfoExtension, ProcessedUptoInfoStuff};
8use serde::{Deserialize, Serialize};
9use tycho_block_util::block::{BlockStuffAug, ValidatorSubsetInfo};
10use tycho_block_util::queue::{QueueDiffStuffAug, QueueKey, QueuePartitionIdx};
11use tycho_block_util::state::{RefMcStateHandle, ShardStateStuff};
12use tycho_crypto::ed25519::KeyPair;
13use tycho_network::PeerId;
14use tycho_types::models::*;
15use tycho_types::prelude::*;
16use tycho_util::FastHashMap;
17use tycho_util::config::PartialConfig;
18
19use crate::collator::ForceMasterCollation;
20use crate::mempool::MempoolAnchorId;
21use crate::utils::block::detect_top_processed_to_anchor;
22use crate::validator::ValidationSessionId;
23
24pub mod processed_upto;
25
26#[derive(Debug, Clone, Serialize, Deserialize, PartialConfig)]
27#[serde(default)]
28pub struct CollatorConfig {
29 #[serde(skip_deserializing, skip_serializing)]
33 pub supported_block_version: u32,
34
35 #[serde(skip_deserializing, skip_serializing)]
41 pub supported_capabilities: GlobalCapabilities,
42
43 pub min_mc_block_delta_from_bc_to_sync: u32,
47 #[important]
51 pub check_value_flow: bool,
52 #[important]
56 pub validate_config: bool,
57 #[important]
61 pub fast_sync: bool,
62 pub accounts_split_depth: u8,
66 pub merkle_split_depth: u8,
70}
71
72impl Default for CollatorConfig {
73 fn default() -> Self {
74 Self {
75 supported_block_version: 100,
76 supported_capabilities: supported_capabilities(),
77 min_mc_block_delta_from_bc_to_sync: 3,
78 check_value_flow: false,
79 validate_config: true,
80 fast_sync: true,
81 accounts_split_depth: 4,
82 merkle_split_depth: 5,
83 }
84 }
85}
86
87pub fn supported_capabilities() -> GlobalCapabilities {
88 GlobalCapabilities::from([
89 GlobalCapability::CapCreateStatsEnabled,
90 GlobalCapability::CapBounceMsgBody,
91 GlobalCapability::CapReportVersion,
92 GlobalCapability::CapShortDequeue,
93 GlobalCapability::CapInitCodeHash,
94 GlobalCapability::CapOffHypercube,
95 GlobalCapability::CapFixTupleIndexBug,
96 GlobalCapability::CapFastStorageStat,
97 GlobalCapability::CapMyCode,
98 GlobalCapability::CapFullBodyInBounced,
99 GlobalCapability::CapStorageFeeToTvm,
100 GlobalCapability::CapWorkchains,
101 GlobalCapability::CapStcontNewFormat,
102 GlobalCapability::CapFastStorageStatBugfix,
103 GlobalCapability::CapResolveMerkleCell,
104 GlobalCapability::CapFeeInGasUnits,
105 GlobalCapability::CapSignatureWithId,
106 GlobalCapability::CapBounceAfterFailedAction,
107 GlobalCapability::CapSuspendedList,
108 GlobalCapability::CapsTvmBugfixes2022,
109 GlobalCapability::CapSuspendByMarks,
110 GlobalCapability::CapOmitMasterBlockHistory,
111 GlobalCapability::CapSignatureDomain,
112 ])
113}
114
115pub struct BlockCollationResult {
116 pub collation_session_id: CollationSessionId,
117 pub candidate: Box<BlockCandidate>,
118 pub prev_mc_block_id: BlockId,
119 pub mc_data: Option<Arc<McData>>,
120 pub collation_config: Arc<CollationConfig>,
121 pub force_next_mc_block: ForceMasterCollation,
122 pub has_processed_externals: bool,
124}
125
126#[derive(Debug)]
127pub struct McData {
128 pub global_id: i32,
129 pub block_id: BlockId,
130
131 pub prev_key_block_seqno: u32,
133
134 pub gen_lt: u64,
135 pub gen_chain_time: u64,
136 pub libraries: Dict<HashBytes, LibDescr>,
137
138 pub total_validator_fees: CurrencyCollection,
139
140 pub global_balance: CurrencyCollection,
141 pub shards: Vec<(ShardIdent, ShardDescriptionShort)>,
142 pub config: BlockchainConfig,
143 pub validator_info: ValidatorInfo,
144 pub consensus_info: ConsensusInfo,
145
146 pub processed_upto: ProcessedUptoInfoStuff,
147
148 pub top_processed_to_anchor: MempoolAnchorId,
151
152 pub ref_mc_state_handle: RefMcStateHandle,
153
154 pub shards_processed_to_by_partitions: FastHashMap<ShardIdent, (bool, ProcessedToByPartitions)>,
155
156 pub prev_mc_data: Option<PrevMcData>,
158}
159
160impl McData {
161 pub fn load_from_state(
165 state_stuff: &ShardStateStuff,
166 prev_mc_state: Option<&ShardStateStuff>,
167 all_shards_processed_to_by_partitions: FastHashMap<
168 ShardIdent,
169 (bool, ProcessedToByPartitions),
170 >,
171 ) -> Result<Arc<Self>> {
172 let block_id = *state_stuff.block_id();
173 let extra = state_stuff.state_extra()?;
174 let state = state_stuff.as_ref();
175
176 let prev_key_block_seqno = if extra.after_key_block {
177 block_id.seqno
178 } else if let Some(block_ref) = &extra.last_key_block {
179 block_ref.seqno
180 } else {
181 0
182 };
183
184 let processed_upto: ProcessedUptoInfoStuff = state.processed_upto.load()?.try_into()?;
185
186 let shards = extra.shards.as_vec()?;
187 let top_processed_to_anchor = detect_top_processed_to_anchor(
188 shards.iter().map(|(_, d)| *d),
189 processed_upto.get_min_externals_processed_to()?.0,
190 );
191
192 let shards_processed_to_by_partitions = all_shards_processed_to_by_partitions
193 .into_iter()
194 .filter(|(shard_id, _)| !shard_id.is_masterchain())
195 .collect();
196
197 let prev_mc_data = if let Some(prev_mc_state) = prev_mc_state {
198 let prev_extra = prev_mc_state.state_extra()?;
199 Some(PrevMcData {
200 shards: prev_extra.shards.as_vec()?,
201 consensus_info: prev_extra.consensus_info,
202 })
203 } else {
204 None
205 };
206
207 Ok(Arc::new(Self {
208 global_id: state.global_id,
209 block_id,
210
211 prev_key_block_seqno,
212 gen_lt: state.gen_lt,
213 gen_chain_time: state_stuff.get_gen_chain_time(),
214 libraries: state.libraries.clone(),
215 total_validator_fees: state.total_validator_fees.clone(),
216
217 global_balance: extra.global_balance.clone(),
218 shards,
219 config: extra.config.clone(),
220 validator_info: extra.validator_info,
221 consensus_info: extra.consensus_info,
222
223 processed_upto,
224 top_processed_to_anchor,
225
226 ref_mc_state_handle: state_stuff.ref_mc_state_handle().clone(),
227 shards_processed_to_by_partitions,
228
229 prev_mc_data,
230 }))
231 }
232
233 pub fn make_block_ref(&self) -> BlockRef {
234 BlockRef {
235 end_lt: self.gen_lt,
236 seqno: self.block_id.seqno,
237 root_hash: self.block_id.root_hash,
238 file_hash: self.block_id.file_hash,
239 }
240 }
241
242 pub fn lt_align(&self) -> u64 {
243 1000000
244 }
245
246 pub fn get_blocks_count_between_masters(&self, current_shard: &ShardIdent) -> u64 {
247 if current_shard.is_masterchain() {
248 1
249 } else {
250 let seqno_from_last_mc_data = self
251 .shards
252 .iter()
253 .find(|(s, _)| s == current_shard)
254 .map(|(_, descr)| descr.seqno);
255 let seqno_from_prev_mc_data = self.prev_mc_data.as_ref().and_then(|prev| {
256 prev.shards
257 .iter()
258 .find(|(s, _)| s == current_shard)
259 .map(|(_, descr)| descr.seqno)
260 });
261
262 match (seqno_from_last_mc_data, seqno_from_prev_mc_data) {
263 (Some(seqno_from_last_mc_data), Some(seqno_from_prev_mc_data)) => {
264 seqno_from_last_mc_data.saturating_sub(seqno_from_prev_mc_data) as u64
265 }
266 _ => 0,
267 }
268 }
269 }
270}
271
272#[derive(Debug)]
273pub struct PrevMcData {
274 pub shards: Vec<(ShardIdent, ShardDescriptionShort)>,
275 pub consensus_info: ConsensusInfo,
276}
277
278#[derive(Clone)]
279pub struct BlockCandidate {
280 pub ref_by_mc_seqno: u32,
281 pub block: BlockStuffAug,
282 pub is_key_block: bool,
283 pub consensus_config_changed: Option<bool>,
286 pub prev_blocks_ids: Vec<BlockId>,
287 pub top_shard_blocks_ids: Vec<BlockId>,
288 pub collated_file_hash: HashBytes,
289 pub chain_time: u64,
290 pub processed_to_anchor_id: u32,
291 pub value_flow: ValueFlow,
292 pub created_by: HashBytes,
293 pub queue_diff_aug: QueueDiffStuffAug,
294 pub consensus_info: ConsensusInfo,
295 pub processed_upto: ProcessedUptoInfoStuff,
296}
297
298#[derive(Default, Clone)]
299pub struct BlockSignatures {
300 pub signatures: FastHashMap<HashBytes, ArcSignature>,
301}
302
303pub type ArcSignature = Arc<[u8; 64]>;
304
305pub struct ValidatedBlock {
306 block: BlockId,
307 signatures: BlockSignatures,
308 valid: bool,
309}
310
311impl ValidatedBlock {
312 pub fn new(block: BlockId, signatures: BlockSignatures, valid: bool) -> Self {
313 Self {
314 block,
315 signatures,
316 valid,
317 }
318 }
319
320 pub fn id(&self) -> &BlockId {
321 &self.block
322 }
323
324 pub fn signatures(&self) -> &BlockSignatures {
325 &self.signatures
326 }
327
328 pub fn is_valid(&self) -> bool {
329 self.valid
330 }
331 pub fn extract_signatures(self) -> BlockSignatures {
332 self.signatures
333 }
334}
335
336pub struct BlockStuffForSync {
337 pub ref_by_mc_seqno: u32,
339
340 pub block_stuff_aug: BlockStuffAug,
341 pub queue_diff_aug: QueueDiffStuffAug,
342 pub signatures: FastHashMap<PeerId, ArcSignature>,
343 pub total_signature_weight: u64,
344 pub prev_blocks_ids: Vec<BlockId>,
345 pub top_shard_blocks_ids: Vec<BlockId>,
346
347 pub consensus_info: ConsensusInfo,
348}
349
350pub(crate) type CollationSessionId = (ShardIdent, u32, u32);
352
353#[derive(Clone)]
354pub struct CollationSessionInfo {
355 shard: ShardIdent,
356 seqno: u32,
358 collators: ValidatorSubsetInfo,
359 current_collator_keypair: Option<Arc<KeyPair>>,
360}
361impl CollationSessionInfo {
362 pub fn new(
363 shard: ShardIdent,
364 seqno: u32,
365 collators: ValidatorSubsetInfo,
366 current_collator_keypair: Option<Arc<KeyPair>>,
367 ) -> Self {
368 Self {
369 shard,
370 seqno,
371 collators,
372 current_collator_keypair,
373 }
374 }
375
376 pub fn id(&self) -> CollationSessionId {
377 (self.shard, self.seqno, self.collators.short_hash)
378 }
379
380 pub fn get_validation_session_id(&self) -> ValidationSessionId {
381 (self.seqno, self.collators.short_hash)
382 }
383
384 pub fn shard(&self) -> ShardIdent {
385 self.shard
386 }
387 pub fn seqno(&self) -> u32 {
388 self.seqno
389 }
390
391 pub fn collators(&self) -> &ValidatorSubsetInfo {
392 &self.collators
393 }
394
395 pub fn current_collator_keypair(&self) -> Option<&Arc<KeyPair>> {
396 self.current_collator_keypair.as_ref()
397 }
398}
399impl fmt::Debug for CollationSessionInfo {
400 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 f.debug_struct("CollationSessionInfo")
402 .field("shard", &self.shard)
403 .field("seqno", &self.seqno)
404 .field("collators", &self.collators)
405 .field(
406 "current_collator_pubkey",
407 &self
408 .current_collator_keypair
409 .as_ref()
410 .map(|kp| kp.public_key),
411 )
412 .finish()
413 }
414}
415
416pub trait IntAdrExt {
417 fn get_address(&self) -> HashBytes;
418}
419impl IntAdrExt for IntAddr {
420 fn get_address(&self) -> HashBytes {
421 match self {
422 Self::Std(std_addr) => std_addr.address,
423 Self::Var(var_addr) => HashBytes::from_slice(var_addr.address.as_slice()),
424 }
425 }
426}
427
428#[derive(Debug, Clone)]
429pub struct TopBlockDescription {
430 pub block_id: BlockId,
431 pub block_info: BlockInfo,
432 pub processed_to_anchor_id: u32,
433 pub value_flow: ValueFlow,
434 pub proof_funds: ShardFeeCreated,
435 #[cfg(feature = "block-creator-stats")]
436 pub creators: Vec<HashBytes>,
437 pub processed_to_by_partitions: ProcessedToByPartitions,
438}
439
440#[derive(Debug, Clone)]
441pub struct TopShardBlockInfo {
442 pub block_id: BlockId,
443 pub processed_to_by_partitions: ProcessedToByPartitions,
444}
445
446pub type ProcessedTo = BTreeMap<ShardIdent, QueueKey>;
447pub type ProcessedToByPartitions = FastHashMap<QueuePartitionIdx, ProcessedTo>;
448
449#[derive(Debug, Clone)]
450pub struct TopBlockIdUpdated {
451 pub block: TopBlockId,
452 pub updated: bool,
453}
454
455#[derive(Debug, Clone)]
456pub struct TopBlockId {
457 pub ref_by_mc_seqno: u32,
458 pub block_id: BlockId,
459}
460
461#[derive(Debug, Clone)]
462pub struct ShardPair {
463 pub shard_ident: ShardIdent,
464 pub prev_block_id: TopBlockId,
465 pub current_block_id: TopBlockId,
466}
467
468#[derive(Debug)]
469pub struct ShortAddr {
470 workchain: i32,
471 prefix: u64,
472}
473
474impl ShortAddr {
475 pub fn new(workchain: i32, prefix: u64) -> Self {
476 Self { workchain, prefix }
477 }
478}
479
480impl Addr for ShortAddr {
481 fn workchain(&self) -> i32 {
482 self.workchain
483 }
484
485 fn prefix(&self) -> u64 {
486 self.prefix
487 }
488}
489
490pub trait BlockIdExt {
491 fn get_next_id_short(&self) -> BlockIdShort;
492}
493impl BlockIdExt for BlockId {
494 fn get_next_id_short(&self) -> BlockIdShort {
495 BlockIdShort {
496 shard: self.shard,
497 seqno: self.seqno + 1,
498 }
499 }
500}
501impl BlockIdExt for BlockIdShort {
502 fn get_next_id_short(&self) -> BlockIdShort {
503 BlockIdShort {
504 shard: self.shard,
505 seqno: self.seqno + 1,
506 }
507 }
508}
509
510pub trait ShardDescriptionShortExt {
511 fn get_block_id(&self, shard_id: ShardIdent) -> BlockId;
512}
513impl ShardDescriptionShortExt for ShardDescription {
514 fn get_block_id(&self, shard_id: ShardIdent) -> BlockId {
515 BlockId {
516 shard: shard_id,
517 seqno: self.seqno,
518 root_hash: self.root_hash,
519 file_hash: self.file_hash,
520 }
521 }
522}
523
524pub struct DebugDisplay<T>(pub T);
525impl<T: std::fmt::Display> std::fmt::Debug for DebugDisplay<T> {
526 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
527 std::fmt::Display::fmt(&self.0, f)
528 }
529}
530
531pub struct DebugDisplayOpt<T>(pub Option<T>);
532impl<T: std::fmt::Display> std::fmt::Debug for DebugDisplayOpt<T> {
533 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
534 std::fmt::Debug::fmt(&self.0.as_ref().map(DebugDisplay), f)
535 }
536}
537
538pub(super) struct DisplayIter<I>(pub I);
539impl<I> std::fmt::Display for DisplayIter<I>
540where
541 I: Iterator<Item: std::fmt::Display> + Clone,
542{
543 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
544 f.debug_list()
545 .entries(self.0.clone().map(DebugDisplay))
546 .finish()
547 }
548}
549
550pub(super) struct DisplayIntoIter<I>(pub I);
551impl<I> std::fmt::Display for DisplayIntoIter<I>
552where
553 I: IntoIterator<Item: std::fmt::Display> + Clone,
554{
555 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
556 f.debug_list()
557 .entries(self.0.clone().into_iter().map(DebugDisplay))
558 .finish()
559 }
560}
561
562pub(super) struct DebugIter<I>(pub I);
563impl<I> std::fmt::Debug for DebugIter<I>
564where
565 I: Iterator<Item: std::fmt::Debug> + Clone,
566{
567 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
568 f.debug_list().entries(self.0.clone()).finish()
569 }
570}
571
572pub(super) struct DisplayAsShortId<'a>(pub &'a BlockId);
573impl std::fmt::Debug for DisplayAsShortId<'_> {
574 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
575 std::fmt::Display::fmt(self, f)
576 }
577}
578impl std::fmt::Display for DisplayAsShortId<'_> {
579 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
580 write!(f, "{}", self.0.as_short_id())
581 }
582}
583
584pub(super) struct DisplayBlockIdsIter<I>(pub I);
585impl<'a, I> std::fmt::Debug for DisplayBlockIdsIter<I>
586where
587 I: Iterator<Item = &'a BlockId> + Clone,
588{
589 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
590 std::fmt::Display::fmt(self, f)
591 }
592}
593impl<'a, I> std::fmt::Display for DisplayBlockIdsIter<I>
594where
595 I: Iterator<Item = &'a BlockId> + Clone,
596{
597 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
598 f.debug_list()
599 .entries(self.0.clone().map(DisplayAsShortId))
600 .finish()
601 }
602}
603
604pub(super) struct DisplayBlockIdsIntoIter<I>(pub I);
605impl<'a, I> std::fmt::Debug for DisplayBlockIdsIntoIter<I>
606where
607 I: IntoIterator<Item = &'a BlockId> + Clone,
608{
609 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
610 std::fmt::Display::fmt(self, f)
611 }
612}
613impl<'a, I> std::fmt::Display for DisplayBlockIdsIntoIter<I>
614where
615 I: IntoIterator<Item = &'a BlockId> + Clone,
616{
617 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
618 f.debug_list()
619 .entries(self.0.clone().into_iter().map(DisplayAsShortId))
620 .finish()
621 }
622}
623
624#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
625pub struct ShardDescriptionShort {
626 pub ext_processed_to_anchor_id: u32,
627 pub top_sc_block_updated: bool,
628 pub reg_mc_seqno: u32,
629 pub end_lt: u64,
630 pub seqno: u32,
631 pub root_hash: HashBytes,
632 pub file_hash: HashBytes,
633}
634
635impl<BorrowShardDescription: Borrow<ShardDescription>> From<BorrowShardDescription>
636 for ShardDescriptionShort
637{
638 fn from(borrow_shard: BorrowShardDescription) -> ShardDescriptionShort {
639 let shard = borrow_shard.borrow();
640 Self {
641 ext_processed_to_anchor_id: shard.ext_processed_to_anchor_id,
642 top_sc_block_updated: shard.top_sc_block_updated,
643 reg_mc_seqno: shard.reg_mc_seqno,
644 end_lt: shard.end_lt,
645 seqno: shard.seqno,
646 root_hash: shard.root_hash,
647 file_hash: shard.file_hash,
648 }
649 }
650}
651
652impl ShardDescriptionShortExt for ShardDescriptionShort {
653 fn get_block_id(&self, shard_id: ShardIdent) -> BlockId {
654 BlockId {
655 shard: shard_id,
656 seqno: self.seqno,
657 root_hash: self.root_hash,
658 file_hash: self.file_hash,
659 }
660 }
661}
662
663pub trait ShardHashesExt<T> {
664 fn as_vec(&self) -> Result<Vec<(ShardIdent, T)>>;
665}
666impl<T> ShardHashesExt<T> for ShardHashes
667where
668 T: From<ShardDescription>,
669{
670 fn as_vec(&self) -> Result<Vec<(ShardIdent, T)>> {
671 let mut res = vec![];
672 for item in self.iter() {
673 let (shard_id, descr) = item?;
674 res.push((shard_id, descr.into()));
675 }
676 Ok(res)
677 }
678}
679
680pub trait ShardIdentExt {
681 fn contains_prefix(&self, workchain_id: i32, prefix_without_tag: u64) -> bool;
682}
683
684impl ShardIdentExt for ShardIdent {
685 fn contains_prefix(&self, workchain_id: i32, prefix_without_tag: u64) -> bool {
686 if self.workchain() == workchain_id {
687 if self.prefix() == 0x8000_0000_0000_0000u64 {
688 return true;
689 }
690 let shift = 64 - self.prefix_len();
691 return (self.prefix() >> shift) == (prefix_without_tag >> shift);
692 }
693 false
694 }
695}
696
697pub trait SaturatingAddAssign {
698 fn saturating_add_assign(&mut self, rhs: Self);
699}
700
701macro_rules! impl_saturating_add_assign {
702 ($($t:ty),+ $(,)?) => {
703 $(
704 impl SaturatingAddAssign for $t {
705 fn saturating_add_assign(&mut self, rhs: Self) {
706 *self = self.saturating_add(rhs);
707 }
708 }
709 )+
710 };
711}
712
713impl_saturating_add_assign!(u32, u64, usize);