Skip to content

Commit a25e4f9

Browse files
committed
feat: modify ContracAddress upper bound to be PATRICIA_KEY_UPPER_BOUND -256
1 parent 9229fbb commit a25e4f9

File tree

3 files changed

+139
-26
lines changed

3 files changed

+139
-26
lines changed

crates/starknet-types-core/src/contract_address.rs

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ use core::str::FromStr;
99

1010
use crate::{
1111
felt::Felt,
12-
patricia_key::{PatriciaKey, PatriciaKeyFromFeltError, PatriciaKeyFromStrError},
12+
patricia_key::{
13+
PatriciaKey, PatriciaKeyFromFeltError, PatriciaKeyFromStrError,
14+
STORAGE_LEAF_ADDRESS_UPPER_BOUND,
15+
},
1316
};
1417

1518
#[repr(transparent)]
@@ -31,7 +34,9 @@ impl ContractAddress {
3134
/// Lower inclusive bound
3235
pub const LOWER_BOUND: Self = Self::ZERO;
3336
/// Upper non-inclusive bound
34-
pub const UPPER_BOUND: Self = Self(PatriciaKey::UPPER_BOUND);
37+
///
38+
/// For consistency with other merkle leaf bounds, [ContractAddress] is also bounded by [STORAGE_LEAF_ADDRESS_UPPER_BOUND]
39+
pub const UPPER_BOUND: Self = Self(STORAGE_LEAF_ADDRESS_UPPER_BOUND);
3540
}
3641

3742
impl core::fmt::Display for ContractAddress {
@@ -64,17 +69,67 @@ impl From<ContractAddress> for PatriciaKey {
6469
}
6570
}
6671

67-
impl From<PatriciaKey> for ContractAddress {
68-
fn from(value: PatriciaKey) -> Self {
69-
ContractAddress(value)
72+
#[derive(Debug)]
73+
pub enum ContractAddressFromPatriciaKeyError {
74+
OutOfBound,
75+
}
76+
77+
impl core::fmt::Display for ContractAddressFromPatriciaKeyError {
78+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
79+
match self {
80+
ContractAddressFromPatriciaKeyError::OutOfBound => write!(
81+
f,
82+
"value out of bound, upper non-inclusive bound is {}",
83+
ContractAddress::UPPER_BOUND
84+
),
85+
}
86+
}
87+
}
88+
89+
#[cfg(feature = "std")]
90+
impl std::error::Error for ContractAddressFromPatriciaKeyError {}
91+
92+
impl TryFrom<PatriciaKey> for ContractAddress {
93+
type Error = ContractAddressFromPatriciaKeyError;
94+
95+
fn try_from(value: PatriciaKey) -> Result<Self, Self::Error> {
96+
if value >= STORAGE_LEAF_ADDRESS_UPPER_BOUND {
97+
Err(ContractAddressFromPatriciaKeyError::OutOfBound)
98+
} else {
99+
Ok(ContractAddress(value))
100+
}
101+
}
102+
}
103+
104+
#[derive(Debug)]
105+
pub enum ContractAddressFromFeltError {
106+
PatriciaKey(PatriciaKeyFromFeltError),
107+
OutOfBound(ContractAddressFromPatriciaKeyError),
108+
}
109+
110+
impl core::fmt::Display for ContractAddressFromFeltError {
111+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112+
match self {
113+
ContractAddressFromFeltError::OutOfBound(e) => {
114+
write!(f, "invalid value for contract address: {e}")
115+
}
116+
ContractAddressFromFeltError::PatriciaKey(e) => {
117+
write!(f, "invalid patricia key value: {e}")
118+
}
119+
}
70120
}
71121
}
72122

123+
#[cfg(feature = "std")]
124+
impl std::error::Error for ContractAddressFromFeltError {}
73125
impl TryFrom<Felt> for ContractAddress {
74-
type Error = PatriciaKeyFromFeltError;
126+
type Error = ContractAddressFromFeltError;
75127

76128
fn try_from(value: Felt) -> Result<Self, Self::Error> {
77-
Ok(ContractAddress(PatriciaKey::try_from(value)?))
129+
let pk = PatriciaKey::try_from(value).map_err(ContractAddressFromFeltError::PatriciaKey)?;
130+
let ca = ContractAddress::try_from(pk).map_err(ContractAddressFromFeltError::OutOfBound)?;
131+
132+
Ok(ca)
78133
}
79134
}
80135

@@ -85,19 +140,43 @@ impl Felt {
85140
}
86141
}
87142

