Add ToAccounts and Wrap skeleton, derive macros
Change-Id: I2ccb742cc8a49db7b170aa3a284bce003193a903
This commit is contained in:
parent
e422bd3d8c
commit
d86dc75734
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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!(),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue