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