Add buffer authority to upgradeable loader (#14482)
This commit is contained in:
		| @@ -59,14 +59,14 @@ impl UserDefinedError for BPFError {} | ||||
| /// Point all log messages to the log collector | ||||
| macro_rules! log { | ||||
|     ($logger:ident, $message:expr) => { | ||||
|         if let Ok(logger) = $logger.try_borrow_mut() { | ||||
|             if let Ok(logger) = $logger.try_borrow_mut() { | ||||
|             if logger.log_enabled() { | ||||
|                 logger.log($message); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     ($logger:ident, $fmt:expr, $($arg:tt)*) => { | ||||
|         if let Ok(logger) = $logger.try_borrow_mut() { | ||||
|             if let Ok(logger) = $logger.try_borrow_mut() { | ||||
|             if logger.log_enabled() { | ||||
|                 logger.log(&format!($fmt, $($arg)*)); | ||||
|             } | ||||
| @@ -128,28 +128,19 @@ pub fn create_and_cache_executor( | ||||
| } | ||||
|  | ||||
| fn write_program_data( | ||||
|     account: &KeyedAccount, | ||||
|     data: &mut [u8], | ||||
|     offset: usize, | ||||
|     bytes: &[u8], | ||||
|     invoke_context: &mut dyn InvokeContext, | ||||
| ) -> Result<(), InstructionError> { | ||||
|     let logger = invoke_context.get_logger(); | ||||
|  | ||||
|     if account.signer_key().is_none() { | ||||
|         log!(logger, "Buffer account did not sign"); | ||||
|         return Err(InstructionError::MissingRequiredSignature); | ||||
|     } | ||||
|     let len = bytes.len(); | ||||
|     if account.data_len()? < offset + len { | ||||
|         log!( | ||||
|             logger, | ||||
|             "Write overflow: {} < {}", | ||||
|             account.data_len()?, | ||||
|             offset + len | ||||
|         ); | ||||
|     if data.len() < offset + len { | ||||
|         log!(logger, "Write overflow: {} < {}", data.len(), offset + len); | ||||
|         return Err(InstructionError::AccountDataTooSmall); | ||||
|     } | ||||
|     account.try_account_ref_mut()?.data[offset..offset + len].copy_from_slice(&bytes); | ||||
|     data[offset..offset + len].copy_from_slice(&bytes); | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| @@ -319,20 +310,41 @@ fn process_loader_upgradeable_instruction( | ||||
|     match limited_deserialize(instruction_data)? { | ||||
|         UpgradeableLoaderInstruction::InitializeBuffer => { | ||||
|             let buffer = next_keyed_account(account_iter)?; | ||||
|             let authority = next_keyed_account(account_iter) | ||||
|                 .ok() | ||||
|                 .map(|account| account.unsigned_key()); | ||||
|  | ||||
|             if UpgradeableLoaderState::Uninitialized != buffer.state()? { | ||||
|                 log!(logger, "Buffer account already initialized"); | ||||
|                 return Err(InstructionError::AccountAlreadyInitialized); | ||||
|             } | ||||
|             buffer.set_state(&UpgradeableLoaderState::Buffer)?; | ||||
|             buffer.set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: authority.cloned(), | ||||
|             })?; | ||||
|         } | ||||
|         UpgradeableLoaderInstruction::Write { offset, bytes } => { | ||||
|             let buffer = next_keyed_account(account_iter)?; | ||||
|             if UpgradeableLoaderState::Buffer != buffer.state()? { | ||||
|             let authority = next_keyed_account(account_iter)?; | ||||
|  | ||||
|             if let UpgradeableLoaderState::Buffer { authority_address } = buffer.state()? { | ||||
|                 if authority_address == None { | ||||
|                     log!(logger, "Buffer is immutable"); | ||||
|                     return Err(InstructionError::Immutable); // TODO better error code | ||||
|                 } | ||||
|                 if authority_address != Some(*authority.unsigned_key()) { | ||||
|                     log!(logger, "Incorrect buffer authority provided"); | ||||
|                     return Err(InstructionError::IncorrectAuthority); | ||||
|                 } | ||||
|                 if authority.signer_key().is_none() { | ||||
|                     log!(logger, "Buffer authority did not sign"); | ||||
|                     return Err(InstructionError::MissingRequiredSignature); | ||||
|                 } | ||||
|             } else { | ||||
|                 log!(logger, "Invalid Buffer account"); | ||||
|                 return Err(InstructionError::InvalidAccountData); | ||||
|             } | ||||
|             write_program_data( | ||||
|                 buffer, | ||||
|                 &mut buffer.try_account_ref_mut()?.data, | ||||
|                 UpgradeableLoaderState::buffer_data_offset()? + offset as usize, | ||||
|                 &bytes, | ||||
|                 invoke_context, | ||||
| @@ -367,7 +379,11 @@ fn process_loader_upgradeable_instruction( | ||||
|  | ||||
|             // Verify Buffer account | ||||
|  | ||||
|             if UpgradeableLoaderState::Buffer != buffer.state()? { | ||||
|             if let UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: _, | ||||
|             } = buffer.state()? | ||||
|             { | ||||
|             } else { | ||||
|                 log!(logger, "Invalid Buffer account"); | ||||
|                 return Err(InstructionError::InvalidArgument); | ||||
|             } | ||||
| @@ -473,9 +489,13 @@ fn process_loader_upgradeable_instruction( | ||||
|  | ||||
|             // Verify Buffer account | ||||
|  | ||||
|             if UpgradeableLoaderState::Buffer != buffer.state()? { | ||||
|             if let UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: _, | ||||
|             } = buffer.state()? | ||||
|             { | ||||
|             } else { | ||||
|                 log!(logger, "Invalid Buffer account"); | ||||
|                 return Err(InstructionError::InvalidAccountData); | ||||
|                 return Err(InstructionError::InvalidArgument); | ||||
|             } | ||||
|  | ||||
|             let buffer_data_offset = UpgradeableLoaderState::buffer_data_offset()?; | ||||
| @@ -500,11 +520,11 @@ fn process_loader_upgradeable_instruction( | ||||
|             { | ||||
|                 if upgrade_authority_address == None { | ||||
|                     log!(logger, "Program not upgradeable"); | ||||
|                     return Err(InstructionError::InvalidArgument); | ||||
|                     return Err(InstructionError::Immutable); | ||||
|                 } | ||||
|                 if upgrade_authority_address != Some(*authority.unsigned_key()) { | ||||
|                     log!(logger, "Upgrade authority not present"); | ||||
|                     return Err(InstructionError::MissingRequiredSignature); | ||||
|                     log!(logger, "Incorrect upgrade authority provided"); | ||||
|                     return Err(InstructionError::IncorrectAuthority); | ||||
|                 } | ||||
|                 if authority.signer_key().is_none() { | ||||
|                     log!(logger, "Upgrade authority did not sign"); | ||||
| @@ -551,36 +571,55 @@ fn process_loader_upgradeable_instruction( | ||||
|             log!(logger, "Upgraded program {:?}", program.unsigned_key()); | ||||
|         } | ||||
|         UpgradeableLoaderInstruction::SetAuthority => { | ||||
|             let programdata = next_keyed_account(account_iter)?; | ||||
|             let account = next_keyed_account(account_iter)?; | ||||
|             let present_authority = next_keyed_account(account_iter)?; | ||||
|             let new_authority = next_keyed_account(account_iter) | ||||
|                 .ok() | ||||
|                 .map(|account| account.unsigned_key()); | ||||
|  | ||||
|             if let UpgradeableLoaderState::ProgramData { | ||||
|                 slot, | ||||
|                 upgrade_authority_address, | ||||
|             } = programdata.state()? | ||||
|             { | ||||
|                 if upgrade_authority_address == None { | ||||
|                     log!(logger, "Program not upgradeable"); | ||||
|                     return Err(InstructionError::InvalidArgument); | ||||
|             match account.state()? { | ||||
|                 UpgradeableLoaderState::Buffer { authority_address } => { | ||||
|                     if authority_address == None { | ||||
|                         log!(logger, "Buffer is immutable"); | ||||
|                         return Err(InstructionError::Immutable); | ||||
|                     } | ||||
|                     if authority_address != Some(*present_authority.unsigned_key()) { | ||||
|                         log!(logger, "Incorrect buffer authority provided"); | ||||
|                         return Err(InstructionError::IncorrectAuthority); | ||||
|                     } | ||||
|                     if present_authority.signer_key().is_none() { | ||||
|                         log!(logger, "Buffer authority did not sign"); | ||||
|                         return Err(InstructionError::MissingRequiredSignature); | ||||
|                     } | ||||
|                     account.set_state(&UpgradeableLoaderState::Buffer { | ||||
|                         authority_address: new_authority.cloned(), | ||||
|                     })?; | ||||
|                 } | ||||
|                 if upgrade_authority_address != Some(*present_authority.unsigned_key()) { | ||||
|                     log!(logger, "Upgrade authority not present"); | ||||
|                     return Err(InstructionError::MissingRequiredSignature); | ||||
|                 } | ||||
|                 if present_authority.signer_key().is_none() { | ||||
|                     log!(logger, "Upgrade authority did not sign"); | ||||
|                     return Err(InstructionError::MissingRequiredSignature); | ||||
|                 } | ||||
|                 programdata.set_state(&UpgradeableLoaderState::ProgramData { | ||||
|                 UpgradeableLoaderState::ProgramData { | ||||
|                     slot, | ||||
|                     upgrade_authority_address: new_authority.cloned(), | ||||
|                 })?; | ||||
|             } else { | ||||
|                 log!(logger, "Not a ProgramData account"); | ||||
|                 return Err(InstructionError::InvalidAccountData); | ||||
|                     upgrade_authority_address, | ||||
|                 } => { | ||||
|                     if upgrade_authority_address == None { | ||||
|                         log!(logger, "Program not upgradeable"); | ||||
|                         return Err(InstructionError::Immutable); | ||||
|                     } | ||||
|                     if upgrade_authority_address != Some(*present_authority.unsigned_key()) { | ||||
|                         log!(logger, "Incorrect upgrade authority provided"); | ||||
|                         return Err(InstructionError::IncorrectAuthority); | ||||
|                     } | ||||
|                     if present_authority.signer_key().is_none() { | ||||
|                         log!(logger, "Upgrade authority did not sign"); | ||||
|                         return Err(InstructionError::MissingRequiredSignature); | ||||
|                     } | ||||
|                     account.set_state(&UpgradeableLoaderState::ProgramData { | ||||
|                         slot, | ||||
|                         upgrade_authority_address: new_authority.cloned(), | ||||
|                     })?; | ||||
|                 } | ||||
|                 _ => { | ||||
|                     log!(logger, "Account does not support authorities"); | ||||
|                     return Err(InstructionError::InvalidAccountData); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             log!(logger, "New authority {:?}", new_authority); | ||||
| @@ -607,7 +646,16 @@ fn process_loader_instruction( | ||||
|     } | ||||
|     match limited_deserialize(instruction_data)? { | ||||
|         LoaderInstruction::Write { offset, bytes } => { | ||||
|             write_program_data(program, offset as usize, &bytes, invoke_context)?; | ||||
|             if program.signer_key().is_none() { | ||||
|                 log!(logger, "Program account did not sign"); | ||||
|                 return Err(InstructionError::MissingRequiredSignature); | ||||
|             } | ||||
|             write_program_data( | ||||
|                 &mut program.try_account_ref_mut()?.data, | ||||
|                 offset as usize, | ||||
|                 &bytes, | ||||
|                 invoke_context, | ||||
|             )?; | ||||
|         } | ||||
|         LoaderInstruction::Finalize => { | ||||
|             if program.signer_key().is_none() { | ||||
| @@ -1150,26 +1198,68 @@ mod tests { | ||||
|             Ok(()), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[KeyedAccount::new(&buffer_address, false, &buffer_account),], | ||||
|                 &[KeyedAccount::new(&buffer_address, false, &buffer_account)], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|         let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); | ||||
|         assert_eq!(state, UpgradeableLoaderState::Buffer); | ||||
|         assert_eq!( | ||||
|             state, | ||||
|             UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: None | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         // Case: Already initialized | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::AccountAlreadyInitialized), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[KeyedAccount::new(&buffer_address, false, &buffer_account),], | ||||
|                 &[KeyedAccount::new(&buffer_address, false, &buffer_account)], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|         let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); | ||||
|         assert_eq!(state, UpgradeableLoaderState::Buffer); | ||||
|         assert_eq!( | ||||
|             state, | ||||
|             UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: None | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         // Case: With authority | ||||
|         let buffer_account = Account::new_ref( | ||||
|             1, | ||||
|             UpgradeableLoaderState::buffer_len(9).unwrap(), | ||||
|             &bpf_loader_upgradeable::id(), | ||||
|         ); | ||||
|         let authority_address = Pubkey::new_unique(); | ||||
|         let authority_account = Account::new_ref( | ||||
|             1, | ||||
|             UpgradeableLoaderState::buffer_len(9).unwrap(), | ||||
|             &bpf_loader_upgradeable::id(), | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             Ok(()), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new(&authority_address, false, &authority_account) | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|         let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); | ||||
|         assert_eq!( | ||||
|             state, | ||||
|             UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(authority_address) | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -1191,7 +1281,10 @@ mod tests { | ||||
|             Err(InstructionError::InvalidAccountData), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[KeyedAccount::new(&buffer_address, true, &buffer_account),], | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new(&buffer_address, true, &buffer_account) | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
| @@ -1205,19 +1298,29 @@ mod tests { | ||||
|         .unwrap(); | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer) | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(buffer_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Ok(()), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[KeyedAccount::new(&buffer_address, true, &buffer_account),], | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new(&buffer_address, true, &buffer_account) | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|         let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); | ||||
|         assert_eq!(state, UpgradeableLoaderState::Buffer); | ||||
|         assert_eq!( | ||||
|             state, | ||||
|             UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(buffer_address) | ||||
|             } | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             &buffer_account.borrow().data[UpgradeableLoaderState::buffer_data_offset().unwrap()..], | ||||
|             &[42; 9] | ||||
| @@ -1236,19 +1339,29 @@ mod tests { | ||||
|         ); | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer) | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(buffer_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Ok(()), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[KeyedAccount::new(&buffer_address, true, &buffer_account),], | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new(&buffer_address, true, &buffer_account) | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|         let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); | ||||
|         assert_eq!(state, UpgradeableLoaderState::Buffer); | ||||
|         assert_eq!( | ||||
|             state, | ||||
|             UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(buffer_address) | ||||
|             } | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             &buffer_account.borrow().data[UpgradeableLoaderState::buffer_data_offset().unwrap()..], | ||||
|             &[0, 0, 0, 42, 42, 42, 42, 42, 42] | ||||
| @@ -1262,13 +1375,18 @@ mod tests { | ||||
|         .unwrap(); | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer) | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(buffer_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::MissingRequiredSignature), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[KeyedAccount::new(&buffer_address, false, &buffer_account),], | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account) | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
| @@ -1282,13 +1400,18 @@ mod tests { | ||||
|         .unwrap(); | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer) | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(buffer_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::AccountDataTooSmall), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[KeyedAccount::new(&buffer_address, true, &buffer_account),], | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new(&buffer_address, true, &buffer_account) | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
| @@ -1302,13 +1425,44 @@ mod tests { | ||||
|         .unwrap(); | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer) | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(buffer_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::AccountDataTooSmall), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[KeyedAccount::new(&buffer_address, true, &buffer_account),], | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new(&buffer_address, true, &buffer_account) | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         // Case: wrong authority | ||||
|         let authority_address = Pubkey::new_unique(); | ||||
|         let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write { | ||||
|             offset: 1, | ||||
|             bytes: vec![42; 9], | ||||
|         }) | ||||
|         .unwrap(); | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(buffer_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::IncorrectAuthority), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new(&authority_address, true, &buffer_account) | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
| @@ -1349,7 +1503,9 @@ mod tests { | ||||
|             &bpf_loader_upgradeable::id(), | ||||
|         ); | ||||
|         buffer_account | ||||
|             .set_state(&UpgradeableLoaderState::Buffer) | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(buffer_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         buffer_account.data[UpgradeableLoaderState::buffer_data_offset().unwrap()..] | ||||
|             .copy_from_slice(&elf); | ||||
| @@ -1771,6 +1927,7 @@ mod tests { | ||||
|  | ||||
|         #[allow(clippy::type_complexity)] | ||||
|         fn get_accounts( | ||||
|             buffer_authority: &Pubkey, | ||||
|             programdata_address: &Pubkey, | ||||
|             upgrade_authority_address: &Pubkey, | ||||
|             slot: u64, | ||||
| @@ -1791,7 +1948,9 @@ mod tests { | ||||
|             ); | ||||
|             buffer_account | ||||
|                 .borrow_mut() | ||||
|                 .set_state(&UpgradeableLoaderState::Buffer) | ||||
|                 .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                     authority_address: Some(*buffer_authority), | ||||
|                 }) | ||||
|                 .unwrap(); | ||||
|             buffer_account.borrow_mut().data | ||||
|                 [UpgradeableLoaderState::buffer_data_offset().unwrap()..] | ||||
| @@ -1832,6 +1991,7 @@ mod tests { | ||||
|  | ||||
|         // Case: Success | ||||
|         let (buffer_account, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -1886,6 +2046,7 @@ mod tests { | ||||
|  | ||||
|         // Case: not upgradable | ||||
|         let (buffer_account, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -1902,7 +2063,7 @@ mod tests { | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::InvalidArgument), | ||||
|             Err(InstructionError::Immutable), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
| @@ -1925,6 +2086,7 @@ mod tests { | ||||
|  | ||||
|         // Case: wrong authority | ||||
|         let (buffer_account, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -1934,7 +2096,7 @@ mod tests { | ||||
|             min_programdata_balance, | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::MissingRequiredSignature), | ||||
|             Err(InstructionError::IncorrectAuthority), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
| @@ -1957,6 +2119,7 @@ mod tests { | ||||
|  | ||||
|         // Case: authority did not sign | ||||
|         let (buffer_account, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -1989,6 +2152,7 @@ mod tests { | ||||
|  | ||||
|         // Case: Program account not executable | ||||
|         let (buffer_account, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -2022,6 +2186,7 @@ mod tests { | ||||
|  | ||||
|         // Case: Program account now owned by loader | ||||
|         let (buffer_account, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -2055,6 +2220,7 @@ mod tests { | ||||
|  | ||||
|         // Case: Program account not initialized | ||||
|         let (buffer_account, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -2091,6 +2257,7 @@ mod tests { | ||||
|  | ||||
|         // Case: ProgramData account not initialized | ||||
|         let (buffer_account, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -2127,6 +2294,7 @@ mod tests { | ||||
|  | ||||
|         // Case: Program ProgramData account mismatch | ||||
|         let (buffer_account, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -2159,6 +2327,7 @@ mod tests { | ||||
|  | ||||
|         // Case: Buffer account not initialized | ||||
|         let (buffer_account, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -2172,7 +2341,7 @@ mod tests { | ||||
|             .set_state(&UpgradeableLoaderState::Uninitialized) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::InvalidAccountData), | ||||
|             Err(InstructionError::InvalidArgument), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
| @@ -2195,6 +2364,7 @@ mod tests { | ||||
|  | ||||
|         // Case: Buffer account too big | ||||
|         let (_, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -2210,7 +2380,9 @@ mod tests { | ||||
|         ); | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer) | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(buffer_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::AccountDataTooSmall), | ||||
| @@ -2236,6 +2408,7 @@ mod tests { | ||||
|  | ||||
|         // Case: bad elf data | ||||
|         let (buffer_account, program_account, programdata_account, spill_account) = get_accounts( | ||||
|             &buffer_address, | ||||
|             &programdata_address, | ||||
|             &upgrade_authority_address, | ||||
|             slot, | ||||
| @@ -2391,7 +2564,7 @@ mod tests { | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::MissingRequiredSignature), | ||||
|             Err(InstructionError::IncorrectAuthority), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
| @@ -2421,7 +2594,7 @@ mod tests { | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::InvalidArgument), | ||||
|             Err(InstructionError::Immutable), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
| @@ -2462,6 +2635,165 @@ mod tests { | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_bpf_loader_upgradeable_set_buffer_authority() { | ||||
|         let instruction = bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(); | ||||
|         let authority_address = Pubkey::new_unique(); | ||||
|         let authority_account = Account::new_ref(1, 0, &Pubkey::new_unique()); | ||||
|         let new_authority_address = Pubkey::new_unique(); | ||||
|         let new_authority_account = Account::new_ref(1, 0, &Pubkey::new_unique()); | ||||
|         let buffer_address = Pubkey::new_unique(); | ||||
|         let buffer_account = Account::new_ref( | ||||
|             1, | ||||
|             UpgradeableLoaderState::buffer_len(0).unwrap(), | ||||
|             &bpf_loader_upgradeable::id(), | ||||
|         ); | ||||
|  | ||||
|         // Case: Set to new authority | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(authority_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Ok(()), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new_readonly(&authority_address, true, &authority_account), | ||||
|                     KeyedAccount::new_readonly( | ||||
|                         &new_authority_address, | ||||
|                         false, | ||||
|                         &new_authority_account | ||||
|                     ) | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|         let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); | ||||
|         assert_eq!( | ||||
|             state, | ||||
|             UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(new_authority_address), | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         // Case: Not upgradeable | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(authority_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Ok(()), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new_readonly(&authority_address, true, &authority_account) | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|         let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); | ||||
|         assert_eq!( | ||||
|             state, | ||||
|             UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: None, | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         // Case: Authority did not sign | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(authority_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::MissingRequiredSignature), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new_readonly(&authority_address, false, &authority_account), | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         // Case: wrong authority | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: Some(authority_address), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::IncorrectAuthority), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account), | ||||
|                     KeyedAccount::new_readonly( | ||||
|                         &new_authority_address, | ||||
|                         false, | ||||
|                         &new_authority_account | ||||
|                     ) | ||||
|                 ], | ||||
|                 &instruction, | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         // Case: No authority | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Buffer { | ||||
|                 authority_address: None, | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::Immutable), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account), | ||||
|                 ], | ||||
|                 &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         // Case: Not a Buffer account | ||||
|         buffer_account | ||||
|             .borrow_mut() | ||||
|             .set_state(&UpgradeableLoaderState::Program { | ||||
|                 programdata_address: Pubkey::new_unique(), | ||||
|             }) | ||||
|             .unwrap(); | ||||
|         assert_eq!( | ||||
|             Err(InstructionError::InvalidAccountData), | ||||
|             process_instruction( | ||||
|                 &bpf_loader_upgradeable::id(), | ||||
|                 &[ | ||||
|                     KeyedAccount::new(&buffer_address, false, &buffer_account), | ||||
|                     KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account), | ||||
|                 ], | ||||
|                 &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), | ||||
|                 &mut MockInvokeContext::default() | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /// fuzzing utility function | ||||
|     fn fuzz<F>( | ||||
|         bytes: &[u8], | ||||
|   | ||||
		Reference in New Issue
	
	Block a user