diff --git a/CHANGELOG.md b/CHANGELOG.md index 94f53cf5..244c1ddd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ incremented for features. ## Features * ts: Allow preloading instructions for state rpc transactions ([cf9c84](https://github.com/project-serum/anchor/commit/cf9c847e4144989b5bc1936149d171e90204777b)). +* cli: Specify programs to embed into local validator genesis via Anchor.toml while testing. ## Fixes diff --git a/cli/src/config.rs b/cli/src/config.rs index 2936bd9b..bc9b85c3 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -8,10 +8,11 @@ use std::path::Path; use std::path::PathBuf; use std::str::FromStr; -#[derive(Default)] +#[derive(Debug, Default)] pub struct Config { pub cluster: Cluster, pub wallet: WalletPath, + pub test: Option, } impl Config { @@ -61,10 +62,11 @@ impl Config { // Pubkey serializes as a byte array so use this type a hack to serialize // into base 58 strings. -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] struct _Config { cluster: String, wallet: String, + test: Option, } impl ToString for Config { @@ -72,6 +74,7 @@ impl ToString for Config { let cfg = _Config { cluster: format!("{}", self.cluster), wallet: self.wallet.to_string(), + test: self.test.clone(), }; toml::to_string(&cfg).expect("Must be well formed") @@ -84,14 +87,27 @@ impl FromStr for Config { fn from_str(s: &str) -> Result { let cfg: _Config = toml::from_str(s) .map_err(|e| anyhow::format_err!("Unable to deserialize config: {}", e.to_string()))?; - Ok(Config { cluster: cfg.cluster.parse()?, wallet: shellexpand::tilde(&cfg.wallet).parse()?, + test: cfg.test, }) } } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Test { + pub genesis: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GenesisEntry { + // Base58 pubkey string. + pub address: String, + // Filepath to the compiled program to embed into the genesis. + pub program: String, +} + // TODO: this should read idl dir instead of parsing source. pub fn read_all_programs() -> Result> { let files = fs::read_dir("programs")?; diff --git a/cli/src/main.rs b/cli/src/main.rs index a7f14449..2ea21883 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -591,7 +591,7 @@ fn test(skip_deploy: bool) -> Result<()> { build(None)?; let flags = match skip_deploy { true => None, - false => Some(genesis_flags()?), + false => Some(genesis_flags(cfg)?), }; Some(start_test_validator(flags)?) } @@ -635,7 +635,7 @@ fn test(skip_deploy: bool) -> Result<()> { // Returns the solana-test-validator flags to embed the workspace programs // in the genesis block. This allows us to run tests without every deploying. -fn genesis_flags() -> Result> { +fn genesis_flags(cfg: &Config) -> Result> { let mut flags = Vec::new(); for mut program in read_all_programs()? { let binary_path = program.binary_path().display().to_string(); @@ -656,6 +656,13 @@ fn genesis_flags() -> Result> { .with_extension("json"); write_idl(&program.idl, OutFile::File(idl_out))?; } + if let Some(test) = cfg.test.as_ref() { + for entry in &test.genesis { + flags.push("--bpf-program".to_string()); + flags.push(entry.address.clone()); + flags.push(entry.program.clone()); + } + } Ok(flags) } diff --git a/examples/misc/Anchor.toml b/examples/misc/Anchor.toml index 6e90c3dd..2b6160ab 100644 --- a/examples/misc/Anchor.toml +++ b/examples/misc/Anchor.toml @@ -1,2 +1,6 @@ cluster = "localnet" wallet = "/home/armaniferrante/.config/solana/id.json" + +[[test.genesis]] +address = "FtMNMKp9DZHKWUyVAsj3Q5QV8ow4P3fUPP7ZrWEQJzKr" +program = "./target/deploy/misc.so" diff --git a/examples/misc/tests/misc.js b/examples/misc/tests/misc.js index 475f4cee..f182e239 100644 --- a/examples/misc/tests/misc.js +++ b/examples/misc/tests/misc.js @@ -1,4 +1,5 @@ -const anchor = require('@project-serum/anchor'); +const anchor = require("@project-serum/anchor"); +const serumCmn = require("@project-serum/common"); const assert = require("assert"); describe("misc", () => { @@ -24,4 +25,12 @@ describe("misc", () => { assert.ok(dataAccount.udata.eq(new anchor.BN(1234))); assert.ok(dataAccount.idata.eq(new anchor.BN(22))); }); + + it("Can embed programs into genesis from the Anchor.toml", async () => { + const pid = new anchor.web3.PublicKey( + "FtMNMKp9DZHKWUyVAsj3Q5QV8ow4P3fUPP7ZrWEQJzKr" + ); + let accInfo = await anchor.getProvider().connection.getAccountInfo(pid); + assert.ok(accInfo.executable); + }); });