143+
#[derive(Debug)]
144+
pub enum ContractAddressFromStrError {
145+
PatriciaKey(PatriciaKeyFromStrError),
146+
OutOfBound(ContractAddressFromPatriciaKeyError),
147+
}
148+
149+
impl core::fmt::Display for ContractAddressFromStrError {
150+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
151+
match self {
152+
ContractAddressFromStrError::PatriciaKey(e) => {
153+
write!(f, "invalid patricia key: {e}")
154+
}
155+
ContractAddressFromStrError::OutOfBound(e) => {
156+
write!(f, "invalid value for contract address: {e}")
157+
}
158+
}
159+
}
160+
}
161+
162+
#[cfg(feature = "std")]
163+
impl std::error::Error for ContractAddressFromStrError {}
164+
88165
impl FromStr for ContractAddress {
89-
type Err = PatriciaKeyFromStrError;
166+
type Err = ContractAddressFromStrError;
90167

91168
fn from_str(s: &str) -> Result<Self, Self::Err> {
92-
Ok(ContractAddress(PatriciaKey::from_str(s)?))
169+
let pk = PatriciaKey::from_str(s).map_err(ContractAddressFromStrError::PatriciaKey)?;
170+
let ca = ContractAddress::try_from(pk).map_err(ContractAddressFromStrError::OutOfBound)?;
171+
172+
Ok(ca)
93173
}
94174
}
95175

