Skip to content

Commit e2ae185

Browse files
Remove BlobsNotFound / EventsNotFound from the ViewError and make the read_blob / read_event return an option. (#4014)
## Motivation The `ViewError` has the possibilities `BlobsNotFound` and `EventsNotFound` which is a leakage of abstraction as it should not be there. ## Proposal There are several considerations for this work. The first is to recognize that while the use of the `ViewError` to park the error is inadequate, it is certainly practical. **Possibility 1**: One possibility is to introduce a specific error type: ```rust enum StorageError { BlobsNotFound(Vec<BlobId>), EventsNotFound(Vec<EventId>), ViewError(ViewError), } ``` while this addresses the concern with `ViewError` it complexifies the code significantly. Worse it presents another problem. Suppose that a crate is using the `read_blob` but not the `read_event`. Then it has to handle the possibility of an `EventsNotFound`. Of course this could never materialize, but the type system implies that either it is marked as unreachable, or an entry is added to the corresponding error type. **Possibility 2**: Add a `ReadBlobError` and a `ReadEventError`. This is satisfying from the type system, though unwieldly. **Possibility 3**: Replace the `read_blob` by `maybe_read_blob` and return an Option. The caller is then responsible for handling the None case and decide what to do in that case. This is the chosen solution. It simplifies away quite some code but introduce some complicacies elsewhere. This is preferable: The handling of missing case is the responsibility of the caller. **Note 1**: The same transformation is probably possible for `read_blob_state(s)`. For `read_certificate(s)` this looks more complicated and a `ReadCertificateError` might be the right solution here. This should be handled in subsequent PR. **Note 2**: The plural `BlobsNotFound` / `EventsNotFound` is kept. However, an examination of the code shows that only singular are built. Maybe there is an underlying issue to address. Other points: * The `BlobsNotFound` has been replaced from `ChainError` as it is never built. * The `read_network_description` has been changed into `maybe_read_network_description` as an Option is returned. ## Test Plan The CI. ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links None.
1 parent b4ecc7c commit e2ae185

File tree

16 files changed

+73
-112
lines changed

16 files changed

+73
-112
lines changed

linera-chain/src/lib.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use linera_base::{
2828
bcs,
2929
crypto::{CryptoError, CryptoHash},
3030
data_types::{ArithmeticError, BlockHeight, Round, Timestamp},
31-
identifiers::{ApplicationId, BlobId, ChainId},
31+
identifiers::{ApplicationId, ChainId},
3232
};
3333
use linera_execution::ExecutionError;
3434
use linera_views::views::ViewError;
@@ -42,7 +42,7 @@ pub enum ChainError {
4242
#[error(transparent)]
4343
ArithmeticError(#[from] ArithmeticError),
4444
#[error(transparent)]
45-
ViewError(ViewError),
45+
ViewError(#[from] ViewError),
4646
#[error("Execution error: {0} during {1:?}")]
4747
ExecutionError(Box<ExecutionError>, ChainExecutionContext),
4848

@@ -168,17 +168,6 @@ pub enum ChainError {
168168
expected: CryptoHash,
169169
actual: CryptoHash,
170170
},
171-
#[error("Blobs not found: {0:?}")]
172-
BlobsNotFound(Vec<BlobId>),
173-
}
174-
175-
impl From<ViewError> for ChainError {
176-
fn from(error: ViewError) -> Self {
177-
match error {
178-
ViewError::BlobsNotFound(blob_ids) => ChainError::BlobsNotFound(blob_ids),
179-
error => ChainError::ViewError(error),
180-
}
181-
}
182171
}
183172

184173
#[derive(Copy, Clone, Debug)]

linera-client/src/chain_listener.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ use linera_core::{
2525
Environment,
2626
};
2727
use linera_storage::{Clock as _, Storage as _};
28-
use linera_views::views::ViewError;
2928
use tokio_util::sync::CancellationToken;
3029
use tracing::{debug, info, instrument, warn, Instrument as _};
3130

@@ -98,8 +97,8 @@ pub trait ClientContextExt: ClientContext {
9897
let blob_id = BlobId::new(chain_id.0, BlobType::ChainDescription);
9998

10099
let blob = match self.storage().read_blob(blob_id).await {
101-
Ok(blob) => blob,
102-
Err(ViewError::BlobsNotFound(blob_ids)) if blob_ids == [blob_id] => {
100+
Ok(Some(blob)) => blob,
101+
Ok(None) => {
103102
// we're missing the blob describing the chain we're assigning - try to
104103
// get it
105104
self.client().ensure_has_chain_description(chain_id).await?

linera-core/src/chain_worker/state/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,8 @@ where
318318
if let Some(blob) = self.chain.manager.pending_blob(&blob_id).await? {
319319
return Ok(blob);
320320
}
321-
Ok(self.storage.read_blob(blob_id).await?)
321+
let blob = self.storage.read_blob(blob_id).await?;
322+
blob.ok_or(WorkerError::BlobsNotFound(vec![blob_id]))
322323
}
323324

324325
/// Adds the blob to pending blocks or validated block certificates that are missing it.

linera-core/src/client/mod.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -473,15 +473,15 @@ impl<Env: Environment> Client<Env> {
473473
chain_id: ChainId,
474474
) -> Result<Blob, ChainClientError> {
475475
let chain_desc_id = BlobId::new(chain_id.0, BlobType::ChainDescription);
476-
if let Ok(blob) = self
476+
let blob = self
477477
.local_node
478478
.storage_client()
479479
.read_blob(chain_desc_id)
480-
.await
481-
{
480+
.await?;
481+
if let Some(blob) = blob {
482482
// We have the blob - return it.
483483
return Ok(blob);
484-
}
484+
};
485485
// Recover history from the current validators, according to the admin chain.
486486
// TODO(#2351): make sure that the blob is legitimately created!
487487
let nodes = self.validator_nodes().await?;
@@ -3300,11 +3300,12 @@ impl<Env: Environment> ChainClient<Env> {
33003300
stream_id: StreamId::system(stream_name),
33013301
index,
33023302
};
3303-
match self.client.storage_client().read_event(event_id).await {
3304-
Ok(_) => Ok(true),
3305-
Err(ViewError::EventsNotFound(_)) => Ok(false),
3306-
Err(error) => Err(error.into()),
3307-
}
3303+
Ok(self
3304+
.client
3305+
.storage_client()
3306+
.read_event(event_id)
3307+
.await?
3308+
.is_some())
33083309
}
33093310

33103311
/// Returns the indices and events from the storage

linera-core/src/local_node.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub enum LocalNodeError {
5555
ArithmeticError(#[from] ArithmeticError),
5656

5757
#[error(transparent)]
58-
ViewError(ViewError),
58+
ViewError(#[from] ViewError),
5959

6060
#[error("Local node operation failed: {0}")]
6161
WorkerError(WorkerError),
@@ -82,15 +82,6 @@ impl From<WorkerError> for LocalNodeError {
8282
}
8383
}
8484

85-
impl From<ViewError> for LocalNodeError {
86-
fn from(error: ViewError) -> Self {
87-
match error {
88-
ViewError::BlobsNotFound(blob_ids) => LocalNodeError::BlobsNotFound(blob_ids),
89-
error => LocalNodeError::ViewError(error),
90-
}
91-
}
92-
}
93-
9485
impl<S> LocalNodeClient<S>
9586
where
9687
S: Storage + Clone + Send + Sync + 'static,

linera-core/src/node.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,8 @@ impl CrossChainMessageDelivery {
288288

289289
impl From<ViewError> for NodeError {
290290
fn from(error: ViewError) -> Self {
291-
match error {
292-
ViewError::BlobsNotFound(blob_ids) => Self::BlobsNotFound(blob_ids),
293-
error => Self::ViewError {
294-
error: error.to_string(),
295-
},
291+
Self::ViewError {
292+
error: error.to_string(),
296293
}
297294
}
298295
}
@@ -326,7 +323,6 @@ impl From<ChainError> for NodeError {
326323
height,
327324
},
328325
ChainError::InactiveChain(chain_id) => Self::InactiveChain(chain_id),
329-
ChainError::BlobsNotFound(blob_ids) => Self::BlobsNotFound(blob_ids),
330326
ChainError::ExecutionError(execution_error, context) => {
331327
if let ExecutionError::BlobsNotFound(blob_ids) = *execution_error {
332328
Self::BlobsNotFound(blob_ids)

linera-core/src/unit_tests/test_utils.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,10 @@ where
513513
.read_blob(blob_id)
514514
.await
515515
.map_err(Into::into);
516+
let blob = match blob {
517+
Ok(blob) => blob.ok_or(NodeError::BlobsNotFound(vec![blob_id])),
518+
Err(error) => Err(error),
519+
};
516520
sender.send(blob.map(|blob| blob.into_content()))
517521
}
518522

linera-core/src/worker.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ pub enum WorkerError {
150150
ArithmeticError(#[from] ArithmeticError),
151151

152152
#[error(transparent)]
153-
ViewError(ViewError),
153+
ViewError(#[from] ViewError),
154154

155155
#[error(transparent)]
156156
ChainError(#[from] Box<ChainError>),
@@ -221,7 +221,6 @@ impl From<ChainError> for WorkerError {
221221
#[instrument(level = "trace", skip(chain_error))]
222222
fn from(chain_error: ChainError) -> Self {
223223
match chain_error {
224-
ChainError::BlobsNotFound(blob_ids) => Self::BlobsNotFound(blob_ids),
225224
ChainError::ExecutionError(execution_error, context) => {
226225
if let ExecutionError::BlobsNotFound(blob_ids) = *execution_error {
227226
Self::BlobsNotFound(blob_ids)
@@ -237,15 +236,6 @@ impl From<ChainError> for WorkerError {
237236
}
238237
}
239238

240-
impl From<ViewError> for WorkerError {
241-
fn from(view_error: ViewError) -> Self {
242-
match view_error {
243-
ViewError::BlobsNotFound(blob_ids) => Self::BlobsNotFound(blob_ids),
244-
error => Self::ViewError(error),
245-
}
246-
}
247-
}
248-
249239
#[cfg(with_testing)]
250240
impl WorkerError {
251241
/// Returns the inner [`ExecutionError`] in this error.

linera-execution/src/execution_state_actor.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,8 +452,9 @@ where
452452
}
453453

454454
ReadEvent { event_id, callback } => {
455-
let event_value = self.context().extra().get_event(event_id).await?;
456-
callback.respond(event_value);
455+
let event = self.context().extra().get_event(event_id.clone()).await?;
456+
let event = event.ok_or(ExecutionError::EventsNotFound(vec![event_id]))?;
457+
callback.respond(event);
457458
}
458459

459460
SubscribeToEvents {

linera-execution/src/lib.rs

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ const _: () = {
193193
#[derive(Error, Debug)]
194194
pub enum ExecutionError {
195195
#[error(transparent)]
196-
ViewError(ViewError),
196+
ViewError(#[from] ViewError),
197197
#[error(transparent)]
198198
ArithmeticError(#[from] ArithmeticError),
199199
#[error("User application reported an error: {0}")]
@@ -283,6 +283,8 @@ pub enum ExecutionError {
283283
ServiceModuleSend(#[from] linera_base::task::SendError<UserServiceCode>),
284284
#[error("Blobs not found: {0:?}")]
285285
BlobsNotFound(Vec<BlobId>),
286+
#[error("Events not found: {0:?}")]
287+
EventsNotFound(Vec<EventId>),
286288

287289
#[error("Invalid HTTP header name used for HTTP request")]
288290
InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
@@ -342,15 +344,6 @@ pub enum ExecutionError {
342344
OutdatedUpdateStreams,
343345
}
344346

345-
impl From<ViewError> for ExecutionError {
346-
fn from(error: ViewError) -> Self {
347-
match error {
348-
ViewError::BlobsNotFound(blob_ids) => ExecutionError::BlobsNotFound(blob_ids),
349-
error => ExecutionError::ViewError(error),
350-
}
351-
}
352-
}
353-
354347
/// The public entry points provided by the contract part of an application.
355348
pub trait UserContract {
356349
/// Instantiate the application state on the chain that owns the application.
@@ -402,9 +395,9 @@ pub trait ExecutionRuntimeContext {
402395
description: &ApplicationDescription,
403396
) -> Result<UserServiceCode, ExecutionError>;
404397

405-
async fn get_blob(&self, blob_id: BlobId) -> Result<Blob, ViewError>;
398+
async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError>;
406399

407-
async fn get_event(&self, event_id: EventId) -> Result<Vec<u8>, ViewError>;
400+
async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError>;
408401

409402
async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
410403

@@ -1070,20 +1063,18 @@ impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
10701063
.clone())
10711064
}
10721065

1073-
async fn get_blob(&self, blob_id: BlobId) -> Result<Blob, ViewError> {
1074-
Ok(self
1075-
.blobs
1076-
.get(&blob_id)
1077-
.ok_or_else(|| ViewError::BlobsNotFound(vec![blob_id]))?
1078-
.clone())
1066+
async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError> {
1067+
match self.blobs.get(&blob_id) {
1068+
None => Ok(None),
1069+
Some(blob) => Ok(Some(blob.clone())),
1070+
}
10791071
}
10801072

1081-
async fn get_event(&self, event_id: EventId) -> Result<Vec<u8>, ViewError> {
1082-
Ok(self
1083-
.events
1084-
.get(&event_id)
1085-
.ok_or_else(|| ViewError::EventsNotFound(vec![event_id]))?
1086-
.clone())
1073+
async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError> {
1074+
match self.events.get(&event_id) {
1075+
None => Ok(None),
1076+
Some(event) => Ok(Some(event.clone())),
1077+
}
10871078
}
10881079

10891080
async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {

0 commit comments

Comments
 (0)