Skip to content

Commit

Permalink
Extract configuration into JSON-file #54 (#55)
Browse files Browse the repository at this point in the history
* Extract configuration into JSON-file #54

* Remove voters-dir from wallet #54

* Add url into config, add payer to argumenta, use default solana payer if missing in args and config #54

* Use RPC URL from solana cli config by default #54

Co-authored-by: Semen Medvedev <sm@neonlabs.org>
  • Loading branch information
s-medvedev and Semen Medvedev authored Jun 29, 2022
1 parent f13d7e7 commit 13cdf8e
Show file tree
Hide file tree
Showing 11 changed files with 267 additions and 35 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions launch-script/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ goblin = "0.4.2"
solana-sdk = "1.10.16"
solana-client = "1.10.16"
solana-clap-utils = "1.10.16"
solana-cli-config = "1.10.16"
borsh = "0.9.1"
log = "0.4.11"
clap = "2.33.3"
thiserror = "1.0"
chrono = "0.4.0"
chronoutil = "0.2.3"
base64 = "0.13.0"
serde = "1.0"
serde_json = "1.0"
spl-token = { version = "3.3", path = "../solana-program-library/token/program", features = [ "no-entrypoint" ] }
spl-associated-token-account = { path = "../solana-program-library/associated-token-account/program", features = [ "no-entrypoint" ] }
spl-governance = { version = "2.2", path = "../solana-program-library/governance/program" }
Expand Down
17 changes: 17 additions & 0 deletions launch-script/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,23 @@ pub struct Configuration<'a> {
}

impl<'a> Configuration<'a> {
pub fn create_from_config(
wallet: &'a Wallet,
client: &'a Client,
send_trx: bool,
verbose: bool,
config: &ConfigFile,
) -> Self {
Self::create(
wallet,
client,
send_trx,
verbose,
config.testing,
Some(config.start_time),
)
}

pub fn create(
wallet: &'a Wallet,
client: &'a Client,
Expand Down
105 changes: 105 additions & 0 deletions launch-script/src/config_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use crate::prelude::*;
use {
serde::Deserialize,
};

#[derive(Debug,Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct ConfigFile {
#[serde(default)]
pub payer: String,

#[serde(default)]
pub url: String,

pub creator: String,
pub community_mint: String,

pub governance_program: String,
pub fixed_weight_addin: String,
pub vesting_addin: String,
pub neon_evm_program: String,
pub maintenance_program: String,

pub testing: bool,

#[serde(with = "serde_datetime")]
pub start_time: NaiveDateTime,
}

//mod serde_pubkey {
// use serde::Deserialize;
// use std::str::FromStr;
// use solana_sdk::pubkey::Pubkey;
//
// pub fn deserialize<'de, D>(deserializer: D) -> Result<Pubkey, D::Error>
// where D: serde::Deserializer<'de>
// {
// let s = String::deserialize(deserializer)?;
// Pubkey::from_str(&s).map_err(serde::de::Error::custom)
// }
//}

mod serde_datetime {
use serde::Deserialize;
use chrono::NaiveDateTime;

pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDateTime, D::Error>
where D: serde::Deserializer<'de>
{
let s = String::deserialize(deserializer)?;
NaiveDateTime::parse_from_str(&s, "%FT%T").map_err(serde::de::Error::custom)
}
}

//mod serde_keypair {
// use serde::Deserialize;
// use solana_sdk::signer::keypair::{Keypair, read_keypair_file};
//
// pub fn deserialize<'de, D>(deserializer: D) -> Result<Keypair, D::Error>
// where D: serde::Deserializer<'de>
// {
// let s = String::deserialize(deserializer)?;
// read_keypair_file(s).map_err(serde::de::Error::custom)
// }
//}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_deserialize() {
let data = r#"{
"payer": "../artifacts/payer.keypair",
"creator": "5mAkTXJMyFxvEeUNpfiwAqUfD3qRSRmdh6j6YgXDwqrm",
"community-mint": "../artifacts/community-mint.keypair",
"governance-program": "../artifacts/spl-governance.keypair",
"fixed-weight-addin": "../artifacts/addin-fixed-wights.keypair",
"vesting-addin": "../artifacts/addin-vesting.keypair",
"neon-evm-program": "../artifacts/neon-evm.keypair",
"maintenance-program": "../artifacts/maintenance.keypair",
"start-date": "2022-06-21T00:00:00",
"testing": true
}"#;

let config_file: ConfigFile = serde_json::from_str(data).unwrap();
println!("ConfigFile: {:#?}", config_file);

