diff --git a/solana/anchor-bridge/programs/bridge/src/api/initialize.rs b/solana/anchor-bridge/programs/bridge/src/api/initialize.rs index f725d9b6..7b7e8158 100644 --- a/solana/anchor-bridge/programs/bridge/src/api/initialize.rs +++ b/solana/anchor-bridge/programs/bridge/src/api/initialize.rs @@ -5,7 +5,7 @@ type Payer<'a> = Signer>; type GuardianSet<'a> = Derive, "GuardianSet">; type Bridge<'a> = Derive, "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); diff --git a/solana/anchor-bridge/programs/solitaire/src/lib.rs b/solana/anchor-bridge/programs/solitaire/src/lib.rs index af222e94..fb639610 100644 --- a/solana/anchor-bridge/programs/solitaire/src/lib.rs +++ b/solana/anchor-bridge/programs/solitaire/src/lib.rs @@ -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, + deps: &'c mut Vec, ) -> 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 Derive, 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 Derive, 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 Derive { fn peel(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result - 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 + for Derive { fn peel(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result { // 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(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result { // 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; +} + +impl Wrap for T +where + T: ToAccounts, +{ + fn wrap(&self) -> Vec { + self.to() + } +} + +impl Wrap for Signer { + fn wrap(&self) -> Vec { + todo!() + } +} +impl Wrap for Derive { + fn wrap(&self) -> Vec { + todo!() + } +} + +impl<'a, T: BorshSerialize, const IsInitialized: bool, const Lazy: bool> Wrap + for Data<'a, T, IsInitialized, Lazy> +{ + fn wrap(&self) -> Vec { + 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)> - where - Self: Sized; + where + Self: Sized; +} + +pub trait ToAccounts { + fn to(&self) -> Vec; } /// This is our main codegen macro. It takes as input a list of enum-like variants mapping field diff --git a/solana/anchor-bridge/rocksalt/src/lib.rs b/solana/anchor-bridge/rocksalt/src/lib.rs index 0a73b59f..29410a19 100644 --- a/solana/anchor-bridge/rocksalt/src/lib.rs +++ b/solana/anchor-bridge/rocksalt/src/lib.rs @@ -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 { + #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)] diff --git a/solana/anchor-bridge/rocksalt/src/to_accounts.rs b/solana/anchor-bridge/rocksalt/src/to_accounts.rs new file mode 100644 index 00000000..4333454a --- /dev/null +++ b/solana/anchor-bridge/rocksalt/src/to_accounts.rs @@ -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!(), + } +}