Skip to content

Commit 529927b

Browse files
feat: Add authority approval/removal for world instances (#82)
1 parent e669ded commit 529927b

File tree

17 files changed

+2188
-70
lines changed

17 files changed

+2188
-70
lines changed

.github/workflows/publish-bolt-crates.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ jobs:
7070
- name: Cache rust
7171
uses: Swatinem/rust-cache@v2
7272
- name: Run fmt
73-
run: cargo fmt -- --check
73+
run: cargo fmt -- --check --verbose
7474
- name: Run clippy
7575
run: cargo clippy -- --deny=warnings
7676

.github/workflows/publish-bolt-sdk.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ jobs:
7070
- name: Cache rust
7171
uses: Swatinem/rust-cache@v2
7272
- name: Run fmt
73-
run: cargo fmt -- --check
73+
run: cargo fmt -- --check --verbose
7474
- name: Run clippy
7575
run: cargo clippy -- --deny=warnings
7676

.github/workflows/run-tests.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626

2727
- uses: actions/setup-node@v4
2828
with:
29-
node-version: 20
29+
node-version: 21
3030

3131
- name: install essentials
3232
run: |
@@ -71,8 +71,8 @@ jobs:
7171
uses: Swatinem/rust-cache@v2
7272
- name: Check Rust version
7373
run: rustc --version
74-
# - name: Run fmt
75-
# run: cargo fmt -- --check --verbose
74+
- name: Run fmt
75+
run: cargo fmt -- --check --verbose
7676
- name: Run clippy
7777
run: cargo clippy -- --deny=warnings
7878

Anchor.toml

+13-17
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[toolchain]
22

33
[features]
4-
seeds = true
4+
resolution = true
55
skip-lint = false
66

77
[programs.devnet]
@@ -20,38 +20,34 @@ velocity = "CbHEFbSQdRN4Wnoby9r16umnJ1zWbULBHg4yqzGQonU1"
2020
url = "https://api.apr.dev"
2121

2222
[provider]
23-
cluster = "localnet"
23+
cluster = "Localnet"
2424
wallet = "./tests/fixtures/provider.json"
2525

26+
[workspace]
27+
members = ["programs/bolt-component", "programs/bolt-system", "programs/world", "examples/component-position", "examples/component-velocity", "examples/system-apply-velocity", "examples/system-fly", "examples/system-simple-movement"]
28+
29+
[scripts]
30+
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/bolt.ts"
31+
2632
[test]
2733
startup_wait = 5000
2834
shutdown_wait = 2000
2935
upgradeable = false
3036

31-
[workspace]
32-
members = [
33-
"programs/bolt-component",
34-
"programs/bolt-system",
35-
"programs/world",
36-
"examples/component-position",
37-
"examples/component-velocity",
38-
"examples/system-apply-velocity",
39-
"examples/system-fly",
40-
"examples/system-simple-movement",
41-
]
42-
4337
[[test.genesis]]
4438
address = "DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh"
4539
program = "tests/fixtures/delegation.so"
4640
upgradeable = false
4741

42+
[test.validator]
43+
bind_address = "0.0.0.0"
44+
ledger = ".anchor/test-ledger"
45+
rpc_port = 8899
46+
4847
[[test.validator.account]]
4948
address = "EEmsg7GbxEAw5f9hGfZRmJRJ27HK8KeGDp7ViW9X2mYa"
5049
filename = "tests/fixtures/commit_record.json"
5150

5251
[[test.validator.account]]
5352
address = "7nQvHcfEqtFmY2q6hiQbidu8BCNdqegnEFfH7HkByFn5"
5453
filename = "tests/fixtures/committed_state.json"
55-
56-
[scripts]
57-
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/bolt.ts"

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ anyhow = { workspace = true }
2929
serde_json = { workspace = true }
3030
heck = { workspace = true }
3131
clap = { workspace = true }
32-
syn = { workspace = true, features = ["full", "extra-traits"] }
32+
syn = { workspace = true, features = ["full", "extra-traits"] }
33+
world = { workspace = true }

cli/src/instructions.rs

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
use anchor_cli::config::{Config, ConfigOverride};
2+
use anchor_client::solana_client::rpc_config::RpcSendTransactionConfig;
3+
use anchor_client::solana_sdk::commitment_config::CommitmentConfig;
4+
use anchor_client::solana_sdk::pubkey::Pubkey;
5+
use anchor_client::solana_sdk::signature::{read_keypair_file, Keypair};
6+
use anchor_client::solana_sdk::signer::Signer;
7+
use anchor_client::solana_sdk::system_program;
8+
use anchor_client::Client;
9+
use anyhow::{anyhow, Result};
10+
use std::rc::Rc;
11+
use world::{accounts, instruction, World, ID};
12+
13+
fn setup_client(cfg_override: &ConfigOverride) -> Result<(Client<Rc<Keypair>>, Keypair)> {
14+
let cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
15+
let wallet_path = cfg.provider.wallet.clone();
16+
17+
let payer = read_keypair_file(wallet_path.to_string())
18+
.map_err(|e| anyhow!("Failed to read keypair file: {}", e))?;
19+
20+
let payer_for_client =
21+
Keypair::from_bytes(&payer.to_bytes()).expect("Failed to create Keypair from bytes");
22+
23+
let client = Client::new_with_options(
24+
cfg.provider.cluster.clone(),
25+
Rc::new(payer_for_client),
26+
CommitmentConfig::confirmed(),
27+
);
28+
Ok((client, payer))
29+
}
30+
31+
fn parse_pubkey(input: &str, error_message: &str) -> Result<Pubkey> {
32+
input
33+
.parse::<Pubkey>()
34+
.map_err(|_| anyhow!(error_message.to_string()))
35+
}
36+
37+
pub fn authorize(
38+
cfg_override: &ConfigOverride,
39+
world: String,
40+
new_authority: String,
41+
) -> Result<()> {
42+
let world_pubkey = parse_pubkey(&world, "Invalid world public key")?;
43+
let new_authority_pubkey = parse_pubkey(&new_authority, "Invalid new authority public key")?;
44+
45+
let (client, payer) = setup_client(cfg_override)?;
46+
let program = client.program(ID)?;
47+
48+
let world_account = program.account::<World>(world_pubkey)?;
49+
let world_id = world_account.id;
50+
let signature = program
51+
.request()
52+
.accounts(accounts::AddAuthority {
53+
authority: payer.pubkey(),
54+
new_authority: new_authority_pubkey,
55+
system_program: system_program::ID,
56+
world: world_pubkey,
57+
})
58+
.args(instruction::AddAuthority { world_id })
59+
.signer(&payer)
60+
.send()?;
61+
62+
println!(
63+
"Authority {} added to world {} with signature {}",
64+
new_authority, world, signature
65+
);
66+
67+
Ok(())
68+
}
69+
70+
pub fn deauthorize(
71+
cfg_override: &ConfigOverride,
72+
world: String,
73+
authority_to_delete: String,
74+
) -> Result<()> {
75+
let world_pubkey = parse_pubkey(&world, "Invalid world public key")?;
76+
let authority_to_delete_pubkey =
77+
parse_pubkey(&authority_to_delete, "Invalid authority public key")?;
78+
79+
let (client, payer) = setup_client(cfg_override)?;
80+
let program = client.program(ID)?;
81+
82+
let world_account = program.account::<World>(world_pubkey)?;
83+
let world_id = world_account.id;
84+
let signature = program
85+
.request()
86+
.accounts(accounts::RemoveAuthority {
87+
authority: payer.pubkey(),
88+
authority_to_delete: authority_to_delete_pubkey,
89+
system_program: system_program::ID,
90+
world: world_pubkey,
91+
})
92+
.args(instruction::RemoveAuthority { world_id })
93+
.signer(&payer)
94+
.send_with_spinner_and_config(RpcSendTransactionConfig {
95+
skip_preflight: true,
96+
..RpcSendTransactionConfig::default()
97+
})?;
98+
99+
println!(
100+
"Authority {} removed from world {} with signature {}",
101+
authority_to_delete, world, signature
102+
);
103+
104+
Ok(())
105+
}
106+
107+
pub fn approve_system(
108+
cfg_override: &ConfigOverride,
109+
world: String,
110+
system_to_approve: String,
111+
) -> Result<()> {
112+
let world_pubkey = parse_pubkey(&world, "Invalid world public key")?;
113+
let system_to_approve_pubkey = parse_pubkey(&system_to_approve, "Invalid system public key")?;
114+
115+
let (client, payer) = setup_client(cfg_override)?;
116+
let program = client.program(ID)?;
117+
118+
let signature = program
119+
.request()
120+
.accounts(accounts::ApproveSystem {
121+
authority: payer.pubkey(),
122+
system: system_to_approve_pubkey,
123+
world: world_pubkey,
124+
system_program: system_program::ID,
125+
})
126+
.args(instruction::ApproveSystem {})
127+
.signer(&payer)
128+
.send()?;
129+
130+
println!(
131+
"System {} approved for world {} with signature {}",
132+
system_to_approve, world, signature
133+
);
134+
135+
Ok(())
136+
}
137+
138+
pub fn remove_system(
139+
cfg_override: &ConfigOverride,
140+
world: String,
141+
system_to_remove: String,
142+
) -> Result<()> {
143+
let world_pubkey = parse_pubkey(&world, "Invalid world public key")?;
144+
let system_to_remove_pubkey = parse_pubkey(&system_to_remove, "Invalid system public key")?;
145+
146+
let (client, payer) = setup_client(cfg_override)?;
147+
let program = client.program(ID)?;
148+
149+
let signature = program
150+
.request()
151+
.accounts(accounts::RemoveSystem {
152+
authority: payer.pubkey(),
153+
system: system_to_remove_pubkey,
154+
world: world_pubkey,
155+
system_program: system_program::ID,
156+
})
157+
.args(instruction::RemoveSystem {})
158+
.signer(&payer)
159+
.send()?;
160+
161+
println!(
162+
"System {} removed from world {} with signature {}",
163+
system_to_remove, world, signature
164+
);
165+
166+
Ok(())
167+
}

cli/src/lib.rs

+51-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
mod component;
2+
mod instructions;
23
mod rust_template;
34
mod system;
45
mod templates;
56
mod workspace;
67

78
use crate::component::new_component;
9+
use crate::instructions::{approve_system, authorize, deauthorize, remove_system};
810
use crate::rust_template::{create_component, create_system};
911
use crate::system::new_system;
1012
use anchor_cli::config;
@@ -23,12 +25,9 @@ use std::io::Write;
2325
use std::io::{self, BufRead};
2426
use std::path::{Path, PathBuf};
2527
use std::process::Stdio;
26-
28+
use std::string::ToString;
2729
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
2830
pub const ANCHOR_VERSION: &str = anchor_cli::VERSION;
29-
30-
pub const WORLD_PROGRAM: &str = "WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n";
31-
3231
#[derive(Subcommand)]
3332
pub enum BoltCommand {
3433
#[clap(about = "Create a new component")]
@@ -38,6 +37,14 @@ pub enum BoltCommand {
3837
// Include all existing commands from anchor_cli::Command
3938
#[clap(flatten)]
4039
Anchor(anchor_cli::Command),
40+
#[clap(about = "Add a new authority for a world instance")]
41+
Authorize(AuthorizeCommand),
42+
#[clap(about = "Remove an authority from a world instance")]
43+
Deauthorize(DeauthorizeCommand),
44+
#[clap(about = "Approve a system for a world instance")]
45+
ApproveSystem(ApproveSystemCommand),
46+
#[clap(about = "Remove a system from a world instance")]
47+
RemoveSystem(RemoveSystemCommand),
4148
}
4249

4350
#[derive(Debug, Parser)]
@@ -56,6 +63,30 @@ pub struct SystemCommand {
5663
pub name: String,
5764
}
5865

66+
#[derive(Debug, Parser)]
67+
pub struct AuthorizeCommand {
68+
pub world: String,
69+
pub new_authority: String,
70+
}
71+
72+
#[derive(Debug, Parser)]
73+
pub struct DeauthorizeCommand {
74+
pub world: String,
75+
pub authority_to_remove: String,
76+
}
77+
78+
#[derive(Debug, Parser)]
79+
pub struct ApproveSystemCommand {
80+
pub world: String,
81+
pub system_to_approve: String,
82+
}
83+
84+
#[derive(Debug, Parser)]
85+
pub struct RemoveSystemCommand {
86+
pub world: String,
87+
pub system_to_remove: String,
88+
}
89+
5990
#[derive(Parser)]
6091
#[clap(version = VERSION)]
6192
pub struct Opts {
@@ -134,11 +165,23 @@ pub fn entry(opts: Opts) -> Result<()> {
134165
},
135166
BoltCommand::Component(command) => new_component(&opts.cfg_override, command.name),
136167
BoltCommand::System(command) => new_system(&opts.cfg_override, command.name),
168+
BoltCommand::Authorize(command) => {
169+
authorize(&opts.cfg_override, command.world, command.new_authority)
170+
}
171+
BoltCommand::Deauthorize(command) => deauthorize(
172+
&opts.cfg_override,
173+
command.world,
174+
command.authority_to_remove,
175+
),
176+
BoltCommand::ApproveSystem(command) => {
177+
approve_system(&opts.cfg_override, command.world, command.system_to_approve)
178+
}
179+
BoltCommand::RemoveSystem(command) => {
180+
remove_system(&opts.cfg_override, command.world, command.system_to_remove)
181+
}
137182
}
138183
}
139-
140184
// Bolt Init
141-
142185
#[allow(clippy::too_many_arguments)]
143186
fn init(
144187
cfg_override: &ConfigOverride,
@@ -268,7 +311,7 @@ fn init(
268311
shutdown_wait: 2000,
269312
validator: Some(validator),
270313
genesis: Some(vec![GenesisEntry {
271-
address: WORLD_PROGRAM.to_owned(),
314+
address: world::id().to_string(),
272315
program: "tests/fixtures/world.so".to_owned(),
273316
upgradeable: Some(false),
274317
}]),
@@ -345,7 +388,7 @@ fn init(
345388
.arg("dump")
346389
.arg("-u")
347390
.arg("d")
348-
.arg(WORLD_PROGRAM)
391+
.arg(world::id().to_string())
349392
.arg("tests/fixtures/world.so")
350393
.stdout(Stdio::inherit())
351394
.stderr(Stdio::inherit())

0 commit comments

Comments
 (0)