Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 15 additions & 30 deletions core/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,9 +17,6 @@ pub enum PoolAction {
PublishDatapoint(PublishDataPointAction),
}

#[derive(Debug)]
pub struct BootstrapAction {}

#[derive(Debug)]
pub struct RefreshAction {
pub tx: UnsignedTransaction,
Expand All @@ -31,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}")]
Expand All @@ -50,34 +36,33 @@ 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)
}
}
}

fn execute_refresh_action(action: RefreshAction) -> Result<(), ActionExecError> {
let _tx_id = sign_and_submit_transaction(&action.tx)?;
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> {
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::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));
}
};
Ok(())
}

/// Given an `ErgoBox`, find its index in the input `Vec<ErgoBox>`
/// 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<ErgoBox>,
) -> Option<usize> {
sorted_datapoint_boxes
.iter()
.position(|b| b.clone() == search_box)
}
3 changes: 0 additions & 3 deletions core/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}))
}

Expand Down
33 changes: 17 additions & 16 deletions core/src/cli_commands/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand All @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -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(
Expand All @@ -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()?;
Expand All @@ -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(
Expand All @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
Expand All @@ -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)?)
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/cli_commands/prepare_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
62 changes: 52 additions & 10 deletions core/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -55,6 +58,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;
Expand All @@ -64,6 +69,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;
Expand Down Expand Up @@ -176,7 +182,8 @@ fn main() {
None
};
logging::setup_log(cmdline_log_level);
log::info!("{}", APP_VERSION);

log_on_launch();

debug!("Args: {:?}", args);

Expand Down Expand Up @@ -320,8 +327,9 @@ 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 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) => {
Expand All @@ -330,24 +338,58 @@ 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 build_action_res = build_action(
cmd,
op,
&wallet,
height as u32,
get_change_address_from_node()?,
)?;
if !read_only {
execute_action(action)?;
}
network_change_address.address(),
);
if let Some(action) =
continue_if_consensus_error(network_change_address.network(), build_action_res)?
{
if !read_only {
execute_action(action)?;
}
};
}
Ok(())
}

fn get_change_address_from_node() -> Result<Address, anyhow::Error> {
fn continue_if_consensus_error(
network_prefix: NetworkPrefix,
res: Result<PoolAction, PoolCommandError>,
) -> Result<Option<PoolAction>, 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::<Vec<String>>()
.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<NetworkAddress, anyhow::Error> {
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)
}

fn log_on_launch() {
log::info!("{}", APP_VERSION);
if let Ok(config) = MAYBE_ORACLE_CONFIG.clone() {
log::info!("Token ids: {:?}", config.token_ids);
}
}
4 changes: 2 additions & 2 deletions core/src/oracle_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Loading