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.
#[derive(Debug)]
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.
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.
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> {
@ -126,6 +146,10 @@ where
/// calls here.
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> {
if ctx.immutable && ctx.info().is_writable {
return Err(SolitaireError::InvalidMutability(*ctx.info().key).into());
}
Ok(ctx.info().clone())
}
fn deps() -> Vec<Pubkey> {
@ -147,6 +171,10 @@ impl<
> Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized>
{
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.
let (initialized, data): (bool, T) = match IsInitialized {
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.
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>>,
/// This is a reference to the instruction data we are processing this
/// account for.
/// Reference to the data passed to the current instruction.
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>>,
/// Whether to enforce immutability.
pub immutable: bool,
}
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 {
Context {
this: program,
info: None,
immutable: true,
iter,
data,
info: None,
}
}

View File

@ -23,6 +23,9 @@ use borsh::{
BorshSerialize,
};
#[repr(transparent)]
pub struct Mut<Next>(pub Next);
#[repr(transparent)]
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> {
type Target = T;
fn deref(&self) -> &Self::Target {