Skip to content

Commit

Permalink
✨ Refactor & Component authority
Browse files Browse the repository at this point in the history
  • Loading branch information
GabrielePicco committed Feb 15, 2024
1 parent 1e984b4 commit f06e92c
Show file tree
Hide file tree
Showing 22 changed files with 345 additions and 107 deletions.
101 changes: 83 additions & 18 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ mod rust_template;

use crate::rust_template::{create_component, create_system};
use anchor_cli::config::{
Config, ConfigOverride, ProgramDeployment, TestValidator, Validator, WithPath,
BootstrapMode, Config, ConfigOverride, ProgramArch, ProgramDeployment, TestValidator,
Validator, WithPath,
};
use anchor_client::Cluster;
use anyhow::{anyhow, Result};
Expand Down Expand Up @@ -54,36 +55,63 @@ pub struct Opts {

pub fn entry(opts: Opts) -> Result<()> {
match opts.command {
BoltCommand::Anchor(command) => {
if let anchor_cli::Command::Init {
BoltCommand::Anchor(command) => match command {
anchor_cli::Command::Init {
name,
javascript,
solidity,
no_git,
jest,
template,
force,
} = command
{
init(
&opts.cfg_override,
name,
javascript,
solidity,
no_git,
jest,
template,
force,
)
} else {
// Delegate to the existing anchor_cli handler
} => init(
&opts.cfg_override,
name,
javascript,
solidity,
no_git,
jest,
template,
force,
),
anchor_cli::Command::Build {
idl,
idl_ts,
verifiable,
program_name,
solana_version,
docker_image,
bootstrap,
cargo_args,
env,
skip_lint,
no_docs,
arch,
} => build(
&opts.cfg_override,
idl,
idl_ts,
verifiable,
skip_lint,
program_name,
solana_version,
docker_image,
bootstrap,
None,
None,
env,
cargo_args,
no_docs,
arch,
),
_ => {
let opts = anchor_cli::Opts {
cfg_override: opts.cfg_override,
command,
};
anchor_cli::entry(opts)
}
}
},
BoltCommand::Component(command) => new_component(&opts.cfg_override, command.name),
BoltCommand::System(command) => new_system(&opts.cfg_override, command.name),
}
Expand Down Expand Up @@ -332,6 +360,43 @@ fn init(
Ok(())
}

#[allow(clippy::too_many_arguments)]
pub fn build(
cfg_override: &ConfigOverride,
idl: Option<String>,
idl_ts: Option<String>,
verifiable: bool,
skip_lint: bool,
program_name: Option<String>,
solana_version: Option<String>,
docker_image: Option<String>,
bootstrap: BootstrapMode,
stdout: Option<File>,
stderr: Option<File>,
env_vars: Vec<String>,
cargo_args: Vec<String>,
no_docs: bool,
arch: ProgramArch,
) -> Result<()> {
anchor_cli::build(
cfg_override,
idl,
idl_ts,
verifiable,
skip_lint,
program_name,
solana_version,
docker_image,
bootstrap,
stdout,
stderr,
env_vars,
cargo_args,
no_docs,
arch,
)
}

// Install node modules
fn install_node_modules(cmd: &str) -> Result<std::process::Output> {
let mut command = std::process::Command::new(if cfg!(target_os = "windows") {
Expand Down
19 changes: 5 additions & 14 deletions cli/src/rust_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ fn create_system_template_simple(name: &str, program_path: &Path) -> Files {
program_path.join("src").join("lib.rs"),
format!(
r#"use bolt_lang::*;
use component_position::Position;
declare_id!("{}");
Expand All @@ -79,21 +80,11 @@ pub mod {} {{
Ok(position)
}}
}}
// Define the Account to parse from the component
#[derive(Accounts)]
pub struct Component<'info> {{
/// CHECK: check that the component is the expected account
pub position: AccountInfo<'info>,
}}
#[system_input]
pub struct Components {{
pub position: Position,
}}
#[component_deserialize]
pub struct Position {{
pub x: i64,
pub y: i64,
pub z: i64,
pub description: String,
}}
"#,
anchor_cli::rust_template::get_or_create_program_id(name),
Expand Down
20 changes: 15 additions & 5 deletions crates/bolt-helpers/attribute/world-apply/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub fn apply_system(attr: TokenStream, item: TokenStream) -> TokenStream {
build_update_context(
ctx.accounts.#component_program_name.clone(),
ctx.accounts.#bolt_component_name.clone(),
ctx.accounts.authority.clone(),
ctx.accounts.instruction_sysvar_account.clone(),
),
res[#index].to_owned()
Expand All @@ -39,14 +40,16 @@ pub fn apply_system(attr: TokenStream, item: TokenStream) -> TokenStream {

quote! {
pub fn #apply_func_name(ctx: Context<#data_struct>, args: Vec<u8>) -> Result<()> {
if !ctx.accounts.authority.is_signer && ctx.accounts.authority.key != &ID {
return Err(WorldError::InvalidAuthority.into());
}
let res = bolt_system::cpi::#execute_func_name(ctx.accounts.build(), args)?.get().to_vec();
#(#updates)*
Ok(())
}
}
});


// Append each generated function to the module's items
if let Some((brace, mut content)) = input.content.take() {
for func in funcs {
Expand All @@ -59,10 +62,17 @@ pub fn apply_system(attr: TokenStream, item: TokenStream) -> TokenStream {
}

let data_def = (2..=max_components).map(|i| {
let data_struct = syn::Ident::new(&format!("ApplySystem{}", i), proc_macro2::Span::call_site());
let data_struct =
syn::Ident::new(&format!("ApplySystem{}", i), proc_macro2::Span::call_site());
let fields = (1..=i).map(|n| {
let component_program_name = syn::Ident::new(&format!("component_program_{}", n), proc_macro2::Span::call_site());
let component_name = syn::Ident::new(&format!("bolt_component_{}", n), proc_macro2::Span::call_site());
let component_program_name = syn::Ident::new(
&format!("component_program_{}", n),
proc_macro2::Span::call_site(),
);
let component_name = syn::Ident::new(
&format!("bolt_component_{}", n),
proc_macro2::Span::call_site(),
);
quote! {
/// CHECK: bolt component program check
pub #component_program_name: UncheckedAccount<'info>,
Expand All @@ -78,7 +88,7 @@ pub fn apply_system(attr: TokenStream, item: TokenStream) -> TokenStream {
pub bolt_system: UncheckedAccount<'info>,
#(#fields)*
/// CHECK: authority check
pub authority: AccountInfo<'info>,
pub authority: UncheckedAccount<'info>,
#[account(address = anchor_lang::solana_program::sysvar::instructions::id())]
/// CHECK: instruction sysvar check
pub instruction_sysvar_account: UncheckedAccount<'info>,
Expand Down
13 changes: 11 additions & 2 deletions crates/bolt-lang/attribute/bolt-program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ fn generate_initialize(component_type: &Type) -> (TokenStream2, TokenStream2) {
0, &ctx.accounts.instruction_sysvar_account.to_account_info()
).unwrap();
if instruction.program_id != World::id() {
panic!("The instruction must be called from the world program");
return Err(BoltError::InvalidCaller.into());
}
ctx.accounts.data.set_inner(<#component_type>::default());
ctx.accounts.data.bolt_metadata.authority = *ctx.accounts.authority.key;
Expand Down Expand Up @@ -148,12 +148,18 @@ fn generate_update(component_type: &Type) -> (TokenStream2, TokenStream2) {
quote! {
#[automatically_derived]
pub fn update(ctx: Context<Update>, data: Vec<u8>) -> Result<()> {
// Check if the instruction is called from the world program
let instruction = anchor_lang::solana_program::sysvar::instructions::get_instruction_relative(
0, &ctx.accounts.instruction_sysvar_account.to_account_info()
).unwrap();
if instruction.program_id != World::id() {
panic!("The instruction must be called from the world program");
return Err(BoltError::InvalidCaller.into());
}
// Check if the authority is authorized to modify the data
if ctx.accounts.bolt_component.bolt_metadata.authority != World::id() && ctx.accounts.bolt_component.bolt_metadata.authority != *ctx.accounts.authority.key {
return Err(BoltError::InvalidAuthority.into());
}

ctx.accounts.bolt_component.set_inner(<#component_type>::try_from_slice(&data)?);
Ok(())
}
Expand All @@ -164,6 +170,9 @@ fn generate_update(component_type: &Type) -> (TokenStream2, TokenStream2) {
pub struct Update<'info> {
#[account(mut)]
pub bolt_component: Account<'info, #component_type>,
#[account()]
/// CHECK: The authority of the component
pub authority: AccountInfo<'info>,
#[account(address = anchor_lang::solana_program::sysvar::instructions::id())]
pub instruction_sysvar_account: UncheckedAccount<'info>,
}
Expand Down
12 changes: 11 additions & 1 deletion crates/bolt-lang/attribute/component-deserialize/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
use bolt_utils::add_bolt_metadata;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Attribute, DeriveInput};
use bolt_utils::add_bolt_metadata;

/// This macro is used to defined a struct as a BOLT component and automatically implements the
/// `ComponentDeserialize` and `AccountDeserialize` traits for the struct.
///
/// #[component]
/// pub struct Position {
/// pub x: i64,
/// pub y: i64,
/// pub z: i64,
/// }
/// ```
#[proc_macro_attribute]
pub fn component_deserialize(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(item as DeriveInput);
Expand Down
6 changes: 4 additions & 2 deletions crates/bolt-lang/attribute/component-id/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{DeriveInput, parse_macro_input};
use syn::{parse_macro_input, DeriveInput};

/// Macro to specify the on-chain ID of a component.
///
/// ```
#[proc_macro_attribute]
pub fn component_id(_attr: TokenStream, item: TokenStream) -> TokenStream {
println!("run test");
let input = parse_macro_input!(item as DeriveInput);
let expanded = quote! {
#input
Expand Down
7 changes: 4 additions & 3 deletions crates/bolt-lang/attribute/component/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use bolt_utils::add_bolt_metadata;
use proc_macro::TokenStream;
use quote::quote;
use syn::{Attribute, DeriveInput, Lit, Meta, NestedMeta, parse_macro_input, parse_quote};
use bolt_utils::{add_bolt_metadata};
use syn::{parse_macro_input, parse_quote, Attribute, DeriveInput, Lit, Meta, NestedMeta};

/// This Component attribute is used to automatically generate the seed and size functions
///
/// The component_id can be used to define the seed used to generate the PDA which stores the component data.
/// The macro also adds the InitSpace and Default derives to the struct.
///
/// #[component]
/// #[component_deserialize]
/// #[derive(Clone)]
/// pub struct Position {
/// pub x: i64,
/// pub y: i64,
Expand Down
23 changes: 10 additions & 13 deletions crates/bolt-lang/attribute/system-input/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro::TokenStream;
use quote::{quote};
use syn::{parse_macro_input, ItemStruct, Fields, Meta, MetaNameValue, Lit};
use quote::quote;
use syn::{parse_macro_input, Fields, ItemStruct, Lit, Meta, MetaNameValue};

/// This macro attribute is used to define a BOLT system input.
///
Expand All @@ -9,16 +9,11 @@ use syn::{parse_macro_input, ItemStruct, Fields, Meta, MetaNameValue, Lit};
///
/// # Example
/// ```ignore
/// pub struct Component {
/// pub position: Position,
/// }
///
/// Will be transfomed into:
///#[derive(Accounts)]
///pub struct Component<'info> {
/// #[account()]
/// pub position: Account<'info, Position>,
///#[system_input]
///pub struct Components {
/// pub position: Position,
///}
///
/// ```
#[proc_macro_attribute]
pub fn system_input(_attr: TokenStream, item: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -91,7 +86,9 @@ pub fn system_input(_attr: TokenStream, item: TokenStream) -> TokenStream {
}
});

let tuple_elements = (0..try_to_vec_fields.len()).map(|_| quote! {Vec<u8>}).collect::<Vec<_>>();
let tuple_elements = (0..try_to_vec_fields.len())
.map(|_| quote! {Vec<u8>})
.collect::<Vec<_>>();
let generated_tuple_type = match tuple_elements.len() {
0 => panic!("system_input macro only supports structs with named fields"),
1 => quote! { (Vec<u8>,) },
Expand All @@ -115,4 +112,4 @@ pub fn system_input(_attr: TokenStream, item: TokenStream) -> TokenStream {
};

TokenStream::from(output)
}
}
Loading

0 comments on commit f06e92c

Please sign in to comment.