Add ToAccounts and Wrap skeleton, derive macros

Change-Id: I2ccb742cc8a49db7b170aa3a284bce003193a903
This commit is contained in:
Stan Drozd 2021-06-01 14:57:28 +02:00
parent e422bd3d8c
commit d86dc75734
4 changed files with 155 additions and 24 deletions

View File

@ -5,7 +5,7 @@ type Payer<'a> = Signer<Info<'a>>;
type GuardianSet<'a> = Derive<Data<'a, GuardianSetData, Uninitialized>, "GuardianSet">;
type Bridge<'a> = Derive<Data<'a, BridgeData, Uninitialized>, "Bridge">;
#[derive(FromAccounts)]
#[derive(FromAccounts, ToAccounts)]
pub struct Initialize<'b> {
pub payer: Payer<'b>,
pub guardian_set: GuardianSet<'b>,
@ -13,9 +13,10 @@ pub struct Initialize<'b> {
pub transfer: Transfer<'b>,
}
impl<'b> InstructionContext<'b> for Initialize<'b> {}
impl<'b> InstructionContext<'b> for Initialize<'b> {
}
#[derive(FromAccounts)]
#[derive(FromAccounts, ToAccounts)]
pub struct Transfer<'b> {
pub mint: Data<'b, Test, Initialized>,
pub from: Data<'b, Test, Initialized>,
@ -37,7 +38,11 @@ pub struct Test {
mint: Pubkey,
}
pub fn initialize(ctx: &ExecutionContext, accs: &mut Initialize, config: BridgeConfig) -> Result<()> {
pub fn initialize(
ctx: &ExecutionContext,
accs: &mut Initialize,
config: BridgeConfig,
) -> Result<()> {
// Initialize the Guardian Set for the first time.
let index = Index::new(0);

View File

@ -15,9 +15,9 @@ pub use solana_program::{
entrypoint,
entrypoint::ProgramResult,
pubkey::Pubkey,
system_instruction,
system_program,
sysvar::{self, SysvarId},
system_instruction,
};
// Later on we will define types that don't actually contain data, PhantomData will help us.
@ -29,11 +29,14 @@ pub use std::ops::{Deref, DerefMut};
// Borsh is Solana's goto serialization target, so we'll need this if we want to do any
// serialization on the users behalf.
pub use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::account_info::next_account_info;
use solana_program::{
account_info::next_account_info,
instruction::AccountMeta,
program::invoke_signed,
program_error::ProgramError,
rent::Rent,
};
use std::slice::Iter;
use solana_program::program::invoke_signed;
use solana_program::rent::Rent;
use solana_program::program_error::ProgramError;
/// There are several places in Solitaire that might fail, we want descriptive errors.
#[derive(Debug)]
@ -154,13 +157,13 @@ impl<'a, 'b: 'a, 'c, T> Context<'a, 'b, 'c, T> {
program: &'a Pubkey,
iter: &'c mut Iter<'a, AccountInfo<'b>>,
data: &'a T,
deps: &'c mut Vec<Pubkey>,
deps: &'c mut Vec<Pubkey>,
) -> Self {
Context {
this: program,
iter,
data,
deps ,
deps,
info: None,
}
}
@ -178,7 +181,7 @@ impl<'a, 'b: 'a, 'c, T> Context<'a, 'b, 'c, T> {
}
impl<'r, T, const IsInitialized: bool, const Lazy: bool> Deref
for Data<'r, T, IsInitialized, Lazy>
for Data<'r, T, IsInitialized, Lazy>
{
type Target = T;
fn deref(&self) -> &Self::Target {
@ -187,7 +190,7 @@ for Data<'r, T, IsInitialized, Lazy>
}
impl<'r, T, const IsInitialized: bool, const Lazy: bool> DerefMut
for Data<'r, T, IsInitialized, Lazy>
for Data<'r, T, IsInitialized, Lazy>
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.1
@ -263,23 +266,47 @@ impl CreationLamports {
pub fn amount(self, size: usize) -> u64 {
match self {
CreationLamports::Exempt => Rent::default().minimum_balance(size),
CreationLamports::Amount(v) => v
CreationLamports::Amount(v) => v,
}
}
}
impl<const Seed: &'static str> Derive<AccountInfo<'_>, Seed> {
pub fn create(&self, ctx: &ExecutionContext, payer: &Pubkey, lamports: CreationLamports, space: usize, owner: &Pubkey) -> Result<()> {
let ix = system_instruction::create_account(payer, self.0.key, lamports.amount(space), space as u64, owner);
pub fn create(
&self,
ctx: &ExecutionContext,
payer: &Pubkey,
lamports: CreationLamports,
space: usize,
owner: &Pubkey,
) -> Result<()> {
let ix = system_instruction::create_account(
payer,
self.0.key,
lamports.amount(space),
space as u64,
owner,
);
invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes()]])
}
}
impl<const Seed: &'static str, T: BorshSerialize> Derive<Data<'_, T, Uninitialized>, Seed> {
pub fn create(&self, ctx: &ExecutionContext, payer: &Pubkey, lamports: CreationLamports) -> Result<()> {
pub fn create(
&self,
ctx: &ExecutionContext,
payer: &Pubkey,
lamports: CreationLamports,
) -> Result<()> {
// Get serialized struct size
let size = self.0.try_to_vec().unwrap().len();
let ix = system_instruction::create_account(payer, self.0.0.key, lamports.amount(size), size as u64, ctx.program_id);
let ix = system_instruction::create_account(
payer,
self.0 .0.key,
lamports.amount(size),
size as u64,
ctx.program_id,
);
invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes()]])
}
}
@ -288,13 +315,13 @@ impl<const Seed: &'static str, T: BorshSerialize> Derive<Data<'_, T, Uninitializ
/// layer of our constraints should check.
pub trait Peel<'a, 'b: 'a, 'c> {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self>
where
Self: Sized;
where
Self: Sized;
}
/// Peel a Derived Key
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>, const Seed: &'static str> Peel<'a, 'b, 'c>
for Derive<T, Seed>
for Derive<T, Seed>
{
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
// Attempt to Derive Seed
@ -347,7 +374,7 @@ impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for Info<'b> {
/// This is our structural recursion base case, the trait system will stop
/// generating new nested calls here.
impl<'a, 'b: 'a, 'c, T: BorshDeserialize, const IsInitialized: bool, const Lazy: bool>
Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized, Lazy>
Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized, Lazy>
{
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
// If we're initializing the type, we should emit system/rent as deps.
@ -363,6 +390,38 @@ Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized, Lazy>
}
}
pub trait Wrap {
fn wrap(&self) -> Vec<AccountMeta>;
}
impl<T> Wrap for T
where
T: ToAccounts,
{
fn wrap(&self) -> Vec<AccountMeta> {
self.to()
}
}
impl<T> Wrap for Signer<T> {
fn wrap(&self) -> Vec<AccountMeta> {
todo!()
}
}
impl<T, const Seed: &'static str> Wrap for Derive<T, Seed> {
fn wrap(&self) -> Vec<AccountMeta> {
todo!()
}
}
impl<'a, T: BorshSerialize, const IsInitialized: bool, const Lazy: bool> Wrap
for Data<'a, T, IsInitialized, Lazy>
{
fn wrap(&self) -> Vec<AccountMeta> {
todo!()
}
}
pub trait InstructionContext<'a> {
fn verify(&self) -> Result<()> {
Ok(())
@ -381,8 +440,12 @@ pub trait FromAccounts<'a, 'b: 'a, 'c> {
_: &'c mut Iter<'a, AccountInfo<'b>>,
_: &'a T,
) -> Result<(Self, Vec<Pubkey>)>
where
Self: Sized;
where
Self: Sized;
}
pub trait ToAccounts {
fn to(&self) -> Vec<AccountMeta>;
}
/// This is our main codegen macro. It takes as input a list of enum-like variants mapping field

