diff --git a/solana/solitaire/program/src/error.rs b/solana/solitaire/program/src/error.rs index fd4d150fb..aa38774ce 100644 --- a/solana/solitaire/program/src/error.rs +++ b/solana/solitaire/program/src/error.rs @@ -12,6 +12,9 @@ pub type ErrBox = Box; /// 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), diff --git a/solana/solitaire/program/src/processors/peel.rs b/solana/solitaire/program/src/processors/peel.rs index b1c173042..60ebd1f14 100644 --- a/solana/solitaire/program/src/processors/peel.rs +++ b/solana/solitaire/program/src/processors/peel.rs @@ -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 +{ + fn peel(mut ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result { + 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 { + 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 { fn peel(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result { @@ -126,6 +146,10 @@ where /// calls here. impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for Info<'b> { fn peel(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result { + if ctx.immutable && ctx.info().is_writable { + return Err(SolitaireError::InvalidMutability(*ctx.info().key).into()); + } + Ok(ctx.info().clone()) } fn deps() -> Vec { @@ -147,6 +171,10 @@ impl< > Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized> { fn peel(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result { + 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 => { diff --git a/solana/solitaire/program/src/types/context.rs b/solana/solitaire/program/src/types/context.rs index 1bd556894..4908e9744 100644 --- a/solana/solitaire/program/src/types/context.rs +++ b/solana/solitaire/program/src/types/context.rs @@ -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, } } diff --git a/solana/solitaire/program/src/types/layers.rs b/solana/solitaire/program/src/types/layers.rs index d55768474..5fb48b2e1 100644 --- a/solana/solitaire/program/src/types/layers.rs +++ b/solana/solitaire/program/src/types/layers.rs @@ -23,6 +23,9 @@ use borsh::{ BorshSerialize, }; +#[repr(transparent)] +pub struct Mut(pub Next); + #[repr(transparent)] pub struct Signer(pub Next); @@ -48,6 +51,19 @@ impl DerefMut for Signer { } } +impl Deref for Mut { + type Target = T; + fn deref(&self) -> &Self::Target { + unsafe { std::mem::transmute(&self.0) } + } +} + +impl DerefMut for Mut { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { std::mem::transmute(&mut self.0) } + } +} + impl Deref for System { type Target = T; fn deref(&self) -> &Self::Target {