lang: Move program check to try_from (#660)
This commit is contained in:
parent
afa218f797
commit
f4f60d7fab
|
@ -28,6 +28,7 @@ incremented for features.
|
||||||
* lang: `bump` must be provided when using the `seeds` constraint. This has been added as an extra safety constraint to ensure that whenever a PDA is initialized via a constraint the bump used is the one created by `Pubkey::find_program_address` ([#641](https://github.com/project-serum/anchor/pull/641)).
|
* lang: `bump` must be provided when using the `seeds` constraint. This has been added as an extra safety constraint to ensure that whenever a PDA is initialized via a constraint the bump used is the one created by `Pubkey::find_program_address` ([#641](https://github.com/project-serum/anchor/pull/641)).
|
||||||
* lang: `try_from_init` has been removed from `Loader`, `ProgramAccount`, and `CpiAccount` and replaced with `try_from_unchecked` ([#641](https://github.com/project-serum/anchor/pull/641)).
|
* lang: `try_from_init` has been removed from `Loader`, `ProgramAccount`, and `CpiAccount` and replaced with `try_from_unchecked` ([#641](https://github.com/project-serum/anchor/pull/641)).
|
||||||
* lang: Remove `AccountsInit` trait ([#641](https://github.com/project-serum/anchor/pull/641)).
|
* lang: Remove `AccountsInit` trait ([#641](https://github.com/project-serum/anchor/pull/641)).
|
||||||
|
* lang: `try_from` methods for `ProgramAccount`, `Loader`, and `ProgramState` now take in an additional `program_id: &Pubkey` parameter ([#660](https://github.com/project-serum/anchor/pull/660)).
|
||||||
|
|
||||||
## [0.13.2] - 2021-08-11
|
## [0.13.2] - 2021-08-11
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,6 @@ module.exports = {
|
||||||
"/tutorials/tutorial-2",
|
"/tutorials/tutorial-2",
|
||||||
"/tutorials/tutorial-3",
|
"/tutorials/tutorial-3",
|
||||||
"/tutorials/tutorial-4",
|
"/tutorials/tutorial-4",
|
||||||
"/tutorials/tutorial-5",
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,84 +1,61 @@
|
||||||
# State structs
|
# Errors
|
||||||
|
|
||||||
Up until now, we've treated programs on Solana as stateless, using accounts to persist
|
If you've ever programmed on a blockchain, you've probably been frustrated by
|
||||||
state between instruction invocations. In this tutorial, we'll give Solana programs the
|
either non existant or opaque error codes. Anchor attempts to address this by
|
||||||
illusion of state by introducing state structs, which define program account
|
providing the `#[error]` attribute, which can be used to create typed Errors with
|
||||||
singletons that can be operated over like any other account.
|
descriptive messages that automatically propagate to the client.
|
||||||
|
|
||||||
## Clone the Repo
|
|
||||||
|
|
||||||
To get started, clone the repo.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/project-serum/anchor
|
|
||||||
```
|
|
||||||
|
|
||||||
And change directories to the [example](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-4).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd anchor/examples/tutorial/basic-4
|
|
||||||
```
|
|
||||||
|
|
||||||
## Defining a Program
|
## Defining a Program
|
||||||
|
|
||||||
<<< @/../examples/tutorial/basic-4/programs/basic-4/src/lib.rs#code
|
For example,
|
||||||
|
|
||||||
Unlike the previous examples, all the instructions here not only take in an `Accounts`
|
```rust
|
||||||
struct, but they also operate over a mutable, global account marked by the `#[state]`
|
use anchor_lang::prelude::*;
|
||||||
attribute. Every instruction defined in the corresponding `impl` block will have access
|
|
||||||
to this account, making it a great place to store global program state.
|
|
||||||
|
|
||||||
### How it works
|
#[program]
|
||||||
|
mod errors {
|
||||||
|
use super::*;
|
||||||
|
pub fn hello(_ctx: Context<Hello>) -> Result<()> {
|
||||||
|
Err(ErrorCode::Hello.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
We are able to give a program the illusion of state by adopting conventions in the framework. When invoking the `new` constructor, Anchor will automatically create a
|
#[derive(Accounts)]
|
||||||
program-owned account inside the program itself, invoking the system program's [create_account_with_seed](https://docs.rs/solana-program/1.5.5/solana_program/system_instruction/fn.create_account_with_seed.html) instruction, using `Pubkey::find_program_address(&[], program_id)` as the **base** and a deterministic string as the **seed** (the string doesn't
|
pub struct Hello {}
|
||||||
matter, as long as the framework is consistent).
|
|
||||||
|
|
||||||
This all has the effect of
|
#[error]
|
||||||
giving the `#[state]` account a deterministic address, and so as long as all clients
|
pub enum ErrorCode {
|
||||||
and programs adopt this convention, programs can have the illusion of state in addition
|
#[msg("This is an error message clients will automatically display")]
|
||||||
to the full power of the lower level Solana accounts API. Of course, Anchor will handle this all for you, so you never have to worry about these details.
|
Hello,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Using the client
|
Observe the [#[error]](https://docs.rs/anchor-lang/latest/anchor_lang/attr.error.html) attribute on the `ErrorCode` enum. This macro generates two types: an `Error` and a `Result`, both of which can be used when returning from your program.
|
||||||
|
|
||||||
### Invoke the constructor
|
To use the `Error`, you can simply use the user defined `ErrorCode` with Rust's [From](https://doc.rust-lang.org/std/convert/trait.From.html) trait. If you're unfamiliar with `From`, no worries. Just know that you need to either call
|
||||||
|
`.into()` when using your `ErrorCode`. Or use Rust's `?` operator, when returning an error.
|
||||||
|
Both of these will automatically convert *into* the correct `Error`.
|
||||||
|
|
||||||
To access the `#[state]` account and associated instructions, you can use the
|
::: details
|
||||||
`anchor.state` namespace on the client. For example, to invoke the constructor,
|
What's the deal with this From stuff? Well, because the Solana runtime expects a [ProgramError](https://docs.rs/solana-program/1.5.5/solana_program/program_error/enum.ProgramError.html) in the return value. The framework needs to wrap the user defined error code into a
|
||||||
|
`ProgramError::Code` variant, before returning. The alternative would be to use the
|
||||||
|
`ProgramError` directly.
|
||||||
|
:::
|
||||||
|
|
||||||
<<< @/../examples/tutorial/basic-4/tests/basic-4.js#ctor
|
## Using the Client
|
||||||
|
|
||||||
Note that the constructor can only be invoked once per program. All subsequent calls
|
When using the client, we get the error message.
|
||||||
to it will fail, since, as explained above, an account at a deterministic address
|
|
||||||
will be created.
|
|
||||||
|
|
||||||
### Fetch the state
|
```javascript
|
||||||
|
try {
|
||||||
|
const tx = await program.rpc.hello();
|
||||||
|
assert.ok(false);
|
||||||
|
} catch (err) {
|
||||||
|
const errMsg = "This is an error message clients will automatically display";
|
||||||
|
assert.equal(err.toString(), errMsg);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
To fetch the state account,
|
It's that easy. :)
|
||||||
|
|
||||||
<<< @/../examples/tutorial/basic-4/tests/basic-4.js#accessor
|
To run the full example, go [here](https://github.com/project-serum/anchor/tree/master/examples/errors).
|
||||||
|
|
||||||
### Invoke an instruction
|
|
||||||
|
|
||||||
To invoke an instruction,
|
|
||||||
|
|
||||||
<<< @/../examples/tutorial/basic-4/tests/basic-4.js#instruction
|
|
||||||
|
|
||||||
## CPI
|
|
||||||
|
|
||||||
Performing CPI from one Anchor program to another's state methods is very similar to performing CPI to normal Anchor instructions, except for two differences:
|
|
||||||
|
|
||||||
1. All the generated instructions are located under the `<my_program>::cpi::state` module.
|
|
||||||
2. You must use a [CpiStateContext](https://docs.rs/anchor-lang/latest/anchor_lang/struct.CpiStateContext.html), instead of a `[CpiContext](https://docs.rs/anchor-lang/latest/anchor_lang/struct.CpiContext.html).
|
|
||||||
|
|
||||||
For a full example, see the `test_state_cpi` instruction, [here](https://github.com/project-serum/anchor/blob/master/examples/misc/programs/misc/src/lib.rs#L39).
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Using state structs is intuitive. However, due to the fact that accounts
|
|
||||||
on Solana have a fixed size, applications often need to use accounts
|
|
||||||
directly in addition to `#[state]` stucts.
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
Next we'll discuss errors.
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
# Errors
|
|
||||||
|
|
||||||
If you've ever programmed on a blockchain, you've probably been frustrated by
|
|
||||||
either non existant or opaque error codes. Anchor attempts to address this by
|
|
||||||
providing the `#[error]` attribute, which can be used to create typed Errors with
|
|
||||||
descriptive messages that automatically propagate to the client.
|
|
||||||
|
|
||||||
## Defining a Program
|
|
||||||
|
|
||||||
For example,
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use anchor_lang::prelude::*;
|
|
||||||
|
|
||||||
#[program]
|
|
||||||
mod errors {
|
|
||||||
use super::*;
|
|
||||||
pub fn hello(_ctx: Context<Hello>) -> Result<()> {
|
|
||||||
Err(ErrorCode::Hello.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Accounts)]
|
|
||||||
pub struct Hello {}
|
|
||||||
|
|
||||||
#[error]
|
|
||||||
pub enum ErrorCode {
|
|
||||||
#[msg("This is an error message clients will automatically display")]
|
|
||||||
Hello,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Observe the [#[error]](https://docs.rs/anchor-lang/latest/anchor_lang/attr.error.html) attribute on the `ErrorCode` enum. This macro generates two types: an `Error` and a `Result`, both of which can be used when returning from your program.
|
|
||||||
|
|
||||||
To use the `Error`, you can simply use the user defined `ErrorCode` with Rust's [From](https://doc.rust-lang.org/std/convert/trait.From.html) trait. If you're unfamiliar with `From`, no worries. Just know that you need to either call
|
|
||||||
`.into()` when using your `ErrorCode`. Or use Rust's `?` operator, when returning an error.
|
|
||||||
Both of these will automatically convert *into* the correct `Error`.
|
|
||||||
|
|
||||||
::: details
|
|
||||||
What's the deal with this From stuff? Well, because the Solana runtime expects a [ProgramError](https://docs.rs/solana-program/1.5.5/solana_program/program_error/enum.ProgramError.html) in the return value. The framework needs to wrap the user defined error code into a
|
|
||||||
`ProgramError::Code` variant, before returning. The alternative would be to use the
|
|
||||||
`ProgramError` directly.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Using the Client
|
|
||||||
|
|
||||||
When using the client, we get the error message.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
try {
|
|
||||||
const tx = await program.rpc.hello();
|
|
||||||
assert.ok(false);
|
|
||||||
} catch (err) {
|
|
||||||
const errMsg = "This is an error message clients will automatically display";
|
|
||||||
assert.equal(err.toString(), errMsg);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
It's that easy. :)
|
|
||||||
|
|
||||||
To run the full example, go [here](https://github.com/project-serum/anchor/tree/master/examples/errors).
|
|
|
@ -703,10 +703,10 @@ impl<'info> DropStakeReward<'info> {
|
||||||
fn into_srm_reward(&self) -> CpiContext<'_, '_, '_, 'info, registry::DropReward<'info>> {
|
fn into_srm_reward(&self) -> CpiContext<'_, '_, '_, 'info, registry::DropReward<'info>> {
|
||||||
let program = self.registry_program.clone();
|
let program = self.registry_program.clone();
|
||||||
let accounts = registry::DropReward {
|
let accounts = registry::DropReward {
|
||||||
registrar: ProgramAccount::try_from(&self.srm.registrar).unwrap(),
|
registrar: ProgramAccount::try_from(program.key, &self.srm.registrar).unwrap(),
|
||||||
reward_event_q: ProgramAccount::try_from(&self.srm.reward_event_q).unwrap(),
|
reward_event_q: ProgramAccount::try_from(program.key, &self.srm.reward_event_q).unwrap(),
|
||||||
pool_mint: self.srm.pool_mint.clone(),
|
pool_mint: self.srm.pool_mint.clone(),
|
||||||
vendor: ProgramAccount::try_from(&self.srm.vendor).unwrap(),
|
vendor: ProgramAccount::try_from(program.key, &self.srm.vendor).unwrap(),
|
||||||
vendor_vault: CpiAccount::try_from(&self.srm.vendor_vault).unwrap(),
|
vendor_vault: CpiAccount::try_from(&self.srm.vendor_vault).unwrap(),
|
||||||
depositor: self.stake.to_account_info(),
|
depositor: self.stake.to_account_info(),
|
||||||
depositor_authority: self.officer.to_account_info(),
|
depositor_authority: self.officer.to_account_info(),
|
||||||
|
@ -720,10 +720,10 @@ impl<'info> DropStakeReward<'info> {
|
||||||
fn into_msrm_reward(&self) -> CpiContext<'_, '_, '_, 'info, registry::DropReward<'info>> {
|
fn into_msrm_reward(&self) -> CpiContext<'_, '_, '_, 'info, registry::DropReward<'info>> {
|
||||||
let program = self.registry_program.clone();
|
let program = self.registry_program.clone();
|
||||||
let accounts = registry::DropReward {
|
let accounts = registry::DropReward {
|
||||||
registrar: ProgramAccount::try_from(&self.msrm.registrar).unwrap(),
|
registrar: ProgramAccount::try_from(program.key, &self.msrm.registrar).unwrap(),
|
||||||
reward_event_q: ProgramAccount::try_from(&self.msrm.reward_event_q).unwrap(),
|
reward_event_q: ProgramAccount::try_from(program.key, &self.msrm.reward_event_q).unwrap(),
|
||||||
pool_mint: self.msrm.pool_mint.clone(),
|
pool_mint: self.msrm.pool_mint.clone(),
|
||||||
vendor: ProgramAccount::try_from(&self.msrm.vendor).unwrap(),
|
vendor: ProgramAccount::try_from(program.key, &self.msrm.vendor).unwrap(),
|
||||||
vendor_vault: CpiAccount::try_from(&self.msrm.vendor_vault).unwrap(),
|
vendor_vault: CpiAccount::try_from(&self.msrm.vendor_vault).unwrap(),
|
||||||
depositor: self.stake.to_account_info(),
|
depositor: self.stake.to_account_info(),
|
||||||
depositor_authority: self.officer.to_account_info(),
|
depositor_authority: self.officer.to_account_info(),
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub struct CpiAccount<'a, T: AccountDeserialize + Clone> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: AccountDeserialize + Clone> CpiAccount<'a, T> {
|
impl<'a, T: AccountDeserialize + Clone> CpiAccount<'a, T> {
|
||||||
pub fn new(info: AccountInfo<'a>, account: Box<T>) -> CpiAccount<'a, T> {
|
fn new(info: AccountInfo<'a>, account: Box<T>) -> CpiAccount<'a, T> {
|
||||||
Self { info, account }
|
Self { info, account }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,9 +39,14 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
||||||
|
|
||||||
/// Constructs a new `Loader` from a previously initialized account.
|
/// Constructs a new `Loader` from a previously initialized account.
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn try_from(acc_info: &AccountInfo<'info>) -> Result<Loader<'info, T>, ProgramError> {
|
pub fn try_from(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
acc_info: &AccountInfo<'info>,
|
||||||
|
) -> Result<Loader<'info, T>, ProgramError> {
|
||||||
|
if acc_info.owner != program_id {
|
||||||
|
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||||
|
}
|
||||||
let data: &[u8] = &acc_info.try_borrow_data()?;
|
let data: &[u8] = &acc_info.try_borrow_data()?;
|
||||||
|
|
||||||
// Discriminator must match.
|
// Discriminator must match.
|
||||||
let mut disc_bytes = [0u8; 8];
|
let mut disc_bytes = [0u8; 8];
|
||||||
disc_bytes.copy_from_slice(&data[..8]);
|
disc_bytes.copy_from_slice(&data[..8]);
|
||||||
|
@ -55,8 +60,12 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
||||||
/// Constructs a new `Loader` from an uninitialized account.
|
/// Constructs a new `Loader` from an uninitialized account.
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn try_from_unchecked(
|
pub fn try_from_unchecked(
|
||||||
|
program_id: &Pubkey,
|
||||||
acc_info: &AccountInfo<'info>,
|
acc_info: &AccountInfo<'info>,
|
||||||
) -> Result<Loader<'info, T>, ProgramError> {
|
) -> Result<Loader<'info, T>, ProgramError> {
|
||||||
|
if acc_info.owner != program_id {
|
||||||
|
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||||
|
}
|
||||||
Ok(Loader::new(acc_info.clone()))
|
Ok(Loader::new(acc_info.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,10 +140,7 @@ impl<'info, T: ZeroCopy> Accounts<'info> for Loader<'info, T> {
|
||||||
}
|
}
|
||||||
let account = &accounts[0];
|
let account = &accounts[0];
|
||||||
*accounts = &accounts[1..];
|
*accounts = &accounts[1..];
|
||||||
let l = Loader::try_from(account)?;
|
let l = Loader::try_from(program_id, account)?;
|
||||||
if l.acc_info.owner != program_id {
|
|
||||||
return Err(ErrorCode::AccountNotProgramOwned.into());
|
|
||||||
}
|
|
||||||
Ok(l)
|
Ok(l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ struct Inner<'info, T: AccountSerialize + AccountDeserialize + Clone> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramAccount<'a, T> {
|
impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramAccount<'a, T> {
|
||||||
pub fn new(info: AccountInfo<'a>, account: T) -> ProgramAccount<'a, T> {
|
fn new(info: AccountInfo<'a>, account: T) -> ProgramAccount<'a, T> {
|
||||||
Self {
|
Self {
|
||||||
inner: Box::new(Inner { info, account }),
|
inner: Box::new(Inner { info, account }),
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,13 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramAccount<'a, T>
|
||||||
|
|
||||||
/// Deserializes the given `info` into a `ProgramAccount`.
|
/// Deserializes the given `info` into a `ProgramAccount`.
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn try_from(info: &AccountInfo<'a>) -> Result<ProgramAccount<'a, T>, ProgramError> {
|
pub fn try_from(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
info: &AccountInfo<'a>,
|
||||||
|
) -> Result<ProgramAccount<'a, T>, ProgramError> {
|
||||||
|
if info.owner != program_id {
|
||||||
|
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||||
|
}
|
||||||
let mut data: &[u8] = &info.try_borrow_data()?;
|
let mut data: &[u8] = &info.try_borrow_data()?;
|
||||||
Ok(ProgramAccount::new(
|
Ok(ProgramAccount::new(
|
||||||
info.clone(),
|
info.clone(),
|
||||||
|
@ -40,14 +46,17 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramAccount<'a, T>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserializes the zero-initialized `info` into a `ProgramAccount` without
|
/// Deserializes the given `info` into a `ProgramAccount` without checking
|
||||||
/// checking the account type. This should only be used upon program account
|
/// the account discriminator. Be careful when using this and avoid it if
|
||||||
/// initialization (since the entire account data array is zeroed and thus
|
/// possible.
|
||||||
/// no account type is set).
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn try_from_unchecked(
|
pub fn try_from_unchecked(
|
||||||
|
program_id: &Pubkey,
|
||||||
info: &AccountInfo<'a>,
|
info: &AccountInfo<'a>,
|
||||||
) -> Result<ProgramAccount<'a, T>, ProgramError> {
|
) -> Result<ProgramAccount<'a, T>, ProgramError> {
|
||||||
|
if info.owner != program_id {
|
||||||
|
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||||
|
}
|
||||||
let mut data: &[u8] = &info.try_borrow_data()?;
|
let mut data: &[u8] = &info.try_borrow_data()?;
|
||||||
Ok(ProgramAccount::new(
|
Ok(ProgramAccount::new(
|
||||||
info.clone(),
|
info.clone(),
|
||||||
|
@ -75,11 +84,7 @@ where
|
||||||
}
|
}
|
||||||
let account = &accounts[0];
|
let account = &accounts[0];
|
||||||
*accounts = &accounts[1..];
|
*accounts = &accounts[1..];
|
||||||
let pa = ProgramAccount::try_from(account)?;
|
ProgramAccount::try_from(program_id, account)
|
||||||
if pa.inner.info.owner != program_id {
|
|
||||||
return Err(ErrorCode::AccountNotProgramOwned.into());
|
|
||||||
}
|
|
||||||
Ok(pa)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ struct Inner<'info, T: AccountSerialize + AccountDeserialize + Clone> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramState<'a, T> {
|
impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramState<'a, T> {
|
||||||
pub fn new(info: AccountInfo<'a>, account: T) -> ProgramState<'a, T> {
|
fn new(info: AccountInfo<'a>, account: T) -> ProgramState<'a, T> {
|
||||||
Self {
|
Self {
|
||||||
inner: Box::new(Inner { info, account }),
|
inner: Box::new(Inner { info, account }),
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,17 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramState<'a, T> {
|
||||||
|
|
||||||
/// Deserializes the given `info` into a `ProgramState`.
|
/// Deserializes the given `info` into a `ProgramState`.
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn try_from(info: &AccountInfo<'a>) -> Result<ProgramState<'a, T>, ProgramError> {
|
pub fn try_from(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
info: &AccountInfo<'a>,
|
||||||
|
) -> Result<ProgramState<'a, T>, ProgramError> {
|
||||||
|
if info.owner != program_id {
|
||||||
|
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||||
|
}
|
||||||
|
if info.key != &Self::address(program_id) {
|
||||||
|
solana_program::msg!("Invalid state address");
|
||||||
|
return Err(ErrorCode::StateInvalidAddress.into());
|
||||||
|
}
|
||||||
let mut data: &[u8] = &info.try_borrow_data()?;
|
let mut data: &[u8] = &info.try_borrow_data()?;
|
||||||
Ok(ProgramState::new(
|
Ok(ProgramState::new(
|
||||||
info.clone(),
|
info.clone(),
|
||||||
|
@ -65,18 +75,7 @@ where
|
||||||
}
|
}
|
||||||
let account = &accounts[0];
|
let account = &accounts[0];
|
||||||
*accounts = &accounts[1..];
|
*accounts = &accounts[1..];
|
||||||
|
ProgramState::try_from(program_id, account)
|
||||||
if account.key != &Self::address(program_id) {
|
|
||||||
solana_program::msg!("Invalid state address");
|
|
||||||
return Err(ErrorCode::StateInvalidAddress.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let pa = ProgramState::try_from(account)?;
|
|
||||||
if pa.inner.info.owner != program_id {
|
|
||||||
solana_program::msg!("Invalid state owner");
|
|
||||||
return Err(ErrorCode::AccountNotProgramOwned.into());
|
|
||||||
}
|
|
||||||
Ok(pa)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,6 +153,7 @@ pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macr
|
||||||
return Err(anchor_lang::__private::ErrorCode::ConstraintZero.into());
|
return Err(anchor_lang::__private::ErrorCode::ConstraintZero.into());
|
||||||
}
|
}
|
||||||
#account_wrapper_ty::try_from_unchecked(
|
#account_wrapper_ty::try_from_unchecked(
|
||||||
|
program_id,
|
||||||
&#field,
|
&#field,
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
|
@ -439,6 +440,7 @@ pub fn generate_pda(
|
||||||
},
|
},
|
||||||
quote! {
|
quote! {
|
||||||
#account_wrapper_ty::try_from_unchecked(
|
#account_wrapper_ty::try_from_unchecked(
|
||||||
|
program_id,
|
||||||
&#field.to_account_info(),
|
&#field.to_account_info(),
|
||||||
)?
|
)?
|
||||||
},
|
},
|
||||||
|
|
|
@ -227,7 +227,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Zero copy deserialize.
|
// Zero copy deserialize.
|
||||||
let loader: anchor_lang::Loader<#mod_name::#name> = anchor_lang::Loader::try_from_unchecked(&ctor_accounts.to)?;
|
let loader: anchor_lang::Loader<#mod_name::#name> = anchor_lang::Loader::try_from_unchecked(program_id, &ctor_accounts.to)?;
|
||||||
|
|
||||||
// Invoke the ctor in a new lexical scope so that
|
// Invoke the ctor in a new lexical scope so that
|
||||||
// the zero-copy RefMut gets dropped. Required
|
// the zero-copy RefMut gets dropped. Required
|
||||||
|
@ -367,7 +367,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
return Err(anchor_lang::__private::ErrorCode::AccountNotEnoughKeys.into());
|
return Err(anchor_lang::__private::ErrorCode::AccountNotEnoughKeys.into());
|
||||||
}
|
}
|
||||||
let state_account = &remaining_accounts[0];
|
let state_account = &remaining_accounts[0];
|
||||||
let loader: anchor_lang::Loader<#mod_name::#name> = anchor_lang::Loader::try_from(&state_account)?;
|
let loader: anchor_lang::Loader<#mod_name::#name> = anchor_lang::Loader::try_from(program_id, &state_account)?;
|
||||||
remaining_accounts = &remaining_accounts[1..];
|
remaining_accounts = &remaining_accounts[1..];
|
||||||
|
|
||||||
// Deserialize accounts.
|
// Deserialize accounts.
|
||||||
|
|
Loading…
Reference in New Issue