|
11 | 11 | entrypoint::ProgramResult, |
12 | 12 | instruction::AccountMeta, |
13 | 13 | msg, |
14 | | - program::invoke_signed, |
| 14 | + program::{invoke, invoke_signed}, |
15 | 15 | program_error::ProgramError, |
16 | 16 | pubkey::Pubkey, |
17 | 17 | rent::Rent, |
@@ -664,10 +664,124 @@ fn process_close(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult |
664 | 664 | /// [ExtendProgram](enum.LoaderV3Instruction.html) |
665 | 665 | /// instruction. |
666 | 666 | 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, |
670 | 670 | ) -> 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 | + |
671 | 785 | Ok(()) |
672 | 786 | } |
673 | 787 |
|
|
0 commit comments