Solitaire: Extend Peel to support CPI re-wrapping

Change-Id: Iefa59f5d4fe36c9f9e2cd0fa997490eba0a1bb44
This commit is contained in:
Stan Drozd 2021-06-30 14:25:53 +02:00
parent 18a6f429cb
commit 5827ba7b30
6 changed files with 90 additions and 7 deletions

View File

@ -14,7 +14,7 @@ solana-program = "=1.7.0"
solana-sdk = "=1.7.0" solana-sdk = "=1.7.0"
solitaire = { path = "../../solitaire/program" } solitaire = { path = "../../solitaire/program" }
solitaire-client = {path = "../../solitaire/client" } solitaire-client = {path = "../../solitaire/client" }
bridge = { path = "../program", features = ["no-entrypoint", "client"] } bridge = { path = "../program", features = ["client"] }
primitive-types = { version = "0.7.2" } primitive-types = { version = "0.7.2" }
hex = "0.4.2" hex = "0.4.2"
thiserror = "1.0.20" thiserror = "1.0.20"

View File

@ -17,7 +17,7 @@ use byteorder::{
BigEndian, BigEndian,
ReadBytesExt, ReadBytesExt,
}; };
use solana_program::pubkey::Pubkey; use solana_program::{instruction::AccountMeta, pubkey::Pubkey};
use solitaire::{ use solitaire::{
processors::seeded::Seeded, processors::seeded::Seeded,
trace, trace,
@ -102,6 +102,10 @@ impl<'a, 'b: 'a, 'c, T: DeserializePayload> Peel<'a, 'b, 'c> for PayloadMessage<
fn persist(&self, program_id: &Pubkey) -> Result<()> { fn persist(&self, program_id: &Pubkey) -> Result<()> {
Data::persist(&self.0, program_id) Data::persist(&self.0, program_id)
} }
fn to_partial_cpi_meta(&self) -> Vec<AccountMeta> {
Data::to_partial_cpi_meta(&self.0)
}
} }
impl<'b, T: DeserializePayload> Deref for PayloadMessage<'b, T> { impl<'b, T: DeserializePayload> Deref for PayloadMessage<'b, T> {

View File

@ -123,4 +123,7 @@ pub trait FromAccounts<'a, 'b: 'a, 'c> {
fn from<T>(_: &'a Pubkey, _: &'c mut Iter<'a, AccountInfo<'b>>, _: &'a T) -> Result<Self> fn from<T>(_: &'a Pubkey, _: &'c mut Iter<'a, AccountInfo<'b>>, _: &'a T) -> Result<Self>
where where
Self: Sized; Self: Sized;
/// Converts the accounts back to vector form to facilitate CPI calls.
fn to_cpi_metas(&self) -> Vec<AccountMeta>;
} }

View File

@ -155,6 +155,10 @@ macro_rules! data_wrapper {
) -> solitaire::Result<()> { ) -> solitaire::Result<()> {
Data::<'_, $embed, { $state }>::persist(self, program_id) Data::<'_, $embed, { $state }>::persist(self, program_id)
} }
fn to_partial_cpi_meta(&self) -> Vec<solana_program::instruction::AccountMeta> {
self.0.to_partial_cpi_meta()
}
} }
impl<'b> solitaire::processors::seeded::Owned for $name<'b> { impl<'b> solitaire::processors::seeded::Owned for $name<'b> {

View File

@ -6,6 +6,7 @@
use borsh::BorshDeserialize; use borsh::BorshDeserialize;
use solana_program::{ use solana_program::{
instruction::AccountMeta,
pubkey::Pubkey, pubkey::Pubkey,
system_program, system_program,
sysvar::{ sysvar::{
@ -38,6 +39,9 @@ pub trait Peel<'a, 'b: 'a, 'c> {
fn deps() -> Vec<Pubkey>; fn deps() -> Vec<Pubkey>;
fn persist(&self, program_id: &Pubkey) -> Result<()>; fn persist(&self, program_id: &Pubkey) -> Result<()>;
/// Special method for turning the type back into AccountMeta for CPI use cases.
fn to_partial_cpi_meta(&self) -> Vec<AccountMeta>;
} }
/// Peel a Derived Key /// Peel a Derived Key
@ -60,6 +64,10 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>, const Seed: &'static str> Peel<'a, 'b,
fn persist(&self, program_id: &Pubkey) -> Result<()> { fn persist(&self, program_id: &Pubkey) -> Result<()> {
T::persist(self, program_id) T::persist(self, program_id)
} }
fn to_partial_cpi_meta(&self) -> Vec<AccountMeta> {
self.0.to_partial_cpi_meta()
}
} }
/// Peel a Mutable key. /// Peel a Mutable key.
@ -98,6 +106,10 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Signer<T> {
fn persist(&self, program_id: &Pubkey) -> Result<()> { fn persist(&self, program_id: &Pubkey) -> Result<()> {
T::persist(self, program_id) T::persist(self, program_id)
} }
fn to_partial_cpi_meta(&self) -> Vec<AccountMeta> {
self.0.to_partial_cpi_meta()
}
} }
/// Expicitly depend upon the System account. /// Expicitly depend upon the System account.
@ -116,6 +128,10 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for System<T> {
fn persist(&self, program_id: &Pubkey) -> Result<()> { fn persist(&self, program_id: &Pubkey) -> Result<()> {
T::persist(self, program_id) T::persist(self, program_id)
} }
fn to_partial_cpi_meta(&self) -> Vec<AccountMeta> {
self.0.to_partial_cpi_meta()
}
} }
/// Peel a Sysvar /// Peel a Sysvar
@ -140,6 +156,10 @@ where
fn persist(&self, _program_id: &Pubkey) -> Result<()> { fn persist(&self, _program_id: &Pubkey) -> Result<()> {
Ok(()) Ok(())
} }
fn to_partial_cpi_meta(&self) -> Vec<AccountMeta> {
self.to_partial_cpi_meta()
}
} }
/// 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
@ -158,6 +178,16 @@ impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for Info<'b> {
fn persist(&self, _program_id: &Pubkey) -> Result<()> { fn persist(&self, _program_id: &Pubkey) -> Result<()> {
Ok(()) Ok(())
} }
fn to_partial_cpi_meta(&self) -> Vec<AccountMeta> {
let meta = if self.is_writable {
AccountMeta::new(self.key.clone(), self.is_signer)
} else {
AccountMeta::new_readonly(self.key.clone(), self.is_signer)
};
vec![meta]
}
} }
/// 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
@ -237,4 +267,8 @@ impl<
Ok(()) Ok(())
} }
fn to_partial_cpi_meta(&self) -> Vec<AccountMeta> {
self.0.to_partial_cpi_meta()
}
} }

