linera_chain/
lib.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module manages the state of a Linera chain, including cross-chain communication.
5
6pub mod block;
7mod certificate;
8
9pub mod types {
10    pub use super::{block::*, certificate::*};
11}
12
13mod block_tracker;
14mod chain;
15pub mod data_types;
16mod inbox;
17pub mod manager;
18mod outbox;
19mod pending_blobs;
20#[cfg(with_testing)]
21pub mod test;
22
23pub use chain::ChainStateView;
24use data_types::{MessageBundle, PostedMessage};
25use linera_base::{
26    bcs,
27    crypto::CryptoError,
28    data_types::{ArithmeticError, BlockHeight, Round, Timestamp},
29    identifiers::{ApplicationId, ChainId},
30};
31use linera_execution::ExecutionError;
32use linera_views::ViewError;
33use rand_distr::WeightedError;
34use thiserror::Error;
35
36#[derive(Error, Debug)]
37pub enum ChainError {
38    #[error("Cryptographic error: {0}")]
39    CryptoError(#[from] CryptoError),
40    #[error(transparent)]
41    ArithmeticError(#[from] ArithmeticError),
42    #[error(transparent)]
43    ViewError(#[from] ViewError),
44    #[error("Execution error: {0} during {1:?}")]
45    ExecutionError(Box<ExecutionError>, ChainExecutionContext),
46
47    #[error("The chain being queried is not active {0}")]
48    InactiveChain(ChainId),
49    #[error(
50        "Cannot vote for block proposal of chain {chain_id} because a message \
51         from chain {origin} at height {height} has not been received yet"
52    )]
53    MissingCrossChainUpdate {
54        chain_id: ChainId,
55        origin: ChainId,
56        height: BlockHeight,
57    },
58    #[error(
59        "Message in block proposed to {chain_id:?} does not match the previously received messages from \
60        origin {origin:?}: was {bundle:?} instead of {previous_bundle:?}"
61    )]
62    UnexpectedMessage {
63        chain_id: ChainId,
64        origin: ChainId,
65        bundle: Box<MessageBundle>,
66        previous_bundle: Box<MessageBundle>,
67    },
68    #[error(
69        "Message in block proposed to {chain_id:?} is out of order compared to previous messages \
70         from origin {origin:?}: {bundle:?}. Block and height should be at least: \
71         {next_height}, {next_index}"
72    )]
73    IncorrectMessageOrder {
74        chain_id: ChainId,
75        origin: ChainId,
76        bundle: Box<MessageBundle>,
77        next_height: BlockHeight,
78        next_index: u32,
79    },
80    #[error(
81        "Block proposed to {chain_id:?} is attempting to reject protected message \
82        {posted_message:?}"
83    )]
84    CannotRejectMessage {
85        chain_id: ChainId,
86        origin: ChainId,
87        posted_message: Box<PostedMessage>,
88    },
89    #[error(
90        "Block proposed to {chain_id:?} is attempting to skip a message bundle \
91         that cannot be skipped: {bundle:?}"
92    )]
93    CannotSkipMessage {
94        chain_id: ChainId,
95        origin: ChainId,
96        bundle: Box<MessageBundle>,
97    },
98    #[error(
99        "Incoming message bundle in block proposed to {chain_id:?} has timestamp \
100        {bundle_timestamp:}, which is later than the block timestamp {block_timestamp:}."
101    )]
102    IncorrectBundleTimestamp {
103        chain_id: ChainId,
104        bundle_timestamp: Timestamp,
105        block_timestamp: Timestamp,
106    },
107    #[error("The signature was not created by a valid entity")]
108    InvalidSigner,
109    #[error(
110        "Was expecting block height {expected_block_height} but found {found_block_height} instead"
111    )]
112    UnexpectedBlockHeight {
113        expected_block_height: BlockHeight,
114        found_block_height: BlockHeight,
115    },
116    #[error("The previous block hash of a new block should match the last block of the chain")]
117    UnexpectedPreviousBlockHash,
118    #[error("Sequence numbers above the maximal value are not usable for blocks")]
119    BlockHeightOverflow,
120    #[error(
121        "Block timestamp {new} must not be earlier than the parent block's timestamp {parent}"
122    )]
123    InvalidBlockTimestamp { parent: Timestamp, new: Timestamp },
124    #[error("Round number should be at least {0:?}")]
125    InsufficientRound(Round),
126    #[error("Round number should be greater than {0:?}")]
127    InsufficientRoundStrict(Round),
128    #[error("Round number should be {0:?}")]
129    WrongRound(Round),
130    #[error("Already voted to confirm a different block for height {0:?} at round number {1:?}")]
131    HasIncompatibleConfirmedVote(BlockHeight, Round),
132    #[error("Proposal for height {0:?} is not newer than locking block in round {1:?}")]
133    MustBeNewerThanLockingBlock(BlockHeight, Round),
134    #[error("Cannot confirm a block before its predecessors: {current_block_height:?}")]
135    MissingEarlierBlocks { current_block_height: BlockHeight },
136    #[error("Signatures in a certificate must be from different validators")]
137    CertificateValidatorReuse,
138    #[error("Signatures in a certificate must form a quorum")]
139    CertificateRequiresQuorum,
140    #[error("Internal error {0}")]
141    InternalError(String),
142    #[error("Block proposal has size {0} which is too large")]
143    BlockProposalTooLarge(usize),
144    #[error(transparent)]
145    BcsError(#[from] bcs::Error),
146    #[error("Invalid owner weights: {0}")]
147    OwnerWeightError(#[from] WeightedError),
148    #[error("Closed chains cannot have operations, accepted messages or empty blocks")]
149    ClosedChain,
150    #[error("Empty blocks are not allowed")]
151    EmptyBlock,
152    #[error("All operations on this chain must be from one of the following applications: {0:?}")]
153    AuthorizedApplications(Vec<ApplicationId>),
154    #[error("Missing operations or messages from mandatory applications: {0:?}")]
155    MissingMandatoryApplications(Vec<ApplicationId>),
156    #[error("Executed block contains fewer oracle responses than requests")]
157    MissingOracleResponseList,
158    #[error("Not signing timeout certificate; current round does not time out")]
159    RoundDoesNotTimeOut,
160    #[error("Not signing timeout certificate; current round times out at time {0}")]
161    NotTimedOutYet(Timestamp),
162}
163
164#[derive(Copy, Clone, Debug)]
165#[cfg_attr(with_testing, derive(Eq, PartialEq))]
166pub enum ChainExecutionContext {
167    Query,
168    DescribeApplication,
169    IncomingBundle(u32),
170    Operation(u32),
171    Block,
172}
173
174pub trait ExecutionResultExt<T> {
175    fn with_execution_context(self, context: ChainExecutionContext) -> Result<T, ChainError>;
176}
177
178impl<T, E> ExecutionResultExt<T> for Result<T, E>
179where
180    E: Into<ExecutionError>,
181{
182    fn with_execution_context(self, context: ChainExecutionContext) -> Result<T, ChainError> {
183        self.map_err(|error| ChainError::ExecutionError(Box::new(error.into()), context))
184    }
185}