Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- `starknet_getEvents` now returns `block_number` for events from pre-confirmed and pre-latest blocks.
- BREAKING: JSON-RPC input no longer accepts hex strings that are missing the `0x` prefix, in line with the Starknet API spec.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've never used BREAKING in the CHANGELOG.

I'd suggest we remove it. We'll highlight it in the release notes (+ major version change).


### Fixed

Expand Down
2 changes: 1 addition & 1 deletion crates/block-commitments/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ mod meta {
const MAINNET_METAINFO: BlockHashMetaInfo = BlockHashMetaInfo {
first_0_7_block: BlockNumber::new_or_panic(833),
fallback_sequencer_address: Some(sequencer_address!(
"021f4b90b0377c82bf330b7b5295820769e72d79d8acd0effa0ebde6e9988bc5"
"0x021f4b90b0377c82bf330b7b5295820769e72d79d8acd0effa0ebde6e9988bc5"
)),
};

Expand Down
19 changes: 12 additions & 7 deletions crates/class-hash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -878,13 +878,18 @@ pub mod json {
fn cairo_0_8() {
// Cairo 0.8 update broke our class hash calculation by adding new attribute
// fields (which we now need to ignore if empty).
assert_eq!(
// Known contract which triggered a hash mismatch failure.
hash(CAIRO_0_8_NEW_ATTRIBUTES),
ComputedClassHash::Cairo(class_hash!(
"056b96c1d1bbfa01af44b465763d1b71150fa00c6c9d54c3947f57e979ff68c3"
))
);
let expected = ComputedClassHash::Cairo(class_hash!(
"056b96c1d1bbfa01af44b465763d1b71150fa00c6c9d54c3947f57e979ff68c3"
));

// Known contract which triggered a hash mismatch failure.
let extract = tokio::task::spawn_blocking(move || -> anyhow::Result<_> {
let hash = compute_class_hash(CAIRO_0_8_NEW_ATTRIBUTES)?;
Ok(hash)
});
let calculated_hash = extract.await.unwrap().unwrap();

assert_eq!(calculated_hash, expected);
Comment on lines +881 to +892
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this at all related to this PR? 👀

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a rebase related hiccup (I refactored this test to use the hash() helper and it was merged to main). This change should be reverted.

}

#[test]
Expand Down
6 changes: 3 additions & 3 deletions crates/common/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use crate::macro_prelude::block_hash;
use crate::BlockHash;

pub const MAINNET_GENESIS_HASH: BlockHash =
block_hash!("047C3637B57C2B079B93C61539950C17E868A28F46CDEF28F88521067F21E943");
block_hash!("0x047C3637B57C2B079B93C61539950C17E868A28F46CDEF28F88521067F21E943");

pub const SEPOLIA_TESTNET_GENESIS_HASH: BlockHash =
block_hash!("5c627d4aeb51280058bed93c7889bce78114d63baad1be0f0aeb32496d5f19c");
block_hash!("0x5c627d4aeb51280058bed93c7889bce78114d63baad1be0f0aeb32496d5f19c");

pub const SEPOLIA_INTEGRATION_GENESIS_HASH: BlockHash =
block_hash!("19f675d3fb226821493a6ab9a1955e384bba80f130de625621a418e9a7c0ca3");
block_hash!("0x19f675d3fb226821493a6ab9a1955e384bba80f130de625621a418e9a7c0ca3");
12 changes: 6 additions & 6 deletions crates/common/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ fn starkhash_to_dec_str(h: &Felt) -> String {

/// A helper conversion function. Only use with __sequencer API related types__.
fn starkhash_from_dec_str(s: &str) -> Result<Felt, anyhow::Error> {
match BigUint::from_str(s) {
Ok(b) => {
let h = Felt::from_be_slice(&b.to_bytes_be())?;
Ok(h)
}
// The order here matters because `Felt::from_hex_str` requires the '0x'
// prefix, so we'll never parse a hex string as a decimal string by mistake.
match Felt::from_hex_str(s) {
Ok(h) => Ok(h),
Err(_) => {
let h = Felt::from_hex_str(s)?;
let b = BigUint::from_str(s)?;
let h = Felt::from_be_slice(&b.to_bytes_be())?;
Ok(h)
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/common/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,9 @@ macro_rules! felt {
Err(pathfinder_crypto::HexParseError::InvalidLength { .. }) => {
panic!("Too many hex digits")
}
Err(pathfinder_crypto::HexParseError::MissingPrefix) => {
panic!("Missing '0x' prefix")
}
Err(pathfinder_crypto::HexParseError::Overflow) => panic!("Felt overflow"),
};
CONST_FELT
Expand Down
94 changes: 47 additions & 47 deletions crates/crypto/src/algebra/field/felt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,8 @@ impl From<CurveOrderMontFelt> for Felt {
impl Felt {
/// A convenience function which parses a hex string into a [Felt].
///
/// Supports both upper and lower case hex strings, as well as an optional
/// "0x" prefix.
/// Supports both upper and lower case hex strings. The '0x' prefix is
/// mandatory.
pub const fn from_hex_str(hex_str: &str) -> Result<Self, HexParseError> {
const fn parse_hex_digit(digit: u8) -> Result<u8, HexParseError> {
match digit {
Expand All @@ -336,19 +336,16 @@ impl Felt {
}

let bytes = hex_str.as_bytes();
let start = if bytes.len() >= 2 && bytes[0] == b'0' && bytes[1] == b'x' {
2
} else {
0
let len = match bytes {
[b'0', b'x', rest @ ..] if rest.len() > 64 => {
return Err(HexParseError::InvalidLength {
max: 64,
actual: rest.len(),
})
}
[b'0', b'x', rest @ ..] => rest.len(),
_ => return Err(HexParseError::MissingPrefix),
};
let len = bytes.len() - start;

if len > 64 {
return Err(HexParseError::InvalidLength {
max: 64,
actual: bytes.len(),
});
}

let mut buf = [0u8; 32];

Expand All @@ -358,7 +355,7 @@ impl Felt {
// Handle a possible odd nibble remaining nibble.
if len % 2 == 1 {
let idx = len / 2;
buf[31 - idx] = match parse_hex_digit(bytes[start]) {
buf[31 - idx] = match parse_hex_digit(bytes[2]) {
Ok(b) => b,
Err(e) => return Err(e),
};
Expand Down Expand Up @@ -461,6 +458,7 @@ impl Felt {
pub enum HexParseError {
InvalidNibble(u8),
InvalidLength { max: usize, actual: usize },
MissingPrefix,
Overflow,
}

Expand All @@ -479,6 +477,7 @@ impl std::fmt::Display for HexParseError {
Self::InvalidLength { max, actual } => {
f.write_fmt(format_args!("More than {} digits found: {}", *max, *actual))
}
Self::MissingPrefix => f.write_str("Missing '0x' prefix"),
Self::Overflow => f.write_str(OVERFLOW_MSG),
}
}
Expand All @@ -498,7 +497,7 @@ mod tests {

#[test]
fn view_bits() {
let one = Felt::from_hex_str("1").unwrap();
let one = Felt::from_hex_str("0x1").unwrap();

let one = one.view_bits().to_bitvec();

Expand Down Expand Up @@ -566,7 +565,7 @@ mod tests {

#[test]
fn round_trip() {
let original = Felt::from_hex_str("abcdef0123456789").unwrap();
let original = Felt::from_hex_str("0xabcdef0123456789").unwrap();
let bytes = original.to_be_bytes();
let result = Felt::from_be_slice(&bytes[..]).unwrap();

Expand All @@ -575,15 +574,15 @@ mod tests {

#[test]
fn too_long() {
let original = Felt::from_hex_str("abcdef0123456789").unwrap();
let original = Felt::from_hex_str("0xabcdef0123456789").unwrap();
let mut bytes = original.to_be_bytes().to_vec();
bytes.push(0);
Felt::from_be_slice(&bytes[..]).unwrap_err();
}

#[test]
fn short_slice() {
let original = Felt::from_hex_str("abcdef0123456789").unwrap();
let original = Felt::from_hex_str("0xabcdef0123456789").unwrap();
let bytes = original.to_be_bytes();
let result = Felt::from_be_slice(&bytes[24..]);

Expand All @@ -610,50 +609,50 @@ mod tests {

#[test]
fn debug() {
let hex_str = "1234567890abcdef000edcba0987654321";
let hex_str = "0x1234567890abcdef000edcba0987654321";
let felt = Felt::from_hex_str(hex_str).unwrap();
let result = format!("{felt:?}");

let mut expected = "0".repeat(64 - hex_str.len());
expected.push_str(hex_str);
let expected = format!("Felt({felt})");
let leading_zeros = "0".repeat(64 + 2 - hex_str.len());
let hex_upper_stripped = hex_str.strip_prefix("0x").unwrap().to_uppercase();
let expected = format!("Felt(0x{leading_zeros}{hex_upper_stripped})");

assert_eq!(result, expected);
}

#[test]
fn fmt() {
let hex_str = "1234567890abcdef000edcba0987654321";
let hex_str = "0x1234567890abcdef000edcba0987654321";
let starkhash = Felt::from_hex_str(hex_str).unwrap();
let result = format!("{starkhash:x}");

let mut expected = "0".repeat(64 - hex_str.len());
expected.push_str(hex_str);
let mut expected = "0".repeat(64 - hex_str.len() + 2);
expected.push_str(&hex_str[2..]);

// We don't really care which casing is used by fmt.
assert_eq!(result.to_lowercase(), expected.to_lowercase());
}

#[test]
fn lower_hex() {
let hex_str = "1234567890abcdef000edcba0987654321";
let hex_str = "0x1234567890abcdef000edcba0987654321";
let starkhash = Felt::from_hex_str(hex_str).unwrap();
let result = format!("{starkhash:x}");

let mut expected = "0".repeat(64 - hex_str.len());
expected.push_str(hex_str);
let mut expected = "0".repeat(64 - hex_str.len() + 2);
expected.push_str(&hex_str[2..]);

assert_eq!(result, expected.to_lowercase());
}

#[test]
fn upper_hex() {
let hex_str = "1234567890abcdef000edcba0987654321";
let hex_str = "0x1234567890abcdef000edcba0987654321";
let starkhash = Felt::from_hex_str(hex_str).unwrap();
let result = format!("{starkhash:X}");

let mut expected = "0".repeat(64 - hex_str.len());
expected.push_str(hex_str);
let mut expected = "0".repeat(64 - hex_str.len() + 2);
expected.push_str(&hex_str[2..]);

assert_eq!(result, expected.to_uppercase());
}
Expand Down Expand Up @@ -686,27 +685,13 @@ mod tests {

#[test]
fn simple() {
let (test_str, expected) = test_data();
let uut = Felt::from_hex_str(test_str).unwrap();
assert_eq!(uut, expected);
}

#[test]
fn prefix() {
let (test_str, expected) = test_data();
let uut = Felt::from_hex_str(&format!("0x{test_str}")).unwrap();
assert_eq!(uut, expected);
}

#[test]
fn leading_zeros() {
let (test_str, expected) = test_data();
let uut = Felt::from_hex_str(&format!("000000000{test_str}")).unwrap();
assert_eq!(uut, expected);
}

#[test]
fn prefix_and_leading_zeros() {
let (test_str, expected) = test_data();
let uut = Felt::from_hex_str(&format!("0x000000000{test_str}")).unwrap();
assert_eq!(uut, expected);
Expand All @@ -717,9 +702,24 @@ mod tests {
assert_matches!(Felt::from_hex_str("0x123z").unwrap_err(), HexParseError::InvalidNibble(n) => assert_eq!(n, b'z'))
}

#[test]
fn missing_prefix() {
assert_matches!(
Felt::from_hex_str("123").unwrap_err(),
HexParseError::MissingPrefix
)
}

#[test]
fn invalid_len() {
assert_matches!(Felt::from_hex_str(&"1".repeat(65)).unwrap_err(), HexParseError::InvalidLength{max: 64, actual: n} => assert_eq!(n, 65))
let invalid_str = "0".repeat(65);
assert_matches!(
Felt::from_hex_str(&format!("0x{invalid_str}")).unwrap_err(),
HexParseError::InvalidLength {
max: 64,
actual: 65
}
);
}

#[test]
Expand Down
6 changes: 5 additions & 1 deletion crates/crypto/src/algebra/field/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ mod tests {

#[test]
fn empty() {
assert_eq!(serde_json::from_str::<Felt>(r#""""#).unwrap(), Felt::ZERO);
assert!(serde_json::from_str::<Felt>(r#""""#)
.unwrap_err()
.to_string()
// Serde errors also include the line/column.
.starts_with("Missing '0x' prefix"));
assert_eq!(serde_json::from_str::<Felt>(r#""0x""#).unwrap(), Felt::ZERO);
}

Expand Down
21 changes: 12 additions & 9 deletions crates/crypto/src/hash/pedersen/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,18 @@ mod tests {
#[test]
fn test_pedersen() {
// Test vector from https://github.com/starkware-libs/crypto-cpp/blob/master/src/starkware/crypto/pedersen_hash_test.cc
let a =
Felt::from_hex_str("03d937c035c878245caf64531a5756109c53068da139362728feb561405371cb")
.unwrap();
let b =
Felt::from_hex_str("0208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a")
.unwrap();
let expected =
Felt::from_hex_str("030e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662")
.unwrap();
let a = Felt::from_hex_str(
"0x03d937c035c878245caf64531a5756109c53068da139362728feb561405371cb",
)
.unwrap();
let b = Felt::from_hex_str(
"0x0208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a",
)
.unwrap();
let expected = Felt::from_hex_str(
"0x030e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662",
)
.unwrap();

let hash = pedersen_hash(a, b);
assert_eq!(hash, expected);
Expand Down
6 changes: 4 additions & 2 deletions crates/crypto/src/hash/poseidon/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,10 @@ mod tests {
#[test]
fn test_sponge() {
let expected_result = MontFelt::from(
Felt::from_hex_str("07b8f30ac298ea12d170c0873f1fa631a18c00756c6e7d1fd273b9a239d0d413")
.unwrap(),
Felt::from_hex_str(
"0x07b8f30ac298ea12d170c0873f1fa631a18c00756c6e7d1fd273b9a239d0d413",
)
.unwrap(),
);

// Construct messages, the first few integers
Expand Down
Loading
Loading