Skip to content

Commit fa20920

Browse files
starknet_api: add PROOF_VERSION_V1 and accept either marker in ProofFactsVariant
1 parent ae57de9 commit fa20920

3 files changed

Lines changed: 117 additions & 10 deletions

File tree

crates/blockifier/src/transaction/account_transactions_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2222,7 +2222,7 @@ fn test_missing_validate_entrypoint_rejects(
22222222
/// Converts SnosProofFacts to ProofFacts for testing.
22232223
fn snos_to_proof_facts(snos: SnosProofFacts) -> ProofFacts {
22242224
vec![
2225-
snos.proof_version,
2225+
snos.proof_version.as_felt(),
22262226
VIRTUAL_SNOS,
22272227
snos.program_hash,
22282228
VIRTUAL_OS_OUTPUT_VERSION,

crates/starknet_api/src/transaction/fields.rs

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ use crate::hash::StarkHash;
1414
use crate::serde_utils::PrefixedBytesAsHex;
1515
use crate::{StarknetApiError, StarknetApiResult};
1616

17+
#[cfg(test)]
18+
#[path = "fields_test.rs"]
19+
mod fields_test;
20+
1721
pub const HIGH_GAS_AMOUNT: u64 = 10000000000; // A high gas amount that should be enough for execution.
1822

1923
/// A fee.
@@ -633,6 +637,53 @@ pub const VIRTUAL_SNOS: Felt = Felt::from_hex_unchecked("0x5649525455414c5f534e4
633637
// Represent the `PROOF_VERSION_V0` marker as a Felt ('PROOF0').
634638
pub const PROOF_VERSION_V0: Felt = Felt::from_hex_unchecked("0x50524f4f4630");
635639

640+
// Represent the `PROOF_VERSION_V1` marker as a Felt ('PROOF1').
641+
pub const PROOF_VERSION_V1: Felt = Felt::from_hex_unchecked("0x50524f4f4631");
642+
643+
/// Supported proof-facts version markers.
644+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
645+
pub enum ProofVersion {
646+
V0,
647+
V1,
648+
}
649+
650+
impl ProofVersion {
651+
/// Felt (Cairo short-string) representation written into proof facts.
652+
pub const fn as_felt(self) -> Felt {
653+
match self {
654+
ProofVersion::V0 => PROOF_VERSION_V0,
655+
ProofVersion::V1 => PROOF_VERSION_V1,
656+
}
657+
}
658+
659+
/// Human-readable short-string label (matches the Cairo constant value).
660+
pub const fn as_str(self) -> &'static str {
661+
match self {
662+
ProofVersion::V0 => "PROOF0",
663+
ProofVersion::V1 => "PROOF1",
664+
}
665+
}
666+
}
667+
668+
impl TryFrom<Felt> for ProofVersion {
669+
type Error = ();
670+
fn try_from(value: Felt) -> Result<Self, Self::Error> {
671+
if value == PROOF_VERSION_V0 {
672+
Ok(ProofVersion::V0)
673+
} else if value == PROOF_VERSION_V1 {
674+
Ok(ProofVersion::V1)
675+
} else {
676+
Err(())
677+
}
678+
}
679+
}
680+
681+
impl fmt::Display for ProofVersion {
682+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
683+
write!(f, "{} ({})", self.as_felt(), self.as_str())
684+
}
685+
}
686+
636687
/// The version of the virtual OS output (short string 'VIRTUAL_SNOS0').
637688
/// This must match the Cairo constant `VIRTUAL_OS_OUTPUT_VERSION` in `virtual_os_output.cairo`.
638689
pub const VIRTUAL_OS_OUTPUT_VERSION: Felt =
@@ -653,6 +704,11 @@ impl ProofFacts {
653704
pub fn hash(&self) -> Felt {
654705
HashChain::new().chain_iter(self.0.iter()).get_poseidon_hash()
655706
}
707+
708+
/// Returns the proof version marker (first felt). `Felt::ZERO` for empty proof facts.
709+
pub fn proof_version_felt(&self) -> Felt {
710+
self.0.first().copied().unwrap_or_default()
711+
}
656712
}
657713

658714
/// Represents the variants of proof facts associated with a transaction.
@@ -681,13 +737,17 @@ impl TryFrom<&ProofFacts> for ProofFactsVariant {
681737
)));
682738
};
683739

