Skip to content

Commit

Permalink
feat: mnemonics allowed, refactor network config (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
hopeyen authored and hopeyen committed Mar 21, 2023
1 parent b312314 commit b7b22df
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 149 deletions.
10 changes: 6 additions & 4 deletions examples/ping-pong/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use dotenv::dotenv;

use graphcast_sdk::{
config::{Config, NetworkName},
config::Config,
graphcast_agent::{
message_typing::GraphcastMessage, waku_handling::WakuHandlingError, GraphcastAgent,
},
graphql::client_graph_node::{get_indexing_statuses, update_network_chainheads},
networks::NetworkName,
BlockPointer,
};
use once_cell::sync::OnceCell;
Expand Down Expand Up @@ -45,16 +46,17 @@ async fn main() -> ! {
// subtopics are optionally provided and used as the content topic identifier of the message subject,
// if not provided then they are usually generated based on indexer allocations
let mut subtopics = vec!["ping-pong-content-topic".to_string()];
for topic in config.topics {
for topic in config.topics.clone() {
if !subtopics.contains(&topic) {
subtopics.push(topic);
}
}

debug!("Initializing the Graphcast Agent");
let graphcast_agent = GraphcastAgent::new(
// private_key resolves into ethereum wallet and indexer identity.
config.private_key,
// wallet key can be either private key or mnemonic, resolves into ethereum wallet and indexer identity.
// Unwrap is okay here thanks to configuration validate_set_up
config.wallet_input().unwrap().to_string(),
// radio_name is used as part of the content topic for the radio application
radio_name,
&config.registry_subgraph,
Expand Down
166 changes: 29 additions & 137 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use clap::Parser;
use ethers::signers::{LocalWallet, WalletError};
use once_cell::sync::Lazy;
use ethers::signers::WalletError;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fs::read_to_string;
use std::str::FromStr;
use tracing::{debug, info};
Expand All @@ -11,7 +9,7 @@ use waku::Multiaddr;
use crate::graphql::client_graph_node::get_indexing_statuses;
use crate::graphql::client_network::query_network_subgraph;
use crate::graphql::client_registry::query_registry_indexer;
use crate::{graphcast_id_address, init_tracing};
use crate::{build_wallet, graphcast_id_address, init_tracing};

#[derive(Clone, Debug, Parser, Serialize, Deserialize)]
#[clap(
Expand Down Expand Up @@ -39,9 +37,18 @@ pub struct Config {
value_parser = Config::parse_key,
env = "PRIVATE_KEY",
hide_env_values = true,
help = "Private key to the Graphcase ID wallet",
help = "Private key to the Graphcast ID wallet (Precendence over mnemonics)",
)]
pub private_key: String,
pub private_key: Option<String>,
#[clap(
long,
value_name = "KEY",
value_parser = Config::parse_key,
env = "MNEMONIC",
hide_env_values = true,
help = "Mnemonic to the Graphcast ID wallet (first address of the wallet is used; Only one of private key or mnemonic is needed)",
)]
pub mnemonic: Option<String>,
#[clap(
long,
value_name = "SUBGRAPH",
Expand Down Expand Up @@ -198,7 +205,7 @@ impl Config {
/// Validate that private key as an Eth wallet
fn parse_key(value: &str) -> Result<String, WalletError> {
// The wallet can be stored instead of the original private key
let wallet = value.parse::<LocalWallet>()?;
let wallet = build_wallet(value)?;
let addr = graphcast_id_address(&wallet);
info!("Resolved Graphcast id: {}", addr);
Ok(String::from(value))
Expand All @@ -224,12 +231,23 @@ impl Config {
Multiaddr::from_str(address).map_err(|e| ConfigError::ValidateInput(e.to_string()))
}

/// Private key takes precedence over mnemonic
pub fn wallet_input(&self) -> Result<&String, ConfigError> {
match (&self.private_key, &self.mnemonic) {
(Some(p), _) => Ok(p),
(_, Some(m)) => Ok(m),
_ => Err(ConfigError::ValidateInput(
"Must provide either private key or mnemonic".to_string(),
)),
}
}
/// Asynchronous validation to the configuration set ups
pub async fn validate_set_up(&self) -> Result<&Self, ConfigError> {
let wallet = self
.private_key
.parse::<LocalWallet>()
.map_err(|e| ConfigError::ValidateInput(format!("Private key links to wallet: {e}")))?;
let wallet = build_wallet(self.wallet_input()?).map_err(|e| {
ConfigError::ValidateInput(format!(
"Invalid key to wallet, use private key or mnemonic: {e}"
))
})?;
let graphcast_id = graphcast_id_address(&wallet);
// TODO: Implies invalidity for both graphcast id and registry, maybe map_err more specifically
let indexer = query_registry_indexer(self.registry_subgraph.to_string(), graphcast_id)
Expand Down Expand Up @@ -258,132 +276,6 @@ impl Config {
}
}

/// Struct for Network and block interval for updates
#[derive(Debug, Clone)]
pub struct Network {
pub name: NetworkName,
pub interval: u64,
}

/// List of supported networks
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum NetworkName {
Goerli,
Mainnet,
Gnosis,
Hardhat,
ArbitrumOne,
ArbitrumGoerli,
Avalanche,
Polygon,
Celo,
Optimism,
Fantom,
Unknown,
}

impl NetworkName {
pub fn from_string(name: &str) -> Self {
match name {
"goerli" => NetworkName::Goerli,
"mainnet" => NetworkName::Mainnet,
"gnosis" => NetworkName::Gnosis,
"hardhat" => NetworkName::Hardhat,
"arbitrum-one" => NetworkName::ArbitrumOne,
"arbitrum-goerli" => NetworkName::ArbitrumGoerli,
"avalanche" => NetworkName::Avalanche,
"polygon" => NetworkName::Polygon,
"celo" => NetworkName::Celo,
"optimism" => NetworkName::Optimism,
"fantom" => NetworkName::Fantom,
_ => NetworkName::Unknown,
}
}
}

impl fmt::Display for NetworkName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
NetworkName::Goerli => "goerli",
NetworkName::Mainnet => "mainnet",
NetworkName::Gnosis => "gnosis",
NetworkName::Hardhat => "hardhat",
NetworkName::ArbitrumOne => "arbitrum-one",
NetworkName::ArbitrumGoerli => "arbitrum-goerli",
NetworkName::Avalanche => "avalanche",
NetworkName::Polygon => "polygon",
NetworkName::Celo => "celo",
NetworkName::Optimism => "optimism",
NetworkName::Fantom => "fantom",
NetworkName::Unknown => "unknown",
};

write!(f, "{name}")
}
}

