diff --git a/colony-chain/Cargo.toml b/colony-chain/Cargo.toml index 00bb41d..735a69f 100644 --- a/colony-chain/Cargo.toml +++ b/colony-chain/Cargo.toml @@ -13,6 +13,10 @@ rust_decimal = "1.25.0" rust_decimal_macros = "1.25.0" pdao-colony-common = { version = "0.0.0" } pdao-colony-contract-common = { version = "0.0.0" } +pdao-cosmos-interact = { path = "../interact"} +pdao-cosmos-light-client = {path = "../contracts/light-client"} serde-tc = "0.3.3" serde_json = { version = "1.0" } anyhow = { version = "1.0" } +cosmwasm-std = "1.0.0" +base64 = "0.13.0" \ No newline at end of file diff --git a/colony-chain/src/lib.rs b/colony-chain/src/lib.rs index 06594e3..d7c4b2d 100644 --- a/colony-chain/src/lib.rs +++ b/colony-chain/src/lib.rs @@ -1,11 +1,26 @@ use async_trait::async_trait; use pdao_colony_common::*; use pdao_colony_contract_common::*; +use pdao_cosmos_interact::{execute, query}; use rust_decimal::prelude::*; use rust_decimal_macros::dec; + +use serde_json::json; use std::collections::HashMap; -pub struct Juno {} +pub struct Juno { + pub full_node_url: String, + pub rpc_url: String, + pub treasury_address: String, + pub lightclient_address: String, + pub relayer_account: String, + + pub chain_id: String, + pub denom: String, + pub mnemonic: String, + pub password: String, + pub account_prefix: String, +} #[async_trait] impl ColonyChain for Juno { @@ -14,25 +29,34 @@ impl ColonyChain for Juno { } async fn get_last_block(&self) -> Result { - Ok(Block { - height: 0, - timestamp: 0, - }) + let height = query::get_latest_block_height(&self.rpc_url) + .await + .map_err(|_| Error::ConnectionError("test".to_string()))?; //TODO: error message + + let timestamp = query::get_latest_block_timestamp(&self.rpc_url) + .await + .map_err(|_| Error::ConnectionError("test".to_string()))?; //TODO: error message + + Ok(Block { height, timestamp }) } async fn check_connection(&self) -> Result<(), Error> { + let _height = query::get_latest_block_height(&self.rpc_url) + .await + .map_err(|_| Error::ConnectionError("test".to_string()))?; //TODO: error message + Ok(()) } async fn get_contract_list(&self) -> Result, Error> { Ok(vec![ ContractInfo { - address: "0xabcd".to_owned(), + address: self.lightclient_address.clone(), contract_type: ContractType::LightClient, sequence: 0, }, ContractInfo { - address: "0x1234".to_owned(), + address: self.treasury_address.clone(), contract_type: ContractType::Treasury, sequence: 0, }, @@ -40,11 +64,37 @@ impl ColonyChain for Juno { } async fn get_relayer_account_info(&self) -> Result<(String, Decimal), Error> { - Ok(("0x12341234".to_owned(), dec!(12.34))) + let res = query::get_balance_amount(&self.full_node_url, &self.relayer_account) + .await + .map_err(|_| Error::ConnectionError("test".to_string()))?; //TODO: error message + + let balance = Decimal::from_str(res.as_ref()) + .map_err(|_| Error::ConnectionError("test".to_string()))?; //TODO: error message + + Ok((self.relayer_account.to_owned(), balance)) } async fn get_light_client_header(&self) -> Result { - Ok("Hmm".to_owned()) + let msg = json!({ + "get_header": {} + }); + + let encode_msg = base64::encode( + &serde_json::to_vec(&msg) + .map_err(|_| Error::InvalidMessageArgument("test".to_string()))?, + ); //TODO: error message + + let response = query::send_query( + &self.full_node_url, + &self.lightclient_address, + encode_msg.as_str(), + ) + .await + .map_err(|_| Error::ConnectionError("test".to_string()))?; //TODO: error message + + let header = response["data"]["header"].to_string(); //TODO: current type of header is string + + Ok(header) } async fn get_treasury_fungible_token_balance(&self) -> Result, Error> { @@ -66,7 +116,33 @@ impl ColonyChain for Juno { } async fn update_light_client(&self, _message: LightClientUpdateMessage) -> Result<(), Error> { - Ok(()) + let msg = json!({ + "update": { + "header": _message.header, + "proof": _message.proof + } + }); + + let execute_msg = + serde_json::to_vec(&msg).map_err(|_| Error::ConnectionError("test".to_string()))?; //TODO: error type and message + + execute::send_execute( + self.mnemonic.clone(), + &self.password, + &self.chain_id, + &self.rpc_url, + &self.full_node_url, + &self.denom, + &self.account_prefix, + 10000, //TODO: fix + &self.lightclient_address, + execute_msg, + 2000000, //TODO: fix + 2000000, //TODO: fix + None, + ) + .await + .map_err(|_| pdao_colony_common::Error::TransactionRejected("test".to_string())) } async fn transfer_treasury_fungible_token( diff --git a/colony-chain/src/main.rs b/colony-chain/src/main.rs index 5e2d44c..19584a4 100644 --- a/colony-chain/src/main.rs +++ b/colony-chain/src/main.rs @@ -14,7 +14,18 @@ async fn main() { port, vec![( "juno".to_owned(), - serde_tc::http::create_http_object(Arc::new(Juno {}) as Arc), + serde_tc::http::create_http_object(Arc::new(Juno { + full_node_url: "https://lcd.uni.juno.deuslabs.fi".to_string(), + rpc_url: "https://rpc.uni.juno.deuslabs.fi".to_string(), + treasury_address: "".to_string(), + lightclient_address: "".to_string(), + relayer_account: "".to_string(), + chain_id: "".to_string(), + denom: "".to_string(), + mnemonic: "".to_string(), + password: "".to_string(), + account_prefix: "".to_string(), + }) as Arc), )] .into_iter() .collect(), diff --git a/interact/src/execute.rs b/interact/src/execute.rs index aeb1be8..fb3fbe1 100644 --- a/interact/src/execute.rs +++ b/interact/src/execute.rs @@ -1,16 +1,17 @@ use cosmrs::{ - crypto, dev, rpc, + dev, rpc, tx::{self, Fee, Msg, SignDoc, SignerInfo}, Coin, }; use super::query::{get_account_number, get_sequence_number}; -use super::utils::private_to_pub_and_account; +use super::utils::{mnemonic_to_private_key, private_to_pub_and_account}; use std::error::Error; #[allow(clippy::too_many_arguments)] pub async fn send_execute( - sender_private_key: &crypto::secp256k1::SigningKey, + mnemonic: String, + password: &str, chain_id: &str, rpc_address: &str, api_address: &str, @@ -23,8 +24,12 @@ pub async fn send_execute( gas_limit: u64, tx_memo: Option<&str>, ) -> Result<(), Box> { - let (sender_public_key, sender_account_id) = - private_to_pub_and_account(sender_private_key, account_id)?; + let (sender_public_key, sender_account_id) = { + let sender_private_key = mnemonic_to_private_key(mnemonic.clone(), password) + .unwrap() + .into(); + private_to_pub_and_account(&sender_private_key, account_id)? + }; let funds = Coin { amount: funds.into(), @@ -54,7 +59,11 @@ pub async fn send_execute( SignerInfo::single_direct(Some(sender_public_key), sequence_number).auth_info(fee); let account_number = get_account_number(api_address, sender_account_id.as_ref()).await?; let sign_doc = SignDoc::new(&tx_body, &auth_info, &chain_id, account_number)?; - let tx_raw = sign_doc.sign(sender_private_key)?; + + let tx_raw = { + let sender_private_key = mnemonic_to_private_key(mnemonic, password).unwrap().into(); + sign_doc.sign(&sender_private_key)? + }; let rpc_address = rpc_address.to_owned(); let rpc_client = rpc::HttpClient::new(rpc_address.as_str())?; diff --git a/interact/src/query.rs b/interact/src/query.rs index b04e14b..4bf4553 100644 --- a/interact/src/query.rs +++ b/interact/src/query.rs @@ -22,6 +22,55 @@ pub async fn send_query( Ok(result) } +pub async fn get_balance_amount( + rest_api_endpoint: &str, + account_address: &str, +) -> Result> { + let client = reqwest::Client::new(); + let response = request( + &client, + &format!( + "{}/cosmos/bank/v1beta1/balances/{}", + rest_api_endpoint, account_address + ), + None, + ) + .await?; + + let current_balance = response["balances"] + .as_array() + .ok_or("Failed to convert balance to array")?[0]["amount"] + .to_string(); + + Ok(current_balance) +} + +pub async fn get_latest_block_height(rest_api_endpoint: &str) -> Result> { + let client = reqwest::Client::new(); + + let response = request(&client, &format!("{}/status?", rest_api_endpoint), None).await?; + + let latest_block_height = response["result"]["sync_info"]["latest_block_height"] + .as_str() + .ok_or("Failed to convert code id to &str")? + .parse::()?; + + Ok(latest_block_height) +} + +pub async fn get_latest_block_timestamp(rest_api_endpoint: &str) -> Result> { + let client = reqwest::Client::new(); + + let response = request(&client, &format!("{}/status?", rest_api_endpoint), None).await?; + + let latest_block_timestamp = response["result"]["sync_info"]["latest_block_time"] + .as_str() + .ok_or("Failed to convert code id to &str")? + .parse::()?; + + Ok(latest_block_timestamp) +} + pub async fn get_code_id( rest_api_endpoint: &str, contract_address: &str, diff --git a/interact/tests/suite1.rs b/interact/tests/suite1.rs index 5b1f6ad..281c53c 100644 --- a/interact/tests/suite1.rs +++ b/interact/tests/suite1.rs @@ -178,17 +178,14 @@ async fn test_execute_increment_fail() { ); let _config = Config::read_from_path(full_path); - let sender_private_key = mnemonic_to_private_key(_config.mnemonic, &_config.password) - .unwrap() - .into(); - // This should be failed since the count is above 10 let msg = json!({ "increment": {"count": 20u64} }); let _result = execute::send_execute( - &sender_private_key, + _config.mnemonic.clone(), + &_config.password, &_config.chain_id, &_config.rpc, &_config.full_node_url, @@ -215,16 +212,13 @@ async fn test_execute_increment() { ); let _config = Config::read_from_path(full_path); - let sender_private_key = mnemonic_to_private_key(_config.mnemonic, &_config.password) - .unwrap() - .into(); - let msg = json!({ "increment": {"count": 5u64} }); let _result = execute::send_execute( - &sender_private_key, + _config.mnemonic.clone(), + &_config.password, &_config.chain_id, &_config.rpc, &_config.full_node_url, @@ -251,16 +245,13 @@ async fn test_execute_reset() { ); let _config = Config::read_from_path(full_path); - let sender_private_key = mnemonic_to_private_key(_config.mnemonic, &_config.password) - .unwrap() - .into(); - let msg = json!({ "reset": {"count": 50u64} }); let _result = execute::send_execute( - &sender_private_key, + _config.mnemonic.clone(), + &_config.password, &_config.chain_id, &_config.rpc, &_config.full_node_url,