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 try_mutate_exists implementation and add tests to check it #70

Merged
merged 11 commits into from
Jun 16, 2023
30 changes: 23 additions & 7 deletions frame/evm-system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,32 @@ impl<T: Config> StoredMap<<T as Config>::AccountId, <T as Config>::AccountData>
k: &<T as Config>::AccountId,
f: impl FnOnce(&mut Option<<T as Config>::AccountData>) -> Result<R, E>,
) -> Result<R, E> {
let mut maybe_account_data = if Self::account_exists(k) {
Some(Account::<T>::get(k).data)
let (mut maybe_account_data, was_providing) = if Self::account_exists(k) {
(Some(Account::<T>::get(k).data), true)
} else {
None
(None, false)
};
let r = f(&mut maybe_account_data)?;
if let Some(account_data) = maybe_account_data {
Account::<T>::mutate(k, |a| a.data = account_data);

let result = f(&mut maybe_account_data)?;

match (maybe_account_data, was_providing) {
(Some(data), false) => {
Account::<T>::mutate(k, |a| a.data = data);
Self::on_created_account(k.clone());
}
(Some(data), true) => {
Account::<T>::mutate(k, |a| a.data = data);
}
(None, true) => {
Account::<T>::remove(k);
Self::on_killed_account(k.clone());
}
(None, false) => {
// Do nothing.
}
}
Ok(r)

Ok(result)
}
}

Expand Down
2 changes: 1 addition & 1 deletion frame/evm-system/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl pallet_evm_system::Config for Test {
type RuntimeEvent = RuntimeEvent;
type AccountId = H160;
type Index = u64;
type AccountData = ();
type AccountData = u64;
type OnNewAccount = MockDummyOnNewAccount;
type OnKilledAccount = MockDummyOnKilledAccount;
}
Expand Down
171 changes: 171 additions & 0 deletions frame/evm-system/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,174 @@ fn inc_account_nonce_works() {
assert_eq!(EvmSystem::account_nonce(&account_id), nonce_before + 1);
});
}

/// This test verifies that try_mutate_exists works as expected in case data wasn't providing
/// and returned data is `Some`. As a result, a new account has been created.
#[test]
fn try_mutate_exists_account_created() {
new_test_ext().execute_with(|| {
// Prepare test data.
let account_id = H160::from_str("1000000000000000000000000000000000000001").unwrap();

// Check test preconditions.
assert!(!EvmSystem::account_exists(&account_id));

// Set mock expectations.
let on_new_account_ctx = MockDummyOnNewAccount::on_new_account_context();
on_new_account_ctx
.expect()
.once()
.with(predicate::eq(account_id))
.return_const(());

// Set block number to enable events.
System::set_block_number(1);

// Invoke the function under test.
EvmSystem::try_mutate_exists(&account_id, |maybe_data| -> Result<(), DispatchError> {
*maybe_data = Some(1);
Ok(())
})
.unwrap();

// Assert state changes.
assert!(EvmSystem::account_exists(&account_id));
assert_eq!(EvmSystem::get(&account_id), 1);
System::assert_has_event(RuntimeEvent::EvmSystem(Event::NewAccount {
account: account_id,
}));

// Assert mock invocations.
on_new_account_ctx.checkpoint();
});
}

/// This test verifies that try_mutate_exists works as expected in case data was providing
/// and returned data is `Some`. As a result, the account has been updated.
#[test]
fn try_mutate_exists_account_updated() {
new_test_ext().execute_with(|| {
// Prepare test data.
let account_id = H160::from_str("1000000000000000000000000000000000000001").unwrap();
let nonce = 1;
let data = 1;
<Account<Test>>::insert(account_id.clone(), AccountInfo { nonce, data });

// Check test preconditions.
assert!(EvmSystem::account_exists(&account_id));

// Set block number to enable events.
System::set_block_number(1);

// Invoke the function under test.
EvmSystem::try_mutate_exists(&account_id, |maybe_data| -> Result<(), DispatchError> {
if let Some(ref mut data) = maybe_data {
*data += 1;
}
Ok(())
})
.unwrap();

// Assert state changes.
assert!(EvmSystem::account_exists(&account_id));
assert_eq!(EvmSystem::get(&account_id), data + 1);
});
}

/// This test verifies that try_mutate_exists works as expected in case data was providing
/// and returned data is `None`. As a result, the account has been removed.
#[test]
fn try_mutate_exists_account_removed() {
new_test_ext().execute_with(|| {
// Prepare test data.
let account_id = H160::from_str("1000000000000000000000000000000000000001").unwrap();
let nonce = 1;
let data = 1;
<Account<Test>>::insert(account_id.clone(), AccountInfo { nonce, data });

// Check test preconditions.
assert!(EvmSystem::account_exists(&account_id));

// Set mock expectations.
let on_killed_account_ctx = MockDummyOnKilledAccount::on_killed_account_context();
on_killed_account_ctx
.expect()
.once()
.with(predicate::eq(account_id))
.return_const(());

// Set block number to enable events.
System::set_block_number(1);

// Invoke the function under test.
EvmSystem::try_mutate_exists(&account_id, |maybe_data| -> Result<(), DispatchError> {
*maybe_data = None;
Ok(())
})
.unwrap();

// Assert state changes.
assert!(!EvmSystem::account_exists(&account_id));
System::assert_has_event(RuntimeEvent::EvmSystem(Event::KilledAccount {
account: account_id,
}));

// Assert mock invocations.
on_killed_account_ctx.checkpoint();
});
}

/// This test verifies that try_mutate_exists works as expected in case data wasn't providing
/// and returned data is `None`. As a result, the account hasn't been created.
#[test]
fn try_mutate_exists_account_not_created() {
new_test_ext().execute_with(|| {
// Prepare test data.
let account_id = H160::from_str("1000000000000000000000000000000000000001").unwrap();

// Check test preconditions.
assert!(!EvmSystem::account_exists(&account_id));

// Set block number to enable events.
System::set_block_number(1);

// Invoke the function under test.
<Account<Test>>::try_mutate_exists(account_id, |maybe_data| -> Result<(), ()> {
*maybe_data = None;
Ok(())
})
.unwrap();

// Assert state changes.
assert!(!EvmSystem::account_exists(&account_id));
});
}

/// This test verifies that try_mutate_exists works as expected in case getting error
/// during data mutation.
#[test]
fn try_mutate_exists_fails_without_changes() {
new_test_ext().execute_with(|| {
// Prepare test data.
let account_id = H160::from_str("1000000000000000000000000000000000000001").unwrap();
let nonce = 1;
let data = 1;
<Account<Test>>::insert(account_id.clone(), AccountInfo { nonce, data });

// Check test preconditions.
assert!(EvmSystem::account_exists(&account_id));

// Invoke the function under test.
assert_noop!(
<Account<Test>>::try_mutate_exists(account_id, |maybe_data| -> Result<(), ()> {
*maybe_data = None;
Err(())
}),
()
);

// Assert state changes.
assert!(EvmSystem::account_exists(&account_id));
assert_eq!(EvmSystem::get(&account_id), data);
});
}
MOZGIII marked this conversation as resolved.
Show resolved Hide resolved