Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make world upgradeable #1210

Merged
merged 2 commits into from
Nov 27, 2023
Merged
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
39 changes: 36 additions & 3 deletions crates/dojo-core/src/world.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ trait IWorld<T> {
fn revoke_writer(ref self: T, model: felt252, system: ContractAddress);
}

#[starknet::interface]
trait IUpgradeableWorld<T> {
fn upgrade(ref self: T, new_class_hash : ClassHash);
}

#[starknet::interface]
trait IWorldProvider<T> {
fn world(self: @T) -> IWorldDispatcher;
Expand All @@ -65,14 +70,14 @@ mod world {
use starknet::{
get_caller_address, get_contract_address, get_tx_info,
contract_address::ContractAddressIntoFelt252, ClassHash, Zeroable, ContractAddress,
syscalls::{deploy_syscall, emit_event_syscall}, SyscallResult, SyscallResultTrait,
syscalls::{deploy_syscall, emit_event_syscall, replace_class_syscall}, SyscallResult, SyscallResultTrait,
SyscallResultTraitImpl
};

use dojo::database;
use dojo::database::index::WhereCondition;
use dojo::executor::{IExecutorDispatcher, IExecutorDispatcherTrait};
use dojo::world::{IWorldDispatcher, IWorld};
use dojo::world::{IWorldDispatcher, IWorld, IUpgradeableWorld};

use dojo::components::upgradeable::{IUpgradeableDispatcher, IUpgradeableDispatcherTrait};

Expand All @@ -87,6 +92,7 @@ mod world {
WorldSpawned: WorldSpawned,
ContractDeployed: ContractDeployed,
ContractUpgraded: ContractUpgraded,
WorldUpgraded: WorldUpgraded,
MetadataUpdate: MetadataUpdate,
ModelRegistered: ModelRegistered,
StoreSetRecord: StoreSetRecord,
Expand All @@ -102,6 +108,11 @@ mod world {
creator: ContractAddress
}

#[derive(Drop, starknet::Event)]
struct WorldUpgraded {
class_hash: ClassHash,
}

#[derive(Drop, starknet::Event)]
struct ContractDeployed {
salt: felt252,
Expand Down Expand Up @@ -616,12 +627,34 @@ mod world {
///
/// # Returns
///
/// * `ContractAddress` - The address of the contract_base contract.
/// * `ClassHash` - The class_hash of the contract_base contract.
fn base(self: @ContractState) -> ClassHash {
self.contract_base.read()
}
}


#[external(v0)]
impl UpgradeableWorld of IUpgradeableWorld<ContractState> {
/// Upgrade world with new_class_hash
///
/// # Arguments
///
/// * `new_class_hash` - The new world class hash.
fn upgrade(ref self: ContractState, new_class_hash : ClassHash){
assert(new_class_hash.is_non_zero(), 'invalid class_hash');
assert(IWorld::is_owner(@self, get_tx_info().unbox().account_contract_address, WORLD), 'only owner can upgrade');

// upgrade to new_class_hash
replace_class_syscall(new_class_hash).unwrap();

// emit Upgrade Event
EventEmitter::emit(
ref self, WorldUpgraded {class_hash: new_class_hash }
);
}
}

/// Asserts that the current caller can write to the model.
///
/// # Arguments
Expand Down
109 changes: 78 additions & 31 deletions crates/dojo-core/src/world_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use starknet::syscalls::deploy_syscall;

use dojo::benchmarks;
use dojo::executor::executor;
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait, world};
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait, world, IUpgradeableWorld, IUpgradeableWorldDispatcher, IUpgradeableWorldDispatcherTrait };
use dojo::database::introspect::Introspect;
use dojo::test_utils::{spawn_test_world, deploy_with_world_address};
use dojo::benchmarks::{Character, end};
Expand Down Expand Up @@ -405,36 +405,6 @@ fn test_set_writer_fails_for_non_owner() {
}


#[starknet::interface]
trait IOrigin<TContractState> {
fn assert_origin(self: @TContractState);
}

#[starknet::contract]
mod origin {
use super::{IWorldDispatcher, ContractAddress};

#[storage]
struct Storage {
world: IWorldDispatcher,
}

#[constructor]
fn constructor(ref self: ContractState, world: ContractAddress) {
self.world.write(IWorldDispatcher { contract_address: world })
}

#[external(v0)]
impl IOriginImpl of super::IOrigin<ContractState> {
fn assert_origin(self: @ContractState) {
assert(
starknet::get_caller_address() == starknet::contract_address_const::<0x1337>(),
'should be equal'
);
}
}
}

#[test]
#[available_gas(60000000)]
fn test_execute_multiple_worlds() {
Expand Down Expand Up @@ -511,3 +481,80 @@ fn bench_execute_complex() {

assert(data.heigth == 1337, 'data not stored');
}


#[starknet::interface]
trait IWorldUpgrade<TContractState> {
fn hello(self: @TContractState) -> felt252;
}

#[starknet::contract]
mod worldupgrade {
use super::{IWorldUpgrade, IWorldDispatcher, ContractAddress};

#[storage]
struct Storage {
world: IWorldDispatcher,
}

#[external(v0)]
impl IWorldUpgradeImpl of super::IWorldUpgrade<ContractState> {
fn hello(self: @ContractState) -> felt252{
'dojo'
}
}
}


#[test]
#[available_gas(60000000)]
fn test_upgradeable_world() {

// Deploy world contract
let world = deploy_world();

let mut upgradeable_world_dispatcher = IUpgradeableWorldDispatcher {
contract_address: world.contract_address
};
upgradeable_world_dispatcher.upgrade(worldupgrade::TEST_CLASS_HASH.try_into().unwrap());

let res = (IWorldUpgradeDispatcher {
contract_address: world.contract_address
}).hello();

assert(res == 'dojo', 'should return dojo');
}

#[test]
#[available_gas(60000000)]
#[should_panic(expected:('invalid class_hash', 'ENTRYPOINT_FAILED'))]
fn test_upgradeable_world_with_class_hash_zero() {

// Deploy world contract
let world = deploy_world();

starknet::testing::set_contract_address(starknet::contract_address_const::<0x1337>());

let mut upgradeable_world_dispatcher = IUpgradeableWorldDispatcher {
contract_address: world.contract_address
};
upgradeable_world_dispatcher.upgrade(0.try_into().unwrap());
}

#[test]
#[available_gas(60000000)]
#[should_panic( expected: ('only owner can upgrade', 'ENTRYPOINT_FAILED'))]
fn test_upgradeable_world_from_non_owner() {

// Deploy world contract
let world = deploy_world();

let not_owner = starknet::contract_address_const::<0x1337>();
starknet::testing::set_contract_address(not_owner);
starknet::testing::set_account_contract_address(not_owner);

let mut upgradeable_world_dispatcher = IUpgradeableWorldDispatcher {
contract_address: world.contract_address
};
upgradeable_world_dispatcher.upgrade(worldupgrade::TEST_CLASS_HASH.try_into().unwrap());
}
42 changes: 41 additions & 1 deletion crates/dojo-lang/src/manifest_test_data/manifest
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ test_manifest_file
"world": {
"name": "world",
"address": null,
"class_hash": "0xb3e374b8087dca92601afbb9881fed855ac0d568e3bf878a876fca5ffcb479",
"class_hash": "0x99b08b2ff33750916e36b5e241b5d4a63e8d48862bf90a68fec2ff58a8de6",
"abi": [
{
"type": "impl",
Expand Down Expand Up @@ -472,6 +472,29 @@ test_manifest_file
}
]
},
{
"type": "impl",
"name": "UpgradeableWorld",
"interface_name": "dojo::world::IUpgradeableWorld"
},
{
"type": "interface",
"name": "dojo::world::IUpgradeableWorld",
"items": [
{
"type": "function",
"name": "upgrade",
"inputs": [
{
"name": "new_class_hash",
"type": "core::starknet::class_hash::ClassHash"
}
],
"outputs": [],
"state_mutability": "external"
}
]
},
{
"type": "constructor",
"name": "constructor",
Expand Down Expand Up @@ -542,6 +565,18 @@ test_manifest_file
}
]
},
{
"type": "event",
"name": "dojo::world::world::WorldUpgraded",
"kind": "struct",
"members": [
{
"name": "class_hash",
"type": "core::starknet::class_hash::ClassHash",
"kind": "data"
}
]
},
{
"type": "event",
"name": "dojo::world::world::MetadataUpdate",
Expand Down Expand Up @@ -706,6 +741,11 @@ test_manifest_file
"type": "dojo::world::world::ContractUpgraded",
"kind": "nested"
},
{
"name": "WorldUpgraded",
"type": "dojo::world::world::WorldUpgraded",
"kind": "nested"
},
{
"name": "MetadataUpdate",
"type": "dojo::world::world::MetadataUpdate",
Expand Down
2 changes: 1 addition & 1 deletion crates/torii/graphql/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ pub async fn spinup_types_test() -> Result<SqlitePool> {
execute_strategy(&ws, &migration, &account, None).await.unwrap();

// Execute `create` and insert 10 records into storage
let records_contract = "0x27f701de7d71a2a6ee670bc1ff47a901fdc671cca26fe234ca1a42273aa7f7d";
let records_contract = "0x2e6254aaf7e47502319f35de01376cece263f9b83afe6169a4b3a76ef47c8a3";
let InvokeTransactionResult { transaction_hash } = account
.execute(vec![Call {
calldata: vec![FieldElement::from_str("0xa").unwrap()],
Expand Down
Loading