96176
impl ContractAddress {
97177
/// Create a new [ContractAddress] from an hex encoded string without checking it is a valid value.
98178
///
99-
/// Should NEVER be used on user inputs,
100-
/// as it can cause erroneous execution if dynamically initialized with bad values.
179+
/// Should NEVER be used on user inputs, as it can cause erroneous execution if dynamically initialized with bad values.
101180
/// Should mostly be used at compilation time on hardcoded static string.
102181
pub const fn from_hex_unchecked(s: &'static str) -> ContractAddress {
103182
let patricia_key = PatriciaKey::from_hex_unchecked(s);

crates/starknet-types-core/src/patricia_key.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,24 @@ use core::str::FromStr;
1212

1313
use crate::felt::Felt;
1414

15-
pub const PATRICIA_KEY_UPPER_BOUND: Felt =
16-
Felt::from_hex_unwrap("0x800000000000000000000000000000000000000000000000000000000000000");
15+
pub const PATRICIA_KEY_UPPER_BOUND: PatriciaKey = PatriciaKey(Felt::from_hex_unwrap(
16+
"0x800000000000000000000000000000000000000000000000000000000000000",
17+
));
18+
19+
/// The index upper bound for a Starknet tree
20+
///
21+
/// Equal to `0x800000000000000000000000000000000000000000000000000000000000000 - 256`.
22+
///
23+
/// In Starknet users are allowed to store up to 256 felts in a tree leaf.
24+
/// Therfore, storage addresses can be used as "pointers" to some specific felt stored in a leaf:
25+
/// ValueAddress = LeafAddress + IndexInsideTheLeaf
26+
/// So, all leaf addresses are modulo this value.
27+
pub const STORAGE_LEAF_ADDRESS_UPPER_BOUND: PatriciaKey = PatriciaKey(Felt::from_raw([
28+
576459263475590224,
29+
18446744073709255680,
30+
160989183,
31+
18446743986131443745,
32+
]));
1733

1834
#[repr(transparent)]
1935
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -29,7 +45,7 @@ impl PatriciaKey {
2945
/// Lower inclusive bound
3046
pub const LOWER_BOUND: Self = Self(Felt::ZERO);
3147
/// Upper non-inclusive bound
32-
pub const UPPER_BOUND: Self = Self(PATRICIA_KEY_UPPER_BOUND);
48+
pub const UPPER_BOUND: Self = PATRICIA_KEY_UPPER_BOUND;
3349
}
3450

3551
impl core::fmt::Display for PatriciaKey {
@@ -78,7 +94,7 @@ impl TryFrom<Felt> for PatriciaKey {
7894
type Error = PatriciaKeyFromFeltError;
7995

8096
fn try_from(value: Felt) -> Result<Self, Self::Error> {
81-
if value >= PATRICIA_KEY_UPPER_BOUND {
97+
if value >= PATRICIA_KEY_UPPER_BOUND.0 {
8298
return Err(PatriciaKeyFromFeltError(value));
8399
}
84100

@@ -128,3 +144,19 @@ impl PatriciaKey {
128144
PatriciaKey(felt)
129145
}
130146
}
147+
148+
#[cfg(test)]
149+
mod tests {
150+
use crate::{
151+
felt::Felt,
152+
patricia_key::{PATRICIA_KEY_UPPER_BOUND, STORAGE_LEAF_ADDRESS_UPPER_BOUND},
153+
};
154+
155+
#[test]
156+
fn enforce_max_storage_leaf_address() {
157+
assert_eq!(
158+
PATRICIA_KEY_UPPER_BOUND.0 - Felt::from(256),
159+
STORAGE_LEAF_ADDRESS_UPPER_BOUND.into(),
160+
);
161+
}
162+
}

crates/starknet-types-core/src/regular_contract_address.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
use core::str::FromStr;
1515

1616
use crate::{
17-
contract_address::ContractAddress,
17+
contract_address::{
18+
ContractAddress, ContractAddressFromFeltError, ContractAddressFromStrError,
19+
},
1820
felt::Felt,
19-
patricia_key::{PatriciaKey, PatriciaKeyFromFeltError, PatriciaKeyFromStrError},
21+
patricia_key::PatriciaKey,
2022
};
2123

2224
#[repr(transparent)]
@@ -143,20 +145,20 @@ impl TryFrom<ContractAddress> for RegularContractAddress {
143145
}
144146
}
145147

146-
#[derive(Debug, Clone, Copy)]
148+
#[derive(Debug)]
147149
pub enum RegularContractAddressFromFeltError {
148-
TooBig(PatriciaKeyFromFeltError),
150+
ContractAddress(ContractAddressFromFeltError),
149151
SpecialAddress(RegularContractAddressFromContractAddressError),
150152
}
151153

152154
impl core::fmt::Display for RegularContractAddressFromFeltError {
153155
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
154156
match self {
155-
RegularContractAddressFromFeltError::TooBig(e) => {
157+
RegularContractAddressFromFeltError::ContractAddress(e) => {
156158
write!(f, "invalid contract address: {}", e)
157159
}
158160
RegularContractAddressFromFeltError::SpecialAddress(e) => {
159-
write!(f, "got special contract address: {e}")
161+
write!(f, "value is a special contract address: {e}")
160162
}
161163
}
162164
}
@@ -182,7 +184,7 @@ impl TryFrom<Felt> for RegularContractAddress {
182184

183185
fn try_from(value: Felt) -> Result<Self, Self::Error> {
184186
let contract_address = ContractAddress::try_from(value)
185-
.map_err(RegularContractAddressFromFeltError::TooBig)?;
187+
.map_err(RegularContractAddressFromFeltError::ContractAddress)?;
186188

187189
RegularContractAddress::try_from(contract_address)
188190
.map_err(RegularContractAddressFromFeltError::SpecialAddress)
@@ -191,14 +193,14 @@ impl TryFrom<Felt> for RegularContractAddress {
191193

192194
#[derive(Debug)]
193195
pub enum RegularContractAddressFromStrError {
194-
BadContractAddress(PatriciaKeyFromStrError),
196+
ContractAddress(ContractAddressFromStrError),
195197
SpecialContractAddress(RegularContractAddressFromContractAddressError),
196198
}
197199

198200
impl core::fmt::Display for RegularContractAddressFromStrError {
199201
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
200202
match self {
201-
RegularContractAddressFromStrError::BadContractAddress(e) => {
203+
RegularContractAddressFromStrError::ContractAddress(e) => {
202204
write!(f, "invalid felt string: {e}")
203205
}
204206
RegularContractAddressFromStrError::SpecialContractAddress(e) => {
@@ -216,7 +218,7 @@ impl FromStr for RegularContractAddress {
216218

217219
fn from_str(s: &str) -> Result<Self, Self::Err> {
218220
let contract_address = ContractAddress::from_str(s)
219-
.map_err(RegularContractAddressFromStrError::BadContractAddress)?;
221+
.map_err(RegularContractAddressFromStrError::ContractAddress)?;
220222

221223
RegularContractAddress::try_from(contract_address)
222224
.map_err(RegularContractAddressFromStrError::SpecialContractAddress)
@@ -253,7 +255,7 @@ mod test {
253255
assert!(RegularContractAddress::try_from(Felt::ONE).is_err());
254256
assert!(RegularContractAddress::try_from(Felt::TWO).is_err());
255257
assert!(RegularContractAddress::try_from(Felt::THREE).is_err());
256-
assert!(RegularContractAddress::try_from(PATRICIA_KEY_UPPER_BOUND).is_err());
258+
assert!(RegularContractAddress::try_from(Felt::from(PATRICIA_KEY_UPPER_BOUND)).is_err());
257259

258260
let felt = Felt::from_hex_unwrap("0xcaffe");
259261
let contract_address = RegularContractAddress::try_from(felt).unwrap();

0 commit comments

Comments
 (0)