Implement account persistence

Accounts with data will now be persisted at the end of the program execution

Change-Id: I1dd29f521f5c659ced5758203acf532b645f3b44
This commit is contained in:
Hendrik Hofstadt 2021-06-11 17:14:21 +02:00
parent c3829b9266
commit b3b083b08a
7 changed files with 72 additions and 24 deletions

View File

@ -68,10 +68,10 @@ fn main() -> Result<(), ErrBox> {
payer: Signer(payer), payer: Signer(payer),
}; };
let init_args = types::BridgeConfig { let init_args = bridge::instruction::Instruction::Initialize(types::BridgeConfig {
guardian_set_expiration_time: DEFAULT_GUARDIAN_SET_EXPIRATION_TIME, guardian_set_expiration_time: DEFAULT_GUARDIAN_SET_EXPIRATION_TIME,
fee: DEFAULT_MESSAGE_FEE, fee: DEFAULT_MESSAGE_FEE,
}; });
let ix_data = init_args.try_to_vec()?; let ix_data = init_args.try_to_vec()?;

View File

@ -96,6 +96,10 @@ impl<'a, 'b: 'a, 'c, T: DeserializePayload> Peel<'a, 'b, 'c> for PayloadMessage<
fn deps() -> Vec<Pubkey> { fn deps() -> Vec<Pubkey> {
Data::<'b, PostedMessage, { AccountState::Initialized }>::deps() Data::<'b, PostedMessage, { AccountState::Initialized }>::deps()
} }
fn persist(&self, program_id: &Pubkey) -> Result<()> {
Data::persist(&self.0, program_id)
}
} }
impl<'b, T: DeserializePayload> Deref for PayloadMessage<'b, T> { impl<'b, T: DeserializePayload> Deref for PayloadMessage<'b, T> {

View File

@ -80,7 +80,8 @@ pub use crate::{
}; };
/// Library name and version to print in entrypoint. Must be evaluated in this crate in order to do the right thing /// Library name and version to print in entrypoint. Must be evaluated in this crate in order to do the right thing
pub const PKG_NAME_VERSION: &'static str = concat!(env!("CARGO_PKG_NAME"), " ", env!("CARGO_PKG_VERSION")); pub const PKG_NAME_VERSION: &'static str =
concat!(env!("CARGO_PKG_NAME"), " ", env!("CARGO_PKG_VERSION"));
pub struct ExecutionContext<'a, 'b: 'a> { pub struct ExecutionContext<'a, 'b: 'a> {
/// A reference to the program_id of the current program. /// A reference to the program_id of the current program.

View File

@ -41,7 +41,7 @@ macro_rules! solitaire {
Instruction::$row(ix_data) => { Instruction::$row(ix_data) => {
let (mut accounts): ($row) = FromAccounts::from(p, &mut a.iter(), &()).unwrap(); let (mut accounts): ($row) = FromAccounts::from(p, &mut a.iter(), &()).unwrap();
$fn(&ExecutionContext{program_id: p, accounts: a}, &mut accounts, ix_data)?; $fn(&ExecutionContext{program_id: p, accounts: a}, &mut accounts, ix_data)?;
accounts.persist(); Persist::persist(&accounts, p)?;
Ok(()) Ok(())
} }
)* )*
@ -128,6 +128,10 @@ macro_rules! data_wrapper {
fn deps() -> Vec<Pubkey> { fn deps() -> Vec<Pubkey> {
Data::<'_, $embed, { $state }>::deps() Data::<'_, $embed, { $state }>::deps()
} }
fn persist(&self, program_id: &Pubkey) -> solitaire::Result<()> {
Data::<'_, $embed, { $state }>::persist(self, program_id)
}
} }
impl<'b> solitaire::processors::seeded::Owned for $name<'b> { impl<'b> solitaire::processors::seeded::Owned for $name<'b> {

View File

@ -26,6 +26,7 @@ use crate::{
Result, Result,
SolitaireError, SolitaireError,
}; };
use borsh::BorshSerialize;
/// Generic Peel trait. This provides a way to describe what each "peeled" /// Generic Peel trait. This provides a way to describe what each "peeled"
/// layer of our constraints should check. /// layer of our constraints should check.
@ -35,6 +36,8 @@ pub trait Peel<'a, 'b: 'a, 'c> {
Self: Sized; Self: Sized;
fn deps() -> Vec<Pubkey>; fn deps() -> Vec<Pubkey>;
fn persist(&self, program_id: &Pubkey) -> Result<()>;
} }
/// Peel a Derived Key /// Peel a Derived Key
@ -49,9 +52,14 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>, const Seed: &'static str> Peel<'a, 'b,
_ => Err(SolitaireError::InvalidDerive(*ctx.info().key).into()), _ => Err(SolitaireError::InvalidDerive(*ctx.info().key).into()),
} }
} }
fn deps() -> Vec<Pubkey> { fn deps() -> Vec<Pubkey> {
T::deps() T::deps()
} }
fn persist(&self, program_id: &Pubkey) -> Result<()> {
T::persist(self, program_id)
}
} }
/// Peel a Signer. /// Peel a Signer.
@ -62,9 +70,14 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Signer<T> {
_ => Err(SolitaireError::InvalidSigner(*ctx.info().key).into()), _ => Err(SolitaireError::InvalidSigner(*ctx.info().key).into()),
} }
} }
fn deps() -> Vec<Pubkey> { fn deps() -> Vec<Pubkey> {
T::deps() T::deps()
} }
fn persist(&self, program_id: &Pubkey) -> Result<()> {
T::persist(self, program_id)
}
} }
/// Expicitly depend upon the System account. /// Expicitly depend upon the System account.
@ -75,9 +88,14 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for System<T> {
_ => panic!(), _ => panic!(),
} }
} }
fn deps() -> Vec<Pubkey> { fn deps() -> Vec<Pubkey> {
T::deps() T::deps()
} }
fn persist(&self, program_id: &Pubkey) -> Result<()> {
T::persist(self, program_id)
}
} }
/// Peel a Sysvar /// Peel a Sysvar
@ -94,9 +112,14 @@ where
_ => Err(SolitaireError::InvalidSysvar(*ctx.info().key).into()), _ => Err(SolitaireError::InvalidSysvar(*ctx.info().key).into()),
} }
} }
fn deps() -> Vec<Pubkey> { fn deps() -> Vec<Pubkey> {
vec![] vec![]
} }
fn persist(&self, _program_id: &Pubkey) -> Result<()> {
Ok(())
}
} }
/// This is our structural recursion base case, the trait system will stop generating new nested /// This is our structural recursion base case, the trait system will stop generating new nested
@ -108,12 +131,20 @@ impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for Info<'b> {
fn deps() -> Vec<Pubkey> { fn deps() -> Vec<Pubkey> {
vec![] vec![]
} }
fn persist(&self, _program_id: &Pubkey) -> Result<()> {
Ok(())
}
} }
/// This is our structural recursion base case, the trait system will stop generating new nested /// This is our structural recursion base case, the trait system will stop generating new nested
/// calls here. /// calls here.
impl<'a, 'b: 'a, 'c, T: BorshDeserialize + Owned + Default, const IsInitialized: AccountState> impl<
Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized> 'a,
'b: 'a,
'c,
T: BorshDeserialize + BorshSerialize + Owned + Default,
const IsInitialized: AccountState,
> 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> {
let mut initialized = false; let mut initialized = false;
@ -165,4 +196,19 @@ impl<'a, 'b: 'a, 'c, T: BorshDeserialize + Owned + Default, const IsInitialized:
vec![sysvar::rent::ID, system_program::ID] vec![sysvar::rent::ID, system_program::ID]
} }
fn persist(&self, program_id: &Pubkey) -> Result<()> {
// Only write to accounts owned by us
if self.0.owner != program_id {
return Ok(());
}
if !self.0.is_writable {
// TODO this needs to be checked properly
return Ok(());
}
self.1.serialize(&mut *self.0.data.borrow_mut())?;
Ok(())
}
} }

