Skip to main content

linera_chain/
block.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2// Copyright (c) Zefchain Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    borrow::Cow,
7    collections::{BTreeMap, BTreeSet},
8    fmt::Debug,
9};
10
11use allocative::Allocative;
12use async_graphql::SimpleObject;
13use linera_base::{
14    crypto::{BcsHashable, CryptoHash},
15    data_types::{Blob, BlockHeight, Epoch, Event, OracleResponse, Timestamp},
16    hashed::Hashed,
17    identifiers::{AccountOwner, BlobId, BlobType, ChainId, EventId, StreamId},
18};
19use linera_execution::{BlobOrigin, BlobState, Operation, OutgoingMessage};
20use serde::{ser::SerializeStruct, Deserialize, Serialize};
21use thiserror::Error;
22
23use crate::{
24    data_types::{
25        BlockExecutionOutcome, IncomingBundle, MessageBundle, OperationResult, OutgoingMessageExt,
26        ProposedBlock, Transaction,
27    },
28    types::CertificateValue,
29};
30
31/// Wrapper around a `Block` that has been validated.
32#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Allocative)]
33#[serde(transparent)]
34pub struct ValidatedBlock(Hashed<Block>);
35
36impl ValidatedBlock {
37    /// Creates a new `ValidatedBlock` from a `Block`.
38    pub fn new(block: Block) -> Self {
39        Self(Hashed::new(block))
40    }
41
42    pub fn from_hashed(block: Hashed<Block>) -> Self {
43        Self(block)
44    }
45
46    pub fn inner(&self) -> &Hashed<Block> {
47        &self.0
48    }
49
50    /// Returns a reference to the [`Block`] contained in this `ValidatedBlock`.
51    pub fn block(&self) -> &Block {
52        self.0.inner()
53    }
54
55    /// Consumes this `ValidatedBlock`, returning the [`Block`] it contains.
56    pub fn into_inner(self) -> Block {
57        self.0.into_inner()
58    }
59
60    pub fn to_log_str(&self) -> &'static str {
61        "validated_block"
62    }
63
64    pub fn chain_id(&self) -> ChainId {
65        self.0.inner().header.chain_id
66    }
67
68    pub fn height(&self) -> BlockHeight {
69        self.0.inner().header.height
70    }
71
72    pub fn epoch(&self) -> Epoch {
73        self.0.inner().header.epoch
74    }
75}
76
77/// Wrapper around a `Block` that has been confirmed.
78#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Allocative)]
79#[serde(transparent)]
80pub struct ConfirmedBlock(Hashed<Block>);
81
82#[async_graphql::Object(cache_control(no_cache))]
83impl ConfirmedBlock {
84    #[graphql(derived(name = "block"))]
85    async fn _block(&self) -> Block {
86        self.0.inner().clone()
87    }
88
89    async fn status(&self) -> String {
90        "confirmed".to_string()
91    }
92
93    async fn hash(&self) -> CryptoHash {
94        self.0.hash()
95    }
96}
97
98impl ConfirmedBlock {
99    pub fn new(block: Block) -> Self {
100        Self(Hashed::new(block))
101    }
102
103    pub fn from_hashed(block: Hashed<Block>) -> Self {
104        Self(block)
105    }
106
107    pub fn inner(&self) -> &Hashed<Block> {
108        &self.0
109    }
110
111    pub fn into_inner(self) -> Hashed<Block> {
112        self.0
113    }
114
115    /// Returns a reference to the `Block` contained in this `ConfirmedBlock`.
116    pub fn block(&self) -> &Block {
117        self.0.inner()
118    }
119
120    /// Consumes this `ConfirmedBlock`, returning the `Block` it contains.
121    pub fn into_block(self) -> Block {
122        self.0.into_inner()
123    }
124
125    pub fn chain_id(&self) -> ChainId {
126        self.0.inner().header.chain_id
127    }
128
129    pub fn height(&self) -> BlockHeight {
130        self.0.inner().header.height
131    }
132
133    pub fn timestamp(&self) -> Timestamp {
134        self.0.inner().header.timestamp
135    }
136
137    pub fn to_log_str(&self) -> &'static str {
138        "confirmed_block"
139    }
140
141    /// Returns whether this block matches the proposal.
142    pub fn matches_proposed_block(&self, block: &ProposedBlock) -> bool {
143        self.block().matches_proposed_block(block)
144    }
145
146    /// Returns a blob state that applies to all blobs used by this block.
147    pub fn to_blob_state(&self, is_stored_block: bool) -> BlobState {
148        BlobState {
149            origin: BlobOrigin::Published {
150                chain_id: self.chain_id(),
151                block_height: self.height(),
152            },
153            last_used_by: is_stored_block.then_some(self.0.hash()),
154            epoch: is_stored_block.then_some(self.epoch()),
155        }
156    }
157}
158
159impl From<Hashed<Block>> for ConfirmedBlock {
160    fn from(block: Hashed<Block>) -> Self {
161        Self::from_hashed(block)
162    }
163}
164
165impl From<Hashed<Block>> for ValidatedBlock {
166    fn from(block: Hashed<Block>) -> Self {
167        Self::from_hashed(block)
168    }
169}
170
171#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Allocative)]
172#[serde(transparent)]
173pub struct Timeout(Hashed<TimeoutInner>);
174
175#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Allocative)]
176#[serde(rename = "Timeout")]
177pub(crate) struct TimeoutInner {
178    chain_id: ChainId,
179    height: BlockHeight,
180    epoch: Epoch,
181}
182
183impl Timeout {
184    pub fn new(chain_id: ChainId, height: BlockHeight, epoch: Epoch) -> Self {
185        let inner = TimeoutInner {
186            chain_id,
187            height,
188            epoch,
189        };
190        Self(Hashed::new(inner))
191    }
192
193    pub fn to_log_str(&self) -> &'static str {
194        "timeout"
195    }
196
197    pub fn chain_id(&self) -> ChainId {
198        self.0.inner().chain_id
199    }
200
201    pub fn height(&self) -> BlockHeight {
202        self.0.inner().height
203    }
204
205    pub fn epoch(&self) -> Epoch {
206        self.0.inner().epoch
207    }
208
209    pub(crate) fn inner(&self) -> &Hashed<TimeoutInner> {
210        &self.0
211    }
212}
213
214impl BcsHashable<'_> for Timeout {}
215impl BcsHashable<'_> for TimeoutInner {}
216
217/// Failure to convert a `Certificate` into one of the expected certificate types.
218#[derive(Clone, Copy, Debug, Error)]
219pub enum ConversionError {
220    /// Failure to convert to [`ConfirmedBlock`] certificate.
221    #[error("Expected a `ConfirmedBlockCertificate` value")]
222    ConfirmedBlock,
223
224    /// Failure to convert to [`ValidatedBlock`] certificate.
225    #[error("Expected a `ValidatedBlockCertificate` value")]
226    ValidatedBlock,
227
228    /// Failure to convert to [`Timeout`] certificate.
229    #[error("Expected a `TimeoutCertificate` value")]
230    Timeout,
231}
232
233/// Block defines the atomic unit of growth of the Linera chain.
234///
235/// As part of the block body, contains all the incoming messages
236/// and operations to execute which define a state transition of the chain.
237/// Resulting messages produced by the operations are also included in the block body,
238/// together with oracle responses and events.
239#[derive(Debug, PartialEq, Eq, Hash, Clone, SimpleObject, Allocative)]
240pub struct Block {
241    /// Header of the block containing metadata of the block.
242    pub header: BlockHeader,
243    /// Body of the block containing all of the data.
244    pub body: BlockBody,
245}
246
247impl Serialize for Block {
248    fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
249        let mut state = serializer.serialize_struct("Block", 2)?;
250
251        let header = SerializedHeader {
252            chain_id: self.header.chain_id,
253            epoch: self.header.epoch,
254            height: self.header.height,
255            timestamp: self.header.timestamp,
256            state_hash: self.header.state_hash,
257            previous_block_hash: self.header.previous_block_hash,
258            authenticated_owner: self.header.authenticated_owner,
259        };
260        state.serialize_field("header", &header)?;
261        state.serialize_field("body", &self.body)?;
262        state.end()
263    }
264}
265
266impl<'de> Deserialize<'de> for Block {
267    fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
268        #[derive(Deserialize)]
269        #[serde(rename = "Block")]
270        struct Inner {
271            header: SerializedHeader,
272            body: BlockBody,
273        }
274        let inner = Inner::deserialize(deserializer)?;
275
276        let transactions_hash = hashing::hash_vec(&inner.body.transactions);
277        let messages_hash = hashing::hash_vec_vec(&inner.body.messages);
278        let previous_message_blocks_hash = CryptoHash::new(&PreviousMessageBlocksMap {
279            inner: Cow::Borrowed(&inner.body.previous_message_blocks),
280        });
281        let previous_event_blocks_hash = CryptoHash::new(&PreviousEventBlocksMap {
282            inner: Cow::Borrowed(&inner.body.previous_event_blocks),
283        });
284        let oracle_responses_hash = hashing::hash_vec_vec(&inner.body.oracle_responses);
285        let events_hash = hashing::hash_vec_vec(&inner.body.events);
286        let blobs_hash = hashing::hash_vec_vec(&inner.body.blobs);
287        let operation_results_hash = hashing::hash_vec(&inner.body.operation_results);
288
289        let header = BlockHeader {
290            chain_id: inner.header.chain_id,
291            epoch: inner.header.epoch,
292            height: inner.header.height,
293            timestamp: inner.header.timestamp,
294            state_hash: inner.header.state_hash,
295            previous_block_hash: inner.header.previous_block_hash,
296            authenticated_owner: inner.header.authenticated_owner,
297            transactions_hash,
298            messages_hash,
299            previous_message_blocks_hash,
300            previous_event_blocks_hash,
301            oracle_responses_hash,
302            events_hash,
303            blobs_hash,
304            operation_results_hash,
305        };
306
307        Ok(Self {
308            header,
309            body: inner.body,
310        })
311    }
312}
313
314/// Succinct representation of a block.
315/// Contains all the metadata to follow the chain of blocks or verifying
316/// inclusion (event, message, oracle response, etc.) in the block's body.
317#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
318pub struct BlockHeader {
319    /// The chain to which this block belongs.
320    pub chain_id: ChainId,
321    /// The number identifying the current configuration.
322    pub epoch: Epoch,
323    /// The block height.
324    pub height: BlockHeight,
325    /// The timestamp when this block was created.
326    pub timestamp: Timestamp,
327    /// The hash of the chain's execution state after this block.
328    pub state_hash: CryptoHash,
329    /// Certified hash of the previous block in the chain, if any.
330    pub previous_block_hash: Option<CryptoHash>,
331    /// The user signing for the operations in the block and paying for their execution
332    /// fees. If set, this must be the `owner` in the block proposal. `None` means that
333    /// the default account of the chain is used. This value is also used as recipient of
334    /// potential refunds for the message grants created by the operations.
335    pub authenticated_owner: Option<AccountOwner>,
336
337    // Inputs to the block, chosen by the block proposer.
338    /// Cryptographic hash of all the transactions in the block.
339    pub transactions_hash: CryptoHash,
340
341    // Outcome of the block execution.
342    /// Cryptographic hash of all the messages in the block.
343    pub messages_hash: CryptoHash,
344    /// Cryptographic hash of the lookup table for previous sending blocks.
345    pub previous_message_blocks_hash: CryptoHash,
346    /// Cryptographic hash of the lookup table for previous blocks publishing events.
347    pub previous_event_blocks_hash: CryptoHash,
348    /// Cryptographic hash of all the oracle responses in the block.
349    pub oracle_responses_hash: CryptoHash,
350    /// Cryptographic hash of all the events in the block.
351    pub events_hash: CryptoHash,
352    /// Cryptographic hash of all the created blobs in the block.
353    pub blobs_hash: CryptoHash,
354    /// A cryptographic hash of the execution results of all operations in a block.
355    pub operation_results_hash: CryptoHash,
356}
357
358/// The body of a block containing all the data included in the block.
359#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
360#[graphql(complex)]
361pub struct BlockBody {
362    /// The transactions to execute in this block. Each transaction can be either
363    /// incoming messages or an operation.
364    #[graphql(skip)]
365    pub transactions: Vec<Transaction>,
366    /// The list of outgoing messages for each transaction.
367    pub messages: Vec<Vec<OutgoingMessage>>,
368    /// The hashes and heights of previous blocks that sent messages to the same recipients.
369    pub previous_message_blocks: BTreeMap<ChainId, (CryptoHash, BlockHeight)>,
370    /// The hashes and heights of previous blocks that published events to the same channels.
371    pub previous_event_blocks: BTreeMap<StreamId, (CryptoHash, BlockHeight)>,
372    /// The record of oracle responses for each transaction.
373    pub oracle_responses: Vec<Vec<OracleResponse>>,
374    /// The list of events produced by each transaction.
375    pub events: Vec<Vec<Event>>,
376    /// The list of blobs produced by each transaction.
377    pub blobs: Vec<Vec<Blob>>,
378    /// The execution result for each operation.
379    pub operation_results: Vec<OperationResult>,
380}
381
382impl BlockBody {
383    /// Returns all operations in this block body.
384    pub fn operations(&self) -> impl Iterator<Item = &Operation> {
385        self.transactions.iter().filter_map(|tx| match tx {
386            Transaction::ExecuteOperation(operation) => Some(operation),
387            Transaction::ReceiveMessages(_) => None,
388        })
389    }
390
391    /// Returns all incoming bundles in this block body.
392    pub fn incoming_bundles(&self) -> impl Iterator<Item = &IncomingBundle> {
393        self.transactions.iter().filter_map(|tx| match tx {
394            Transaction::ReceiveMessages(bundle) => Some(bundle),
395            Transaction::ExecuteOperation(_) => None,
396        })
397    }
398
399    /// Returns whether the first transaction in this block body is a
400    /// `SystemOperation::Checkpoint`. See [`ProposedBlock::starts_with_checkpoint`].
401    pub fn starts_with_checkpoint(&self) -> bool {
402        self.transactions
403            .first()
404            .is_some_and(Transaction::is_checkpoint)
405    }
406}
407
408#[async_graphql::ComplexObject]
409impl BlockBody {
410    /// Metadata about the transactions in this block.
411    async fn transaction_metadata(&self) -> Vec<crate::data_types::TransactionMetadata> {
412        self.transactions
413            .iter()
414            .map(crate::data_types::TransactionMetadata::from_transaction)
415            .collect()
416    }
417}
418
419impl Block {
420    pub fn new(block: ProposedBlock, outcome: BlockExecutionOutcome) -> Self {
421        let transactions_hash = hashing::hash_vec(&block.transactions);
422        let messages_hash = hashing::hash_vec_vec(&outcome.messages);
423        let previous_message_blocks_hash = CryptoHash::new(&PreviousMessageBlocksMap {
424            inner: Cow::Borrowed(&outcome.previous_message_blocks),
425        });
426        let previous_event_blocks_hash = CryptoHash::new(&PreviousEventBlocksMap {
427            inner: Cow::Borrowed(&outcome.previous_event_blocks),
428        });
429        let oracle_responses_hash = hashing::hash_vec_vec(&outcome.oracle_responses);
430        let events_hash = hashing::hash_vec_vec(&outcome.events);
431        let blobs_hash = hashing::hash_vec_vec(&outcome.blobs);
432        let operation_results_hash = hashing::hash_vec(&outcome.operation_results);
433
434        let header = BlockHeader {
435            chain_id: block.chain_id,
436            epoch: block.epoch,
437            height: block.height,
438            timestamp: block.timestamp,
439            state_hash: outcome.state_hash,
440            previous_block_hash: block.previous_block_hash,
441            authenticated_owner: block.authenticated_owner,
442            transactions_hash,
443            messages_hash,
444            previous_message_blocks_hash,
445            previous_event_blocks_hash,
446            oracle_responses_hash,
447            events_hash,
448            blobs_hash,
449            operation_results_hash,
450        };
451
452        let body = BlockBody {
453            transactions: block.transactions,
454            messages: outcome.messages,
455            previous_message_blocks: outcome.previous_message_blocks,
456            previous_event_blocks: outcome.previous_event_blocks,
457            oracle_responses: outcome.oracle_responses,
458            events: outcome.events,
459            blobs: outcome.blobs,
460            operation_results: outcome.operation_results,
461        };
462
463        Self { header, body }
464    }
465
466    /// Returns the bundles of messages sent via the given medium to the specified
467    /// recipient. Messages originating from different transactions of the original block
468    /// are kept in separate bundles. If the medium is a channel, does not verify that the
469    /// recipient is actually subscribed to that channel.
470    pub fn message_bundles_for(
471        &self,
472        recipient: ChainId,
473        certificate_hash: CryptoHash,
474    ) -> impl Iterator<Item = (Epoch, MessageBundle)> + '_ {
475        let block_height = self.header.height;
476        let block_timestamp = self.header.timestamp;
477        let block_epoch = self.header.epoch;
478
479        (0u32..)
480            .zip(self.messages())
481            .filter_map(move |(transaction_index, txn_messages)| {
482                let messages = txn_messages
483                    .iter()
484                    .filter(|message| message.destination == recipient)
485                    .map(|message| message.clone().into_posted())
486                    .collect::<Vec<_>>();
487                (!messages.is_empty()).then(|| {
488                    let bundle = MessageBundle {
489                        height: block_height,
490                        timestamp: block_timestamp,
491                        certificate_hash,
492                        transaction_index,
493                        messages,
494                    };
495                    (block_epoch, bundle)
496                })
497            })
498    }
499
500    /// Returns all the blob IDs required by this block.
501    /// Either as oracle responses or as published blobs.
502    pub fn required_blob_ids(&self) -> BTreeSet<BlobId> {
503        let mut blob_ids = self.oracle_blob_ids();
504        blob_ids.extend(self.published_blob_ids());
505        blob_ids.extend(self.created_blob_ids());
506        if self.header.height == BlockHeight(0) {
507            // the initial block implicitly depends on the chain description blob
508            blob_ids.insert(BlobId::new(
509                self.header.chain_id.0,
510                BlobType::ChainDescription,
511            ));
512        }
513        blob_ids
514    }
515
516    /// Returns whether this block requires the blob with the specified ID.
517    pub fn requires_or_creates_blob(&self, blob_id: &BlobId) -> bool {
518        self.oracle_blob_ids().contains(blob_id)
519            || self.published_blob_ids().contains(blob_id)
520            || self.created_blob_ids().contains(blob_id)
521            || (self.header.height == BlockHeight(0)
522                && (blob_id.blob_type == BlobType::ChainDescription
523                    && blob_id.hash == self.header.chain_id.0))
524    }
525
526    /// Returns all the published blob IDs in this block's transactions.
527    pub fn published_blob_ids(&self) -> BTreeSet<BlobId> {
528        self.body
529            .operations()
530            .flat_map(Operation::published_blob_ids)
531            .collect()
532    }
533
534    /// Returns whether the first transaction in this block is a
535    /// `SystemOperation::Checkpoint`. See [`BlockBody::starts_with_checkpoint`].
536    pub fn starts_with_checkpoint(&self) -> bool {
537        self.body.starts_with_checkpoint()
538    }
539
540    /// Returns all the blob IDs created by the block's transactions.
541    pub fn created_blob_ids(&self) -> BTreeSet<BlobId> {
542        self.body
543            .blobs
544            .iter()
545            .flatten()
546            .map(|blob| blob.id())
547            .collect()
548    }
549
550    /// Returns all the blobs created by the block's transactions.
551    pub fn created_blobs(&self) -> BTreeMap<BlobId, Blob> {
552        self.body
553            .blobs
554            .iter()
555            .flatten()
556            .map(|blob| (blob.id(), blob.clone()))
557            .collect()
558    }
559
560    /// Returns set of blob IDs that were a result of an oracle call.
561    pub fn oracle_blob_ids(&self) -> BTreeSet<BlobId> {
562        let mut required_blob_ids = BTreeSet::new();
563        for responses in &self.body.oracle_responses {
564            for response in responses {
565                match response {
566                    OracleResponse::Blob(blob_id) => {
567                        required_blob_ids.insert(*blob_id);
568                    }
569                    OracleResponse::Checkpoint { used_blobs, .. } => {
570                        required_blob_ids.extend(used_blobs.iter().copied());
571                    }
572                    _ => {}
573                }
574            }
575        }
576
577        required_blob_ids
578    }
579
580    /// Returns reference to the outgoing messages in the block.
581    pub fn messages(&self) -> &Vec<Vec<OutgoingMessage>> {
582        &self.body.messages
583    }
584
585    /// Returns all recipients of messages in this block.
586    pub fn recipients(&self) -> BTreeSet<ChainId> {
587        self.body
588            .messages
589            .iter()
590            .flat_map(|messages| messages.iter().map(|message| message.destination))
591            .collect()
592    }
593
594    /// Returns whether there are any oracle responses in this block.
595    pub fn has_oracle_responses(&self) -> bool {
596        self.body
597            .oracle_responses
598            .iter()
599            .any(|responses| !responses.is_empty())
600    }
601
602    /// Returns whether this block matches the proposal.
603    pub fn matches_proposed_block(&self, block: &ProposedBlock) -> bool {
604        let ProposedBlock {
605            chain_id,
606            epoch,
607            transactions,
608            height,
609            timestamp,
610            authenticated_owner,
611            previous_block_hash,
612        } = block;
613        *chain_id == self.header.chain_id
614            && *epoch == self.header.epoch
615            && *transactions == self.body.transactions
616            && *height == self.header.height
617            && *timestamp == self.header.timestamp
618            && *authenticated_owner == self.header.authenticated_owner
619            && *previous_block_hash == self.header.previous_block_hash
620    }
621
622    /// Returns whether the block's execution produced the given outcome.
623    pub fn outcome_matches(&self, expected: &BlockExecutionOutcome) -> bool {
624        let BlockExecutionOutcome {
625            state_hash,
626            messages,
627            previous_message_blocks,
628            previous_event_blocks,
629            oracle_responses,
630            events,
631            blobs,
632            operation_results,
633        } = expected;
634        self.header.state_hash == *state_hash
635            && self.body.messages == *messages
636            && self.body.previous_message_blocks == *previous_message_blocks
637            && self.body.previous_event_blocks == *previous_event_blocks
638            && self.body.oracle_responses == *oracle_responses
639            && self.body.events == *events
640            && self.body.blobs == *blobs
641            && self.body.operation_results == *operation_results
642    }
643
644    pub fn into_proposal(self) -> (ProposedBlock, BlockExecutionOutcome) {
645        let proposed_block = ProposedBlock {
646            chain_id: self.header.chain_id,
647            epoch: self.header.epoch,
648            transactions: self.body.transactions,
649            height: self.header.height,
650            timestamp: self.header.timestamp,
651            authenticated_owner: self.header.authenticated_owner,
652            previous_block_hash: self.header.previous_block_hash,
653        };
654        let outcome = BlockExecutionOutcome {
655            state_hash: self.header.state_hash,
656            messages: self.body.messages,
657            previous_message_blocks: self.body.previous_message_blocks,
658            previous_event_blocks: self.body.previous_event_blocks,
659            oracle_responses: self.body.oracle_responses,
660            events: self.body.events,
661            blobs: self.body.blobs,
662            operation_results: self.body.operation_results,
663        };
664        (proposed_block, outcome)
665    }
666
667    /// Returns the IDs of all events in this block.
668    pub fn event_ids(&self) -> impl Iterator<Item = EventId> + '_ {
669        let to_id = |event: &Event| event.id(self.header.chain_id);
670        self.body.events.iter().flatten().map(to_id)
671    }
672}
673
674impl BcsHashable<'_> for Block {}
675
676#[derive(Serialize, Deserialize)]
677pub struct PreviousMessageBlocksMap<'a> {
678    inner: Cow<'a, BTreeMap<ChainId, (CryptoHash, BlockHeight)>>,
679}
680
681impl<'de> BcsHashable<'de> for PreviousMessageBlocksMap<'de> {}
682
683#[derive(Serialize, Deserialize)]
684pub struct PreviousEventBlocksMap<'a> {
685    inner: Cow<'a, BTreeMap<StreamId, (CryptoHash, BlockHeight)>>,
686}
687
688impl<'de> BcsHashable<'de> for PreviousEventBlocksMap<'de> {}
689
690#[derive(Serialize, Deserialize)]
691#[serde(rename = "BlockHeader")]
692struct SerializedHeader {
693    chain_id: ChainId,
694    epoch: Epoch,
695    height: BlockHeight,
696    timestamp: Timestamp,
697    state_hash: CryptoHash,
698    previous_block_hash: Option<CryptoHash>,
699    authenticated_owner: Option<AccountOwner>,
700}
701
702mod hashing {
703    use linera_base::crypto::{BcsHashable, CryptoHash, CryptoHashVec};
704
705    pub(super) fn hash_vec<'de, T: BcsHashable<'de>>(it: impl AsRef<[T]>) -> CryptoHash {
706        let v = CryptoHashVec(it.as_ref().iter().map(CryptoHash::new).collect::<Vec<_>>());
707        CryptoHash::new(&v)
708    }
709
710    pub(super) fn hash_vec_vec<'de, T: BcsHashable<'de>>(it: impl AsRef<[Vec<T>]>) -> CryptoHash {
711        let v = CryptoHashVec(it.as_ref().iter().map(hash_vec).collect::<Vec<_>>());
712        CryptoHash::new(&v)
713    }
714}