lang: programdata_address field for Program account (#1125)
This commit is contained in:
parent
96f79993dd
commit
911620ee27
|
@ -13,7 +13,8 @@ incremented for features.
|
|||
|
||||
### Features
|
||||
|
||||
* lang,ts,ci,cli,docs: update solana toolchain([#1133](https://github.com/project-serum/anchor/pull/1133))
|
||||
* lang: Add `programdata_address: Option<Pubkey>` field to `Program` account. Will be populated if account is a program owned by the upgradable bpf loader ([#1125](https://github.com/project-serum/anchor/pull/1125))
|
||||
* lang,ts,ci,cli,docs: update solana toolchain to version 1.8.5([#1133](https://github.com/project-serum/anchor/pull/1133))
|
||||
|
||||
## [0.19.0] - 2021-12-08
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::*;
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState};
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::program_error::ProgramError;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
|
@ -12,6 +13,7 @@ use std::ops::Deref;
|
|||
pub struct Program<'info, T: Id + AccountDeserialize + Clone> {
|
||||
_account: T,
|
||||
info: AccountInfo<'info>,
|
||||
programdata_address: Option<Pubkey>,
|
||||
}
|
||||
|
||||
impl<'info, T: Id + AccountDeserialize + Clone + fmt::Debug> fmt::Debug for Program<'info, T> {
|
||||
|
@ -19,13 +21,22 @@ impl<'info, T: Id + AccountDeserialize + Clone + fmt::Debug> fmt::Debug for Prog
|
|||
f.debug_struct("Program")
|
||||
.field("account", &self._account)
|
||||
.field("info", &self.info)
|
||||
.field("programdata_address", &self.programdata_address)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Id + AccountDeserialize + Clone> Program<'a, T> {
|
||||
fn new(info: AccountInfo<'a>, _account: T) -> Program<'a, T> {
|
||||
Self { info, _account }
|
||||
fn new(
|
||||
info: AccountInfo<'a>,
|
||||
_account: T,
|
||||
programdata_address: Option<Pubkey>,
|
||||
) -> Program<'a, T> {
|
||||
Self {
|
||||
info,
|
||||
_account,
|
||||
programdata_address,
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializes the given `info` into a `Program`.
|
||||
|
@ -37,9 +48,44 @@ impl<'a, T: Id + AccountDeserialize + Clone> Program<'a, T> {
|
|||
if !info.executable {
|
||||
return Err(ErrorCode::InvalidProgramExecutable.into());
|
||||
}
|
||||
let programdata_address = if *info.owner == bpf_loader_upgradeable::ID {
|
||||
let mut data: &[u8] = &info.try_borrow_data()?;
|
||||
let upgradable_loader_state =
|
||||
UpgradeableLoaderState::try_deserialize_unchecked(&mut data)?;
|
||||
|
||||
match upgradable_loader_state {
|
||||
UpgradeableLoaderState::Uninitialized
|
||||
| UpgradeableLoaderState::Buffer {
|
||||
authority_address: _,
|
||||
}
|
||||
| UpgradeableLoaderState::ProgramData {
|
||||
slot: _,
|
||||
upgrade_authority_address: _,
|
||||
} => {
|
||||
// Unreachable because check above already
|
||||
// ensures that program is executable
|
||||
// and therefore a program account.
|
||||
unreachable!()
|
||||
}
|
||||
UpgradeableLoaderState::Program {
|
||||
programdata_address,
|
||||
} => Some(programdata_address),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Programs have no data so use an empty slice.
|
||||
let mut empty = &[][..];
|
||||
Ok(Program::new(info.clone(), T::try_deserialize(&mut empty)?))
|
||||
Ok(Program::new(
|
||||
info.clone(),
|
||||
T::try_deserialize(&mut empty)?,
|
||||
programdata_address,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn programdata_address(&self) -> Option<Pubkey> {
|
||||
self.programdata_address
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,14 @@ pub mod bpf_upgradeable_state {
|
|||
ctx.accounts.settings.admin_data = admin_data;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_admin_settings_use_program_state(
|
||||
ctx: Context<SetAdminSettingsUseProgramState>,
|
||||
admin_data: u64,
|
||||
) -> ProgramResult {
|
||||
ctx.accounts.settings.admin_data = admin_data;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[account]
|
||||
|
@ -36,6 +44,7 @@ pub struct Settings {
|
|||
pub enum CustomError {
|
||||
InvalidProgramDataAddress,
|
||||
AccountNotProgram,
|
||||
AccountNotBpfUpgradableProgram,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
|
@ -51,3 +60,17 @@ pub struct SetAdminSettings<'info> {
|
|||
pub program_data: Account<'info, ProgramData>,
|
||||
pub system_program: Program<'info, System>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
#[instruction(admin_data: u64)]
|
||||
pub struct SetAdminSettingsUseProgramState<'info> {
|
||||
#[account(init, payer = authority)]
|
||||
pub settings: Account<'info, Settings>,
|
||||
#[account(mut)]
|
||||
pub authority: Signer<'info>,
|
||||
#[account(constraint = program.programdata_address() == Some(program_data.key()))]
|
||||
pub program: Program<'info, crate::program::BpfUpgradeableState>,
|
||||
#[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
|
||||
pub program_data: Account<'info, ProgramData>,
|
||||
pub system_program: Program<'info, System>,
|
||||
}
|
||||
|
|
|
@ -29,8 +29,21 @@ describe('bpf_upgradeable_state', () => {
|
|||
signers: [settings]
|
||||
});
|
||||
assert.equal((await program.account.settings.fetch(settings.publicKey)).adminData, 500);
|
||||
});
|
||||
|
||||
console.log("Your transaction signature", tx);
|
||||
it('Reads ProgramData and sets field, uses program state', async () => {
|
||||
const settings = anchor.web3.Keypair.generate();
|
||||
const tx = await program.rpc.setAdminSettingsUseProgramState(new anchor.BN(500), {
|
||||
accounts: {
|
||||
authority: program.provider.wallet.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
programData: programDataAddress,
|
||||
program: program.programId,
|
||||
settings: settings.publicKey
|
||||
},
|
||||
signers: [settings]
|
||||
});
|
||||
assert.equal((await program.account.settings.fetch(settings.publicKey)).adminData, 500);
|
||||
});
|
||||
|
||||
it('Validates constraint on ProgramData', async () => {
|
||||
|
@ -122,4 +135,29 @@ describe('bpf_upgradeable_state', () => {
|
|||
assert.equal(err.code, 6000);
|
||||
}
|
||||
});
|
||||
|
||||
it('Deserializes Program and validates that programData is the expected account', async () => {
|
||||
const secondProgramAddress = new PublicKey("Fkv67TwmbakfZw2PoW57wYPbqNexAH6vuxpyT8vmrc3B");
|
||||
const secondProgramProgramDataAddress = findProgramAddressSync(
|
||||
[secondProgramAddress.toBytes()],
|
||||
new anchor.web3.PublicKey("BPFLoaderUpgradeab1e11111111111111111111111")
|
||||
)[0];
|
||||
|
||||
const settings = anchor.web3.Keypair.generate();
|
||||
try {
|
||||
await program.rpc.setAdminSettingsUseProgramState(new anchor.BN(500), {
|
||||
accounts: {
|
||||
authority: program.provider.wallet.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
programData: secondProgramProgramDataAddress,
|
||||
settings: settings.publicKey,
|
||||
program: program.programId,
|
||||
},
|
||||
signers: [settings]
|
||||
});
|
||||
assert.ok(false);
|
||||
} catch (err) {
|
||||
assert.equal(err.code, 2003);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue