diff --git a/crates/dojo-core/src/world.cairo b/crates/dojo-core/src/world.cairo index ece860a1f9..8ada8e82c6 100644 --- a/crates/dojo-core/src/world.cairo +++ b/crates/dojo-core/src/world.cairo @@ -165,6 +165,7 @@ mod world { models: LegacyMap::, owners: LegacyMap::<(felt252, ContractAddress), bool>, writers: LegacyMap::<(felt252, ContractAddress), bool>, + reserved_events: LegacyMap::, } #[constructor] @@ -174,6 +175,17 @@ mod world { self.contract_base.write(contract_base); self.owners.write((WORLD, creator), true); + self.reserved_events.write(selector!("WorldSpawned"), true); + self.reserved_events.write(selector!("ContractDeployed"), true); + self.reserved_events.write(selector!("ContractUpgraded"), true); + self.reserved_events.write(selector!("MetadataUpdate"), true); + self.reserved_events.write(selector!("ModelRegistered"), true); + self.reserved_events.write(selector!("StoreSetRecord"), true); + self.reserved_events.write(selector!("StoreDelRecord"), true); + self.reserved_events.write(selector!("WriterUpdated"), true); + self.reserved_events.write(selector!("OwnerUpdated"), true); + self.reserved_events.write(selector!("ExecutorUpdated"), true); + EventEmitter::emit(ref self, WorldSpawned { address: get_contract_address(), creator }); } @@ -455,6 +467,7 @@ mod world { /// * `keys` - The keys of the event. /// * `values` - The data to be logged by the event. fn emit(self: @ContractState, mut keys: Array, values: Span) { + assert(keys.len() > 0 && !self.reserved_events.read(*keys.at(0)), 'reserved event name'); let system = get_caller_address(); system.serialize(ref keys); emit_event_syscall(keys.span(), values).unwrap_syscall(); diff --git a/crates/dojo-core/src/world_test.cairo b/crates/dojo-core/src/world_test.cairo index 69869c0d63..038770f543 100644 --- a/crates/dojo-core/src/world_test.cairo +++ b/crates/dojo-core/src/world_test.cairo @@ -399,3 +399,90 @@ fn test_execute_multiple_worlds() { assert(data2.a == 7331, 'data2 not stored'); } + + +#[starknet::interface] +trait IMalicious { + fn emit_worldspawned(self: @TContractState); + fn emit_modelregistered(self: @TContractState); +} + +#[dojo::contract] +mod malicious { + use starknet::get_caller_address; + use super::ContractAddress; + use super::ClassHash; + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + WorldSpawned: WorldSpawned, + ModelRegistered: ModelRegistered, + } + + #[derive(Drop, starknet::Event)] + struct WorldSpawned { + address: ContractAddress, + creator: ContractAddress + } + + #[derive(Drop, starknet::Event)] + struct ModelRegistered { + name: felt252, + class_hash: ClassHash, + prev_class_hash: ClassHash + } + + + #[external(v0)] + impl IMaliciousImpl of super::IMalicious { + fn emit_worldspawned(self: @ContractState) { + let address = get_caller_address(); + emit!(self.world_dispatcher.read(), WorldSpawned { + address: address, + creator: address, + }) + } + + fn emit_modelregistered(self: @ContractState) { + let address = get_caller_address(); + emit!(self.world_dispatcher.read(), ModelRegistered { + name: 'Malicious', + class_hash: 0.try_into().unwrap(), + prev_class_hash: 0.try_into().unwrap(), + }); + } + } +} + +#[test] +#[available_gas(60000000)] +#[should_panic(expected:('reserved event name','ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED'))] +fn test_reserved_event_worldspawned() { + let world = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let malicious_contract = IMaliciousDispatcher { + contract_address: world.deploy_contract('peper', malicious::TEST_CLASS_HASH.try_into().unwrap()) + }; + malicious_contract.emit_worldspawned(); +} + +#[test] +#[available_gas(60000000)] +#[should_panic(expected:('reserved event name','ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED'))] +fn test_reserved_event_modelregistered() { + let world = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let malicious_contract = IMaliciousDispatcher { + contract_address: world.deploy_contract('tree', malicious::TEST_CLASS_HASH.try_into().unwrap()) + }; + malicious_contract.emit_modelregistered(); +} + +#[test] +#[available_gas(60000000)] +#[should_panic(expected:('reserved event name','ENTRYPOINT_FAILED'))] +fn test_reserved_event_storesetrecord() { + let world = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let mut keys = array![selector!("StoreSetRecord")]; + let mut values = array![]; + world.emit(keys, values.span()); +} \ No newline at end of file diff --git a/crates/dojo-lang/src/manifest_test_data/manifest b/crates/dojo-lang/src/manifest_test_data/manifest index ea6e971778..4ae0ddc1c7 100644 --- a/crates/dojo-lang/src/manifest_test_data/manifest +++ b/crates/dojo-lang/src/manifest_test_data/manifest @@ -8,7 +8,7 @@ test_manifest_file "world": { "name": "world", "address": null, - "class_hash": "0x5179c281a8d3cca6cfb820201fa7a81150b56f9db492405a29e9564222370b8", + "class_hash": "0x83ec4c151ba2dc7c4debc96430272afc6f36f2f750b83e072cd1da88ed19cb", "abi": [ { "type": "impl",