Skip to content

Commit 37de10c

Browse files
committed
fix: check reserved type names for contract types
1 parent 0e14f5c commit 37de10c

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

soroban-sdk-macros/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use derive_trait::derive_trait;
4040

4141
use darling::{ast::NestedMeta, FromMeta};
4242
use macro_string::MacroString;
43+
use map_type::is_reserved_type_name;
4344
use proc_macro::TokenStream;
4445
use proc_macro2::{Span, TokenStream as TokenStream2};
4546
use quote::{format_ident, quote, ToTokens};
@@ -421,6 +422,17 @@ pub fn contracttype(metadata: TokenStream, input: TokenStream) -> TokenStream {
421422
let vis = &input.vis;
422423
let ident = &input.ident;
423424
let attrs = &input.attrs;
425+
if is_reserved_type_name(&ident.to_string()) {
426+
return Error::new(
427+
ident.span(),
428+
format!(
429+
"type name `{}` conflicts with a Soroban type and cannot be used as a contract type name",
430+
ident,
431+
),
432+
)
433+
.to_compile_error()
434+
.into();
435+
}
424436
// If the export argument has a value, do as it instructs regarding
425437
// exporting. If it does not have a value, export if the type is pub,
426438
// or always export when spec shaking is enabled.

soroban-sdk-macros/src/map_type.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,57 @@ pub const BN254_FP_SERIALIZED_SIZE: u32 = 32;
2121
pub const BN254_G1_SERIALIZED_SIZE: u32 = BN254_FP_SERIALIZED_SIZE * 2; // 64
2222
pub const BN254_G2_SERIALIZED_SIZE: u32 = BN254_G1_SERIALIZED_SIZE * 2; // 128
2323

24+
/// Returns true if `name` matches a built-in Soroban type that is
25+
/// recognized by `map_type`. Types with these names cannot be used as
26+
/// `#[contracttype]` names because `map_type` would silently classify them
27+
/// as built-in types instead of user-defined types in the contract spec.
28+
pub fn is_reserved_type_name(name: &str) -> bool {
29+
matches!(
30+
name,
31+
// Non-generic built-in types
32+
"Val"
33+
| "u64"
34+
| "i64"
35+
| "u32"
36+
| "i32"
37+
| "u128"
38+
| "i128"
39+
| "U256"
40+
| "I256"
41+
| "bool"
42+
| "Symbol"
43+
| "String"
44+
| "Error"
45+
| "Bytes"
46+
| "Address"
47+
| "MuxedAddress"
48+
| "Timepoint"
49+
| "Duration"
50+
// BLS12-381 types (unprefixed)
51+
| "Fp"
52+
| "Fp2"
53+
| "G1Affine"
54+
| "G2Affine"
55+
| "Fr"
56+
// BLS12-381 types (prefixed)
57+
| "Bls12381Fp"
58+
| "Bls12381Fp2"
59+
| "Bls12381G1Affine"
60+
| "Bls12381G2Affine"
61+
// BN254 types
62+
| "Bn254Fp"
63+
| "Bn254G1Affine"
64+
| "Bn254G2Affine"
65+
// Generic built-in types
66+
| "Result"
67+
| "Option"
68+
| "Vec"
69+
| "Map"
70+
| "BytesN"
71+
| "Hash"
72+
)
73+
}
74+
2475
#[allow(clippy::too_many_lines)]
2576
pub fn map_type(t: &Type, allow_ref: bool, allow_hash: bool) -> Result<ScSpecTypeDef, Error> {
2677
match t {
@@ -299,4 +350,59 @@ mod test {
299350
}))
300351
);
301352
}
353+
354+
#[test]
355+
fn test_is_reserved_type_name() {
356+
// Non-generic built-in types
357+
assert!(is_reserved_type_name("Val"));
358+
assert!(is_reserved_type_name("u64"));
359+
assert!(is_reserved_type_name("i64"));
360+
assert!(is_reserved_type_name("u32"));
361+
assert!(is_reserved_type_name("i32"));
362+
assert!(is_reserved_type_name("u128"));
363+
assert!(is_reserved_type_name("i128"));
364+
assert!(is_reserved_type_name("U256"));
365+
assert!(is_reserved_type_name("I256"));
366+
assert!(is_reserved_type_name("bool"));
367+
assert!(is_reserved_type_name("Symbol"));
368+
assert!(is_reserved_type_name("String"));
369+
assert!(is_reserved_type_name("Error"));
370+
assert!(is_reserved_type_name("Bytes"));
371+
assert!(is_reserved_type_name("Address"));
372+
assert!(is_reserved_type_name("MuxedAddress"));
373+
assert!(is_reserved_type_name("Timepoint"));
374+
assert!(is_reserved_type_name("Duration"));
375+
376+
// BLS12-381 types (unprefixed)
377+
assert!(is_reserved_type_name("Fp"));
378+
assert!(is_reserved_type_name("Fp2"));
379+
assert!(is_reserved_type_name("G1Affine"));
380+
assert!(is_reserved_type_name("G2Affine"));
381+
assert!(is_reserved_type_name("Fr"));
382+
383+
// BLS12-381 types (prefixed)
384+
assert!(is_reserved_type_name("Bls12381Fp"));
385+
assert!(is_reserved_type_name("Bls12381Fp2"));
386+
assert!(is_reserved_type_name("Bls12381G1Affine"));
387+
assert!(is_reserved_type_name("Bls12381G2Affine"));
388+
389+
// BN254 types
390+
assert!(is_reserved_type_name("Bn254Fp"));
391+
assert!(is_reserved_type_name("Bn254G1Affine"));
392+
assert!(is_reserved_type_name("Bn254G2Affine"));
393+
394+
// Generic built-in types
395+
assert!(is_reserved_type_name("Result"));
396+
assert!(is_reserved_type_name("Option"));
397+
assert!(is_reserved_type_name("Vec"));
398+
assert!(is_reserved_type_name("Map"));
399+
assert!(is_reserved_type_name("BytesN"));
400+
assert!(is_reserved_type_name("Hash"));
401+
402+
// Non-reserved names
403+
assert!(!is_reserved_type_name("MyStruct"));
404+
assert!(!is_reserved_type_name("Token"));
405+
assert!(!is_reserved_type_name("MyAddress"));
406+
assert!(!is_reserved_type_name("address"));
407+
}
302408
}

0 commit comments

Comments
 (0)