/// Maintained static list of supported Networks, the intervals target ~5minutes
/// depending on the blockchain average block processing time
pub static NETWORKS: Lazy<Vec<Network>> = Lazy::new(|| {
vec![
// Goerli (Ethereum Testnet): ~15 seconds
Network {
name: NetworkName::from_string("goerli"),
interval: 20,
},
// Mainnet (Ethereum): ~10-12 seconds
Network {
name: NetworkName::from_string("mainnet"),
interval: 30,
},
// Gnosis: ~5 seconds
Network {
name: NetworkName::from_string("gnosis"),
interval: 60,
},
// Local test network
Network {
name: NetworkName::from_string("hardhat"),
interval: 10,
},
// ArbitrumOne: ~0.5-1 second
Network {
name: NetworkName::from_string("arbitrum-one"),
interval: 50,
},
// ArbitrumGoerli (Arbitrum Testnet): ~6 seconds
Network {
name: NetworkName::from_string("arbitrum-goerli"),
interval: 60,
},
// Avalanche: ~3-5 seconds
Network {
name: NetworkName::from_string("avalanche"),
interval: 60,
},
// Polygon: ~2 seconds
Network {
name: NetworkName::from_string("polygon"),
interval: 150,
},
// Celo: ~5-10 seconds
Network {
name: NetworkName::from_string("celo"),
interval: 30,
},
// Optimism: ~10-15 seconds
Network {
name: NetworkName::from_string("optimism"),
interval: 20,
},
// Fantom: ~2-3 seconds
Network {
name: NetworkName::from_string("optimism"),
interval: 100,
},
]
});

