diff --git a/orchestrator/Cargo.lock b/orchestrator/Cargo.lock index 4e3072c39..8308d6b7e 100644 --- a/orchestrator/Cargo.lock +++ b/orchestrator/Cargo.lock @@ -470,9 +470,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.41" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61" +checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" [[package]] name = "arrayvec" @@ -1226,6 +1226,7 @@ name = "gbt" version = "0.5.6" dependencies = [ "actix-rt 2.2.0", + "anyhow", "clap", "clarity", "cosmos_gravity", @@ -1312,6 +1313,7 @@ dependencies = [ "serde", "serde_derive", "sha3", + "thiserror", "tokio 1.8.1", "tonic", "url", @@ -2903,18 +2905,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.26" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.26" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", diff --git a/orchestrator/cosmos_gravity/src/query.rs b/orchestrator/cosmos_gravity/src/query.rs index bf553fe93..308b29cd2 100644 --- a/orchestrator/cosmos_gravity/src/query.rs +++ b/orchestrator/cosmos_gravity/src/query.rs @@ -29,8 +29,8 @@ use tonic::transport::Channel; pub async fn get_gravity_params( client: &mut GravityQueryClient, ) -> Result { - let request = client.params(QueryParamsRequest {}).await?.into_inner(); - Ok(request.params.unwrap()) + let response = client.params(QueryParamsRequest {}).await?.into_inner(); + Ok(response.params.unwrap()) } /// get the valset for a given nonce (block) height @@ -38,10 +38,10 @@ pub async fn get_valset( client: &mut GravityQueryClient, nonce: u64, ) -> Result, GravityError> { - let request = client + let response = client .valset_request(QueryValsetRequestRequest { nonce }) .await?; - let valset = request.into_inner().valset; + let valset = response.into_inner().valset; let valset = valset.map(|v| v.into()); Ok(valset) } @@ -54,8 +54,8 @@ pub async fn get_valset( pub async fn get_current_valset( client: &mut GravityQueryClient, ) -> Result { - let request = client.current_valset(QueryCurrentValsetRequest {}).await?; - let valset = request.into_inner().valset; + let response = client.current_valset(QueryCurrentValsetRequest {}).await?; + let valset = response.into_inner().valset; if let Some(valset) = valset { Ok(valset.into()) } else { @@ -73,12 +73,12 @@ pub async fn get_oldest_unsigned_valsets( address: Address, prefix: String, ) -> Result, GravityError> { - let request = client + let response = client .last_pending_valset_request_by_addr(QueryLastPendingValsetRequestByAddrRequest { address: address.to_bech32(prefix).unwrap(), }) .await?; - let valsets = request.into_inner().valsets; + let valsets = response.into_inner().valsets; // convert from proto valset type to rust valset type let valsets = valsets.iter().map(|v| v.into()).collect(); Ok(valsets) @@ -89,10 +89,10 @@ pub async fn get_oldest_unsigned_valsets( pub async fn get_latest_valsets( client: &mut GravityQueryClient, ) -> Result, GravityError> { - let request = client + let response = client .last_valset_requests(QueryLastValsetRequestsRequest {}) .await?; - let valsets = request.into_inner().valsets; + let valsets = response.into_inner().valsets; Ok(valsets.iter().map(|v| v.into()).collect()) } @@ -101,10 +101,10 @@ pub async fn get_all_valset_confirms( client: &mut GravityQueryClient, nonce: u64, ) -> Result, GravityError> { - let request = client + let response = client .valset_confirms_by_nonce(QueryValsetConfirmsByNonceRequest { nonce }) .await?; - let confirms = request.into_inner().confirms; + let confirms = response.into_inner().confirms; let mut parsed_confirms = Vec::new(); for item in confirms { let v: ValsetConfirmResponse = ValsetConfirmResponse::try_from(&item)?; @@ -118,12 +118,12 @@ pub async fn get_oldest_unsigned_transaction_batch( address: Address, prefix: String, ) -> Result, GravityError> { - let request = client + let response = client .last_pending_batch_request_by_addr(QueryLastPendingBatchRequestByAddrRequest { address: address.to_bech32(prefix).unwrap(), }) .await?; - let batch = request.into_inner().batch; + let batch = response.into_inner().batch; match batch { Some(batch) => Ok(Some(TransactionBatch::try_from(batch)?)), None => Ok(None), @@ -135,10 +135,10 @@ pub async fn get_oldest_unsigned_transaction_batch( pub async fn get_latest_transaction_batches( client: &mut GravityQueryClient, ) -> Result, GravityError> { - let request = client + let response = client .outgoing_tx_batches(QueryOutgoingTxBatchesRequest {}) .await?; - let batches = request.into_inner().batches; + let batches = response.into_inner().batches; let mut out = Vec::new(); for batch in batches { out.push(TransactionBatch::try_from(batch)?) @@ -152,13 +152,13 @@ pub async fn get_transaction_batch_signatures( nonce: u64, contract_address: EthAddress, ) -> Result, GravityError> { - let request = client + let response = client .batch_confirms(QueryBatchConfirmsRequest { nonce, contract_address: contract_address.to_string(), }) .await?; - let batch_confirms = request.into_inner().confirms; + let batch_confirms = response.into_inner().confirms; let mut out = Vec::new(); for confirm in batch_confirms { out.push(BatchConfirmResponse::try_from(confirm)?) @@ -173,22 +173,22 @@ pub async fn get_last_event_nonce_for_validator( address: Address, prefix: String, ) -> Result { - let request = client + let response = client .last_event_nonce_by_addr(QueryLastEventNonceByAddrRequest { address: address.to_bech32(prefix).unwrap(), }) .await?; - Ok(request.into_inner().event_nonce) + Ok(response.into_inner().event_nonce) } /// Gets the 100 latest logic calls for a relayer to consider relaying pub async fn get_latest_logic_calls( client: &mut GravityQueryClient, ) -> Result, GravityError> { - let request = client + let response = client .outgoing_logic_calls(QueryOutgoingLogicCallsRequest {}) .await?; - let calls = request.into_inner().calls; + let calls = response.into_inner().calls; let mut out = Vec::new(); for call in calls { out.push(LogicCall::try_from(call)?); @@ -201,13 +201,13 @@ pub async fn get_logic_call_signatures( invalidation_id: Vec, invalidation_nonce: u64, ) -> Result, GravityError> { - let request = client + let response = client .logic_confirms(QueryLogicConfirmsRequest { invalidation_id, invalidation_nonce, }) .await?; - let call_confirms = request.into_inner().confirms; + let call_confirms = response.into_inner().confirms; let mut out = Vec::new(); for confirm in call_confirms { out.push(LogicCallConfirmResponse::try_from(confirm)?) @@ -220,12 +220,12 @@ pub async fn get_oldest_unsigned_logic_call( address: Address, prefix: String, ) -> Result, GravityError> { - let request = client + let response = client .last_pending_logic_call_by_addr(QueryLastPendingLogicCallByAddrRequest { address: address.to_bech32(prefix).unwrap(), }) .await?; - let call = request.into_inner().call; + let call = response.into_inner().call; match call { Some(call) => Ok(Some(LogicCall::try_from(call)?)), None => Ok(None), @@ -236,12 +236,12 @@ pub async fn get_attestations( client: &mut GravityQueryClient, limit: Option, ) -> Result, GravityError> { - let request = client + let response = client .get_attestations(QueryAttestationsRequest { limit: limit.or(Some(1000u64)).unwrap(), }) .await?; - let attestations = request.into_inner().attestations; + let attestations = response.into_inner().attestations; Ok(attestations) } @@ -250,10 +250,10 @@ pub async fn get_pending_send_to_eth( client: &mut GravityQueryClient, sender_address: Address, ) -> Result { - let request = client + let response = client .get_pending_send_to_eth(QueryPendingSendToEth { sender_address: sender_address.to_string(), }) .await?; - Ok(request.into_inner()) + Ok(response.into_inner()) } diff --git a/orchestrator/cosmos_gravity/src/send.rs b/orchestrator/cosmos_gravity/src/send.rs index 7fb621e94..59c2ad0b8 100644 --- a/orchestrator/cosmos_gravity/src/send.rs +++ b/orchestrator/cosmos_gravity/src/send.rs @@ -25,7 +25,8 @@ use gravity_proto::gravity::MsgValsetUpdatedClaim; use gravity_proto::gravity::{MsgBatchSendToEthClaim, MsgSubmitBadSignatureEvidence}; use gravity_proto::gravity::{MsgCancelSendToEth, MsgConfirmBatch}; use gravity_utils::types::*; -use std::{collections::HashMap, time::Duration}; +use std::collections::BTreeMap; +use std::time::Duration; use crate::utils::BadSignatureEvidence; @@ -262,8 +263,8 @@ pub async fn send_ethereum_claims( // would require a truly horrendous (nearly 100 line) match statement to deal with all combinations. That match statement // could be reduced by adding two traits to sort against but really this is the easiest option. // - // We index the events by event nonce in an unordered hashmap and then play them back in order into a vec - let mut unordered_msgs = HashMap::new(); + // We index the events by event nonce in a sorted hashmap, skipping the need to sort it later + let mut ordered_msgs = BTreeMap::new(); for deposit in deposits { let claim = MsgSendToCosmosClaim { event_nonce: deposit.event_nonce, @@ -275,7 +276,7 @@ pub async fn send_ethereum_claims( orchestrator: our_address.to_string(), }; let msg = Msg::new("/gravity.v1.MsgSendToCosmosClaim", claim); - unordered_msgs.insert(deposit.event_nonce, msg); + ordered_msgs.insert(deposit.event_nonce, msg); } for withdraw in withdraws { let claim = MsgBatchSendToEthClaim { @@ -286,7 +287,7 @@ pub async fn send_ethereum_claims( orchestrator: our_address.to_string(), }; let msg = Msg::new("/gravity.v1.MsgBatchSendToEthClaim", claim); - unordered_msgs.insert(withdraw.event_nonce, msg); + ordered_msgs.insert(withdraw.event_nonce, msg); } for deploy in erc20_deploys { let claim = MsgErc20DeployedClaim { @@ -300,7 +301,7 @@ pub async fn send_ethereum_claims( orchestrator: our_address.to_string(), }; let msg = Msg::new("/gravity.v1.MsgERC20DeployedClaim", claim); - unordered_msgs.insert(deploy.event_nonce, msg); + ordered_msgs.insert(deploy.event_nonce, msg); } for call in logic_calls { let claim = MsgLogicCallExecutedClaim { @@ -311,7 +312,7 @@ pub async fn send_ethereum_claims( orchestrator: our_address.to_string(), }; let msg = Msg::new("/gravity.v1.MsgLogicCallExecutedClaim", claim); - unordered_msgs.insert(call.event_nonce, msg); + ordered_msgs.insert(call.event_nonce, msg); } for valset in valsets { let claim = MsgValsetUpdatedClaim { @@ -327,18 +328,10 @@ pub async fn send_ethereum_claims( orchestrator: our_address.to_string(), }; let msg = Msg::new("/gravity.v1.MsgValsetUpdatedClaim", claim); - unordered_msgs.insert(valset.event_nonce, msg); + ordered_msgs.insert(valset.event_nonce, msg); } - let mut keys = Vec::new(); - for (key, _) in unordered_msgs.iter() { - keys.push(*key); - } - keys.sort_unstable(); - let mut msgs = Vec::new(); - for i in keys { - msgs.push(unordered_msgs.remove_entry(&i).unwrap().1); - } + let msgs: Vec = ordered_msgs.into_iter().map(|(_, v)| v).collect(); let fee = Fee { amount: vec![fee], diff --git a/orchestrator/ethereum_gravity/src/utils.rs b/orchestrator/ethereum_gravity/src/utils.rs index 92882f67b..b7606d948 100644 --- a/orchestrator/ethereum_gravity/src/utils.rs +++ b/orchestrator/ethereum_gravity/src/utils.rs @@ -11,14 +11,13 @@ pub fn downcast_uint256(input: Uint256) -> Option { if input >= U64MAX.into() { None } else { - let mut val = input.to_bytes_be(); - // pad to 8 bytes - while val.len() < 8 { - val.insert(0, 0); - } + let val = input.to_bytes_be(); let mut lower_bytes: [u8; 8] = [0; 8]; + // get the start index after the trailing zeros + let start_index = 8 - val.len(); + // get the 'lowest' 8 bytes from a 256 bit integer - lower_bytes.copy_from_slice(&val[0..val.len()]); + lower_bytes[start_index..].copy_from_slice(val.as_slice()); Some(u64::from_be_bytes(lower_bytes)) } } @@ -27,14 +26,11 @@ pub fn downcast_to_u128(input: Uint256) -> Option { if input >= U128MAX.into() { None } else { - let mut val = input.to_bytes_be(); - // pad to 8 bytes - while val.len() < 16 { - val.insert(0, 0); - } + let val = input.to_bytes_be(); let mut lower_bytes: [u8; 16] = [0; 16]; + let start_index = 16 - val.len(); // get the 'lowest' 16 bytes from a 256 bit integer - lower_bytes.copy_from_slice(&val[0..val.len()]); + lower_bytes[start_index..].copy_from_slice(val.as_slice()); Some(u128::from_be_bytes(lower_bytes)) } } diff --git a/orchestrator/gbt/Cargo.toml b/orchestrator/gbt/Cargo.toml index 5a0996b8b..c7d5835af 100644 --- a/orchestrator/gbt/Cargo.toml +++ b/orchestrator/gbt/Cargo.toml @@ -1,37 +1,37 @@ [package] -name = "gbt" -version = "0.5.6" authors = ["Justin Kilpatrick "] +description = "Gravity Bridge Tools. User tooling for the Althea Ethereum <=> Cosmos Gravtiy bridge" edition = "2018" +homepage = "https://gravitybridge.althea.net/" license = "Apache-2.0" -description = "Gravity Bridge Tools. User tooling for the Althea Ethereum <=> Cosmos Gravtiy bridge" +name = "gbt" repository = "https://github.com/althea-net/cosmos-gravity-bridge" -homepage = "https://gravitybridge.althea.net/" - +version = "0.5.6" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum_gravity = {path = "../ethereum_gravity"} cosmos_gravity = {path = "../cosmos_gravity"} -gravity_utils = {path = "../gravity_utils"} +ethereum_gravity = {path = "../ethereum_gravity"} gravity_proto = {path = "../gravity_proto/"} -relayer = {path = "../relayer/"} +gravity_utils = {path = "../gravity_utils"} orchestrator = {path = "../orchestrator/"} +relayer = {path = "../relayer/"} -deep_space = "2.4" -serde_derive = "1.0" -clarity = "0.4" -clap = "3.0.0-beta.2" -serde = "1.0" actix-rt = "2.2" -lazy_static = "1" -url = "2" -web30 = "0.15" +anyhow = "1.0.44" +clap = "3.0.0-beta.2" +clarity = "0.4" +deep_space = "2.4" +dirs = "3.0" env_logger = "0.9" +lazy_static = "1" log = "0.4" openssl-probe = "0.1" -tokio = "1.4" rand = "0.8" -dirs = "3.0" -toml = "0.5" \ No newline at end of file +serde = "1.0" +serde_derive = "1.0" +tokio = "1.4" +toml = "0.5" +url = "2" +web30 = "0.15" diff --git a/orchestrator/gbt/src/client/cosmos_to_eth.rs b/orchestrator/gbt/src/client/cosmos_to_eth.rs index 7d4c0d1c3..854615e19 100644 --- a/orchestrator/gbt/src/client/cosmos_to_eth.rs +++ b/orchestrator/gbt/src/client/cosmos_to_eth.rs @@ -1,13 +1,13 @@ use crate::utils::print_eth; use crate::utils::TIMEOUT; use crate::{args::CosmosToEthOpts, utils::print_atom}; +use anyhow::{anyhow, Result}; use cosmos_gravity::send::{send_request_batch, send_to_eth}; use deep_space::Coin; use gravity_proto::gravity::QueryDenomToErc20Request; use gravity_utils::connection_prep::{check_for_fee, create_rpc_connections}; -use std::process::exit; -pub async fn cosmos_to_eth(args: CosmosToEthOpts, address_prefix: String) { +pub async fn cosmos_to_eth(args: CosmosToEthOpts, address_prefix: String) -> Result<()> { let cosmos_key = args.cosmos_phrase; let gravity_coin = args.amount; let fee = args.fees; @@ -41,7 +41,10 @@ pub async fn cosmos_to_eth(args: CosmosToEthOpts, address_prefix: String) { "Asset {} has no ERC20 representation, you may need to deploy an ERC20 for it!", gravity_coin.denom ); - exit(1); + return Err(anyhow!( + "Asset {} has no ERC20 representation", + gravity_coin.denom + )); } } @@ -50,8 +53,8 @@ pub async fn cosmos_to_eth(args: CosmosToEthOpts, address_prefix: String) { denom: gravity_coin.denom.clone(), amount: 1u64.into(), }; - check_for_fee(&gravity_coin, cosmos_address, &contact).await; - check_for_fee(&fee, cosmos_address, &contact).await; + check_for_fee(&gravity_coin, cosmos_address, &contact).await?; + check_for_fee(&fee, cosmos_address, &contact).await?; let balances = contact .get_balances(cosmos_address) @@ -68,14 +71,16 @@ pub async fn cosmos_to_eth(args: CosmosToEthOpts, address_prefix: String) { if found.is_none() { error!("You don't have any {} tokens!", gravity_coin.denom); - exit(1); + return Err(anyhow!("You don't have any {} tokens!", gravity_coin.denom)); } else if amount.amount.clone() >= found.unwrap().amount { if is_cosmos_originated { error!("Your transfer of {} {} tokens is greater than your balance of {} tokens. Remember you need some to pay for fees!", print_atom(amount.amount), gravity_coin.denom, print_atom(found.unwrap().amount.clone())); } else { error!("Your transfer of {} {} tokens is greater than your balance of {} tokens. Remember you need some to pay for fees!", print_eth(amount.amount), gravity_coin.denom, print_eth(found.unwrap().amount.clone())); } - exit(1); + return Err(anyhow!( + "You transfer of tokens is greater than your balance!" + )); } info!( @@ -110,4 +115,6 @@ pub async fn cosmos_to_eth(args: CosmosToEthOpts, address_prefix: String) { } else { info!("--no-batch specified, your transfer will wait until someone requests a batch for this token type") } + + Ok(()) } diff --git a/orchestrator/gbt/src/client/deploy_erc20_representation.rs b/orchestrator/gbt/src/client/deploy_erc20_representation.rs index 0d35ab342..2ce2854d3 100644 --- a/orchestrator/gbt/src/client/deploy_erc20_representation.rs +++ b/orchestrator/gbt/src/client/deploy_erc20_representation.rs @@ -1,19 +1,17 @@ use crate::{args::DeployErc20RepresentationOpts, utils::TIMEOUT}; +use anyhow::{anyhow, Result}; use cosmos_gravity::query::get_gravity_params; use ethereum_gravity::deploy_erc20::deploy_erc20; use gravity_proto::gravity::QueryDenomToErc20Request; use gravity_utils::connection_prep::{check_for_eth, create_rpc_connections}; -use std::{ - process::exit, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use tokio::time::sleep as delay_for; use web30::types::SendTxOption; pub async fn deploy_erc20_representation( args: DeployErc20RepresentationOpts, address_prefix: String, -) { +) -> Result<()> { let grpc_url = args.cosmos_grpc; let ethereum_rpc = args.ethereum_rpc; let ethereum_key = args.ethereum_key; @@ -26,7 +24,7 @@ pub async fn deploy_erc20_representation( let mut grpc = connections.grpc.unwrap(); let ethereum_public_key = ethereum_key.to_public_key().unwrap(); - check_for_eth(ethereum_public_key, &web3).await; + check_for_eth(ethereum_public_key, &web3).await?; let contract_address = if let Some(c) = args.gravity_contract_address { c @@ -35,7 +33,9 @@ pub async fn deploy_erc20_representation( let c = params.bridge_ethereum_address.parse(); if c.is_err() { error!("The Gravity address is not yet set as a chain parameter! You must specify --gravity-contract-address"); - exit(1); + return Err(anyhow!( + "The Gravity address is not yet set as a chain parameter! You must specify --gravity-contract-address" + )); } c.unwrap() }; @@ -46,12 +46,13 @@ pub async fn deploy_erc20_representation( }) .await; if let Ok(val) = res { - info!( + let erc20 = val.into_inner().erc20; + info!("Asset {} already has ERC20 representation {}", denom, erc20); + return Err(anyhow!( "Asset {} already has ERC20 representation {}", denom, - val.into_inner().erc20 - ); - exit(1); + erc20 + )); } info!("Starting deploy of ERC20"); @@ -85,12 +86,14 @@ pub async fn deploy_erc20_representation( denom, val.into_inner().erc20 ); - exit(0); + return Ok(()); } if Instant::now() - start > Duration::from_secs(100) { info!("Your ERC20 contract was not adopted, double check the metadata and try again"); - exit(1); + return Err(anyhow!( + "Your ERC20 contract was not adopted, double check the metadata and try again" + )); } delay_for(Duration::from_secs(1)).await; } diff --git a/orchestrator/gbt/src/client/eth_to_cosmos.rs b/orchestrator/gbt/src/client/eth_to_cosmos.rs index 64e7c79ed..dbb8e13ba 100644 --- a/orchestrator/gbt/src/client/eth_to_cosmos.rs +++ b/orchestrator/gbt/src/client/eth_to_cosmos.rs @@ -1,13 +1,12 @@ -use std::process::exit; - use crate::args::EthToCosmosOpts; use crate::utils::fraction_to_exponent; use crate::utils::TIMEOUT; +use anyhow::{anyhow, Result}; use ethereum_gravity::send_to_cosmos::send_to_cosmos; use ethereum_gravity::utils::get_valset_nonce; use gravity_utils::connection_prep::{check_for_eth, create_rpc_connections}; -pub async fn eth_to_cosmos(args: EthToCosmosOpts, prefix: String) { +pub async fn eth_to_cosmos(args: EthToCosmosOpts, prefix: String) -> Result<()> { let gravity_address = args.gravity_contract_address; let erc20_address = args.token_contract_address; let cosmos_dest = args.destination; @@ -24,7 +23,7 @@ pub async fn eth_to_cosmos(args: EthToCosmosOpts, prefix: String) { .await .expect("Incorrect Gravity Address or otherwise unable to contact Gravity"); - check_for_eth(ethereum_public_key, &web3).await; + check_for_eth(ethereum_public_key, &web3).await?; let res = web3 .get_erc20_decimals(erc20_address, ethereum_public_key) @@ -43,10 +42,10 @@ pub async fn eth_to_cosmos(args: EthToCosmosOpts, prefix: String) { "You have zero {} tokens, please double check your sender and erc20 addresses!", erc20_address ); - exit(1); + return Err(anyhow!("You have zero {} tokens", erc20_address)); } else if amount.clone() > erc20_balance { error!("Insufficient balance {} > {}", amount, erc20_balance); - exit(1); + return Err(anyhow!("Insufficient balance")); } info!( @@ -69,4 +68,6 @@ pub async fn eth_to_cosmos(args: EthToCosmosOpts, prefix: String) { Ok(tx_id) => info!("Send to Cosmos txid: {:#066x}", tx_id), Err(e) => info!("Failed to send tokens! {:?}", e), } + + Ok(()) } diff --git a/orchestrator/gbt/src/keys/register_orchestrator_address.rs b/orchestrator/gbt/src/keys/register_orchestrator_address.rs index 0c03b325e..73aedd812 100644 --- a/orchestrator/gbt/src/keys/register_orchestrator_address.rs +++ b/orchestrator/gbt/src/keys/register_orchestrator_address.rs @@ -7,18 +7,20 @@ use crate::config::load_keys; use crate::config::save_keys; use crate::config::KeyStorage; use crate::utils::TIMEOUT; +use anyhow::Result; use clarity::PrivateKey as EthPrivateKey; use cosmos_gravity::send::set_gravity_delegate_addresses; use deep_space::{mnemonic::Mnemonic, private_key::PrivateKey as CosmosPrivateKey}; use gravity_utils::connection_prep::check_for_fee; use gravity_utils::connection_prep::{create_rpc_connections, wait_for_cosmos_node_ready}; +use gravity_utils::error::ValidityCheckError; use rand::{thread_rng, Rng}; pub async fn register_orchestrator_address( args: RegisterOrchestratorAddressOpts, prefix: String, home_dir: PathBuf, -) { +) -> Result<()> { let fee = args.fees; let cosmos_grpc = args.cosmos_grpc; let validator_key = args.validator_phrase; @@ -28,7 +30,7 @@ pub async fn register_orchestrator_address( if !args.no_save && !config_exists(&home_dir) { error!("Please run `gbt init` before running this command!"); - exit(1); + return Err(ValidityCheckError::ConfigDoesNotExist.into()); } let connections = create_rpc_connections(prefix, Some(cosmos_grpc), None, TIMEOUT).await; @@ -36,7 +38,7 @@ pub async fn register_orchestrator_address( wait_for_cosmos_node_ready(&contact).await; let validator_addr = validator_key.to_address(&contact.get_prefix()).unwrap(); - check_for_fee(&fee, validator_addr, &contact).await; + check_for_fee(&fee, validator_addr, &contact).await?; // Set the cosmos key to either the cli value, the value in the config, or a generated // value if the config has not been setup @@ -135,4 +137,6 @@ pub async fn register_orchestrator_address( }; save_keys(&home_dir, new_keys); } + + Ok(()) } diff --git a/orchestrator/gbt/src/main.rs b/orchestrator/gbt/src/main.rs index 8d2e59d7c..56794fb9a 100644 --- a/orchestrator/gbt/src/main.rs +++ b/orchestrator/gbt/src/main.rs @@ -17,6 +17,7 @@ use env_logger::Env; use keys::register_orchestrator_address::register_orchestrator_address; use keys::set_eth_key; use keys::set_orchestrator_key; +use std::process::exit; mod args; mod client; @@ -41,11 +42,12 @@ async fn main() { let config = load_config(&home_dir); // control flow for the command structure - match opts.subcmd { + if let Err(_) = match opts.subcmd { SubCommand::Client(client_opts) => match client_opts.subcmd { ClientSubcommand::EthToCosmos(eth_to_cosmos_opts) => { eth_to_cosmos(eth_to_cosmos_opts, address_prefix).await } + ClientSubcommand::CosmosToEth(cosmos_to_eth_opts) => { cosmos_to_eth(cosmos_to_eth_opts, address_prefix).await } @@ -62,12 +64,17 @@ async fn main() { ) .await } - KeysSubcommand::Show => show_keys(&home_dir, &address_prefix), + KeysSubcommand::Show => { + show_keys(&home_dir, &address_prefix); + Ok(()) + } KeysSubcommand::SetEthereumKey(set_eth_key_opts) => { - set_eth_key(&home_dir, set_eth_key_opts) + set_eth_key(&home_dir, set_eth_key_opts); + Ok(()) } KeysSubcommand::SetOrchestratorKey(set_orch_key_opts) => { - set_orchestrator_key(&home_dir, set_orch_key_opts) + set_orchestrator_key(&home_dir, set_orch_key_opts); + Ok(()) } }, SubCommand::Orchestrator(orchestrator_opts) => { @@ -76,6 +83,11 @@ async fn main() { SubCommand::Relayer(relayer_opts) => { relayer(relayer_opts, address_prefix, &home_dir, &config.relayer).await } - SubCommand::Init(init_opts) => init_config(init_opts, home_dir), + SubCommand::Init(init_opts) => { + init_config(init_opts, home_dir); + Ok(()) + } + } { + exit(1); } } diff --git a/orchestrator/gbt/src/orchestrator.rs b/orchestrator/gbt/src/orchestrator.rs index 7ef024db4..894a58435 100644 --- a/orchestrator/gbt/src/orchestrator.rs +++ b/orchestrator/gbt/src/orchestrator.rs @@ -1,26 +1,27 @@ use crate::args::OrchestratorOpts; use crate::config::config_exists; use crate::config::load_keys; +use anyhow::Result; use cosmos_gravity::query::get_gravity_params; use deep_space::PrivateKey as CosmosPrivateKey; use gravity_utils::connection_prep::{ check_delegate_addresses, check_for_eth, wait_for_cosmos_node_ready, }; use gravity_utils::connection_prep::{check_for_fee, create_rpc_connections}; +use gravity_utils::error::ValidityCheckError; use gravity_utils::types::GravityBridgeToolsConfig; use orchestrator::main_loop::orchestrator_main_loop; use 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; use std::path::Path; -use std::process::exit; pub async fn orchestrator( args: OrchestratorOpts, address_prefix: String, home_dir: &Path, config: GravityBridgeToolsConfig, -) { +) -> Result<()> { let fee = args.fees; let cosmos_grpc = args.cosmos_grpc; let ethereum_rpc = args.ethereum_rpc; @@ -42,7 +43,7 @@ pub async fn orchestrator( error!("To generate, register, and store a key use `gbt keys register-orchestrator-address`"); error!("Store an already registered key using 'gbt keys set-orchestrator-key`"); error!("To run from the command line, with no key storage use 'gbt orchestrator --cosmos-phrase your phrase' "); - exit(1); + return Err(ValidityCheckError::CosmosPhraseNotSpecified.into()); } k.unwrap() }; @@ -61,7 +62,7 @@ pub async fn orchestrator( error!("To generate, register, and store a key use `gbt keys register-orchestrator-address`"); error!("Store an already registered key using 'gbt keys set-ethereum-key`"); error!("To run from the command line, with no key storage use 'gbt orchestrator --ethereum-key your key' "); - exit(1); + return Err(ValidityCheckError::EthereumKeyNotSpecified.into()); } k.unwrap() }; @@ -107,11 +108,11 @@ pub async fn orchestrator( public_cosmos_key, &contact.get_prefix(), ) - .await; + .await?; // check if we actually have the promised balance of tokens to pay fees - check_for_fee(&fee, public_cosmos_key, &contact).await; - check_for_eth(public_eth_key, &web3).await; + check_for_fee(&fee, public_cosmos_key, &contact).await?; + check_for_eth(public_eth_key, &web3).await?; // get the gravity contract address, if not provided let contract_address = if let Some(c) = args.gravity_contract_address { @@ -121,7 +122,7 @@ pub async fn orchestrator( let c = params.bridge_ethereum_address.parse(); if c.is_err() { error!("The Gravity address is not yet set as a chain parameter! You must specify --gravity-contract-address"); - exit(1); + return Err(ValidityCheckError::GravityAddressNotYetSet.into()); } c.unwrap() }; @@ -137,4 +138,6 @@ pub async fn orchestrator( config, ) .await; + + Ok(()) } diff --git a/orchestrator/gbt/src/relayer.rs b/orchestrator/gbt/src/relayer.rs index abd2688a1..395c9c1dd 100644 --- a/orchestrator/gbt/src/relayer.rs +++ b/orchestrator/gbt/src/relayer.rs @@ -1,22 +1,23 @@ use crate::args::RelayerOpts; use crate::config::config_exists; use crate::config::load_keys; +use anyhow::Result; use cosmos_gravity::query::get_gravity_params; use gravity_utils::connection_prep::{ check_for_eth, create_rpc_connections, wait_for_cosmos_node_ready, }; +use gravity_utils::error::ValidityCheckError; use gravity_utils::types::RelayerConfig; use relayer::main_loop::relayer_main_loop; use relayer::main_loop::LOOP_SPEED; use std::path::Path; -use std::process::exit; pub async fn relayer( args: RelayerOpts, address_prefix: String, home_dir: &Path, config: &RelayerConfig, -) { +) -> Result<()> { let cosmos_grpc = args.cosmos_grpc; let ethereum_rpc = args.ethereum_rpc; let ethereum_key = args.ethereum_key; @@ -43,7 +44,7 @@ pub async fn relayer( error!("To generate, register, and store a key use `gbt keys register-orchestrator-address`"); error!("Store an already registered key using 'gbt keys set-ethereum-key`"); error!("To run from the command line, with no key storage use 'gbt orchestrator --ethereum-key your key' "); - exit(1); + return Err(ValidityCheckError::EthereumKeyNotSpecified.into()); } k.unwrap() }; @@ -62,7 +63,7 @@ pub async fn relayer( // 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_for_eth(public_eth_key, &web3).await; + check_for_eth(public_eth_key, &web3).await?; // get the gravity contract address, if not provided let contract_address = if let Some(c) = args.gravity_contract_address { @@ -72,10 +73,12 @@ pub async fn relayer( let c = params.bridge_ethereum_address.parse(); if c.is_err() { error!("The Gravity address is not yet set as a chain parameter! You must specify --gravity-contract-address"); - exit(1); + return Err(ValidityCheckError::GravityAddressNotYetSet.into()); } c.unwrap() }; - relayer_main_loop(ethereum_key, web3, grpc, contract_address, config).await + relayer_main_loop(ethereum_key, web3, grpc, contract_address, config).await; + + Ok(()) } diff --git a/orchestrator/gravity_utils/Cargo.toml b/orchestrator/gravity_utils/Cargo.toml index a78d03e15..30e045df4 100644 --- a/orchestrator/gravity_utils/Cargo.toml +++ b/orchestrator/gravity_utils/Cargo.toml @@ -1,26 +1,28 @@ [package] -name = "gravity_utils" -version = "0.1.0" authors = ["Justin Kilpatrick "] edition = "2018" +name = "gravity_utils" +version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] gravity_proto = {path = "../gravity_proto/"} -deep_space = "2.4" -web30 = "0.15" clarity = "0.4" +deep_space = "2.4" +log = "0.4" +num-bigint = "0.4" num256 = "0.3" -serde_derive = "1.0" serde = "1.0" +serde_derive = "1.0" +sha3 = "0.9" +thiserror = "1.0.30" tokio = "1.4" tonic = "0.4" -num-bigint = "0.4" -log = "0.4" url = "2" -sha3 = "0.9" +web30 = "0.15" + [dev_dependencies] +actix = "0.12" rand = "0.8" -actix = "0.12" \ No newline at end of file diff --git a/orchestrator/gravity_utils/src/connection_prep.rs b/orchestrator/gravity_utils/src/connection_prep.rs index c8d15d3f9..3cba0a2e2 100644 --- a/orchestrator/gravity_utils/src/connection_prep.rs +++ b/orchestrator/gravity_utils/src/connection_prep.rs @@ -2,6 +2,9 @@ //! It's a common problem to have conflicts between ipv4 and ipv6 localhost and this module is first and foremost supposed to resolve that problem //! by trying more than one thing to handle potentially misconfigured inputs. +use crate::error::ValidityCheckError; +use crate::get_with_retry::get_balances_with_retry; +use crate::get_with_retry::get_eth_balances_with_retry; use clarity::Address as EthAddress; use deep_space::Address as CosmosAddress; use deep_space::Contact; @@ -9,16 +12,12 @@ use deep_space::{client::ChainStatus, Coin}; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; use gravity_proto::gravity::QueryDelegateKeysByEthAddress; use gravity_proto::gravity::QueryDelegateKeysByOrchestratorAddress; -use std::process::exit; use std::time::Duration; use tokio::time::sleep as delay_for; use tonic::transport::Channel; use url::Url; use web30::client::Web3; -use crate::get_with_retry::get_balances_with_retry; -use crate::get_with_retry::get_eth_balances_with_retry; - pub struct Connections { pub web3: Option, pub grpc: Option>, @@ -237,7 +236,7 @@ pub async fn check_delegate_addresses( delegate_eth_address: EthAddress, delegate_orchestrator_address: CosmosAddress, prefix: &str, -) { +) -> Result<(), ValidityCheckError> { let eth_response = client .get_delegate_key_by_eth(QueryDelegateKeysByEthAddress { eth_address: delegate_eth_address.to_string(), @@ -269,7 +268,12 @@ pub async fn check_delegate_addresses( delegate_orchestrator_address, req_delegate_orchestrator_address ); error!("In order to resolve this issue you should double check your input value or re-register your delegate keys"); - exit(1); + Err(ValidityCheckError::EthreumOrchestratorAddressesIncorrect { + delegate_eth_address, + req_delegate_eth_address, + delegate_orchestrator_address, + req_delegate_orchestrator_address, + }) } else if req_delegate_eth_address != delegate_eth_address { error!("Your Delegate Ethereum address is incorrect!"); error!( @@ -277,60 +281,77 @@ pub async fn check_delegate_addresses( delegate_eth_address, req_delegate_eth_address ); error!("In order to resolve this issue you should double check how you input your eth private key"); - exit(1); + Err(ValidityCheckError::EthereumAddressIncorrect { + delegate_eth_address, + req_delegate_eth_address, + }) } else if req_delegate_orchestrator_address != delegate_orchestrator_address { error!("Your Delegate Orchestrator address is incorrect!"); error!( "You provided {} Correct Value {}", - delegate_eth_address, req_delegate_eth_address + delegate_orchestrator_address, req_delegate_orchestrator_address ); error!("In order to resolve this issue you should double check how you input your Orchestrator address phrase, make sure you didn't use your Validator phrase!"); - exit(1); - } - - if e.validator_address != o.validator_address { + Err(ValidityCheckError::OrchestratorAddressIncorrect { + delegate_orchestrator_address, + req_delegate_orchestrator_address, + }) + } else if e.validator_address != o.validator_address { error!("You are using delegate keys from two different validator addresses!"); error!("If you get this error message I would just blow everything away and start again"); - exit(1); + Err(ValidityCheckError::DifferentValidtatorAddresses) + } else { + Ok(()) } } (Err(e), Ok(_)) => { error!("Your delegate Ethereum address is incorrect, please double check you private key. If you can't locate the correct private key register your delegate keys again and use the new value {:?}", e); - exit(1); + Err(ValidityCheckError::EthereumAddressPrivateKeyIncorrect(e)) } (Ok(_), Err(e)) => { error!("Your delegate Cosmos address is incorrect, please double check your phrase. If you can't locate the correct phrase register your delegate keys again and use the new value {:?}", e); - exit(1); + Err(ValidityCheckError::OrchestratorAddressPharseIncorrect(e)) } (Err(_), Err(_)) => { error!("Delegate keys are not set! Please Register your delegate keys"); - exit(1); + Err(ValidityCheckError::DelegateKeysNotSet) } } } /// Checks if a given Coin, used for fees is in the provided address in a sufficient quantity -pub async fn check_for_fee(fee: &Coin, address: CosmosAddress, contact: &Contact) { +pub async fn check_for_fee( + fee: &Coin, + address: CosmosAddress, + contact: &Contact, +) -> Result<(), ValidityCheckError> { let balances = get_balances_with_retry(address, contact).await; + for balance in balances { if balance.denom.contains(&fee.denom) { if balance.amount < fee.amount { error!("You have specified a fee that is greater than your balance of that coin! {}{} > {}{} ", fee.amount, fee.denom, balance.amount, balance.denom); - exit(1); + return Err(ValidityCheckError::FeeGreaterThanBalance); } else { - return; + return Ok(()); } } } + error!("You have specified that fees should be paid in {} but account {} has no balance of that token!", fee.denom, address); - exit(1); + Err(ValidityCheckError::NoBalanceForSpecifiedToken { + denom: fee.denom.clone(), + address, + }) } /// Checks the user has some Ethereum in their address to pay for things -pub async fn check_for_eth(address: EthAddress, web3: &Web3) { +pub async fn check_for_eth(address: EthAddress, web3: &Web3) -> Result<(), ValidityCheckError> { let balance = get_eth_balances_with_retry(address, web3).await; if balance == 0u8.into() { error!("You don't have any Ethereum! You will need to send some to {} for this program to work. Dust will do for basic operations, more info about average relaying costs will be presented as the program runs", address); - exit(1); + Err(ValidityCheckError::EthereumBalanceZero) + } else { + Ok(()) } } diff --git a/orchestrator/gravity_utils/src/error.rs b/orchestrator/gravity_utils/src/error.rs index 64d82addc..025592cb8 100644 --- a/orchestrator/gravity_utils/src/error.rs +++ b/orchestrator/gravity_utils/src/error.rs @@ -1,67 +1,98 @@ //! for things that don't belong in the cosmos or ethereum libraries but also don't belong //! in a function specific library +use clarity::Address; use clarity::Error as ClarityError; use deep_space::error::AddressError as CosmosAddressError; use deep_space::error::CosmosGrpcError; +use deep_space::Address as CosmosAddress; use num_bigint::ParseBigIntError; -use std::fmt::{self, Debug}; +use std::fmt::Debug; +use thiserror::Error; use tokio::time::error::Elapsed; use tonic::Status; use web30::jsonrpc::error::Web3Error; -#[derive(Debug)] +#[derive(Error, Debug)] +#[allow(clippy::large_enum_variant)] +pub enum ValidityCheckError { + #[error("Your Delegate Ethereum and Orchestrator addresses are both incorrect!")] + EthreumOrchestratorAddressesIncorrect { + delegate_eth_address: Address, + req_delegate_eth_address: Address, + delegate_orchestrator_address: CosmosAddress, + req_delegate_orchestrator_address: CosmosAddress, + }, + #[error("Your Delegate Ethereum address is incorrect!")] + EthereumAddressIncorrect { + delegate_eth_address: Address, + req_delegate_eth_address: Address, + }, + #[error("Your Delegate Orchestrator address is incorrect!")] + OrchestratorAddressIncorrect { + delegate_orchestrator_address: CosmosAddress, + req_delegate_orchestrator_address: CosmosAddress, + }, + #[error("You are using delegate keys from two different validator addresses!")] + DifferentValidtatorAddresses, + #[error("Your delegate Ethereum address is incorrect, please double check you private key")] + EthereumAddressPrivateKeyIncorrect(Status), + #[error("Your delegate Cosmos address is incorrect, please double check your phrase")] + OrchestratorAddressPharseIncorrect(Status), + #[error("Delegate keys are not set!")] + DelegateKeysNotSet, + #[error("You have specified a fee that is greater than your balance of that coin!")] + FeeGreaterThanBalance, + #[error("You have specified that fees should be paid in {denom} but account {address} has no balance of that token!")] + NoBalanceForSpecifiedToken { + denom: String, + address: CosmosAddress, + }, + #[error("You don't have any Ethereum!")] + EthereumBalanceZero, + #[error("You must specify an Ethereum key!")] + EthereumKeyNotSpecified, + #[error("You must specify a Cosmos key phrase!")] + CosmosPhraseNotSpecified, + #[error("The Gravity address is not yet set as a chain parameter!")] + GravityAddressNotYetSet, + #[error("Please run `gbt init` before running this command!")] + ConfigDoesNotExist, +} + +#[derive(Error, Debug)] #[allow(clippy::large_enum_variant)] pub enum GravityError { + #[error("Got invalid BigInt from cosmos! {0}")] InvalidBigInt(ParseBigIntError), + #[error("Cosmos gRPC error {0}")] CosmosGrpcError(CosmosGrpcError), + #[error("Cosmos Address error {0}")] CosmosAddressError(CosmosAddressError), + #[error("Ethereum REST error {0}")] EthereumRestError(Web3Error), + #[error("Invalid bridge state! {0}")] InvalidBridgeStateError(String), + #[error("ValidatorSetUpdate Failed!")] FailedToUpdateValset, + #[error("Contract operation failed: {0}")] EthereumContractError(String), + #[error("Invalid TX options for this call {0}")] InvalidOptionsError(String), + #[error("Clarity Error {0}")] ClarityError(ClarityError), + #[error("Operation timed out!")] TimeoutError, + #[error("InvalidEvent: {0}")] InvalidEventLogError(String), + #[error("Gravity gRPC error {0}")] GravityGrpcError(Status), + #[error("{0}")] InsufficientVotingPowerToPass(String), + #[error("Failed to parse big integer {0}")] ParseBigIntError(ParseBigIntError), } -impl fmt::Display for GravityError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - GravityError::GravityGrpcError(val) => write!(f, "Gravity gRPC error {}", val), - GravityError::CosmosGrpcError(val) => write!(f, "Cosmos gRPC error {}", val), - GravityError::InvalidBigInt(val) => { - write!(f, "Got invalid BigInt from cosmos! {}", val) - } - GravityError::CosmosAddressError(val) => write!(f, "Cosmos Address error {}", val), - GravityError::EthereumRestError(val) => write!(f, "Ethereum REST error {}", val), - GravityError::InvalidOptionsError(val) => { - write!(f, "Invalid TX options for this call {}", val) - } - GravityError::InvalidBridgeStateError(val) => { - write!(f, "Invalid bridge state! {}", val) - } - GravityError::FailedToUpdateValset => write!(f, "ValidatorSetUpdate Failed!"), - GravityError::TimeoutError => write!(f, "Operation timed out!"), - GravityError::ClarityError(val) => write!(f, "Clarity Error {}", val), - GravityError::InvalidEventLogError(val) => write!(f, "InvalidEvent: {}", val), - GravityError::EthereumContractError(val) => { - write!(f, "Contract operation failed: {}", val) - } - GravityError::InsufficientVotingPowerToPass(val) => { - write!(f, "{}", val) - } - GravityError::ParseBigIntError(val) => write!(f, "Failed to parse big integer {}", val), - } - } -} - -impl std::error::Error for GravityError {} - impl From for GravityError { fn from(error: CosmosGrpcError) -> Self { GravityError::CosmosGrpcError(error)