let data = r#"{
"creator": "5mAkTXJMyFxvEeUNpfiwAqUfD3qRSRmdh6j6YgXDwqrm",
"community-mint": "EjLGfD8mpxKLwGDi8AiTisAbGtWWM2L3htkJ6MpvS8Hk",
"governance-program": "82pQHEmBbW6CQS8GzLP3WE2pCgMUPSW2XzpuSih3aFDk",
"fixed-weight-addin": "56cFVhzLFKuvRXQW68ACLpcbJonZeUBNDdLdZoo5fGnB",
"vesting-addin": "5tgpCGfXYaZhKWJsrNR4zyp4o4n3wSQ81i5MzPqKEeAK",
"neon-evm-program": "DCPSnJHB38e7vNK6o3AVLswJGRaP87iiNx2zvvapiKBz",
"maintenance-program": "7aPH9mBAvUtJDGV2L1KyvpR5nKF7man5DZzBPaxmisg5",
"start-date": "2022-06-21T00:00:00",
"testing": true
}"#;

let config_file: ConfigFile = serde_json::from_str(data).unwrap();
println!("ConfigFile2: {:#?}", config_file);
}
}

14 changes: 13 additions & 1 deletion launch-script/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use log::error;

use solana_sdk::pubkey::{Pubkey, PubkeyError};
use solana_sdk::pubkey::{Pubkey, ParsePubkeyError, PubkeyError};
//use solana_sdk::signer::SignerError as SolanaSignerError;
use solana_sdk::decode_error::DecodeError;
use solana_sdk::program_option::COption;
Expand Down Expand Up @@ -82,6 +82,9 @@ pub enum StateError {
#[error("Unknown MultiSig governed {0:?} {1:?}")]
UnknownMultiSigGoverned(String, Pubkey),

#[error("ConfigError {0:?}")]
ConfigError(String),

#[error("Invalid voter list")]
InvalidVoterList,
}
Expand Down Expand Up @@ -113,6 +116,9 @@ pub enum ScriptError {
#[error("Pubkey error: {0:?}")]
Pubkey(PubkeyError),

#[error("Parse Pubkey error: {0:?}")]
ParsePubkey(ParsePubkeyError),

#[error("Missing signer keypair")]
MissingSignerKeypair,

Expand Down Expand Up @@ -248,6 +254,12 @@ impl From<PubkeyError> for ScriptError {
}
}

impl From<ParsePubkeyError> for ScriptError {
fn from(e: ParsePubkeyError) -> ScriptError {
ScriptError::ParsePubkey(e)
}
}