View File

@ -1,3 +1,5 @@
use solana_program::pubkey::Pubkey;
pub trait Persist { pub trait Persist {
fn persist(self); fn persist(&self, program_id: &Pubkey) -> crate::Result<()>;
} }

View File

@ -129,15 +129,16 @@ pub fn derive_from_accounts(input: TokenStream) -> TokenStream {
fn deps() -> Vec<solana_program::pubkey::Pubkey> { fn deps() -> Vec<solana_program::pubkey::Pubkey> {
#deps_method #deps_method
} }
fn persist(&self, program_id: &solana_program::pubkey::Pubkey) -> solitaire::Result<()> {
solitaire::Persist::persist(self, program_id)
}
} }
/// Macro generated implementation of Persist by Solitaire. /// Macro generated implementation of Persist by Solitaire.
impl #type_impl_g solitaire::Persist for #name #type_g { impl #type_impl_g solitaire::Persist for #name #type_g {
fn persist(self) { fn persist(&self, program_id: &solana_program::pubkey::Pubkey) -> solitaire::Result<()> {
use borsh::BorshSerialize; #persist_method
//self.guardian_set.serialize(
// &mut *self.guardian_set.0.data.borrow_mut()
//);
} }
} }
}; };
@ -256,24 +257,14 @@ fn generate_persist(name: &syn::Ident, data: &Data) -> TokenStream2 {
let ty = &f.ty; let ty = &f.ty;
quote! { quote! {
let #name: #ty = Peel::peel(&mut solitaire::Context::new( Peel::persist(&self.#name, program_id)?;
pid,
iter,
data,
))?;
} }
}); });
let names = fields.named.iter().map(|f| {
let name = &f.ident;
quote!(#name)
});
// Write out our iterator and return the filled structure. // Write out our iterator and return the filled structure.
quote! { quote! {
use solana_program::account_info::next_account_info;
#(#recurse;)* #(#recurse;)*
Ok(#name { #(#names,)* }) Ok(())
} }
} }