1pub use super::BlockHeight;
2use crate::{
3 common::{
4 fuel_crypto::Hasher,
5 fuel_tx::{
6 Bytes32,
7 Input,
8 Transaction,
9 UniqueIdentifier,
10 },
11 fuel_types::{
12 bytes::SerializableVec,
13 Address,
14 },
15 },
16 model::DaBlockHeight,
17};
18use derive_more::{
19 AsRef,
20 Display,
21 From,
22 FromStr,
23 Into,
24 LowerHex,
25 UpperHex,
26};
27use fuel_vm::{
28 fuel_crypto,
29 fuel_merkle,
30 fuel_types::MessageId,
31 prelude::Signature,
32};
33use tai64::Tai64;
34
35#[derive(
37 Clone,
38 Copy,
39 Debug,
40 PartialEq,
41 Eq,
42 PartialOrd,
43 Ord,
44 Hash,
45 Default,
46 FromStr,
47 From,
48 Into,
49 LowerHex,
50 UpperHex,
51 Display,
52 AsRef,
53)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
55#[cfg_attr(feature = "serde", serde(transparent))]
56#[repr(transparent)]
57pub struct BlockId(Bytes32);
58
59impl BlockId {
60 pub fn into_message(self) -> fuel_crypto::Message {
62 unsafe { fuel_crypto::Message::from_bytes_unchecked(*self.0) }
64 }
67
68 pub fn as_message(&self) -> &fuel_crypto::Message {
69 unsafe { fuel_crypto::Message::as_ref_unchecked(self.0.as_slice()) }
71 }
74}
75
76#[derive(Clone, Debug)]
77#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
78#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
79pub struct FuelBlockHeader {
82 pub application: FuelApplicationHeader<GeneratedApplicationFields>,
84 pub consensus: FuelConsensusHeader<GeneratedConsensusFields>,
86 #[cfg_attr(feature = "serde", serde(skip))]
88 pub metadata: Option<HeaderMetadata>,
89}
90
91#[derive(Clone, Debug)]
92#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
93pub struct PartialFuelBlockHeader {
96 pub application: FuelApplicationHeader<Empty>,
98 pub consensus: FuelConsensusHeader<Empty>,
100 pub metadata: Option<HeaderMetadata>,
102}
103
104#[derive(Clone, Copy, Debug, Default)]
105pub struct Empty;
107
108#[derive(Clone, Debug)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
111pub struct FuelApplicationHeader<Generated> {
114 pub da_height: DaBlockHeight,
121 pub generated: Generated,
123}
124
125#[derive(Clone, Debug)]
126#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
127#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
128pub struct GeneratedApplicationFields {
131 pub transactions_count: u64,
133 pub output_messages_count: u64,
135 pub transactions_root: Bytes32,
137 pub output_messages_root: Bytes32,
139}
140
141#[derive(Clone, Debug)]
142#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
143pub struct FuelConsensusHeader<Generated> {
147 pub prev_root: Bytes32,
149 pub height: BlockHeight,
151 pub time: Tai64,
153 pub generated: Generated,
155}
156
157#[derive(Clone, Debug)]
158#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
159#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
160pub struct GeneratedConsensusFields {
163 pub application_hash: Bytes32,
165}
166
167#[derive(Clone, Debug)]
168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
169pub struct HeaderMetadata {
171 id: BlockId,
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
176pub enum ConsensusType {
177 PoA,
178}
179
180impl FuelBlockHeader {
182 pub fn prev_root(&self) -> &Bytes32 {
184 &self.as_ref().prev_root
185 }
186 pub fn height(&self) -> &BlockHeight {
188 &self.as_ref().height
189 }
190 pub fn time(&self) -> Tai64 {
192 self.as_ref().time
193 }
194 pub fn application_hash(&self) -> &Bytes32 {
196 &self.as_ref().application_hash
197 }
198
199 pub fn consensus_type(&self) -> ConsensusType {
201 ConsensusType::PoA
202 }
203}
204
205impl PartialFuelBlockHeader {
207 pub fn prev_root(&self) -> &Bytes32 {
209 &self.as_ref().prev_root
210 }
211 pub fn height(&self) -> &BlockHeight {
213 &self.as_ref().height
214 }
215 pub fn time(&self) -> &Tai64 {
217 &self.as_ref().time
218 }
219 pub fn consensus_type(&self) -> ConsensusType {
221 ConsensusType::PoA
222 }
223}
224
225impl FuelBlockHeader {
226 pub fn recalculate_metadata(&mut self) {
228 self.metadata = Some(HeaderMetadata { id: self.hash() });
229 }
230
231 pub fn hash(&self) -> BlockId {
233 self.consensus.hash()
235 }
236
237 pub fn id(&self) -> BlockId {
239 if let Some(ref metadata) = self.metadata {
240 metadata.id
241 } else {
242 self.hash()
243 }
244 }
245}
246
247impl PartialFuelBlockHeader {
248 pub fn generate(
261 self,
262 transactions: &[Vec<u8>],
263 message_ids: &[MessageId],
264 ) -> FuelBlockHeader {
265 let mut transaction_tree = fuel_merkle::binary::in_memory::MerkleTree::new();
267 for id in transactions {
268 transaction_tree.push(id.as_ref());
269 }
270 let transactions_root = transaction_tree.root().into();
271
272 let mut message_tree = fuel_merkle::binary::in_memory::MerkleTree::new();
274 for id in message_ids {
275 message_tree.push(id.as_ref());
276 }
277 let output_messages_root = message_tree.root().into();
278
279 let application = FuelApplicationHeader {
280 da_height: self.application.da_height,
281 generated: GeneratedApplicationFields {
282 transactions_count: transactions.len() as u64,
283 output_messages_count: message_ids.len() as u64,
284 transactions_root,
285 output_messages_root,
286 },
287 };
288
289 let application_hash = application.hash();
291 let mut header = FuelBlockHeader {
292 application,
293 consensus: FuelConsensusHeader {
294 prev_root: self.consensus.prev_root,
295 height: self.consensus.height,
296 time: self.consensus.time,
297 generated: GeneratedConsensusFields { application_hash },
298 },
299 metadata: None,
300 };
301
302 header.recalculate_metadata();
304 header
305 }
306
307 fn without_generated(self) -> FuelBlockHeader {
310 FuelBlockHeader {
311 application: FuelApplicationHeader {
312 da_height: self.application.da_height,
313 generated: GeneratedApplicationFields {
314 transactions_count: Default::default(),
315 output_messages_count: Default::default(),
316 transactions_root: Default::default(),
317 output_messages_root: Default::default(),
318 },
319 },
320 consensus: FuelConsensusHeader {
321 prev_root: self.consensus.prev_root,
322 height: self.consensus.height,
323 time: self.consensus.time,
324 generated: GeneratedConsensusFields {
325 application_hash: Default::default(),
326 },
327 },
328 metadata: None,
329 }
330 }
331}
332
333impl FuelApplicationHeader<GeneratedApplicationFields> {
334 fn hash(&self) -> Bytes32 {
336 let mut hasher = Hasher::default();
338 hasher.input(&self.da_height.to_bytes()[..]);
339 hasher.input(self.transactions_count.to_be_bytes());
340 hasher.input(self.output_messages_count.to_be_bytes());
341 hasher.input(self.transactions_root.as_ref());
342 hasher.input(self.output_messages_root.as_ref());
343 hasher.digest()
344 }
345}
346
347impl FuelConsensusHeader<GeneratedConsensusFields> {
348 fn hash(&self) -> BlockId {
350 let mut hasher = Hasher::default();
352 hasher.input(self.prev_root.as_ref());
353 hasher.input(&self.height.to_bytes()[..]);
354 hasher.input(self.time.0.to_be_bytes());
355 hasher.input(self.application_hash.as_ref());
356 BlockId(hasher.digest())
357 }
358}
359
360impl core::ops::Deref for FuelBlockHeader {
361 type Target = FuelApplicationHeader<GeneratedApplicationFields>;
362
363 fn deref(&self) -> &Self::Target {
364 &self.application
365 }
366}
367
368impl core::ops::Deref for PartialFuelBlockHeader {
369 type Target = FuelApplicationHeader<Empty>;
370
371 fn deref(&self) -> &Self::Target {
372 &self.application
373 }
374}
375
376impl core::ops::Deref for FuelApplicationHeader<GeneratedApplicationFields> {
377 type Target = GeneratedApplicationFields;
378
379 fn deref(&self) -> &Self::Target {
380 &self.generated
381 }
382}
383
384impl core::ops::Deref for FuelConsensusHeader<GeneratedConsensusFields> {
385 type Target = GeneratedConsensusFields;
386
387 fn deref(&self) -> &Self::Target {
388 &self.generated
389 }
390}
391
392impl core::convert::AsRef<FuelConsensusHeader<GeneratedConsensusFields>>
393 for FuelBlockHeader
394{
395 fn as_ref(&self) -> &FuelConsensusHeader<GeneratedConsensusFields> {
396 &self.consensus
397 }
398}
399
400impl core::convert::AsRef<FuelConsensusHeader<Empty>> for PartialFuelBlockHeader {
401 fn as_ref(&self) -> &FuelConsensusHeader<Empty> {
402 &self.consensus
403 }
404}
405
406#[derive(Clone, Debug)]
408#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
409#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
410pub struct FuelBlockDb {
411 pub header: FuelBlockHeader,
412 pub transactions: Vec<Bytes32>,
413}
414
415impl FuelBlockDb {
416 pub fn id(&self) -> BlockId {
418 self.header.id()
419 }
420
421 pub fn consensus_type(&self) -> ConsensusType {
423 self.header.consensus_type()
424 }
425}
426
427#[derive(Clone, Debug)]
429#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
430#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
431pub struct FuelBlock {
432 header: FuelBlockHeader,
434 transactions: Vec<Transaction>,
436}
437
438#[derive(Clone, Debug)]
445pub struct PartialFuelBlock {
446 pub header: PartialFuelBlockHeader,
448 pub transactions: Vec<Transaction>,
451}
452
453impl FuelBlock {
454 pub fn new(
465 header: PartialFuelBlockHeader,
466 mut transactions: Vec<Transaction>,
467 message_ids: &[MessageId],
468 ) -> Self {
469 let transaction_ids: Vec<_> =
472 transactions.iter_mut().map(|tx| tx.to_bytes()).collect();
473 Self {
474 header: header.generate(&transaction_ids[..], message_ids),
475 transactions,
476 }
477 }
478
479 pub fn id(&self) -> BlockId {
481 self.header.id()
482 }
483
484 pub fn to_db_block(&self) -> FuelBlockDb {
486 FuelBlockDb {
487 header: self.header.clone(),
488 transactions: self.transactions.iter().map(|tx| tx.id()).collect(),
489 }
490 }
491
492 pub fn from_db_block(db_block: FuelBlockDb, transactions: Vec<Transaction>) -> Self {
494 Self {
497 header: db_block.header,
498 transactions,
499 }
500 }
501
502 pub fn transactions(&self) -> &[Transaction] {
504 &self.transactions[..]
505 }
506
507 pub fn header(&self) -> &FuelBlockHeader {
509 &self.header
510 }
511
512 #[cfg(any(test, feature = "test-helpers"))]
513 pub fn transactions_mut(&mut self) -> &mut Vec<Transaction> {
514 &mut self.transactions
515 }
516
517 #[cfg(any(test, feature = "test-helpers"))]
518 pub fn header_mut(&mut self) -> &mut FuelBlockHeader {
519 &mut self.header
520 }
521}
522
523impl PartialFuelBlock {
524 pub fn new(header: PartialFuelBlockHeader, transactions: Vec<Transaction>) -> Self {
525 Self {
526 header,
527 transactions,
528 }
529 }
530
531 pub fn to_partial_db_block(&self) -> FuelBlockDb {
537 FuelBlockDb {
538 header: self.header.clone().without_generated(),
539 transactions: self.transactions.iter().map(|tx| tx.id()).collect(),
540 }
541 }
542
543 pub fn generate(self, message_ids: &[MessageId]) -> FuelBlock {
551 FuelBlock::new(self.header, self.transactions, message_ids)
552 }
553}
554
555impl From<FuelBlock> for PartialFuelBlock {
556 fn from(block: FuelBlock) -> Self {
557 let FuelBlock {
558 header:
559 FuelBlockHeader {
560 application: FuelApplicationHeader { da_height, .. },
561 consensus:
562 FuelConsensusHeader {
563 prev_root,
564 height,
565 time,
566 ..
567 },
568 ..
569 },
570 transactions,
571 } = block;
572 Self {
573 header: PartialFuelBlockHeader {
574 application: FuelApplicationHeader {
575 da_height,
576 generated: Empty {},
577 },
578 consensus: FuelConsensusHeader {
579 prev_root,
580 height,
581 time,
582 generated: Empty {},
583 },
584 metadata: None,
585 },
586 transactions,
587 }
588 }
589}
590
591#[derive(Clone, Debug, Default)]
596#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
597pub struct Genesis {
598 pub chain_config_hash: Bytes32,
601 pub coins_root: Bytes32,
603 pub contracts_root: Bytes32,
605 pub messages_root: Bytes32,
607}
608
609#[derive(Clone, Debug)]
610#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
611pub enum FuelBlockConsensus {
614 PoA(FuelBlockPoAConsensus),
615 Genesis(Genesis),
617}
618
619impl FuelBlockConsensus {
620 pub fn block_producer(&self, block_id: &BlockId) -> anyhow::Result<Address> {
622 match &self {
623 FuelBlockConsensus::Genesis(_) => Ok(Address::zeroed()),
624 FuelBlockConsensus::PoA(poa_data) => {
625 let public_key = poa_data.signature.recover(block_id.as_message())?;
626 let address = Input::owner(&public_key);
627 Ok(address)
628 }
629 }
630 }
631}
632
633#[derive(Clone, Debug, Default)]
634#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
635pub struct FuelBlockPoAConsensus {
638 pub signature: Signature,
640}
641
642#[cfg(any(test, feature = "test-helpers"))]
643impl<T> Default for FuelConsensusHeader<T>
644where
645 T: Default,
646{
647 fn default() -> Self {
648 Self {
649 time: Tai64::UNIX_EPOCH,
650 height: BlockHeight::default(),
651 prev_root: Bytes32::default(),
652 generated: Default::default(),
653 }
654 }
655}
656
657#[cfg(any(test, feature = "test-helpers"))]
658impl Default for FuelBlockConsensus {
659 fn default() -> Self {
660 FuelBlockConsensus::PoA(Default::default())
661 }
662}
663
664#[derive(Clone, Debug)]
665#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
666#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
667pub struct SealedFuelBlock {
669 pub block: FuelBlock,
670 pub consensus: FuelBlockConsensus,
671}
672
673impl FuelBlockPoAConsensus {
674 pub fn new(signature: Signature) -> Self {
676 Self { signature }
677 }
678}
679
680#[derive(Clone, Debug)]
681#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
682#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
683pub struct SealedFuelBlockHeader {
685 pub header: FuelBlockHeader,
686 pub consensus: FuelBlockConsensus,
687}