lang: Add ProgramData account (#1095)
This commit is contained in:
parent
517838e494
commit
3321a3f9c9
|
@ -17,3 +17,5 @@ runs:
|
||||||
shell: bash
|
shell: bash
|
||||||
- run: solana-keygen new --no-bip39-passphrase
|
- run: solana-keygen new --no-bip39-passphrase
|
||||||
shell: bash
|
shell: bash
|
||||||
|
- run: solana config set --url localhost
|
||||||
|
shell: bash
|
||||||
|
|
|
@ -179,6 +179,48 @@ jobs:
|
||||||
- uses: ./.github/actions/setup-solana/
|
- uses: ./.github/actions/setup-solana/
|
||||||
- run: cd client/example && ./run-test.sh
|
- run: cd client/example && ./run-test.sh
|
||||||
|
|
||||||
|
test-bpf-upgradeable-state:
|
||||||
|
needs: setup-anchor-cli
|
||||||
|
name: Test tests/bpf-upgradeable-state
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: ./.github/actions/setup/
|
||||||
|
- uses: ./.github/actions/setup-ts/
|
||||||
|
- uses: ./.github/actions/setup-solana/
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
name: Cache Cargo registry + index
|
||||||
|
id: cache-anchor
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/bin/
|
||||||
|
~/.cargo/registry/index/
|
||||||
|
~/.cargo/registry/cache/
|
||||||
|
~/.cargo/git/db/
|
||||||
|
./target/
|
||||||
|
key: cargo-${{ runner.os }}-anchor-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: anchor-binary
|
||||||
|
path: ~/.cargo/bin/
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
name: Cache tests/bpf-upgradeable-state target
|
||||||
|
id: cache-test-target
|
||||||
|
with:
|
||||||
|
path: tests/bpf-upgradeable-state/target
|
||||||
|
key: cargo-${{ runner.os }}-tests/bpf-upgradeable-state-${{ env.ANCHOR_VERSION }}
|
||||||
|
|
||||||
|
- run: solana-test-validator -r --quiet &
|
||||||
|
name: start validator
|
||||||
|
- run: cd tests/bpf-upgradeable-state && yarn
|
||||||
|
- run: cd tests/bpf-upgradeable-state && yarn link @project-serum/anchor
|
||||||
|
- run: cd tests/bpf-upgradeable-state && anchor build
|
||||||
|
- run: cd tests/bpf-upgradeable-state && solana program deploy --program-id program_with_different_programdata.json target/deploy/bpf_upgradeable_state.so
|
||||||
|
- run: cd tests/bpf-upgradeable-state && cp bpf_upgradeable_state-keypair.json target/deploy/bpf_upgradeable_state-keypair.json && anchor deploy && anchor test --skip-deploy --skip-build
|
||||||
|
|
||||||
test-programs:
|
test-programs:
|
||||||
needs: setup-anchor-cli
|
needs: setup-anchor-cli
|
||||||
name: Test ${{ matrix.node.path }}
|
name: Test ${{ matrix.node.path }}
|
||||||
|
|
|
@ -21,6 +21,7 @@ incremented for features.
|
||||||
|
|
||||||
* lang: Add `ErrorCode::AccountNotInitialized` error to separate the situation when the account has the wrong owner from when it does not exist (#[1024](https://github.com/project-serum/anchor/pull/1024))
|
* lang: Add `ErrorCode::AccountNotInitialized` error to separate the situation when the account has the wrong owner from when it does not exist (#[1024](https://github.com/project-serum/anchor/pull/1024))
|
||||||
* lang: Called instructions now log their name by default. This can be turned off with the `no-log-ix-name` flag ([#1057](https://github.com/project-serum/anchor/pull/1057))
|
* lang: Called instructions now log their name by default. This can be turned off with the `no-log-ix-name` flag ([#1057](https://github.com/project-serum/anchor/pull/1057))
|
||||||
|
* lang: `ProgramData` and `UpgradableLoaderState` can now be passed into `Account` as generics. see [UpgradeableLoaderState](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/enum.UpgradeableLoaderState.html). `UpgradableLoaderState` can also be matched on to get `ProgramData`, but when `ProgramData` is used instead, anchor does the serialization and checking that it is actually program data for you ([#1095](https://github.com/project-serum/anchor/pull/1095))
|
||||||
* ts: Add better error msgs in the ts client if something wrong (i.e. not a pubkey or a string) is passed in as an account in an instruction accounts object ([#1098](https://github.com/project-serum/anchor/pull/1098))
|
* ts: Add better error msgs in the ts client if something wrong (i.e. not a pubkey or a string) is passed in as an account in an instruction accounts object ([#1098](https://github.com/project-serum/anchor/pull/1098))
|
||||||
|
|
||||||
## [0.18.2] - 2021-11-14
|
## [0.18.2] - 2021-11-14
|
||||||
|
|
|
@ -81,11 +81,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-attribute-constant"
|
name = "anchor-attribute-constant"
|
||||||
version = "0.18.0"
|
version = "0.18.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-syn",
|
"anchor-syn",
|
||||||
"proc-macro2 1.0.29",
|
"proc-macro2 1.0.32",
|
||||||
"syn 1.0.75",
|
"syn 1.0.81",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -213,6 +213,7 @@ dependencies = [
|
||||||
"anchor-attribute-state",
|
"anchor-attribute-state",
|
||||||
"anchor-derive-accounts",
|
"anchor-derive-accounts",
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
|
"bincode",
|
||||||
"borsh",
|
"borsh",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"solana-program",
|
"solana-program",
|
||||||
|
|
|
@ -38,3 +38,4 @@ borsh = "0.9"
|
||||||
bytemuck = "1.4.0"
|
bytemuck = "1.4.0"
|
||||||
solana-program = "1.8.0"
|
solana-program = "1.8.0"
|
||||||
thiserror = "1.0.20"
|
thiserror = "1.0.20"
|
||||||
|
bincode = "1.3.3"
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
use crate::{AccountDeserialize, AccountSerialize, Owner};
|
||||||
|
use solana_program::{
|
||||||
|
bpf_loader_upgradeable::UpgradeableLoaderState, program_error::ProgramError, pubkey::Pubkey,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ProgramData {
|
||||||
|
pub slot: u64,
|
||||||
|
pub upgrade_authority_address: Option<Pubkey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountDeserialize for ProgramData {
|
||||||
|
fn try_deserialize(
|
||||||
|
buf: &mut &[u8],
|
||||||
|
) -> Result<Self, solana_program::program_error::ProgramError> {
|
||||||
|
ProgramData::try_deserialize_unchecked(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_deserialize_unchecked(
|
||||||
|
buf: &mut &[u8],
|
||||||
|
) -> Result<Self, solana_program::program_error::ProgramError> {
|
||||||
|
let program_state = AccountDeserialize::try_deserialize_unchecked(buf)?;
|
||||||
|
|
||||||
|
match program_state {
|
||||||
|
UpgradeableLoaderState::Uninitialized => {
|
||||||
|
Err(anchor_lang::error::ErrorCode::AccountNotProgramData.into())
|
||||||
|
}
|
||||||
|
UpgradeableLoaderState::Buffer {
|
||||||
|
authority_address: _,
|
||||||
|
} => Err(anchor_lang::error::ErrorCode::AccountNotProgramData.into()),
|
||||||
|
UpgradeableLoaderState::Program {
|
||||||
|
programdata_address: _,
|
||||||
|
} => Err(anchor_lang::error::ErrorCode::AccountNotProgramData.into()),
|
||||||
|
UpgradeableLoaderState::ProgramData {
|
||||||
|
slot,
|
||||||
|
upgrade_authority_address,
|
||||||
|
} => Ok(ProgramData {
|
||||||
|
slot,
|
||||||
|
upgrade_authority_address,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountSerialize for ProgramData {
|
||||||
|
fn try_serialize<W: std::io::Write>(
|
||||||
|
&self,
|
||||||
|
_writer: &mut W,
|
||||||
|
) -> Result<(), solana_program::program_error::ProgramError> {
|
||||||
|
// no-op
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Owner for ProgramData {
|
||||||
|
fn owner() -> solana_program::pubkey::Pubkey {
|
||||||
|
anchor_lang::solana_program::bpf_loader_upgradeable::ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Owner for UpgradeableLoaderState {
|
||||||
|
fn owner() -> Pubkey {
|
||||||
|
anchor_lang::solana_program::bpf_loader_upgradeable::ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountSerialize for UpgradeableLoaderState {
|
||||||
|
fn try_serialize<W: std::io::Write>(&self, _writer: &mut W) -> Result<(), ProgramError> {
|
||||||
|
// no-op
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountDeserialize for UpgradeableLoaderState {
|
||||||
|
fn try_deserialize(buf: &mut &[u8]) -> Result<Self, ProgramError> {
|
||||||
|
UpgradeableLoaderState::try_deserialize_unchecked(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, ProgramError> {
|
||||||
|
bincode::deserialize(buf).map_err(|_| ProgramError::InvalidAccountData)
|
||||||
|
}
|
||||||
|
}
|
|
@ -76,6 +76,8 @@ pub enum ErrorCode {
|
||||||
AccountNotSystemOwned,
|
AccountNotSystemOwned,
|
||||||
#[msg("The program expected this account to be already initialized")]
|
#[msg("The program expected this account to be already initialized")]
|
||||||
AccountNotInitialized,
|
AccountNotInitialized,
|
||||||
|
#[msg("The given account is not a program data account")]
|
||||||
|
AccountNotProgramData,
|
||||||
|
|
||||||
// State.
|
// State.
|
||||||
#[msg("The given state account does not have the correct address")]
|
#[msg("The given state account does not have the correct address")]
|
||||||
|
|
|
@ -35,6 +35,7 @@ mod account;
|
||||||
mod account_info;
|
mod account_info;
|
||||||
mod account_meta;
|
mod account_meta;
|
||||||
mod boxed;
|
mod boxed;
|
||||||
|
mod bpf_upgradeable_state;
|
||||||
mod common;
|
mod common;
|
||||||
mod context;
|
mod context;
|
||||||
mod cpi_account;
|
mod cpi_account;
|
||||||
|
@ -56,6 +57,7 @@ mod unchecked_account;
|
||||||
mod vec;
|
mod vec;
|
||||||
|
|
||||||
pub use crate::account::Account;
|
pub use crate::account::Account;
|
||||||
|
pub use crate::bpf_upgradeable_state::*;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
pub use crate::context::CpiStateContext;
|
pub use crate::context::CpiStateContext;
|
||||||
|
@ -252,9 +254,10 @@ impl Key for Pubkey {
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::{
|
pub use super::{
|
||||||
access_control, account, constant, declare_id, emit, error, event, interface, program,
|
access_control, account, constant, declare_id, emit, error, event, interface, program,
|
||||||
require, state, zero_copy, Account, AccountDeserialize, AccountLoader, AccountSerialize,
|
require, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, state, zero_copy,
|
||||||
Accounts, AccountsExit, AnchorDeserialize, AnchorSerialize, Context, CpiContext, Id, Key,
|
Account, AccountDeserialize, AccountLoader, AccountSerialize, Accounts, AccountsExit,
|
||||||
Owner, Program, Signer, System, SystemAccount, Sysvar, ToAccountInfo, ToAccountInfos,
|
AnchorDeserialize, AnchorSerialize, Context, CpiContext, Id, Key, Owner, Program,
|
||||||
|
ProgramData, Signer, System, SystemAccount, Sysvar, ToAccountInfo, ToAccountInfos,
|
||||||
ToAccountMetas, UncheckedAccount,
|
ToAccountMetas, UncheckedAccount,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -486,11 +486,9 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
.methods
|
.methods
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ix| {
|
.map(|ix| {
|
||||||
if state.is_zero_copy {
|
// Easy to implement. Just need to write a test.
|
||||||
// Easy to implement. Just need to write a test.
|
// Feel free to open a PR.
|
||||||
// Feel free to open a PR.
|
assert!(!state.is_zero_copy, "Trait implementations not yet implemented for zero copy state structs. Please file an issue.");
|
||||||
panic!("Trait implementations not yet implemented for zero copy state structs. Please file an issue.");
|
|
||||||
}
|
|
||||||
|
|
||||||
let ix_arg_names: Vec<&syn::Ident> =
|
let ix_arg_names: Vec<&syn::Ident> =
|
||||||
ix.args.iter().map(|arg| &arg.name).collect();
|
ix.args.iter().map(|arg| &arg.name).collect();
|
||||||
|
|
|
@ -184,6 +184,9 @@ impl Field {
|
||||||
Ty::Signer => quote! {
|
Ty::Signer => quote! {
|
||||||
Signer
|
Signer
|
||||||
},
|
},
|
||||||
|
Ty::ProgramData => quote! {
|
||||||
|
ProgramData
|
||||||
|
},
|
||||||
Ty::SystemAccount => quote! {
|
Ty::SystemAccount => quote! {
|
||||||
SystemAccount
|
SystemAccount
|
||||||
},
|
},
|
||||||
|
@ -298,6 +301,7 @@ impl Field {
|
||||||
Ty::UncheckedAccount => quote! {},
|
Ty::UncheckedAccount => quote! {},
|
||||||
Ty::Signer => quote! {},
|
Ty::Signer => quote! {},
|
||||||
Ty::SystemAccount => quote! {},
|
Ty::SystemAccount => quote! {},
|
||||||
|
Ty::ProgramData => quote! {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,6 +320,9 @@ impl Field {
|
||||||
Ty::SystemAccount => quote! {
|
Ty::SystemAccount => quote! {
|
||||||
SystemAccount
|
SystemAccount
|
||||||
},
|
},
|
||||||
|
Ty::ProgramData => quote! {
|
||||||
|
ProgramData
|
||||||
|
},
|
||||||
Ty::ProgramAccount(ty) => {
|
Ty::ProgramAccount(ty) => {
|
||||||
let ident = &ty.account_type_path;
|
let ident = &ty.account_type_path;
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -405,6 +412,7 @@ pub enum Ty {
|
||||||
Program(ProgramTy),
|
Program(ProgramTy),
|
||||||
Signer,
|
Signer,
|
||||||
SystemAccount,
|
SystemAccount,
|
||||||
|
ProgramData,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
|
|
@ -79,6 +79,7 @@ fn is_field_primitive(f: &syn::Field) -> ParseResult<bool> {
|
||||||
| "Program"
|
| "Program"
|
||||||
| "Signer"
|
| "Signer"
|
||||||
| "SystemAccount"
|
| "SystemAccount"
|
||||||
|
| "ProgramData"
|
||||||
);
|
);
|
||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
|
@ -102,6 +103,7 @@ fn parse_ty(f: &syn::Field) -> ParseResult<Ty> {
|
||||||
"Program" => Ty::Program(parse_program_ty(&path)?),
|
"Program" => Ty::Program(parse_program_ty(&path)?),
|
||||||
"Signer" => Ty::Signer,
|
"Signer" => Ty::Signer,
|
||||||
"SystemAccount" => Ty::SystemAccount,
|
"SystemAccount" => Ty::SystemAccount,
|
||||||
|
"ProgramData" => Ty::ProgramData,
|
||||||
_ => return Err(ParseError::new(f.ty.span(), "invalid account type given")),
|
_ => return Err(ParseError::new(f.ty.span(), "invalid account type given")),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
yarn.lock
|
|
@ -0,0 +1,12 @@
|
||||||
|
[programs.localnet]
|
||||||
|
bpf_upgradeable_state = "Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e"
|
||||||
|
|
||||||
|
[registry]
|
||||||
|
url = "https://anchor.projectserum.com"
|
||||||
|
|
||||||
|
[provider]
|
||||||
|
cluster = "localnet"
|
||||||
|
wallet = "~/.config/solana/id.json"
|
||||||
|
|
||||||
|
[scripts]
|
||||||
|
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
|
|
@ -0,0 +1,4 @@
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"programs/*"
|
||||||
|
]
|
|
@ -0,0 +1 @@
|
||||||
|
[114,99,192,17,48,208,90,184,231,46,220,91,47,115,132,253,218,163,228,101,8,121,220,138,41,140,176,127,254,91,51,28,176,244,174,182,223,57,57,125,117,201,31,213,9,39,207,212,100,173,88,252,61,235,89,156,53,86,4,90,16,251,191,219]
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Migrations are an early feature. Currently, they're nothing more than this
|
||||||
|
// single deploy script that's invoked from the CLI, injecting a provider
|
||||||
|
// configured from the workspace's Anchor.toml.
|
||||||
|
|
||||||
|
const anchor = require("@project-serum/anchor");
|
||||||
|
|
||||||
|
module.exports = async function (provider) {
|
||||||
|
// Configure client to use the provider.
|
||||||
|
anchor.setProvider(provider);
|
||||||
|
|
||||||
|
// Add your deploy script here.
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@project-serum/anchor": "^0.18.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"chai": "^4.3.4",
|
||||||
|
"mocha": "^9.0.3",
|
||||||
|
"ts-mocha": "^8.0.0",
|
||||||
|
"@types/mocha": "^9.0.0",
|
||||||
|
"typescript": "^4.3.5"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
[86,234,116,86,82,140,116,250,254,32,75,217,35,39,9,238,39,98,242,254,25,216,201,66,1,239,93,12,81,19,34,108,219,67,158,98,245,234,81,126,228,157,205,206,130,5,14,54,1,21,88,246,128,124,240,93,157,49,102,19,253,19,205,178]
|
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "bpf-upgradeable-state"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Created with Anchor"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "lib"]
|
||||||
|
name = "bpf_upgradeable_state"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
no-entrypoint = []
|
||||||
|
no-idl = []
|
||||||
|
cpi = ["no-entrypoint"]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anchor-lang = { path = "../../../../lang" }
|
|
@ -0,0 +1,2 @@
|
||||||
|
[target.bpfel-unknown-unknown.dependencies.std]
|
||||||
|
features = []
|
|
@ -0,0 +1,53 @@
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
|
declare_id!("Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e");
|
||||||
|
|
||||||
|
// TODO: Once anchor can deserialize data of programs (=programdata_address) automatically, add another test to this file.
|
||||||
|
// Instead of using UpgradeableLoaderState, it should use Program<'info, MY_PROGRAM>
|
||||||
|
|
||||||
|
#[program]
|
||||||
|
pub mod bpf_upgradeable_state {
|
||||||
|
use super::*;
|
||||||
|
pub fn set_admin_settings(ctx: Context<SetAdminSettings>, admin_data: u64) -> ProgramResult {
|
||||||
|
match *ctx.accounts.program {
|
||||||
|
UpgradeableLoaderState::Program {
|
||||||
|
programdata_address,
|
||||||
|
} => {
|
||||||
|
if programdata_address != ctx.accounts.program_data.key() {
|
||||||
|
return Err(CustomError::InvalidProgramDataAddress.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(CustomError::AccountNotProgram.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctx.accounts.settings.admin_data = admin_data;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[account]
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct Settings {
|
||||||
|
admin_data: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[error]
|
||||||
|
pub enum CustomError {
|
||||||
|
InvalidProgramDataAddress,
|
||||||
|
AccountNotProgram,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
#[instruction(admin_data: u64)]
|
||||||
|
pub struct SetAdminSettings<'info> {
|
||||||
|
#[account(init, payer = authority)]
|
||||||
|
pub settings: Account<'info, Settings>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub authority: Signer<'info>,
|
||||||
|
#[account(address = crate::ID)]
|
||||||
|
pub program: Account<'info, UpgradeableLoaderState>,
|
||||||
|
#[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
|
||||||
|
pub program_data: Account<'info, ProgramData>,
|
||||||
|
pub system_program: Program<'info, System>,
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
import * as anchor from '@project-serum/anchor';
|
||||||
|
import { Program } from '@project-serum/anchor';
|
||||||
|
import { findProgramAddressSync } from '@project-serum/anchor/dist/cjs/utils/pubkey';
|
||||||
|
import { PublicKey } from '@solana/web3.js';
|
||||||
|
import assert from 'assert';
|
||||||
|
import { BpfUpgradeableState } from '../target/types/bpf_upgradeable_state';
|
||||||
|
|
||||||
|
describe('bpf_upgradeable_state', () => {
|
||||||
|
const provider = anchor.Provider.env();
|
||||||
|
// Configure the client to use the local cluster.
|
||||||
|
anchor.setProvider(provider);
|
||||||
|
|
||||||
|
const program = anchor.workspace.BpfUpgradeableState as Program<BpfUpgradeableState>;
|
||||||
|
const programDataAddress = findProgramAddressSync(
|
||||||
|
[program.programId.toBytes()],
|
||||||
|
new anchor.web3.PublicKey("BPFLoaderUpgradeab1e11111111111111111111111")
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
it('Reads ProgramData and sets field', async () => {
|
||||||
|
const settings = anchor.web3.Keypair.generate();
|
||||||
|
const tx = await program.rpc.setAdminSettings(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);
|
||||||
|
|
||||||
|
console.log("Your transaction signature", tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Validates constraint on ProgramData', async () => {
|
||||||
|
const settings = anchor.web3.Keypair.generate();
|
||||||
|
try {
|
||||||
|
const authority = anchor.web3.Keypair.generate();
|
||||||
|
await provider.connection.confirmTransaction(
|
||||||
|
await provider.connection.requestAirdrop(authority.publicKey, 10000000000),
|
||||||
|
"confirmed"
|
||||||
|
);
|
||||||
|
await program.rpc.setAdminSettings(new anchor.BN(500), {
|
||||||
|
accounts: {
|
||||||
|
authority: authority.publicKey,
|
||||||
|
systemProgram: anchor.web3.SystemProgram.programId,
|
||||||
|
programData: programDataAddress,
|
||||||
|
settings: settings.publicKey,
|
||||||
|
program: program.programId,
|
||||||
|
},
|
||||||
|
signers: [settings, authority]
|
||||||
|
});
|
||||||
|
assert.ok(false);
|
||||||
|
} catch (err) {
|
||||||
|
assert.equal(err.code, 143);
|
||||||
|
assert.equal(err.msg, "A raw constraint was violated");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Validates that account is ProgramData', async () => {
|
||||||
|
const settings = anchor.web3.Keypair.generate();
|
||||||
|
try {
|
||||||
|
await program.rpc.setAdminSettings(new anchor.BN(500), {
|
||||||
|
accounts: {
|
||||||
|
authority: program.provider.wallet.publicKey,
|
||||||
|
systemProgram: anchor.web3.SystemProgram.programId,
|
||||||
|
programData: program.programId,
|
||||||
|
settings: settings.publicKey,
|
||||||
|
program: program.programId,
|
||||||
|
},
|
||||||
|
signers: [settings]
|
||||||
|
});
|
||||||
|
assert.ok(false);
|
||||||
|
} catch (err) {
|
||||||
|
assert.equal(err.code, 173);
|
||||||
|
assert.equal(err.msg, "The given account is not a program data account");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Validates that account is owned by the upgradeable bpf loader', async () => {
|
||||||
|
const settings = anchor.web3.Keypair.generate();
|
||||||
|
try {
|
||||||
|
await program.rpc.setAdminSettings(new anchor.BN(500), {
|
||||||
|
accounts: {
|
||||||
|
authority: program.provider.wallet.publicKey,
|
||||||
|
systemProgram: anchor.web3.SystemProgram.programId,
|
||||||
|
programData: program.provider.wallet.publicKey,
|
||||||
|
settings: settings.publicKey,
|
||||||
|
program: program.programId,
|
||||||
|
},
|
||||||
|
signers: [settings]
|
||||||
|
});
|
||||||
|
assert.ok(false);
|
||||||
|
} catch (err) {
|
||||||
|
assert.equal(err.code, 167);
|
||||||
|
assert.equal(err.msg, "The given account is not owned by the executing program");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Deserializes UpgradableLoaderState 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.setAdminSettings(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, 300);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["mocha", "chai"],
|
||||||
|
"typeRoots": ["./node_modules/@types"],
|
||||||
|
"lib": ["es2015"],
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es6",
|
||||||
|
"esModuleInterop": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,10 +50,10 @@
|
||||||
snake-case "^3.0.4"
|
snake-case "^3.0.4"
|
||||||
toml "^3.0.0"
|
toml "^3.0.0"
|
||||||
|
|
||||||
"@project-serum/anchor@^0.18.0":
|
"@project-serum/anchor@^0.18.2":
|
||||||
version "0.18.0"
|
version "0.18.2"
|
||||||
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.18.0.tgz#867144282e59482230f797f73ee9f5634f846061"
|
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.18.2.tgz#0f13b5c2046446b7c24cf28763eec90febb28485"
|
||||||
integrity sha512-WTm+UB93MoxyCbjnHIibv/uUEoO/5gL4GEtE/aMioLF8Z4i0vCMPnvAN0xpk9VBu3t7ld2DcCE/L+6Z7dwU++w==
|
integrity sha512-uyjiN/3Ipp+4hrZRm/hG18HzGLZyvP790LXrCsGO3IWxSl28YRhiGEpKnZycfMW94R7nxdUoE3wY67V+ZHSQBQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@project-serum/borsh" "^0.2.2"
|
"@project-serum/borsh" "^0.2.2"
|
||||||
"@solana/web3.js" "^1.17.0"
|
"@solana/web3.js" "^1.17.0"
|
||||||
|
|
|
@ -90,6 +90,7 @@ const LangErrorCode = {
|
||||||
AccountNotSigner: 170,
|
AccountNotSigner: 170,
|
||||||
AccountNotSystemOwned: 171,
|
AccountNotSystemOwned: 171,
|
||||||
AccountNotInitialized: 172,
|
AccountNotInitialized: 172,
|
||||||
|
AccountNotProgramData: 173,
|
||||||
|
|
||||||
// State.
|
// State.
|
||||||
StateInvalidAddress: 180,
|
StateInvalidAddress: 180,
|
||||||
|
@ -180,6 +181,10 @@ const LangErrorMessage = new Map([
|
||||||
LangErrorCode.AccountNotInitialized,
|
LangErrorCode.AccountNotInitialized,
|
||||||
"The program expected this account to be already initialized",
|
"The program expected this account to be already initialized",
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
LangErrorCode.AccountNotProgramData,
|
||||||
|
"The given account is not a program data account",
|
||||||
|
],
|
||||||
|
|
||||||
// State.
|
// State.
|
||||||
[
|
[
|
||||||
|
|
Loading…
Reference in New Issue