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

Fix crash while removing values from maps containing complex types #2225

Merged
merged 6 commits into from
Feb 4, 2025
Merged
Changes from 1 commit
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
Next Next commit
Fix crash while removing values from maps containing complex types
bzawisto committed Jan 24, 2025
commit 3c619c5cf35df0ec26c4b3e2ea226ad8f5c04418
46 changes: 42 additions & 4 deletions zilliqa/src/scilla.rs
Original file line number Diff line number Diff line change
@@ -923,10 +923,48 @@ impl ActiveCall {
}

if ignore_value {
// We only supporting deleting a single value of a map.
assert_eq!(indices.len(), depth);
let storage_slot = self.state.load_storage(self.sender, &name, &indices)?;
*storage_slot = None;
assert!(indices.len() <= depth);
// Remove single element
if indices.len() == depth {
let storage_slot = self.state.load_storage(self.sender, &name, &indices)?;
*storage_slot = None;
} else {
// Remove multiple elements from storage having the same prefix specified by `indices`

// Collect all paths of indexes up to a value that is not of a map type
fn collect_indices(
map: BTreeMap<Vec<u8>, StorageValue>,
path: Vec<Vec<u8>>,
all_paths: &mut Vec<Vec<Vec<u8>>>,
) {
for (key, value) in map {
let mut path = path.clone();
path.push(key);
match value {
StorageValue::Map { map, .. } => {
collect_indices(map, path, all_paths);
}
StorageValue::Value(_) => {
all_paths.push(path.clone());
}
}
}
}

let load_map_from_prefix =
self.state
.load_storage_by_prefix(self.sender, &name, &indices)?;

let mut all_indices = Vec::new();
let path = Vec::new();

collect_indices(load_map_from_prefix, path, &mut all_indices);

for path in all_indices {
self.state
.set_storage(self.sender, &name, &path, StorageValue::Value(None))?
}
}
} else if indices.len() == depth {
let Some(ValType::Bval(value)) = value.val_type else {
return Err(anyhow!("invalid value"));
109 changes: 109 additions & 0 deletions zilliqa/tests/it/zil.rs
Original file line number Diff line number Diff line change
@@ -340,6 +340,12 @@ pub fn scilla_test_contract_code() -> String {
field welcome_msg : String = "default"
field welcome_map : Map Uint32 (Map Uint32 String) = Emp Uint32 (Map Uint32 String)

transition removeHello()
delete welcome_map[one][two];
e = {_eventname : "removeHello"};
event e
end

transition setHello (msg : String)
is_owner = builtin eq owner _sender;
match is_owner with
@@ -2959,6 +2965,109 @@ async fn get_smart_contract_sub_state(mut network: Network) {
assert!(substate2.get("welcome_msg").is_none());
}

#[zilliqa_macros::test(restrict_concurrency)]
async fn nested_maps_insert_removal(mut network: Network) {
let (secret_key, address) = zilliqa_account(&mut network).await;

let code = scilla_test_contract_code();
let data = scilla_test_contract_data(address);
let contract_address = deploy_scilla_contract(&mut network, &secret_key, &code, &data).await;

// Set nested map to some value
{
let call = r#"{
"_tag": "setHello",
"params": [
{
"vname": "msg",
"value": "foobar",
"type": "String"
}
]
}"#;

let (_, txn) = send_transaction(
&mut network,
&secret_key,
2,
ToAddr::Address(contract_address),
0,
50_000,
None,
Some(&call),
)
.await;
let event = &txn["receipt"]["event_logs"][0];
assert_eq!(event["_eventname"], "setHello");
}

// Confirm the value exists in the nested map
{
let call = r#"{
"_tag": "getHello",
"params": []
}"#;
let (_, txn) = send_transaction(
&mut network,
&secret_key,
3,
ToAddr::Address(contract_address),
0,
50_000,
None,
Some(call),
)
.await;
for event in txn["receipt"]["event_logs"].as_array().unwrap() {
assert_eq!(event["_eventname"], "getHello");
assert_eq!(event["params"][0]["value"], "foobar");
}
}

// Remove entry from map
{
let call = r#"{
"_tag": "removeHello",
"params": []
}"#;

let (_, txn) = send_transaction(
&mut network,
&secret_key,
4,
ToAddr::Address(contract_address),
0,
50_000,
None,
Some(&call),
)
.await;
let event = &txn["receipt"]["event_logs"][0];
assert_eq!(event["_eventname"], "removeHello");
}

// Check and confirm the entry does not exist anymore
{
let call = r#"{
"_tag": "getHello",
"params": []
}"#;
let (_, txn) = send_transaction(
&mut network,
&secret_key,
5,
ToAddr::Address(contract_address),
0,
50_000,
None,
Some(call),
)
.await;
let event = &txn["receipt"]["event_logs"][0];
assert_eq!(event["params"][0]["value"], "failed");
}
}

#[zilliqa_macros::test]
async fn get_state_proof(mut network: Network) {
let wallet = network.genesis_wallet().await;
Loading