Add peel mutability checks for accounts.

Change-Id: Ic6a6fadd13a2b41d60a0c98f3b5d80d23ac263a6
This commit is contained in:
Reisen 2021-07-06 11:39:54 +00:00
parent 72951531f6
commit 76066c8cc6
4 changed files with 57 additions and 3 deletions

View File

@ -12,6 +12,9 @@ pub type ErrBox = Box<dyn std::error::Error>;
/// There are several places in Solitaire that might fail, we want descriptive errors. /// There are several places in Solitaire that might fail, we want descriptive errors.
#[derive(Debug)] #[derive(Debug)]
pub enum SolitaireError { pub enum SolitaireError {
/// The AccountInfo parser expected a mutable key where a readonly was found, or vice versa.
InvalidMutability(Pubkey),
/// The AccountInfo parser expected a Signer, but the account did not sign. /// The AccountInfo parser expected a Signer, but the account did not sign.
InvalidSigner(Pubkey), InvalidSigner(Pubkey),

View File

@ -62,6 +62,26 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>, const Seed: &'static str> Peel<'a, 'b,
} }
} }
/// Peel a Mutable key.
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Mut<T>
{
fn peel<I>(mut ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
ctx.immutable = false;
match ctx.info().is_writable {
true => T::peel(ctx).map(|v| Mut(v)),
_ => Err(SolitaireError::InvalidMutability(*ctx.info().key).into()),
}
}
fn deps() -> Vec<Pubkey> {
T::deps()
}
fn persist(&self, program_id: &Pubkey) -> Result<()> {
T::persist(self, program_id)
}
}
/// Peel a Signer. /// Peel a Signer.
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Signer<T> { impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Signer<T> {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> { fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
@ -126,6 +146,10 @@ where
/// calls here. /// calls here.
impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for Info<'b> { impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for Info<'b> {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> { fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
if ctx.immutable && ctx.info().is_writable {
return Err(SolitaireError::InvalidMutability(*ctx.info().key).into());
}
Ok(ctx.info().clone()) Ok(ctx.info().clone())
} }
fn deps() -> Vec<Pubkey> { fn deps() -> Vec<Pubkey> {
@ -147,6 +171,10 @@ impl<
> Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized> > Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized>
{ {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> { fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
if ctx.immutable && ctx.info().is_writable {
return Err(SolitaireError::InvalidMutability(*ctx.info().key).into());
}
// If we're initializing the type, we should emit system/rent as deps. // If we're initializing the type, we should emit system/rent as deps.
let (initialized, data): (bool, T) = match IsInitialized { let (initialized, data): (bool, T) = match IsInitialized {
AccountState::Uninitialized => { AccountState::Uninitialized => {

View File

@ -14,22 +14,29 @@ pub struct Context<'a, 'b: 'a, 'c, T> {
/// A reference to the program_id of the current program. /// A reference to the program_id of the current program.
pub this: &'a Pubkey, pub this: &'a Pubkey,
/// A reference to the instructions account list, one or more keys may be extracted during
/// the peeling process.
pub iter: &'c mut Iter<'a, AccountInfo<'b>>, pub iter: &'c mut Iter<'a, AccountInfo<'b>>,
/// This is a reference to the instruction data we are processing this /// Reference to the data passed to the current instruction.
/// account for.
pub data: &'a T, pub data: &'a T,
/// An optional account info for this Peelable item, some fields may be other structures that
/// do not themselves have an account info associated with the field.
pub info: Option<&'a AccountInfo<'b>>, pub info: Option<&'a AccountInfo<'b>>,
/// Whether to enforce immutability.
pub immutable: bool,
} }
impl<'a, 'b: 'a, 'c, T> Context<'a, 'b, 'c, T> { impl<'a, 'b: 'a, 'c, T> Context<'a, 'b, 'c, T> {
pub fn new(program: &'a Pubkey, iter: &'c mut Iter<'a, AccountInfo<'b>>, data: &'a T) -> Self { pub fn new(program: &'a Pubkey, iter: &'c mut Iter<'a, AccountInfo<'b>>, data: &'a T) -> Self {
Context { Context {
this: program, this: program,
info: None,
immutable: true,
iter, iter,
data, data,
info: None,
} }
} }

View File

@ -23,6 +23,9 @@ use borsh::{
BorshSerialize, BorshSerialize,
}; };
#[repr(transparent)]
pub struct Mut<Next>(pub Next);
#[repr(transparent)] #[repr(transparent)]
pub struct Signer<Next>(pub Next); pub struct Signer<Next>(pub Next);
@ -48,6 +51,19 @@ impl<T> DerefMut for Signer<T> {
} }
} }
impl<T> Deref for Mut<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { std::mem::transmute(&self.0) }
}
}
impl<T> DerefMut for Mut<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { std::mem::transmute(&mut self.0) }
}
}
impl<T> Deref for System<T> { impl<T> Deref for System<T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {