Skip to content

Commit

Permalink
#355 Allow upgrade from user to contract account (#356)
Browse files Browse the repository at this point in the history
  • Loading branch information
anton-lisanin committed Nov 3, 2021
1 parent 413e9a8 commit ede5a49
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 63 deletions.
19 changes: 17 additions & 2 deletions evm_loader/cli/src/account_storage.rs
Expand Up @@ -262,7 +262,7 @@ impl<'a> EmulatorAccountStorage<'a> {
// SolidityAccount::new(&account.key, data_rc, account.account.lamports).unwrap()
// }


#[allow(clippy::too_many_lines)]
pub fn apply<A, I>(&self, values: A)
where
A: IntoIterator<Item=Apply<I>>,
Expand Down Expand Up @@ -336,8 +336,23 @@ impl<'a> EmulatorAccountStorage<'a> {
*acc.writable.borrow_mut() = true;
}
}
else if let Some((code, valids)) = code_and_valids.clone() {
if acc_desc.trx_count != 0 {
eprintln!("deploy to existing account: {}", &address.to_string());
exit(1);
}

code_begin = Contract::SIZE + 1;
code_size = code.len();
valids_size = valids.len();

let hamt_begin = code_begin + code_size + valids_size;
*acc.code_size.borrow_mut() = Some(hamt_begin + hamt_size(&vec![0_u8; 0], hamt_begin));
*acc.code_size_current.borrow_mut() = Some(0);
*acc.writable.borrow_mut() = true;
}
else{
if reset_storage || exist_items || code_and_valids.is_some() {
if reset_storage || exist_items {
eprintln!("changes to the storage can only be applied to the contract account; existing address: {}", &address.to_string());
exit(1);
}
Expand Down
30 changes: 21 additions & 9 deletions evm_loader/program/src/account_data.rs
Expand Up @@ -197,10 +197,10 @@ impl AccountData {
///
/// Will return:
/// `ProgramError::InvalidAccountData` if doesn't contain `Account` struct
pub const fn get_account(&self) -> Result<&Account, ProgramError> {
pub fn get_account(&self) -> Result<&Account, ProgramError> {
match self {
Self::Account(ref acc) => Ok(acc),
_ => Err(ProgramError::InvalidAccountData),
_ => Err!(ProgramError::InvalidAccountData),
}
}

Expand All @@ -221,10 +221,10 @@ impl AccountData {
///
/// Will return:
/// `ProgramError::InvalidAccountData` if doesn't contain `Contract` struct
pub const fn get_contract(&self) -> Result<&Contract, ProgramError> {
pub fn get_contract(&self) -> Result<&Contract, ProgramError> {
match self {
Self::Contract(ref acc) => Ok(acc),
_ => Err(ProgramError::InvalidAccountData),
_ => Err!(ProgramError::InvalidAccountData),
}
}

Expand All @@ -245,10 +245,10 @@ impl AccountData {
///
/// Will return:
/// `ProgramError::InvalidAccountData` if doesn't contain `Storage` struct
pub const fn get_storage(&self) -> Result<&Storage, ProgramError> {
pub fn get_storage(&self) -> Result<&Storage, ProgramError> {
match self {
Self::Storage(ref acc) => Ok(acc),
_ => Err(ProgramError::InvalidAccountData),
_ => Err!(ProgramError::InvalidAccountData),
}
}

Expand All @@ -269,10 +269,10 @@ impl AccountData {
///
/// Will return:
/// `ProgramError::InvalidAccountData` if doesn't contain `Storage` struct
pub const fn get_erc20_allowance(&self) -> Result<&ERC20Allowance, ProgramError> {
pub fn get_erc20_allowance(&self) -> Result<&ERC20Allowance, ProgramError> {
match self {
Self::ERC20Allowance(ref acc) => Ok(acc),
_ => Err(ProgramError::InvalidAccountData),
_ => Err!(ProgramError::InvalidAccountData),
}
}

Expand All @@ -284,7 +284,19 @@ impl AccountData {
pub fn get_mut_erc20_allowance(&mut self) -> Result<&mut ERC20Allowance, ProgramError> {
match self {
Self::ERC20Allowance(ref mut acc) => Ok(acc),
_ => Err(ProgramError::InvalidAccountData),
_ => Err!(ProgramError::InvalidAccountData),
}
}

/// Check if the account is empty
/// # Errors
///
/// Will return:
/// `ProgramError::InvalidAccountData` if account is not empty
pub fn check_empty(&self) -> Result<(), ProgramError> {
match self {
Self::Empty => Ok(()),
_ => Err!(ProgramError::InvalidAccountData),
}
}
}
Expand Down
89 changes: 38 additions & 51 deletions evm_loader/program/src/entrypoint.rs
Expand Up @@ -646,6 +646,7 @@ fn process_instruction<'a>(
},
EvmInstruction::ResizeStorageAccount {seed} => {
debug_print!("Execute ResizeStorageAccount");

let account_info = next_account_info(account_info_iter)?;
let code_account_info = next_account_info(account_info_iter)?;
let code_account_new_info = next_account_info(account_info_iter)?;
Expand All @@ -655,69 +656,55 @@ fn process_instruction<'a>(
return Err!(ProgramError::InvalidAccountData);
}

let mut info_data = account_info.try_borrow_mut_data()?;
if let AccountData::Account(mut data) = AccountData::unpack(&info_data)? {
if data.rw_blocked_acc.is_some() || data.ro_blocked_cnt >0 {
debug_print!("Cannot resize account data. Account is blocked {:?}", *account_info.key);
return Ok(())
}
data.code_account = *code_account_new_info.key;
AccountData::pack(&AccountData::Account(data), &mut info_data)?;
} else {
return Err!(ProgramError::InvalidAccountData)

let mut account_data = AccountData::unpack(&account_info.try_borrow_data()?)?;
let account = account_data.get_mut_account()?;
if account.rw_blocked_acc.is_some() || account.ro_blocked_cnt > 0 {
return Err!(ProgramError::InvalidInstructionData; "Cannot resize account data. Account is blocked {:?}", *account_info.key);
}

if code_account_new_info.owner == program_id {
let rent = Rent::get()?;
if !rent.is_exempt(code_account_new_info.lamports(), code_account_new_info.data_len()) {
return Err!(ProgramError::InvalidArgument; "New code account is not rent exempt. lamports={:?}, data_len={:?}",
code_account_new_info.lamports(), code_account_new_info.data_len());
}

if account.code_account != *code_account_info.key {
return Err!(ProgramError::InvalidArgument; "account.code_account<{:?}> != *code_account_info.key<{:?}>", account.code_account, code_account_info.key);
}
else {
return Err!(ProgramError::InvalidArgument; "code_account_new_info.owner<{:?}> != program_id<{:?}>", code_account_new_info.owner, program_id);
if (account.code_account == Pubkey::new_from_array([0; 32])) && (account.trx_count != 0) {
return Err!(ProgramError::InvalidArgument; "Cannot change user account to contract account");
}

let expected_address = Pubkey::create_with_seed(
operator_sol_info.key,
std::str::from_utf8(seed).map_err(|e| E!(ProgramError::InvalidInstructionData; "Seed decode error={:?}", e))?,
program_id)?;

let seed = std::str::from_utf8(seed).map_err(|e| E!(ProgramError::InvalidInstructionData; "Seed decode error={:?}", e))?;
let expected_address = Pubkey::create_with_seed(operator_sol_info.key, seed, program_id)?;
if *code_account_new_info.key != expected_address {
return Err!(ProgramError::InvalidArgument; "new code_account must be created by operator, new code_account {:?}, operator {:?}, expected address {:?}",
*code_account_new_info.key, *operator_sol_info.key, expected_address);
return Err!(ProgramError::InvalidArgument; "New code_account must be created by transaction signer");
}

let mut code_account_new_data = code_account_new_info.try_borrow_mut_data()?;
match AccountData::unpack(&code_account_new_data) {
Ok(AccountData::Empty) => {},
_ => return Err!(ProgramError::InvalidAccountData)
}
AccountData::unpack(&code_account_new_info.try_borrow_data()?)?.check_empty()?;

if code_account_info.owner != program_id {
return Err!(ProgramError::InvalidArgument; "code_account_info.owner<{:?}> != program_id<{:?}>", code_account_info.owner, program_id);
let rent = Rent::get()?;
if !rent.is_exempt(code_account_new_info.lamports(), code_account_new_info.data_len()) {
return Err!(ProgramError::InvalidArgument; "New code account is not rent exempt.");
}
{
let mut code_account_data = code_account_info.try_borrow_mut_data()?;
if code_account_data.len() >= code_account_new_data.len(){
return Err!(ProgramError::InvalidArgument; "current account size >= new account size, account={:?}, size={:?}, new_size={:?}",
*account_info.key, code_account_data.len(), code_account_new_data.len());
}
if let AccountData::Contract(data) = AccountData::unpack(&code_account_data)? {
if data.owner != *account_info.key {
return Err!(ProgramError::InvalidAccountData;
"code_account.data.owner!=contract.key, code_account.data.owner={:?}, contract.key={:?}", data.owner, *account_info.key)
}
debug_print!("move code and storage from {:?} to {:?}", *code_account_info.key, *code_account_new_info.key);
AccountData::pack(&AccountData::Contract(data.clone()), &mut code_account_new_data)?;
let begin = AccountData::Contract(data).size();
let end = code_account_data.len();
code_account_new_data[begin..end].copy_from_slice(&code_account_data[begin..]);

AccountData::pack(&AccountData::Empty, &mut code_account_data)?;
} else {
return Err!(ProgramError::InvalidAccountData)
};

account.code_account = *code_account_new_info.key;
account_data.pack(&mut account_info.try_borrow_mut_data()?)?;


if *code_account_info.key == Pubkey::new_from_array([0; 32]) {
let contract_data = AccountData::Contract( Contract {owner: *account_info.key, code_size: 0_u32} );
contract_data.pack(&mut code_account_new_info.try_borrow_mut_data()?)?;

return Ok(());
}


debug_print!("move code and storage from {:?} to {:?}", *code_account_info.key, *code_account_new_info.key);
let mut code_account_data = code_account_info.try_borrow_mut_data()?;
let mut code_account_new_data = code_account_new_info.try_borrow_mut_data()?;

code_account_new_data[..code_account_data.len()].copy_from_slice(&code_account_data);
AccountData::pack(&AccountData::Empty, &mut code_account_data)?;

payment::transfer_from_code_account_to_operator(code_account_info, operator_sol_info, code_account_info.lamports())?;

Ok(())
Expand Down
4 changes: 3 additions & 1 deletion evm_loader/test_rw_block.py
Expand Up @@ -389,7 +389,9 @@ def test_04_resizing_with_account_lock(self):

self.assertIsNotNone(resize_instr)
# send resizing transaction
send_transaction(client, Transaction().add(resize_instr), self.acc2)
with self.assertRaisesRegex(Exception, "invalid instruction data"):
send_transaction(client, Transaction().add(resize_instr), self.acc2)

# get info about resizing account
info = getAccountData(client, self.reId, ACCOUNT_INFO_LAYOUT.sizeof())
info_data = AccountInfo.frombytes(info)
Expand Down

0 comments on commit ede5a49

Please sign in to comment.