From dd1bc0dc32ccde2576aa38193d3adabd0219f763 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Wed, 28 Sep 2022 17:31:56 +0300 Subject: [PATCH 1/6] ignore node's double spending error on data point publishing #121 handle error on scan registration and remove redundant scan registration check; --- core/src/actions.rs | 17 +++++++++--- core/src/oracle_state.rs | 58 ++++++++++++++++------------------------ 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/core/src/actions.rs b/core/src/actions.rs index dbca3fae..b9ad2754 100644 --- a/core/src/actions.rs +++ b/core/src/actions.rs @@ -50,11 +50,11 @@ pub enum ActionExecError { pub fn execute_action(action: PoolAction) -> Result<(), ActionExecError> { match action { PoolAction::Refresh(action) => { - log::info!("Executing refresh action: {:?}", action); + log::debug!("Executing refresh action: {:?}", action); execute_refresh_action(action) } PoolAction::PublishDatapoint(action) => { - log::info!("Executing publish datapoint action: {:?}", action); + log::debug!("Executing publish datapoint action: {:?}", action); execute_publish_datapoint_action(action) } } @@ -62,11 +62,22 @@ pub fn execute_action(action: PoolAction) -> Result<(), ActionExecError> { fn execute_refresh_action(action: RefreshAction) -> Result<(), ActionExecError> { let _tx_id = sign_and_submit_transaction(&action.tx)?; + log::info!("Refresh action executed successfully"); Ok(()) } fn execute_publish_datapoint_action(action: PublishDataPointAction) -> Result<(), ActionExecError> { - let _tx_id = sign_and_submit_transaction(&action.tx)?; + match sign_and_submit_transaction(&action.tx) { + Ok(tx_id) => { + log::info!("Datapoint published successfully, tx id = {}", tx_id); + } + Err(NodeError::BadRequest(msg)) if msg.as_str() == "Double spending attempt" => { + log::debug!("Ignoring node returned double spending attempt error (probably due to our previous data point tx is still in the mempool)"); + } + Err(e) => { + return Err(ActionExecError::NodeError(e)); + } + }; Ok(()) } diff --git a/core/src/oracle_state.rs b/core/src/oracle_state.rs index 00fd0909..1b932e05 100644 --- a/core/src/oracle_state.rs +++ b/core/src/oracle_state.rs @@ -255,7 +255,7 @@ impl<'a> OraclePool<'a> { data_point_source, datapoint_stage: DatapointStage { stage: Stage { - contract_address: datapoint_contract.to_base16_bytes().unwrap(), + contract_address: datapoint_contract.to_base16_bytes()?, scan: datapoint_scan, }, oracle_box_wrapper_inputs: &config.oracle_box_wrapper_inputs, @@ -442,8 +442,8 @@ impl<'a> DatapointBoxesSource for DatapointStage<'a> { .stage .get_boxes()? .into_iter() - .map(|b| OracleBoxWrapper::new(b, self.oracle_box_wrapper_inputs).unwrap()) - .collect(); + .map(|b| OracleBoxWrapper::new(b, self.oracle_box_wrapper_inputs)) + .collect::, OracleBoxError>>()?; Ok(res) } } @@ -511,46 +511,34 @@ fn register_and_save_scans_inner() -> std::result::Result<(), Error> { OracleContract::checked_load(&config.oracle_box_wrapper_inputs.contract_inputs)? .ergo_tree(); - let mut scans = vec![ + let ballot_contract_address = + BallotContract::checked_load(&config.ballot_box_wrapper_inputs.contract_inputs)? + .ergo_tree(); + + let scans = vec![ register_datapoint_scan( &oracle_pool_participant_token_id, &datapoint_contract_address, - ) - .unwrap(), - register_update_box_scan(&config.token_ids.update_nft_token_id).unwrap(), - register_pool_box_scan(config.pool_box_wrapper_inputs.clone()).unwrap(), + )?, + register_update_box_scan(&config.token_ids.update_nft_token_id)?, + register_pool_box_scan(config.pool_box_wrapper_inputs.clone())?, register_refresh_box_scan( refresh_box_scan_name, config.refresh_box_wrapper_inputs.clone(), - ) - .unwrap(), + )?, + register_local_oracle_datapoint_scan( + &oracle_pool_participant_token_id, + &datapoint_contract_address, + &local_oracle_address, + )?, + register_local_ballot_box_scan( + &ballot_contract_address, + &config.token_ids.ballot_token_id, + &config.oracle_address, + )?, + register_ballot_box_scan(&ballot_contract_address, &config.token_ids.ballot_token_id)?, ]; - // Local datapoint box may not exist yet. - if let Ok(local_scan) = register_local_oracle_datapoint_scan( - &oracle_pool_participant_token_id, - &datapoint_contract_address, - &local_oracle_address, - ) { - scans.push(local_scan); - } - - let ballot_contract_address = - BallotContract::checked_load(&config.ballot_box_wrapper_inputs.contract_inputs)? - .ergo_tree(); - // Local ballot box may not exist yet. - if let Ok(local_scan) = register_local_ballot_box_scan( - &ballot_contract_address, - &config.token_ids.ballot_token_id, - &config.oracle_address, - ) { - scans.push(local_scan); - } - scans.push( - register_ballot_box_scan(&ballot_contract_address, &config.token_ids.ballot_token_id) - .unwrap(), - ); - log::info!("Registering UTXO-Set Scans"); save_scan_ids_locally(scans)?; log::info!("Triggering wallet rescan"); From 1397120dc67fa56d2fda5c173e090a03056d2923 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 29 Sep 2022 10:37:31 +0300 Subject: [PATCH 2/6] fix incorrect token id in saving/loading of RefreshBoxWrapperInputs #127; add more logging; --- core/src/actions.rs | 15 +------------- core/src/cli_commands/bootstrap.rs | 33 +++++++++++++++--------------- core/src/main.rs | 11 +++++++++- core/src/oracle_config.rs | 4 ++-- core/src/serde.rs | 4 ++-- 5 files changed, 32 insertions(+), 35 deletions(-) diff --git a/core/src/actions.rs b/core/src/actions.rs index b9ad2754..2fc252e3 100644 --- a/core/src/actions.rs +++ b/core/src/actions.rs @@ -3,7 +3,6 @@ /// are implemented on the `OraclePool` struct. use crate::node_interface::sign_and_submit_transaction; use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; -use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox; use derive_more::From; use ergo_node_interface::node_interface::NodeError; @@ -72,7 +71,7 @@ fn execute_publish_datapoint_action(action: PublishDataPointAction) -> Result<() log::info!("Datapoint published successfully, tx id = {}", tx_id); } Err(NodeError::BadRequest(msg)) if msg.as_str() == "Double spending attempt" => { - log::debug!("Ignoring node returned double spending attempt error (probably due to our previous data point tx is still in the mempool)"); + log::info!("Failed commiting datapoint (double spending attempt error, probably due to our previous data point tx is still in the mempool)"); } Err(e) => { return Err(ActionExecError::NodeError(e)); @@ -80,15 +79,3 @@ fn execute_publish_datapoint_action(action: PublishDataPointAction) -> Result<() }; Ok(()) } - -/// Given an `ErgoBox`, find its index in the input `Vec` -/// If index cannot be found, then local oracle has not submit their -/// own datapoint, and thus the function returns `None` -fn find_box_index_in_list( - search_box: ErgoBox, - sorted_datapoint_boxes: &Vec, -) -> Option { - sorted_datapoint_boxes - .iter() - .position(|b| b.clone() == search_box) -} diff --git a/core/src/cli_commands/bootstrap.rs b/core/src/cli_commands/bootstrap.rs index 7cbc337d..ffefe2ab 100644 --- a/core/src/cli_commands/bootstrap.rs +++ b/core/src/cli_commands/bootstrap.rs @@ -234,7 +234,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( }; // Mint pool NFT token -------------------------------------------------------------------------- - info!("Minting pool NFT tx"); + info!("Creating and signing minting pool NFT tx"); let unspent_boxes = wallet.get_unspent_wallet_boxes()?; debug!("unspent boxes: {:?}", unspent_boxes); let target_balance = calc_target_balance(num_transactions_left)?; @@ -254,7 +254,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( debug!("signed_mint_pool_nft_tx: {:?}", signed_mint_pool_nft_tx); // Mint refresh NFT token ---------------------------------------------------------------------- - info!("Minting refresh NFT tx"); + info!("Creating and signing minting refresh NFT tx"); let inputs = filter_tx_outputs(signed_mint_pool_nft_tx.outputs.clone()); debug!("inputs for refresh NFT mint: {:?}", inputs); let (refresh_nft_token, signed_mint_refresh_nft_tx) = mint_token( @@ -271,7 +271,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( ); // Mint ballot tokens -------------------------------------------------------------------------- - info!("Minting ballot tokens tx"); + info!("Creating and signing minting ballot tokens tx"); let inputs = filter_tx_outputs(signed_mint_refresh_nft_tx.outputs.clone()); debug!("inputs for ballot tokens mint: {:?}", inputs); let (ballot_token, signed_mint_ballot_tokens_tx) = mint_token( @@ -300,7 +300,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( ballot_token.token_id.clone(), )?)?; - info!("Minting update NFT tx"); + info!("Creating and signing minting update NFT tx"); let inputs = filter_tx_outputs(signed_mint_ballot_tokens_tx.outputs.clone()); debug!("inputs for update NFT mint: {:?}", inputs); let (update_nft_token, signed_mint_update_nft_tx) = mint_token( @@ -314,7 +314,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( debug!("signed_mint_update_nft_tx: {:?}", signed_mint_update_nft_tx); // Mint oracle tokens -------------------------------------------------------------------------- - info!("Minting oracle tokens tx"); + info!("Creating and signing minting oracle tokens tx"); let inputs = filter_tx_outputs(signed_mint_update_nft_tx.outputs.clone()); debug!("inputs for oracle tokens mint: {:?}", inputs); let oracle_tokens_pk_ergo_tree = config.oracle_address.address().script()?; @@ -337,7 +337,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( ); // Mint reward tokens -------------------------------------------------------------------------- - info!("Minting reward tokens tx"); + info!("Creating and signing minting reward tokens tx"); let inputs = filter_tx_outputs(signed_mint_oracle_tokens_tx.outputs.clone()); debug!("inputs for reward tokens mint: {:?}", inputs); let (reward_token, signed_mint_reward_tokens_tx) = mint_token( @@ -355,7 +355,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( )?; // Create pool box ----------------------------------------------------------------------------- - info!("Create pool box tx"); + info!("Create and sign pool box tx"); let token_ids = TokenIds { pool_nft_token_id: pool_nft_token.token_id.clone(), @@ -440,7 +440,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( num_transactions_left -= 1; // Create refresh box -------------------------------------------------------------------------- - info!("Create refresh box tx"); + info!("Create and sign refresh box tx"); let refresh_contract_inputs = RefreshContractInputs::build_with( config.refresh_contract_parameters.clone(), @@ -496,21 +496,21 @@ pub(crate) fn perform_bootstrap_chained_transaction( // --------------------------------------------------------------------------------------------- let tx_id = submit_tx.submit_transaction(&signed_mint_pool_nft_tx)?; - info!("Minting pool NFT TxId: {}", tx_id); + info!("Minted pool NFT TxId: {}", tx_id); let tx_id = submit_tx.submit_transaction(&signed_mint_refresh_nft_tx)?; - info!("Minting refresh NFT TxId: {}", tx_id); + info!("Minted refresh NFT TxId: {}", tx_id); let tx_id = submit_tx.submit_transaction(&signed_mint_ballot_tokens_tx)?; - info!("Minting ballot tokens TxId: {}", tx_id); + info!("Minted ballot tokens TxId: {}", tx_id); let tx_id = submit_tx.submit_transaction(&signed_mint_update_nft_tx)?; - info!("Minting update NFT TxId: {}", tx_id); + info!("Minted update NFT TxId: {}", tx_id); let tx_id = submit_tx.submit_transaction(&signed_mint_oracle_tokens_tx)?; - info!("Minting oracle tokens TxId: {}", tx_id); + info!("Minted oracle tokens TxId: {}", tx_id); let tx_id = submit_tx.submit_transaction(&signed_mint_reward_tokens_tx)?; - info!("Minting reward tokens TxId: {}", tx_id); + info!("Minted reward tokens TxId: {}", tx_id); let tx_id = submit_tx.submit_transaction(&signed_pool_box_tx)?; - info!("Creating initial pool box TxId: {}", tx_id); + info!("Created initial pool box TxId: {}", tx_id); let tx_id = submit_tx.submit_transaction(&signed_refresh_box_tx)?; - info!("Creating initial refresh box TxId: {}", tx_id); + info!("Created initial refresh box TxId: {}", tx_id); let token_ids = TokenIds { pool_nft_token_id: pool_nft_token.token_id, @@ -520,6 +520,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( reward_token_id: reward_token.token_id, ballot_token_id: ballot_token.token_id, }; + info!("Minted tokens: {:?}", token_ids); Ok(OracleConfig::create(config, token_ids, height)?) } diff --git a/core/src/main.rs b/core/src/main.rs index 0a6a6274..44d7506b 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -64,6 +64,7 @@ use wallet::WalletData; use crate::api::start_rest_server; use crate::default_parameters::print_contract_hashes; +use crate::oracle_config::MAYBE_ORACLE_CONFIG; /// A Base58 encoded String of a Ergo P2PK address. Using this type def until sigma-rust matures further with the actual Address type. pub type P2PKAddress = String; @@ -176,7 +177,8 @@ fn main() { None }; logging::setup_log(cmdline_log_level); - log::info!("{}", APP_VERSION); + + log_on_launch(); debug!("Args: {:?}", args); @@ -351,3 +353,10 @@ fn get_change_address_from_node() -> Result { let addr = AddressEncoder::unchecked_parse_address_from_str(&change_address_str)?; Ok(addr) } + +fn log_on_launch() { + log::info!("{}", APP_VERSION); + if let Ok(config) = MAYBE_ORACLE_CONFIG.clone() { + log::info!("Token ids: {:?}", config.token_ids); + } +} diff --git a/core/src/oracle_config.rs b/core/src/oracle_config.rs index ae937e58..6eb01e34 100644 --- a/core/src/oracle_config.rs +++ b/core/src/oracle_config.rs @@ -107,9 +107,9 @@ impl OracleConfig { )?; let refresh_box_wrapper_inputs = RefreshBoxWrapperInputs::build_with( bootstrap.refresh_contract_parameters.clone(), - token_ids.refresh_nft_token_id.clone(), token_ids.oracle_token_id.clone(), - token_ids.reward_token_id.clone(), + token_ids.pool_nft_token_id.clone(), + token_ids.refresh_nft_token_id.clone(), )?; let pool_box_wrapper_inputs = PoolBoxWrapperInputs::build_with( bootstrap.pool_contract_parameters.clone(), diff --git a/core/src/serde.rs b/core/src/serde.rs index 396f21d9..1be168ce 100644 --- a/core/src/serde.rs +++ b/core/src/serde.rs @@ -190,9 +190,9 @@ impl TryFrom for OracleConfig { let refresh_box_wrapper_inputs = RefreshBoxWrapperInputs::checked_load( refresh_contract_parameters.clone(), - c.token_ids.refresh_nft_token_id.clone(), c.token_ids.oracle_token_id.clone(), - c.token_ids.reward_token_id.clone(), + c.token_ids.pool_nft_token_id.clone(), + c.token_ids.refresh_nft_token_id.clone(), ) .map_err(OracleConfigError::from)?; From 9a22dbb86ecd487f12381c47518e13c2a6d6a970 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 29 Sep 2022 12:37:42 +0300 Subject: [PATCH 3/6] fix expected refresh contract bytes in prepare update tests; --- core/src/cli_commands/prepare_update.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/cli_commands/prepare_update.rs b/core/src/cli_commands/prepare_update.rs index 12ab9853..5c3b7aec 100644 --- a/core/src/cli_commands/prepare_update.rs +++ b/core/src/cli_commands/prepare_update.rs @@ -431,7 +431,7 @@ addresses: ballot_token_owner_address: 3WzD3VNSK4RtDCZe8njzLzRnWbxcfpCneUcQncAVV9JBDE37nLxR refresh_contract_parameters: - ergo_tree_bytes: 1016043c040004000e202f9111945d8f05f5c12732743e601b1de802fe741c38065e6a6372e1329c96fa01000502010105000400040004020402040204080400040a05c8010e206e5c5a93e24ba3bdcd2b510d8623a9c6e74efe7e0e6d20c114c47a19b299f57f0400040404020408d80ed60199a37300d602b2a4730100d603b5a4d901036395e6c672030605eded928cc77203017201938cb2db6308720373020001730393e4c672030504e4c6720205047304d604b17203d605b0720386027305860273067307d901053c413d0563d803d607e4c68c7205020605d6088c720501d6098c720802860272078602ed8c720901908c72080172079a8c7209027207d6068c720502d6078c720501d608db63087202d609b27208730800d60ab2a5730900d60bdb6308720ad60cb2720b730a00d60db27208730b00d60eb2a5730c00ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02cde4c6b27203e4e30004000407d18f8cc77202017201d1927204730dd18c720601d190997207e4c6b27203730e0006059d9c72077e730f057310d1938c7209017311d193b2720b7312007209d1938c720c018c720d01d1928c720c02998c720d027e9c7204731305d193b1720bb17208d193e4c6720a04059d8c7206027e720405d193e4c6720a05049ae4c6720205047314d193c2720ac27202d192c1720ac17202d1928cc7720a0199a37315d193db6308720edb6308a7d193c2720ec2a7d192c1720ec1a7 + ergo_tree_bytes: 1016043c040004000e206e5c5a93e24ba3bdcd2b510d8623a9c6e74efe7e0e6d20c114c47a19b299f57f01000502010105000400040004020402040204080400040a05c8010e2014717f917cdb1951fc378e31ffc0a0a778bddb10d6509c201cb4ed2550ef9f830400040404020408d80ed60199a37300d602b2a4730100d603b5a4d901036395e6c672030605eded928cc77203017201938cb2db6308720373020001730393e4c672030504e4c6720205047304d604b17203d605b0720386027305860273067307d901053c413d0563d803d607e4c68c7205020605d6088c720501d6098c720802860272078602ed8c720901908c72080172079a8c7209027207d6068c720502d6078c720501d608db63087202d609b27208730800d60ab2a5730900d60bdb6308720ad60cb2720b730a00d60db27208730b00d60eb2a5730c00ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02ea02cde4c6b27203e4e30004000407d18f8cc77202017201d1927204730dd18c720601d190997207e4c6b27203730e0006059d9c72077e730f057310d1938c7209017311d193b2720b7312007209d1938c720c018c720d01d1928c720c02998c720d027e9c7204731305d193b1720bb17208d193e4c6720a04059d8c7206027e720405d193e4c6720a05049ae4c6720205047314d193c2720ac27202d192c1720ac17202d1928cc7720a0199a37315d193db6308720edb6308a7d193c2720ec2a7d192c1720ec1a7 pool_nft_index: 17 oracle_token_id_index: 3 min_data_points_index: 13 From 45f3363525ac4be2d12ac615da1be1f91f1832c0 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 30 Sep 2022 15:23:19 +0300 Subject: [PATCH 4/6] gracefully handle refresh without enough datapoints #128; add more details to logging; --- core/src/actions.rs | 6 +++--- core/src/api.rs | 3 --- core/src/main.rs | 23 +++++++++++++++++--- core/src/oracle_state.rs | 32 ++++------------------------ core/src/pool_commands.rs | 8 +++---- core/src/pool_commands/refresh.rs | 35 +++++++++++++++++++------------ core/src/state.rs | 21 +++++++++++++------ 7 files changed, 68 insertions(+), 60 deletions(-) diff --git a/core/src/actions.rs b/core/src/actions.rs index 2fc252e3..b157cb3b 100644 --- a/core/src/actions.rs +++ b/core/src/actions.rs @@ -60,15 +60,15 @@ pub fn execute_action(action: PoolAction) -> Result<(), ActionExecError> { } fn execute_refresh_action(action: RefreshAction) -> Result<(), ActionExecError> { - let _tx_id = sign_and_submit_transaction(&action.tx)?; - log::info!("Refresh action executed successfully"); + let tx_id = sign_and_submit_transaction(&action.tx)?; + log::info!("Refresh action executed successfully, tx id: {}", tx_id); Ok(()) } fn execute_publish_datapoint_action(action: PublishDataPointAction) -> Result<(), ActionExecError> { match sign_and_submit_transaction(&action.tx) { Ok(tx_id) => { - log::info!("Datapoint published successfully, tx id = {}", tx_id); + log::info!("Datapoint published successfully, tx id: {}", tx_id); } Err(NodeError::BadRequest(msg)) if msg.as_str() == "Double spending attempt" => { log::info!("Failed commiting datapoint (double spending attempt error, probably due to our previous data point tx is still in the mempool)"); diff --git a/core/src/api.rs b/core/src/api.rs index 0993d26f..8a8b05ba 100644 --- a/core/src/api.rs +++ b/core/src/api.rs @@ -98,17 +98,14 @@ async fn pool_status() -> impl IntoResponse { let mut latest_datapoint = 0; let mut current_epoch_id = "".to_string(); - let mut epoch_ends = 0; if let Ok(l) = op.get_live_epoch_state() { latest_datapoint = l.latest_pool_datapoint; current_epoch_id = l.epoch_id.to_string(); - epoch_ends = l.epoch_ends; } Json(json!({ "current_pool_stage": current_stage, "latest_datapoint": latest_datapoint, "current_epoch_id" : current_epoch_id, - "epoch_ends": epoch_ends, })) } diff --git a/core/src/main.rs b/core/src/main.rs index 44d7506b..658e712d 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -55,6 +55,8 @@ use node_interface::new_node_interface; use oracle_state::register_and_save_scans; use oracle_state::OraclePool; use pool_commands::build_action; +use pool_commands::refresh::RefreshActionError; +use pool_commands::PoolCommandError; use state::process; use state::PoolState; use std::convert::TryInto; @@ -322,7 +324,7 @@ fn handle_oracle_command(command: Command) { } fn main_loop_iteration(op: &OraclePool, read_only: bool) -> std::result::Result<(), anyhow::Error> { - let height = current_block_height()?; + let height = current_block_height()? as u32; let wallet = WalletData::new(); let pool_state = match op.get_live_epoch_state() { Ok(live_epoch_state) => PoolState::LiveEpoch(live_epoch_state), @@ -332,13 +334,28 @@ fn main_loop_iteration(op: &OraclePool, read_only: bool) -> std::result::Result< } }; if let Some(cmd) = process(pool_state, height)? { - let action = build_action( + let action = match build_action( cmd, op, &wallet, height as u32, get_change_address_from_node()?, - )?; + ) { + Ok(action) => action, + Err(PoolCommandError::RefreshActionError( + e @ RefreshActionError::FailedToReachConsensus { + expected: _, + found_public_keys: _, + found_num: _, + }, + )) => { + log::error!("{e}"); + return Ok(()); + } + Err(e) => { + return Err(anyhow!(e)); + } + }; if !read_only { execute_action(action)?; } diff --git a/core/src/oracle_state.rs b/core/src/oracle_state.rs index 1b932e05..01940f3a 100644 --- a/core/src/oracle_state.rs +++ b/core/src/oracle_state.rs @@ -16,7 +16,6 @@ use crate::scans::{ register_update_box_scan, save_scan_ids_locally, Scan, ScanError, }; use crate::state::PoolState; -use crate::{BlockHeight, NanoErg}; use anyhow::Error; use derive_more::From; @@ -166,23 +165,8 @@ pub struct UpdateBoxScan<'a> { pub struct LiveEpochState { pub epoch_id: u32, pub commit_datapoint_in_epoch: bool, - pub epoch_ends: BlockHeight, pub latest_pool_datapoint: u64, -} - -/// The state of the oracle pool when it is in the Epoch Preparation stage -#[derive(Debug, Clone)] -pub struct PreparationState { - pub funds: NanoErg, - pub next_epoch_ends: BlockHeight, - pub latest_pool_datapoint: u64, -} - -/// The current UTXO-set state of all of the Pool Deposit boxes -#[derive(Debug, Clone)] -pub struct PoolDepositsState { - pub number_of_boxes: u64, - pub total_nanoergs: NanoErg, + pub latest_pool_box_height: u32, } impl<'a> OraclePool<'a> { @@ -292,21 +276,13 @@ impl<'a> OraclePool<'a> { false }; - let latest_pool_datapoint = pool_box.rate(); - - // Block height epochs ends is held in R5 of the epoch box - let epoch_ends = pool_box.get_box().creation_height - + ORACLE_CONFIG - .refresh_box_wrapper_inputs - .contract_inputs - .contract_parameters() - .epoch_length() as u32; + let latest_pool_datapoint = pool_box.rate() as u64; let epoch_state = LiveEpochState { epoch_id, commit_datapoint_in_epoch, - epoch_ends: epoch_ends as u64, - latest_pool_datapoint: latest_pool_datapoint as u64, + latest_pool_datapoint, + latest_pool_box_height: pool_box.get_box().creation_height, }; Ok(epoch_state) diff --git a/core/src/pool_commands.rs b/core/src/pool_commands.rs index 2318c050..bd4aaf62 100644 --- a/core/src/pool_commands.rs +++ b/core/src/pool_commands.rs @@ -13,10 +13,10 @@ use crate::wallet::WalletDataSource; use self::publish_datapoint::build_publish_datapoint_action; use self::publish_datapoint::PublishDatapointActionError; use self::refresh::build_refresh_action; -use self::refresh::RefrechActionError; +use self::refresh::RefreshActionError; -mod publish_datapoint; -mod refresh; +pub mod publish_datapoint; +pub mod refresh; #[cfg(test)] pub(crate) mod test_utils; @@ -32,7 +32,7 @@ pub enum PoolCommandError { #[error("box builder error: {0}")] Unexpected(String), #[error("error on building RefreshAction: {0}")] - RefrechActionError(RefrechActionError), + RefreshActionError(RefreshActionError), #[error("error on building PublishDatapointAction: {0}")] PublishDatapointActionError(PublishDatapointActionError), #[error("Digest error: {0}")] diff --git a/core/src/pool_commands/refresh.rs b/core/src/pool_commands/refresh.rs index 400af659..bc99066e 100644 --- a/core/src/pool_commands/refresh.rs +++ b/core/src/pool_commands/refresh.rs @@ -35,9 +35,13 @@ use thiserror::Error; use std::convert::TryInto; #[derive(Debug, From, Error)] -pub enum RefrechActionError { - #[error("Failed collecting datapoints. The minimum consensus number could not be reached, meaning that an insufficient number of oracles posted datapoints within the deviation range: found {found}, expected {expected}")] - FailedToReachConsensus { found: u32, expected: u32 }, +pub enum RefreshActionError { + #[error("Refresh failed, not enough datapoints. The minimum number of datapoints within the deviation range: required minumum {expected}, found {found_num} from public keys {found_public_keys},")] + FailedToReachConsensus { + found_public_keys: String, + found_num: u32, + expected: u32, + }, #[error("Not enough datapoints left during the removal of the outliers")] NotEnoughDatapoints, #[error("stage error: {0}")] @@ -63,7 +67,7 @@ pub fn build_refresh_action( height: u32, change_address: Address, my_oracle_pk: &EcPoint, -) -> Result { +) -> Result { let tx_fee = *BASE_FEE; let in_pool_box = pool_box_source.get_pool_box()?; @@ -80,9 +84,14 @@ pub fn build_refresh_action( .filter(|b| valid_in_oracle_boxes_datapoints.contains(&b.rate())) .collect::>(); if (valid_in_oracle_boxes.len() as u32) < min_data_points { - return Err(RefrechActionError::FailedToReachConsensus { - found: valid_in_oracle_boxes.len() as u32, + return Err(RefreshActionError::FailedToReachConsensus { + found_num: valid_in_oracle_boxes.len() as u32, expected: min_data_points, + found_public_keys: valid_in_oracle_boxes + .iter() + .map(|b| format!("{:?}", b.public_key())) + .collect::>() + .join(","), }); } let rate = calc_pool_rate(valid_in_oracle_boxes.iter().map(|b| b.rate()).collect()); @@ -143,7 +152,7 @@ pub fn build_refresh_action( fn filtered_oracle_boxes( oracle_boxes: Vec, deviation_range: u32, -) -> Result, RefrechActionError> { +) -> Result, RefreshActionError> { let mut successful_boxes = oracle_boxes.clone(); // The min oracle box's rate must be within deviation_range(5%) of that of the max while !deviation_check(deviation_range, &successful_boxes) { @@ -166,10 +175,10 @@ fn deviation_check(max_deviation_range: u32, datapoint_boxes: &Vec) -> bool /// said datapoint which deviates further. fn remove_largest_local_deviation_datapoint( datapoint_boxes: Vec, -) -> Result, RefrechActionError> { +) -> Result, RefreshActionError> { // Check if sufficient number of datapoint boxes to start removing if datapoint_boxes.len() <= 2 { - Err(RefrechActionError::NotEnoughDatapoints) + Err(RefreshActionError::NotEnoughDatapoints) } else { let mean = (datapoint_boxes.iter().sum::() as f32) / datapoint_boxes.len() as f32; let min_datapoint = *datapoint_boxes.iter().min().unwrap(); @@ -202,7 +211,7 @@ fn build_out_pool_box( creation_height: u32, rate: u64, reward_decrement: u64, -) -> Result { +) -> Result { let new_epoch_counter: i32 = (in_pool_box.epoch_counter() + 1) as i32; let reward_token = in_pool_box.reward_token(); let new_reward_token: Token = ( @@ -229,7 +238,7 @@ fn build_out_pool_box( fn build_out_refresh_box( in_refresh_box: &RefreshBoxWrapper, creation_height: u32, -) -> Result { +) -> Result { make_refresh_box_candidate( in_refresh_box.contract(), in_refresh_box.refresh_nft_token(), @@ -243,7 +252,7 @@ fn build_out_oracle_boxes( valid_oracle_boxes: &Vec, creation_height: u32, my_public_key: &EcPoint, -) -> Result, RefrechActionError> { +) -> Result, RefreshActionError> { valid_oracle_boxes .iter() .map(|in_ob| { @@ -269,7 +278,7 @@ fn build_out_oracle_boxes( ) .map_err(Into::into) }) - .collect::, RefrechActionError>>() + .collect::, RefreshActionError>>() } #[cfg(test)] diff --git a/core/src/state.rs b/core/src/state.rs index 8dbaf405..efb20338 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -2,9 +2,9 @@ use crate::actions::CollectionError; use crate::datapoint_source::DataPointSource; +use crate::oracle_config::ORACLE_CONFIG; use crate::oracle_state::LiveEpochState; use crate::oracle_state::OraclePool; -use crate::oracle_state::PreparationState; use crate::oracle_state::Stage; use crate::oracle_state::StageError; use crate::pool_commands::PoolCommand; @@ -21,7 +21,7 @@ pub enum PoolState { LiveEpoch(LiveEpochState), } -pub fn process(pool_state: PoolState, height: u64) -> Result, StageError> { +pub fn process(pool_state: PoolState, height: u32) -> Result, StageError> { match pool_state { PoolState::NeedsBootstrap => { log::warn!( @@ -30,13 +30,22 @@ pub fn process(pool_state: PoolState, height: u64) -> Result Ok(None) } PoolState::LiveEpoch(live_epoch) => { - let epoch_is_over = - height >= live_epoch.epoch_ends && live_epoch.commit_datapoint_in_epoch; + let epoch_length = ORACLE_CONFIG + .refresh_box_wrapper_inputs + .contract_inputs + .contract_parameters() + .epoch_length() as u32; + let epoch_is_over = height >= live_epoch.latest_pool_box_height + epoch_length + && live_epoch.commit_datapoint_in_epoch; if epoch_is_over { - log::info!("Epoch is over, calling refresh"); + log::info!( + "Height {height}. Epoch id {}, previous epoch ended (pool box) at {} + epoch lengh {epoch_length}, calling refresh action", + live_epoch.epoch_id, + live_epoch.latest_pool_box_height, + ); Ok(Some(PoolCommand::Refresh)) } else if !live_epoch.commit_datapoint_in_epoch { - log::info!("Commiting datapoint..."); + log::info!("Height {height}. Publishing datapoint..."); Ok(Some(PoolCommand::PublishDataPoint)) } else { Ok(None) From b99e3df170583c83242c8f06f450925686e711de Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Mon, 3 Oct 2022 09:59:17 +0300 Subject: [PATCH 5/6] show oracles address when not enough datapoints collected on refresh action; --- core/src/actions.rs | 13 ------- core/src/main.rs | 58 ++++++++++++++++++++----------- core/src/pool_commands/refresh.rs | 10 +++--- core/src/state.rs | 6 ---- 4 files changed, 42 insertions(+), 45 deletions(-) diff --git a/core/src/actions.rs b/core/src/actions.rs index b157cb3b..c8d6c978 100644 --- a/core/src/actions.rs +++ b/core/src/actions.rs @@ -17,9 +17,6 @@ pub enum PoolAction { PublishDatapoint(PublishDataPointAction), } -#[derive(Debug)] -pub struct BootstrapAction {} - #[derive(Debug)] pub struct RefreshAction { pub tx: UnsignedTransaction, @@ -30,16 +27,6 @@ pub struct PublishDataPointAction { pub tx: UnsignedTransaction, } -#[derive(Error, Debug)] -pub enum CollectionError { - #[error("Failed collecting datapoints. The minimum consensus number could not be reached, meaning that an insufficient number of oracles posted datapoints within the deviation range.")] - FailedToReachConsensus(), - #[error("Failed collecting datapoints. The local oracle did not post a datapoint in the current epoch.")] - LocalOracleFailedToPostDatapoint(), - #[error("Failed collecting datapoints. The local oracle did not post a datapoint within the deviation range (when compared to datapoints posted by other oracles in the pool).")] - LocalOracleFailedToPostDatapointWithinDeviation(), -} - #[derive(Error, Debug, From)] pub enum ActionExecError { #[error("node error: {0}")] diff --git a/core/src/main.rs b/core/src/main.rs index 658e712d..35cc0067 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -38,11 +38,14 @@ mod tests; mod wallet; use actions::execute_action; +use actions::PoolAction; use anyhow::anyhow; use clap::{Parser, Subcommand}; use crossbeam::channel::bounded; use ergo_lib::ergotree_ir::chain::address::Address; use ergo_lib::ergotree_ir::chain::address::AddressEncoder; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::ergotree_ir::chain::address::NetworkPrefix; use ergo_lib::ergotree_ir::chain::token::Token; use ergo_lib::ergotree_ir::chain::token::TokenId; use log::debug; @@ -326,6 +329,7 @@ fn handle_oracle_command(command: Command) { fn main_loop_iteration(op: &OraclePool, read_only: bool) -> std::result::Result<(), anyhow::Error> { let height = current_block_height()? as u32; let wallet = WalletData::new(); + let networt_change_address = get_change_address_from_node()?; let pool_state = match op.get_live_epoch_state() { Ok(live_epoch_state) => PoolState::LiveEpoch(live_epoch_state), Err(error) => { @@ -334,40 +338,52 @@ fn main_loop_iteration(op: &OraclePool, read_only: bool) -> std::result::Result< } }; if let Some(cmd) = process(pool_state, height)? { - let action = match build_action( + let build_action_res = build_action( cmd, op, &wallet, height as u32, - get_change_address_from_node()?, - ) { - Ok(action) => action, - Err(PoolCommandError::RefreshActionError( - e @ RefreshActionError::FailedToReachConsensus { - expected: _, - found_public_keys: _, - found_num: _, - }, - )) => { - log::error!("{e}"); - return Ok(()); - } - Err(e) => { - return Err(anyhow!(e)); + networt_change_address.address(), + ); + if let Some(action) = + continue_if_consensus_error(networt_change_address.network(), build_action_res)? + { + if !read_only { + execute_action(action)?; } }; - if !read_only { - execute_action(action)?; - } } Ok(()) } -fn get_change_address_from_node() -> Result { +fn continue_if_consensus_error( + network_prefix: NetworkPrefix, + res: Result, +) -> Result, PoolCommandError> { + match res { + Ok(action) => Ok(Some(action)), + Err(PoolCommandError::RefreshActionError(RefreshActionError::FailedToReachConsensus { + expected, + found_public_keys, + found_num, + })) => { + let found_oracle_addresses: String = found_public_keys + .into_iter() + .map(|pk| NetworkAddress::new(network_prefix, &Address::P2Pk(pk)).to_base58()) + .collect::>() + .join(", "); + log::error!("Refresh failed, not enough datapoints. The minimum number of datapoints within the deviation range: required minumum {expected}, found {found_num} from addresses {found_oracle_addresses},"); + Ok(None) + } + Err(e) => Err(e), + } +} + +fn get_change_address_from_node() -> Result { let change_address_str = get_wallet_status()? .change_address .ok_or_else(|| anyhow!("failed to get wallet's change address (locked wallet?)"))?; - let addr = AddressEncoder::unchecked_parse_address_from_str(&change_address_str)?; + let addr = AddressEncoder::unchecked_parse_network_address_from_str(&change_address_str)?; Ok(addr) } diff --git a/core/src/pool_commands/refresh.rs b/core/src/pool_commands/refresh.rs index bc99066e..9371982f 100644 --- a/core/src/pool_commands/refresh.rs +++ b/core/src/pool_commands/refresh.rs @@ -23,6 +23,7 @@ use ergo_lib::ergotree_ir::chain::address::Address; use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBoxCandidate; use ergo_lib::ergotree_ir::chain::token::Token; use ergo_lib::ergotree_ir::chain::token::TokenAmount; +use ergo_lib::ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog; use ergo_lib::wallet::box_selector::BoxSelection; use ergo_lib::wallet::box_selector::BoxSelector; use ergo_lib::wallet::box_selector::BoxSelectorError; @@ -36,9 +37,9 @@ use std::convert::TryInto; #[derive(Debug, From, Error)] pub enum RefreshActionError { - #[error("Refresh failed, not enough datapoints. The minimum number of datapoints within the deviation range: required minumum {expected}, found {found_num} from public keys {found_public_keys},")] + #[error("Refresh failed, not enough datapoints. The minimum number of datapoints within the deviation range: required minumum {expected}, found {found_num} from public keys {found_public_keys:?},")] FailedToReachConsensus { - found_public_keys: String, + found_public_keys: Vec, found_num: u32, expected: u32, }, @@ -89,9 +90,8 @@ pub fn build_refresh_action( expected: min_data_points, found_public_keys: valid_in_oracle_boxes .iter() - .map(|b| format!("{:?}", b.public_key())) - .collect::>() - .join(","), + .map(|b| b.public_key()) + .collect(), }); } let rate = calc_pool_rate(valid_in_oracle_boxes.iter().map(|b| b.rate()).collect()); diff --git a/core/src/state.rs b/core/src/state.rs index efb20338..c8def296 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -1,11 +1,5 @@ -#![allow(unused_imports)] - -use crate::actions::CollectionError; -use crate::datapoint_source::DataPointSource; use crate::oracle_config::ORACLE_CONFIG; use crate::oracle_state::LiveEpochState; -use crate::oracle_state::OraclePool; -use crate::oracle_state::Stage; use crate::oracle_state::StageError; use crate::pool_commands::PoolCommand; use anyhow::Result; From bd0e07b83b742f0959a19b2951c41f91c9243aaa Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Tue, 4 Oct 2022 09:58:54 +0300 Subject: [PATCH 6/6] fix typo; --- core/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main.rs b/core/src/main.rs index 35cc0067..14bea8a3 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -329,7 +329,7 @@ fn handle_oracle_command(command: Command) { fn main_loop_iteration(op: &OraclePool, read_only: bool) -> std::result::Result<(), anyhow::Error> { let height = current_block_height()? as u32; let wallet = WalletData::new(); - let networt_change_address = get_change_address_from_node()?; + let network_change_address = get_change_address_from_node()?; let pool_state = match op.get_live_epoch_state() { Ok(live_epoch_state) => PoolState::LiveEpoch(live_epoch_state), Err(error) => { @@ -343,10 +343,10 @@ fn main_loop_iteration(op: &OraclePool, read_only: bool) -> std::result::Result< op, &wallet, height as u32, - networt_change_address.address(), + network_change_address.address(), ); if let Some(action) = - continue_if_consensus_error(networt_change_address.network(), build_action_res)? + continue_if_consensus_error(network_change_address.network(), build_action_res)? { if !read_only { execute_action(action)?;