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