diff --git a/orchestrator/Cargo.lock b/orchestrator/Cargo.lock index 62ebc69b9..bec5cc12b 100644 --- a/orchestrator/Cargo.lock +++ b/orchestrator/Cargo.lock @@ -43,11 +43,12 @@ dependencies = [ [[package]] name = "abscissa_tokio" -version = "0.6.0-pre.1" +version = "0.6.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5483def0ba1533f8040c0288451373a3522f13be7386426bb81cf7605a806a50" +checksum = "80063fd5884d9a4220c4de14287b19c6aaf4a8f30a7dafb84cb757ff202edbff" dependencies = [ "abscissa_core", + "actix-rt 2.2.0", "tokio 1.5.0", ] @@ -1521,14 +1522,17 @@ dependencies = [ "gumdrop", "k256", "once_cell", + "orchestrator", "pkcs8", "rand_core 0.6.2", "regex", + "relayer", "rpassword", "serde", "signatory", "thiserror", "tokio 1.5.0", + "tonic", "web30", ] diff --git a/orchestrator/Cargo.toml b/orchestrator/Cargo.toml index e871199ba..05c72c511 100644 --- a/orchestrator/Cargo.toml +++ b/orchestrator/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +default-members = ["gorc", "orchestrator", "test_runner"] members = [ "orchestrator", "cosmos_gravity", @@ -12,4 +13,3 @@ members = [ "register_delegate_keys", "gorc", ] -default-members = ["orchestrator", "test_runner"] \ No newline at end of file diff --git a/orchestrator/gorc/Cargo.toml b/orchestrator/gorc/Cargo.toml index 645ca8acd..53c2aecc0 100644 --- a/orchestrator/gorc/Cargo.toml +++ b/orchestrator/gorc/Cargo.toml @@ -10,10 +10,13 @@ serde = { version = "1", features = ["serde_derive"] } thiserror = "1" regex = "1.5.4" -ethereum_gravity = { path = "../ethereum_gravity" } cosmos_gravity = { path = "../cosmos_gravity" } +ethereum_gravity = { path = "../ethereum_gravity" } +gravity_proto = { path = "../gravity_proto" } gravity_utils = { path = "../gravity_utils" } -gravity_proto = { path = "../gravity_proto/" } +orchestrator = { path = "../orchestrator" } +relayer = { path = "../relayer" } + deep_space = "2.4.3" clarity = "0.4.12" actix-rt = "2.2" @@ -24,9 +27,10 @@ pkcs8 = { version = "0.7", features = ["pem"] } signatory = "0.23.0-pre" rand_core = { version = "0.6", features = ["std"] } -abscissa_tokio = "0.6.0-pre.1" +abscissa_tokio = { version = "0.6.0-pre.2", features = ["actix"] } web30 = "0.14" tokio = "1" +tonic = "0.4" [dependencies.abscissa_core] version = "0.6.0-pre.1" diff --git a/orchestrator/gorc/gorc.toml b/orchestrator/gorc/gorc.toml index b4b8be6d7..dce59b860 100644 --- a/orchestrator/gorc/gorc.toml +++ b/orchestrator/gorc/gorc.toml @@ -2,6 +2,7 @@ keystore = "/tmp/keystore" [gravity] contract = "0x6b175474e89094c44da98b954eedeac495271d0f" +fees_denom = "stake" [ethereum] key_derivation_path = "m/44'/60'/0'/0/0" diff --git a/orchestrator/gorc/src/application.rs b/orchestrator/gorc/src/application.rs index e39bef905..6d3a9d063 100644 --- a/orchestrator/gorc/src/application.rs +++ b/orchestrator/gorc/src/application.rs @@ -87,4 +87,4 @@ impl Application for GorcApp { trace::Config::default() } } -} +} \ No newline at end of file diff --git a/orchestrator/gorc/src/commands.rs b/orchestrator/gorc/src/commands.rs index 03650c21a..0c0628fa1 100644 --- a/orchestrator/gorc/src/commands.rs +++ b/orchestrator/gorc/src/commands.rs @@ -12,20 +12,18 @@ mod deploy; mod keys; +mod orchestrator; mod query; -mod start; mod tests; mod tx; mod version; use self::{ - keys::KeysCmd, query::QueryCmd, start::StartCmd, tests::TestsCmd, tx::TxCmd, + keys::KeysCmd, orchestrator::OrchestratorCmd, query::QueryCmd, tests::TestsCmd, tx::TxCmd, version::VersionCmd, }; use crate::config::GorcConfig; -use abscissa_core::{ - Command, Configurable, Help, Options, Runnable, -}; +use abscissa_core::{Command, Configurable, Help, Options, Runnable}; use std::path::PathBuf; /// Gorc Configuration Filename @@ -34,8 +32,14 @@ pub const CONFIG_FILE: &str = "gorc.toml"; /// Gorc Subcommands #[derive(Command, Debug, Options, Runnable)] pub enum GorcCmd { - #[options(help = "create transactions on either ethereum or cosmos chains")] - Tx(TxCmd), + #[options(help = "get usage information")] + Help(Help), + + #[options(help = "key management commands")] + Keys(KeysCmd), + + #[options(help = "orchestrator")] + Orchestrator(OrchestratorCmd), #[options(help = "query state on either ethereum or cosmos chains")] Query(QueryCmd), @@ -43,14 +47,8 @@ pub enum GorcCmd { #[options(help = "run tests against configured chains")] Tests(TestsCmd), - #[options(help = "start the application")] - Start(StartCmd), - - #[options(help = "key management commands")] - Keys(KeysCmd), - - #[options(help = "get usage information")] - Help(Help), + #[options(help = "create transactions on either ethereum or cosmos chains")] + Tx(TxCmd), #[options(help = "display version information")] Version(VersionCmd), diff --git a/orchestrator/gorc/src/commands/deploy.rs b/orchestrator/gorc/src/commands/deploy.rs index afad09ce6..5671f611a 100644 --- a/orchestrator/gorc/src/commands/deploy.rs +++ b/orchestrator/gorc/src/commands/deploy.rs @@ -6,7 +6,7 @@ use clarity::Address as EthAddress; use clarity::PrivateKey as EthPrivateKey; use ethereum_gravity::deploy_erc20::deploy_erc20; use gravity_proto::gravity::DenomToErc20Request; -use gravity_utils::connection_prep::{create_rpc_connections}; +use gravity_utils::connection_prep::create_rpc_connections; use std::time::Instant; use std::{process::exit, time::Duration}; use tokio::time::sleep as delay_for; diff --git a/orchestrator/gorc/src/commands/keys.rs b/orchestrator/gorc/src/commands/keys.rs index 7a5efae59..224d93067 100644 --- a/orchestrator/gorc/src/commands/keys.rs +++ b/orchestrator/gorc/src/commands/keys.rs @@ -1,5 +1,3 @@ -//! `keys` subcommand - mod cosmos; mod eth; diff --git a/orchestrator/gorc/src/commands/keys/cosmos/show.rs b/orchestrator/gorc/src/commands/keys/cosmos/show.rs index 8758f371f..6199d6782 100644 --- a/orchestrator/gorc/src/commands/keys/cosmos/show.rs +++ b/orchestrator/gorc/src/commands/keys/cosmos/show.rs @@ -1,12 +1,9 @@ use crate::application::APP; use abscissa_core::{Application, Command, Options, Runnable}; -use deep_space; -use signatory::FsKeyStore; -use std::path::Path; #[derive(Command, Debug, Default, Options)] pub struct ShowCosmosKeyCmd { - #[options(free, help = "delete [name]")] + #[options(free, help = "show [name]")] pub args: Vec, } @@ -14,21 +11,8 @@ pub struct ShowCosmosKeyCmd { impl Runnable for ShowCosmosKeyCmd { fn run(&self) { let config = APP.config(); - let keystore = Path::new(&config.keystore); - let keystore = FsKeyStore::create_or_open(keystore).unwrap(); let name = self.args.get(0).expect("name is required"); - let name = name.parse().expect("Could not parse name"); - - let key = keystore.load(&name).expect("Could not load key"); - let key = key - .to_pem() - .parse::>() - .expect("Could not parse key"); - - let key = deep_space::utils::bytes_to_hex_str(&key.to_bytes()); - let key = key - .parse::() - .expect("Could not parse private key"); + let key = config.load_deep_space_key(name.clone()); let address = key .to_address(config.cosmos.prefix.trim()) diff --git a/orchestrator/gorc/src/commands/keys/eth/show.rs b/orchestrator/gorc/src/commands/keys/eth/show.rs index 596b8e287..b7d8137e9 100644 --- a/orchestrator/gorc/src/commands/keys/eth/show.rs +++ b/orchestrator/gorc/src/commands/keys/eth/show.rs @@ -1,8 +1,5 @@ use crate::application::APP; use abscissa_core::{Application, Command, Options, Runnable}; -use clarity; -use signatory::FsKeyStore; -use std::path; #[derive(Command, Debug, Default, Options)] pub struct ShowEthKeyCmd { @@ -14,19 +11,8 @@ pub struct ShowEthKeyCmd { impl Runnable for ShowEthKeyCmd { fn run(&self) { let config = APP.config(); - let keystore = path::Path::new(&config.keystore); - let keystore = FsKeyStore::create_or_open(keystore).expect("Could not open keystore"); - let name = self.args.get(0).expect("name is required"); - let name = name.parse().expect("Could not parse name"); - - let key = keystore.load(&name).expect("Could not load key"); - let key = key - .to_pem() - .parse::>() - .expect("Could not parse key"); - - let key = clarity::PrivateKey::from_slice(&key.to_bytes()).expect("Could not convert key"); + let key = config.load_clarity_key(name.clone()); let pub_key = key.to_public_key().expect("Could not build public key"); diff --git a/orchestrator/gorc/src/commands/orchestrator.rs b/orchestrator/gorc/src/commands/orchestrator.rs new file mode 100644 index 000000000..43d16e6c3 --- /dev/null +++ b/orchestrator/gorc/src/commands/orchestrator.rs @@ -0,0 +1,16 @@ +mod start; + +use abscissa_core::{Command, Options, Runnable}; + +/// `orchestator` subcommand +/// +/// The `Options` proc macro generates an option parser based on the struct +/// definition, and is defined in the `gumdrop` crate. See their documentation +/// for a more comprehensive example: +/// +/// +#[derive(Command, Debug, Options, Runnable)] +pub enum OrchestratorCmd { + #[options(name = "start")] + Start(start::StartCommand), +} \ No newline at end of file diff --git a/orchestrator/gorc/src/commands/orchestrator/start.rs b/orchestrator/gorc/src/commands/orchestrator/start.rs new file mode 100644 index 000000000..b86020d26 --- /dev/null +++ b/orchestrator/gorc/src/commands/orchestrator/start.rs @@ -0,0 +1,98 @@ +use crate::{application::APP, prelude::*}; +use abscissa_core::{Command, Options, Runnable}; +use clarity::address::Address as EthAddress; +use gravity_utils::connection_prep::{ + check_delegate_addresses, check_for_eth, check_for_fee_denom, create_rpc_connections, + wait_for_cosmos_node_ready, +}; +use orchestrator::main_loop::{ + orchestrator_main_loop, ETH_ORACLE_LOOP_SPEED, ETH_SIGNER_LOOP_SPEED, +}; +use relayer::main_loop::LOOP_SPEED as RELAYER_LOOP_SPEED; +use std::cmp::min; + +#[derive(Command, Debug, Options)] +pub struct StartCommand { + #[options(help = "cosmos key name")] + cosmos_key: String, + + #[options(help = "ethereum key name")] + ethereum_key: String, +} + +impl Runnable for StartCommand { + fn run(&self) { + let config = APP.config(); + let cosmos_prefix = config.cosmos.prefix.clone(); + + let cosmos_key = config.load_deep_space_key(self.cosmos_key.clone()); + let cosmos_address = cosmos_key.to_address(&cosmos_prefix).unwrap(); + + let ethereum_key = config.load_clarity_key(self.ethereum_key.clone()); + let ethereum_address = ethereum_key.to_public_key().unwrap(); + + let contract_address: EthAddress = config + .gravity + .contract + .parse() + .expect("Could not parse gravity contract address"); + + let fees_denom = config.gravity.fees_denom.clone(); + + let timeout = min( + min(ETH_SIGNER_LOOP_SPEED, ETH_ORACLE_LOOP_SPEED), + RELAYER_LOOP_SPEED, + ); + + abscissa_tokio::run_with_actix(&APP, async { + let connections = create_rpc_connections( + cosmos_prefix, + Some(config.cosmos.grpc.clone()), + Some(config.ethereum.rpc.clone()), + timeout, + ) + .await; + + let mut grpc = connections.grpc.clone().unwrap(); + let contact = connections.contact.clone().unwrap(); + let web3 = connections.web3.clone().unwrap(); + + info!("Starting Relayer + Oracle + Ethereum Signer"); + info!("Ethereum Address: {}", ethereum_address); + info!("Cosmos Address {}", cosmos_address); + + // check if the cosmos node is syncing, if so wait for it + // we can't move any steps above this because they may fail on an incorrect + // historic chain state while syncing occurs + wait_for_cosmos_node_ready(&contact).await; + + // check if the delegate addresses are correctly configured + check_delegate_addresses( + &mut grpc, + ethereum_address, + cosmos_address, + &contact.get_prefix(), + ) + .await; + + // check if we actually have the promised balance of tokens to pay fees + check_for_fee_denom(&fees_denom, cosmos_address, &contact).await; + check_for_eth(ethereum_address, &web3).await; + + orchestrator_main_loop( + cosmos_key, + ethereum_key, + web3, + contact, + grpc, + contract_address, + fees_denom, + ) + .await; + }) + .unwrap_or_else(|e| { + status_err!("executor exited with error: {}", e); + std::process::exit(1); + }); + } +} diff --git a/orchestrator/gorc/src/commands/start.rs b/orchestrator/gorc/src/commands/start.rs deleted file mode 100644 index 311f2776e..000000000 --- a/orchestrator/gorc/src/commands/start.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! `start` subcommand - example of how to write a subcommand - -use crate::{application::APP, prelude::*}; -/// App-local prelude includes `app_reader()`/`app_writer()`/`app_config()` -/// accessors along with logging macros. Customize as you see fit. -use abscissa_core::{Command, Options, Runnable}; - -/// `start` subcommand - -#[derive(Command, Debug, Options)] -pub enum StartCmd { - /// To whom are we saying hello? - #[options(help = "orchestrator [contract-address] [fee-denom]")] - Orchestrator(Orchestrator), - - #[options(help = "relayer")] - Relayer(Relayer), -} - -impl Runnable for StartCmd { - /// Start the application. - fn run(&self) { - //Your code goes here - } -} - -#[derive(Command, Debug, Options)] -pub struct Orchestrator { - #[options(free)] - free: Vec, - - #[options(help = "print help message")] - help: bool, -} - -impl Runnable for Orchestrator { - fn run(&self) { - assert!(self.free.len() == 2); - let _contract_address = self.free[0].clone(); - let _fee_denom = self.free[1].clone(); - - abscissa_tokio::run(&APP, async { unimplemented!() }).unwrap_or_else(|e| { - status_err!("executor exited with error: {}", e); - std::process::exit(1); - }); - } -} - -#[derive(Command, Debug, Options)] -pub struct Relayer { - #[options(help = "print help message")] - help: bool, -} - -impl Runnable for Relayer { - /// Start the application. - fn run(&self) {} -} diff --git a/orchestrator/gorc/src/config.rs b/orchestrator/gorc/src/config.rs index da4a13c46..e333df180 100644 --- a/orchestrator/gorc/src/config.rs +++ b/orchestrator/gorc/src/config.rs @@ -1,4 +1,6 @@ use serde::{Deserialize, Serialize}; +use signatory::FsKeyStore; +use std::path::Path; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(deny_unknown_fields)] @@ -9,6 +11,27 @@ pub struct GorcConfig { pub cosmos: CosmosSection, } +impl GorcConfig { + fn load_secret_key(&self, name: String) -> k256::elliptic_curve::SecretKey { + let keystore = Path::new(&self.keystore); + let keystore = FsKeyStore::create_or_open(keystore).expect("Could not open keystore"); + let name = name.parse().expect("Could not parse name"); + let key = keystore.load(&name).expect("Could not load key"); + return key.to_pem().parse().expect("Could not parse pem"); + } + + pub fn load_clarity_key(&self, name: String) -> clarity::PrivateKey { + let key = self.load_secret_key(name).to_bytes(); + return clarity::PrivateKey::from_slice(&key).expect("Could not convert key"); + } + + pub fn load_deep_space_key(&self, name: String) -> deep_space::private_key::PrivateKey { + let key = self.load_secret_key(name).to_bytes(); + let key = deep_space::utils::bytes_to_hex_str(&key); + return key.parse().expect("Could not parse private key"); + } +} + impl Default for GorcConfig { fn default() -> Self { Self { @@ -24,12 +47,14 @@ impl Default for GorcConfig { #[serde(deny_unknown_fields)] pub struct GravitySection { pub contract: String, + pub fees_denom: String, } impl Default for GravitySection { fn default() -> Self { Self { contract: "0x6b175474e89094c44da98b954eedeac495271d0f".to_owned(), + fees_denom: "stake".to_owned(), } } } diff --git a/orchestrator/orchestrator/Cargo.toml b/orchestrator/orchestrator/Cargo.toml index e4168c921..dad2191c8 100644 --- a/orchestrator/orchestrator/Cargo.toml +++ b/orchestrator/orchestrator/Cargo.toml @@ -13,11 +13,11 @@ name = "orchestrator" path = "src/main.rs" [dependencies] -relayer = {path = "../relayer/"} -ethereum_gravity = {path = "../ethereum_gravity"} -cosmos_gravity = {path = "../cosmos_gravity"} -gravity_utils = {path = "../gravity_utils"} -gravity_proto = {path = "../gravity_proto/"} +relayer = { path = "../relayer" } +ethereum_gravity = { path = "../ethereum_gravity" } +cosmos_gravity = { path = "../cosmos_gravity" } +gravity_utils = { path = "../gravity_utils" } +gravity_proto = { path = "../gravity_proto" } deep_space = "2.4.3" serde_derive = "1.0" @@ -43,4 +43,4 @@ openssl-probe = "0.1" # this crate. This allows for easy cross compiled builds because the 'vendored' # feature includes it's own OpenSSL version that's compiled on the fly # If ANY crate in this workspace has this it will work for all of them. -openssl = {version = "0.10", features = ["vendored"]} +openssl = { version = "0.10", features = ["vendored"] } diff --git a/orchestrator/relayer/Cargo.toml b/orchestrator/relayer/Cargo.toml index 92e80352c..bdf1b368a 100644 --- a/orchestrator/relayer/Cargo.toml +++ b/orchestrator/relayer/Cargo.toml @@ -13,10 +13,10 @@ name = "relayer" path = "src/main.rs" [dependencies] -ethereum_gravity = {path = "../ethereum_gravity"} -cosmos_gravity = {path = "../cosmos_gravity"} -gravity_utils = {path = "../gravity_utils"} -gravity_proto = {path = "../gravity_proto/"} +ethereum_gravity = { path = "../ethereum_gravity" } +cosmos_gravity = { path = "../cosmos_gravity" } +gravity_utils = { path = "../gravity_utils" } +gravity_proto = { path = "../gravity_proto" } deep_space = "2.4.3" serde_derive = "1.0" @@ -35,4 +35,4 @@ openssl-probe = "0.1" [dev-dependencies] -actix = "0.11" \ No newline at end of file +actix = "0.11" diff --git a/orchestrator/test_runner/src/utils.rs b/orchestrator/test_runner/src/utils.rs index 230c0ba2d..04c782465 100644 --- a/orchestrator/test_runner/src/utils.rs +++ b/orchestrator/test_runner/src/utils.rs @@ -1,5 +1,5 @@ use crate::TOTAL_TIMEOUT; -use crate::{one_eth, MINER_PRIVATE_KEY}; +use crate::{MINER_PRIVATE_KEY}; use crate::{MINER_ADDRESS, OPERATION_TIMEOUT}; use clarity::{Address as EthAddress, Uint256}; use clarity::{PrivateKey as EthPrivateKey, Transaction}; @@ -11,57 +11,6 @@ use futures::future::join_all; use rand::Rng; use web30::{client::Web3, types::SendTxOption}; -/// This overly complex function primarily exists to parallelize the sending of Eth to the -/// orchestrators, waiting for these there transactions takes up nearly a minute of test time -/// and it seemed like low hanging fruit. It in fact was not, mostly because we are sending -/// these tx's from the same address and we therefore need to take into account the correct -/// nonce given the other transactions in flight. This means we need to build the transactions -/// ourselves with that info right here. If you have to modify this seriously consider -/// just calling send_one_eth in a loop. -pub async fn send_eth_to_orchestrators(keys: &[ValidatorKeys], web30: &Web3) { - let balance = web30.eth_get_balance(*MINER_ADDRESS).await.unwrap(); - info!( - "Sending orchestrators 100 eth to pay for fees miner has {} ETH", - balance / one_eth() - ); - let mut eth_addresses = Vec::new(); - for k in keys { - let e_key = k.eth_key; - eth_addresses.push(e_key.to_public_key().unwrap()) - } - let net_version = web30.net_version().await.unwrap(); - let mut nonce = web30 - .eth_get_transaction_count(*MINER_ADDRESS) - .await - .unwrap(); - let mut transactions = Vec::new(); - for address in eth_addresses { - let t = Transaction { - to: address, - nonce: nonce.clone(), - gas_price: 1_000_000_000u64.into(), - gas_limit: 24000u64.into(), - value: one_eth() * 100u16.into(), - data: Vec::new(), - signature: None, - }; - let t = t.sign(&*MINER_PRIVATE_KEY, Some(net_version)); - transactions.push(t); - nonce += 1u64.into(); - } - let mut sends = Vec::new(); - for tx in transactions { - sends.push(web30.eth_send_raw_transaction(tx.to_bytes().unwrap())); - } - let txids = join_all(sends).await; - let mut wait_for_txid = Vec::new(); - for txid in txids { - let wait = web30.wait_for_transaction(txid.unwrap(), TOTAL_TIMEOUT, None); - wait_for_txid.push(wait); - } - join_all(wait_for_txid).await; -} - pub async fn send_one_eth(dest: EthAddress, web30: &Web3) { let txid = web30 .send_transaction(