{"record":{"id":"edb20c268caaf977","repo":"linera-io/linera-protocol","slug":"fast-blocks-cannot-query-oracles","errorCode":null,"errorMessage":"Fast blocks cannot query oracles","messagePattern":"Fast blocks cannot query oracles","errorType":"validation","errorClass":"WorkerError","httpStatus":null,"severity":"error","filePath":"linera-core/src/chain_worker/state.rs","lineNumber":2648,"sourceCode":"\n        self.chain\n            .remove_bundles_from_inboxes(block.timestamp, true, block.incoming_bundles())\n            .await?;\n        let block = if let Some(outcome) = outcome {\n            outcome.clone().with(proposal.content.block.clone())\n        } else {\n            let (executed_block, _resource_tracker, _) = Box::pin(self.execute_block(\n                block.clone(),\n                local_time,\n                round.multi_leader(),\n                &published_blobs,\n                BlockExecution::HandleProposal,\n            ))\n            .await?;\n            executed_block\n        };\n\n        ensure!(\n            !round.is_fast() || !block.has_oracle_responses(),\n            WorkerError::FastBlockUsingOracles\n        );\n        let chain = &mut self.chain;\n        // Don't save the changes since the block is not confirmed yet.\n        chain.rollback();\n\n        // Create the vote and store it in the chain state.\n        let blobs = self\n            .get_required_blobs(proposal.expected_blob_ids(), block.created_blobs())\n            .await?;\n        let key_pair = self.config.key_pair();\n        let manager = &mut self.chain.manager;\n        match manager.create_vote(&proposal, block, key_pair, local_time, blobs)? {\n            // Cache the value we voted on, so the client doesn't have to send it again.\n            Some(Either::Left(vote)) => {\n                self.block_values\n                    .insert_hashed(Cow::Borrowed(vote.value.inner()));","sourceCodeStart":2630,"sourceCodeEnd":2666,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-core/src/chain_worker/state.rs#L2630-L2666","documentation":"Fast rounds let super owners finalize blocks without a full quorum, so validators must be able to verify the block purely from its content. After executing the proposal, try_handle_block_proposal checks that a fast-round block produced no oracle responses (block.has_oracle_responses()); any oracle query in the outcome makes fast-path validation impossible and the proposal is rejected with FastBlockUsingOracles.","triggerScenarios":"Proposing in Round::Fast a block whose execution queries oracles (time or benchmark queries, application service oracles); oracle-dependent operations left inside a block that a super owner submits as fast.","commonSituations":"Applications that routinely query oracles (timers, price feeds, application oracles) and default to fast proposals on super-owner chains; tests reusing fast blocks that gained oracle-reading operations after an app upgrade.","solutions":["Propose oracle-using blocks in a multi-leader or single-leader round instead of Round::Fast.","Split the block: oracle-free operations may go fast, oracle queries go through a regular round.","After trial execution, check has_oracle_responses() on the outcome and downgrade the round before submitting."],"exampleFix":"// before: fast round for a block that queries oracles\nlet round = Round::Fast;\n\n// after: choose the round based on the trial outcome\nlet executed = trial_execute(&block).await?;\nlet round = if executed.has_oracle_responses() {\n    Round::MultiLeader(0)\n} else {\n    Round::Fast\n};","handlingStrategy":"validation","validationCode":"// After trial execution, before settling on the fast round.\nif round.is_fast() && executed_block.has_oracle_responses() {\n    // Downgrade to a regular round; validators will reject a fast submission.\n    round = Round::MultiLeader(0);\n}","typeGuard":"// Fast-round eligibility check on an executed block.\nfn is_fast_round_safe(block: &Block) -> bool {\n    !block.has_oracle_responses()\n}","tryCatchPattern":"match node.handle_block_proposal(proposal).await {\n    Err(NodeError::WorkerError(err)) if matches!(*err, WorkerError::FastBlockUsingOracles) => {\n        // Re-propose the same block in a non-fast round (e.g. MultiLeader(0)).\n    }\n    result => result,\n}","preventionTips":["Default to regular rounds; use fast only for provably oracle-free blocks.","Run a trial execution and check has_oracle_responses() before choosing Round::Fast.","Keep oracle-querying operations (timers, price feeds, application oracles) out of fast blocks.","Treat this rejection as an automatic round downgrade, not a permanent failure."],"tags":["rust","linera","blockchain","consensus","fast-round","oracles"],"backgroundTag":"fast-path-unsupported-operation","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}