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