View File

@ -1,5 +1,9 @@
#![allow(warnings)]
mod to_accounts;
use to_accounts::*;
use solana_program::{
account_info::AccountInfo,
entrypoint,
@ -22,6 +26,24 @@ use syn::{
Index,
};
#[proc_macro_derive(ToAccounts)]
pub fn derive_to_accounts(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let to_method_body = generate_to_method(&name, &input.data);
let expanded = quote! {
/// Macro-generated implementation of ToAccounts by Solitaire.
impl<'a> solitaire::ToAccounts for #name<'a> {
fn to(&self) -> Vec<solana_program::instruction::AccountMeta> {
#to_method_body
}
}
};
TokenStream::from(expanded)
}
/// Generate a FromAccounts implementation for a product of accounts. Each field is constructed by
/// a call to the Verify::verify instance of its type.
#[proc_macro_derive(FromAccounts)]

View File

@ -0,0 +1,41 @@
//! Derive macro logic for ToAccounts
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, quote_spanned};
use syn::{
parse_macro_input,
parse_quote,
spanned::Spanned,
Data,
DataStruct,
DeriveInput,
Fields,
GenericParam,
Generics,
Index,
};
pub fn generate_to_method(name: &syn::Ident, data: &Data) -> TokenStream2 {
match *data {
Data::Struct(DataStruct {
fields: Fields::Named(ref fields),
..
}) => {
let expanded_fields = fields.named.iter().map(|field| {
let name = &field.ident;
quote! {
v.append(&mut solitaire::Wrap::wrap(&self.#name))
}
});
quote! {
let mut v = Vec::new();
#(#expanded_fields;)*
v
}
}
_ => unimplemented!(),
}
}