From 2e04a37101326289eb62b331955690291ee89a0b Mon Sep 17 00:00:00 2001 From: Moein zargarzadeh Date: Fri, 7 Mar 2025 18:19:12 +0330 Subject: [PATCH 1/6] removed node's scan/wallet dependency from source code and add mnemonic config --- README.md | 12 +- core/Cargo.toml | 3 +- core/src/actions.rs | 11 +- core/src/address_util.rs | 9 + core/src/api.rs | 12 +- core/src/cli_commands/bootstrap.rs | 110 +++--- .../src/cli_commands/extract_reward_tokens.rs | 93 +++-- core/src/cli_commands/import_pool_update.rs | 19 - core/src/cli_commands/prepare_update.rs | 68 ++-- .../src/cli_commands/transfer_oracle_token.rs | 95 +++-- core/src/cli_commands/update_pool.rs | 115 +++--- core/src/cli_commands/vote_update_pool.rs | 135 +++---- core/src/get_boxes.rs | 39 ++ core/src/get_boxes/generic_token_fetch.rs | 64 ++++ core/src/get_boxes/registry.rs | 59 ++++ core/src/main.rs | 62 +--- core/src/metrics.rs | 13 +- core/src/node_interface.rs | 82 +---- core/src/node_interface/node_api.rs | 333 +++++++++++++----- core/src/node_interface/test_utils.rs | 94 +++++ core/src/oracle_config.rs | 50 ++- core/src/oracle_state.rs | 180 +++++----- core/src/pool_commands.rs | 23 +- core/src/pool_commands/publish_datapoint.rs | 179 ++++------ core/src/pool_commands/refresh.rs | 110 +++--- core/src/pool_commands/test_utils.rs | 49 +-- core/src/scans.rs | 56 --- core/src/scans/generic_token_scan.rs | 76 ---- core/src/scans/registry.rs | 278 --------------- core/src/templates.rs | 15 - core/src/tests/bootstrap_and_run.rs | 40 +-- core/src/wallet.rs | 25 -- docs/how_to_bootstrap.md | 2 +- docs/update_epoch_length.md | 2 +- 34 files changed, 1113 insertions(+), 1400 deletions(-) create mode 100644 core/src/get_boxes.rs create mode 100644 core/src/get_boxes/generic_token_fetch.rs create mode 100644 core/src/get_boxes/registry.rs create mode 100644 core/src/node_interface/test_utils.rs delete mode 100644 core/src/scans.rs delete mode 100644 core/src/scans/generic_token_scan.rs delete mode 100644 core/src/scans/registry.rs delete mode 100644 core/src/templates.rs delete mode 100644 core/src/wallet.rs diff --git a/README.md b/README.md index d2c20762..6a6e3d0b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Oracle Core v2.0 -The oracle core requires that the user has access to a full node wallet in order to create txs & perform UTXO-set scanning. Furthermore, each oracle core is designed to work with only a single oracle pool. If an operator runs several oracles in several oracle pools then a single full node can be used, but several instances of oracle cores must be run (and set with different API ports). +The oracle core requires that the user has access to a public node with activated extra option to create txs. Furthermore, each oracle core is designed to work with only a single oracle pool. If an operator runs several oracles in several oracle pools then a single full node can be used, but several instances of oracle cores must be run (and set with different API ports). The current oracle core is built to run the protocol specified in the [EIP-0023 PR](https://github.com/ergoplatform/eips/pull/41). @@ -19,7 +19,7 @@ docker run -d \ -v /path/on/host:/data \ -p 9010:9010 \ -p 9011:9011 \ - -e ORACLE_NODE_API_KEY=CHANGE_ME_KEY \ + -e ORACLE_WALLET_MNEMONIC=CHANGE_ME \ ergoplatform/oracle-core:latest ``` @@ -54,7 +54,7 @@ and set the required parameters: - `oracle_address` - a node's address that will be used by this oracle-core instance(pay tx fees, keep tokens, etc.). Make sure it has coins; - `node_url` node URL; -Set the environment variable `ORACLE_NODE_API_KEY` to the node's API key. You can put it in the `.secrets` file and then run `source .secrets` to load it into the environment. This way, the key does not get stored in the shell history. +Set the environment variable `ORACLE_WALLET_MNEMONIC` to the oracle's mnemonic. You can put it in the `.secrets` file and then run `source .secrets` to load it into the environment. This way, the key does not get stored in the shell history. ## Bootstrapping a new oracle pool @@ -198,8 +198,8 @@ With optional(only if minted) parameters: - reward token amount in the pool box at the time of update transaction is committed (only if minted) This will submit an update tx. -After the update tx is confirmed, remove `scanIds.json` and use `pool_config_updated.yaml` to run the oracle (i.e., rename it to `pool_config.yaml` and restart the oracle). -Distribute the `pool_config.yaml` file to all the oracles. Be sure they delete `scanIds.json` before restart. +After the update tx is confirmed and use `pool_config_updated.yaml` to run the oracle (i.e., rename it to `pool_config.yaml` and restart the oracle). +Distribute the `pool_config.yaml` file to all the oracles. ### Import update pool config with `import-pool-update` command @@ -210,7 +210,7 @@ Run oracle-core import-update-pool pool_config_updated.yaml ``` -This will update the pool_config.yaml, removes `scanIds.json`. Restart the oracle afterwards. +This will update the pool_config.yaml. Restart the oracle afterwards. ## How to run as systemd daemon diff --git a/core/Cargo.toml b/core/Cargo.toml index 8e60f6af..17cbc641 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -32,8 +32,7 @@ tokio = { version = "1", features = ["full"] } tower-http = { version = "0.3.0", features = ["cors"] } axum = "0.6" ergo-lib = { workspace = true } -ergo-node-interface = { git = "https://github.com/ergoplatform/ergo-node-interface-rust", rev = "143c2a3dc8fb772d1af37f1f1e1924067c6aad14" } -# ergo-node-interface = { version = "0.4" } +ergo-node-interface = { version = "0.5.0" } derive_more = "0.99" clap = { version = "4.2.4", features = ["derive"] } exitcode = "1.1.2" diff --git a/core/src/actions.rs b/core/src/actions.rs index 9b918231..4766954e 100644 --- a/core/src/actions.rs +++ b/core/src/actions.rs @@ -1,9 +1,10 @@ /// This file holds all the actions which can be performed /// by an oracle part of the oracle pool. These actions /// are implemented on the `OraclePool` struct. -use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; use derive_more::From; +use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::wallet::signing::TransactionContext; use ergo_node_interface::node_interface::NodeError; use thiserror::Error; @@ -23,12 +24,12 @@ pub enum PoolAction { #[derive(Debug)] pub struct RefreshAction { - pub tx: UnsignedTransaction, + pub transaction_context: TransactionContext } #[derive(Debug)] pub struct PublishDataPointAction { - pub tx: UnsignedTransaction, + pub transaction_context: TransactionContext } #[derive(Error, Debug)] @@ -63,7 +64,7 @@ fn execute_refresh_action( action: RefreshAction, node_api: &NodeApi, ) -> Result<(), ActionExecError> { - let tx_id = node_api.sign_and_submit_transaction(&action.tx)?; + let tx_id = node_api.sign_and_submit_transaction(action.transaction_context)?; let network_prefix = &ORACLE_CONFIG.oracle_address.network(); log::info!( "Refresh tx published. Check status: {}", @@ -76,7 +77,7 @@ fn execute_publish_datapoint_action( action: PublishDataPointAction, node_api: &NodeApi, ) -> Result<(), ActionExecError> { - let tx_id = node_api.sign_and_submit_transaction(&action.tx)?; + let tx_id = node_api.sign_and_submit_transaction(action.transaction_context)?; let network_prefix = &ORACLE_CONFIG.oracle_address.network(); log::info!( "Datapoint tx published. Check status: {}", diff --git a/core/src/address_util.rs b/core/src/address_util.rs index 1c4d2ca1..3f80ea5e 100644 --- a/core/src/address_util.rs +++ b/core/src/address_util.rs @@ -5,6 +5,7 @@ use ergo_lib::ergotree_ir::chain::address::NetworkAddress; use ergo_lib::ergotree_ir::chain::address::NetworkPrefix; use ergo_lib::ergotree_ir::serialization::SigmaParsingError; use ergo_lib::ergotree_ir::serialization::SigmaSerializationError; +use ergo_lib::ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog; use thiserror::Error; #[derive(Error, Debug)] @@ -31,3 +32,11 @@ pub fn pks_to_network_addresses( .map(|pk| NetworkAddress::new(network_prefix, &Address::P2Pk(pk.into()))) .collect() } + +pub fn address_to_p2pk(addr: &Address) -> Result { + if let Address::P2Pk(public_key) = addr { + Ok(public_key.clone()) + } else { + Err(AddressUtilError::ExpectedP2PK) + } +} diff --git a/core/src/api.rs b/core/src/api.rs index 56223cbd..bc3b10a3 100644 --- a/core/src/api.rs +++ b/core/src/api.rs @@ -7,7 +7,7 @@ use crate::monitor::{ check_oracle_health, check_pool_health, HealthStatus, OracleHealth, PoolHealth, }; use crate::node_interface::node_api::{NodeApi, NodeApiError}; -use crate::oracle_config::{ORACLE_CONFIG, ORACLE_SECRETS}; +use crate::oracle_config::ORACLE_CONFIG; use crate::oracle_state::{DataSourceError, LocalDatapointState, OraclePool}; use crate::pool_config::POOL_CONFIG; use axum::http::StatusCode; @@ -129,8 +129,6 @@ async fn pool_status(oracle_pool: Arc) -> Result) -> Result, ApiError> { let node_api = NodeApi::new( - ORACLE_SECRETS.node_api_key.clone(), - ORACLE_SECRETS.wallet_password.clone(), &ORACLE_CONFIG.node_url, ); let current_height = node_api.node.current_block_height()? as u32; @@ -161,8 +159,6 @@ fn pool_status_sync(oracle_pool: Arc) -> Result Result { let current_height = task::spawn_blocking(move || { let node_api = NodeApi::new( - ORACLE_SECRETS.node_api_key.clone(), - ORACLE_SECRETS.wallet_password.clone(), &ORACLE_CONFIG.node_url, ); node_api.node.current_block_height() @@ -206,8 +202,6 @@ async fn oracle_health(oracle_pool: Arc) -> impl IntoResponse { fn oracle_health_sync(oracle_pool: Arc) -> Result { let node_api = NodeApi::new( - ORACLE_SECRETS.node_api_key.clone(), - ORACLE_SECRETS.wallet_password.clone(), &ORACLE_CONFIG.node_url, ); let current_height = (node_api.node.current_block_height()? as u32).into(); @@ -252,14 +246,12 @@ async fn pool_health(oracle_pool: Arc) -> impl IntoResponse { fn pool_health_sync(oracle_pool: Arc) -> Result { let node_api = NodeApi::new( - ORACLE_SECRETS.node_api_key.clone(), - ORACLE_SECRETS.wallet_password.clone(), &ORACLE_CONFIG.node_url, ); let current_height = (node_api.node.current_block_height()? as u32).into(); let pool_box = &oracle_pool.get_pool_box_source().get_pool_box()?; let pool_box_height = pool_box.get_box().creation_height.into(); - let network_prefix = node_api.get_change_address()?.network(); + let network_prefix = ORACLE_CONFIG.change_address.clone().unwrap().network(); let pool_health = check_pool_health( current_height, pool_box_height, diff --git a/core/src/cli_commands/bootstrap.rs b/core/src/cli_commands/bootstrap.rs index 17280587..3dde107b 100644 --- a/core/src/cli_commands/bootstrap.rs +++ b/core/src/cli_commands/bootstrap.rs @@ -23,6 +23,7 @@ use ergo_lib::{ tx_builder::{TxBuilder, TxBuilderError}, }, }; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_node_interface::node_interface::NodeError; use log::{debug, info}; use serde::{Deserialize, Serialize}; @@ -43,10 +44,9 @@ use crate::{ }, explorer_api::wait_for_txs_confirmation, node_interface::{ - node_api::{NodeApi, NodeApiError}, - try_ensure_wallet_unlocked, SignTransactionWithInputs, SubmitTransaction, + node_api::{NodeApi, NodeApiTrait, NodeApiError}, }, - oracle_config::{BASE_FEE, ORACLE_CONFIG, ORACLE_SECRETS}, + oracle_config::{BASE_FEE, ORACLE_CONFIG}, oracle_types::{BlockHeight, EpochCounter}, pool_config::{ PoolConfig, PoolConfigError, PredefinedDataPointSource, TokenIds, @@ -57,32 +57,22 @@ use crate::{ BallotTokenId, OracleTokenId, PoolTokenId, RefreshTokenId, RewardTokenId, SpecToken, TokenIdKind, UpdateTokenId, }, - wallet::{WalletDataError, WalletDataSource}, }; /// Loads bootstrap configuration file and performs the chain-transactions for minting of tokens and /// box creations. An oracle configuration file is then created which contains the `TokenId`s of the /// minted tokens. -pub fn bootstrap(config_file_name: String) -> Result<(), anyhow::Error> { +pub fn bootstrap(config_file_name: String, node_api: &NodeApi) -> Result<(), anyhow::Error> { let oracle_config = &ORACLE_CONFIG; let s = std::fs::read_to_string(config_file_name)?; let config: BootstrapConfig = serde_yaml::from_str(&s)?; - - let node_api = NodeApi::new( - ORACLE_SECRETS.node_api_key.clone(), - ORACLE_SECRETS.wallet_password.clone(), - &oracle_config.node_url, - ); - try_ensure_wallet_unlocked(&node_api); - let change_address = node_api.get_change_address()?; + let change_address = ORACLE_CONFIG.change_address.clone().unwrap(); debug!("Change address: {:?}", change_address); let erg_value_per_box = config.oracle_contract_parameters.min_storage_rent; let input = BootstrapInput { oracle_address: oracle_config.oracle_address.clone(), config, - wallet: &node_api as &dyn WalletDataSource, - tx_signer: &node_api.node as &dyn SignTransactionWithInputs, - submit_tx: &node_api.node as &dyn SubmitTransaction, + node_api, tx_fee: *BASE_FEE, erg_value_per_box, change_address: change_address.address(), @@ -122,9 +112,7 @@ pub fn generate_bootstrap_config_template(config_file_name: String) -> Result<() pub struct BootstrapInput<'a> { pub oracle_address: NetworkAddress, pub config: BootstrapConfig, - pub wallet: &'a dyn WalletDataSource, - pub tx_signer: &'a dyn SignTransactionWithInputs, - pub submit_tx: &'a dyn SubmitTransaction, + pub node_api: &'a dyn NodeApiTrait, pub tx_fee: BoxValue, pub erg_value_per_box: BoxValue, pub change_address: Address, @@ -140,9 +128,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( let BootstrapInput { oracle_address, config, - wallet, - tx_signer: wallet_sign, - submit_tx, + node_api, tx_fee, erg_value_per_box, change_address, @@ -232,7 +218,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( .build()?; output_candidates.push(remaining_funds.clone()); - let inputs = box_selection.boxes.clone(); + let inputs = box_selection.boxes.clone().to_vec(); let tx_builder = TxBuilder::new( box_selection, output_candidates, @@ -242,17 +228,21 @@ pub(crate) fn perform_bootstrap_chained_transaction( ); let mint_token_tx = tx_builder.build()?; debug!("Mint token unsigned transaction: {:?}", mint_token_tx); - let signed_tx = wallet_sign.sign_transaction_with_inputs(&mint_token_tx, inputs, None)?; + let context = match TransactionContext::new(mint_token_tx, inputs, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(BootstrapError::TxSigningError(e)), + }; + let signed_tx = node_api.sign_transaction(context)?; *num_transactions_left -= 1; Ok((token, signed_tx)) }; // Mint pool NFT token -------------------------------------------------------------------------- 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)?; debug!("target_balance: {:?}", target_balance); + let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), target_balance, [].into())?; + debug!("unspent boxes: {:?}", unspent_boxes); let box_selector = SimpleBoxSelector::new(); let box_selection = box_selector.select(unspent_boxes.clone(), target_balance, &[])?; debug!("box selection: {:?}", box_selection); @@ -446,7 +436,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( target_balance, &[pool_nft_token.clone(), reward_tokens_for_pool_box.clone()], )?; - let inputs = box_selection.boxes.clone(); + let inputs = box_selection.boxes.clone().to_vec(); let tx_builder = TxBuilder::new( box_selection, output_candidates, @@ -456,8 +446,11 @@ pub(crate) fn perform_bootstrap_chained_transaction( ); let pool_box_tx = tx_builder.build()?; debug!("unsigned pool_box_tx: {:?}", pool_box_tx); - let signed_pool_box_tx = - wallet_sign.sign_transaction_with_inputs(&pool_box_tx, inputs, None)?; + let context = match TransactionContext::new(pool_box_tx, inputs, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(BootstrapError::TxSigningError(e)), + }; + let signed_pool_box_tx = node_api.sign_transaction(context)?; num_transactions_left -= 1; // Create refresh box -------------------------------------------------------------------------- @@ -502,7 +495,7 @@ pub(crate) fn perform_bootstrap_chained_transaction( let box_selection = box_selector.select(inputs, target_balance, &[refresh_nft_token.clone()])?; - let inputs = box_selection.boxes.clone(); + let inputs = box_selection.boxes.clone().to_vec(); let tx_builder = TxBuilder::new( box_selection, output_candidates, @@ -512,33 +505,37 @@ pub(crate) fn perform_bootstrap_chained_transaction( ); let refresh_box_tx = tx_builder.build()?; debug!("unsigned refresh_box_tx: {:?}", refresh_box_tx); - let signed_refresh_box_tx = - wallet_sign.sign_transaction_with_inputs(&refresh_box_tx, inputs, None)?; + let context = match TransactionContext::new(refresh_box_tx, inputs, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(BootstrapError::TxSigningError(e)), + }; + + let signed_refresh_box_tx = node_api.sign_transaction(context)?; // --------------------------------------------------------------------------------------------- let mut submitted_tx_ids = vec![]; - let tx_id = submit_tx.submit_transaction(&signed_mint_pool_nft_tx)?; + let tx_id = node_api.submit_transaction(&signed_mint_pool_nft_tx)?; submitted_tx_ids.push(signed_mint_pool_nft_tx.id()); info!("Minted pool NFT TxId: {}", tx_id); - let tx_id = submit_tx.submit_transaction(&signed_mint_refresh_nft_tx)?; + let tx_id = node_api.submit_transaction(&signed_mint_refresh_nft_tx)?; submitted_tx_ids.push(signed_mint_refresh_nft_tx.id()); info!("Minted refresh NFT TxId: {}", tx_id); - let tx_id = submit_tx.submit_transaction(&signed_mint_ballot_tokens_tx)?; + let tx_id = node_api.submit_transaction(&signed_mint_ballot_tokens_tx)?; submitted_tx_ids.push(signed_mint_ballot_tokens_tx.id()); info!("Minted ballot tokens TxId: {}", tx_id); - let tx_id = submit_tx.submit_transaction(&signed_mint_update_nft_tx)?; + let tx_id = node_api.submit_transaction(&signed_mint_update_nft_tx)?; submitted_tx_ids.push(signed_mint_update_nft_tx.id()); info!("Minted update NFT TxId: {}", tx_id); - let tx_id = submit_tx.submit_transaction(&signed_mint_oracle_tokens_tx)?; + let tx_id = node_api.submit_transaction(&signed_mint_oracle_tokens_tx)?; submitted_tx_ids.push(signed_mint_oracle_tokens_tx.id()); info!("Minted oracle tokens TxId: {}", tx_id); - let tx_id = submit_tx.submit_transaction(&signed_mint_reward_tokens_tx)?; + let tx_id = node_api.submit_transaction(&signed_mint_reward_tokens_tx)?; submitted_tx_ids.push(signed_mint_reward_tokens_tx.id()); info!("Minted reward tokens TxId: {}", tx_id); - let tx_id = submit_tx.submit_transaction(&signed_pool_box_tx)?; + let tx_id = node_api.submit_transaction(&signed_pool_box_tx)?; submitted_tx_ids.push(signed_pool_box_tx.id()); info!("Created initial pool box TxId: {}", tx_id); - let tx_id = submit_tx.submit_transaction(&signed_refresh_box_tx)?; + let tx_id = node_api.submit_transaction(&signed_refresh_box_tx)?; submitted_tx_ids.push(signed_refresh_box_tx.id()); info!("Created initial refresh box TxId: {}", tx_id); @@ -637,6 +634,8 @@ pub enum BootstrapError { NodeApiError(#[from] NodeApiError), #[error("box selector error: {0}")] BoxSelector(#[from] BoxSelectorError), + #[error("tx signing error: {0}")] + TxSigningError(#[from] TxSigningError), #[error("box value error: {0}")] BoxValue(#[from] BoxValueError), #[error("IO error: {0}")] @@ -663,8 +662,6 @@ pub enum BootstrapError { PoolConfigError(#[from] PoolConfigError), #[error("Pool contract error: {0}")] PoolContractError(#[from] PoolContractError), - #[error("WalletData error: {0}")] - WalletData(#[from] WalletDataError), } #[cfg(test)] @@ -676,26 +673,15 @@ pub(crate) mod tests { address::{AddressEncoder, NetworkAddress, NetworkPrefix}, ergo_box::{ErgoBox, NonMandatoryRegisters}, }, - wallet::Wallet, }; use sigma_test_util::force_any_val; use super::*; - use crate::pool_commands::test_utils::{LocalTxSigner, WalletDataMock}; + use crate::node_interface::test_utils::MockNodeApi; use std::cell::RefCell; #[derive(Default)] pub(crate) struct SubmitTxMock { - transactions: RefCell>, - } - - impl SubmitTransaction for SubmitTxMock { - fn submit_transaction( - &self, - tx: &ergo_lib::chain::transaction::Transaction, - ) -> crate::node_interface::Result { - self.transactions.borrow_mut().push(tx.clone()); - Ok(tx.id()) - } + pub(crate) transactions: RefCell>, } #[test] @@ -707,7 +693,6 @@ pub(crate) mod tests { NetworkPrefix::Mainnet, &Address::P2Pk(secret.public_image()), ); - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); let ergo_tree = address.address().script().unwrap(); let value = BASE_FEE.checked_mul_u32(10000).unwrap(); @@ -733,15 +718,13 @@ pub(crate) mod tests { let oracle_config = perform_bootstrap_chained_transaction(BootstrapInput { oracle_address: address, config: bootstrap_config.clone(), - wallet: &WalletDataMock { + node_api: &MockNodeApi { unspent_boxes: unspent_boxes.clone(), - change_address: change_address.clone(), - }, - tx_signer: &mut LocalTxSigner { - ctx: &ctx, - wallet: &wallet, + ctx: ctx.clone(), + secrets: vec![secret.clone().into()], + submitted_txs: &submit_tx.transactions, + chain_submit_tx: None }, - submit_tx: &submit_tx, tx_fee: *BASE_FEE, erg_value_per_box: *BASE_FEE, change_address: change_address.address(), @@ -901,7 +884,6 @@ tokens_to_mint: quantity: 100000000 node_ip: 10.94.77.47 node_port: 9052 -node_api_key: hello core_api_port: 9010 data_point_source: NanoErgUsd data_point_source_custom_script: ~ diff --git a/core/src/cli_commands/extract_reward_tokens.rs b/core/src/cli_commands/extract_reward_tokens.rs index 17d69edf..fddc5e67 100644 --- a/core/src/cli_commands/extract_reward_tokens.rs +++ b/core/src/cli_commands/extract_reward_tokens.rs @@ -3,7 +3,6 @@ use std::convert::TryInto; use ergo_lib::{ chain::{ ergo_box::box_builder::{ErgoBoxCandidateBuilder, ErgoBoxCandidateBuilderError}, - transaction::unsigned::UnsignedTransaction, }, ergotree_interpreter::sigma_protocol::prover::ContextExtension, ergotree_ir::{ @@ -14,10 +13,14 @@ use ergo_lib::{ serialization::SigmaParsingError, }, wallet::{ - box_selector::{BoxSelection, BoxSelector, BoxSelectorError, SimpleBoxSelector}, + box_selector::{BoxSelection, BoxSelectorError}, tx_builder::{TxBuilder, TxBuilderError}, }, }; +use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_node_interface::node_interface::NodeError; use thiserror::Error; @@ -26,13 +29,13 @@ use crate::{ make_collected_oracle_box_candidate, make_oracle_box_candidate, OracleBox, OracleBoxWrapper, }, explorer_api::ergo_explorer_transaction_link, - node_interface::{SignTransaction, SubmitTransaction}, oracle_config::BASE_FEE, oracle_state::{DataSourceError, LocalDatapointBoxSource}, oracle_types::BlockHeight, spec_token::SpecToken, - wallet::{WalletDataError, WalletDataSource}, }; +use crate::node_interface::node_api::NodeApiTrait; +use crate::oracle_config::ORACLE_CONFIG; #[derive(Debug, Error)] pub enum ExtractRewardTokensActionError { @@ -48,6 +51,8 @@ pub enum ExtractRewardTokensActionError { Node(#[from] NodeError), #[error("box selector error: {0}")] BoxSelector(#[from] BoxSelectorError), + #[error("tx signing error: {0}")] + TxSigningError(#[from] TxSigningError), #[error("Sigma parsing error: {0}")] SigmaParse(#[from] SigmaParsingError), #[error("tx builder error: {0}")] @@ -60,14 +65,10 @@ pub enum ExtractRewardTokensActionError { NoChangeAddressSetInNode, #[error("IO error: {0}")] Io(#[from] std::io::Error), - #[error("WalletData error: {0}")] - WalletData(#[from] WalletDataError), } pub fn extract_reward_tokens( - wallet: &dyn WalletDataSource, - tx_signer: &dyn SignTransaction, - tx_submit: &dyn SubmitTransaction, + node_api: &dyn NodeApiTrait, local_datapoint_box_source: &dyn LocalDatapointBoxSource, rewards_destination_str: String, height: BlockHeight, @@ -75,15 +76,15 @@ pub fn extract_reward_tokens( let rewards_destination = AddressEncoder::unchecked_parse_network_address_from_str(&rewards_destination_str)?; let network_prefix = rewards_destination.network(); - let change_address = wallet - .get_change_address() - .map_err(ExtractRewardTokensActionError::WalletData)?; - let (unsigned_tx, num_reward_tokens) = build_extract_reward_tokens_tx( + let oracle_address = ORACLE_CONFIG.oracle_address.clone(); + let change_address = ORACLE_CONFIG.change_address.clone(); + let (context, num_reward_tokens) = build_extract_reward_tokens_tx( local_datapoint_box_source, - wallet, + node_api, rewards_destination.address(), height, - change_address.address(), + oracle_address, + change_address.unwrap().address(), )?; println!( @@ -93,8 +94,8 @@ pub fn extract_reward_tokens( let mut input = String::new(); std::io::stdin().read_line(&mut input)?; if input.trim() == "YES" { - let signed_tx = tx_signer.sign_transaction(&unsigned_tx)?; - let tx_id = tx_submit.submit_transaction(&signed_tx)?; + let signed_tx = node_api.sign_transaction(context)?; + let tx_id = node_api.submit_transaction(&signed_tx)?; crate::explorer_api::wait_for_tx_confirmation(signed_tx.id()); println!( "Transaction made. Check status here: {}", @@ -108,11 +109,12 @@ pub fn extract_reward_tokens( fn build_extract_reward_tokens_tx( local_datapoint_box_source: &dyn LocalDatapointBoxSource, - wallet: &dyn WalletDataSource, + node_api: &dyn NodeApiTrait, rewards_destination: Address, height: BlockHeight, + oracle_address: NetworkAddress, change_address: Address, -) -> Result<(UnsignedTransaction, u64), ExtractRewardTokensActionError> { +) -> Result<(TransactionContext, u64), ExtractRewardTokensActionError> { let in_oracle_box = local_datapoint_box_source .get_local_oracle_datapoint_box()? .ok_or(ExtractRewardTokensActionError::NoLocalDatapointBox)?; @@ -164,10 +166,9 @@ fn build_extract_reward_tokens_tx( builder.add_token(extracted_reward_tokens); let reward_box_candidate = builder.build()?; - let unspent_boxes = wallet.get_unspent_wallet_boxes()?; - // `BASE_FEE` each for the fee and the box holding the extracted reward tokens. let target_balance = BASE_FEE.checked_mul_u32(2).unwrap(); + let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), target_balance, [].into())?; let box_selector = SimpleBoxSelector::new(); let selection = box_selector.select(unspent_boxes, target_balance, &[])?; @@ -177,6 +178,7 @@ fn build_extract_reward_tokens_tx( boxes: input_boxes.try_into().unwrap(), change_boxes: selection.change_boxes, }; + let inputs = box_selection.boxes.clone().to_vec(); let mut tx_builder = TxBuilder::new( box_selection, vec![oracle_box_candidate, reward_box_candidate], @@ -190,7 +192,11 @@ fn build_extract_reward_tokens_tx( }; tx_builder.set_context_extension(in_oracle_box.get_box().box_id(), ctx_ext); let tx = tx_builder.build()?; - Ok((tx, num_reward_tokens - 1)) + let context = match TransactionContext::new(tx, inputs, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(ExtractRewardTokensActionError::TxSigningError(e)), + }; + Ok((context, num_reward_tokens - 1)) } else { Err(ExtractRewardTokensActionError::IncorrectDestinationAddress) } @@ -206,15 +212,15 @@ mod tests { use crate::contracts::oracle::OracleContractParameters; use crate::oracle_types::EpochCounter; use crate::pool_commands::test_utils::{ - find_input_boxes, generate_token_ids, make_datapoint_box, make_wallet_unspent_box, - OracleBoxMock, WalletDataMock, + generate_token_ids, make_datapoint_box, make_wallet_unspent_box, + OracleBoxMock, }; use ergo_lib::chain::ergo_state_context::ErgoStateContext; use ergo_lib::ergotree_interpreter::sigma_protocol::private_input::DlogProverInput; use ergo_lib::ergotree_ir::chain::address::AddressEncoder; - use ergo_lib::wallet::signing::TransactionContext; - use ergo_lib::wallet::Wallet; use sigma_test_util::force_any_val; + use crate::cli_commands::bootstrap::tests::SubmitTxMock; + use crate::node_interface::test_utils::MockNodeApi; #[test] fn test_extract_reward_tokens() { @@ -222,7 +228,6 @@ mod tests { let height = BlockHeight(ctx.pre_header.height); let token_ids = generate_token_ids(); let secret = force_any_val::(); - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); let oracle_pub_key = secret.public_image().h; let num_reward_tokens_in_box = 2; @@ -245,7 +250,7 @@ mod tests { .unwrap(); let local_datapoint_box_source = OracleBoxMock { oracle_box }; - let change_address = AddressEncoder::unchecked_parse_network_address_from_str( + let address = AddressEncoder::unchecked_parse_network_address_from_str( "9iHyKxXs2ZNLMp9N9gbUT9V8gTbsV7HED1C1VhttMfBUMPDyF7r", ) .unwrap(); @@ -255,35 +260,25 @@ mod tests { BASE_FEE.checked_mul_u32(10000).unwrap(), None, ); - let wallet_mock = WalletDataMock { + let mock_node_api = &MockNodeApi { unspent_boxes: vec![wallet_unspent_box], - change_address: change_address.clone(), + ctx: ctx.clone(), + secrets: vec![secret.clone().into()], + submitted_txs: &SubmitTxMock::default().transactions, + chain_submit_tx: None }; - let (tx, num_reward_tokens) = build_extract_reward_tokens_tx( + let (tx_context, num_reward_tokens) = build_extract_reward_tokens_tx( &local_datapoint_box_source, - &wallet_mock, - change_address.address(), + mock_node_api, + address.address(), height, - change_address.address(), + address.clone(), + address.address(), ) .unwrap(); assert_eq!(num_reward_tokens, num_reward_tokens_in_box - 1); - let mut possible_input_boxes = vec![local_datapoint_box_source - .get_local_oracle_datapoint_box() - .unwrap() - .unwrap() - .get_box() - .clone()]; - possible_input_boxes.append(&mut wallet_mock.get_unspent_wallet_boxes().unwrap()); - - let tx_context = TransactionContext::new( - tx.clone(), - find_input_boxes(tx, possible_input_boxes), - Vec::new(), - ) - .unwrap(); - let _signed_tx = wallet.sign_transaction(tx_context, &ctx, None).unwrap(); + let _signed_tx = mock_node_api.sign_transaction(tx_context).unwrap(); } } diff --git a/core/src/cli_commands/import_pool_update.rs b/core/src/cli_commands/import_pool_update.rs index 86577b4d..08afd321 100644 --- a/core/src/cli_commands/import_pool_update.rs +++ b/core/src/cli_commands/import_pool_update.rs @@ -3,11 +3,8 @@ use std::path::Path; use anyhow::anyhow; use crate::box_kind::OracleBox; -use crate::node_interface::node_api::NodeApi; use crate::oracle_state::LocalDatapointBoxSource; use crate::pool_config::PoolConfig; -use crate::pool_config::POOL_CONFIG; -use crate::scans::NodeScanRegistry; use crate::spec_token::OracleTokenId; use crate::spec_token::RewardTokenId; @@ -18,9 +15,6 @@ pub fn import_pool_update( reward_token_id: &RewardTokenId, current_pool_config_path: &Path, local_datapoint_box_source: &dyn LocalDatapointBoxSource, - scan_ids_path: &Path, - node_scan_registry: NodeScanRegistry, - node_api: &NodeApi, ) -> Result<(), anyhow::Error> { let new_pool_config_str = std::fs::read_to_string(new_pool_config_file.clone()).map_err(|e| { @@ -54,19 +48,6 @@ pub fn import_pool_update( anyhow!("Since new reward token is minted reward tokens from the current oracle box will be lost. Please transfer them to a different address with extract-reward-tokens command before importing new pool config.") ); } - - let new_token_ids = &new_pool_config.token_ids; - let old_token_ids = &POOL_CONFIG.token_ids; - if new_token_ids.pool_nft_token_id != old_token_ids.pool_nft_token_id - || new_token_ids.refresh_nft_token_id != old_token_ids.refresh_nft_token_id - || new_token_ids.oracle_token_id != old_token_ids.oracle_token_id - || new_token_ids.update_nft_token_id != old_token_ids.update_nft_token_id - || new_token_ids.ballot_token_id != old_token_ids.ballot_token_id - { - node_scan_registry.deregister_all_scans(node_api).unwrap(); - std::fs::remove_file(scan_ids_path) - .map_err(|e| anyhow!("Failed to remove scan ids file {:?}: {}", scan_ids_path, e))?; - } new_pool_config.save(current_pool_config_path)?; Ok(()) } diff --git a/core/src/cli_commands/prepare_update.rs b/core/src/cli_commands/prepare_update.rs index de51fb2e..c8e3cde3 100644 --- a/core/src/cli_commands/prepare_update.rs +++ b/core/src/cli_commands/prepare_update.rs @@ -30,6 +30,7 @@ use ergo_lib::{ tx_builder::{TxBuilder, TxBuilderError}, }, }; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_node_interface::node_interface::NodeError; use log::{debug, info}; use serde::{Deserialize, Serialize}; @@ -53,8 +54,7 @@ use crate::{ }, explorer_api::wait_for_txs_confirmation, node_interface::{ - node_api::{NodeApi, NodeApiError}, - SignTransactionWithInputs, SubmitTransaction, + node_api::{NodeApi, NodeApiError} }, oracle_config::{OracleConfig, BASE_FEE, ORACLE_CONFIG}, oracle_state::{DataSourceError, OraclePool}, @@ -64,9 +64,8 @@ use crate::{ spec_token::{ BallotTokenId, OracleTokenId, RefreshTokenId, RewardTokenId, TokenIdKind, UpdateTokenId, }, - wallet::{WalletDataError, WalletDataSource}, }; - +use crate::node_interface::node_api::NodeApiTrait; use super::bootstrap::{NftMintDetails, TokenMintDetails}; #[derive(Debug, Deserialize, Serialize, Clone)] @@ -94,12 +93,10 @@ pub fn prepare_update( let s = std::fs::read_to_string(config_file_name)?; let config_serde: UpdateBootstrapConfigSerde = serde_yaml::from_str(&s)?; - let change_address = node_api.get_change_address()?.address(); + let change_address = ORACLE_CONFIG.change_address.clone().unwrap().address(); let config = UpdateBootstrapConfig::try_from(config_serde)?; let update_bootstrap_input = PrepareUpdateInput { - wallet: node_api, - tx_signer: &node_api.node, - submit_tx: &node_api.node, + node_api, tx_fee: *BASE_FEE, erg_value_per_box: *BASE_FEE, change_address, @@ -181,9 +178,7 @@ fn print_hints_for_voting(height: BlockHeight) -> Result<(), PrepareUpdateError> } struct PrepareUpdateInput<'a> { - pub wallet: &'a dyn WalletDataSource, - pub tx_signer: &'a dyn SignTransactionWithInputs, - pub submit_tx: &'a dyn SubmitTransaction, + pub node_api: &'a dyn NodeApiTrait, pub tx_fee: BoxValue, pub erg_value_per_box: BoxValue, pub change_address: Address, @@ -260,7 +255,7 @@ impl<'a> PrepareUpdate<'a> { .build()?; output_candidates.push(remaining_funds.clone()); - let inputs = box_selection.boxes.clone(); + let inputs = box_selection.boxes.clone().to_vec(); let tx_builder = TxBuilder::new( box_selection, output_candidates, @@ -270,10 +265,14 @@ impl<'a> PrepareUpdate<'a> { ); let mint_token_tx = tx_builder.build()?; debug!("Mint token unsigned transaction: {:?}", mint_token_tx); + let context = match TransactionContext::new(mint_token_tx, inputs, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(PrepareUpdateError::TxSigningError(e)), + }; let signed_tx = self.input - .tx_signer - .sign_transaction_with_inputs(&mint_token_tx, inputs, None)?; + .node_api + .sign_transaction(context)?; self.num_transactions_left -= 1; self.built_txs.push(signed_tx.clone()); self.inputs_for_next_tx = self.filter_tx_outputs(signed_tx.outputs.clone()); @@ -306,19 +305,20 @@ impl<'a> PrepareUpdate<'a> { ) .build()?; output_candidates.push(remaining_funds.clone()); + let inputs = box_selection.boxes.clone().to_vec(); let tx_builder = TxBuilder::new( - box_selection.clone(), + box_selection, output_candidates, self.input.height.0, self.input.tx_fee, self.input.change_address.clone(), ); let refresh_box_tx = tx_builder.build()?; - let signed_refresh_box_tx = self.input.tx_signer.sign_transaction_with_inputs( - &refresh_box_tx, - box_selection.boxes.clone(), - None, - )?; + let context = match TransactionContext::new(refresh_box_tx, inputs, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(PrepareUpdateError::TxSigningError(e)), + }; + let signed_refresh_box_tx = self.input.node_api.sign_transaction(context)?; self.num_transactions_left -= 1; self.built_txs.push(signed_refresh_box_tx.clone()); self.inputs_for_next_tx = self.filter_tx_outputs(signed_refresh_box_tx.outputs.clone()); @@ -343,10 +343,9 @@ impl<'a> PrepareUpdate<'a> { let mut need_pool_contract_update = false; let mut need_ballot_contract_update = false; - let unspent_boxes = self.input.wallet.get_unspent_wallet_boxes()?; - debug!("unspent boxes: {:?}", unspent_boxes); let target_balance = self.calc_target_balance(self.num_transactions_left)?; debug!("target_balance: {:?}", target_balance); + let unspent_boxes = self.input.node_api.get_unspent_boxes_by_address(&self.oracle_config.oracle_address.to_base58(), target_balance, [].into())?; let box_selector = SimpleBoxSelector::new(); let box_selection = box_selector.select(unspent_boxes.clone(), target_balance, &[])?; debug!("box selection: {:?}", box_selection); @@ -502,7 +501,7 @@ impl<'a> PrepareUpdate<'a> { let mut submitted_tx_ids = Vec::new(); for tx in self.built_txs { - let _ = self.input.submit_tx.submit_transaction(&tx)?; + let _ = self.input.node_api.submit_transaction(&tx)?; submitted_tx_ids.push(tx.id()); } Ok((new_pool_config, submitted_tx_ids)) @@ -539,6 +538,8 @@ pub enum PrepareUpdateError { UpdateContract(#[from] UpdateContractError), #[error("Pool contract failed: {0}")] PoolContract(#[from] PoolContractError), + #[error("tx signing error: {0}")] + TxSigningError(#[from] TxSigningError), #[error("Bootstrap config file already exists")] ConfigFilenameAlreadyExists, #[error("No parameters were added for update")] @@ -547,8 +548,6 @@ pub enum PrepareUpdateError { NoMintDetails, #[error("Serde conversion error {0}")] SerdeConversion(#[from] SerdeConversionError), - #[error("WalletData error: {0}")] - WalletData(#[from] WalletDataError), #[error("Ballot contract error: {0}")] BallotContract(#[from] BallotContractError), #[error("Node API error: {0}")] @@ -569,12 +568,13 @@ mod test { wallet::Wallet, }; use sigma_test_util::force_any_val; - + use url::Url; use super::*; use crate::{ cli_commands::bootstrap::tests::SubmitTxMock, - pool_commands::test_utils::{LocalTxSigner, WalletDataMock}, + pool_commands::test_utils::LocalTxSigner, }; + use crate::node_interface::test_utils::MockNodeApi; #[test] fn test_prepare_update_transaction() { @@ -626,7 +626,6 @@ rescan_height: 141887 let old_oracle_config: OracleConfig = serde_yaml::from_str( r#" node_url: http://10.94.77.47:9052 -node_api_key: hello base_fee: 1100000 scan_start_height: 0 log_level: ~ @@ -649,7 +648,6 @@ data_point_source_custom_script: ~ oracle_address: network_address.clone(), ..old_oracle_config }; - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); let ergo_tree = network_address.address().script().unwrap(); let value = BASE_FEE.checked_mul_u32(10000).unwrap(); @@ -702,15 +700,13 @@ data_point_source_custom_script: ~ let height = BlockHeight(ctx.pre_header.height); let submit_tx = SubmitTxMock::default(); let prepare_update_input = PrepareUpdateInput { - wallet: &WalletDataMock { + node_api: &MockNodeApi { unspent_boxes: unspent_boxes.clone(), - change_address: change_address.clone(), - }, - tx_signer: &mut LocalTxSigner { - ctx: &ctx, - wallet: &wallet, + ctx: ctx.clone(), + secrets: vec![secret.clone().into()], + submitted_txs: &submit_tx.transactions, + chain_submit_tx: None }, - submit_tx: &submit_tx, tx_fee: *BASE_FEE, erg_value_per_box: *BASE_FEE, change_address: change_address.address(), diff --git a/core/src/cli_commands/transfer_oracle_token.rs b/core/src/cli_commands/transfer_oracle_token.rs index 5786bd5a..38323bfe 100644 --- a/core/src/cli_commands/transfer_oracle_token.rs +++ b/core/src/cli_commands/transfer_oracle_token.rs @@ -1,20 +1,21 @@ use std::convert::TryInto; use ergo_lib::{ - chain::{ - ergo_box::box_builder::ErgoBoxCandidateBuilderError, - transaction::unsigned::UnsignedTransaction, - }, + chain::ergo_box::box_builder::ErgoBoxCandidateBuilderError, ergotree_interpreter::sigma_protocol::prover::ContextExtension, ergotree_ir::{ chain::address::{Address, AddressEncoder, AddressEncoderError}, serialization::SigmaParsingError, }, wallet::{ - box_selector::{BoxSelection, BoxSelector, BoxSelectorError, SimpleBoxSelector}, + box_selector::{BoxSelection, BoxSelectorError}, tx_builder::{TxBuilder, TxBuilderError}, }, }; +use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_node_interface::node_interface::NodeError; use thiserror::Error; @@ -23,12 +24,12 @@ use crate::{ make_collected_oracle_box_candidate, make_oracle_box_candidate, OracleBox, OracleBoxWrapper, }, explorer_api::ergo_explorer_transaction_link, - node_interface::{SignTransaction, SubmitTransaction}, oracle_config::BASE_FEE, oracle_state::{DataSourceError, LocalDatapointBoxSource}, oracle_types::BlockHeight, - wallet::{WalletDataError, WalletDataSource}, }; +use crate::node_interface::node_api::NodeApiTrait; +use crate::oracle_config::ORACLE_CONFIG; #[derive(Debug, Error)] pub enum TransferOracleTokenActionError { @@ -45,6 +46,8 @@ pub enum TransferOracleTokenActionError { DataSourceError(#[from] DataSourceError), #[error("node error: {0}")] Node(#[from] NodeError), + #[error("tx signing error: {0}")] + TxSigningError(#[from] TxSigningError), #[error("box selector error: {0}")] BoxSelector(#[from] BoxSelectorError), #[error("Sigma parsing error: {0}")] @@ -59,29 +62,27 @@ pub enum TransferOracleTokenActionError { AddressEncoder(#[from] AddressEncoderError), #[error("IO error: {0}")] Io(#[from] std::io::Error), - #[error("WalletData error: {0}")] - WalletData(#[from] WalletDataError), } pub fn transfer_oracle_token( - wallet: &dyn WalletDataSource, - tx_signer: &dyn SignTransaction, - tx_submit: &dyn SubmitTransaction, + node_api: &dyn NodeApiTrait, local_datapoint_box_source: &dyn LocalDatapointBoxSource, rewards_destination_str: String, height: BlockHeight, ) -> Result<(), anyhow::Error> { let rewards_destination = AddressEncoder::unchecked_parse_network_address_from_str(&rewards_destination_str)?; + let oracle_address = ORACLE_CONFIG.oracle_address.clone(); let (change_address, network_prefix) = { - let net_address = wallet.get_change_address()?; + let net_address = ORACLE_CONFIG.change_address.clone().unwrap(); (net_address.address(), net_address.network()) }; - let unsigned_tx = build_transfer_oracle_token_tx( + let context = build_transfer_oracle_token_tx( local_datapoint_box_source, - wallet, + node_api, rewards_destination.address(), height, + oracle_address, change_address, )?; @@ -92,9 +93,8 @@ pub fn transfer_oracle_token( let mut input = String::new(); std::io::stdin().read_line(&mut input)?; if input.trim() == "YES" { - let signed_tx = tx_signer.sign_transaction(&unsigned_tx)?; - let tx_id = tx_submit.submit_transaction(&signed_tx)?; - crate::explorer_api::wait_for_tx_confirmation(signed_tx.id()); + let tx_id = node_api.sign_and_submit_transaction(context)?; + crate::explorer_api::wait_for_tx_confirmation(tx_id); println!( "Transaction made. Check status here: {}", ergo_explorer_transaction_link(tx_id, network_prefix) @@ -106,11 +106,12 @@ pub fn transfer_oracle_token( } fn build_transfer_oracle_token_tx( local_datapoint_box_source: &dyn LocalDatapointBoxSource, - wallet: &dyn WalletDataSource, + node_api: &dyn NodeApiTrait, oracle_token_destination: Address, height: BlockHeight, + oracle_address: NetworkAddress, change_address: Address, -) -> Result { +) -> Result, TransferOracleTokenActionError> { let in_oracle_box = local_datapoint_box_source .get_local_oracle_datapoint_box()? .ok_or(TransferOracleTokenActionError::NoLocalDatapointBox)?; @@ -146,16 +147,15 @@ fn build_transfer_oracle_token_tx( )? }; - let unspent_boxes = wallet.get_unspent_wallet_boxes()?; - let target_balance = *BASE_FEE; + let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), target_balance, [].into())?; let box_selector = SimpleBoxSelector::new(); let selection = box_selector.select(unspent_boxes, target_balance, &[])?; let mut input_boxes = vec![in_oracle_box.get_box().clone()]; input_boxes.append(selection.boxes.as_vec().clone().as_mut()); let box_selection = BoxSelection { - boxes: input_boxes.try_into().unwrap(), + boxes: input_boxes.clone().try_into().unwrap(), change_boxes: selection.change_boxes, }; let mut tx_builder = TxBuilder::new( @@ -171,7 +171,12 @@ fn build_transfer_oracle_token_tx( }; tx_builder.set_context_extension(in_oracle_box.get_box().box_id(), ctx_ext); let tx = tx_builder.build()?; - Ok(tx) + + let context = match TransactionContext::new(tx, input_boxes, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(TransferOracleTokenActionError::TxSigningError(e)), + }; + Ok(context) } else { Err(TransferOracleTokenActionError::IncorrectDestinationAddress) } @@ -187,15 +192,16 @@ mod tests { use crate::contracts::oracle::OracleContractParameters; use crate::oracle_types::EpochCounter; use crate::pool_commands::test_utils::{ - find_input_boxes, generate_token_ids, make_datapoint_box, make_wallet_unspent_box, - OracleBoxMock, WalletDataMock, + generate_token_ids, make_datapoint_box, make_wallet_unspent_box, + OracleBoxMock, }; use ergo_lib::chain::ergo_state_context::ErgoStateContext; use ergo_lib::ergotree_interpreter::sigma_protocol::private_input::DlogProverInput; use ergo_lib::ergotree_ir::chain::address::AddressEncoder; - use ergo_lib::wallet::signing::TransactionContext; use ergo_lib::wallet::Wallet; use sigma_test_util::force_any_val; + use crate::cli_commands::bootstrap::tests::SubmitTxMock; + use crate::node_interface::test_utils::MockNodeApi; #[test] fn test_transfer_oracle_datapoint() { @@ -224,7 +230,7 @@ mod tests { .unwrap(); let local_datapoint_box_source = OracleBoxMock { oracle_box }; - let change_address = AddressEncoder::unchecked_parse_network_address_from_str( + let address = AddressEncoder::unchecked_parse_network_address_from_str( "9iHyKxXs2ZNLMp9N9gbUT9V8gTbsV7HED1C1VhttMfBUMPDyF7r", ) .unwrap(); @@ -234,34 +240,23 @@ mod tests { BASE_FEE.checked_mul_u32(10000).unwrap(), None, ); - let wallet_mock = WalletDataMock { + let mock_node_api = &MockNodeApi { unspent_boxes: vec![wallet_unspent_box], - change_address: change_address.clone(), + ctx: ctx.clone(), + secrets: vec![secret.clone().into()], + submitted_txs: &SubmitTxMock::default().transactions, + chain_submit_tx: None }; - let tx = build_transfer_oracle_token_tx( + let context = build_transfer_oracle_token_tx( &local_datapoint_box_source, - &wallet_mock, - change_address.address(), + mock_node_api, + address.address(), height, - change_address.address(), - ) - .unwrap(); - - let mut possible_input_boxes = vec![local_datapoint_box_source - .get_local_oracle_datapoint_box() - .unwrap() - .unwrap() - .get_box() - .clone()]; - possible_input_boxes.append(&mut wallet_mock.get_unspent_wallet_boxes().unwrap()); - - let tx_context = TransactionContext::new( - tx.clone(), - find_input_boxes(tx, possible_input_boxes), - Vec::new(), + address.clone(), + address.address(), ) .unwrap(); - let _signed_tx = wallet.sign_transaction(tx_context, &ctx, None).unwrap(); + let _signed_tx = wallet.sign_transaction(context, &ctx, None).unwrap(); } } diff --git a/core/src/cli_commands/update_pool.rs b/core/src/cli_commands/update_pool.rs index 6de21956..18b9184a 100644 --- a/core/src/cli_commands/update_pool.rs +++ b/core/src/cli_commands/update_pool.rs @@ -2,25 +2,27 @@ use ergo_lib::{ chain::{ ergo_box::box_builder::ErgoBoxCandidateBuilder, ergo_box::box_builder::ErgoBoxCandidateBuilderError, - transaction::unsigned::UnsignedTransaction, }, ergo_chain_types::blake2b256_hash, ergotree_interpreter::sigma_protocol::prover::ContextExtension, ergotree_ir::chain::{ address::Address, - ergo_box::{ErgoBox, NonMandatoryRegisterId}, + ergo_box::{NonMandatoryRegisterId}, }, ergotree_ir::serialization::SigmaSerializable, wallet::{ - box_selector::{BoxSelection, BoxSelector, BoxSelectorError, SimpleBoxSelector}, - signing::{TransactionContext, TxSigningError}, + box_selector::{BoxSelection, BoxSelectorError}, + signing::TxSigningError, tx_builder::{TxBuilder, TxBuilderError}, }, }; use ergo_node_interface::node_interface::NodeError; use log::{error, info}; use std::convert::TryInto; - +use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; +use ergo_lib::wallet::signing::TransactionContext; use crate::{ box_kind::{ make_pool_box_candidate_unchecked, BallotBox, CastBallotBoxVoteParameters, PoolBox, @@ -28,7 +30,6 @@ use crate::{ }, contracts::pool::PoolContract, explorer_api::ergo_explorer_transaction_link, - node_interface::{SignTransaction, SubmitTransaction}, oracle_config::BASE_FEE, oracle_state::{ DataSourceError, OraclePool, PoolBoxSource, UpdateBoxSource, VoteBallotBoxesSource, @@ -36,9 +37,10 @@ use crate::{ oracle_types::BlockHeight, pool_config::{PoolConfig, POOL_CONFIG}, spec_token::{RewardTokenId, SpecToken, TokenIdKind}, - wallet::{WalletDataError, WalletDataSource}, }; use thiserror::Error; +use crate::node_interface::node_api::NodeApiTrait; +use crate::oracle_config::ORACLE_CONFIG; #[derive(Debug, Error)] pub enum UpdatePoolError { @@ -68,15 +70,11 @@ pub enum UpdatePoolError { YamlError(#[from] serde_yaml::Error), #[error("Update pool: could not find unspent wallot boxes that do not contain ballot tokens")] NoUsableWalletBoxes, - #[error("WalletData error: {0}")] - WalletData(#[from] WalletDataError), } pub fn update_pool( op: &OraclePool, - wallet: &dyn WalletDataSource, - tx_signer: &dyn SignTransaction, - tx_submit: &dyn SubmitTransaction, + node_api: &dyn NodeApiTrait, new_reward_tokens: Option>, height: BlockHeight, ) -> Result<(), anyhow::Error> { @@ -90,8 +88,9 @@ pub fn update_pool( "Reward token id in pool_config_updated.yaml does not match the one from the command line" ); } + let oracle_address = ORACLE_CONFIG.oracle_address.clone(); let (change_address, network_prefix) = { - let net_addr = wallet.get_change_address()?; + let net_addr = ORACLE_CONFIG.change_address.clone().unwrap(); (net_addr.address(), net_addr.network()) }; @@ -111,19 +110,20 @@ pub fn update_pool( new_reward_tokens.clone(), ); - let tx = build_update_pool_box_tx( + let context = build_update_pool_box_tx( op.get_pool_box_source(), op.get_ballot_boxes_source(), - wallet, + node_api, op.get_update_box_source(), new_reward_tokens.clone(), height, + oracle_address, change_address, new_pool_contract, )?; - log::debug!("Signing update pool box tx: {:#?}", tx); - let signed_tx = tx_signer.sign_transaction(&tx.spending_tx)?; + log::debug!("Signing update pool box tx: {:#?}", context); + let signed_tx = node_api.sign_transaction(context)?; println!( "YOU WILL BE SUBMITTING AN UPDATE TO THE POOL CONTRACT:\ @@ -142,7 +142,7 @@ pub fn update_pool( let mut input = String::new(); std::io::stdin().read_line(&mut input)?; if input.trim_end() == "YES" { - let tx_id_str = tx_submit.submit_transaction(&signed_tx)?; + let tx_id_str = node_api.submit_transaction(&signed_tx)?; crate::explorer_api::wait_for_tx_confirmation(signed_tx.id()); println!( "Update pool box transaction submitted: view here, {}", @@ -267,10 +267,11 @@ fn remind_send_minted_tokens_to_oracles( fn build_update_pool_box_tx( pool_box_source: &dyn PoolBoxSource, ballot_boxes: &dyn VoteBallotBoxesSource, - wallet: &dyn WalletDataSource, + node_api: &dyn NodeApiTrait, update_box: &dyn UpdateBoxSource, new_reward_tokens: Option>, height: BlockHeight, + oracle_address: NetworkAddress, change_address: Address, new_pool_contract: PoolContract, ) -> Result, UpdatePoolError> { @@ -336,27 +337,6 @@ fn build_update_pool_box_tx( update_box_candidate.add_token(update_box.update_nft()); let update_box_candidate = update_box_candidate.build()?; - // Find unspent boxes without ballot token, see: https://github.com/ergoplatform/oracle-core/pull/80#issuecomment-1200258458 - let unspent_boxes: Vec = wallet - .get_unspent_wallet_boxes()? - .into_iter() - .filter(|wallet_box| { - wallet_box - .tokens - .as_ref() - .and_then(|tokens| { - tokens - .iter() - .find(|token| token.token_id == update_box.ballot_token_id()) - }) - .is_none() - }) - .collect(); - if unspent_boxes.is_empty() { - error!("Could not find unspent wallet boxes that do not contain ballot token. Please move ballot tokens to another address"); - return Err(UpdatePoolError::NoUsableWalletBoxes); - } - let target_balance = *BASE_FEE; let target_tokens = if reward_tokens.token_id.token_id() != old_pool_box.reward_token().token_id() { @@ -364,6 +344,21 @@ fn build_update_pool_box_tx( } else { vec![] }; + + // Find unspent boxes without ballot token, see: https://github.com/ergoplatform/oracle-core/pull/80#issuecomment-1200258458 + let unspent_boxes = node_api + .get_unspent_boxes_by_address_with_token_filter_option( + &oracle_address.to_base58(), + target_balance, + target_tokens.clone(), + vec![update_box.ballot_token_id()] + )?; + + if unspent_boxes.is_empty() { + error!("Could not find unspent wallet boxes that do not contain ballot token. Please move ballot tokens to another address"); + return Err(UpdatePoolError::NoUsableWalletBoxes); + } + let box_selector = SimpleBoxSelector::new(); let selection = box_selector.select(unspent_boxes, target_balance, &target_tokens)?; let mut input_boxes = vec![old_pool_box.get_box().clone(), update_box.get_box().clone()]; @@ -375,7 +370,7 @@ fn build_update_pool_box_tx( ); input_boxes.extend_from_slice(selection.boxes.as_vec()); let box_selection = BoxSelection { - boxes: input_boxes.try_into().unwrap(), + boxes: input_boxes.clone().try_into().unwrap(), change_boxes: selection.change_boxes, }; @@ -395,7 +390,7 @@ fn build_update_pool_box_tx( } let mut tx_builder = TxBuilder::new( - box_selection.clone(), + box_selection, outputs.clone(), height.0, *BASE_FEE, @@ -415,11 +410,11 @@ fn build_update_pool_box_tx( ) } let unsigned_tx = tx_builder.build()?; - Ok(TransactionContext::new( - unsigned_tx, - box_selection.boxes.into(), - vec![], - )?) + let context = match TransactionContext::new(unsigned_tx, input_boxes, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(UpdatePoolError::TxSigningError(e)), + }; + Ok(context) } #[cfg(test)] @@ -439,7 +434,6 @@ mod tests { }, serialization::SigmaSerializable, }, - wallet::Wallet, }; use sigma_test_util::force_any_val; use std::convert::TryInto; @@ -458,11 +452,13 @@ mod tests { oracle_types::{BlockHeight, EpochCounter}, pool_commands::test_utils::{ generate_token_ids, make_wallet_unspent_box, BallotBoxesMock, PoolBoxMock, - UpdateBoxMock, WalletDataMock, + UpdateBoxMock, }, spec_token::{RefreshTokenId, RewardTokenId, SpecToken, TokenIdKind}, }; - + use crate::cli_commands::bootstrap::tests::SubmitTxMock; + use crate::node_interface::node_api::NodeApiTrait; + use crate::node_interface::test_utils::MockNodeApi; use super::build_update_pool_box_tx; fn force_any_tokenid() -> TokenId { @@ -608,15 +604,17 @@ mod tests { BASE_FEE.checked_mul_u32(4_000_000_000).unwrap(), Some(vec![new_reward_tokens.clone().into()].try_into().unwrap()), ); - let change_address = AddressEncoder::unchecked_parse_network_address_from_str( + let address = AddressEncoder::unchecked_parse_network_address_from_str( "9iHyKxXs2ZNLMp9N9gbUT9V8gTbsV7HED1C1VhttMfBUMPDyF7r", ) .unwrap(); - let wallet_mock = WalletDataMock { + let mock_node_api = &MockNodeApi { unspent_boxes: vec![wallet_unspent_box], - change_address: change_address.clone(), + ctx: ctx.clone(), + secrets: vec![secret.clone().into()], + submitted_txs: &SubmitTxMock::default().transactions, + chain_submit_tx: None }; - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); let update_mock = UpdateBoxMock { update_box: UpdateBoxWrapper::new( update_box, @@ -639,18 +637,19 @@ mod tests { .unwrap(), }; - let update_tx = build_update_pool_box_tx( + let tx_context = build_update_pool_box_tx( &pool_mock, &ballot_boxes_mock, - &wallet_mock, + mock_node_api, &update_mock, Some(new_reward_tokens), BlockHeight(height.0 + 1), - change_address.address(), + address.clone(), + address.address(), new_pool_contract, ) .unwrap(); - wallet.sign_transaction(update_tx, &ctx, None).unwrap(); + mock_node_api.sign_transaction(tx_context).unwrap(); } } diff --git a/core/src/cli_commands/vote_update_pool.rs b/core/src/cli_commands/vote_update_pool.rs index 76e2c770..da9c37fb 100644 --- a/core/src/cli_commands/vote_update_pool.rs +++ b/core/src/cli_commands/vote_update_pool.rs @@ -3,16 +3,20 @@ use std::convert::{TryFrom, TryInto}; use ergo_lib::{ chain::{ ergo_box::box_builder::ErgoBoxCandidateBuilderError, - transaction::unsigned::UnsignedTransaction, }, ergo_chain_types::{Digest32, DigestNError, EcPoint}, ergotree_interpreter::sigma_protocol::prover::ContextExtension, ergotree_ir::chain::address::Address, wallet::{ - box_selector::{BoxSelection, BoxSelector, BoxSelectorError, SimpleBoxSelector}, + box_selector::{BoxSelection, BoxSelectorError}, tx_builder::{TxBuilder, TxBuilderError}, }, }; +use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::ergotree_ir::chain::token::Token; +use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_node_interface::node_interface::NodeError; use crate::{ @@ -21,15 +25,14 @@ use crate::{ BallotContract, BallotContractError, BallotContractInputs, BallotContractParameters, }, explorer_api::ergo_explorer_transaction_link, - node_interface::{SignTransaction, SubmitTransaction}, oracle_config::{BASE_FEE, ORACLE_CONFIG}, oracle_state::{DataSourceError, LocalBallotBoxSource}, oracle_types::BlockHeight, pool_config::{TokenIds, POOL_CONFIG}, spec_token::{RewardTokenId, SpecToken, TokenIdKind}, - wallet::{WalletDataError, WalletDataSource}, }; use thiserror::Error; +use crate::node_interface::node_api::NodeApiTrait; #[derive(Debug, Error)] pub enum VoteUpdatePoolError { @@ -53,15 +56,13 @@ pub enum VoteUpdatePoolError { Digest(#[from] DigestNError), #[error("Vote update pool: Ballot contract error {0}")] BallotContract(#[from] BallotContractError), - #[error("WalletData error: {0}")] - WalletData(#[from] WalletDataError), + #[error("tx signing error: {0}")] + TxSigningError(#[from] TxSigningError), } #[allow(clippy::too_many_arguments)] pub fn vote_update_pool( - wallet: &dyn WalletDataSource, - tx_signer: &dyn SignTransaction, - tx_submit: &dyn SubmitTransaction, + node_api: &dyn NodeApiTrait, local_ballot_box_source: &dyn LocalBallotBoxSource, new_pool_box_address_hash_str: String, reward_token_opt: Option>, @@ -69,7 +70,8 @@ pub fn vote_update_pool( height: BlockHeight, ballot_contract: &BallotContract, ) -> Result<(), anyhow::Error> { - let change_network_address = wallet.get_change_address()?; + let oracle_address = ORACLE_CONFIG.oracle_address.clone(); + let change_network_address = ORACLE_CONFIG.change_address.clone().unwrap(); let network_prefix = change_network_address.network(); let new_pool_box_address_hash = Digest32::try_from(new_pool_box_address_hash_str)?; let ballot_token_owner = @@ -78,18 +80,19 @@ pub fn vote_update_pool( } else { return Err(VoteUpdatePoolError::IncorrectBallotTokenOwnerAddress.into()); }; - let unsigned_tx = if let Some(local_ballot_box) = local_ballot_box_source.get_ballot_box()? { + let context = if let Some(local_ballot_box) = local_ballot_box_source.get_ballot_box()? { log::debug!("Found local ballot box"); // Note: the ballot box contains the ballot token, but the box is guarded by the contract, // which stipulates that the address in R4 is the 'owner' of the token build_tx_with_existing_ballot_box( &local_ballot_box, ballot_contract, - wallet, + node_api, new_pool_box_address_hash, reward_token_opt.clone(), update_box_creation_height, height, + oracle_address, change_network_address.address(), ballot_token_owner.as_ref(), )? @@ -98,7 +101,7 @@ pub fn vote_update_pool( // Note: the ballot box contains the ballot token, but the box is guarded by the contract, // Ballot token is assumed to be in some unspent box of the node's wallet. build_tx_for_first_ballot_box( - wallet, + node_api, new_pool_box_address_hash, reward_token_opt.clone(), update_box_creation_height, @@ -109,6 +112,7 @@ pub fn vote_update_pool( .contract_parameters(), &POOL_CONFIG.token_ids, height, + oracle_address, change_network_address.address(), )? }; @@ -131,14 +135,14 @@ pub fn vote_update_pool( if input.trim_end() == "YES" { log::debug!( "Signing vote tx: {:?} ", - &serde_json::to_string_pretty(&unsigned_tx) + &serde_json::to_string_pretty(&context.spending_tx) ); - let signed_tx = tx_signer.sign_transaction(&unsigned_tx)?; + let signed_tx = node_api.sign_transaction(context)?; log::debug!( "Submitting signed vote tx: {:?} ", &serde_json::to_string_pretty(&signed_tx) ); - let tx_id_str = tx_submit.submit_transaction(&signed_tx)?; + let tx_id_str = node_api.submit_transaction(&signed_tx)?; crate::explorer_api::wait_for_tx_confirmation(signed_tx.id()); println!( "Transaction made. Check status here: {}", @@ -154,15 +158,15 @@ pub fn vote_update_pool( fn build_tx_with_existing_ballot_box( in_ballot_box: &BallotBoxWrapper, ballot_contract: &BallotContract, - wallet: &dyn WalletDataSource, + node_api: &dyn NodeApiTrait, new_pool_box_address_hash: Digest32, reward_token_opt: Option>, update_box_creation_height: BlockHeight, height: BlockHeight, + oracle_address: NetworkAddress, change_address: Address, ballot_token_owner_pk: &EcPoint, -) -> Result { - let unspent_boxes = wallet.get_unspent_wallet_boxes()?; +) -> Result, VoteUpdatePoolError> { #[allow(clippy::todo)] let ballot_box_candidate = make_local_ballot_box_candidate( ballot_contract.ergo_tree(), @@ -174,12 +178,13 @@ fn build_tx_with_existing_ballot_box( in_ballot_box.get_box().value, height, )?; + let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), *BASE_FEE, vec![])?; let box_selector = SimpleBoxSelector::new(); let selection = box_selector.select(unspent_boxes, *BASE_FEE, &[])?; let mut input_boxes = vec![in_ballot_box.get_box().clone()]; input_boxes.append(selection.boxes.as_vec().clone().as_mut()); let box_selection = BoxSelection { - boxes: input_boxes.try_into().unwrap(), + boxes: input_boxes.clone().try_into().unwrap(), change_boxes: selection.change_boxes, }; let mut tx_builder = TxBuilder::new( @@ -195,12 +200,16 @@ fn build_tx_with_existing_ballot_box( }; tx_builder.set_context_extension(in_ballot_box.get_box().box_id(), ctx_ext); let tx = tx_builder.build()?; - Ok(tx) + let context = match TransactionContext::new(tx, input_boxes, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(VoteUpdatePoolError::TxSigningError(e)), + }; + Ok(context) } #[allow(clippy::too_many_arguments)] fn build_tx_for_first_ballot_box( - wallet: &dyn WalletDataSource, + node_api: &dyn NodeApiTrait, new_pool_box_address_hash: Digest32, reward_token_opt: Option>, update_box_creation_height: BlockHeight, @@ -208,9 +217,9 @@ fn build_tx_for_first_ballot_box( ballot_contract_parameters: &BallotContractParameters, token_ids: &TokenIds, height: BlockHeight, + oracle_address: NetworkAddress, change_address: Address, -) -> Result { - let unspent_boxes = wallet.get_unspent_wallet_boxes()?; +) -> Result, VoteUpdatePoolError> { let out_ballot_box_value = ballot_contract_parameters.min_storage_rent(); let inputs = BallotContractInputs::build_with( ballot_contract_parameters.clone(), @@ -233,15 +242,22 @@ fn build_tx_for_first_ballot_box( )?; let box_selector = SimpleBoxSelector::new(); let selection_target_balance = out_ballot_box_value.checked_add(&BASE_FEE).unwrap(); + let target_tokens: Vec = vec![ballot_token.into()]; + let unspent_boxes = node_api + .get_unspent_boxes_by_address( + &oracle_address.to_base58(), + selection_target_balance, + target_tokens.clone() + )?; let selection = box_selector.select( unspent_boxes, selection_target_balance, - &[ballot_token.into()], - )?; + &target_tokens)?; let box_selection = BoxSelection { boxes: selection.boxes.as_vec().clone().try_into().unwrap(), change_boxes: selection.change_boxes, }; + let input_boxes = box_selection.boxes.as_vec().clone(); let mut tx_builder = TxBuilder::new( box_selection, vec![ballot_box_candidate], @@ -255,7 +271,11 @@ fn build_tx_for_first_ballot_box( }; tx_builder.set_context_extension(selection.boxes.first().box_id(), ctx_ext); let tx = tx_builder.build()?; - Ok(tx) + let context = match TransactionContext::new(tx, input_boxes, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(VoteUpdatePoolError::TxSigningError(e)), + }; + Ok(context) } #[cfg(test)] @@ -271,7 +291,6 @@ mod tests { ergo_box::{box_value::BoxValue, BoxTokens, ErgoBox}, token::{Token, TokenId}, }, - wallet::{signing::TransactionContext, Wallet}, }; use sigma_test_util::force_any_val; @@ -281,12 +300,13 @@ mod tests { oracle_config::BASE_FEE, oracle_types::{BlockHeight, EpochLength}, pool_commands::test_utils::{ - find_input_boxes, generate_token_ids, make_wallet_unspent_box, WalletDataMock, + generate_token_ids, make_wallet_unspent_box, }, spec_token::{RewardTokenId, SpecToken, TokenIdKind}, - wallet::WalletDataSource, }; - + use crate::cli_commands::bootstrap::tests::SubmitTxMock; + use crate::node_interface::node_api::NodeApiTrait; + use crate::node_interface::test_utils::MockNodeApi; use super::{build_tx_for_first_ballot_box, build_tx_with_existing_ballot_box}; #[test] @@ -296,8 +316,7 @@ mod tests { let secret = force_any_val::(); let new_pool_box_address_hash = force_any_val::(); - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); - let change_address = AddressEncoder::unchecked_parse_network_address_from_str( + let address = AddressEncoder::unchecked_parse_network_address_from_str( "9iHyKxXs2ZNLMp9N9gbUT9V8gTbsV7HED1C1VhttMfBUMPDyF7r", ) .unwrap(); @@ -318,9 +337,12 @@ mod tests { BASE_FEE.checked_mul_u32(100_000_000).unwrap(), Some(BoxTokens::from_vec(vec![ballot_token]).unwrap()), ); - let wallet_mock = WalletDataMock { + let mock_node_api = &MockNodeApi { unspent_boxes: vec![wallet_unspent_box], - change_address: change_address.clone(), + ctx: ctx.clone(), + secrets: vec![secret.clone().into()], + submitted_txs: &SubmitTxMock::default().transactions, + chain_submit_tx: None }; let new_reward_token = SpecToken { @@ -328,14 +350,14 @@ mod tests { amount: 100_000.try_into().unwrap(), }; - let ballot_token_owner = if let Address::P2Pk(ballot_token_owner) = change_address.address() + let ballot_token_owner = if let Address::P2Pk(ballot_token_owner) = address.address() { ballot_token_owner.h } else { panic!("Expected P2PK address"); }; - let unsigned_tx = build_tx_for_first_ballot_box( - &wallet_mock, + let tx_context = build_tx_for_first_ballot_box( + mock_node_api, new_pool_box_address_hash, Some(new_reward_token), BlockHeight(height.0) - 3, @@ -343,18 +365,12 @@ mod tests { ballot_contract_inputs.contract_parameters(), &token_ids, height, - change_address.address(), + address.clone(), + address.address(), ) .unwrap(); - let tx_context = TransactionContext::new( - unsigned_tx.clone(), - find_input_boxes(unsigned_tx, wallet_mock.get_unspent_wallet_boxes().unwrap()), - Vec::new(), - ) - .unwrap(); - - let _signed_tx = wallet.sign_transaction(tx_context, &ctx, None).unwrap(); + let _signed_tx = mock_node_api.sign_transaction(tx_context).unwrap(); } #[test] @@ -364,8 +380,7 @@ mod tests { let secret = force_any_val::(); let new_pool_box_address_hash = force_any_val::(); - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); - let change_address = AddressEncoder::unchecked_parse_network_address_from_str( + let address = AddressEncoder::unchecked_parse_network_address_from_str( "9iHyKxXs2ZNLMp9N9gbUT9V8gTbsV7HED1C1VhttMfBUMPDyF7r", ) .unwrap(); @@ -410,14 +425,17 @@ mod tests { BASE_FEE.checked_mul_u32(100_000_000).unwrap(), None, ); - let wallet_mock = WalletDataMock { + let mock_node_api = &MockNodeApi { unspent_boxes: vec![wallet_unspent_box], - change_address: change_address.clone(), + ctx: ctx.clone(), + secrets: vec![secret.clone().into()], + submitted_txs: &SubmitTxMock::default().transactions, + chain_submit_tx: None }; - let unsigned_tx = build_tx_with_existing_ballot_box( + let tx_context = build_tx_with_existing_ballot_box( &ballot_box, &ballot_contract, - &wallet_mock, + mock_node_api, new_pool_box_address_hash, Some(SpecToken { token_id: token_ids.reward_token_id, @@ -425,17 +443,12 @@ mod tests { }), height - EpochLength(3), height, - change_address.address(), + address.clone(), + address.address(), secret.public_image().h.as_ref(), ) .unwrap(); - let mut input_boxes = vec![in_ballot_box]; - input_boxes.append(wallet_mock.get_unspent_wallet_boxes().unwrap().as_mut()); - let boxes_to_spend = find_input_boxes(unsigned_tx.clone(), input_boxes); - assert!(!boxes_to_spend.is_empty()); - let tx_context = TransactionContext::new(unsigned_tx, boxes_to_spend, Vec::new()).unwrap(); - - let _signed_tx = wallet.sign_transaction(tx_context, &ctx, None).unwrap(); + let _signed_tx = mock_node_api.sign_transaction(tx_context).unwrap(); } } diff --git a/core/src/get_boxes.rs b/core/src/get_boxes.rs new file mode 100644 index 00000000..8ad7c3a9 --- /dev/null +++ b/core/src/get_boxes.rs @@ -0,0 +1,39 @@ +use crate::node_interface::node_api::{NodeApi, NodeApiError}; +use crate::oracle_config::ORACLE_CONFIG; + +use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox; +use ergo_node_interface::node_interface::NodeError; +use thiserror::Error; + +mod generic_token_fetch; +mod registry; + +pub use generic_token_fetch::*; +pub use registry::*; +use crate::spec_token::TokenIdKind; + +#[derive(Debug, Error)] +pub enum GetBoxesError { + #[error("node error: {0}")] + NodeError(#[from] NodeError), + #[error("node api error: {0}")] + NodeApiError(#[from] NodeApiError), + #[error("no boxes found")] + NoBoxesFound, + #[error("IO error: {0}")] + IoError(#[from] std::io::Error), +} + +pub trait GetBoxes: TokenIdKind { + fn get_boxes(&self) -> Result, GetBoxesError> { + let node_api = NodeApi::new( + &ORACLE_CONFIG.node_url + ); + let boxes = node_api.get_all_unspent_boxes_by_token_id(&self.token_id())?; + Ok(boxes) + } + + fn get_box(&self) -> Result, GetBoxesError> { + Ok(self.get_boxes()?.first().cloned()) + } +} diff --git a/core/src/get_boxes/generic_token_fetch.rs b/core/src/get_boxes/generic_token_fetch.rs new file mode 100644 index 00000000..d92db812 --- /dev/null +++ b/core/src/get_boxes/generic_token_fetch.rs @@ -0,0 +1,64 @@ +use crate::spec_token::TokenIdKind; +use derive_more::From; +use derive_more::Into; +use ergo_lib::ergo_chain_types::Digest32; +use ergo_lib::ergotree_ir::chain::token::TokenId; +use serde::Deserialize; +use serde::Serialize; + +use super::GetBoxes; +use super::GetBoxesError; + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, From, Into)] +#[serde(try_from = "String", into = "String")] +pub struct GenericTokenFetch { + token_id: TokenId, + fantom: std::marker::PhantomData, +} + +impl GenericTokenFetch { + pub fn new(token_id: TokenId) -> Self { + Self { + token_id, + fantom: std::marker::PhantomData, + } + } + + pub fn register(token_id: &T) -> Result { + let token_id = token_id.token_id(); + Ok(GenericTokenFetch:: { + token_id, + fantom: std::marker::PhantomData, + }) + } +} + +impl TryFrom for GenericTokenFetch { + type Error = GetBoxesError; + + fn try_from(value: String) -> Result { + let token_id = Digest32::try_from(value).unwrap().into(); + Ok(GenericTokenFetch { + token_id, + fantom: std::marker::PhantomData, + }) + } +} + +impl From> for String { + fn from(token_fech: GenericTokenFetch) -> Self { + token_fech.token_id.into() + } +} + +impl TokenIdKind for GenericTokenFetch { + fn token_id(&self) -> TokenId { + self.token_id + } + + fn from_token_id_unchecked(token: TokenId) -> Self { + Self::new(token) + } +} + +impl GetBoxes for GenericTokenFetch {} \ No newline at end of file diff --git a/core/src/get_boxes/registry.rs b/core/src/get_boxes/registry.rs new file mode 100644 index 00000000..fedc76b8 --- /dev/null +++ b/core/src/get_boxes/registry.rs @@ -0,0 +1,59 @@ +use crate::pool_config::POOL_CONFIG; +use crate::spec_token::BallotTokenId; +use crate::spec_token::BuybackTokenId; +use crate::spec_token::OracleTokenId; +use crate::spec_token::PoolTokenId; +use crate::spec_token::RefreshTokenId; +use crate::spec_token::UpdateTokenId; + +use ::serde::Deserialize; +use ::serde::Serialize; +use super::generic_token_fetch::GenericTokenFetch; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct TokenFetchRegistry { + #[serde(rename = "All Datapoints Fetch")] + pub oracle_token_fetch: GenericTokenFetch, + #[serde(rename = "Pool Box Fetch")] + pub pool_token_fetch: GenericTokenFetch, + #[serde(rename = "Ballot Box Fetch")] + pub ballot_token_fetch: GenericTokenFetch, + #[serde(rename = "Refresh Box Fetch")] + pub refresh_token_fetch: GenericTokenFetch, + #[serde(rename = "Update Box Fetch")] + pub update_token_fetch: GenericTokenFetch, + pub buyback_token_fetch: Option>, +} + +impl TokenFetchRegistry { + + pub fn load() -> Result { + log::info!("Registering token fetches"); + let pool_config = &POOL_CONFIG; + let oracle_token_fetch = + GenericTokenFetch::register(&pool_config.token_ids.oracle_token_id)?; + let pool_token_fetch = + GenericTokenFetch::register(&pool_config.token_ids.pool_nft_token_id)?; + let ballot_token_fetch = + GenericTokenFetch::register(&pool_config.token_ids.ballot_token_id)?; + let refresh_token_fetch = + GenericTokenFetch::register(&pool_config.token_ids.refresh_nft_token_id)?; + let update_token_fetch = + GenericTokenFetch::register(&pool_config.token_ids.update_nft_token_id)?; + let buyback_token_fetch = + if let Some(buyback_token_id) = pool_config.buyback_token_id.clone() { + Some(GenericTokenFetch::register(&buyback_token_id)?) + } else { + None + }; + let registry = Self { + oracle_token_fetch, + pool_token_fetch, + ballot_token_fetch, + refresh_token_fetch, + update_token_fetch, + buyback_token_fetch, + }; + Ok(registry) + } +} diff --git a/core/src/main.rs b/core/src/main.rs index be506993..c7a4276e 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -38,13 +38,11 @@ mod oracle_state; mod oracle_types; mod pool_commands; mod pool_config; -mod scans; +mod get_boxes; mod serde; mod spec_token; mod state; -mod templates; mod util; -mod wallet; #[cfg(test)] mod tests; @@ -52,7 +50,6 @@ mod tests; use action_report::ActionReportStorage; use action_report::PoolActionReport; use actions::PoolAction; -use anyhow::anyhow; use anyhow::Context; use clap::{Parser, Subcommand}; use crossbeam::channel::bounded; @@ -67,9 +64,7 @@ use log::LevelFilter; use metrics::start_metrics_server; use metrics::update_metrics; use node_interface::node_api::NodeApi; -use node_interface::try_ensure_wallet_unlocked; use oracle_config::ORACLE_CONFIG; -use oracle_config::ORACLE_SECRETS; use oracle_state::OraclePool; use oracle_types::BlockHeight; use pool_commands::build_action; @@ -78,8 +73,6 @@ use pool_commands::refresh::RefreshActionError; use pool_commands::PoolCommandError; use pool_config::DEFAULT_POOL_CONFIG_FILE_NAME; use pool_config::POOL_CONFIG; -use scans::get_scans_file_path; -use scans::wait_for_node_rescan; use spec_token::RewardTokenId; use spec_token::SpecToken; use spec_token::TokenIdKind; @@ -94,7 +87,6 @@ use std::sync::Arc; use std::sync::RwLock; use std::thread; use std::time::Duration; - use crate::actions::execute_action; use crate::address_util::pks_to_network_addresses; use crate::api::start_rest_server; @@ -107,7 +99,7 @@ use crate::oracle_config::DEFAULT_ORACLE_CONFIG_FILE_NAME; use crate::oracle_config::ORACLE_CONFIG_FILE_PATH; use crate::oracle_config::ORACLE_CONFIG_OPT; use crate::pool_config::POOL_CONFIG_FILE_PATH; -use crate::scans::NodeScanRegistry; +use crate::get_boxes::TokenFetchRegistry; const APP_VERSION: &str = concat!( "v", @@ -132,7 +124,7 @@ struct Args { /// Set path of pool configuration file to use. Default is ./pool_config.yaml #[clap(long)] pool_config_file: Option, - /// Set folder path for the data files (scanIDs.json, logs). Default is the current folder. + /// Set folder path for the data files (logs). Default is the current folder. #[clap(short, long)] data_dir: Option, } @@ -276,25 +268,21 @@ fn main() { .flatten(); logging::setup_log(cmdline_log_level, config_log_level, &data_dir_path); - scans::SCANS_DIR_PATH.set(data_dir_path).unwrap(); - let action_report_storage: Arc> = Arc::new(RwLock::new(ActionReportStorage::new())); log_on_launch(); let node_api = NodeApi::new( - ORACLE_SECRETS.node_api_key.clone(), - ORACLE_SECRETS.wallet_password.clone(), &ORACLE_CONFIG.node_url, ); - try_ensure_wallet_unlocked(&node_api); - wait_for_node_rescan(&node_api).unwrap(); - let pool_config = &POOL_CONFIG; + if !node_api.node.indexer_status().unwrap().is_active { + error!("Blockchain indexer is not active on the node"); + std::process::exit(exitcode::SOFTWARE); + } + let _ = node_api.wait_for_indexer_sync(); - let change_address = node_api - .get_change_address() - .expect("failed to get change address from the node"); + let change_address = ORACLE_CONFIG.change_address.clone().unwrap(); let network_prefix = change_address.network(); #[allow(clippy::wildcard_enum_match_arm)] @@ -316,7 +304,7 @@ fn main() { if generate_config_template { cli_commands::bootstrap::generate_bootstrap_config_template(yaml_config_name)?; } else { - cli_commands::bootstrap::bootstrap(yaml_config_name)?; + cli_commands::bootstrap::bootstrap(yaml_config_name, &node_api)?; } Ok(()) })() { @@ -336,9 +324,8 @@ fn main() { let tokio_runtime = tokio::runtime::Runtime::new().unwrap(); let (_, repost_receiver) = bounded::(1); - let node_scan_registry = - NodeScanRegistry::ensure_node_registered_scans(&node_api, pool_config).unwrap(); - let oracle_pool = Arc::new(OraclePool::new(&node_scan_registry).unwrap()); + let token_fetch_registry = TokenFetchRegistry::load().unwrap(); + let oracle_pool = Arc::new(OraclePool::new(&token_fetch_registry).unwrap()); let datapoint_source = RuntimeDataPointSource::new( POOL_CONFIG.data_point_source, ORACLE_CONFIG.data_point_source_custom_script.clone(), @@ -378,7 +365,7 @@ fn main() { error!("error: {:?}", e); } // Delay loop restart - thread::sleep(Duration::new(30, 0)); + thread::sleep(Duration::new(2, 0)); } } oracle_command => handle_pool_command(oracle_command, &node_api, network_prefix), @@ -388,15 +375,12 @@ fn main() { /// Handle all other commands fn handle_pool_command(command: Command, node_api: &NodeApi, network_prefix: NetworkPrefix) { let height = BlockHeight(node_api.node.current_block_height().unwrap() as u32); - let node_scan_registry = NodeScanRegistry::load().unwrap(); - let op = OraclePool::new(&node_scan_registry).unwrap(); + let token_fetch_registry = TokenFetchRegistry::load().unwrap(); + let op = OraclePool::new(&token_fetch_registry).unwrap(); match command { Command::ExtractRewardTokens { rewards_address } => { if let Err(e) = cli_commands::extract_reward_tokens::extract_reward_tokens( - // TODO: pass the NodeApi instance instead of these three node_api, - &node_api.node, - &node_api.node, op.get_local_datapoint_box_source(), rewards_address, height, @@ -420,8 +404,6 @@ fn handle_pool_command(command: Command, node_api: &NodeApi, network_prefix: Net } => { if let Err(e) = cli_commands::transfer_oracle_token::transfer_oracle_token( node_api, - &node_api.node, - &node_api.node, op.get_local_datapoint_box_source(), oracle_token_address, height, @@ -456,8 +438,6 @@ fn handle_pool_command(command: Command, node_api: &NodeApi, network_prefix: Net .unwrap(); if let Err(e) = cli_commands::vote_update_pool::vote_update_pool( node_api, - &node_api.node, - &node_api.node, op.get_local_ballot_box_source(), new_pool_box_address_hash_str, reward_token_opt, @@ -477,8 +457,6 @@ fn handle_pool_command(command: Command, node_api: &NodeApi, network_prefix: Net if let Err(e) = cli_commands::update_pool::update_pool( &op, node_api, - &node_api.node, - &node_api.node, reward_token_opt, height, ) { @@ -504,10 +482,7 @@ fn handle_pool_command(command: Command, node_api: &NodeApi, network_prefix: Net &POOL_CONFIG.token_ids.oracle_token_id, &POOL_CONFIG.token_ids.reward_token_id, POOL_CONFIG_FILE_PATH.get().unwrap(), - op.get_local_datapoint_box_source(), - &get_scans_file_path(), - node_scan_registry, - node_api, + op.get_local_datapoint_box_source() ) { error!("Fatal import pool update error : {:?}", e); std::process::exit(exitcode::SOFTWARE); @@ -531,9 +506,6 @@ fn main_loop_iteration( report_storage: Arc>, change_address: &NetworkAddress, ) -> std::result::Result<(), anyhow::Error> { - if !node_api.node.wallet_status()?.unlocked { - return Err(anyhow!("Wallet is locked!")); - } let height = BlockHeight( node_api .node @@ -571,7 +543,7 @@ fn main_loop_iteration( } }; } - update_metrics(oracle_pool)?; + update_metrics(oracle_pool, node_api)?; Ok(()) } diff --git a/core/src/metrics.rs b/core/src/metrics.rs index 06093880..b8ee7908 100644 --- a/core/src/metrics.rs +++ b/core/src/metrics.rs @@ -23,7 +23,6 @@ use crate::monitor::OracleHealth; use crate::monitor::PoolHealth; use crate::node_interface::node_api::NodeApi; use crate::oracle_config::ORACLE_CONFIG; -use crate::oracle_config::ORACLE_SECRETS; use crate::oracle_state::OraclePool; static POOL_BOX_HEIGHT: Lazy = Lazy::new(|| { @@ -37,6 +36,7 @@ static POOL_BOX_HEIGHT: Lazy = Lazy::new(|| { m }); + static POOL_BOX_RATE: Lazy = Lazy::new(|| { let m = IntGauge::with_opts( Opts::new("pool_box_rate", "exchange rate from the pool box") @@ -334,14 +334,9 @@ fn update_my_claimable_reward_tokens(oracle_pool: Arc) { } } -pub fn update_metrics(oracle_pool: Arc) -> Result<(), anyhow::Error> { - let node_api = NodeApi::new( - ORACLE_SECRETS.node_api_key.clone(), - ORACLE_SECRETS.wallet_password.clone(), - &ORACLE_CONFIG.node_url, - ); +pub fn update_metrics(oracle_pool: Arc, node_api: &NodeApi) -> Result<(), anyhow::Error> { let current_height = (node_api.node.current_block_height()? as u32).into(); - let network_prefix = node_api.get_change_address()?.network(); + let network_prefix = ORACLE_CONFIG.change_address.clone().unwrap().network(); let pool_box = &oracle_pool.get_pool_box_source().get_pool_box()?; { let rate = pool_box.rate(); @@ -363,7 +358,7 @@ pub fn update_metrics(oracle_pool: Arc) -> Result<(), anyhow::Error> pool_health.details.epoch_length, )?; update_oracle_health(&oracle_health); - let wallet_balance: i64 = node_api.node.wallet_nano_ergs_balance()? as i64; + let wallet_balance: i64 = node_api.node.nano_ergs_balance(&ORACLE_CONFIG.oracle_address.to_base58())? as i64; ORACLE_NODE_WALLET_BALANCE.set(wallet_balance); POOL_BOX_REWARD_TOKEN_AMOUNT.set(pool_box.reward_token().amount.into()); update_reward_tokens_in_buyback_box(oracle_pool.clone()); diff --git a/core/src/node_interface.rs b/core/src/node_interface.rs index ed49771f..a38edc08 100644 --- a/core/src/node_interface.rs +++ b/core/src/node_interface.rs @@ -1,82 +1,4 @@ -use crate::node_interface::node_api::NodeApi; -use ergo_lib::{ - chain::transaction::{unsigned::UnsignedTransaction, Transaction, TxId, TxIoVec}, - ergotree_ir::chain::ergo_box::ErgoBox, -}; -use ergo_node_interface::node_interface::{NodeError, NodeInterface}; -use log::debug; -use log::error; - pub mod node_api; -pub type Result = std::result::Result; - -pub trait SubmitTransaction { - fn submit_transaction(&self, tx: &Transaction) -> Result; -} - -pub trait SignTransactionWithInputs { - fn sign_transaction_with_inputs( - &self, - unsigned_tx: &UnsignedTransaction, - inputs: TxIoVec, - data_boxes: Option>, - ) -> Result; -} - -pub trait SignTransaction { - fn sign_transaction(&self, unsigned_tx: &UnsignedTransaction) -> Result; -} - -// Note that we need the following trait implementations for `NodeInterface` because we can't rely -// on any of the functions in the `crate::node_interface` module since they all implicitly rely on -// the existence of an oracle-pool `yaml` config file. - -impl SignTransaction for NodeInterface { - fn sign_transaction(&self, unsigned_tx: &UnsignedTransaction) -> Result { - self.sign_transaction(unsigned_tx, None, None) - } -} - -impl SubmitTransaction for NodeInterface { - fn submit_transaction(&self, tx: &Transaction) -> crate::node_interface::Result { - log::trace!( - "Submitting signed transaction: {}", - serde_json::to_string_pretty(&tx).unwrap() - ); - self.submit_transaction(tx) - } -} - -impl SignTransactionWithInputs for NodeInterface { - fn sign_transaction_with_inputs( - &self, - unsigned_tx: &ergo_lib::chain::transaction::unsigned::UnsignedTransaction, - inputs: ergo_lib::chain::transaction::TxIoVec, - data_boxes: Option>, - ) -> Result { - self.sign_transaction( - unsigned_tx, - Some(inputs.as_vec().clone()), - data_boxes.map(|bs| bs.as_vec().clone()), - ) - } -} - -pub fn try_ensure_wallet_unlocked(node: &NodeApi) { - let unlocked = node.node.wallet_status().unwrap().unlocked; - - if !unlocked { - if let Some(wallet_pass) = &node.wallet_pass { - if let Err(e) = node.wallet_unlock(wallet_pass) { - error!("Failed to unlock wallet. Wallet must be unlocked for node operations. error: {:?}", e); - std::process::exit(exitcode::SOFTWARE); - } - } else { - error!("Wallet must be unlocked for node operations"); - std::process::exit(exitcode::SOFTWARE); - } - } else { - debug!("Wallet unlocked"); - } -} +#[cfg(test)] +pub mod test_utils; \ No newline at end of file diff --git a/core/src/node_interface/node_api.rs b/core/src/node_interface/node_api.rs index b0598f4f..e80dc962 100644 --- a/core/src/node_interface/node_api.rs +++ b/core/src/node_interface/node_api.rs @@ -1,129 +1,276 @@ +use ergo_lib::chain::ergo_state_context::ErgoStateContext; +use ergo_lib::chain::transaction::{Transaction, TxId}; use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; -use ergo_lib::chain::transaction::TxId; -use ergo_lib::ergotree_ir::chain::address::AddressEncoder; use ergo_lib::ergotree_ir::chain::address::AddressEncoderError; -use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::ergotree_ir::chain::ergo_box::box_value::BoxValue; use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox; +use ergo_lib::ergotree_ir::chain::token::{Token, TokenId}; +use ergo_lib::wallet::box_selector::{BoxSelection, BoxSelector, BoxSelectorError, ErgoBoxAssets, SimpleBoxSelector}; +use ergo_lib::wallet::signing::TransactionContext; +use ergo_lib::wallet::{Wallet, WalletError}; use ergo_node_interface::scanning::NodeError; -use ergo_node_interface::NodeInterface; -use ergo_node_interface::ScanId; -use log::info; +use ergo_node_interface::{NodeInterface, P2PKAddressString}; +// use log::info; use reqwest::Url; -use serde_json::json; +// use serde_json::json; use thiserror::Error; +use crate::oracle_config::ORACLE_SECRETS; -use crate::scans::ScanID; -use crate::wallet::WalletDataError; -use crate::wallet::WalletDataSource; +pub trait NodeApiTrait { + fn get_unspent_boxes_by_address_with_token_filter_option( + &self, + address: &P2PKAddressString, + target_balance: BoxValue, + target_tokens: Vec, + filter_boxes_token_ids: Vec, + ) -> Result, BoxSelectorError>; + + fn get_unspent_boxes_by_address( + &self, + address: &P2PKAddressString, + target_balance: BoxValue, + target_tokens: Vec, + ) -> Result, BoxSelectorError>; + + fn get_unspent_boxes_by_token_id( + &self, + token_id: &TokenId, + ) -> Result, NodeApiError>; + + fn get_state_context(&self) -> Result; + + fn get_wallet(&self) -> Result; + + fn sign_transaction( + &self, + transaction_context: TransactionContext, + ) -> Result; + + fn submit_transaction( + &self, + tx: &Transaction, + ) -> Result; + + fn sign_and_submit_transaction( + &self, + transaction_context: TransactionContext, + ) -> Result; +} + +impl NodeApiTrait for NodeApi { + + fn get_unspent_boxes_by_address_with_token_filter_option( + &self, + address: &P2PKAddressString, + target_balance: BoxValue, + target_tokens: Vec, + filter_boxes_token_ids: Vec, + ) -> Result, BoxSelectorError> { + self.get_unspent_boxes_by_address_with_token_filter_option(address, target_balance, target_tokens, filter_boxes_token_ids) + } + + fn get_unspent_boxes_by_address( + &self, + address: &P2PKAddressString, + target_balance: BoxValue, + target_tokens: Vec, + ) -> Result, BoxSelectorError> { + self.get_unspent_boxes_by_address(address, target_balance, target_tokens) + } + + fn get_unspent_boxes_by_token_id( + &self, + token_id: &TokenId + ) -> Result, NodeApiError> { + self.get_all_unspent_boxes_by_token_id(token_id) + } + + fn get_state_context(&self) -> Result { + self.get_state_context() + } + + fn get_wallet(&self) -> Result { + self.get_wallet() + } + + fn sign_transaction( + &self, + transaction_context: TransactionContext, + ) -> Result { + self.sign_transaction(transaction_context) + } + + fn submit_transaction( + &self, + tx: &Transaction, + ) -> Result { + self.submit_transaction(tx) + } + + fn sign_and_submit_transaction( + &self, + transaction_context: TransactionContext, + ) -> Result { + self.sign_and_submit_transaction(transaction_context) + } +} pub struct NodeApi { pub node: NodeInterface, - pub wallet_pass: Option, } impl NodeApi { - pub fn new(api_key: String, wallet_pass: Option, node_url: &Url) -> Self { - let node = NodeInterface::from_url(&api_key, node_url.clone()); - Self { node, wallet_pass } + pub fn new(node_url: &Url) -> Self { + let node = NodeInterface::from_url("", node_url.clone()); + Self { node } } - pub fn get_change_address(&self) -> Result { - let change_address_str = self - .node - .wallet_status()? - .change_address - .ok_or(NodeApiError::NoChangeAddressSetInNode)?; - let addr = AddressEncoder::unchecked_parse_network_address_from_str(&change_address_str)?; - Ok(addr) + /// Get unspent boxes by address with token filter option + pub fn get_unspent_boxes_by_address_with_token_filter_option(&self, address: &P2PKAddressString, target_balance: BoxValue, target_tokens: Vec, filter_boxes_token_ids: Vec) -> Result, BoxSelectorError> { + let default_limit = 100; + let box_selector = SimpleBoxSelector::new(); + let mut unspent_boxes: Vec = vec![]; + let mut offset = 0; + let mut selection: Option, BoxSelectorError>> = None; + loop { + let boxes = self.node.unspent_boxes_by_address(address, offset, default_limit); + if boxes.is_ok() { + let boxes_clone = boxes.unwrap().clone(); + if boxes_clone.is_empty() { break; } + for box_ in boxes_clone.iter() { + let tokens = box_.tokens().clone(); + if tokens.is_none() { + unspent_boxes.push(box_.clone()); + } else { + let tokens = tokens.unwrap().to_vec(); + if tokens.iter().any(|token| filter_boxes_token_ids.contains(&token.token_id)) { + continue; + } + unspent_boxes.push(box_.clone()); + } + } + let local_selection = box_selector.select(unspent_boxes.clone(), target_balance, target_tokens.as_slice()); + selection = Some(local_selection.clone()); + if local_selection.is_ok() { break; } + offset += default_limit; + } else { break; } + } + log::trace!("get_unspent_boxes_by_address_with_token_filter_option for address: {:#?} and found {:#?} boxes", address, unspent_boxes.len()); + Ok(selection.unwrap()?.boxes.to_vec()) } - /// Registers a scan with the node and either returns the `scan_id` or an error - pub fn register_scan_raw(&self, scan_json: serde_json::Value) -> Result { - let scan_id = self.node.register_scan(scan_json)?; - Ok(scan_id.to_string()) + /// Get unspent boxes by address + pub fn get_unspent_boxes_by_address(&self, address: &P2PKAddressString, target_balance: BoxValue, target_tokens: Vec) -> Result, BoxSelectorError> { + let default_limit = 100; + let box_selector = SimpleBoxSelector::new(); + let mut unspent_boxes: Vec = vec![]; + let mut offset = 0; + let mut selection: Option, BoxSelectorError>> = None; + loop { + let boxes = self.node.unspent_boxes_by_address(address, offset, default_limit); + if boxes.is_ok() { + let mut boxes_clone = boxes.unwrap().clone(); + if boxes_clone.is_empty() { break; } + unspent_boxes.append(&mut boxes_clone); + let local_selection = box_selector.select(unspent_boxes.clone(), target_balance, target_tokens.as_slice()); + selection = Some(local_selection.clone()); + if local_selection.is_ok() { break; } + offset += default_limit; + } else { break; } + } + log::trace!("get_unspent_boxes_by_address for address: {:#?} and found {:#?} boxes", address, unspent_boxes.len()); + Ok(selection.unwrap()?.boxes.to_vec()) } - pub fn register_scan( - &self, - name: String, - tracking_rule: serde_json::Value, - ) -> std::result::Result { - let scan_json = json!({ - "scanName": name, - "trackingRule": tracking_rule, - }); - log::info!( - "Registering Scan:\n{}", - serde_json::to_string_pretty(&scan_json).unwrap() - ); - let scan_id_str = self.register_scan_raw(scan_json)?; - let scan_id_raw = scan_id_str - .parse::() - .map_err(|_| NodeApiError::InvalidScanId(scan_id_str))?; - let scan_id = scan_id_raw.into(); - info!("Scan Successfully registered.\nID: {}", scan_id); - Ok(scan_id) + /// Get unspent boxes by token id + pub fn get_all_unspent_boxes_by_token_id(&self, token_id: &TokenId) -> Result, NodeApiError> { + let default_limit = 100; + let mut unspent_boxes: Vec = vec![]; + let mut offset = 0; + loop { + let boxes = self.node.unspent_boxes_by_token_id(token_id, offset, default_limit); + if boxes.is_ok() { + let mut boxes_clone = boxes.unwrap().clone(); + if boxes_clone.is_empty() { break; } + unspent_boxes.append(&mut boxes_clone); + offset += default_limit; + } else { break; } + } + log::trace!("get_unspent_boxes_by_token_id for token: {:#?} and found {:#?} boxes", token_id, unspent_boxes.len()); + Ok(unspent_boxes) } - pub fn deregister_scan(&self, scan_id: ScanId) -> Result { - log::info!("Deregistering Scan: {}", scan_id); - let scan_id = self.node.deregister_scan(scan_id)?; - Ok(scan_id) + /// Get the current state context of the Ergo blockchain. + pub fn get_state_context(&self) -> Result { + Ok(self.node.get_state_context()?) } - pub fn rescan_from_height(&self, height: u32) -> Result<(), NodeApiError> { - log::info!("Triggering wallet rescan"); - self.node.send_post_req( - "/wallet/rescan", - format!("{{ \"fromHeight\": {} }} ", height), - )?; - Ok(()) + /// Get the wallet instance from the oracle secrets. + pub fn get_wallet(&self) -> Result { + let secret = ORACLE_SECRETS.secret_key.clone(); + Ok(Wallet::from_secrets(vec![secret])) } - /// Sign an `UnsignedTransaction` and then submit it to the mempool. - pub fn sign_and_submit_transaction( + /// Sign an `UnsignedTransaction` and return the signed `Transaction`. + pub fn sign_transaction( &self, - unsigned_tx: &UnsignedTransaction, - ) -> Result { + transaction_context: TransactionContext, + ) -> Result { log::trace!( "Signing transaction: {}", - serde_json::to_string_pretty(&unsigned_tx).unwrap() - ); - let signed_tx = self.node.sign_transaction(unsigned_tx, None, None)?; - log::trace!( - "Submitting signed transaction: {}", - serde_json::to_string_pretty(&signed_tx).unwrap() + serde_json::to_string_pretty(&transaction_context.spending_tx).unwrap() ); - Ok(self.node.submit_transaction(&signed_tx)?) + let wallet = self.get_wallet()?; + let signed_tx = wallet.sign_transaction(transaction_context, &self.node.get_state_context()?, None); + match signed_tx { + Ok(tx) => { + log::trace!( + "Signed transaction: {}", + serde_json::to_string_pretty(&tx).unwrap() + ); + Ok(tx) + } + Err(wallet_err) => { + log::error!( + "Sign Transaction Failed: {}", + wallet_err.to_string() + ); + Err(NodeApiError::WalletError(wallet_err)) + } + } } - /// Unlock wallet - pub fn wallet_unlock(&self, password: &str) -> Result { - let endpoint = "/wallet/unlock"; - let body = json! ({ - "pass": password, - }); - - let res = self.node.send_post_req(endpoint, body.to_string())?; - - if res.status().is_success() { - Ok(true) - } else { - let json = self.node.parse_response_to_json(Ok(res))?; - Err(NodeApiError::NodeInterfaceError(NodeError::BadRequest( - json["error"].to_string(), - ))) - } + /// Submit a signed `Transaction` to the mempool. + pub fn submit_transaction( + &self, + tx: &Transaction + ) -> Result { + Ok(self.node.submit_transaction(tx)?) } -} -impl WalletDataSource for NodeApi { - fn get_unspent_wallet_boxes(&self) -> Result, WalletDataError> { - self.node.unspent_boxes().map_err(Into::into) + /// Sign an `UnsignedTransaction` and submit the signed `Transaction` to the mempool. + pub fn sign_and_submit_transaction( + &self, + transaction_context: TransactionContext, + ) -> Result { + let tx = self.sign_transaction(transaction_context)?; + self.submit_transaction(&tx) } - fn get_change_address(&self) -> Result { - self.get_change_address().map_err(Into::into) + /// Waits for the indexer to sync. This function will block until the indexer is fully synced. + pub fn wait_for_indexer_sync(&self) -> Result<(), NodeApiError> { + let indexer_status = self.node.indexer_status()?; + if indexer_status.is_sync { + log::debug!("Your indexer is already synced."); + return Ok(()); + } + Ok(loop { + if indexer_status.is_sync { + log::debug!("Your indexer synced successfully."); + break + } + std::thread::sleep(std::time::Duration::from_secs(1)); + }) } } @@ -131,10 +278,10 @@ impl WalletDataSource for NodeApi { pub enum NodeApiError { #[error("Node error: {0}")] NodeInterfaceError(#[from] NodeError), + #[error("Wallet error: {0}")] + WalletError(#[from] WalletError), #[error("AddressEncoder error: {0}")] AddressEncoderError(#[from] AddressEncoderError), #[error("no change address is set in node")] NoChangeAddressSetInNode, - #[error("invalid scan id: {0}")] - InvalidScanId(String), } diff --git a/core/src/node_interface/test_utils.rs b/core/src/node_interface/test_utils.rs new file mode 100644 index 00000000..4d24fabe --- /dev/null +++ b/core/src/node_interface/test_utils.rs @@ -0,0 +1,94 @@ +use std::cell::RefCell; +use ergo_lib::chain::ergo_state_context::ErgoStateContext; +use ergo_lib::chain::transaction::{Transaction, TxId}; +use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::ergotree_ir::chain::ergo_box::box_value::BoxValue; +use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox; +use ergo_lib::ergotree_ir::chain::token::{Token, TokenId}; +use ergo_lib::wallet::box_selector::{BoxSelectorError}; +use ergo_lib::wallet::secret_key::SecretKey; +use ergo_lib::wallet::signing::TransactionContext; +use ergo_lib::wallet::Wallet; +use ergo_node_interface::{P2PKAddressString}; +use ergo_node_interface::node_interface::NodeError; +use ergo_chain_sim::{Block, ChainSim}; +use crate::node_interface::node_api::{NodeApiError, NodeApiTrait}; + +pub struct ChainSubmitTx<'a> { + pub(crate) chain: RefCell<&'a mut ChainSim>, +} + +pub struct MockNodeApi<'a> { + pub unspent_boxes: Vec, + pub secrets: Vec, + pub submitted_txs: &'a RefCell>, + pub chain_submit_tx: Option<&'a mut ChainSubmitTx<'a>>, + pub ctx: ErgoStateContext, +} + +impl NodeApiTrait for MockNodeApi<'_>{ + fn get_unspent_boxes_by_address_with_token_filter_option(&self, _address: &P2PKAddressString, _target_balance: BoxValue, _target_tokens: Vec, _filter_boxes_token_ids: Vec) -> Result, BoxSelectorError> { + Ok(self.unspent_boxes.clone()) + } + + fn get_unspent_boxes_by_address( + &self, + _address: &P2PKAddressString, + _target_balance: BoxValue, + _target_tokens: Vec, + ) -> Result, BoxSelectorError> { + Ok(self.unspent_boxes.clone()) + } + + fn get_unspent_boxes_by_token_id( + &self, + _token_id: &TokenId, + ) -> Result, NodeApiError> { + Ok(self.unspent_boxes.clone()) + } + + fn get_state_context(&self) -> Result { + Ok(self.ctx.clone()) + } + + fn get_wallet(&self) -> Result { + let wallet = Wallet::from_secrets(self.secrets.clone()); + Ok(wallet) + } + + fn sign_transaction(&self, _transaction_context: TransactionContext) -> Result { + self.get_wallet()?.sign_transaction(_transaction_context, &self.ctx.clone(), None) + .map_err(|e| NodeApiError::NodeInterfaceError(NodeError::Other(e.to_string()))) + } + + fn submit_transaction(&self, _tx: &Transaction) -> Result { + self.submitted_txs.borrow_mut().push(_tx.clone()); + if let Some(ref chain_submit_tx) = &self.chain_submit_tx { + chain_submit_tx.chain.borrow_mut().add_block(Block::new(vec![_tx.clone()])); + } + Ok(_tx.id()) + } + + fn sign_and_submit_transaction( + &self, + _transaction_context: TransactionContext, + ) -> Result { + self.sign_transaction(_transaction_context) + .and_then(|tx| self.submit_transaction(&tx)) + } +} + +// impl TransactionUtils for MockNodeApi{ +// +// fn sign_transaction(&self, _transaction_context: TransactionContext) -> Result { +// self.signed_tx.clone().ok_or(NodeApiError::NodeInterfaceError( +// NodeError::Other("No signed transaction provided".to_string()), +// )) +// } +// +// fn submit_transaction(&self, _tx: &Transaction) -> Result { +// self.tx_id.clone().ok_or(NodeApiError::NodeInterfaceError( +// NodeError::Other("No transaction ID provided".to_string()), +// )) +// } +// } \ No newline at end of file diff --git a/core/src/oracle_config.rs b/core/src/oracle_config.rs index 5abbde63..364c6d5a 100644 --- a/core/src/oracle_config.rs +++ b/core/src/oracle_config.rs @@ -16,7 +16,10 @@ use ergo_lib::{ }, wallet::tx_builder::{self, SUGGESTED_TX_FEE}, }; -use log::{warn, LevelFilter}; +use ergo_lib::wallet::ext_secret_key::ExtSecretKey; +use ergo_lib::wallet::mnemonic::Mnemonic; +use ergo_lib::wallet::secret_key::SecretKey; +use log::{LevelFilter}; use once_cell::sync; use reqwest::Url; use serde::{Deserialize, Serialize}; @@ -30,34 +33,33 @@ pub const DEFAULT_ORACLE_CONFIG_FILE_NAME: &str = "oracle_config.yaml"; pub struct OracleConfig { pub node_url: Url, pub base_fee: u64, - pub scan_start_height: u32, pub log_level: Option, pub core_api_port: u16, pub oracle_address: NetworkAddress, + pub change_address: Option, pub data_point_source_custom_script: Option, pub explorer_url: Option, pub metrics_port: Option, } pub struct OracleSecrets { - pub node_api_key: String, - pub wallet_password: Option, + pub secret_key: SecretKey, } impl OracleSecrets { pub fn load() -> Self { - let api_key = std::env::var("ORACLE_NODE_API_KEY").unwrap_or_else(|_| { - panic!("ORACLE_NODE_API_KEY environment variable for node API key is not set") + let mnemonic = std::env::var("ORACLE_WALLET_MNEMONIC").unwrap_or_else(|_| { + panic!("ORACLE_WALLET_MNEMONIC environment variable for sign transactions is not set") }); - let wallet_pass = std::env::var("ORACLE_NODE_WALLET_PASSWORD").ok(); - if wallet_pass.is_none() { - warn!("ORACLE_NODE_WALLET_PASSWORD environment variable for automatic unlock of node wallet is not set"); - } + let seed = Mnemonic::to_seed(&mnemonic, ""); + let ext_sk = ExtSecretKey::derive_master(seed).unwrap(); + // bip-32 path for the first key + let path = "m/44'/429'/0'/0/0"; + let secret = ext_sk.derive(path.parse().unwrap()).unwrap().secret_key(); Self { - node_api_key: api_key, - wallet_password: wallet_pass, + secret_key: secret, } } } @@ -79,12 +81,20 @@ impl OracleConfig { "failed to load oracle config file from {}", config_file_path.display() ))?; - let config = + let mut config = Self::load_from_str(&config_str).context("failed to parse oracle config file")?; + if config.change_address.is_none() { + config.change_address = Some(config.oracle_address.clone()); + log::info!("Set oracle address as change address"); + } let _ = config .oracle_address_p2pk() .context("failed to parse oracle address")?; - Ok(config) + + let _ = config + .change_address_p2pk() + .context("failed to parse change address")?; + Ok(config.clone()) } pub fn load_from_str(config_str: &str) -> Result { @@ -106,6 +116,14 @@ impl OracleConfig { Err(OracleConfigFileError::InvalidOracleAddress) } } + + pub fn change_address_p2pk(&self) -> Result { + if let Address::P2Pk(public_key) = self.change_address.clone().unwrap().address() { + Ok(public_key.clone()) + } else { + Err(OracleConfigFileError::InvalidChangeAddress) + } + } } #[derive(Clone, Debug, Error)] @@ -116,6 +134,8 @@ pub enum OracleConfigFileError { ParseError(String), #[error("Invalid oracle address, must be P2PK")] InvalidOracleAddress, + #[error("Invalid change address, must be P2PK")] + InvalidChangeAddress, } impl Default for OracleConfig { @@ -126,8 +146,8 @@ impl Default for OracleConfig { .unwrap(); Self { oracle_address: address.clone(), + change_address: None, core_api_port: 9010, - scan_start_height: 0, data_point_source_custom_script: None, base_fee: *tx_builder::SUGGESTED_TX_FEE().as_u64(), log_level: LevelFilter::Info.into(), diff --git a/core/src/oracle_state.rs b/core/src/oracle_state.rs index c4c8fd2c..83ff4ab0 100644 --- a/core/src/oracle_state.rs +++ b/core/src/oracle_state.rs @@ -9,7 +9,7 @@ use crate::datapoint_source::DataPointSourceError; use crate::oracle_config::ORACLE_CONFIG; use crate::oracle_types::{BlockHeight, EpochCounter, Rate}; use crate::pool_config::POOL_CONFIG; -use crate::scans::{GenericTokenScan, NodeScanRegistry, ScanError, ScanGetBoxes}; +use crate::get_boxes::{GenericTokenFetch, GetBoxes, TokenFetchRegistry, GetBoxesError}; use crate::spec_token::{ BallotTokenId, BuybackTokenId, OracleTokenId, PoolTokenId, RefreshTokenId, RewardTokenId, TokenIdKind, UpdateTokenId, @@ -27,8 +27,8 @@ pub type Result = std::result::Result; pub enum DataSourceError { #[error("unexpected data error: {0}")] UnexpectedData(#[from] TryExtractFromError), - #[error("scan error: {0}")] - ScanError(#[from] ScanError), + #[error("get boxes error: {0}")] + GetBoxesError(#[from] GetBoxesError), #[error("pool box error: {0}")] PoolBoxError(#[from] PoolBoxError), #[error("pool box not found")] @@ -90,63 +90,63 @@ pub trait BuybackBoxSource { /// Overarching struct which allows for acquiring the state of the whole oracle pool protocol #[derive(Debug)] pub struct OraclePool { - oracle_datapoint_scan: OracleDatapointScan, - local_oracle_datapoint_scan: LocalOracleDatapointScan, - local_ballot_box_scan: LocalBallotBoxScan, - pool_box_scan: PoolBoxScan, - refresh_box_scan: RefreshBoxScan, - ballot_boxes_scan: BallotBoxesScan, - update_box_scan: UpdateBoxScan, - buyback_box_scan: Option, + oracle_datapoint_fetch: OracleDatapointFetch, + local_oracle_datapoint_fetch: LocalOracleDatapointFetch, + local_ballot_box_fetch: LocalBallotBoxFetch, + pool_box_fetch: PoolBoxFetch, + refresh_box_fetch: RefreshBoxFetch, + ballot_boxes_fetch: BallotBoxesFetch, + update_box_fetch: UpdateBoxFetch, + buyback_box_fetch: Option, } #[derive(Debug)] -pub struct OracleDatapointScan { - scan: GenericTokenScan, +pub struct OracleDatapointFetch { + token_fetch: GenericTokenFetch, oracle_box_wrapper_inputs: OracleBoxWrapperInputs, } #[derive(Debug)] -pub struct LocalOracleDatapointScan { - scan: GenericTokenScan, +pub struct LocalOracleDatapointFetch { + token_fetch: GenericTokenFetch, oracle_box_wrapper_inputs: OracleBoxWrapperInputs, oracle_pk: ProveDlog, } #[derive(Debug)] -pub struct LocalBallotBoxScan { - scan: GenericTokenScan, +pub struct LocalBallotBoxFetch { + token_fetch: GenericTokenFetch, ballot_box_wrapper_inputs: BallotBoxWrapperInputs, ballot_token_owner_pk: ProveDlog, } #[derive(Debug)] -pub struct PoolBoxScan { - scan: GenericTokenScan, +pub struct PoolBoxFetch { + token_fetch: GenericTokenFetch, pool_box_wrapper_inputs: PoolBoxWrapperInputs, } #[derive(Debug)] -pub struct RefreshBoxScan { - scan: GenericTokenScan, +pub struct RefreshBoxFetch { + token_fetch: GenericTokenFetch, refresh_box_wrapper_inputs: RefreshBoxWrapperInputs, } #[derive(Debug)] -pub struct BallotBoxesScan { - scan: GenericTokenScan, +pub struct BallotBoxesFetch { + token_fetch: GenericTokenFetch, ballot_box_wrapper_inputs: BallotBoxWrapperInputs, } #[derive(Debug)] -pub struct UpdateBoxScan { - scan: GenericTokenScan, +pub struct UpdateBoxFetch { + token_fetch: GenericTokenFetch, update_box_wrapper_inputs: UpdateBoxWrapperInputs, } #[derive(Debug)] -pub struct BuybackBoxScan { - scan: GenericTokenScan, +pub struct BuybackBoxFetch { + token_fetch: GenericTokenFetch, reward_token_id: RewardTokenId, } @@ -172,75 +172,75 @@ pub enum LocalDatapointState { } impl OraclePool { - pub fn new(node_scan_registry: &NodeScanRegistry) -> std::result::Result { + pub fn new(token_fetch_registry: &TokenFetchRegistry) -> std::result::Result { let pool_config = &POOL_CONFIG; let oracle_config = &ORACLE_CONFIG; let oracle_pk = oracle_config.oracle_address_p2pk()?; - // Create all `Scan` structs for protocol - let oracle_datapoint_scan = OracleDatapointScan { - scan: node_scan_registry.oracle_token_scan.clone(), + // Create all tokens structs for protocol + let oracle_datapoint_fetch = OracleDatapointFetch { + token_fetch: token_fetch_registry.oracle_token_fetch.clone(), oracle_box_wrapper_inputs: pool_config.oracle_box_wrapper_inputs.clone(), }; - let local_oracle_datapoint_scan = LocalOracleDatapointScan { - scan: node_scan_registry.oracle_token_scan.clone(), + let local_oracle_datapoint_fetch = LocalOracleDatapointFetch { + token_fetch: token_fetch_registry.oracle_token_fetch.clone(), oracle_box_wrapper_inputs: pool_config.oracle_box_wrapper_inputs.clone(), oracle_pk: oracle_pk.clone(), }; - let local_ballot_box_scan = LocalBallotBoxScan { - scan: node_scan_registry.ballot_token_scan.clone(), + let local_ballot_box_fetch = LocalBallotBoxFetch { + token_fetch: token_fetch_registry.ballot_token_fetch.clone(), ballot_box_wrapper_inputs: pool_config.ballot_box_wrapper_inputs.clone(), ballot_token_owner_pk: oracle_pk.clone(), }; - let ballot_boxes_scan = BallotBoxesScan { - scan: node_scan_registry.ballot_token_scan.clone(), + let ballot_boxes_fetch = BallotBoxesFetch { + token_fetch: token_fetch_registry.ballot_token_fetch.clone(), ballot_box_wrapper_inputs: pool_config.ballot_box_wrapper_inputs.clone(), }; - let pool_box_scan = PoolBoxScan { - scan: node_scan_registry.pool_token_scan.clone(), + let pool_box_fetch = PoolBoxFetch { + token_fetch: token_fetch_registry.pool_token_fetch.clone(), pool_box_wrapper_inputs: pool_config.pool_box_wrapper_inputs.clone(), }; - let refresh_box_scan = RefreshBoxScan { - scan: node_scan_registry.refresh_token_scan.clone(), + let refresh_box_fetch = RefreshBoxFetch { + token_fetch: token_fetch_registry.refresh_token_fetch.clone(), refresh_box_wrapper_inputs: pool_config.refresh_box_wrapper_inputs.clone(), }; - let update_box_scan = UpdateBoxScan { - scan: node_scan_registry.update_token_scan.clone(), + let update_box_fetch = UpdateBoxFetch { + token_fetch: token_fetch_registry.update_token_fetch.clone(), update_box_wrapper_inputs: pool_config.update_box_wrapper_inputs.clone(), }; - let buyback_box_scan = - node_scan_registry - .buyback_token_scan + let buyback_box_fetch = + token_fetch_registry + .buyback_token_fetch .clone() - .map(|scan| BuybackBoxScan { - scan, + .map(|token_fetch| BuybackBoxFetch { + token_fetch, reward_token_id: pool_config.token_ids.reward_token_id.clone(), }); - log::debug!("Scans loaded"); + log::debug!("Tokens loaded"); Ok(OraclePool { - oracle_datapoint_scan, - local_oracle_datapoint_scan, - local_ballot_box_scan, - ballot_boxes_scan, - pool_box_scan, - refresh_box_scan, - update_box_scan, - buyback_box_scan, + oracle_datapoint_fetch, + local_oracle_datapoint_fetch, + local_ballot_box_fetch, + ballot_boxes_fetch, + pool_box_fetch, + refresh_box_fetch, + update_box_fetch, + buyback_box_fetch, }) } - /// Create a new `OraclePool` struct with loaded scans + /// Create a new `OraclePool` struct with loaded get_boxes pub fn load() -> std::result::Result { - let node_scan_registry = NodeScanRegistry::load()?; - Self::new(&node_scan_registry) + let token_fetch_registry = TokenFetchRegistry::load()?; + Self::new(&token_fetch_registry) } /// Get the state of the current oracle pool epoch @@ -275,53 +275,53 @@ impl OraclePool { } pub fn get_pool_box_source(&self) -> &dyn PoolBoxSource { - &self.pool_box_scan as &dyn PoolBoxSource + &self.pool_box_fetch as &dyn PoolBoxSource } pub fn get_local_ballot_box_source(&self) -> &dyn LocalBallotBoxSource { - &self.local_ballot_box_scan as &dyn LocalBallotBoxSource + &self.local_ballot_box_fetch as &dyn LocalBallotBoxSource } pub fn get_ballot_boxes_source(&self) -> &dyn VoteBallotBoxesSource { - &self.ballot_boxes_scan as &dyn VoteBallotBoxesSource + &self.ballot_boxes_fetch as &dyn VoteBallotBoxesSource } pub fn get_refresh_box_source(&self) -> &dyn RefreshBoxSource { - &self.refresh_box_scan as &dyn RefreshBoxSource + &self.refresh_box_fetch as &dyn RefreshBoxSource } pub fn get_posted_datapoint_boxes_source(&self) -> &dyn PostedDatapointBoxesSource { - &self.oracle_datapoint_scan as &dyn PostedDatapointBoxesSource + &self.oracle_datapoint_fetch as &dyn PostedDatapointBoxesSource } pub fn get_collected_datapoint_boxes_source(&self) -> &dyn CollectedDatapointBoxesSource { - &self.oracle_datapoint_scan as &dyn CollectedDatapointBoxesSource + &self.oracle_datapoint_fetch as &dyn CollectedDatapointBoxesSource } pub fn get_local_datapoint_box_source(&self) -> &dyn LocalDatapointBoxSource { - &self.local_oracle_datapoint_scan as &dyn LocalDatapointBoxSource + &self.local_oracle_datapoint_fetch as &dyn LocalDatapointBoxSource } pub fn get_update_box_source(&self) -> &dyn UpdateBoxSource { - &self.update_box_scan as &dyn UpdateBoxSource + &self.update_box_fetch as &dyn UpdateBoxSource } pub fn get_buyback_box_source(&self) -> Option<&dyn BuybackBoxSource> { - self.buyback_box_scan + self.buyback_box_fetch .as_ref() .map(|b| b as &dyn BuybackBoxSource) } pub fn get_total_oracle_token_count(&self) -> Result { Ok(self - .oracle_datapoint_scan - .scan + .oracle_datapoint_fetch + .token_fetch .get_boxes()? .into_iter() .map(|b| { get_token_count( b, - self.oracle_datapoint_scan + self.oracle_datapoint_fetch .oracle_box_wrapper_inputs .oracle_token_id .token_id(), @@ -331,10 +331,10 @@ impl OraclePool { } } -impl PoolBoxSource for PoolBoxScan { +impl PoolBoxSource for PoolBoxFetch { fn get_pool_box(&self) -> Result { let box_wrapper = PoolBoxWrapper::new( - self.scan + self.token_fetch .get_box()? .ok_or(DataSourceError::PoolBoxNotFoundError)?, &self.pool_box_wrapper_inputs, @@ -343,10 +343,10 @@ impl PoolBoxSource for PoolBoxScan { } } -impl LocalBallotBoxSource for LocalBallotBoxScan { +impl LocalBallotBoxSource for LocalBallotBoxFetch { fn get_ballot_box(&self) -> Result> { Ok(self - .scan + .token_fetch .get_boxes()? .into_iter() .filter_map(|b| BallotBoxWrapper::new(b, &self.ballot_box_wrapper_inputs).ok()) @@ -354,10 +354,10 @@ impl LocalBallotBoxSource for LocalBallotBoxScan { } } -impl RefreshBoxSource for RefreshBoxScan { +impl RefreshBoxSource for RefreshBoxFetch { fn get_refresh_box(&self) -> Result { let box_wrapper = RefreshBoxWrapper::new( - self.scan + self.token_fetch .get_box()? .ok_or(DataSourceError::RefreshBoxNotFoundError)?, &self.refresh_box_wrapper_inputs, @@ -366,10 +366,10 @@ impl RefreshBoxSource for RefreshBoxScan { } } -impl LocalDatapointBoxSource for LocalOracleDatapointScan { +impl LocalDatapointBoxSource for LocalOracleDatapointFetch { fn get_local_oracle_datapoint_box(&self) -> Result> { Ok(self - .scan + .token_fetch .get_boxes()? .into_iter() .filter_map(|b| OracleBoxWrapper::new(b, &self.oracle_box_wrapper_inputs).ok()) @@ -377,10 +377,10 @@ impl LocalDatapointBoxSource for LocalOracleDatapointScan { } } -impl VoteBallotBoxesSource for BallotBoxesScan { +impl VoteBallotBoxesSource for BallotBoxesFetch { fn get_ballot_boxes(&self) -> Result> { Ok(self - .scan + .token_fetch .get_boxes()? .into_iter() .filter_map(|ballot_box| { @@ -390,10 +390,10 @@ impl VoteBallotBoxesSource for BallotBoxesScan { } } -impl UpdateBoxSource for UpdateBoxScan { +impl UpdateBoxSource for UpdateBoxFetch { fn get_update_box(&self) -> Result { let box_wrapper = UpdateBoxWrapper::new( - self.scan + self.token_fetch .get_box()? .ok_or(DataSourceError::UpdateBoxNotFoundError)?, &self.update_box_wrapper_inputs, @@ -402,10 +402,10 @@ impl UpdateBoxSource for UpdateBoxScan { } } -impl PostedDatapointBoxesSource for OracleDatapointScan { +impl PostedDatapointBoxesSource for OracleDatapointFetch { fn get_posted_datapoint_boxes(&self) -> Result> { let posted_boxes = self - .scan + .token_fetch .get_boxes()? .into_iter() .filter_map(|b| OracleBoxWrapper::new(b, &self.oracle_box_wrapper_inputs).ok()) @@ -418,10 +418,10 @@ impl PostedDatapointBoxesSource for OracleDatapointScan { } } -impl CollectedDatapointBoxesSource for OracleDatapointScan { +impl CollectedDatapointBoxesSource for OracleDatapointFetch { fn get_collected_datapoint_boxes(&self) -> Result> { let posted_boxes = self - .scan + .token_fetch .get_boxes()? .into_iter() .filter_map(|b| OracleBoxWrapper::new(b, &self.oracle_box_wrapper_inputs).ok()) @@ -434,10 +434,10 @@ impl CollectedDatapointBoxesSource for OracleDatapointScan { } } -impl BuybackBoxSource for BuybackBoxScan { +impl BuybackBoxSource for BuybackBoxFetch { fn get_buyback_box(&self) -> Result> { Ok(self - .scan + .token_fetch .get_box()? .map(|ergo_box| BuybackBoxWrapper::new(ergo_box, self.reward_token_id.clone()))) } diff --git a/core/src/pool_commands.rs b/core/src/pool_commands.rs index f992854c..767f4552 100644 --- a/core/src/pool_commands.rs +++ b/core/src/pool_commands.rs @@ -10,8 +10,7 @@ use crate::oracle_config::ORACLE_CONFIG; use crate::oracle_state::{DataSourceError, OraclePool}; use crate::oracle_types::BlockHeight; use crate::pool_config::POOL_CONFIG; -use crate::wallet::WalletDataSource; - +use crate::node_interface::node_api::NodeApi; use self::publish_datapoint::build_publish_first_datapoint_action; use self::publish_datapoint::{ build_subsequent_publish_datapoint_action, PublishDatapointActionError, @@ -52,7 +51,7 @@ pub enum PoolCommandError { pub fn build_action( cmd: PoolCommand, op: &OraclePool, - wallet: &dyn WalletDataSource, + node_api: &NodeApi, height: BlockHeight, change_address: Address, datapoint_source: &RuntimeDataPointSource, @@ -61,18 +60,13 @@ pub fn build_action( let datapoint_boxes_source = op.get_posted_datapoint_boxes_source(); let pool_box = op.get_pool_box_source().get_pool_box()?; let current_epoch_counter = pool_box.epoch_counter(); - let oracle_public_key = - if let Address::P2Pk(public_key) = ORACLE_CONFIG.oracle_address.address() { - *public_key.h - } else { - return Err(PoolCommandError::WrongOracleAddressType); - }; + let oracle_address = &ORACLE_CONFIG.oracle_address; match cmd { PoolCommand::PublishFirstDataPoint => build_publish_first_datapoint_action( - wallet, + node_api, height, + oracle_address.clone(), change_address, - oracle_public_key, POOL_CONFIG.oracle_box_wrapper_inputs.clone(), datapoint_source, ) @@ -86,8 +80,9 @@ pub fn build_action( let new_epoch_counter = current_epoch_counter; build_subsequent_publish_datapoint_action( &local_datapoint_box, - wallet, + node_api, height, + oracle_address.clone(), change_address, datapoint_source, new_epoch_counter, @@ -115,10 +110,10 @@ pub fn build_action( .contract_inputs .contract_parameters() .min_data_points(), - wallet, + node_api, height, + oracle_address.clone(), change_address, - &oracle_public_key, op.get_buyback_box_source(), ) .map_err(Into::into) diff --git a/core/src/pool_commands/publish_datapoint.rs b/core/src/pool_commands/publish_datapoint.rs index 7c1773fb..2f84aa1f 100644 --- a/core/src/pool_commands/publish_datapoint.rs +++ b/core/src/pool_commands/publish_datapoint.rs @@ -2,14 +2,16 @@ use std::convert::TryFrom; use ergo_lib::{ chain::ergo_box::box_builder::ErgoBoxCandidateBuilderError, - ergo_chain_types::EcPoint, ergotree_interpreter::sigma_protocol::prover::ContextExtension, ergotree_ir::chain::{address::Address, token::TokenAmount}, wallet::{ - box_selector::{BoxSelector, BoxSelectorError, SimpleBoxSelector}, + box_selector::BoxSelectorError, tx_builder::{TxBuilder, TxBuilderError}, }, }; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use thiserror::Error; use crate::{ @@ -22,8 +24,9 @@ use crate::{ oracle_state::DataSourceError, oracle_types::{BlockHeight, EpochCounter}, spec_token::{OracleTokenId, RewardTokenId, SpecToken}, - wallet::{WalletDataError, WalletDataSource}, }; +use crate::address_util::address_to_p2pk; +use crate::node_interface::node_api::NodeApiTrait; #[derive(Debug, Error)] pub enum PublishDatapointActionError { @@ -35,8 +38,8 @@ pub enum PublishDatapointActionError { TxBuilder(#[from] TxBuilderError), #[error("box builder error: {0}")] ErgoBoxCandidateBuilder(#[from] ErgoBoxCandidateBuilderError), - #[error("WalletData error: {0}")] - WalletData(#[from] WalletDataError), + #[error("tx signing error: {0}")] + TxSigningError(#[from] TxSigningError), #[error("box selector error: {0}")] BoxSelector(#[from] BoxSelectorError), #[error("datapoint source error: {0}")] @@ -47,8 +50,9 @@ pub enum PublishDatapointActionError { pub fn build_subsequent_publish_datapoint_action( local_datapoint_box: &OracleBoxWrapper, - wallet: &dyn WalletDataSource, + node_api: &dyn NodeApiTrait, height: BlockHeight, + oracle_address: NetworkAddress, change_address: Address, datapoint_source: &dyn DataPointSource, new_epoch_counter: EpochCounter, @@ -76,17 +80,17 @@ pub fn build_subsequent_publish_datapoint_action( in_oracle_box.get_box().value, height, )?; - - let mut unspent_boxes = wallet.get_unspent_wallet_boxes()?; - let tx_fee = *BASE_FEE; let box_selector = SimpleBoxSelector::new(); + let tx_fee = *BASE_FEE; + let mut unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), tx_fee, vec![])?; let target_tokens = vec![ in_oracle_box.oracle_token().into(), outbox_reward_tokens.into(), ]; - let target_balace = in_oracle_box.get_box().value.checked_add(&tx_fee).unwrap(); + let target_balance = in_oracle_box.get_box().value.checked_add(&tx_fee).unwrap(); unspent_boxes.push(in_oracle_box.get_box().clone()); - let selection = box_selector.select(unspent_boxes, target_balace, target_tokens.as_slice())?; + let selection = box_selector.select(unspent_boxes, target_balance, target_tokens.as_slice())?; + let inputs = selection.boxes.clone().to_vec(); let mut tx_builder = TxBuilder::new( selection, vec![output_candidate], @@ -104,20 +108,23 @@ pub fn build_subsequent_publish_datapoint_action( let report = PublishDatapointActionReport { posted_datapoint: new_datapoint, }; - Ok((PublishDataPointAction { tx }, report)) + let context = match TransactionContext::new(tx, inputs, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(PublishDatapointActionError::TxSigningError(e)), + }; + Ok((PublishDataPointAction { transaction_context: context }, report)) } #[allow(clippy::too_many_arguments)] pub fn build_publish_first_datapoint_action( - wallet: &dyn WalletDataSource, + node_api: &dyn NodeApiTrait, height: BlockHeight, + oracle_address: NetworkAddress, change_address: Address, - public_key: EcPoint, inputs: OracleBoxWrapperInputs, datapoint_source: &dyn DataPointSource, ) -> Result<(PublishDataPointAction, PublishDatapointActionReport), PublishDatapointActionError> { let new_datapoint = datapoint_source.get_datapoint()?; - let unspent_boxes = wallet.get_unspent_wallet_boxes()?; let tx_fee = *BASE_FEE; let box_selector = SimpleBoxSelector::new(); let oracle_token: SpecToken = SpecToken { @@ -132,16 +139,20 @@ pub fn build_publish_first_datapoint_action( let contract = OracleContract::checked_load(&inputs.contract_inputs)?; let min_storage_rent = contract.parameters().min_storage_rent; let target_balance = min_storage_rent.checked_add(&tx_fee).unwrap(); + let target_tokens = vec![ + oracle_token.clone().into(), reward_token.clone().into() + ]; - let wallet_boxes_selection = box_selector.select( + let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), target_balance, target_tokens.clone())?; + let box_selection = box_selector.select( unspent_boxes.clone(), target_balance, - &[oracle_token.clone().into(), reward_token.clone().into()], + target_tokens.as_slice(), )?; - + let oracle_pk = address_to_p2pk(&oracle_address.address()).unwrap(); let output_candidate = make_oracle_box_candidate( &contract, - public_key, + *oracle_pk.h, new_datapoint, EpochCounter(1), oracle_token, @@ -150,9 +161,10 @@ pub fn build_publish_first_datapoint_action( height, )?; - let box_id = wallet_boxes_selection.boxes.first().box_id(); + let box_id = box_selection.boxes.first().box_id(); + let inputs = box_selection.boxes.clone().to_vec(); let mut tx_builder = TxBuilder::new( - wallet_boxes_selection, + box_selection, vec![output_candidate], height.0, tx_fee, @@ -168,7 +180,11 @@ pub fn build_publish_first_datapoint_action( let report = PublishDatapointActionReport { posted_datapoint: new_datapoint, }; - Ok((PublishDataPointAction { tx }, report)) + let context = match TransactionContext::new(tx, inputs, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(PublishDatapointActionError::TxSigningError(e)), + }; + Ok((PublishDataPointAction { transaction_context: context }, report)) } #[cfg(test)] @@ -176,28 +192,22 @@ mod tests { use std::convert::TryInto; use super::*; - use crate::box_kind::PoolBox; use crate::contracts::oracle::OracleContractParameters; - use crate::contracts::pool::PoolContractParameters; - use crate::oracle_state::PoolBoxSource; use crate::oracle_types::{EpochLength, Rate}; - use crate::pool_commands::test_utils::{ - find_input_boxes, generate_token_ids, make_datapoint_box, make_pool_box, - make_wallet_unspent_box, PoolBoxMock, WalletDataMock, - }; + use crate::pool_commands::test_utils::{generate_token_ids, make_datapoint_box, make_wallet_unspent_box}; use crate::spec_token::TokenIdKind; use ergo_lib::chain::ergo_state_context::ErgoStateContext; use ergo_lib::chain::transaction::TxId; use ergo_lib::ergotree_interpreter::sigma_protocol::private_input::DlogProverInput; - use ergo_lib::ergotree_ir::chain::address::AddressEncoder; + use ergo_lib::ergotree_ir::chain::address::{AddressEncoder, NetworkPrefix}; use ergo_lib::ergotree_ir::chain::ergo_box::{BoxTokens, ErgoBox, NonMandatoryRegisters}; use ergo_lib::ergotree_ir::chain::token::{Token, TokenId}; use ergo_lib::ergotree_ir::ergo_tree::ErgoTree; use ergo_lib::ergotree_ir::mir::constant::Constant; use ergo_lib::ergotree_ir::mir::expr::Expr; - use ergo_lib::wallet::signing::TransactionContext; - use ergo_lib::wallet::Wallet; use sigma_test_util::force_any_val; + use crate::cli_commands::bootstrap::tests::SubmitTxMock; + use crate::node_interface::test_utils::MockNodeApi; #[derive(Debug)] struct MockDatapointSource { @@ -216,24 +226,10 @@ mod tests { let height = BlockHeight(ctx.pre_header.height); let token_ids = generate_token_ids(); let oracle_contract_parameters = OracleContractParameters::default(); - let pool_contract_parameters = PoolContractParameters::default(); let pool_box_epoch_id = EpochCounter(1); - let in_pool_box = make_pool_box( - 200, - pool_box_epoch_id, - *BASE_FEE, - height - EpochLength(32), // from previous epoch - &pool_contract_parameters, - &token_ids, - ); let secret = force_any_val::(); - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); + let oracle_address = NetworkAddress::new(NetworkPrefix::Mainnet, &Address::P2Pk(secret.public_image().clone())); let oracle_pub_key = secret.public_image().h; - - let pool_box_mock = PoolBoxMock { - pool_box: in_pool_box, - }; - let oracle_box_wrapper_inputs = OracleBoxWrapperInputs::try_from((oracle_contract_parameters, &token_ids)).unwrap(); let oracle_box = OracleBoxWrapper::new( @@ -263,9 +259,12 @@ mod tests { BASE_FEE.checked_mul_u32(10000).unwrap(), None, ); - let wallet_mock = WalletDataMock { + let mock_node_api = MockNodeApi { unspent_boxes: vec![wallet_unspent_box], - change_address: change_address.clone(), + ctx: ctx.clone(), + secrets: vec![secret.clone().into()], + submitted_txs: &SubmitTxMock::default().transactions, + chain_submit_tx: None }; let datapoint_source = MockDatapointSource { @@ -273,8 +272,9 @@ mod tests { }; let (action, _) = build_subsequent_publish_datapoint_action( &oracle_box, - &wallet_mock, + &mock_node_api, height, + oracle_address, change_address.address(), &datapoint_source, pool_box_epoch_id, @@ -282,20 +282,7 @@ mod tests { ) .unwrap(); - let mut possible_input_boxes = vec![ - pool_box_mock.get_pool_box().unwrap().get_box().clone(), - oracle_box.get_box().clone(), - ]; - possible_input_boxes.append(&mut wallet_mock.get_unspent_wallet_boxes().unwrap()); - - let tx_context = TransactionContext::new( - action.tx.clone(), - find_input_boxes(action.tx, possible_input_boxes.clone()), - Vec::new(), - ) - .unwrap(); - - let _signed_tx = wallet.sign_transaction(tx_context, &ctx, None).unwrap(); + let _signed_tx = mock_node_api.sign_transaction(action.transaction_context).unwrap(); } #[test] @@ -317,7 +304,7 @@ mod tests { .unwrap(); let secret = force_any_val::(); - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); + let oracle_address = NetworkAddress::new(NetworkPrefix::Mainnet, &Address::P2Pk(secret.public_image().clone())); let c: Constant = secret.public_image().into(); let expr: Expr = c.into(); let ergo_tree = ErgoTree::try_from(expr).unwrap(); @@ -356,14 +343,19 @@ mod tests { let oracle_box_wrapper_inputs = OracleBoxWrapperInputs::try_from((oracle_contract_parameters.clone(), &token_ids)) .unwrap(); + let mock_node_api = MockNodeApi { + unspent_boxes: unspent_boxes.clone(), + ctx: ctx.clone(), + secrets: vec![secret.clone().into()], + submitted_txs: &SubmitTxMock::default().transactions, + chain_submit_tx: None + }; + let (action, _) = build_publish_first_datapoint_action( - &WalletDataMock { - unspent_boxes: unspent_boxes.clone(), - change_address: change_address.clone(), - }, + &mock_node_api, height, + oracle_address, change_address.address(), - *secret.public_image().h, oracle_box_wrapper_inputs, &MockDatapointSource { datapoint: 201.into(), @@ -372,14 +364,11 @@ mod tests { .unwrap(); assert_eq!( - action.tx.output_candidates.first().value, + action.transaction_context.spending_tx.output_candidates.first().value, oracle_contract_parameters.min_storage_rent ); - let tx_context = - TransactionContext::new(action.tx.clone(), unspent_boxes, Vec::new()).unwrap(); - - let _signed_tx = wallet.sign_transaction(tx_context, &ctx, None).unwrap(); + let _signed_tx = mock_node_api.sign_transaction(action.transaction_context).unwrap(); } #[test] @@ -390,26 +379,13 @@ mod tests { let minted_reward_token_id = RewardTokenId::from_token_id_unchecked(force_any_val::()); let oracle_contract_parameters = OracleContractParameters::default(); - let pool_contract_parameters = PoolContractParameters::default(); let pool_box_epoch_id = EpochCounter(1); dbg!(&token_ids); dbg!(&minted_reward_token_id); - let in_pool_box = make_pool_box( - 200, - pool_box_epoch_id, - *BASE_FEE, - height - EpochLength(32), // from previous epoch - &pool_contract_parameters, - &token_ids, - ); let secret = force_any_val::(); - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); + let oracle_address = NetworkAddress::new(NetworkPrefix::Mainnet, &Address::P2Pk(secret.public_image().clone())); let oracle_pub_key = secret.public_image().h; - let pool_box_mock = PoolBoxMock { - pool_box: in_pool_box, - }; - let oracle_box_wrapper_inputs = OracleBoxWrapperInputs::try_from((oracle_contract_parameters, &token_ids)).unwrap(); let oracle_box = OracleBoxWrapper::new( @@ -446,9 +422,12 @@ mod tests { .unwrap(), ), ); - let wallet_mock = WalletDataMock { + let mock_node_api = MockNodeApi { unspent_boxes: vec![wallet_unspent_box], - change_address: change_address.clone(), + ctx: ctx.clone(), + secrets: vec![secret.clone().into()], + submitted_txs: &SubmitTxMock::default().transactions, + chain_submit_tx: None }; let datapoint_source = MockDatapointSource { @@ -456,8 +435,9 @@ mod tests { }; let (action, _) = build_subsequent_publish_datapoint_action( &oracle_box, - &wallet_mock, + &mock_node_api, height, + oracle_address, change_address.address(), &datapoint_source, pool_box_epoch_id, @@ -465,19 +445,6 @@ mod tests { ) .unwrap(); - let mut possible_input_boxes = vec![ - pool_box_mock.get_pool_box().unwrap().get_box().clone(), - oracle_box.get_box().clone(), - ]; - possible_input_boxes.append(&mut wallet_mock.get_unspent_wallet_boxes().unwrap()); - - let tx_context = TransactionContext::new( - action.tx.clone(), - find_input_boxes(action.tx, possible_input_boxes.clone()), - Vec::new(), - ) - .unwrap(); - - let _signed_tx = wallet.sign_transaction(tx_context, &ctx, None).unwrap(); + let _signed_tx = mock_node_api.sign_transaction(action.transaction_context).unwrap(); } } diff --git a/core/src/pool_commands/refresh.rs b/core/src/pool_commands/refresh.rs index 9f0ba0cd..bd0089cd 100644 --- a/core/src/pool_commands/refresh.rs +++ b/core/src/pool_commands/refresh.rs @@ -8,6 +8,7 @@ use crate::box_kind::PoolBoxWrapper; use crate::box_kind::PostedOracleBox; use crate::box_kind::RefreshBox; use crate::box_kind::RefreshBoxWrapper; +use crate::node_interface::node_api::NodeApiTrait; use crate::oracle_config::BASE_FEE; use crate::oracle_state::BuybackBoxSource; use crate::oracle_state::DataSourceError; @@ -20,13 +21,11 @@ use crate::oracle_types::MinDatapoints; use crate::oracle_types::Rate; use crate::spec_token::RewardTokenId; use crate::spec_token::SpecToken; -use crate::wallet::WalletDataError; -use crate::wallet::WalletDataSource; use ergo_lib::chain::ergo_box::box_builder::ErgoBoxCandidateBuilderError; use ergo_lib::ergo_chain_types::EcPoint; use ergo_lib::ergotree_interpreter::sigma_protocol::prover::ContextExtension; -use ergo_lib::ergotree_ir::chain::address::Address; +use ergo_lib::ergotree_ir::chain::address::{Address, NetworkAddress}; use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBoxCandidate; use ergo_lib::ergotree_ir::chain::token::TokenAmount; use ergo_lib::wallet::box_selector::BoxSelection; @@ -38,6 +37,8 @@ use ergo_lib::wallet::tx_builder::TxBuilderError; use thiserror::Error; use std::convert::TryInto; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; +use crate::address_util::address_to_p2pk; #[derive(Debug, Error)] pub enum RefreshActionError { @@ -51,12 +52,12 @@ pub enum RefreshActionError { NotEnoughDatapoints, #[error("data source error: {0}")] DataSourceError(#[from] DataSourceError), - #[error("WalletData error: {0}")] - WalletData(#[from] WalletDataError), #[error("box selector error: {0}")] BoxSelectorError(#[from] BoxSelectorError), #[error("tx builder error: {0}")] TxBuilderError(#[from] TxBuilderError), + #[error("tx signing error: {0}")] + TxSigningError(#[from] TxSigningError), #[error("box builder error: {0}")] ErgoBoxCandidateBuilderError(#[from] ErgoBoxCandidateBuilderError), #[error("failed to found my own oracle box in the filtered posted oracle boxes")] @@ -70,10 +71,10 @@ pub fn build_refresh_action( datapoint_src: &dyn PostedDatapointBoxesSource, max_deviation_percent: u32, min_data_points: MinDatapoints, - wallet: &dyn WalletDataSource, + node_api: &dyn NodeApiTrait, height: BlockHeight, + oracle_address: NetworkAddress, change_address: Address, - my_oracle_pk: &EcPoint, buyback_box_source: Option<&dyn BuybackBoxSource>, ) -> Result<(RefreshAction, RefreshActionReport), RefreshActionError> { let tx_fee = *BASE_FEE; @@ -89,7 +90,6 @@ pub fn build_refresh_action( && b.epoch_counter() == in_pool_box_epoch_id }) .collect(); - // log::info!("Building refresh action {:?}", in_oracle_boxes); let deviation_range = max_deviation_percent; in_oracle_boxes.sort_by_key(|b| b.rate()); let valid_in_oracle_boxes_datapoints = filtered_oracle_boxes_by_rate( @@ -113,15 +113,16 @@ pub fn build_refresh_action( let rate = calc_pool_rate(valid_in_oracle_boxes.iter().map(|b| b.rate()).collect()); let reward_decrement = valid_in_oracle_boxes.len() as u64 * 2; let out_refresh_box = build_out_refresh_box(&in_refresh_box, height)?; + let my_oracle_pk = *address_to_p2pk(&oracle_address.address()).unwrap().h; let mut out_oracle_boxes = - build_out_oracle_boxes(&valid_in_oracle_boxes, height, my_oracle_pk)?; + build_out_oracle_boxes(&valid_in_oracle_boxes, height, &my_oracle_pk)?; let in_buyback_box_opt = buyback_box_source .map(|s| s.get_buyback_box()) .transpose()? .flatten(); - let unspent_boxes = wallet.get_unspent_wallet_boxes()?; + let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), tx_fee, vec![])?; let box_selector = SimpleBoxSelector::new(); let selection = box_selector.select(unspent_boxes, tx_fee, &[])?; @@ -131,7 +132,7 @@ pub fn build_refresh_action( ]; let my_input_oracle_box_index: i32 = valid_in_oracle_boxes .iter() - .position(|b| &b.public_key() == my_oracle_pk) + .position(|b| &b.public_key() == &my_oracle_pk) .ok_or(RefreshActionError::MyOracleBoxNoFound)? as i32; @@ -206,14 +207,20 @@ pub fn build_refresh_action( }; b.set_context_extension(ob.get_box().box_id(), ob_ctx_ext); }); - let tx = b.build()?; + let tx = b.clone().build()?; let report = RefreshActionReport { oracle_boxes_collected: valid_in_oracle_boxes .iter() .map(|b| b.public_key()) .collect(), }; - Ok((RefreshAction { tx }, report)) + let binding = b.box_selection(); + let ins = binding.boxes.as_vec().clone(); + let context = match TransactionContext::new(tx, ins, vec![]) { + Ok(ctx) => ctx, + Err(e) => return Err(RefreshActionError::TxSigningError(e)), + }; + Ok((RefreshAction { transaction_context: context }, report)) } fn filtered_oracle_boxes_by_rate( @@ -376,13 +383,11 @@ mod tests { use ergo_lib::chain::transaction::TxId; use ergo_lib::ergo_chain_types::EcPoint; use ergo_lib::ergotree_interpreter::sigma_protocol::private_input::DlogProverInput; - use ergo_lib::ergotree_ir::chain::address::AddressEncoder; + use ergo_lib::ergotree_ir::chain::address::{AddressEncoder, NetworkPrefix}; use ergo_lib::ergotree_ir::chain::ergo_box::box_value::BoxValue; use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox; use ergo_lib::ergotree_ir::chain::ergo_box::NonMandatoryRegisters; use ergo_lib::ergotree_ir::chain::token::Token; - use ergo_lib::wallet::signing::TransactionContext; - use ergo_lib::wallet::Wallet; use sigma_test_util::force_any_val; use crate::box_kind::BuybackBoxWrapper; @@ -390,20 +395,21 @@ mod tests { use crate::box_kind::PostedOracleBox; use crate::box_kind::RefreshBoxWrapper; use crate::box_kind::RefreshBoxWrapperInputs; + use crate::cli_commands::bootstrap::tests::SubmitTxMock; use crate::contracts::oracle::OracleContractParameters; use crate::contracts::pool::PoolContractParameters; use crate::contracts::refresh::RefreshContract; use crate::contracts::refresh::RefreshContractInputs; use crate::contracts::refresh::RefreshContractParameters; + use crate::node_interface::test_utils::MockNodeApi; use crate::oracle_config::BASE_FEE; use crate::oracle_state::DataSourceError; use crate::oracle_types::EpochLength; - use crate::pool_commands::test_utils::generate_token_ids; use crate::pool_commands::test_utils::BuybackBoxSourceMock; use crate::pool_commands::test_utils::{ - find_input_boxes, make_datapoint_box, make_pool_box, make_wallet_unspent_box, PoolBoxMock, - WalletDataMock, + make_datapoint_box, make_pool_box, make_wallet_unspent_box, PoolBoxMock, }; + use crate::pool_commands::test_utils::generate_token_ids; use crate::pool_config::TokenIds; use crate::spec_token::TokenIdKind; @@ -442,8 +448,8 @@ mod tests { inputs.refresh_nft_token_id.token_id(), 1u64.try_into().unwrap(), ))] - .try_into() - .unwrap(); + .try_into() + .unwrap(); RefreshBoxWrapper::new( ErgoBox::new( value, @@ -456,10 +462,10 @@ mod tests { force_any_val::(), 0, ) - .unwrap(), + .unwrap(), inputs, ) - .unwrap() + .unwrap() } #[allow(clippy::too_many_arguments)] @@ -528,7 +534,7 @@ mod tests { &token_ids, ); let secret = force_any_val::(); - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); + let oracle_address = NetworkAddress::new(NetworkPrefix::Mainnet, &Address::P2Pk(secret.public_image().clone())); let oracle_pub_key = secret.public_image().h; let oracle_pub_keys = vec![ @@ -549,11 +555,6 @@ mod tests { &oracle_contract_parameters, &token_ids, ); - let mut in_oracle_boxes_raw: Vec = in_oracle_boxes - .clone() - .into_iter() - .map(Into::into) - .collect(); let pool_box_mock = PoolBoxMock { pool_box: in_pool_box, @@ -571,9 +572,12 @@ mod tests { BASE_FEE.checked_mul_u32(10000).unwrap(), None, ); - let wallet_mock = WalletDataMock { + let mock_node_api = &MockNodeApi { unspent_boxes: vec![wallet_unspent_box], - change_address: change_address.clone(), + ctx: ctx.clone(), + secrets: vec![secret.clone().into()], + submitted_txs: &SubmitTxMock::default().transactions, + chain_submit_tx: None }; let (action, report) = build_refresh_action( @@ -584,35 +588,17 @@ mod tests { }), 5, MinDatapoints(4), - &wallet_mock, + mock_node_api, height, + oracle_address.clone(), change_address.address(), - &oracle_pub_key, None, ) .unwrap(); assert_eq!(report.oracle_boxes_collected.len(), 5); - let mut possible_input_boxes = vec![ - pool_box_mock.get_pool_box().unwrap().get_box().clone(), - refresh_box_mock - .get_refresh_box() - .unwrap() - .get_box() - .clone(), - ]; - possible_input_boxes.append(&mut in_oracle_boxes_raw); - possible_input_boxes.append(&mut wallet_mock.get_unspent_wallet_boxes().unwrap()); - - let tx_context = TransactionContext::new( - action.tx.clone(), - find_input_boxes(action.tx, possible_input_boxes), - Vec::new(), - ) - .unwrap(); - - let _signed_tx = wallet.sign_transaction(tx_context, &ctx, None).unwrap(); + let _signed_tx = mock_node_api.sign_transaction(action.transaction_context).unwrap(); let wrong_epoch_id_datapoints_mock = DatapointSourceMock { datapoints: make_datapoint_boxes( @@ -631,10 +617,10 @@ mod tests { &wrong_epoch_id_datapoints_mock, 5, MinDatapoints(4), - &wallet_mock, + mock_node_api, height, + oracle_address.clone(), change_address.address(), - &oracle_pub_key, None, ); dbg!(&wrong_epoch_res); @@ -680,17 +666,18 @@ mod tests { }), 5, MinDatapoints(4), - &wallet_mock, + mock_node_api, height, + oracle_address, change_address.address(), - &oracle_pub_key, Some(&buyback_source), ) .unwrap(); assert_eq!( action_with_buyback - .tx + .transaction_context + .spending_tx .output_candidates .get(2) .unwrap() @@ -703,7 +690,8 @@ mod tests { ); assert_eq!( action_with_buyback - .tx + .transaction_context + .spending_tx .output_candidates .get(2) .unwrap() @@ -718,7 +706,8 @@ mod tests { ); assert_eq!( action_with_buyback - .tx + .transaction_context + .spending_tx .output_candidates .get(2) .unwrap() @@ -735,7 +724,8 @@ mod tests { assert_eq!( action_with_buyback - .tx + .transaction_context + .spending_tx .output_candidates .get(0) .unwrap() diff --git a/core/src/pool_commands/test_utils.rs b/core/src/pool_commands/test_utils.rs index c237a10a..8fb865f6 100644 --- a/core/src/pool_commands/test_utils.rs +++ b/core/src/pool_commands/test_utils.rs @@ -6,10 +6,9 @@ use std::option::Option; use ergo_lib::chain::ergo_state_context::ErgoStateContext; use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; use ergo_lib::chain::transaction::TxId; -use ergo_lib::chain::transaction::TxIoVec; +// use ergo_lib::chain::transaction::TxIoVec; use ergo_lib::ergo_chain_types::Digest32; use ergo_lib::ergo_chain_types::EcPoint; -use ergo_lib::ergotree_ir::chain::address::NetworkAddress; use ergo_lib::ergotree_ir::chain::ergo_box::box_value::BoxValue; use ergo_lib::ergotree_ir::chain::ergo_box::BoxTokens; use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox; @@ -20,9 +19,9 @@ use ergo_lib::ergotree_ir::ergo_tree::ErgoTree; use ergo_lib::ergotree_ir::mir::constant::Constant; use ergo_lib::ergotree_ir::mir::expr::Expr; use ergo_lib::ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog; -use ergo_lib::wallet::signing::TransactionContext; +// use ergo_lib::wallet::signing::TransactionContext; use ergo_lib::wallet::Wallet; -use ergo_node_interface::node_interface::NodeError; +// use ergo_node_interface::node_interface::NodeError; use sigma_test_util::force_any_val; use crate::box_kind::BallotBoxWrapper; @@ -40,7 +39,6 @@ use crate::contracts::oracle::OracleContractParameters; use crate::contracts::pool::PoolContract; use crate::contracts::pool::PoolContractInputs; use crate::contracts::pool::PoolContractParameters; -use crate::node_interface::SignTransactionWithInputs; use crate::oracle_state::BuybackBoxSource; use crate::oracle_state::LocalBallotBoxSource; use crate::oracle_state::UpdateBoxSource; @@ -55,7 +53,6 @@ use crate::spec_token::RefreshTokenId; use crate::spec_token::RewardTokenId; use crate::spec_token::TokenIdKind; use crate::spec_token::UpdateTokenId; -use crate::wallet::WalletDataError; use super::*; @@ -104,22 +101,6 @@ impl VoteBallotBoxesSource for BallotBoxesMock { } } -#[derive(Clone)] -pub(crate) struct WalletDataMock { - pub unspent_boxes: Vec, - pub change_address: NetworkAddress, -} - -impl WalletDataSource for WalletDataMock { - fn get_unspent_wallet_boxes(&self) -> Result, WalletDataError> { - Ok(self.unspent_boxes.clone()) - } - - fn get_change_address(&self) -> Result { - Ok(self.change_address.clone()) - } -} - pub(crate) struct UpdateBoxMock { pub update_box: UpdateBoxWrapper, } @@ -292,30 +273,6 @@ pub struct LocalTxSigner<'a> { pub wallet: &'a Wallet, } -impl<'a> SignTransactionWithInputs for LocalTxSigner<'a> { - fn sign_transaction_with_inputs( - &self, - unsigned_tx: &UnsignedTransaction, - inputs: TxIoVec, - data_boxes: Option>, - ) -> Result { - let tx = self - .wallet - .sign_transaction( - TransactionContext::new( - unsigned_tx.clone(), - inputs.as_vec().clone(), - data_boxes.map(|bs| bs.as_vec().clone()).unwrap_or_default(), - ) - .unwrap(), - self.ctx, - None, - ) - .unwrap(); - Ok(tx) - } -} - pub fn init_log_tests() { // set log level via RUST_LOG=info env var env_logger::builder().is_test(true).try_init().unwrap(); diff --git a/core/src/scans.rs b/core/src/scans.rs deleted file mode 100644 index ac1f855c..00000000 --- a/core/src/scans.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::contracts::pool::PoolContractError; -use crate::contracts::refresh::RefreshContractError; -use crate::node_interface::node_api::{NodeApi, NodeApiError}; -use crate::oracle_config::{ORACLE_CONFIG, ORACLE_SECRETS}; - -use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox; -use ergo_node_interface::node_interface::NodeError; -use ergo_node_interface::ScanId; -use thiserror::Error; - -mod generic_token_scan; -mod registry; - -pub use generic_token_scan::*; -pub use registry::*; - -/// Integer which is provided by the Ergo node to reference a given scan. -pub type ScanID = String; - -#[derive(Debug, Error)] -pub enum ScanError { - #[error("node error: {0}")] - NodeError(#[from] NodeError), - #[error("node api error: {0}")] - NodeApiError(#[from] NodeApiError), - #[error("no boxes found")] - NoBoxesFound, - #[error("failed to register scan")] - FailedToRegister, - #[error("IO error: {0}")] - IoError(#[from] std::io::Error), - #[error("refresh contract error: {0}")] - RefreshContract(#[from] RefreshContractError), - #[error("pool contract error: {0}")] - PoolContract(#[from] PoolContractError), -} - -pub trait NodeScanId { - fn scan_id(&self) -> ScanId; -} - -pub trait ScanGetBoxes: NodeScanId { - fn get_boxes(&self) -> Result, ScanError> { - let node_api = NodeApi::new( - ORACLE_SECRETS.node_api_key.clone(), - ORACLE_SECRETS.wallet_password.clone(), - &ORACLE_CONFIG.node_url, - ); - let boxes = node_api.node.scan_boxes(self.scan_id())?; - Ok(boxes) - } - - fn get_box(&self) -> Result, ScanError> { - Ok(self.get_boxes()?.first().cloned()) - } -} diff --git a/core/src/scans/generic_token_scan.rs b/core/src/scans/generic_token_scan.rs deleted file mode 100644 index aff32cd5..00000000 --- a/core/src/scans/generic_token_scan.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::spec_token::TokenIdKind; -use crate::NodeApi; -use derive_more::From; -use derive_more::Into; -use ergo_node_interface::ScanId; -use serde::Deserialize; -use serde::Serialize; -use serde_json::json; - -use super::NodeScanId; -use super::ScanError; -use super::ScanGetBoxes; - -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, From, Into)] -#[serde(try_from = "String", into = "String")] -pub struct GenericTokenScan { - id: ScanId, - fantom: std::marker::PhantomData, -} - -impl GenericTokenScan { - pub fn new(id: ScanId) -> Self { - Self { - id, - fantom: std::marker::PhantomData, - } - } - - pub fn register(node_api: &NodeApi, token_id: &T) -> Result { - let scan_name = format!("token scan for {}", String::from(token_id.token_id())); - let id = node_api.register_scan(scan_name, Self::tracking_rule(token_id))?; - Ok(GenericTokenScan:: { - id, - fantom: std::marker::PhantomData, - }) - } - - pub fn tracking_rule(token_id: &T) -> serde_json::Value { - json!({ - "predicate": "and", - "args": - [ - { - "predicate": "containsAsset", - "assetId": token_id.token_id(), - } - ] - }) - } -} - -impl TryFrom for GenericTokenScan { - type Error = ScanError; - - fn try_from(value: String) -> Result { - let id = value.parse::().unwrap().into(); - Ok(GenericTokenScan { - id, - fantom: std::marker::PhantomData, - }) - } -} - -impl From> for String { - fn from(scan: GenericTokenScan) -> Self { - scan.id.to_string() - } -} - -impl NodeScanId for GenericTokenScan { - fn scan_id(&self) -> ScanId { - self.id - } -} - -impl ScanGetBoxes for GenericTokenScan {} diff --git a/core/src/scans/registry.rs b/core/src/scans/registry.rs deleted file mode 100644 index a99a71af..00000000 --- a/core/src/scans/registry.rs +++ /dev/null @@ -1,278 +0,0 @@ -use std::path::PathBuf; - -use crate::node_interface::node_api::NodeApi; -use crate::node_interface::node_api::NodeApiError; -use crate::pool_config::PoolConfig; -use crate::spec_token::BallotTokenId; -use crate::spec_token::BuybackTokenId; -use crate::spec_token::OracleTokenId; -use crate::spec_token::PoolTokenId; -use crate::spec_token::RefreshTokenId; -use crate::spec_token::UpdateTokenId; - -use crate::oracle_config::ORACLE_CONFIG; -use ::serde::Deserialize; -use ::serde::Serialize; -use once_cell::sync; -use thiserror::Error; - -use super::generic_token_scan::GenericTokenScan; -use super::NodeScanId; -use super::ScanError; - -pub static SCANS_DIR_PATH: sync::OnceCell = sync::OnceCell::new(); - -pub fn get_scans_file_path() -> PathBuf { - SCANS_DIR_PATH.get().unwrap().join("scanIDs.json") -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct NodeScanRegistry { - #[serde(rename = "All Datapoints Scan")] - pub oracle_token_scan: GenericTokenScan, - #[serde(rename = "Pool Box Scan")] - pub pool_token_scan: GenericTokenScan, - #[serde(rename = "Ballot Box Scan")] - pub ballot_token_scan: GenericTokenScan, - #[serde(rename = "Refresh Box Scan")] - pub refresh_token_scan: GenericTokenScan, - #[serde(rename = "Update Box Scan")] - pub update_token_scan: GenericTokenScan, - pub buyback_token_scan: Option>, -} - -impl NodeScanRegistry { - fn load_from_json_str(json_str: &str) -> Result { - Ok(serde_json::from_str(json_str) - .map_err(|e| NodeScanRegistryError::Parse(e.to_string()))?) - } - - fn save_to_json_str(&self) -> String { - serde_json::to_string_pretty(&self).unwrap() - } - - fn save_to_json_file(&self, file_path: &PathBuf) -> Result<(), anyhow::Error> { - let json_str = self.save_to_json_str(); - log::debug!("Saving scan IDs to {}", file_path.display()); - Ok(std::fs::write(file_path, json_str) - .map_err(|e| NodeScanRegistryError::Io(e.to_string()))?) - } - - fn register_and_save_scans_inner( - node_api: &NodeApi, - pool_config: &PoolConfig, - ) -> std::result::Result { - log::info!("Registering UTXO-Set Scans"); - let oracle_token_scan = - GenericTokenScan::register(node_api, &pool_config.token_ids.oracle_token_id)?; - let pool_token_scan = - GenericTokenScan::register(node_api, &pool_config.token_ids.pool_nft_token_id)?; - let ballot_token_scan = - GenericTokenScan::register(node_api, &pool_config.token_ids.ballot_token_id)?; - let refresh_token_scan = - GenericTokenScan::register(node_api, &pool_config.token_ids.refresh_nft_token_id)?; - let update_token_scan = - GenericTokenScan::register(node_api, &pool_config.token_ids.update_nft_token_id)?; - let buyback_token_scan = - if let Some(buyback_token_id) = pool_config.buyback_token_id.clone() { - Some(GenericTokenScan::register(node_api, &buyback_token_id)?) - } else { - None - }; - let registry = Self { - oracle_token_scan, - pool_token_scan, - ballot_token_scan, - refresh_token_scan, - update_token_scan, - buyback_token_scan, - }; - registry.save_to_json_file(&get_scans_file_path())?; - node_api.rescan_from_height(ORACLE_CONFIG.scan_start_height)?; - Ok(registry) - } - - pub fn load() -> Result { - let path = get_scans_file_path(); - log::info!("Loading scan IDs from {}", path.display()); - let json_str = - std::fs::read_to_string(path).map_err(|e| NodeScanRegistryError::Io(e.to_string()))?; - let registry = Self::load_from_json_str(&json_str)?; - Ok(registry) - } - - pub fn ensure_node_registered_scans( - node_api: &NodeApi, - pool_config: &PoolConfig, - ) -> std::result::Result { - let path = get_scans_file_path(); - log::info!("Loading scan IDs from {}", path.display()); - let registry = if let Ok(json_str) = std::fs::read_to_string(path) { - let loaded_registry = Self::load_from_json_str(&json_str)?; - if let Some(pool_config_buyback_token_id) = pool_config.buyback_token_id.clone() { - log::info!("Buyback token is found in pool config, checking if scan is registered"); - if loaded_registry.buyback_token_scan.is_some() { - log::info!("Buyback token scan is already registered"); - loaded_registry - } else { - let buyback_token_scan = - GenericTokenScan::register(node_api, &pool_config_buyback_token_id)?; - node_api.rescan_from_height(ORACLE_CONFIG.scan_start_height)?; - let new_registry = Self { - buyback_token_scan: Some(buyback_token_scan), - ..loaded_registry - }; - new_registry.save_to_json_file(&get_scans_file_path())?; - new_registry - } - } else { - // no buyback token in pool config - if let Some(buy_back_token_scan) = loaded_registry.buyback_token_scan.clone() { - log::info!("No buyback token in the pool config but scan is registered. Deregistering it"); - // but registry has buyback token scan, deregister it - node_api.deregister_scan(buy_back_token_scan.scan_id())?; - let new_registry = Self { - buyback_token_scan: None, - ..loaded_registry - }; - new_registry.save_to_json_file(&get_scans_file_path())?; - new_registry - } else { - loaded_registry - } - } - } else { - log::info!("Scans not found"); - Self::register_and_save_scans_inner(node_api, pool_config)? - }; - wait_for_node_rescan(node_api)?; - Ok(registry) - } - - pub fn deregister_all_scans(self, node_api: &NodeApi) -> Result<(), NodeApiError> { - node_api.deregister_scan(self.oracle_token_scan.scan_id())?; - node_api.deregister_scan(self.pool_token_scan.scan_id())?; - node_api.deregister_scan(self.ballot_token_scan.scan_id())?; - node_api.deregister_scan(self.refresh_token_scan.scan_id())?; - node_api.deregister_scan(self.update_token_scan.scan_id())?; - if let Some(buy_back_token_scan) = self.buyback_token_scan { - node_api.deregister_scan(buy_back_token_scan.scan_id())?; - } - Ok(()) - } -} - -pub fn wait_for_node_rescan(node_api: &NodeApi) -> Result<(), NodeApiError> { - let wallet_height = node_api.node.wallet_status()?.height; - let block_height = node_api.node.current_block_height()?; - if wallet_height == block_height { - log::debug!("No wallet scan is running"); - return Ok(()); - } - Ok(loop { - let wallet_height = node_api.node.wallet_status()?.height; - let block_height = node_api.node.current_block_height()?; - println!("Scanned {}/{} blocks", wallet_height, block_height); - if wallet_height == block_height { - log::info!("Wallet Scan Complete!"); - break; - } - std::thread::sleep(std::time::Duration::from_secs(1)); - }) -} - -#[derive(Debug, Error)] -pub enum NodeScanRegistryError { - #[error("Error registering scan: {0}")] - Scan(#[from] ScanError), - #[error("Error node: {0}")] - NodeApi(#[from] NodeApiError), - #[error("Error parsing scans file: {0}")] - Parse(String), - #[error("Error reading/writing file: {0}")] - Io(String), -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::scans::NodeScanId; - use ergo_node_interface::ScanId; - use expect_test::expect; - use pretty_assertions::assert_eq; - - fn expect_json(json_str: &str, expected_json: expect_test::Expect) { - expected_json.assert_eq(json_str); - } - - #[test] - fn parse_legacy_json() { - let json_str = r#"{ - "All Datapoints Scan": "185", - "Update Box Scan": "186", - "Pool Box Scan": "187", - "Refresh Box Scan": "188", - "Local Oracle Datapoint Scan": "189", - "Local Ballot Box Scan": "190", - "Ballot Box Scan": "191" - }"#; - let registry = NodeScanRegistry::load_from_json_str(json_str).unwrap(); - assert_eq!(registry.oracle_token_scan.scan_id(), ScanId::from(185)); - assert_eq!(registry.pool_token_scan.scan_id(), ScanId::from(187)); - } - - #[test] - fn check_encoded_json_id_as_string() { - let registry = NodeScanRegistry { - oracle_token_scan: GenericTokenScan::new(ScanId::from(185)), - pool_token_scan: GenericTokenScan::new(ScanId::from(187)), - ballot_token_scan: GenericTokenScan::new(ScanId::from(191)), - refresh_token_scan: GenericTokenScan::new(ScanId::from(188)), - update_token_scan: GenericTokenScan::new(ScanId::from(186)), - buyback_token_scan: None, - }; - let json_str = registry.save_to_json_str(); - expect_json( - &json_str, - expect![[r#" - { - "All Datapoints Scan": "185", - "Pool Box Scan": "187", - "Ballot Box Scan": "191", - "Refresh Box Scan": "188", - "Update Box Scan": "186", - "buyback_token_scan": null - }"#]], - ); - } - - #[test] - fn json_roundtrip() { - let registry = NodeScanRegistry { - oracle_token_scan: GenericTokenScan::new(ScanId::from(185)), - pool_token_scan: GenericTokenScan::new(ScanId::from(187)), - ballot_token_scan: GenericTokenScan::new(ScanId::from(191)), - refresh_token_scan: GenericTokenScan::new(ScanId::from(188)), - update_token_scan: GenericTokenScan::new(ScanId::from(186)), - buyback_token_scan: None, - }; - let json_str = registry.save_to_json_str(); - let registry2 = NodeScanRegistry::load_from_json_str(&json_str).unwrap(); - assert_eq!(registry, registry2); - } - - #[test] - fn json_roundtrip_with_buyback() { - let registry = NodeScanRegistry { - oracle_token_scan: GenericTokenScan::new(ScanId::from(185)), - pool_token_scan: GenericTokenScan::new(ScanId::from(187)), - ballot_token_scan: GenericTokenScan::new(ScanId::from(191)), - refresh_token_scan: GenericTokenScan::new(ScanId::from(188)), - update_token_scan: GenericTokenScan::new(ScanId::from(186)), - buyback_token_scan: Some(GenericTokenScan::new(ScanId::from(192))), - }; - let json_str = registry.save_to_json_str(); - let registry2 = NodeScanRegistry::load_from_json_str(&json_str).unwrap(); - assert_eq!(registry, registry2); - } -} diff --git a/core/src/templates.rs b/core/src/templates.rs deleted file mode 100644 index bd39ee64..00000000 --- a/core/src/templates.rs +++ /dev/null @@ -1,15 +0,0 @@ -/// Tx Request Template -pub static BASIC_TRANSACTION_SEND_REQUEST: &str = r#" -{ - "requests": [ - { - "address": "", - "value": 1000000, - "assets": [], - "registers": {} - } - ], - "fee": 1, - "inputsRaw": [], - "dataInputsRaw": [] -}"#; diff --git a/core/src/tests/bootstrap_and_run.rs b/core/src/tests/bootstrap_and_run.rs index 56b8e607..72baf6e1 100644 --- a/core/src/tests/bootstrap_and_run.rs +++ b/core/src/tests/bootstrap_and_run.rs @@ -1,44 +1,24 @@ -use std::cell::RefCell; use std::convert::TryInto; -use ergo_chain_sim::Block; use ergo_chain_sim::ChainSim; use ergo_lib::chain::ergo_state_context::ErgoStateContext; -use ergo_lib::chain::transaction::Transaction; -use ergo_lib::chain::transaction::TxId; use ergo_lib::ergotree_interpreter::sigma_protocol::private_input::DlogProverInput; use ergo_lib::ergotree_ir::chain::address::Address; use ergo_lib::ergotree_ir::chain::address::NetworkAddress; use ergo_lib::ergotree_ir::chain::address::NetworkPrefix; -use ergo_lib::wallet::Wallet; +use ergo_lib::wallet::secret_key::SecretKey; use sigma_test_util::force_any_val; - use crate::cli_commands::bootstrap::perform_bootstrap_chained_transaction; use crate::cli_commands::bootstrap::BootstrapConfig; use crate::cli_commands::bootstrap::BootstrapInput; -use crate::node_interface; -use crate::node_interface::SubmitTransaction; +use crate::cli_commands::bootstrap::tests::SubmitTxMock; +use crate::node_interface::test_utils::{ChainSubmitTx, MockNodeApi}; use crate::oracle_config::BASE_FEE; use crate::oracle_types::BlockHeight; use crate::pool_commands::test_utils::init_log_tests; -use crate::pool_commands::test_utils::LocalTxSigner; -use crate::pool_commands::test_utils::WalletDataMock; use crate::pool_config::PoolConfig; -struct ChainSubmitTx<'a> { - chain: RefCell<&'a mut ChainSim>, -} - -impl<'a> SubmitTransaction for ChainSubmitTx<'a> { - fn submit_transaction(&self, tx: &Transaction) -> node_interface::Result { - self.chain - .borrow_mut() - .add_block(Block::new(vec![tx.clone()])); - Ok(tx.id()) - } -} - -fn bootstrap(wallet: &Wallet, net_address: &NetworkAddress, chain: &mut ChainSim) -> PoolConfig { +fn bootstrap(secrets: Vec, net_address: &NetworkAddress, chain: &mut ChainSim) -> PoolConfig { let ctx = force_any_val::(); let unspent_boxes = chain.get_unspent_boxes(&net_address.address().script().unwrap()); @@ -52,12 +32,13 @@ fn bootstrap(wallet: &Wallet, net_address: &NetworkAddress, chain: &mut ChainSim perform_bootstrap_chained_transaction(BootstrapInput { oracle_address: net_address.clone(), config: bootstrap_config, - wallet: &WalletDataMock { + node_api: &MockNodeApi { unspent_boxes: unspent_boxes.clone(), - change_address: net_address.clone(), + ctx: ctx.clone(), + secrets, + chain_submit_tx: Some(& mut submit_tx_mock), + submitted_txs: &SubmitTxMock::default().transactions, }, - tx_signer: &mut LocalTxSigner { ctx: &ctx, wallet }, - submit_tx: &mut submit_tx_mock, tx_fee: *BASE_FEE, erg_value_per_box: *BASE_FEE, change_address: net_address.address(), @@ -72,7 +53,6 @@ fn test_bootstrap_and_run() { init_log_tests(); let mut chain = ChainSim::new(); let secret = force_any_val::(); - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); let net_address = NetworkAddress::new( NetworkPrefix::Mainnet, &Address::P2Pk(secret.public_image()), @@ -82,6 +62,6 @@ fn test_bootstrap_and_run() { 100_000_000_u64.try_into().unwrap(), None, ); - let _oracle_config = bootstrap(&wallet, &net_address, &mut chain); + let _oracle_config = bootstrap(vec![secret.clone().into()], &net_address, &mut chain); assert_eq!(chain.height, 8); } diff --git a/core/src/wallet.rs b/core/src/wallet.rs deleted file mode 100644 index e7b607f9..00000000 --- a/core/src/wallet.rs +++ /dev/null @@ -1,25 +0,0 @@ -use ergo_lib::ergotree_ir::chain::address::AddressEncoderError; -use ergo_lib::ergotree_ir::chain::address::NetworkAddress; -use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox; -use ergo_node_interface::node_interface::NodeError; -use thiserror::Error; - -use crate::node_interface::node_api::NodeApiError; - -#[derive(Debug, Error)] -pub enum WalletDataError { - #[error("node error: {0}")] - NodeError(#[from] NodeError), - #[error("no change address found")] - NoChangeAddressSetInNode, - #[error("AddressEncoder error: {0}")] - AddressEncoder(#[from] AddressEncoderError), - #[error("node api error: {0}")] - NodeApiError(#[from] NodeApiError), -} - -// TODO: remove and pass unspent boxes and change address directly? -pub trait WalletDataSource { - fn get_unspent_wallet_boxes(&self) -> Result, WalletDataError>; - fn get_change_address(&self) -> Result; -} diff --git a/docs/how_to_bootstrap.md b/docs/how_to_bootstrap.md index d780a316..1a1ec19c 100644 --- a/docs/how_to_bootstrap.md +++ b/docs/how_to_bootstrap.md @@ -19,7 +19,7 @@ and set the required parameters: - `oracle_address` to my node's wallet address (make sure you have coins). - `node_url` - node URL; -Set the environment variable `ORACLE_NODE_API_KEY` to the node's API key. You can put it in the `.secrets` file and then run `source .secrets` to load it into the environment. This way, the key does not get stored in the shell history. +Set the environment variable `ORACLE_WALLET_MNEMONIC` to the oracle's mnemonic. You can put it in the `.secrets` file and then run `source .secrets` to load it into the environment. This way, the key does not get stored in the shell history. Run diff --git a/docs/update_epoch_length.md b/docs/update_epoch_length.md index 16335cc3..3a842765 100644 --- a/docs/update_epoch_length.md +++ b/docs/update_epoch_length.md @@ -77,6 +77,6 @@ Run oracle-core import-update-pool pool_config_updated.yaml ``` -This will update the pool_config.yaml, removes `scanIds.json`. Restart the oracle afterwards. +This will update the pool_config.yaml. Restart the oracle afterwards. Distribute the `pool_config_updated.yaml` file to all the oracles and instruct them to run the above `import-update-pool` command. \ No newline at end of file From 3f8d5248977560b74e08ec99b585224d40472a0f Mon Sep 17 00:00:00 2001 From: Moein zargarzadeh Date: Fri, 7 Mar 2025 19:18:33 +0330 Subject: [PATCH 2/6] improve structs and clean unused imports --- core/src/cli_commands/bootstrap.rs | 7 +----- .../src/cli_commands/extract_reward_tokens.rs | 3 +-- core/src/cli_commands/prepare_update.rs | 7 +----- .../src/cli_commands/transfer_oracle_token.rs | 7 ++---- core/src/cli_commands/update_pool.rs | 3 +-- core/src/cli_commands/vote_update_pool.rs | 3 +-- core/src/main.rs | 2 +- core/src/node_interface.rs | 2 +- core/src/node_interface/node_api.rs | 2 -- core/src/node_interface/test_utils.rs | 22 +++++-------------- core/src/pool_commands/publish_datapoint.rs | 3 +-- core/src/pool_commands/refresh.rs | 3 +-- core/src/pool_commands/test_utils.rs | 10 --------- core/src/tests/bootstrap_and_run.rs | 3 +-- 14 files changed, 18 insertions(+), 59 deletions(-) diff --git a/core/src/cli_commands/bootstrap.rs b/core/src/cli_commands/bootstrap.rs index 3dde107b..273435dd 100644 --- a/core/src/cli_commands/bootstrap.rs +++ b/core/src/cli_commands/bootstrap.rs @@ -677,12 +677,7 @@ pub(crate) mod tests { use sigma_test_util::force_any_val; use super::*; - use crate::node_interface::test_utils::MockNodeApi; - use std::cell::RefCell; - #[derive(Default)] - pub(crate) struct SubmitTxMock { - pub(crate) transactions: RefCell>, - } + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; #[test] fn test_bootstrap() { diff --git a/core/src/cli_commands/extract_reward_tokens.rs b/core/src/cli_commands/extract_reward_tokens.rs index fddc5e67..468184cb 100644 --- a/core/src/cli_commands/extract_reward_tokens.rs +++ b/core/src/cli_commands/extract_reward_tokens.rs @@ -219,8 +219,7 @@ mod tests { use ergo_lib::ergotree_interpreter::sigma_protocol::private_input::DlogProverInput; use ergo_lib::ergotree_ir::chain::address::AddressEncoder; use sigma_test_util::force_any_val; - use crate::cli_commands::bootstrap::tests::SubmitTxMock; - use crate::node_interface::test_utils::MockNodeApi; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; #[test] fn test_extract_reward_tokens() { diff --git a/core/src/cli_commands/prepare_update.rs b/core/src/cli_commands/prepare_update.rs index c8e3cde3..0238b073 100644 --- a/core/src/cli_commands/prepare_update.rs +++ b/core/src/cli_commands/prepare_update.rs @@ -568,13 +568,8 @@ mod test { wallet::Wallet, }; use sigma_test_util::force_any_val; - use url::Url; use super::*; - use crate::{ - cli_commands::bootstrap::tests::SubmitTxMock, - pool_commands::test_utils::LocalTxSigner, - }; - use crate::node_interface::test_utils::MockNodeApi; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; #[test] fn test_prepare_update_transaction() { diff --git a/core/src/cli_commands/transfer_oracle_token.rs b/core/src/cli_commands/transfer_oracle_token.rs index 38323bfe..fb1ed8c9 100644 --- a/core/src/cli_commands/transfer_oracle_token.rs +++ b/core/src/cli_commands/transfer_oracle_token.rs @@ -198,10 +198,8 @@ mod tests { use ergo_lib::chain::ergo_state_context::ErgoStateContext; use ergo_lib::ergotree_interpreter::sigma_protocol::private_input::DlogProverInput; use ergo_lib::ergotree_ir::chain::address::AddressEncoder; - use ergo_lib::wallet::Wallet; use sigma_test_util::force_any_val; - use crate::cli_commands::bootstrap::tests::SubmitTxMock; - use crate::node_interface::test_utils::MockNodeApi; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; #[test] fn test_transfer_oracle_datapoint() { @@ -209,7 +207,6 @@ mod tests { let height = BlockHeight(ctx.pre_header.height); let token_ids = generate_token_ids(); let secret = force_any_val::(); - let wallet = Wallet::from_secrets(vec![secret.clone().into()]); let oracle_pub_key = secret.public_image().h; let parameters = OracleContractParameters::default(); @@ -257,6 +254,6 @@ mod tests { ) .unwrap(); - let _signed_tx = wallet.sign_transaction(context, &ctx, None).unwrap(); + let _signed_tx = mock_node_api.sign_transaction(context).unwrap(); } } diff --git a/core/src/cli_commands/update_pool.rs b/core/src/cli_commands/update_pool.rs index 18b9184a..a19518ea 100644 --- a/core/src/cli_commands/update_pool.rs +++ b/core/src/cli_commands/update_pool.rs @@ -456,9 +456,8 @@ mod tests { }, spec_token::{RefreshTokenId, RewardTokenId, SpecToken, TokenIdKind}, }; - use crate::cli_commands::bootstrap::tests::SubmitTxMock; use crate::node_interface::node_api::NodeApiTrait; - use crate::node_interface::test_utils::MockNodeApi; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; use super::build_update_pool_box_tx; fn force_any_tokenid() -> TokenId { diff --git a/core/src/cli_commands/vote_update_pool.rs b/core/src/cli_commands/vote_update_pool.rs index da9c37fb..22d334ef 100644 --- a/core/src/cli_commands/vote_update_pool.rs +++ b/core/src/cli_commands/vote_update_pool.rs @@ -304,9 +304,8 @@ mod tests { }, spec_token::{RewardTokenId, SpecToken, TokenIdKind}, }; - use crate::cli_commands::bootstrap::tests::SubmitTxMock; use crate::node_interface::node_api::NodeApiTrait; - use crate::node_interface::test_utils::MockNodeApi; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; use super::{build_tx_for_first_ballot_box, build_tx_with_existing_ballot_box}; #[test] diff --git a/core/src/main.rs b/core/src/main.rs index c7a4276e..4f7f755b 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -365,7 +365,7 @@ fn main() { error!("error: {:?}", e); } // Delay loop restart - thread::sleep(Duration::new(2, 0)); + thread::sleep(Duration::new(30, 0)); } } oracle_command => handle_pool_command(oracle_command, &node_api, network_prefix), diff --git a/core/src/node_interface.rs b/core/src/node_interface.rs index a38edc08..3e217b55 100644 --- a/core/src/node_interface.rs +++ b/core/src/node_interface.rs @@ -1,4 +1,4 @@ pub mod node_api; #[cfg(test)] -pub mod test_utils; \ No newline at end of file +pub mod test_utils; diff --git a/core/src/node_interface/node_api.rs b/core/src/node_interface/node_api.rs index e80dc962..e05f6388 100644 --- a/core/src/node_interface/node_api.rs +++ b/core/src/node_interface/node_api.rs @@ -10,9 +10,7 @@ use ergo_lib::wallet::signing::TransactionContext; use ergo_lib::wallet::{Wallet, WalletError}; use ergo_node_interface::scanning::NodeError; use ergo_node_interface::{NodeInterface, P2PKAddressString}; -// use log::info; use reqwest::Url; -// use serde_json::json; use thiserror::Error; use crate::oracle_config::ORACLE_SECRETS; diff --git a/core/src/node_interface/test_utils.rs b/core/src/node_interface/test_utils.rs index 4d24fabe..cbc7ac65 100644 --- a/core/src/node_interface/test_utils.rs +++ b/core/src/node_interface/test_utils.rs @@ -18,6 +18,11 @@ pub struct ChainSubmitTx<'a> { pub(crate) chain: RefCell<&'a mut ChainSim>, } +#[derive(Default)] +pub(crate) struct SubmitTxMock { + pub(crate) transactions: RefCell>, +} + pub struct MockNodeApi<'a> { pub unspent_boxes: Vec, pub secrets: Vec, @@ -26,7 +31,7 @@ pub struct MockNodeApi<'a> { pub ctx: ErgoStateContext, } -impl NodeApiTrait for MockNodeApi<'_>{ +impl NodeApiTrait for MockNodeApi<'_> { fn get_unspent_boxes_by_address_with_token_filter_option(&self, _address: &P2PKAddressString, _target_balance: BoxValue, _target_tokens: Vec, _filter_boxes_token_ids: Vec) -> Result, BoxSelectorError> { Ok(self.unspent_boxes.clone()) } @@ -77,18 +82,3 @@ impl NodeApiTrait for MockNodeApi<'_>{ .and_then(|tx| self.submit_transaction(&tx)) } } - -// impl TransactionUtils for MockNodeApi{ -// -// fn sign_transaction(&self, _transaction_context: TransactionContext) -> Result { -// self.signed_tx.clone().ok_or(NodeApiError::NodeInterfaceError( -// NodeError::Other("No signed transaction provided".to_string()), -// )) -// } -// -// fn submit_transaction(&self, _tx: &Transaction) -> Result { -// self.tx_id.clone().ok_or(NodeApiError::NodeInterfaceError( -// NodeError::Other("No transaction ID provided".to_string()), -// )) -// } -// } \ No newline at end of file diff --git a/core/src/pool_commands/publish_datapoint.rs b/core/src/pool_commands/publish_datapoint.rs index 2f84aa1f..65a828d2 100644 --- a/core/src/pool_commands/publish_datapoint.rs +++ b/core/src/pool_commands/publish_datapoint.rs @@ -206,8 +206,7 @@ mod tests { use ergo_lib::ergotree_ir::mir::constant::Constant; use ergo_lib::ergotree_ir::mir::expr::Expr; use sigma_test_util::force_any_val; - use crate::cli_commands::bootstrap::tests::SubmitTxMock; - use crate::node_interface::test_utils::MockNodeApi; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; #[derive(Debug)] struct MockDatapointSource { diff --git a/core/src/pool_commands/refresh.rs b/core/src/pool_commands/refresh.rs index bd0089cd..3caca3e4 100644 --- a/core/src/pool_commands/refresh.rs +++ b/core/src/pool_commands/refresh.rs @@ -395,13 +395,12 @@ mod tests { use crate::box_kind::PostedOracleBox; use crate::box_kind::RefreshBoxWrapper; use crate::box_kind::RefreshBoxWrapperInputs; - use crate::cli_commands::bootstrap::tests::SubmitTxMock; use crate::contracts::oracle::OracleContractParameters; use crate::contracts::pool::PoolContractParameters; use crate::contracts::refresh::RefreshContract; use crate::contracts::refresh::RefreshContractInputs; use crate::contracts::refresh::RefreshContractParameters; - use crate::node_interface::test_utils::MockNodeApi; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; use crate::oracle_config::BASE_FEE; use crate::oracle_state::DataSourceError; use crate::oracle_types::EpochLength; diff --git a/core/src/pool_commands/test_utils.rs b/core/src/pool_commands/test_utils.rs index 8fb865f6..ad516298 100644 --- a/core/src/pool_commands/test_utils.rs +++ b/core/src/pool_commands/test_utils.rs @@ -3,10 +3,8 @@ use std::convert::TryFrom; use std::convert::TryInto; use std::option::Option; -use ergo_lib::chain::ergo_state_context::ErgoStateContext; use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; use ergo_lib::chain::transaction::TxId; -// use ergo_lib::chain::transaction::TxIoVec; use ergo_lib::ergo_chain_types::Digest32; use ergo_lib::ergo_chain_types::EcPoint; use ergo_lib::ergotree_ir::chain::ergo_box::box_value::BoxValue; @@ -19,9 +17,6 @@ use ergo_lib::ergotree_ir::ergo_tree::ErgoTree; use ergo_lib::ergotree_ir::mir::constant::Constant; use ergo_lib::ergotree_ir::mir::expr::Expr; use ergo_lib::ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog; -// use ergo_lib::wallet::signing::TransactionContext; -use ergo_lib::wallet::Wallet; -// use ergo_node_interface::node_interface::NodeError; use sigma_test_util::force_any_val; use crate::box_kind::BallotBoxWrapper; @@ -268,11 +263,6 @@ pub(crate) fn find_input_boxes( .clone() } -pub struct LocalTxSigner<'a> { - pub ctx: &'a ErgoStateContext, - pub wallet: &'a Wallet, -} - pub fn init_log_tests() { // set log level via RUST_LOG=info env var env_logger::builder().is_test(true).try_init().unwrap(); diff --git a/core/src/tests/bootstrap_and_run.rs b/core/src/tests/bootstrap_and_run.rs index 72baf6e1..4e087fbf 100644 --- a/core/src/tests/bootstrap_and_run.rs +++ b/core/src/tests/bootstrap_and_run.rs @@ -11,8 +11,7 @@ use sigma_test_util::force_any_val; use crate::cli_commands::bootstrap::perform_bootstrap_chained_transaction; use crate::cli_commands::bootstrap::BootstrapConfig; use crate::cli_commands::bootstrap::BootstrapInput; -use crate::cli_commands::bootstrap::tests::SubmitTxMock; -use crate::node_interface::test_utils::{ChainSubmitTx, MockNodeApi}; +use crate::node_interface::test_utils::{ChainSubmitTx, MockNodeApi, SubmitTxMock}; use crate::oracle_config::BASE_FEE; use crate::oracle_types::BlockHeight; use crate::pool_commands::test_utils::init_log_tests; From 07720ea14dbfec85eed90fb98bfdc8378fb6c606 Mon Sep 17 00:00:00 2001 From: Moein zargarzadeh Date: Fri, 7 Mar 2025 21:13:50 +0330 Subject: [PATCH 3/6] apply fmt --- core/src/actions.rs | 5 +- core/src/api.rs | 16 +- core/src/cli_commands/bootstrap.rs | 14 +- .../src/cli_commands/extract_reward_tokens.rs | 29 ++-- core/src/cli_commands/prepare_update.rs | 27 ++-- .../src/cli_commands/transfer_oracle_token.rs | 25 ++-- core/src/cli_commands/update_pool.rs | 68 ++++----- core/src/cli_commands/vote_update_pool.rs | 50 +++---- core/src/get_boxes.rs | 6 +- core/src/get_boxes/generic_token_fetch.rs | 4 +- core/src/get_boxes/registry.rs | 3 +- core/src/main.rs | 43 +++--- core/src/metrics.rs | 11 +- core/src/node_interface/node_api.rs | 140 ++++++++++++------ core/src/node_interface/test_utils.rs | 33 +++-- core/src/oracle_config.rs | 14 +- core/src/oracle_state.rs | 6 +- core/src/pool_commands.rs | 14 +- core/src/pool_commands/publish_datapoint.rs | 83 ++++++++--- core/src/pool_commands/refresh.rs | 35 +++-- core/src/tests/bootstrap_and_run.rs | 24 +-- 21 files changed, 372 insertions(+), 278 deletions(-) diff --git a/core/src/actions.rs b/core/src/actions.rs index 4766954e..e862eee1 100644 --- a/core/src/actions.rs +++ b/core/src/actions.rs @@ -1,7 +1,6 @@ /// This file holds all the actions which can be performed /// by an oracle part of the oracle pool. These actions /// are implemented on the `OraclePool` struct. - use derive_more::From; use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; use ergo_lib::wallet::signing::TransactionContext; @@ -24,12 +23,12 @@ pub enum PoolAction { #[derive(Debug)] pub struct RefreshAction { - pub transaction_context: TransactionContext + pub transaction_context: TransactionContext, } #[derive(Debug)] pub struct PublishDataPointAction { - pub transaction_context: TransactionContext + pub transaction_context: TransactionContext, } #[derive(Error, Debug)] diff --git a/core/src/api.rs b/core/src/api.rs index bc3b10a3..c7691dcf 100644 --- a/core/src/api.rs +++ b/core/src/api.rs @@ -128,9 +128,7 @@ async fn pool_status(oracle_pool: Arc) -> Result) -> Result, ApiError> { - let node_api = NodeApi::new( - &ORACLE_CONFIG.node_url, - ); + let node_api = NodeApi::new(&ORACLE_CONFIG.node_url); let current_height = node_api.node.current_block_height()? as u32; let pool_box = oracle_pool.get_pool_box_source().get_pool_box()?; let epoch_length = POOL_CONFIG @@ -158,9 +156,7 @@ fn pool_status_sync(oracle_pool: Arc) -> Result Result { let current_height = task::spawn_blocking(move || { - let node_api = NodeApi::new( - &ORACLE_CONFIG.node_url, - ); + let node_api = NodeApi::new(&ORACLE_CONFIG.node_url); node_api.node.current_block_height() }) .await @@ -201,9 +197,7 @@ async fn oracle_health(oracle_pool: Arc) -> impl IntoResponse { } fn oracle_health_sync(oracle_pool: Arc) -> Result { - let node_api = NodeApi::new( - &ORACLE_CONFIG.node_url, - ); + let node_api = NodeApi::new(&ORACLE_CONFIG.node_url); let current_height = (node_api.node.current_block_height()? as u32).into(); let epoch_length = POOL_CONFIG .refresh_box_wrapper_inputs @@ -245,9 +239,7 @@ async fn pool_health(oracle_pool: Arc) -> impl IntoResponse { } fn pool_health_sync(oracle_pool: Arc) -> Result { - let node_api = NodeApi::new( - &ORACLE_CONFIG.node_url, - ); + let node_api = NodeApi::new(&ORACLE_CONFIG.node_url); let current_height = (node_api.node.current_block_height()? as u32).into(); let pool_box = &oracle_pool.get_pool_box_source().get_pool_box()?; let pool_box_height = pool_box.get_box().creation_height.into(); diff --git a/core/src/cli_commands/bootstrap.rs b/core/src/cli_commands/bootstrap.rs index 273435dd..6656f24b 100644 --- a/core/src/cli_commands/bootstrap.rs +++ b/core/src/cli_commands/bootstrap.rs @@ -1,6 +1,7 @@ //! Bootstrap a new oracle pool use std::{convert::TryInto, io::Write, path::Path}; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_lib::{ chain::{ ergo_box::box_builder::{ErgoBoxCandidateBuilder, ErgoBoxCandidateBuilderError}, @@ -23,7 +24,6 @@ use ergo_lib::{ tx_builder::{TxBuilder, TxBuilderError}, }, }; -use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_node_interface::node_interface::NodeError; use log::{debug, info}; use serde::{Deserialize, Serialize}; @@ -43,9 +43,7 @@ use crate::{ }, }, explorer_api::wait_for_txs_confirmation, - node_interface::{ - node_api::{NodeApi, NodeApiTrait, NodeApiError}, - }, + node_interface::node_api::{NodeApi, NodeApiError, NodeApiTrait}, oracle_config::{BASE_FEE, ORACLE_CONFIG}, oracle_types::{BlockHeight, EpochCounter}, pool_config::{ @@ -241,7 +239,11 @@ pub(crate) fn perform_bootstrap_chained_transaction( info!("Creating and signing minting pool NFT tx"); let target_balance = calc_target_balance(num_transactions_left)?; debug!("target_balance: {:?}", target_balance); - let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), target_balance, [].into())?; + let unspent_boxes = node_api.get_unspent_boxes_by_address( + &oracle_address.to_base58(), + target_balance, + [].into(), + )?; debug!("unspent boxes: {:?}", unspent_boxes); let box_selector = SimpleBoxSelector::new(); let box_selection = box_selector.select(unspent_boxes.clone(), target_balance, &[])?; @@ -718,7 +720,7 @@ pub(crate) mod tests { ctx: ctx.clone(), secrets: vec![secret.clone().into()], submitted_txs: &submit_tx.transactions, - chain_submit_tx: None + chain_submit_tx: None, }, tx_fee: *BASE_FEE, erg_value_per_box: *BASE_FEE, diff --git a/core/src/cli_commands/extract_reward_tokens.rs b/core/src/cli_commands/extract_reward_tokens.rs index 468184cb..04c1c6bf 100644 --- a/core/src/cli_commands/extract_reward_tokens.rs +++ b/core/src/cli_commands/extract_reward_tokens.rs @@ -1,9 +1,11 @@ use std::convert::TryInto; +use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_lib::{ - chain::{ - ergo_box::box_builder::{ErgoBoxCandidateBuilder, ErgoBoxCandidateBuilderError}, - }, + chain::ergo_box::box_builder::{ErgoBoxCandidateBuilder, ErgoBoxCandidateBuilderError}, ergotree_interpreter::sigma_protocol::prover::ContextExtension, ergotree_ir::{ chain::{ @@ -17,13 +19,11 @@ use ergo_lib::{ tx_builder::{TxBuilder, TxBuilderError}, }, }; -use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; -use ergo_lib::ergotree_ir::chain::address::NetworkAddress; -use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; -use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_node_interface::node_interface::NodeError; use thiserror::Error; +use crate::node_interface::node_api::NodeApiTrait; +use crate::oracle_config::ORACLE_CONFIG; use crate::{ box_kind::{ make_collected_oracle_box_candidate, make_oracle_box_candidate, OracleBox, OracleBoxWrapper, @@ -34,8 +34,6 @@ use crate::{ oracle_types::BlockHeight, spec_token::SpecToken, }; -use crate::node_interface::node_api::NodeApiTrait; -use crate::oracle_config::ORACLE_CONFIG; #[derive(Debug, Error)] pub enum ExtractRewardTokensActionError { @@ -168,7 +166,11 @@ fn build_extract_reward_tokens_tx( // `BASE_FEE` each for the fee and the box holding the extracted reward tokens. let target_balance = BASE_FEE.checked_mul_u32(2).unwrap(); - let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), target_balance, [].into())?; + let unspent_boxes = node_api.get_unspent_boxes_by_address( + &oracle_address.to_base58(), + target_balance, + [].into(), + )?; let box_selector = SimpleBoxSelector::new(); let selection = box_selector.select(unspent_boxes, target_balance, &[])?; @@ -210,16 +212,15 @@ mod tests { use super::*; use crate::box_kind::{OracleBoxWrapper, OracleBoxWrapperInputs}; use crate::contracts::oracle::OracleContractParameters; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; use crate::oracle_types::EpochCounter; use crate::pool_commands::test_utils::{ - generate_token_ids, make_datapoint_box, make_wallet_unspent_box, - OracleBoxMock, + generate_token_ids, make_datapoint_box, make_wallet_unspent_box, OracleBoxMock, }; use ergo_lib::chain::ergo_state_context::ErgoStateContext; use ergo_lib::ergotree_interpreter::sigma_protocol::private_input::DlogProverInput; use ergo_lib::ergotree_ir::chain::address::AddressEncoder; use sigma_test_util::force_any_val; - use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; #[test] fn test_extract_reward_tokens() { @@ -264,7 +265,7 @@ mod tests { ctx: ctx.clone(), secrets: vec![secret.clone().into()], submitted_txs: &SubmitTxMock::default().transactions, - chain_submit_tx: None + chain_submit_tx: None, }; let (tx_context, num_reward_tokens) = build_extract_reward_tokens_tx( &local_datapoint_box_source, diff --git a/core/src/cli_commands/prepare_update.rs b/core/src/cli_commands/prepare_update.rs index 0238b073..bcc9acc6 100644 --- a/core/src/cli_commands/prepare_update.rs +++ b/core/src/cli_commands/prepare_update.rs @@ -7,6 +7,7 @@ use std::{ }; use derive_more::From; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_lib::{ chain::{ ergo_box::box_builder::{ErgoBoxCandidateBuilder, ErgoBoxCandidateBuilderError}, @@ -30,12 +31,13 @@ use ergo_lib::{ tx_builder::{TxBuilder, TxBuilderError}, }, }; -use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_node_interface::node_interface::NodeError; use log::{debug, info}; use serde::{Deserialize, Serialize}; use thiserror::Error; +use super::bootstrap::{NftMintDetails, TokenMintDetails}; +use crate::node_interface::node_api::NodeApiTrait; use crate::{ box_kind::{ make_refresh_box_candidate, BallotBoxWrapperInputs, PoolBox, PoolBoxWrapperInputs, @@ -53,9 +55,7 @@ use crate::{ }, }, explorer_api::wait_for_txs_confirmation, - node_interface::{ - node_api::{NodeApi, NodeApiError} - }, + node_interface::node_api::{NodeApi, NodeApiError}, oracle_config::{OracleConfig, BASE_FEE, ORACLE_CONFIG}, oracle_state::{DataSourceError, OraclePool}, oracle_types::BlockHeight, @@ -65,8 +65,6 @@ use crate::{ BallotTokenId, OracleTokenId, RefreshTokenId, RewardTokenId, TokenIdKind, UpdateTokenId, }, }; -use crate::node_interface::node_api::NodeApiTrait; -use super::bootstrap::{NftMintDetails, TokenMintDetails}; #[derive(Debug, Deserialize, Serialize, Clone)] pub struct UpdateTokensToMint { @@ -269,10 +267,7 @@ impl<'a> PrepareUpdate<'a> { Ok(ctx) => ctx, Err(e) => return Err(PrepareUpdateError::TxSigningError(e)), }; - let signed_tx = - self.input - .node_api - .sign_transaction(context)?; + let signed_tx = self.input.node_api.sign_transaction(context)?; self.num_transactions_left -= 1; self.built_txs.push(signed_tx.clone()); self.inputs_for_next_tx = self.filter_tx_outputs(signed_tx.outputs.clone()); @@ -345,7 +340,11 @@ impl<'a> PrepareUpdate<'a> { let target_balance = self.calc_target_balance(self.num_transactions_left)?; debug!("target_balance: {:?}", target_balance); - let unspent_boxes = self.input.node_api.get_unspent_boxes_by_address(&self.oracle_config.oracle_address.to_base58(), target_balance, [].into())?; + let unspent_boxes = self.input.node_api.get_unspent_boxes_by_address( + &self.oracle_config.oracle_address.to_base58(), + target_balance, + [].into(), + )?; let box_selector = SimpleBoxSelector::new(); let box_selection = box_selector.select(unspent_boxes.clone(), target_balance, &[])?; debug!("box selection: {:?}", box_selection); @@ -558,6 +557,8 @@ pub enum PrepareUpdateError { #[cfg(test)] mod test { + use super::*; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; use ergo_lib::{ chain::{ergo_state_context::ErgoStateContext, transaction::TxId}, ergotree_interpreter::sigma_protocol::private_input::DlogProverInput, @@ -568,8 +569,6 @@ mod test { wallet::Wallet, }; use sigma_test_util::force_any_val; - use super::*; - use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; #[test] fn test_prepare_update_transaction() { @@ -700,7 +699,7 @@ data_point_source_custom_script: ~ ctx: ctx.clone(), secrets: vec![secret.clone().into()], submitted_txs: &submit_tx.transactions, - chain_submit_tx: None + chain_submit_tx: None, }, tx_fee: *BASE_FEE, erg_value_per_box: *BASE_FEE, diff --git a/core/src/cli_commands/transfer_oracle_token.rs b/core/src/cli_commands/transfer_oracle_token.rs index fb1ed8c9..570bca83 100644 --- a/core/src/cli_commands/transfer_oracle_token.rs +++ b/core/src/cli_commands/transfer_oracle_token.rs @@ -1,5 +1,9 @@ use std::convert::TryInto; +use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_lib::{ chain::ergo_box::box_builder::ErgoBoxCandidateBuilderError, ergotree_interpreter::sigma_protocol::prover::ContextExtension, @@ -12,13 +16,11 @@ use ergo_lib::{ tx_builder::{TxBuilder, TxBuilderError}, }, }; -use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; -use ergo_lib::ergotree_ir::chain::address::NetworkAddress; -use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; -use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_node_interface::node_interface::NodeError; use thiserror::Error; +use crate::node_interface::node_api::NodeApiTrait; +use crate::oracle_config::ORACLE_CONFIG; use crate::{ box_kind::{ make_collected_oracle_box_candidate, make_oracle_box_candidate, OracleBox, OracleBoxWrapper, @@ -28,8 +30,6 @@ use crate::{ oracle_state::{DataSourceError, LocalDatapointBoxSource}, oracle_types::BlockHeight, }; -use crate::node_interface::node_api::NodeApiTrait; -use crate::oracle_config::ORACLE_CONFIG; #[derive(Debug, Error)] pub enum TransferOracleTokenActionError { @@ -149,7 +149,11 @@ fn build_transfer_oracle_token_tx( let target_balance = *BASE_FEE; - let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), target_balance, [].into())?; + let unspent_boxes = node_api.get_unspent_boxes_by_address( + &oracle_address.to_base58(), + target_balance, + [].into(), + )?; let box_selector = SimpleBoxSelector::new(); let selection = box_selector.select(unspent_boxes, target_balance, &[])?; let mut input_boxes = vec![in_oracle_box.get_box().clone()]; @@ -190,16 +194,15 @@ mod tests { use super::*; use crate::box_kind::{OracleBoxWrapper, OracleBoxWrapperInputs}; use crate::contracts::oracle::OracleContractParameters; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; use crate::oracle_types::EpochCounter; use crate::pool_commands::test_utils::{ - generate_token_ids, make_datapoint_box, make_wallet_unspent_box, - OracleBoxMock, + generate_token_ids, make_datapoint_box, make_wallet_unspent_box, OracleBoxMock, }; use ergo_lib::chain::ergo_state_context::ErgoStateContext; use ergo_lib::ergotree_interpreter::sigma_protocol::private_input::DlogProverInput; use ergo_lib::ergotree_ir::chain::address::AddressEncoder; use sigma_test_util::force_any_val; - use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; #[test] fn test_transfer_oracle_datapoint() { @@ -242,7 +245,7 @@ mod tests { ctx: ctx.clone(), secrets: vec![secret.clone().into()], submitted_txs: &SubmitTxMock::default().transactions, - chain_submit_tx: None + chain_submit_tx: None, }; let context = build_transfer_oracle_token_tx( &local_datapoint_box_source, diff --git a/core/src/cli_commands/update_pool.rs b/core/src/cli_commands/update_pool.rs index a19518ea..157c3914 100644 --- a/core/src/cli_commands/update_pool.rs +++ b/core/src/cli_commands/update_pool.rs @@ -1,3 +1,24 @@ +use crate::node_interface::node_api::NodeApiTrait; +use crate::oracle_config::ORACLE_CONFIG; +use crate::{ + box_kind::{ + make_pool_box_candidate_unchecked, BallotBox, CastBallotBoxVoteParameters, PoolBox, + PoolBoxWrapper, VoteBallotBoxWrapper, + }, + contracts::pool::PoolContract, + explorer_api::ergo_explorer_transaction_link, + oracle_config::BASE_FEE, + oracle_state::{ + DataSourceError, OraclePool, PoolBoxSource, UpdateBoxSource, VoteBallotBoxesSource, + }, + oracle_types::BlockHeight, + pool_config::{PoolConfig, POOL_CONFIG}, + spec_token::{RewardTokenId, SpecToken, TokenIdKind}, +}; +use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; +use ergo_lib::wallet::signing::TransactionContext; use ergo_lib::{ chain::{ ergo_box::box_builder::ErgoBoxCandidateBuilder, @@ -5,10 +26,7 @@ use ergo_lib::{ }, ergo_chain_types::blake2b256_hash, ergotree_interpreter::sigma_protocol::prover::ContextExtension, - ergotree_ir::chain::{ - address::Address, - ergo_box::{NonMandatoryRegisterId}, - }, + ergotree_ir::chain::{address::Address, ergo_box::NonMandatoryRegisterId}, ergotree_ir::serialization::SigmaSerializable, wallet::{ box_selector::{BoxSelection, BoxSelectorError}, @@ -19,28 +37,7 @@ use ergo_lib::{ use ergo_node_interface::node_interface::NodeError; use log::{error, info}; use std::convert::TryInto; -use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; -use ergo_lib::ergotree_ir::chain::address::NetworkAddress; -use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; -use ergo_lib::wallet::signing::TransactionContext; -use crate::{ - box_kind::{ - make_pool_box_candidate_unchecked, BallotBox, CastBallotBoxVoteParameters, PoolBox, - PoolBoxWrapper, VoteBallotBoxWrapper, - }, - contracts::pool::PoolContract, - explorer_api::ergo_explorer_transaction_link, - oracle_config::BASE_FEE, - oracle_state::{ - DataSourceError, OraclePool, PoolBoxSource, UpdateBoxSource, VoteBallotBoxesSource, - }, - oracle_types::BlockHeight, - pool_config::{PoolConfig, POOL_CONFIG}, - spec_token::{RewardTokenId, SpecToken, TokenIdKind}, -}; use thiserror::Error; -use crate::node_interface::node_api::NodeApiTrait; -use crate::oracle_config::ORACLE_CONFIG; #[derive(Debug, Error)] pub enum UpdatePoolError { @@ -346,13 +343,12 @@ fn build_update_pool_box_tx( }; // Find unspent boxes without ballot token, see: https://github.com/ergoplatform/oracle-core/pull/80#issuecomment-1200258458 - let unspent_boxes = node_api - .get_unspent_boxes_by_address_with_token_filter_option( - &oracle_address.to_base58(), - target_balance, - target_tokens.clone(), - vec![update_box.ballot_token_id()] - )?; + let unspent_boxes = node_api.get_unspent_boxes_by_address_with_token_filter_option( + &oracle_address.to_base58(), + target_balance, + target_tokens.clone(), + vec![update_box.ballot_token_id()], + )?; if unspent_boxes.is_empty() { error!("Could not find unspent wallet boxes that do not contain ballot token. Please move ballot tokens to another address"); @@ -438,6 +434,9 @@ mod tests { use sigma_test_util::force_any_val; use std::convert::TryInto; + use super::build_update_pool_box_tx; + use crate::node_interface::node_api::NodeApiTrait; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; use crate::{ box_kind::{ make_local_ballot_box_candidate, make_pool_box_candidate, PoolBoxWrapper, @@ -456,9 +455,6 @@ mod tests { }, spec_token::{RefreshTokenId, RewardTokenId, SpecToken, TokenIdKind}, }; - use crate::node_interface::node_api::NodeApiTrait; - use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; - use super::build_update_pool_box_tx; fn force_any_tokenid() -> TokenId { use proptest::strategy::Strategy; @@ -612,7 +608,7 @@ mod tests { ctx: ctx.clone(), secrets: vec![secret.clone().into()], submitted_txs: &SubmitTxMock::default().transactions, - chain_submit_tx: None + chain_submit_tx: None, }; let update_mock = UpdateBoxMock { update_box: UpdateBoxWrapper::new( diff --git a/core/src/cli_commands/vote_update_pool.rs b/core/src/cli_commands/vote_update_pool.rs index 22d334ef..58bfbb0a 100644 --- a/core/src/cli_commands/vote_update_pool.rs +++ b/core/src/cli_commands/vote_update_pool.rs @@ -1,9 +1,12 @@ use std::convert::{TryFrom, TryInto}; +use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::ergotree_ir::chain::token::Token; +use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_lib::{ - chain::{ - ergo_box::box_builder::ErgoBoxCandidateBuilderError, - }, + chain::ergo_box::box_builder::ErgoBoxCandidateBuilderError, ergo_chain_types::{Digest32, DigestNError, EcPoint}, ergotree_interpreter::sigma_protocol::prover::ContextExtension, ergotree_ir::chain::address::Address, @@ -12,13 +15,9 @@ use ergo_lib::{ tx_builder::{TxBuilder, TxBuilderError}, }, }; -use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; -use ergo_lib::ergotree_ir::chain::address::NetworkAddress; -use ergo_lib::ergotree_ir::chain::token::Token; -use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; -use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_node_interface::node_interface::NodeError; +use crate::node_interface::node_api::NodeApiTrait; use crate::{ box_kind::{make_local_ballot_box_candidate, BallotBox, BallotBoxWrapper}, contracts::ballot::{ @@ -32,7 +31,6 @@ use crate::{ spec_token::{RewardTokenId, SpecToken, TokenIdKind}, }; use thiserror::Error; -use crate::node_interface::node_api::NodeApiTrait; #[derive(Debug, Error)] pub enum VoteUpdatePoolError { @@ -178,7 +176,8 @@ fn build_tx_with_existing_ballot_box( in_ballot_box.get_box().value, height, )?; - let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), *BASE_FEE, vec![])?; + let unspent_boxes = + node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), *BASE_FEE, vec![])?; let box_selector = SimpleBoxSelector::new(); let selection = box_selector.select(unspent_boxes, *BASE_FEE, &[])?; let mut input_boxes = vec![in_ballot_box.get_box().clone()]; @@ -243,16 +242,12 @@ fn build_tx_for_first_ballot_box( let box_selector = SimpleBoxSelector::new(); let selection_target_balance = out_ballot_box_value.checked_add(&BASE_FEE).unwrap(); let target_tokens: Vec = vec![ballot_token.into()]; - let unspent_boxes = node_api - .get_unspent_boxes_by_address( - &oracle_address.to_base58(), - selection_target_balance, - target_tokens.clone() - )?; - let selection = box_selector.select( - unspent_boxes, + let unspent_boxes = node_api.get_unspent_boxes_by_address( + &oracle_address.to_base58(), selection_target_balance, - &target_tokens)?; + target_tokens.clone(), + )?; + let selection = box_selector.select(unspent_boxes, selection_target_balance, &target_tokens)?; let box_selection = BoxSelection { boxes: selection.boxes.as_vec().clone().try_into().unwrap(), change_boxes: selection.change_boxes, @@ -294,19 +289,17 @@ mod tests { }; use sigma_test_util::force_any_val; + use super::{build_tx_for_first_ballot_box, build_tx_with_existing_ballot_box}; + use crate::node_interface::node_api::NodeApiTrait; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; use crate::{ box_kind::{make_local_ballot_box_candidate, BallotBoxWrapper, BallotBoxWrapperInputs}, contracts::ballot::{BallotContract, BallotContractInputs, BallotContractParameters}, oracle_config::BASE_FEE, oracle_types::{BlockHeight, EpochLength}, - pool_commands::test_utils::{ - generate_token_ids, make_wallet_unspent_box, - }, + pool_commands::test_utils::{generate_token_ids, make_wallet_unspent_box}, spec_token::{RewardTokenId, SpecToken, TokenIdKind}, }; - use crate::node_interface::node_api::NodeApiTrait; - use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; - use super::{build_tx_for_first_ballot_box, build_tx_with_existing_ballot_box}; #[test] fn test_vote_update_pool_no_existing_ballot_box() { @@ -341,7 +334,7 @@ mod tests { ctx: ctx.clone(), secrets: vec![secret.clone().into()], submitted_txs: &SubmitTxMock::default().transactions, - chain_submit_tx: None + chain_submit_tx: None, }; let new_reward_token = SpecToken { @@ -349,8 +342,7 @@ mod tests { amount: 100_000.try_into().unwrap(), }; - let ballot_token_owner = if let Address::P2Pk(ballot_token_owner) = address.address() - { + let ballot_token_owner = if let Address::P2Pk(ballot_token_owner) = address.address() { ballot_token_owner.h } else { panic!("Expected P2PK address"); @@ -429,7 +421,7 @@ mod tests { ctx: ctx.clone(), secrets: vec![secret.clone().into()], submitted_txs: &SubmitTxMock::default().transactions, - chain_submit_tx: None + chain_submit_tx: None, }; let tx_context = build_tx_with_existing_ballot_box( &ballot_box, diff --git a/core/src/get_boxes.rs b/core/src/get_boxes.rs index 8ad7c3a9..d49a48ed 100644 --- a/core/src/get_boxes.rs +++ b/core/src/get_boxes.rs @@ -8,9 +8,9 @@ use thiserror::Error; mod generic_token_fetch; mod registry; +use crate::spec_token::TokenIdKind; pub use generic_token_fetch::*; pub use registry::*; -use crate::spec_token::TokenIdKind; #[derive(Debug, Error)] pub enum GetBoxesError { @@ -26,9 +26,7 @@ pub enum GetBoxesError { pub trait GetBoxes: TokenIdKind { fn get_boxes(&self) -> Result, GetBoxesError> { - let node_api = NodeApi::new( - &ORACLE_CONFIG.node_url - ); + let node_api = NodeApi::new(&ORACLE_CONFIG.node_url); let boxes = node_api.get_all_unspent_boxes_by_token_id(&self.token_id())?; Ok(boxes) } diff --git a/core/src/get_boxes/generic_token_fetch.rs b/core/src/get_boxes/generic_token_fetch.rs index d92db812..1109fc71 100644 --- a/core/src/get_boxes/generic_token_fetch.rs +++ b/core/src/get_boxes/generic_token_fetch.rs @@ -57,8 +57,8 @@ impl TokenIdKind for GenericTokenFetch { } fn from_token_id_unchecked(token: TokenId) -> Self { - Self::new(token) + Self::new(token) } } -impl GetBoxes for GenericTokenFetch {} \ No newline at end of file +impl GetBoxes for GenericTokenFetch {} diff --git a/core/src/get_boxes/registry.rs b/core/src/get_boxes/registry.rs index fedc76b8..f3f06897 100644 --- a/core/src/get_boxes/registry.rs +++ b/core/src/get_boxes/registry.rs @@ -6,9 +6,9 @@ use crate::spec_token::PoolTokenId; use crate::spec_token::RefreshTokenId; use crate::spec_token::UpdateTokenId; +use super::generic_token_fetch::GenericTokenFetch; use ::serde::Deserialize; use ::serde::Serialize; -use super::generic_token_fetch::GenericTokenFetch; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct TokenFetchRegistry { @@ -26,7 +26,6 @@ pub struct TokenFetchRegistry { } impl TokenFetchRegistry { - pub fn load() -> Result { log::info!("Registering token fetches"); let pool_config = &POOL_CONFIG; diff --git a/core/src/main.rs b/core/src/main.rs index 4f7f755b..e5d9ce3d 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -28,6 +28,7 @@ mod contracts; mod datapoint_source; mod default_parameters; mod explorer_api; +mod get_boxes; mod logging; mod metrics; mod migrate; @@ -38,7 +39,6 @@ mod oracle_state; mod oracle_types; mod pool_commands; mod pool_config; -mod get_boxes; mod serde; mod spec_token; mod state; @@ -47,6 +47,19 @@ mod util; #[cfg(test)] mod tests; +use crate::actions::execute_action; +use crate::address_util::pks_to_network_addresses; +use crate::api::start_rest_server; +use crate::box_kind::BallotBox; +use crate::contracts::ballot::BallotContract; +use crate::default_parameters::print_contract_hashes; +use crate::get_boxes::TokenFetchRegistry; +use crate::migrate::check_migration_to_split_config; +use crate::oracle_config::OracleConfig; +use crate::oracle_config::DEFAULT_ORACLE_CONFIG_FILE_NAME; +use crate::oracle_config::ORACLE_CONFIG_FILE_PATH; +use crate::oracle_config::ORACLE_CONFIG_OPT; +use crate::pool_config::POOL_CONFIG_FILE_PATH; use action_report::ActionReportStorage; use action_report::PoolActionReport; use actions::PoolAction; @@ -87,19 +100,6 @@ use std::sync::Arc; use std::sync::RwLock; use std::thread; use std::time::Duration; -use crate::actions::execute_action; -use crate::address_util::pks_to_network_addresses; -use crate::api::start_rest_server; -use crate::box_kind::BallotBox; -use crate::contracts::ballot::BallotContract; -use crate::default_parameters::print_contract_hashes; -use crate::migrate::check_migration_to_split_config; -use crate::oracle_config::OracleConfig; -use crate::oracle_config::DEFAULT_ORACLE_CONFIG_FILE_NAME; -use crate::oracle_config::ORACLE_CONFIG_FILE_PATH; -use crate::oracle_config::ORACLE_CONFIG_OPT; -use crate::pool_config::POOL_CONFIG_FILE_PATH; -use crate::get_boxes::TokenFetchRegistry; const APP_VERSION: &str = concat!( "v", @@ -272,9 +272,7 @@ fn main() { Arc::new(RwLock::new(ActionReportStorage::new())); log_on_launch(); - let node_api = NodeApi::new( - &ORACLE_CONFIG.node_url, - ); + let node_api = NodeApi::new(&ORACLE_CONFIG.node_url); if !node_api.node.indexer_status().unwrap().is_active { error!("Blockchain indexer is not active on the node"); @@ -454,12 +452,9 @@ fn handle_pool_command(command: Command, node_api: &NodeApi, network_prefix: Net reward_token_amount, } => { let reward_token_opt = check_reward_token_opt(reward_token_id, reward_token_amount); - if let Err(e) = cli_commands::update_pool::update_pool( - &op, - node_api, - reward_token_opt, - height, - ) { + if let Err(e) = + cli_commands::update_pool::update_pool(&op, node_api, reward_token_opt, height) + { error!("Fatal update-pool error: {:?}", e); std::process::exit(exitcode::SOFTWARE); } @@ -482,7 +477,7 @@ fn handle_pool_command(command: Command, node_api: &NodeApi, network_prefix: Net &POOL_CONFIG.token_ids.oracle_token_id, &POOL_CONFIG.token_ids.reward_token_id, POOL_CONFIG_FILE_PATH.get().unwrap(), - op.get_local_datapoint_box_source() + op.get_local_datapoint_box_source(), ) { error!("Fatal import pool update error : {:?}", e); std::process::exit(exitcode::SOFTWARE); diff --git a/core/src/metrics.rs b/core/src/metrics.rs index b8ee7908..8e89f43e 100644 --- a/core/src/metrics.rs +++ b/core/src/metrics.rs @@ -36,7 +36,6 @@ static POOL_BOX_HEIGHT: Lazy = Lazy::new(|| { m }); - static POOL_BOX_RATE: Lazy = Lazy::new(|| { let m = IntGauge::with_opts( Opts::new("pool_box_rate", "exchange rate from the pool box") @@ -334,7 +333,10 @@ fn update_my_claimable_reward_tokens(oracle_pool: Arc) { } } -pub fn update_metrics(oracle_pool: Arc, node_api: &NodeApi) -> Result<(), anyhow::Error> { +pub fn update_metrics( + oracle_pool: Arc, + node_api: &NodeApi, +) -> Result<(), anyhow::Error> { let current_height = (node_api.node.current_block_height()? as u32).into(); let network_prefix = ORACLE_CONFIG.change_address.clone().unwrap().network(); let pool_box = &oracle_pool.get_pool_box_source().get_pool_box()?; @@ -358,7 +360,10 @@ pub fn update_metrics(oracle_pool: Arc, node_api: &NodeApi) -> Resul pool_health.details.epoch_length, )?; update_oracle_health(&oracle_health); - let wallet_balance: i64 = node_api.node.nano_ergs_balance(&ORACLE_CONFIG.oracle_address.to_base58())? as i64; + let wallet_balance: i64 = node_api + .node + .nano_ergs_balance(&ORACLE_CONFIG.oracle_address.to_base58())? + as i64; ORACLE_NODE_WALLET_BALANCE.set(wallet_balance); POOL_BOX_REWARD_TOKEN_AMOUNT.set(pool_box.reward_token().amount.into()); update_reward_tokens_in_buyback_box(oracle_pool.clone()); diff --git a/core/src/node_interface/node_api.rs b/core/src/node_interface/node_api.rs index e05f6388..d76899a2 100644 --- a/core/src/node_interface/node_api.rs +++ b/core/src/node_interface/node_api.rs @@ -1,18 +1,20 @@ +use crate::oracle_config::ORACLE_SECRETS; use ergo_lib::chain::ergo_state_context::ErgoStateContext; -use ergo_lib::chain::transaction::{Transaction, TxId}; use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::chain::transaction::{Transaction, TxId}; use ergo_lib::ergotree_ir::chain::address::AddressEncoderError; use ergo_lib::ergotree_ir::chain::ergo_box::box_value::BoxValue; use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox; use ergo_lib::ergotree_ir::chain::token::{Token, TokenId}; -use ergo_lib::wallet::box_selector::{BoxSelection, BoxSelector, BoxSelectorError, ErgoBoxAssets, SimpleBoxSelector}; +use ergo_lib::wallet::box_selector::{ + BoxSelection, BoxSelector, BoxSelectorError, ErgoBoxAssets, SimpleBoxSelector, +}; use ergo_lib::wallet::signing::TransactionContext; use ergo_lib::wallet::{Wallet, WalletError}; use ergo_node_interface::scanning::NodeError; use ergo_node_interface::{NodeInterface, P2PKAddressString}; use reqwest::Url; use thiserror::Error; -use crate::oracle_config::ORACLE_SECRETS; pub trait NodeApiTrait { fn get_unspent_boxes_by_address_with_token_filter_option( @@ -44,10 +46,7 @@ pub trait NodeApiTrait { transaction_context: TransactionContext, ) -> Result; - fn submit_transaction( - &self, - tx: &Transaction, - ) -> Result; + fn submit_transaction(&self, tx: &Transaction) -> Result; fn sign_and_submit_transaction( &self, @@ -56,7 +55,6 @@ pub trait NodeApiTrait { } impl NodeApiTrait for NodeApi { - fn get_unspent_boxes_by_address_with_token_filter_option( &self, address: &P2PKAddressString, @@ -64,7 +62,12 @@ impl NodeApiTrait for NodeApi { target_tokens: Vec, filter_boxes_token_ids: Vec, ) -> Result, BoxSelectorError> { - self.get_unspent_boxes_by_address_with_token_filter_option(address, target_balance, target_tokens, filter_boxes_token_ids) + self.get_unspent_boxes_by_address_with_token_filter_option( + address, + target_balance, + target_tokens, + filter_boxes_token_ids, + ) } fn get_unspent_boxes_by_address( @@ -78,7 +81,7 @@ impl NodeApiTrait for NodeApi { fn get_unspent_boxes_by_token_id( &self, - token_id: &TokenId + token_id: &TokenId, ) -> Result, NodeApiError> { self.get_all_unspent_boxes_by_token_id(token_id) } @@ -98,10 +101,7 @@ impl NodeApiTrait for NodeApi { self.sign_transaction(transaction_context) } - fn submit_transaction( - &self, - tx: &Transaction, - ) -> Result { + fn submit_transaction(&self, tx: &Transaction) -> Result { self.submit_transaction(tx) } @@ -124,77 +124,132 @@ impl NodeApi { } /// Get unspent boxes by address with token filter option - pub fn get_unspent_boxes_by_address_with_token_filter_option(&self, address: &P2PKAddressString, target_balance: BoxValue, target_tokens: Vec, filter_boxes_token_ids: Vec) -> Result, BoxSelectorError> { + pub fn get_unspent_boxes_by_address_with_token_filter_option( + &self, + address: &P2PKAddressString, + target_balance: BoxValue, + target_tokens: Vec, + filter_boxes_token_ids: Vec, + ) -> Result, BoxSelectorError> { let default_limit = 100; let box_selector = SimpleBoxSelector::new(); let mut unspent_boxes: Vec = vec![]; let mut offset = 0; let mut selection: Option, BoxSelectorError>> = None; loop { - let boxes = self.node.unspent_boxes_by_address(address, offset, default_limit); + let boxes = self + .node + .unspent_boxes_by_address(address, offset, default_limit); if boxes.is_ok() { let boxes_clone = boxes.unwrap().clone(); - if boxes_clone.is_empty() { break; } + if boxes_clone.is_empty() { + break; + } for box_ in boxes_clone.iter() { let tokens = box_.tokens().clone(); if tokens.is_none() { unspent_boxes.push(box_.clone()); } else { let tokens = tokens.unwrap().to_vec(); - if tokens.iter().any(|token| filter_boxes_token_ids.contains(&token.token_id)) { + if tokens + .iter() + .any(|token| filter_boxes_token_ids.contains(&token.token_id)) + { continue; } unspent_boxes.push(box_.clone()); } } - let local_selection = box_selector.select(unspent_boxes.clone(), target_balance, target_tokens.as_slice()); + let local_selection = box_selector.select( + unspent_boxes.clone(), + target_balance, + target_tokens.as_slice(), + ); selection = Some(local_selection.clone()); - if local_selection.is_ok() { break; } + if local_selection.is_ok() { + break; + } offset += default_limit; - } else { break; } + } else { + break; + } } log::trace!("get_unspent_boxes_by_address_with_token_filter_option for address: {:#?} and found {:#?} boxes", address, unspent_boxes.len()); Ok(selection.unwrap()?.boxes.to_vec()) } /// Get unspent boxes by address - pub fn get_unspent_boxes_by_address(&self, address: &P2PKAddressString, target_balance: BoxValue, target_tokens: Vec) -> Result, BoxSelectorError> { + pub fn get_unspent_boxes_by_address( + &self, + address: &P2PKAddressString, + target_balance: BoxValue, + target_tokens: Vec, + ) -> Result, BoxSelectorError> { let default_limit = 100; let box_selector = SimpleBoxSelector::new(); let mut unspent_boxes: Vec = vec![]; let mut offset = 0; let mut selection: Option, BoxSelectorError>> = None; loop { - let boxes = self.node.unspent_boxes_by_address(address, offset, default_limit); + let boxes = self + .node + .unspent_boxes_by_address(address, offset, default_limit); if boxes.is_ok() { let mut boxes_clone = boxes.unwrap().clone(); - if boxes_clone.is_empty() { break; } + if boxes_clone.is_empty() { + break; + } unspent_boxes.append(&mut boxes_clone); - let local_selection = box_selector.select(unspent_boxes.clone(), target_balance, target_tokens.as_slice()); + let local_selection = box_selector.select( + unspent_boxes.clone(), + target_balance, + target_tokens.as_slice(), + ); selection = Some(local_selection.clone()); - if local_selection.is_ok() { break; } + if local_selection.is_ok() { + break; + } offset += default_limit; - } else { break; } + } else { + break; + } } - log::trace!("get_unspent_boxes_by_address for address: {:#?} and found {:#?} boxes", address, unspent_boxes.len()); + log::trace!( + "get_unspent_boxes_by_address for address: {:#?} and found {:#?} boxes", + address, + unspent_boxes.len() + ); Ok(selection.unwrap()?.boxes.to_vec()) } /// Get unspent boxes by token id - pub fn get_all_unspent_boxes_by_token_id(&self, token_id: &TokenId) -> Result, NodeApiError> { + pub fn get_all_unspent_boxes_by_token_id( + &self, + token_id: &TokenId, + ) -> Result, NodeApiError> { let default_limit = 100; let mut unspent_boxes: Vec = vec![]; let mut offset = 0; loop { - let boxes = self.node.unspent_boxes_by_token_id(token_id, offset, default_limit); + let boxes = self + .node + .unspent_boxes_by_token_id(token_id, offset, default_limit); if boxes.is_ok() { let mut boxes_clone = boxes.unwrap().clone(); - if boxes_clone.is_empty() { break; } + if boxes_clone.is_empty() { + break; + } unspent_boxes.append(&mut boxes_clone); offset += default_limit; - } else { break; } + } else { + break; + } } - log::trace!("get_unspent_boxes_by_token_id for token: {:#?} and found {:#?} boxes", token_id, unspent_boxes.len()); + log::trace!( + "get_unspent_boxes_by_token_id for token: {:#?} and found {:#?} boxes", + token_id, + unspent_boxes.len() + ); Ok(unspent_boxes) } @@ -219,30 +274,25 @@ impl NodeApi { serde_json::to_string_pretty(&transaction_context.spending_tx).unwrap() ); let wallet = self.get_wallet()?; - let signed_tx = wallet.sign_transaction(transaction_context, &self.node.get_state_context()?, None); + let signed_tx = + wallet.sign_transaction(transaction_context, &self.node.get_state_context()?, None); match signed_tx { Ok(tx) => { log::trace!( - "Signed transaction: {}", - serde_json::to_string_pretty(&tx).unwrap() + "Signed transaction: {}", + serde_json::to_string_pretty(&tx).unwrap() ); Ok(tx) } Err(wallet_err) => { - log::error!( - "Sign Transaction Failed: {}", - wallet_err.to_string() - ); + log::error!("Sign Transaction Failed: {}", wallet_err.to_string()); Err(NodeApiError::WalletError(wallet_err)) } } } /// Submit a signed `Transaction` to the mempool. - pub fn submit_transaction( - &self, - tx: &Transaction - ) -> Result { + pub fn submit_transaction(&self, tx: &Transaction) -> Result { Ok(self.node.submit_transaction(tx)?) } @@ -265,7 +315,7 @@ impl NodeApi { Ok(loop { if indexer_status.is_sync { log::debug!("Your indexer synced successfully."); - break + break; } std::thread::sleep(std::time::Duration::from_secs(1)); }) diff --git a/core/src/node_interface/test_utils.rs b/core/src/node_interface/test_utils.rs index cbc7ac65..022d076e 100644 --- a/core/src/node_interface/test_utils.rs +++ b/core/src/node_interface/test_utils.rs @@ -1,18 +1,18 @@ -use std::cell::RefCell; +use crate::node_interface::node_api::{NodeApiError, NodeApiTrait}; +use ergo_chain_sim::{Block, ChainSim}; use ergo_lib::chain::ergo_state_context::ErgoStateContext; -use ergo_lib::chain::transaction::{Transaction, TxId}; use ergo_lib::chain::transaction::unsigned::UnsignedTransaction; +use ergo_lib::chain::transaction::{Transaction, TxId}; use ergo_lib::ergotree_ir::chain::ergo_box::box_value::BoxValue; use ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox; use ergo_lib::ergotree_ir::chain::token::{Token, TokenId}; -use ergo_lib::wallet::box_selector::{BoxSelectorError}; +use ergo_lib::wallet::box_selector::BoxSelectorError; use ergo_lib::wallet::secret_key::SecretKey; use ergo_lib::wallet::signing::TransactionContext; use ergo_lib::wallet::Wallet; -use ergo_node_interface::{P2PKAddressString}; use ergo_node_interface::node_interface::NodeError; -use ergo_chain_sim::{Block, ChainSim}; -use crate::node_interface::node_api::{NodeApiError, NodeApiTrait}; +use ergo_node_interface::P2PKAddressString; +use std::cell::RefCell; pub struct ChainSubmitTx<'a> { pub(crate) chain: RefCell<&'a mut ChainSim>, @@ -32,7 +32,13 @@ pub struct MockNodeApi<'a> { } impl NodeApiTrait for MockNodeApi<'_> { - fn get_unspent_boxes_by_address_with_token_filter_option(&self, _address: &P2PKAddressString, _target_balance: BoxValue, _target_tokens: Vec, _filter_boxes_token_ids: Vec) -> Result, BoxSelectorError> { + fn get_unspent_boxes_by_address_with_token_filter_option( + &self, + _address: &P2PKAddressString, + _target_balance: BoxValue, + _target_tokens: Vec, + _filter_boxes_token_ids: Vec, + ) -> Result, BoxSelectorError> { Ok(self.unspent_boxes.clone()) } @@ -61,15 +67,22 @@ impl NodeApiTrait for MockNodeApi<'_> { Ok(wallet) } - fn sign_transaction(&self, _transaction_context: TransactionContext) -> Result { - self.get_wallet()?.sign_transaction(_transaction_context, &self.ctx.clone(), None) + fn sign_transaction( + &self, + _transaction_context: TransactionContext, + ) -> Result { + self.get_wallet()? + .sign_transaction(_transaction_context, &self.ctx.clone(), None) .map_err(|e| NodeApiError::NodeInterfaceError(NodeError::Other(e.to_string()))) } fn submit_transaction(&self, _tx: &Transaction) -> Result { self.submitted_txs.borrow_mut().push(_tx.clone()); if let Some(ref chain_submit_tx) = &self.chain_submit_tx { - chain_submit_tx.chain.borrow_mut().add_block(Block::new(vec![_tx.clone()])); + chain_submit_tx + .chain + .borrow_mut() + .add_block(Block::new(vec![_tx.clone()])); } Ok(_tx.id()) } diff --git a/core/src/oracle_config.rs b/core/src/oracle_config.rs index 364c6d5a..233dd2c2 100644 --- a/core/src/oracle_config.rs +++ b/core/src/oracle_config.rs @@ -5,6 +5,9 @@ use std::{ }; use anyhow::Context; +use ergo_lib::wallet::ext_secret_key::ExtSecretKey; +use ergo_lib::wallet::mnemonic::Mnemonic; +use ergo_lib::wallet::secret_key::SecretKey; use ergo_lib::{ ergotree_ir::chain::address::NetworkAddress, ergotree_ir::{ @@ -16,10 +19,7 @@ use ergo_lib::{ }, wallet::tx_builder::{self, SUGGESTED_TX_FEE}, }; -use ergo_lib::wallet::ext_secret_key::ExtSecretKey; -use ergo_lib::wallet::mnemonic::Mnemonic; -use ergo_lib::wallet::secret_key::SecretKey; -use log::{LevelFilter}; +use log::LevelFilter; use once_cell::sync; use reqwest::Url; use serde::{Deserialize, Serialize}; @@ -54,13 +54,11 @@ impl OracleSecrets { let seed = Mnemonic::to_seed(&mnemonic, ""); let ext_sk = ExtSecretKey::derive_master(seed).unwrap(); - // bip-32 path for the first key + // bip-32 path for the first key let path = "m/44'/429'/0'/0/0"; let secret = ext_sk.derive(path.parse().unwrap()).unwrap().secret_key(); - Self { - secret_key: secret, - } + Self { secret_key: secret } } } diff --git a/core/src/oracle_state.rs b/core/src/oracle_state.rs index 83ff4ab0..2012f512 100644 --- a/core/src/oracle_state.rs +++ b/core/src/oracle_state.rs @@ -6,10 +6,10 @@ use crate::box_kind::{ UpdateBoxWrapper, UpdateBoxWrapperInputs, VoteBallotBoxWrapper, }; use crate::datapoint_source::DataPointSourceError; +use crate::get_boxes::{GenericTokenFetch, GetBoxes, GetBoxesError, TokenFetchRegistry}; use crate::oracle_config::ORACLE_CONFIG; use crate::oracle_types::{BlockHeight, EpochCounter, Rate}; use crate::pool_config::POOL_CONFIG; -use crate::get_boxes::{GenericTokenFetch, GetBoxes, TokenFetchRegistry, GetBoxesError}; use crate::spec_token::{ BallotTokenId, BuybackTokenId, OracleTokenId, PoolTokenId, RefreshTokenId, RewardTokenId, TokenIdKind, UpdateTokenId, @@ -172,7 +172,9 @@ pub enum LocalDatapointState { } impl OraclePool { - pub fn new(token_fetch_registry: &TokenFetchRegistry) -> std::result::Result { + pub fn new( + token_fetch_registry: &TokenFetchRegistry, + ) -> std::result::Result { let pool_config = &POOL_CONFIG; let oracle_config = &ORACLE_CONFIG; let oracle_pk = oracle_config.oracle_address_p2pk()?; diff --git a/core/src/pool_commands.rs b/core/src/pool_commands.rs index 767f4552..3c4f95a7 100644 --- a/core/src/pool_commands.rs +++ b/core/src/pool_commands.rs @@ -2,21 +2,21 @@ use ergo_lib::ergo_chain_types::DigestNError; use ergo_lib::ergotree_ir::chain::address::{Address, AddressEncoderError}; use thiserror::Error; +use self::publish_datapoint::build_publish_first_datapoint_action; +use self::publish_datapoint::{ + build_subsequent_publish_datapoint_action, PublishDatapointActionError, +}; +use self::refresh::build_refresh_action; +use self::refresh::RefreshActionError; use crate::action_report::PoolActionReport; use crate::actions::PoolAction; use crate::box_kind::PoolBox; use crate::datapoint_source::RuntimeDataPointSource; +use crate::node_interface::node_api::NodeApi; use crate::oracle_config::ORACLE_CONFIG; use crate::oracle_state::{DataSourceError, OraclePool}; use crate::oracle_types::BlockHeight; use crate::pool_config::POOL_CONFIG; -use crate::node_interface::node_api::NodeApi; -use self::publish_datapoint::build_publish_first_datapoint_action; -use self::publish_datapoint::{ - build_subsequent_publish_datapoint_action, PublishDatapointActionError, -}; -use self::refresh::build_refresh_action; -use self::refresh::RefreshActionError; pub mod publish_datapoint; pub mod refresh; diff --git a/core/src/pool_commands/publish_datapoint.rs b/core/src/pool_commands/publish_datapoint.rs index 65a828d2..e4ccf858 100644 --- a/core/src/pool_commands/publish_datapoint.rs +++ b/core/src/pool_commands/publish_datapoint.rs @@ -1,5 +1,8 @@ use std::convert::TryFrom; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use ergo_lib::{ chain::ergo_box::box_builder::ErgoBoxCandidateBuilderError, ergotree_interpreter::sigma_protocol::prover::ContextExtension, @@ -9,11 +12,10 @@ use ergo_lib::{ tx_builder::{TxBuilder, TxBuilderError}, }, }; -use ergo_lib::ergotree_ir::chain::address::NetworkAddress; -use ergo_lib::wallet::box_selector::{BoxSelector, SimpleBoxSelector}; -use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use thiserror::Error; +use crate::address_util::address_to_p2pk; +use crate::node_interface::node_api::NodeApiTrait; use crate::{ action_report::PublishDatapointActionReport, actions::PublishDataPointAction, @@ -25,8 +27,6 @@ use crate::{ oracle_types::{BlockHeight, EpochCounter}, spec_token::{OracleTokenId, RewardTokenId, SpecToken}, }; -use crate::address_util::address_to_p2pk; -use crate::node_interface::node_api::NodeApiTrait; #[derive(Debug, Error)] pub enum PublishDatapointActionError { @@ -82,7 +82,8 @@ pub fn build_subsequent_publish_datapoint_action( )?; let box_selector = SimpleBoxSelector::new(); let tx_fee = *BASE_FEE; - let mut unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), tx_fee, vec![])?; + let mut unspent_boxes = + node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), tx_fee, vec![])?; let target_tokens = vec![ in_oracle_box.oracle_token().into(), outbox_reward_tokens.into(), @@ -112,7 +113,12 @@ pub fn build_subsequent_publish_datapoint_action( Ok(ctx) => ctx, Err(e) => return Err(PublishDatapointActionError::TxSigningError(e)), }; - Ok((PublishDataPointAction { transaction_context: context }, report)) + Ok(( + PublishDataPointAction { + transaction_context: context, + }, + report, + )) } #[allow(clippy::too_many_arguments)] @@ -139,11 +145,13 @@ pub fn build_publish_first_datapoint_action( let contract = OracleContract::checked_load(&inputs.contract_inputs)?; let min_storage_rent = contract.parameters().min_storage_rent; let target_balance = min_storage_rent.checked_add(&tx_fee).unwrap(); - let target_tokens = vec![ - oracle_token.clone().into(), reward_token.clone().into() - ]; + let target_tokens = vec![oracle_token.clone().into(), reward_token.clone().into()]; - let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), target_balance, target_tokens.clone())?; + let unspent_boxes = node_api.get_unspent_boxes_by_address( + &oracle_address.to_base58(), + target_balance, + target_tokens.clone(), + )?; let box_selection = box_selector.select( unspent_boxes.clone(), target_balance, @@ -184,7 +192,12 @@ pub fn build_publish_first_datapoint_action( Ok(ctx) => ctx, Err(e) => return Err(PublishDatapointActionError::TxSigningError(e)), }; - Ok((PublishDataPointAction { transaction_context: context }, report)) + Ok(( + PublishDataPointAction { + transaction_context: context, + }, + report, + )) } #[cfg(test)] @@ -193,8 +206,11 @@ mod tests { use super::*; use crate::contracts::oracle::OracleContractParameters; + use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; use crate::oracle_types::{EpochLength, Rate}; - use crate::pool_commands::test_utils::{generate_token_ids, make_datapoint_box, make_wallet_unspent_box}; + use crate::pool_commands::test_utils::{ + generate_token_ids, make_datapoint_box, make_wallet_unspent_box, + }; use crate::spec_token::TokenIdKind; use ergo_lib::chain::ergo_state_context::ErgoStateContext; use ergo_lib::chain::transaction::TxId; @@ -206,7 +222,6 @@ mod tests { use ergo_lib::ergotree_ir::mir::constant::Constant; use ergo_lib::ergotree_ir::mir::expr::Expr; use sigma_test_util::force_any_val; - use crate::node_interface::test_utils::{MockNodeApi, SubmitTxMock}; #[derive(Debug)] struct MockDatapointSource { @@ -227,7 +242,10 @@ mod tests { let oracle_contract_parameters = OracleContractParameters::default(); let pool_box_epoch_id = EpochCounter(1); let secret = force_any_val::(); - let oracle_address = NetworkAddress::new(NetworkPrefix::Mainnet, &Address::P2Pk(secret.public_image().clone())); + let oracle_address = NetworkAddress::new( + NetworkPrefix::Mainnet, + &Address::P2Pk(secret.public_image().clone()), + ); let oracle_pub_key = secret.public_image().h; let oracle_box_wrapper_inputs = OracleBoxWrapperInputs::try_from((oracle_contract_parameters, &token_ids)).unwrap(); @@ -263,7 +281,7 @@ mod tests { ctx: ctx.clone(), secrets: vec![secret.clone().into()], submitted_txs: &SubmitTxMock::default().transactions, - chain_submit_tx: None + chain_submit_tx: None, }; let datapoint_source = MockDatapointSource { @@ -281,7 +299,9 @@ mod tests { ) .unwrap(); - let _signed_tx = mock_node_api.sign_transaction(action.transaction_context).unwrap(); + let _signed_tx = mock_node_api + .sign_transaction(action.transaction_context) + .unwrap(); } #[test] @@ -303,7 +323,10 @@ mod tests { .unwrap(); let secret = force_any_val::(); - let oracle_address = NetworkAddress::new(NetworkPrefix::Mainnet, &Address::P2Pk(secret.public_image().clone())); + let oracle_address = NetworkAddress::new( + NetworkPrefix::Mainnet, + &Address::P2Pk(secret.public_image().clone()), + ); let c: Constant = secret.public_image().into(); let expr: Expr = c.into(); let ergo_tree = ErgoTree::try_from(expr).unwrap(); @@ -347,7 +370,7 @@ mod tests { ctx: ctx.clone(), secrets: vec![secret.clone().into()], submitted_txs: &SubmitTxMock::default().transactions, - chain_submit_tx: None + chain_submit_tx: None, }; let (action, _) = build_publish_first_datapoint_action( @@ -363,11 +386,18 @@ mod tests { .unwrap(); assert_eq!( - action.transaction_context.spending_tx.output_candidates.first().value, + action + .transaction_context + .spending_tx + .output_candidates + .first() + .value, oracle_contract_parameters.min_storage_rent ); - let _signed_tx = mock_node_api.sign_transaction(action.transaction_context).unwrap(); + let _signed_tx = mock_node_api + .sign_transaction(action.transaction_context) + .unwrap(); } #[test] @@ -382,7 +412,10 @@ mod tests { dbg!(&token_ids); dbg!(&minted_reward_token_id); let secret = force_any_val::(); - let oracle_address = NetworkAddress::new(NetworkPrefix::Mainnet, &Address::P2Pk(secret.public_image().clone())); + let oracle_address = NetworkAddress::new( + NetworkPrefix::Mainnet, + &Address::P2Pk(secret.public_image().clone()), + ); let oracle_pub_key = secret.public_image().h; let oracle_box_wrapper_inputs = @@ -426,7 +459,7 @@ mod tests { ctx: ctx.clone(), secrets: vec![secret.clone().into()], submitted_txs: &SubmitTxMock::default().transactions, - chain_submit_tx: None + chain_submit_tx: None, }; let datapoint_source = MockDatapointSource { @@ -444,6 +477,8 @@ mod tests { ) .unwrap(); - let _signed_tx = mock_node_api.sign_transaction(action.transaction_context).unwrap(); + let _signed_tx = mock_node_api + .sign_transaction(action.transaction_context) + .unwrap(); } } diff --git a/core/src/pool_commands/refresh.rs b/core/src/pool_commands/refresh.rs index 3caca3e4..23b4b6ce 100644 --- a/core/src/pool_commands/refresh.rs +++ b/core/src/pool_commands/refresh.rs @@ -36,9 +36,9 @@ use ergo_lib::wallet::tx_builder::TxBuilder; use ergo_lib::wallet::tx_builder::TxBuilderError; use thiserror::Error; -use std::convert::TryInto; -use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; use crate::address_util::address_to_p2pk; +use ergo_lib::wallet::signing::{TransactionContext, TxSigningError}; +use std::convert::TryInto; #[derive(Debug, Error)] pub enum RefreshActionError { @@ -122,7 +122,8 @@ pub fn build_refresh_action( .transpose()? .flatten(); - let unspent_boxes = node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), tx_fee, vec![])?; + let unspent_boxes = + node_api.get_unspent_boxes_by_address(&oracle_address.to_base58(), tx_fee, vec![])?; let box_selector = SimpleBoxSelector::new(); let selection = box_selector.select(unspent_boxes, tx_fee, &[])?; @@ -220,7 +221,12 @@ pub fn build_refresh_action( Ok(ctx) => ctx, Err(e) => return Err(RefreshActionError::TxSigningError(e)), }; - Ok((RefreshAction { transaction_context: context }, report)) + Ok(( + RefreshAction { + transaction_context: context, + }, + report, + )) } fn filtered_oracle_boxes_by_rate( @@ -404,11 +410,11 @@ mod tests { use crate::oracle_config::BASE_FEE; use crate::oracle_state::DataSourceError; use crate::oracle_types::EpochLength; + use crate::pool_commands::test_utils::generate_token_ids; use crate::pool_commands::test_utils::BuybackBoxSourceMock; use crate::pool_commands::test_utils::{ make_datapoint_box, make_pool_box, make_wallet_unspent_box, PoolBoxMock, }; - use crate::pool_commands::test_utils::generate_token_ids; use crate::pool_config::TokenIds; use crate::spec_token::TokenIdKind; @@ -447,8 +453,8 @@ mod tests { inputs.refresh_nft_token_id.token_id(), 1u64.try_into().unwrap(), ))] - .try_into() - .unwrap(); + .try_into() + .unwrap(); RefreshBoxWrapper::new( ErgoBox::new( value, @@ -461,10 +467,10 @@ mod tests { force_any_val::(), 0, ) - .unwrap(), + .unwrap(), inputs, ) - .unwrap() + .unwrap() } #[allow(clippy::too_many_arguments)] @@ -533,7 +539,10 @@ mod tests { &token_ids, ); let secret = force_any_val::(); - let oracle_address = NetworkAddress::new(NetworkPrefix::Mainnet, &Address::P2Pk(secret.public_image().clone())); + let oracle_address = NetworkAddress::new( + NetworkPrefix::Mainnet, + &Address::P2Pk(secret.public_image().clone()), + ); let oracle_pub_key = secret.public_image().h; let oracle_pub_keys = vec![ @@ -576,7 +585,7 @@ mod tests { ctx: ctx.clone(), secrets: vec![secret.clone().into()], submitted_txs: &SubmitTxMock::default().transactions, - chain_submit_tx: None + chain_submit_tx: None, }; let (action, report) = build_refresh_action( @@ -597,7 +606,9 @@ mod tests { assert_eq!(report.oracle_boxes_collected.len(), 5); - let _signed_tx = mock_node_api.sign_transaction(action.transaction_context).unwrap(); + let _signed_tx = mock_node_api + .sign_transaction(action.transaction_context) + .unwrap(); let wrong_epoch_id_datapoints_mock = DatapointSourceMock { datapoints: make_datapoint_boxes( diff --git a/core/src/tests/bootstrap_and_run.rs b/core/src/tests/bootstrap_and_run.rs index 4e087fbf..04d6e9ac 100644 --- a/core/src/tests/bootstrap_and_run.rs +++ b/core/src/tests/bootstrap_and_run.rs @@ -1,13 +1,5 @@ use std::convert::TryInto; -use ergo_chain_sim::ChainSim; -use ergo_lib::chain::ergo_state_context::ErgoStateContext; -use ergo_lib::ergotree_interpreter::sigma_protocol::private_input::DlogProverInput; -use ergo_lib::ergotree_ir::chain::address::Address; -use ergo_lib::ergotree_ir::chain::address::NetworkAddress; -use ergo_lib::ergotree_ir::chain::address::NetworkPrefix; -use ergo_lib::wallet::secret_key::SecretKey; -use sigma_test_util::force_any_val; use crate::cli_commands::bootstrap::perform_bootstrap_chained_transaction; use crate::cli_commands::bootstrap::BootstrapConfig; use crate::cli_commands::bootstrap::BootstrapInput; @@ -16,8 +8,20 @@ use crate::oracle_config::BASE_FEE; use crate::oracle_types::BlockHeight; use crate::pool_commands::test_utils::init_log_tests; use crate::pool_config::PoolConfig; +use ergo_chain_sim::ChainSim; +use ergo_lib::chain::ergo_state_context::ErgoStateContext; +use ergo_lib::ergotree_interpreter::sigma_protocol::private_input::DlogProverInput; +use ergo_lib::ergotree_ir::chain::address::Address; +use ergo_lib::ergotree_ir::chain::address::NetworkAddress; +use ergo_lib::ergotree_ir::chain::address::NetworkPrefix; +use ergo_lib::wallet::secret_key::SecretKey; +use sigma_test_util::force_any_val; -fn bootstrap(secrets: Vec, net_address: &NetworkAddress, chain: &mut ChainSim) -> PoolConfig { +fn bootstrap( + secrets: Vec, + net_address: &NetworkAddress, + chain: &mut ChainSim, +) -> PoolConfig { let ctx = force_any_val::(); let unspent_boxes = chain.get_unspent_boxes(&net_address.address().script().unwrap()); @@ -35,7 +39,7 @@ fn bootstrap(secrets: Vec, net_address: &NetworkAddress, chain: &mut unspent_boxes: unspent_boxes.clone(), ctx: ctx.clone(), secrets, - chain_submit_tx: Some(& mut submit_tx_mock), + chain_submit_tx: Some(&mut submit_tx_mock), submitted_txs: &SubmitTxMock::default().transactions, }, tx_fee: *BASE_FEE, From 5a442a79ddf4fe4c7768546e1a8209c18edcba54 Mon Sep 17 00:00:00 2001 From: Moein zargarzadeh Date: Fri, 7 Mar 2025 22:27:42 +0330 Subject: [PATCH 4/6] update rustc to 1.81.0 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 80627411..e3a84f22 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.74.1 +1.81.0 \ No newline at end of file From 675be3ba1d52572e0b3bf6bed0cc02a16b671bbe Mon Sep 17 00:00:00 2001 From: Moein zargarzadeh Date: Mon, 24 Mar 2025 16:14:22 +0330 Subject: [PATCH 5/6] fix typo --- core/src/get_boxes/generic_token_fetch.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/get_boxes/generic_token_fetch.rs b/core/src/get_boxes/generic_token_fetch.rs index 1109fc71..0ddff826 100644 --- a/core/src/get_boxes/generic_token_fetch.rs +++ b/core/src/get_boxes/generic_token_fetch.rs @@ -46,8 +46,8 @@ impl TryFrom for GenericTokenFetch { } impl From> for String { - fn from(token_fech: GenericTokenFetch) -> Self { - token_fech.token_id.into() + fn from(token_fetch: GenericTokenFetch) -> Self { + token_fetch.token_id.into() } } From 6d64cd4c2d1d34c92f8df34e3db4b79749ac2c3b Mon Sep 17 00:00:00 2001 From: Moein zargarzadeh Date: Wed, 2 Apr 2025 23:10:14 +0330 Subject: [PATCH 6/6] apply clippy suggestions --- core/src/pool_commands/publish_datapoint.rs | 1 + core/src/pool_commands/refresh.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/pool_commands/publish_datapoint.rs b/core/src/pool_commands/publish_datapoint.rs index e4ccf858..4d29bd09 100644 --- a/core/src/pool_commands/publish_datapoint.rs +++ b/core/src/pool_commands/publish_datapoint.rs @@ -48,6 +48,7 @@ pub enum PublishDatapointActionError { OracleContract(#[from] OracleContractError), } +#[allow(clippy::too_many_arguments)] pub fn build_subsequent_publish_datapoint_action( local_datapoint_box: &OracleBoxWrapper, node_api: &dyn NodeApiTrait, diff --git a/core/src/pool_commands/refresh.rs b/core/src/pool_commands/refresh.rs index 23b4b6ce..47c89701 100644 --- a/core/src/pool_commands/refresh.rs +++ b/core/src/pool_commands/refresh.rs @@ -133,7 +133,7 @@ pub fn build_refresh_action( ]; let my_input_oracle_box_index: i32 = valid_in_oracle_boxes .iter() - .position(|b| &b.public_key() == &my_oracle_pk) + .position(|b| b.public_key() == my_oracle_pk) .ok_or(RefreshActionError::MyOracleBoxNoFound)? as i32;