#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
#[error("Validate the input: {0}")]
Expand Down
2 changes: 1 addition & 1 deletion src/graphcast_agent/message_typing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ use tracing::debug;
use waku::{Running, WakuContentTopic, WakuMessage, WakuNodeHandle, WakuPeerData, WakuPubSubTopic};

use crate::{
config::NetworkName,
graphql::{
client_graph_node::query_graph_node_network_block_hash,
client_network::query_network_subgraph, client_registry::query_registry_indexer,
QueryError,
},
networks::NetworkName,
NoncesMap,
};

Expand Down
8 changes: 4 additions & 4 deletions src/graphcast_agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ use self::waku_handling::{
setup_node_handle, WakuHandlingError,
};

use crate::config::NetworkName;
use crate::graphcast_agent::waku_handling::unsubscribe_peer;
use crate::graphql::client_graph_node::query_graph_node_network_block_hash;
use crate::graphql::client_registry::query_registry_indexer;
use crate::graphql::QueryError;
use crate::{graphcast_id_address, NoncesMap};
use crate::networks::NetworkName;
use crate::{build_wallet, graphcast_id_address, NoncesMap};

pub mod message_typing;
pub mod waku_handling;
Expand Down Expand Up @@ -105,7 +105,7 @@ impl GraphcastAgent {
/// ```
#[allow(clippy::too_many_arguments)]
pub async fn new(
private_key: String,
wallet_key: String,
radio_name: &'static str,
registry_subgraph: &str,
network_subgraph: &str,
Expand All @@ -118,7 +118,7 @@ impl GraphcastAgent {
waku_port: Option<String>,
waku_addr: Option<String>,
) -> Result<GraphcastAgent, GraphcastAgentError> {
let wallet = private_key.parse::<LocalWallet>()?;
let wallet = build_wallet(&wallet_key)?;
let pubsub_topic: WakuPubSubTopic = pubsub_topic(graphcast_namespace);

//Should we allow the setting of waku node host and port?
Expand Down
2 changes: 1 addition & 1 deletion src/graphql/client_graph_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};

use crate::graphql::QueryError;
use crate::NetworkPointer;
use crate::{config::NetworkName, BlockPointer};
use crate::{networks::NetworkName, BlockPointer};
use graphql_client::{GraphQLQuery, Response};
use serde_derive::{Deserialize, Serialize};
use tracing::{debug, trace, warn};
Expand Down
14 changes: 12 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
//! For more explanation, see the crate documentation.
//!
use config::{NetworkName, NETWORKS};
use ethers::signers::{Signer, Wallet};
use ethers::signers::{
coins_bip39::English, LocalWallet, MnemonicBuilder, Signer, Wallet, WalletError,
};
use ethers_core::k256::ecdsa::SigningKey;
use graphcast_agent::message_typing::GraphcastMessage;
use networks::{NetworkName, NETWORKS};

use once_cell::sync::OnceCell;
use prost::Message;
Expand All @@ -42,6 +44,7 @@ pub mod bots;
pub mod config;
pub mod graphcast_agent;
pub mod graphql;
pub mod networks;

type NoncesMap = HashMap<String, HashMap<String, i64>>;

Expand Down Expand Up @@ -78,6 +81,13 @@ pub fn config_env_var(name: &str) -> Result<String, String> {
env::var(name).map_err(|e| format!("{name}: {e}"))
}

/// Build Wallet from Private key or Mnemonic
pub fn build_wallet(value: &str) -> Result<Wallet<SigningKey>, WalletError> {
value
.parse::<LocalWallet>()
.or(MnemonicBuilder::<English>::default().phrase(value).build())
}

/// Get the graphcastID address from the wallet
pub fn graphcast_id_address(wallet: &Wallet<SigningKey>) -> String {
debug!("{}", format!("Wallet address: {:?}", wallet.address()));
Expand Down
Loading

0 comments on commit b7b22df

Please sign in to comment.