684-
// Validate that the first element is PROOF_VERSION_V0.
685-
if *proof_version != PROOF_VERSION_V0 {
686-
return Err(StarknetApiError::InvalidProofFacts(format!(
687-
"Expected first field to be {} (PROOF_VERSION_V0), but got {}",
688-
PROOF_VERSION_V0, proof_version
689-
)));
690-
}
740+
// Validate that the first element is a supported proof version marker.
741+
let proof_version = ProofVersion::try_from(*proof_version).map_err(|()| {
742+
StarknetApiError::InvalidProofFacts(format!(
743+
"Expected first field to be {} ({}) or {} ({}), but got {}",
744+
ProofVersion::V0.as_felt(),
745+
ProofVersion::V0.as_str(),
746+
ProofVersion::V1.as_felt(),
747+
ProofVersion::V1.as_str(),
748+
proof_version,
749+
))
750+
})?;
691751

692752
// Validate that the second element is VIRTUAL_SNOS.
693753
if *variant_marker != VIRTUAL_SNOS {
@@ -725,7 +785,7 @@ impl TryFrom<&ProofFacts> for ProofFactsVariant {
725785
})?);
726786

727787
Ok(ProofFactsVariant::Snos(SnosProofFacts {
728-
proof_version: *proof_version,
788+
proof_version,
729789
program_hash: *program_hash,
730790
block_number,
731791
block_hash: BlockHash(*block_hash),
@@ -738,7 +798,7 @@ impl TryFrom<&ProofFacts> for ProofFactsVariant {
738798
///
739799
/// A valid SNOS proof facts structure must include these fields as its first five entries.
740800
pub struct SnosProofFacts {
741-
pub proof_version: Felt,
801+
pub proof_version: ProofVersion,
742802
pub program_hash: StarkHash,
743803
pub block_number: BlockNumber,
744804
pub block_hash: BlockHash,
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use rstest::rstest;
2+
3+
use super::*;
4+
5+
/// Returns SNOS-shaped `ProofFacts` whose first felt is the given proof version.
6+
fn proof_facts_given_proof_version(proof_version: Felt) -> ProofFacts {
7+
let mut facts = ProofFacts::snos_proof_facts_for_testing();
8+
Arc::make_mut(&mut facts.0)[0] = proof_version;
9+
facts
10+
}
11+
12+
#[rstest]
13+
#[case::v0(ProofVersion::V0)]
14+
#[case::v1(ProofVersion::V1)]
15+
fn proof_facts_variant_accepts_supported_versions(#[case] version: ProofVersion) {
16+
let variant = ProofFactsVariant::try_from(&proof_facts_given_proof_version(version.as_felt()))
17+
.expect("supported version should parse");
18+
match variant {
19+
ProofFactsVariant::Snos(snos) => assert_eq!(snos.proof_version, version),
20+
ProofFactsVariant::Empty => panic!("expected Snos variant"),
21+
}
22+
}
23+
24+
#[test]
25+
fn proof_facts_variant_rejects_unknown_version() {
26+
let facts = proof_facts_given_proof_version(Felt::from_hex_unchecked("0xDEAD"));
27+
assert!(matches!(
28+
ProofFactsVariant::try_from(&facts),
29+
Err(StarknetApiError::InvalidProofFacts(_))
30+
));
31+
}
32+
33+
/// Encodes a Cairo short-string into a felt (big-endian, right-aligned).
34+
fn short_string_to_felt(s: &str) -> Felt {
35+
let bytes = s.as_bytes();
36+
assert!(bytes.len() <= 32, "short string exceeds felt width");
37+
let mut padded = [0u8; 32];
38+
padded[32 - bytes.len()..].copy_from_slice(bytes);
39+
Felt::from_bytes_be(&padded)
40+
}
41+
42+
#[rstest]
43+
#[case::v0(ProofVersion::V0)]
44+
#[case::v1(ProofVersion::V1)]
45+
fn proof_version_str_encodes_to_felt(#[case] version: ProofVersion) {
46+
assert_eq!(short_string_to_felt(version.as_str()), version.as_felt());
47+
}

0 commit comments

Comments
 (0)