1#![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, CryptoHash},
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 origin {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 InvalidBlockHeight,
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("Cannot initiate a new block while the previous one is still pending confirmation")]
127 PreviousBlockMustBeConfirmedFirst,
128 #[error("Round number should be at least {0:?}")]
129 InsufficientRound(Round),
130 #[error("Round number should be greater than {0:?}")]
131 InsufficientRoundStrict(Round),
132 #[error("Round number should be {0:?}")]
133 WrongRound(Round),
134 #[error("Already voted to confirm a different block for height {0:?} at round number {1:?}")]
135 HasIncompatibleConfirmedVote(BlockHeight, Round),
136 #[error("Proposal for height {0:?} is not newer than locking block in round {1:?}")]
137 MustBeNewerThanLockingBlock(BlockHeight, Round),
138 #[error("Cannot confirm a block before its predecessors: {current_block_height:?}")]
139 MissingEarlierBlocks { current_block_height: BlockHeight },
140 #[error("Signatures in a certificate must be from different validators")]
141 CertificateValidatorReuse,
142 #[error("Signatures in a certificate must form a quorum")]
143 CertificateRequiresQuorum,
144 #[error("Certificate signature verification failed: {error}")]
145 CertificateSignatureVerificationFailed { error: String },
146 #[error("Internal error {0}")]
147 InternalError(String),
148 #[error("Block proposal has size {0} which is too large")]
149 BlockProposalTooLarge(usize),
150 #[error(transparent)]
151 BcsError(#[from] bcs::Error),
152 #[error("Insufficient balance to pay the fees")]
153 InsufficientBalance,
154 #[error("Invalid owner weights: {0}")]
155 OwnerWeightError(#[from] WeightedError),
156 #[error("Closed chains cannot have operations, accepted messages or empty blocks")]
157 ClosedChain,
158 #[error("Empty blocks are not allowed")]
159 EmptyBlock,
160 #[error("All operations on this chain must be from one of the following applications: {0:?}")]
161 AuthorizedApplications(Vec<ApplicationId>),
162 #[error("Missing operations or messages from mandatory applications: {0:?}")]
163 MissingMandatoryApplications(Vec<ApplicationId>),
164 #[error("Can't use grant across different broadcast messages")]
165 GrantUseOnBroadcast,
166 #[error("Executed block contains fewer oracle responses than requests")]
167 MissingOracleResponseList,
168 #[error("Unexpected hash for CertificateValue! Expected: {expected:?}, Actual: {actual:?}")]
169 CertificateValueHashMismatch {
170 expected: CryptoHash,
171 actual: CryptoHash,
172 },
173}
174
175#[derive(Copy, Clone, Debug)]
176#[cfg_attr(with_testing, derive(Eq, PartialEq))]
177pub enum ChainExecutionContext {
178 Query,
179 DescribeApplication,
180 IncomingBundle(u32),
181 Operation(u32),
182 Block,
183}
184
185pub trait ExecutionResultExt<T> {
186 fn with_execution_context(self, context: ChainExecutionContext) -> Result<T, ChainError>;
187}
188
189impl<T, E> ExecutionResultExt<T> for Result<T, E>
190where
191 E: Into<ExecutionError>,
192{
193 fn with_execution_context(self, context: ChainExecutionContext) -> Result<T, ChainError> {
194 self.map_err(|error| ChainError::ExecutionError(Box::new(error.into()), context))
195 }
196}