impl From<SolanaProgramError> for ScriptError {
fn from(e: SolanaProgramError) -> ScriptError {
ScriptError::Program(e)
Expand Down
1 change: 1 addition & 0 deletions launch-script/src/lockup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ impl Lockup {
#[cfg(test)]
mod test {
use super::*;
use chrono::{TimeZone, Utc};

#[test]
fn test_date() {
Expand Down
72 changes: 54 additions & 18 deletions launch-script/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod wallet;
mod helpers;
mod clap_utils;
mod config;
mod config_file;
mod env;
mod lockup;
mod msig;
Expand All @@ -15,6 +16,7 @@ pub mod prelude {
pub use crate::{
clap_utils::is_valid_pubkey_or_none,
config::Configuration,
config_file::ConfigFile,
env::prelude::*,
errors::{ScriptError, StateError},
helpers::{ProposalTransactionInserter, TransactionCollector, TransactionExecutor},
Expand Down Expand Up @@ -47,6 +49,10 @@ pub mod prelude {
pub use std::path::Path;
}
use prelude::*;
use solana_cli_config::{
Config as SolanaCliConfig,
CONFIG_FILE as SOLANA_CLI_CONFIG_FILE,
};

pub const TOKEN_MULT: u64 = u64::pow(10, 9);
pub const REALM_NAME: &str = "NEON";
Expand Down Expand Up @@ -96,26 +102,27 @@ fn main() {
.help("Send transactions to blockchain")
)
.arg(
Arg::with_name("testing")
.long("testing")
.takes_value(false)
.help("Configure testing environment")
Arg::with_name("config")
.long("config")
.short("c")
.takes_value(true)
.required(true)
.help("Configuration file")
)
.arg(
Arg::with_name("url")
.long("url")
.short("u")
.takes_value(true)
.global(true)
.default_value("http://localhost:8899")
.help("Url to solana cluster")
.help("Url to solana cluster [overrides 'url' value in config file, default: solana cli RPC URL]")
)
.arg(
Arg::with_name("artifacts")
.long("artifacts")
.default_value("artifacts")
Arg::with_name("payer")
.long("payer")
.takes_value(true)
.help("Directory with keypair- or pubkey-files")
.global(true)
.help("Path to payer keypair [overrides 'payer' value in config file, default: solana cli keypair]")
)

.subcommand(SubCommand::with_name("environment")
Expand Down Expand Up @@ -291,6 +298,13 @@ fn main() {
)
.subcommand(SubCommand::with_name("approve")
.about("Approve proposal")
.arg(
Arg::with_name("voters")
.long("voters")
.required(true)
.takes_value(true)
.help("Path to directory with voters keypairs")
)
)
.subcommand(SubCommand::with_name("finalize-vote")
.about("Finalize vote for proposal")
Expand All @@ -300,17 +314,38 @@ fn main() {
)
).get_matches();

let wallet = Wallet::new(Path::new(matches.value_of("artifacts").unwrap())).unwrap();
let config_file = std::fs::File::open(matches.value_of("config").unwrap())
.expect("config file should exists");
let config = {
let mut config: ConfigFile = serde_json::from_reader(config_file)
.expect("file should be proper JSON");
let solana_config = SolanaCliConfig::load(SOLANA_CLI_CONFIG_FILE.as_ref().unwrap())
.expect("Solana cli config file");

if let Some(payer) = matches.value_of("payer") {
config.payer = payer.to_string();
} else if config.payer.is_empty() {
config.payer = solana_config.keypair_path;
}

if let Some(url) = matches.value_of("url") {
config.url = url.to_string();
} else if config.url.is_empty() {
config.url = solana_config.json_rpc_url;
}

config
};

println!("ConfigFile: {:#?}", config);
let wallet = Wallet::new_from_config(&config).expect("invalid wallet configuration");
wallet.display();

let url = matches.value_of("url").unwrap();
let client = Client::new(url, &wallet.payer_keypair);
let client = Client::new(&config.url, &wallet.payer_keypair);

let send_trx: bool = matches.is_present("send_trx");
let verbose: bool = matches.is_present("verbose");
let testing: bool = matches.is_present("testing");
// TODO: parse `start_time`
let cfg = Configuration::create(&wallet, &client, send_trx, verbose, testing, None);
let cfg = Configuration::create_from_config(&wallet, &client, send_trx, verbose, &config);

match matches.subcommand() {
("environment", Some(arg_matches)) => {
Expand Down Expand Up @@ -373,7 +408,7 @@ fn main() {
&cfg,
)
.unwrap(),
(cmd, _) if ["sign-off", "approve", "finalize-vote", "execute"].contains(&cmd) => {
(cmd, Some(cmd_matches)) if ["sign-off", "approve", "finalize-vote", "execute"].contains(&cmd) => {
let proposal = match proposal_info {
ProposalInfo::Last => {
let proposal_index = governance.get_proposals_count().unwrap() - 1;
Expand All @@ -400,7 +435,8 @@ fn main() {
.unwrap()
}
"approve" => {
approve_proposal(&wallet, &client, &proposal, verbose).unwrap()
let voters_dir: String = value_of(cmd_matches, "voters").unwrap();
approve_proposal(&wallet, &client, &proposal, verbose, &voters_dir).unwrap()
}
"finalize-vote" => {
finalize_vote_proposal(&wallet, &client, &proposal, verbose).unwrap()
Expand Down
21 changes: 19 additions & 2 deletions launch-script/src/proposals/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub mod prelude {
pub use super::ProposalInfo;
}

use solana_sdk::signer::keypair::read_keypair_file;

use crate::prelude::*;
use proposal_delegate_vote::setup_proposal_delegate_vote;
use proposal_tge::setup_proposal_tge;
Expand Down Expand Up @@ -247,17 +249,32 @@ pub fn sign_off_proposal(
}

pub fn approve_proposal(
wallet: &Wallet,
_wallet: &Wallet,
client: &Client,
proposal: &Proposal,
_verbose: bool,
voters_dir: &str,
) -> Result<(), ScriptError> {
use spl_governance::state::vote_record::get_vote_record_address;
let proposal_data = proposal
.get_data()?
.ok_or(StateError::InvalidProposalIndex)?;

for voter in wallet.voter_keypairs.iter() {
let voter_keypairs = {
let mut voter_keypairs = vec!();
for file in Path::new(voters_dir).read_dir()
.map_err(|err| StateError::ConfigError(format!("'{}' should be a directory: {}", voters_dir, err)))?
{
let path = file?.path();
match read_keypair_file(path.clone()) {
Ok(keypair) => voter_keypairs.push(keypair),
Err(err) => println!("Skip '{}' due to {}", path.display(), err),
};
}
voter_keypairs
};

for voter in voter_keypairs.iter() {
let token_owner = proposal
.governance
.realm
Expand Down
Loading

0 comments on commit 13cdf8e

Please sign in to comment.