Skip to content

Commit e7e7abf

Browse files
starknet_transaction_prover: make prover forward compatible
1 parent 3eb5573 commit e7e7abf

2 files changed

Lines changed: 69 additions & 4 deletions

File tree

crates/starknet_transaction_prover/src/running/virtual_block_executor.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ use blockifier_reexecution::state_reader::prefetched_state_reader::{
2323
use blockifier_reexecution::state_reader::rpc_objects::{BlockHeader, BlockId};
2424
use blockifier_reexecution::state_reader::rpc_state_reader::RpcStateReader;
2525
use serde::{Deserialize, Serialize};
26-
use starknet_api::block::{BlockHash, BlockInfo};
26+
use starknet_api::block::{BlockHash, BlockInfo, StarknetVersion};
2727
use starknet_api::block_hash::block_hash_calculator::{concat_counts, BlockHeaderCommitments};
2828
use starknet_api::contract_class::SierraVersion;
2929
use starknet_api::core::ClassHash;
3030
use starknet_api::transaction::fields::Fee;
3131
use starknet_api::transaction::{InvokeTransaction, MessageToL1, Transaction, TransactionHash};
3232
use starknet_api::versioned_constants_logic::VersionedConstantsTrait;
33-
use tracing::error;
33+
use tracing::{error, warn};
3434

3535
use crate::errors::VirtualBlockExecutorError;
3636

@@ -85,13 +85,45 @@ pub(crate) struct BaseBlockInfo {
8585
pub(crate) prev_base_block_hash: BlockHash,
8686
}
8787

88+
/// Parses the block header's Starknet version, tolerating versions newer than this binary knows.
89+
///
90+
/// `StarknetVersion` is a closed enum, so a block produced by a Starknet version released after
91+
/// this binary was built can't be parsed. The prover runs the OS with the latest versioned
92+
/// constants regardless of the block's version, so a *strictly newer* version is treated as
93+
/// `StarknetVersion::LATEST`. Older or malformed versions still error — we only forgive the
94+
/// forward-compatibility case, never mask genuinely unsupported data.
95+
pub(crate) fn forward_compatible_starknet_version(
96+
raw_version: &str,
97+
) -> Result<StarknetVersion, VirtualBlockExecutorError> {
98+
if let Ok(version) = StarknetVersion::try_from(raw_version.to_string()) {
99+
return Ok(version);
100+
}
101+
let latest = StarknetVersion::LATEST;
102+
// Compare numerically: Vec<u8> orders lexicographically, matching version ordering
103+
// (major, minor, patch, [fourth]).
104+
let version_bytes: Result<Vec<u8>, _> =
105+
raw_version.split('.').map(|component| component.parse::<u8>()).collect();
106+
match version_bytes {
107+
Ok(version_bytes) if version_bytes > Vec::<u8>::from(latest) => {
108+
warn!(
109+
"Block header reports unknown Starknet version '{raw_version}', newer than the \
110+
latest known version {latest}; falling back to {latest} for proving."
111+
);
112+
Ok(latest)
113+
}
114+
_ => Err(VirtualBlockExecutorError::TransactionExecutionError(format!(
115+
"Unsupported Starknet version '{raw_version}' (latest known: {latest})"
116+
))),
117+
}
118+
}
119+
88120
impl BaseBlockInfo {
89121
/// Creates a `BaseBlockInfo` from a block header and chain info.
90122
///
91123
/// When `use_latest_versioned_constants` is `true`, the latest versioned constants are used
92124
/// instead of the ones matching the block's Starknet version.
93125
pub(crate) fn new(
94-
header: BlockHeader,
126+
mut header: BlockHeader,
95127
chain_info: ChainInfo,
96128
use_latest_versioned_constants: bool,
97129
) -> Result<Self, VirtualBlockExecutorError> {
@@ -110,6 +142,10 @@ impl BaseBlockInfo {
110142
),
111143
};
112144

145+
// Forward compatibility: tolerate versions newer than this binary knows (see helper).
146+
let starknet_version = forward_compatible_starknet_version(&header.starknet_version)?;
147+
header.starknet_version = starknet_version.to_string();
148+
113149
let block_info: BlockInfo = header.try_into().map_err(|e| {
114150
VirtualBlockExecutorError::TransactionExecutionError(format!(
115151
"Failed to convert block header to block info: {e}"

crates/starknet_transaction_prover/src/running/virtual_block_executor_test.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use blockifier_reexecution::state_reader::rpc_objects::BlockId;
66
use blockifier_reexecution::utils::get_chain_info;
77
use rstest::rstest;
88
use starknet_api::abi::abi_utils::{get_storage_var_address, selector_from_name};
9-
use starknet_api::block::BlockNumber;
9+
use starknet_api::block::{BlockNumber, StarknetVersion};
1010
use starknet_api::core::{ChainId, ContractAddress, Nonce};
1111
use starknet_api::test_utils::invoke::invoke_tx;
1212
use starknet_api::transaction::fields::ValidResourceBounds;
@@ -15,6 +15,7 @@ use starknet_api::{calldata, felt, invoke_tx_args};
1515

1616
use crate::errors::VirtualBlockExecutorError;
1717
use crate::running::virtual_block_executor::{
18+
forward_compatible_starknet_version,
1819
RpcVirtualBlockExecutor,
1920
RpcVirtualBlockExecutorConfig,
2021
VirtualBlockExecutor,
@@ -28,6 +29,34 @@ use crate::test_utils::{
2829
TEST_BLOCK_NUMBER,
2930
};
3031

32+
/// A known version parses to its exact variant; a version strictly newer than the latest known
33+
/// version falls back to `StarknetVersion::LATEST` so newer blocks don't block proving.
34+
#[test]
35+
fn test_forward_compatible_version_falls_back_for_newer() {
36+
assert_matches!(
37+
forward_compatible_starknet_version("0.14.2"),
38+
Ok(version) if version == StarknetVersion::V0_14_2
39+
);
40+
assert_matches!(
41+
forward_compatible_starknet_version("0.14.3"),
42+
Ok(version) if version == StarknetVersion::LATEST
43+
);
44+
assert_matches!(
45+
forward_compatible_starknet_version("0.99.0"),
46+
Ok(version) if version == StarknetVersion::LATEST
47+
);
48+
}
49+
50+
/// Unknown versions that are older than the latest known version, or malformed, must error —
51+
/// the fallback only forgives strictly-newer versions, it never masks genuinely bad data.
52+
#[test]
53+
fn test_forward_compatible_version_rejects_older_and_malformed() {
54+
// [0, 13, 99] < [0, 14, 2], so this is an unknown *older* version.
55+
assert_matches!(forward_compatible_starknet_version("0.13.99"), Err(_));
56+
assert_matches!(forward_compatible_starknet_version("not.a.version"), Err(_));
57+
assert_matches!(forward_compatible_starknet_version(""), Err(_));
58+
}
59+
3160
/// Constructs an Invoke transaction that calls `balanceOf` on the STRK token contract.
3261
///
3362
/// Since we skip validation and fee charging, we can use dummy values for signature,

0 commit comments

Comments
 (0)