diff --git a/linera-chain/src/chain.rs b/linera-chain/src/chain.rs index 63e656ac4854..0d69dbdd48b8 100644 --- a/linera-chain/src/chain.rs +++ b/linera-chain/src/chain.rs @@ -26,7 +26,7 @@ use linera_execution::{ use linera_views::{ context::Context, log_view::LogView, - map_view::MapView, + map_view::{CustomMapView, MapView}, reentrant_collection_view::{ReadGuardedView, ReentrantCollectionView}, register_view::RegisterView, views::{ClonableView, RootView, View}, @@ -212,9 +212,10 @@ where /// The incomplete sets of blobs for upcoming proposals. pub pending_proposed_blobs: ReentrantCollectionView>, - /// Hashes of all certified blocks for this sender. - /// This ends with `block_hash` and has length `usize::from(next_block_height)`. - pub confirmed_log: LogView, + /// Hashes of all known blocks in this chain, indexed by their height. A block at + /// `height < next_block_height` is executed; a block at `height >= next_block_height` + /// is preprocessed (verified but not yet executed) and may not be contiguous. + pub block_hashes: CustomMapView, /// Sender chain and height of all certified blocks known as a receiver (local ordering). pub received_log: LogView, /// The number of `received_log` entries we have synchronized, for each validator. @@ -233,8 +234,6 @@ where /// Outboxes with at least one pending message. This allows us to avoid loading all outboxes. pub nonempty_outboxes: RegisterView>, - /// Blocks that have been verified but not executed yet, and that may not be contiguous. - pub preprocessed_blocks: MapView, /// Inboxes with at least one pending added bundle. This allows us to avoid loading all inboxes. pub nonempty_inboxes: RegisterView>, @@ -242,6 +241,16 @@ where /// reset-on-incorrect-outcome from looping: if not enough time has elapsed since /// the last reset, the error is returned instead. pub block_zero_executed_at: RegisterView, + + /// The height at which the next block can be preprocessed: one past the highest + /// height in `block_hashes` (executed or preprocessed), or `next_block_height` if + /// `block_hashes` is empty. + /// + /// Maintained as an O(1) shortcut for `next_height_to_preprocess`, since + /// `CustomMapView` does not yet expose a `last_index` lookup. Once + /// `linera-views` gains efficient first/last key support, this field can be + /// removed in favor of `block_hashes.last_index()`. + pub next_height_to_preprocess: RegisterView, } /// Block-chaining state. @@ -478,14 +487,20 @@ where Ok(()) } - /// Returns the height of the highest block we have, plus one. Includes preprocessed blocks. - /// - /// The "+ 1" is so that it can be used in the same places as `next_block_height`. - pub async fn next_height_to_preprocess(&self) -> Result { - if let Some(height) = self.preprocessed_blocks.indices().await?.last() { - return Ok(height.saturating_add(BlockHeight(1))); + /// Inserts `(height, hash)` into `block_hashes` and updates the + /// `next_height_to_preprocess` register accordingly. Every write to + /// `block_hashes` must go through this helper so the register stays in sync. + fn insert_block_hash( + &mut self, + height: BlockHeight, + hash: CryptoHash, + ) -> Result<(), ChainError> { + self.block_hashes.insert(&height, hash)?; + let next = self.next_height_to_preprocess.get_mut(); + if *next <= height { + *next = height.try_add_one()?; } - Ok(self.tip_state.get().next_block_height) + Ok(()) } /// Attempts to process a new `bundle` of messages from the given `origin`. Returns an @@ -674,7 +689,7 @@ where ))] async fn execute_block_inner( chain: &mut ExecutionStateView, - confirmed_log: &LogView, + block_hashes: &CustomMapView, block: &mut ProposedBlock, local_time: Timestamp, round: Option, @@ -875,7 +890,6 @@ where let recipients = block_execution_tracker.recipients(); let mut recipient_heights = Vec::new(); - let mut indices = Vec::new(); for (recipient, height) in chain .previous_message_blocks .multi_get_pairs(recipients) @@ -885,36 +899,35 @@ where .previous_message_blocks .insert(&recipient, block.height)?; if let Some(height) = height { - let index = usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)?; - indices.push(index); recipient_heights.push((recipient, height)); } } - let hashes = confirmed_log.multi_get(indices).await?; + let hashes = block_hashes + .multi_get(recipient_heights.iter().map(|(_, height)| height)) + .await?; let mut previous_message_blocks = BTreeMap::new(); for (hash, (recipient, height)) in hashes.into_iter().zip(recipient_heights) { let hash = hash.ok_or_else(|| { - ChainError::CorruptedChainState("missing entry in confirmed_log".into()) + ChainError::CorruptedChainState("missing entry in block_hashes".into()) })?; previous_message_blocks.insert(recipient, (hash, height)); } let streams = block_execution_tracker.event_streams(); let mut stream_heights = Vec::new(); - let mut indices = Vec::new(); for (stream, height) in chain.previous_event_blocks.multi_get_pairs(streams).await? { chain.previous_event_blocks.insert(&stream, block.height)?; if let Some(height) = height { - let index = usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)?; - indices.push(index); stream_heights.push((stream, height)); } } - let hashes = confirmed_log.multi_get(indices).await?; + let hashes = block_hashes + .multi_get(stream_heights.iter().map(|(_, height)| height)) + .await?; let mut previous_event_blocks = BTreeMap::new(); for (hash, (stream, height)) in hashes.into_iter().zip(stream_heights) { let hash = hash.ok_or_else(|| { - ChainError::CorruptedChainState("missing entry in confirmed_log".into()) + ChainError::CorruptedChainState("missing entry in block_hashes".into()) })?; previous_event_blocks.insert(stream, (hash, height)); } @@ -1035,7 +1048,7 @@ where Self::execute_block_inner( &mut self.execution_state, - &self.confirmed_log, + &self.block_hashes, &mut block, local_time, round, @@ -1079,12 +1092,11 @@ where tip.block_hash = Some(hash); tip.next_block_height.try_add_assign_one()?; tip.update_counters(&block.body.transactions, &block.body.messages)?; - self.confirmed_log.push(hash); - self.preprocessed_blocks.remove(&block.header.height)?; + self.insert_block_hash(block.header.height, hash)?; Ok(updated_streams) } - /// Adds a block to `preprocessed_blocks`, and updates the outboxes where possible. + /// Adds a block to `block_hashes` as preprocessed, and updates the outboxes where possible. /// Returns the set of streams that were updated as a result of preprocessing the block. #[instrument(skip_all, fields( chain_id = %self.chain_id(), @@ -1102,7 +1114,7 @@ where } self.process_outgoing_messages(block).await?; let updated_streams = self.process_emitted_events(block).await?; - self.preprocessed_blocks.insert(&height, hash)?; + self.insert_block_hash(height, hash)?; Ok(updated_streams) } @@ -1157,36 +1169,22 @@ where Ok(()) } - /// Returns the hashes of all blocks we have in the given range. - /// - /// If the input heights are in ascending order, the hashes will be in the same order. - /// Otherwise they may be unordered. + /// Returns the hashes of all blocks we have at the given heights, in input order. + /// Unknown heights are skipped. #[instrument(skip_all, fields( chain_id = %self.chain_id(), next_block_height = %self.tip_state.get().next_block_height, ))] - pub async fn block_hashes( + pub async fn block_hashes_for_heights( &self, heights: impl IntoIterator, ) -> Result, ChainError> { - let next_height = self.tip_state.get().next_block_height; - // Everything up to (excluding) next_height is in confirmed_log. - let (confirmed_heights, unconfirmed_heights) = heights - .into_iter() - .partition::, _>(|height| *height < next_height); - let confirmed_indices = confirmed_heights - .into_iter() - .map(|height| usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)) - .collect::>()?; - let confirmed_hashes = self.confirmed_log.multi_get(confirmed_indices).await?; - // Everything after (including) next_height in preprocessed_blocks if we have it. - let unconfirmed_hashes = self - .preprocessed_blocks - .multi_get(&unconfirmed_heights) - .await?; - Ok(confirmed_hashes + let heights = heights.into_iter().collect::>(); + Ok(self + .block_hashes + .multi_get(&heights) + .await? .into_iter() - .chain(unconfirmed_hashes) .flatten() .collect()) } @@ -1244,24 +1242,11 @@ where } let maybe_prev_hash = match outbox.next_height_to_schedule.get().try_sub_one().ok() { - // The block with the last added message has already been executed; look up its - // hash in the confirmed_log. - Some(height) if height < next_height => { - let index = - usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)?; - Some(self.confirmed_log.get(index).await?.ok_or_else(|| { - ChainError::CorruptedChainState("missing entry in confirmed_log".into()) + Some(height) => { + Some(self.block_hashes.get(&height).await?.ok_or_else(|| { + ChainError::CorruptedChainState("missing entry in block_hashes".into()) })?) } - // The block with last added message has not been executed yet. If we have it, - // it's in preprocessed_blocks. - Some(height) => Some(self.preprocessed_blocks.get(&height).await?.ok_or_else( - || { - ChainError::CorruptedChainState( - "missing entry in preprocessed_blocks".into(), - ) - }, - )?), None => None, // No message to that sender was added yet. }; // Only schedule if this block contains the next message for that recipient. diff --git a/linera-chain/src/unit_tests/chain_tests.rs b/linera-chain/src/unit_tests/chain_tests.rs index 169a3d45e357..749e4e4c55b3 100644 --- a/linera-chain/src/unit_tests/chain_tests.rs +++ b/linera-chain/src/unit_tests/chain_tests.rs @@ -884,3 +884,20 @@ async fn prepare_test_with_dummy_mock_application( Ok((application, application_id, chain, block, time)) } + +/// The `next_height_to_preprocess` register is read directly by callers rather +/// than being computed from `block_hashes.indices()`. This pins down that the +/// register is consulted (no fallback to `indices()` that would mis-sort heights +/// spanning a byte boundary under BCS little-endian `u64` encoding). +#[tokio::test] +async fn test_next_height_to_preprocess_register() { + let chain_id = TestEnvironment::new().admin_chain_id(); + let mut chain = ChainStateView::new(chain_id).await; + + // Empty chain. + assert_eq!(*chain.next_height_to_preprocess.get(), BlockHeight(0)); + + // Set the register to a height crossing the first byte boundary. + chain.next_height_to_preprocess.set(BlockHeight(257)); + assert_eq!(*chain.next_height_to_preprocess.get(), BlockHeight(257)); +} diff --git a/linera-core/src/chain_worker/state.rs b/linera-core/src/chain_worker/state.rs index cd8529cc7a9a..c1c27993667b 100644 --- a/linera-core/src/chain_worker/state.rs +++ b/linera-core/src/chain_worker/state.rs @@ -462,7 +462,10 @@ where .flatten() .copied() .collect::>(); - let hashes = self.chain.block_hashes(heights.iter().copied()).await?; + let hashes = self + .chain + .block_hashes_for_heights(heights.iter().copied()) + .await?; let blocks = self.read_confirmed_blocks(&hashes).await?; @@ -1155,13 +1158,17 @@ where while current_height >= retransmit_from { // We arrived at current_height via previous_message_blocks links, starting from the // chain state and following the links downwards. So these blocks should all be in - // confirmed_log or preprocessed_blocks already. + // `block_hashes` already. heights_to_re_add.push(current_height); // Load the block at current_height to find the previous message block - let hash = match &*self.chain.block_hashes([current_height]).await? { + let hash = match &*self + .chain + .block_hashes_for_heights([current_height]) + .await? + { [hash] => *hash, _ => { - return Err(WorkerError::ConfirmedBlockHashNotFound { + return Err(WorkerError::BlockHashNotFound { height: current_height, chain_id: self.chain_id(), }) @@ -1263,8 +1270,7 @@ where // 1. Collect all sender chain IDs and block hashes before clearing. let sender_ids = self.chain.inboxes.indices().await?; - let hashes = self.chain.confirmed_log.read(..).await?; - let preprocessed = self.chain.preprocessed_blocks.index_values().await?; + let block_hashes = self.chain.block_hashes.index_values().await?; // 2. Snapshot safety-critical manager state so that we cannot be tricked // into double-signing if the reset wipes votes we already cast. @@ -1287,17 +1293,7 @@ where ); // 4. Re-load certificates one at a time by hash and re-process them. - for (index, hash) in hashes.into_iter().enumerate() { - let height = BlockHeight(index as u64); - let cert = self - .storage - .read_certificate(hash) - .await? - .map(Arc::unwrap_or_clone) - .ok_or_else(|| WorkerError::LocalBlockNotFound { height, chain_id })?; - Box::pin(self.process_confirmed_block(cert, None)).await?; - } - for (height, hash) in preprocessed { + for (height, hash) in block_hashes { let cert = self .storage .read_certificate(hash) @@ -1373,7 +1369,7 @@ where let mut hashes = Vec::new(); let mut height = start; while height < end { - match self.chain.preprocessed_blocks.get(&height).await? { + match self.chain.block_hashes.get(&height).await? { Some(hash) => hashes.push(hash), None => break, } @@ -1421,7 +1417,7 @@ where &self, heights: Vec, ) -> Result, WorkerError> { - Ok(self.chain.block_hashes(heights).await?) + Ok(self.chain.block_hashes_for_heights(heights).await?) } /// Gets proposed blobs from the manager for specified blob IDs. @@ -1510,8 +1506,8 @@ where } /// Gets the next height to preprocess. - pub(crate) async fn get_next_height_to_preprocess(&self) -> Result { - Ok(self.chain.next_height_to_preprocess().await?) + pub(crate) fn get_next_height_to_preprocess(&self) -> BlockHeight { + *self.chain.next_height_to_preprocess.get() } /// Attempts to vote for a leader timeout, if possible. @@ -1643,7 +1639,7 @@ where &self, height: BlockHeight, ) -> Result>, WorkerError> { - let certificate_hash = match self.chain.confirmed_log.get(height.try_into()?).await? { + let certificate_hash = match self.chain.block_hashes.get(&height).await? { Some(hash) => hash, None => return Ok(None), }; @@ -2020,7 +2016,7 @@ where info.requested_pending_message_bundles = bundles; } let hashes = chain - .block_hashes(query.request_sent_certificate_hashes_by_heights) + .block_hashes_for_heights(query.request_sent_certificate_hashes_by_heights) .await?; info.requested_sent_certificate_hashes = hashes; if let Some(start) = query.request_received_log_excluding_first_n { @@ -2041,18 +2037,18 @@ where .previous_event_blocks .multi_get(&stream_ids) .await?; - let mut indices = Vec::new(); let mut streams_with_heights = Vec::new(); for (stream_id, height) in stream_ids.into_iter().zip(heights) { if let Some(height) = height { - let index = usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)?; - indices.push(index); streams_with_heights.push((stream_id, height)); } } - let hashes = chain.confirmed_log.multi_get(indices).await?; + let hashes = chain + .block_hashes + .multi_get(streams_with_heights.iter().map(|(_, height)| height)) + .await?; for (maybe_hash, (stream_id, height)) in hashes.into_iter().zip(streams_with_heights) { - let hash = maybe_hash.ok_or_else(|| WorkerError::ConfirmedLogEntryNotFound { + let hash = maybe_hash.ok_or_else(|| WorkerError::BlockHashNotFound { height, chain_id: info.chain_id, })?; diff --git a/linera-core/src/unit_tests/wasm_client_tests.rs b/linera-core/src/unit_tests/wasm_client_tests.rs index 4ef9b7fc404e..7cdfe0427a87 100644 --- a/linera-core/src/unit_tests/wasm_client_tests.rs +++ b/linera-core/src/unit_tests/wasm_client_tests.rs @@ -575,10 +575,7 @@ where .await?; assert_eq!(chain.tip_state.get().next_block_height.0, 0); assert_eq!( - chain - .preprocessed_blocks - .get(&cert.inner().height()) - .await?, + chain.block_hashes.get(&cert.inner().height()).await?, Some(cert.hash()) ); } diff --git a/linera-core/src/unit_tests/worker_tests.rs b/linera-core/src/unit_tests/worker_tests.rs index 7fc830e42b57..7beadbecc8bd 100644 --- a/linera-core/src/unit_tests/worker_tests.rs +++ b/linera-core/src/unit_tests/worker_tests.rs @@ -1962,7 +1962,7 @@ where }] if amount == Amount::from_tokens(995)), "Unexpected bundle", ); - assert_eq!(chain.confirmed_log.count(), 1); + assert_eq!(chain.tip_state.get().next_block_height, BlockHeight(1)); assert_eq!(Some(certificate.hash()), chain.tip_state.get().block_hash); let chain = env.worker().chain_state_view(chain_2).await?; assert!(chain.is_active().await?); @@ -2014,7 +2014,10 @@ where BlockHeight::from(1), new_sender_chain.tip_state.get().next_block_height ); - assert_eq!(new_sender_chain.confirmed_log.count(), 1); + assert_eq!( + new_sender_chain.tip_state.get().next_block_height, + BlockHeight(1) + ); assert_eq!( Some(certificate.hash()), new_sender_chain.tip_state.get().block_hash @@ -2070,7 +2073,7 @@ where BlockHeight::from(1), chain.tip_state.get().next_block_height ); - assert_eq!(chain.confirmed_log.count(), 1); + assert_eq!(chain.tip_state.get().next_block_height, BlockHeight(1)); assert_eq!(Some(certificate.hash()), chain.tip_state.get().block_hash); Ok(()) } @@ -2150,7 +2153,10 @@ where BlockHeight::from(1), chain_1_state.tip_state.get().next_block_height ); - assert_eq!(chain_1_state.confirmed_log.count(), 1); + assert_eq!( + chain_1_state.tip_state.get().next_block_height, + BlockHeight(1) + ); assert_eq!( Some(certificate.hash()), chain_1_state.tip_state.get().block_hash @@ -2226,7 +2232,7 @@ where }] if amount == Amount::from_tokens(10)), "Unexpected bundle", ); - assert_eq!(chain.confirmed_log.count(), 0); + assert_eq!(chain.tip_state.get().next_block_height, BlockHeight(0)); assert_eq!(None, chain.tip_state.get().block_hash); assert_eq!(chain.received_log.count(), 1); Ok(()) @@ -2466,7 +2472,10 @@ where && ownership.super_owners.is_empty() && ownership.owners.len() == 1 ); - assert_eq!(recipient_chain.confirmed_log.count(), 1); + assert_eq!( + recipient_chain.tip_state.get().next_block_height, + BlockHeight(1) + ); assert_eq!( recipient_chain.tip_state.get().block_hash, Some(certificate.hash()) @@ -4648,7 +4657,7 @@ where Some(&cert_1), ) .await; - // Process cert_2 on chain_1 so the block is in confirmed_log. + // Process cert_2 on chain_1 so the block is in block_hashes. env.worker() .process_confirmed_block(cert_2.clone(), None) .await?; diff --git a/linera-core/src/worker.rs b/linera-core/src/worker.rs index 21171e4cd415..72dc4069bbec 100644 --- a/linera-core/src/worker.rs +++ b/linera-core/src/worker.rs @@ -324,7 +324,7 @@ pub enum WorkerError { #[error(transparent)] ViewError(#[from] ViewError), - #[error("Certificates are in confirmed_log but not in storage: {0:?}")] + #[error("Certificates referenced from chain state are missing in storage: {0:?}")] ReadCertificatesError(Vec), #[error(transparent)] @@ -381,20 +381,8 @@ pub enum WorkerError { FastBlockUsingOracles, #[error("Blobs not found: {0:?}")] BlobsNotFound(Vec), - #[error("confirmed_log entry at height {height} for chain {chain_id:8} not found")] - ConfirmedLogEntryNotFound { - height: BlockHeight, - chain_id: ChainId, - }, - #[error("preprocessed_blocks entry at height {height} for chain {chain_id:8} not found")] - PreprocessedBlocksEntryNotFound { - height: BlockHeight, - chain_id: ChainId, - }, - #[error( - "confirmed_log/preprocessed_blocks entry at height {height} for chain {chain_id} not found" - )] - ConfirmedBlockHashNotFound { + #[error("Block hash at height {height} for chain {chain_id} not found")] + BlockHashNotFound { height: BlockHeight, chain_id: ChainId, }, @@ -443,9 +431,7 @@ impl WorkerError { WorkerError::BcsError(_) | WorkerError::InvalidCrossChainRequest | WorkerError::ViewError(_) - | WorkerError::ConfirmedLogEntryNotFound { .. } - | WorkerError::PreprocessedBlocksEntryNotFound { .. } - | WorkerError::ConfirmedBlockHashNotFound { .. } + | WorkerError::BlockHashNotFound { .. } | WorkerError::LocalBlockNotFound { .. } | WorkerError::MissingNetworkDescription | WorkerError::Thread(_) @@ -1717,7 +1703,7 @@ where chain_id: ChainId, ) -> Result { self.chain_read(chain_id, |guard| async move { - guard.get_next_height_to_preprocess().await + Ok(guard.get_next_height_to_preprocess()) }) .await } diff --git a/linera-explorer/src/components/Chain.vue b/linera-explorer/src/components/Chain.vue index 007dd3cb33a8..13b4a7bb8387 100644 --- a/linera-explorer/src/components/Chain.vue +++ b/linera-explorer/src/components/Chain.vue @@ -358,17 +358,17 @@ function pendingBundleCount(): number { - -
  • - Confirmed Log ({{ chain.confirmedLog.entries.length }}) + +
  • + Block Hashes ({{ chain.blockHashes.entries.length }})
  • -
    + diff --git a/linera-service-graphql-client/gql/service_requests.graphql b/linera-service-graphql-client/gql/service_requests.graphql index 019a657bf8cc..13cdfc7947ef 100644 --- a/linera-service-graphql-client/gql/service_requests.graphql +++ b/linera-service-graphql-client/gql/service_requests.graphql @@ -153,8 +153,11 @@ query Chain( fallbackOwners currentRound } - confirmedLog { - entries + blockHashes { + entries { + key + value + } } receivedLog { entries { diff --git a/linera-service-graphql-client/gql/service_schema.graphql b/linera-service-graphql-client/gql/service_schema.graphql index bc738d74fa7f..330167a210b4 100644 --- a/linera-service-graphql-client/gql/service_schema.graphql +++ b/linera-service-graphql-client/gql/service_schema.graphql @@ -349,10 +349,11 @@ type ChainStateExtendedView { """ pendingProposedBlobs: ReentrantCollectionView_AccountOwner_PendingBlobsView_d58d342d! """ - Hashes of all certified blocks for this sender. - This ends with `block_hash` and has length `usize::from(next_block_height)`. + Hashes of all known blocks in this chain, indexed by their height. A block at + `height < next_block_height` is executed; a block at `height >= next_block_height` + is preprocessed (verified but not yet executed) and may not be contiguous. """ - confirmedLog: LogView_CryptoHash_87fbb60c! + blockHashes: CustomMapView_BlockHeight_CryptoHash_1bae6d76! """ Sender chain and height of all certified blocks known as a receiver (local ordering). """ @@ -384,10 +385,6 @@ type ChainStateExtendedView { """ nonemptyOutboxes: [ChainId!]! """ - Blocks that have been verified but not executed yet, and that may not be contiguous. - """ - preprocessedBlocks: MapView_BlockHeight_CryptoHash_1bae6d76! - """ Inboxes with at least one pending added bundle. This allows us to avoid loading all inboxes. """ nonemptyInboxes: [ChainId!]! @@ -397,6 +394,17 @@ type ChainStateExtendedView { the last reset, the error is returned instead. """ blockZeroExecutedAt: Timestamp! + """ + The height at which the next block can be preprocessed: one past the highest + height in `block_hashes` (executed or preprocessed), or `next_block_height` if + `block_hashes` is empty. + + Maintained as an O(1) shortcut for `next_height_to_preprocess`, since + `CustomMapView` does not yet expose a `last_index` lookup. Once + `linera-views` gains efficient first/last key support, this field can be + removed in favor of `block_hashes.last_index()`. + """ + nextHeightToPreprocess: BlockHeight! } """ @@ -521,6 +529,12 @@ type Cursor { index: Int! } +type CustomMapView_BlockHeight_CryptoHash_1bae6d76 { + keys(count: Int): [BlockHeight!]! + entry(key: BlockHeight!): Entry_BlockHeight_CryptoHash_74e16b71! + entries(input: MapInput_BlockHeight_e824a938): [Entry_BlockHeight_CryptoHash_74e16b71!]! +} + """ A GraphQL-visible map item, complete with key. """ @@ -698,11 +712,6 @@ type LogView_ChainAndHeight_7af83576 { entries(start: Int, end: Int): [ChainAndHeight!]! } -type LogView_CryptoHash_87fbb60c { - count: Int! - entries(start: Int, end: Int): [CryptoHash!]! -} - input MapFilters_AccountOwner_d6668c53 { keys: [AccountOwner!] } @@ -764,13 +773,6 @@ type MapView_BlobId_Blob_9f0b41f3 { entries(input: MapInput_BlobId_4d2a0555): [Entry_BlobId_Blob_50b95aa1!]! } -type MapView_BlockHeight_CryptoHash_1bae6d76 { - keys(count: Int): [BlockHeight!]! - count: Int! - entry(key: BlockHeight!): Entry_BlockHeight_CryptoHash_74e16b71! - entries(input: MapInput_BlockHeight_e824a938): [Entry_BlockHeight_CryptoHash_74e16b71!]! -} - type MapView_StreamId_Int_3d7f5335 { keys(count: Int): [StreamId!]! count: Int! diff --git a/linera-service/src/cli/main.rs b/linera-service/src/cli/main.rs index 50bb70aa54f2..2d2371b6a9a0 100644 --- a/linera-service/src/cli/main.rs +++ b/linera-service/src/cli/main.rs @@ -1837,7 +1837,7 @@ impl Runnable for Job { .await .context("Failed to load chain")?; let block_hash = chain_state_view - .block_hashes([height]) + .block_hashes_for_heights([height]) .await .context("Failed to find a block hash for the given height")?[0]; let block = context diff --git a/linera-views/src/common.rs b/linera-views/src/common.rs index da95e97124bb..05ad13bd0330 100644 --- a/linera-views/src/common.rs +++ b/linera-views/src/common.rs @@ -303,6 +303,19 @@ impl CustomSerialize for u128 { } } +impl CustomSerialize for linera_base::data_types::BlockHeight { + fn to_custom_bytes(&self) -> Result, ViewError> { + Ok(self.0.to_be_bytes().to_vec()) + } + + fn from_custom_bytes(bytes: &[u8]) -> Result { + let array: [u8; 8] = bytes + .try_into() + .map_err(|_| ViewError::PostLoadValuesError)?; + Ok(Self(u64::from_be_bytes(array))) + } +} + /// This computes the offset of the BCS serialization of a vector. /// The formula that should be satisfied is /// `serialized_size(vec![v_1, ...., v_n]) = get_uleb128_size(n)`