View File

@ -27,6 +27,7 @@ use syn::{
parse_quote, parse_quote,
spanned::Spanned, spanned::Spanned,
Data, Data,
DataStruct,
DeriveInput, DeriveInput,
Fields, Fields,
GenericParam, GenericParam,
@ -106,7 +107,8 @@ pub fn derive_from_accounts(input: TokenStream) -> TokenStream {
} }
let (combined_impl_g, _, _) = combined_generics.split_for_impl(); let (combined_impl_g, _, _) = combined_generics.split_for_impl();
let from_method = generate_fields(&name, &input.data); let from_method = generate_from(&name, &input.data);
let to_cpi_metas_method = generate_to_cpi_metas(&name, &input.data);
let persist_method = generate_persist(&name, &input.data); let persist_method = generate_persist(&name, &input.data);
let deps_method = generate_deps_fields(&name, &input.data); let deps_method = generate_deps_fields(&name, &input.data);
let expanded = quote! { let expanded = quote! {
@ -115,8 +117,12 @@ pub fn derive_from_accounts(input: TokenStream) -> TokenStream {
fn from<DataType>(pid: &'a solana_program::pubkey::Pubkey, iter: &'c mut std::slice::Iter<'a, solana_program::account_info::AccountInfo<'b>>, data: &'a DataType) -> solitaire::Result<Self> { fn from<DataType>(pid: &'a solana_program::pubkey::Pubkey, iter: &'c mut std::slice::Iter<'a, solana_program::account_info::AccountInfo<'b>>, data: &'a DataType) -> solitaire::Result<Self> {
#from_method #from_method
} }
fn to_cpi_metas(&self) -> Vec<solana_program::instruction::AccountMeta> {
#to_cpi_metas_method
}
} }
/// Macro generated implementation of Peel by Solitaire.
impl #combined_impl_g solitaire::Peel<'a, 'b, 'c> for #name #type_g { impl #combined_impl_g solitaire::Peel<'a, 'b, 'c> for #name #type_g {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> solitaire::Result<Self> where Self: Sized { fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> solitaire::Result<Self> where Self: Sized {
let v: #name #type_g = FromAccounts::from(ctx.this, ctx.iter, ctx.data)?; let v: #name #type_g = FromAccounts::from(ctx.this, ctx.iter, ctx.data)?;
@ -134,6 +140,10 @@ pub fn derive_from_accounts(input: TokenStream) -> TokenStream {
fn persist(&self, program_id: &solana_program::pubkey::Pubkey) -> solitaire::Result<()> { fn persist(&self, program_id: &solana_program::pubkey::Pubkey) -> solitaire::Result<()> {
solitaire::Persist::persist(self, program_id) solitaire::Persist::persist(self, program_id)
} }
fn to_partial_cpi_meta(&self) -> Vec<solana_program::instruction::AccountMeta> {
self.to_cpi_metas()
}
} }
/// Macro generated implementation of Persist by Solitaire. /// Macro generated implementation of Persist by Solitaire.
@ -149,7 +159,7 @@ pub fn derive_from_accounts(input: TokenStream) -> TokenStream {
} }
/// This function does the heavy lifting of generating the field parsers. /// This function does the heavy lifting of generating the field parsers.
fn generate_fields(name: &syn::Ident, data: &Data) -> TokenStream2 { fn generate_from(name: &syn::Ident, data: &Data) -> TokenStream2 {
match *data { match *data {
// We only care about structures. // We only care about structures.
Data::Struct(ref data) => { Data::Struct(ref data) => {
@ -204,6 +214,37 @@ fn generate_fields(name: &syn::Ident, data: &Data) -> TokenStream2 {
} }
} }
fn generate_to_cpi_metas(name: &syn::Ident, data: &Data) -> TokenStream2 {
if let Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) = data
{
let v_appends = fields.named.iter().map(|f| {
// Field name, to assign to.
let field_name = &f.ident;
let ty = &f.ty;
quote! {
v.append(&mut self.#field_name.to_partial_cpi_meta());
}
});
let names = fields.named.iter().map(|f| {
let name = &f.ident;
quote!(#name)
});
quote! {
let mut v = Vec::new();
#(#v_appends;)*
v
}
} else {
unimplemented!()
}
}
/// This function does the heavy lifting of generating the field parsers. /// This function does the heavy lifting of generating the field parsers.
fn generate_deps_fields(name: &syn::Ident, data: &Data) -> TokenStream2 { fn generate_deps_fields(name: &syn::Ident, data: &Data) -> TokenStream2 {
match *data { match *data {
@ -252,9 +293,6 @@ fn generate_persist(name: &syn::Ident, data: &Data) -> TokenStream2 {
match data.fields { match data.fields {
// For now, we only care about struct { a: T } forms, not struct(T); // For now, we only care about struct { a: T } forms, not struct(T);
Fields::Named(ref fields) => { Fields::Named(ref fields) => {
// For each field, generate an expression that parses an account info field
// from the Solana accounts list. This relies on Verify::verify to do most of
// the work.
let recurse = fields.named.iter().map(|f| { let recurse = fields.named.iter().map(|f| {
// Field name, to assign to. // Field name, to assign to.
let name = &f.ident; let name = &f.ident;