Skip to content

Commit 7d41fed

Browse files
committed
processor: extend program
1 parent 96e5bba commit 7d41fed

File tree

1 file changed

+118
-4
lines changed

1 file changed

+118
-4
lines changed

program/src/processor.rs

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use {
1111
entrypoint::ProgramResult,
1212
instruction::AccountMeta,
1313
msg,
14-
program::invoke_signed,
14+
program::{invoke, invoke_signed},
1515
program_error::ProgramError,
1616
pubkey::Pubkey,
1717
rent::Rent,
@@ -664,10 +664,124 @@ fn process_close(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult
664664
/// [ExtendProgram](enum.LoaderV3Instruction.html)
665665
/// instruction.
666666
fn process_extend_program(
667-
_program_id: &Pubkey,
668-
_accounts: &[AccountInfo],
669-
_additional_bytes: u32,
667+
program_id: &Pubkey,
668+
accounts: &[AccountInfo],
669+
additional_bytes: u32,
670670
) -> ProgramResult {
671+
let accounts_iter = &mut accounts.iter();
672+
673+
let programdata_info = next_account_info(accounts_iter)?;
674+
let program_info = next_account_info(accounts_iter)?;
675+
676+
if programdata_info.owner != program_id {
677+
msg!("ProgramData owner is invalid");
678+
return Err(ProgramError::InvalidAccountOwner);
679+
}
680+
if !programdata_info.is_writable {
681+
msg!("ProgramData is not writable");
682+
return Err(ProgramError::InvalidArgument);
683+
}
684+
685+
if !program_info.is_writable {
686+
msg!("Program account is not writable");
687+
return Err(ProgramError::InvalidArgument);
688+
}
689+
if program_info.owner != program_id {
690+
msg!("Program account not owned by loader");
691+
return Err(ProgramError::IncorrectProgramId);
692+
}
693+
694+
match UpgradeableLoaderState::deserialize(&program_info.try_borrow_data()?)? {
695+
UpgradeableLoaderState::Program {
696+
programdata_address,
697+
} => {
698+
if programdata_address != *programdata_info.key {
699+
msg!("Program account does not match ProgramData account");
700+
return Err(ProgramError::InvalidArgument);
701+
}
702+
}
703+
_ => {
704+
msg!("Invalid Program account");
705+
return Err(ProgramError::InvalidAccountData);
706+
}
707+
}
708+
709+
let old_len = programdata_info.data_len();
710+
let new_len = old_len.saturating_add(additional_bytes as usize);
711+
if new_len > MAX_PERMITTED_DATA_LENGTH as usize {
712+
msg!(
713+
"Extended ProgramData length of {} bytes exceeds max account data length of {} bytes",
714+
new_len,
715+
MAX_PERMITTED_DATA_LENGTH
716+
);
717+
return Err(ProgramError::InvalidRealloc);
718+
}
719+
720+
let clock = <Clock as Sysvar>::get()?;
721+
let clock_slot = clock.slot;
722+
723+
let upgrade_authority_address = if let UpgradeableLoaderState::ProgramData {
724+
slot,
725+
upgrade_authority_address,
726+
} =
727+
UpgradeableLoaderState::deserialize(&programdata_info.try_borrow_data()?)?
728+
{
729+
if clock_slot == slot {
730+
msg!("Program was extended in this block already");
731+
return Err(ProgramError::InvalidArgument);
732+
}
733+
734+
if upgrade_authority_address.is_none() {
735+
msg!("Cannot extend ProgramData accounts that are not upgradeable");
736+
return Err(ProgramError::Immutable);
737+
}
738+
upgrade_authority_address
739+
} else {
740+
msg!("ProgramData state is invalid");
741+
return Err(ProgramError::InvalidAccountData);
742+
};
743+
744+
let required_payment = {
745+
let balance = programdata_info.lamports();
746+
let rent = <Rent as Sysvar>::get()?;
747+
let min_balance = rent.minimum_balance(new_len).max(1);
748+
min_balance.saturating_sub(balance)
749+
};
750+
751+
if required_payment > 0 {
752+
let system_program_info = next_account_info(accounts_iter)?;
753+
let payer_info = next_account_info(accounts_iter)?;
754+
invoke(
755+
&system_instruction::transfer(payer_info.key, programdata_info.key, required_payment),
756+
&[
757+
payer_info.clone(),
758+
programdata_info.clone(),
759+
system_program_info.clone(),
760+
],
761+
)?;
762+
}
763+
764+
programdata_info.realloc(new_len, false)?;
765+
766+
// [CORE BPF]: We'll see what happens with on-chain verification...
767+
// Something like this would be nice:
768+
// invoke(
769+
// &solana_bpf_verify_program::instruction::verify(programdata_info.key),
770+
// &[buffer_info.clone()],
771+
// )?;
772+
773+
let mut programdata_data = programdata_info.try_borrow_mut_data()?;
774+
bincode::serialize_into(
775+
&mut programdata_data[old_len..],
776+
&UpgradeableLoaderState::ProgramData {
777+
slot: clock_slot,
778+
upgrade_authority_address,
779+
},
780+
)
781+
.map_err(|_| ProgramError::InvalidAccountData)?;
782+
783+
msg!("Extended ProgramData account by {} bytes", additional_bytes);
784+
671785
Ok(())
672786
}
673787

0 commit comments

Comments
 (0)