From ae30e7c6061c253074fa89bd09a265dbf919b21d Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Tue, 14 Feb 2023 15:56:15 +0200 Subject: [PATCH 01/15] make vote-update reward token parameter optional; use SpecToken for reward tokens in vote-update and update-pool commands; --- core/src/box_kind/ballot_box.rs | 61 ++++++++++++------- core/src/box_kind/pool_box.rs | 5 +- core/src/cli_commands/update_pool.rs | 44 +++++++------- core/src/cli_commands/vote_update_pool.rs | 74 ++++++++++------------- core/src/main.rs | 47 +++++++++----- core/src/pool_config.rs | 10 --- 6 files changed, 129 insertions(+), 112 deletions(-) diff --git a/core/src/box_kind/ballot_box.rs b/core/src/box_kind/ballot_box.rs index 217c7a75..54a5b8b3 100644 --- a/core/src/box_kind/ballot_box.rs +++ b/core/src/box_kind/ballot_box.rs @@ -3,8 +3,7 @@ use crate::{ BallotContract, BallotContractError, BallotContractInputs, BallotContractParameters, }, oracle_types::BlockHeight, - pool_config::CastBallotBoxVoteParameters, - spec_token::{BallotTokenId, SpecToken, TokenIdKind, UpdateTokenId}, + spec_token::{BallotTokenId, RewardTokenId, SpecToken, TokenIdKind, UpdateTokenId}, }; use ergo_lib::{ chain::ergo_box::box_builder::{ErgoBoxCandidateBuilder, ErgoBoxCandidateBuilderError}, @@ -13,7 +12,7 @@ use ergo_lib::{ chain::{ address::{Address, AddressEncoderError}, ergo_box::{box_value::BoxValue, ErgoBox, ErgoBoxCandidate, NonMandatoryRegisterId}, - token::{Token, TokenId}, + token::TokenId, }, mir::constant::{TryExtractFromError, TryExtractInto}, serialization::SigmaSerializationError, @@ -28,10 +27,6 @@ pub enum BallotBoxError { NoBallotToken, #[error("ballot box: unknown ballot token id in `TOKENS(0)`")] UnknownBallotTokenId, - #[error("ballot box: no reward token id in R7 register")] - NoRewardTokenIdInR7, - #[error("ballot box: no reward token quantity in R8 register")] - NoRewardTokenQuantityInR8, #[error("ballot box: no group element in R4 register")] NoGroupElementInR4, #[error("ballot box: unexpected group element in R4 register")] @@ -48,6 +43,8 @@ pub enum BallotBoxError { TryExtractFrom(#[from] TryExtractFromError), #[error("ballot box: SigmaSerializationError {0:?}")] SigmaSerialization(#[from] SigmaSerializationError), + #[error("invalid reward token: {0:?}")] + InvalidRewardToken(String), } pub trait BallotBox { @@ -130,6 +127,13 @@ impl BallotBoxWrapperInputs { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CastBallotBoxVoteParameters { + pub pool_box_address_hash: Digest32, + pub reward_token_opt: Option>, + pub update_box_creation_height: i32, +} + /// A Ballot Box with vote parameters guaranteed to be set #[derive(Clone, Debug)] pub struct VoteBallotBoxWrapper { @@ -169,21 +173,31 @@ impl VoteBallotBoxWrapper { .ok_or(BallotBoxError::NoPoolBoxAddressInR6)? .try_extract_into::()?; - let reward_token_id = ergo_box + let reward_token_id_opt = ergo_box .get_register(NonMandatoryRegisterId::R7.into()) - .ok_or(BallotBoxError::NoRewardTokenIdInR7)? - .try_extract_into::()?; - let reward_token_quantity = ergo_box + .map(|c| c.try_extract_into::()); + let reward_token_quantity_opt = ergo_box .get_register(NonMandatoryRegisterId::R8.into()) - .ok_or(BallotBoxError::NoRewardTokenQuantityInR8)? - .try_extract_into::()? as u64; + .map(|c| c.try_extract_into::()); + let reward_token_opt = match (reward_token_id_opt, reward_token_quantity_opt) { + (Some(Ok(reward_token_id)), Some(Ok(reward_token_quantity))) => Some(SpecToken { + token_id: RewardTokenId::from_token_id_unchecked(reward_token_id), + amount: (reward_token_quantity as u64).try_into().unwrap(), + }), + (None, None) => None, + (id, amt) => { + return Err(BallotBoxError::InvalidRewardToken(format!( + "Reward token id {:?} amount {:?}", + id, amt + ))) + } + }; let contract = BallotContract::from_ergo_tree(ergo_box.ergo_tree.clone(), &inputs.contract_inputs)?; let vote_parameters = CastBallotBoxVoteParameters { pool_box_address_hash, - reward_token_id, - reward_token_quantity, + reward_token_opt, update_box_creation_height, }; Ok(Self { @@ -269,7 +283,7 @@ pub fn make_local_ballot_box_candidate( update_box_creation_height: BlockHeight, ballot_token: SpecToken, pool_box_address_hash: Digest32, - reward_tokens: Token, + reward_token_opt: Option>, value: BoxValue, creation_height: BlockHeight, ) -> Result { @@ -283,11 +297,16 @@ pub fn make_local_ballot_box_candidate( (update_box_creation_height.0 as i32).into(), ); builder.set_register_value(NonMandatoryRegisterId::R6, pool_box_address_hash.into()); - builder.set_register_value(NonMandatoryRegisterId::R7, reward_tokens.token_id.into()); - builder.set_register_value( - NonMandatoryRegisterId::R8, - (*reward_tokens.amount.as_u64() as i64).into(), - ); + if let Some(reward_tokens) = reward_token_opt { + builder.set_register_value( + NonMandatoryRegisterId::R7, + reward_tokens.token_id.token_id().into(), + ); + builder.set_register_value( + NonMandatoryRegisterId::R8, + (*reward_tokens.amount.as_u64() as i64).into(), + ); + } builder.add_token(ballot_token.into()); builder.build() } diff --git a/core/src/box_kind/pool_box.rs b/core/src/box_kind/pool_box.rs index a828d838..155a609a 100644 --- a/core/src/box_kind/pool_box.rs +++ b/core/src/box_kind/pool_box.rs @@ -4,7 +4,6 @@ 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::ErgoBoxCandidate; use ergo_lib::ergotree_ir::chain::ergo_box::NonMandatoryRegisterId; -use ergo_lib::ergotree_ir::chain::token::Token; use ergo_lib::ergotree_ir::mir::constant::TryExtractInto; use thiserror::Error; @@ -229,7 +228,7 @@ pub fn make_pool_box_candidate_unchecked( datapoint: i64, epoch_counter: EpochCounter, pool_nft_token: SpecToken, - reward_token: Token, + reward_token: SpecToken, value: BoxValue, creation_height: BlockHeight, ) -> Result { @@ -237,6 +236,6 @@ pub fn make_pool_box_candidate_unchecked( builder.set_register_value(NonMandatoryRegisterId::R4, datapoint.into()); builder.set_register_value(NonMandatoryRegisterId::R5, (epoch_counter.0 as i32).into()); builder.add_token(pool_nft_token.into()); - builder.add_token(reward_token); + builder.add_token(reward_token.into()); builder.build() } diff --git a/core/src/cli_commands/update_pool.rs b/core/src/cli_commands/update_pool.rs index b1ef83c0..97c63d4a 100644 --- a/core/src/cli_commands/update_pool.rs +++ b/core/src/cli_commands/update_pool.rs @@ -9,7 +9,6 @@ use ergo_lib::{ ergotree_ir::chain::{ address::Address, ergo_box::{ErgoBox, NonMandatoryRegisterId}, - token::Token, }, ergotree_ir::serialization::SigmaSerializable, wallet::{ @@ -24,7 +23,8 @@ use std::convert::TryInto; use crate::{ box_kind::{ - make_pool_box_candidate_unchecked, BallotBox, PoolBox, PoolBoxWrapper, VoteBallotBoxWrapper, + make_pool_box_candidate_unchecked, BallotBox, CastBallotBoxVoteParameters, PoolBox, + PoolBoxWrapper, VoteBallotBoxWrapper, }, cli_commands::ergo_explorer_transaction_link, contracts::pool::PoolContract, @@ -32,8 +32,8 @@ use crate::{ oracle_config::BASE_FEE, oracle_state::{OraclePool, PoolBoxSource, StageError, UpdateBoxSource, VoteBallotBoxesSource}, oracle_types::BlockHeight, - pool_config::{CastBallotBoxVoteParameters, PoolConfig, POOL_CONFIG}, - spec_token::TokenIdKind, + pool_config::{PoolConfig, POOL_CONFIG}, + spec_token::{RewardTokenId, SpecToken, TokenIdKind}, wallet::{WalletDataError, WalletDataSource}, }; use derive_more::From; @@ -77,7 +77,7 @@ pub fn update_pool( tx_signer: &dyn SignTransaction, tx_submit: &dyn SubmitTransaction, new_pool_box_hash_str: Option, - new_reward_tokens: Option, + new_reward_tokens: Option>, height: BlockHeight, ) -> Result<(), UpdatePoolError> { info!("Opening pool_config_updated.yaml"); @@ -137,9 +137,9 @@ fn display_update_diff( old_pool_config: &PoolConfig, new_pool_config: &PoolConfig, old_pool_box: PoolBoxWrapper, - new_reward_tokens: Option, + new_reward_tokens: Option>, ) { - let new_tokens = new_reward_tokens.unwrap_or_else(|| old_pool_box.reward_token().into()); + let new_tokens = new_reward_tokens.unwrap_or_else(|| old_pool_box.reward_token()); let new_pool_contract = PoolContract::checked_load(&new_pool_config.pool_box_wrapper_inputs.contract_inputs) .unwrap(); @@ -189,7 +189,7 @@ fn build_update_pool_box_tx( wallet: &dyn WalletDataSource, update_box: &dyn UpdateBoxSource, new_pool_contract: PoolContract, - new_reward_tokens: Option, + new_reward_tokens: Option>, height: BlockHeight, change_address: Address, ) -> Result, UpdatePoolError> { @@ -202,13 +202,12 @@ fn build_update_pool_box_tx( .sigma_serialize_bytes() .unwrap(), ); - let reward_tokens = new_reward_tokens.unwrap_or_else(|| old_pool_box.reward_token().into()); let vote_parameters = CastBallotBoxVoteParameters { pool_box_address_hash: pool_box_hash, - reward_token_id: reward_tokens.token_id, - reward_token_quantity: *reward_tokens.amount.as_u64(), + reward_token_opt: new_reward_tokens.clone(), update_box_creation_height: update_box.get_box().creation_height as i32, }; + let reward_tokens = new_reward_tokens.unwrap_or_else(|| old_pool_box.reward_token()); // Find ballot boxes that are voting for the new pool hash let mut sorted_ballot_boxes = ballot_boxes.get_ballot_boxes()?; // Sort in descending order of ballot token amounts. If two boxes have the same amount of ballot tokens, also compare box value, in case some boxes were incorrectly created below minStorageRent @@ -278,11 +277,12 @@ fn build_update_pool_box_tx( } let target_balance = *BASE_FEE; - let target_tokens = if reward_tokens.token_id != old_pool_box.reward_token().token_id() { - vec![reward_tokens.clone()] - } else { - vec![] - }; + let target_tokens = + if reward_tokens.token_id.token_id() != old_pool_box.reward_token().token_id() { + vec![reward_tokens.clone().into()] + } else { + vec![] + }; 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()]; @@ -321,7 +321,7 @@ fn build_update_pool_box_tx( change_address, ); - if reward_tokens.token_id != old_pool_box.reward_token().token_id() { + if reward_tokens.token_id.token_id() != old_pool_box.reward_token().token_id() { tx_builder.set_token_burn_permit(vec![old_pool_box.reward_token().into()]); } @@ -379,7 +379,7 @@ mod tests { generate_token_ids, make_wallet_unspent_box, BallotBoxesMock, PoolBoxMock, UpdateBoxMock, WalletDataMock, }, - spec_token::{RefreshTokenId, SpecToken, TokenIdKind}, + spec_token::{RefreshTokenId, RewardTokenId, SpecToken, TokenIdKind}, }; use super::build_update_pool_box_tx; @@ -405,8 +405,8 @@ mod tests { token_id: token_ids.reward_token_id.clone(), amount: 1500.try_into().unwrap(), }; - let new_reward_tokens = Token { - token_id: force_any_tokenid(), + let new_reward_tokens = SpecToken { + token_id: RewardTokenId::from_token_id_unchecked(force_any_tokenid()), amount: force_any_val(), }; dbg!(&new_reward_tokens); @@ -498,7 +498,7 @@ mod tests { amount: 1.try_into().unwrap(), }, pool_box_hash, - new_reward_tokens.clone(), + Some(new_reward_tokens.clone()), ballot_contract.min_storage_rent(), height, ) @@ -524,7 +524,7 @@ mod tests { // create a wallet box with new reward tokens secret.public_image(), BASE_FEE.checked_mul_u32(4_000_000_000).unwrap(), - Some(vec![new_reward_tokens.clone()].try_into().unwrap()), + Some(vec![new_reward_tokens.clone().into()].try_into().unwrap()), ); let change_address = AddressEncoder::unchecked_parse_network_address_from_str( "9iHyKxXs2ZNLMp9N9gbUT9V8gTbsV7HED1C1VhttMfBUMPDyF7r", diff --git a/core/src/cli_commands/vote_update_pool.rs b/core/src/cli_commands/vote_update_pool.rs index f7132b32..67a6ea88 100644 --- a/core/src/cli_commands/vote_update_pool.rs +++ b/core/src/cli_commands/vote_update_pool.rs @@ -7,10 +7,7 @@ use ergo_lib::{ }, ergo_chain_types::{Digest32, DigestNError}, ergotree_interpreter::sigma_protocol::prover::ContextExtension, - ergotree_ir::chain::{ - address::Address, - token::{Token, TokenAmount, TokenId}, - }, + ergotree_ir::chain::address::Address, wallet::{ box_selector::{BoxSelection, BoxSelector, BoxSelectorError, SimpleBoxSelector}, tx_builder::{TxBuilder, TxBuilderError}, @@ -29,7 +26,7 @@ use crate::{ oracle_state::{LocalBallotBoxSource, StageError}, oracle_types::BlockHeight, pool_config::{TokenIds, POOL_CONFIG}, - spec_token::SpecToken, + spec_token::{RewardTokenId, SpecToken, TokenIdKind}, wallet::{WalletDataError, WalletDataSource}, }; use derive_more::From; @@ -68,15 +65,13 @@ pub fn vote_update_pool( tx_submit: &dyn SubmitTransaction, local_ballot_box_source: &dyn LocalBallotBoxSource, new_pool_box_address_hash_str: String, - reward_token_id_str: String, - reward_token_amount: u32, + reward_token_opt: Option>, update_box_creation_height: BlockHeight, height: BlockHeight, ) -> Result<(), VoteUpdatePoolError> { let change_network_address = wallet.get_change_address()?; let network_prefix = change_network_address.network(); let new_pool_box_address_hash = Digest32::try_from(new_pool_box_address_hash_str)?; - let reward_token_id: TokenId = Digest32::try_from(reward_token_id_str)?.into(); let unsigned_tx = if let Some(local_ballot_box) = local_ballot_box_source.get_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 @@ -84,8 +79,7 @@ pub fn vote_update_pool( local_ballot_box, wallet, new_pool_box_address_hash, - reward_token_id, - reward_token_amount, + reward_token_opt.clone(), update_box_creation_height, height, change_network_address.address(), @@ -95,8 +89,7 @@ pub fn vote_update_pool( build_tx_for_first_ballot_box( wallet, new_pool_box_address_hash, - reward_token_id, - reward_token_amount, + reward_token_opt.clone(), update_box_creation_height, ORACLE_CONFIG.oracle_address.address(), POOL_CONFIG @@ -110,15 +103,18 @@ pub fn vote_update_pool( }; println!( "YOU WILL BE CASTING A VOTE FOR THE FOLLOWING ITEMS:\ - - Hash of new pool box address: {}\ - - Reward token Id: {}\ - - Reward token amount: {}\n - TYPE 'YES' TO INITIATE THE TRANSACTION. - ", + - Hash of new pool box contract: {}", String::from(new_pool_box_address_hash), - String::from(reward_token_id), - reward_token_amount, ); + if let Some(reward_token) = reward_token_opt { + println!( + " - Reward token Id: {}\ + - Reward token amount: {}", + String::from(reward_token.token_id.token_id()), + reward_token.amount.as_u64(), + ); + } + println!("TYPE 'YES' TO INITIATE THE TRANSACTION."); let mut input = String::new(); std::io::stdin().read_line(&mut input)?; if input.trim_end() == "YES" { @@ -140,24 +136,19 @@ fn build_tx_with_existing_ballot_box( in_ballot_box: BallotBoxWrapper, wallet: &dyn WalletDataSource, new_pool_box_address_hash: Digest32, - reward_token_id: TokenId, - reward_token_amount: u32, + reward_token_opt: Option>, update_box_creation_height: BlockHeight, height: BlockHeight, change_address: Address, ) -> Result { let unspent_boxes = wallet.get_unspent_wallet_boxes()?; - let reward_token = Token { - token_id: reward_token_id, - amount: TokenAmount::try_from(reward_token_amount as u64).unwrap(), - }; let ballot_box_candidate = make_local_ballot_box_candidate( in_ballot_box.contract(), in_ballot_box.ballot_token_owner(), update_box_creation_height, in_ballot_box.ballot_token(), new_pool_box_address_hash, - reward_token, + reward_token_opt, in_ballot_box.get_box().value, height, )?; @@ -189,8 +180,7 @@ fn build_tx_with_existing_ballot_box( fn build_tx_for_first_ballot_box( wallet: &dyn WalletDataSource, new_pool_box_address_hash: Digest32, - reward_token_id: TokenId, - reward_token_amount: u32, + reward_token_opt: Option>, update_box_creation_height: BlockHeight, ballot_token_owner_address: Address, ballot_contract_parameters: &BallotContractParameters, @@ -200,10 +190,6 @@ fn build_tx_for_first_ballot_box( ) -> Result { let unspent_boxes = wallet.get_unspent_wallet_boxes()?; let out_ballot_box_value = ballot_contract_parameters.min_storage_rent(); - let reward_token = Token { - token_id: reward_token_id, - amount: TokenAmount::try_from(reward_token_amount as u64).unwrap(), - }; let inputs = BallotContractInputs::build_with( ballot_contract_parameters.clone(), token_ids.update_nft_token_id.clone(), @@ -220,7 +206,7 @@ fn build_tx_for_first_ballot_box( update_box_creation_height, ballot_token.clone(), new_pool_box_address_hash, - reward_token, + reward_token_opt, out_ballot_box_value, height, )?; @@ -279,7 +265,7 @@ mod tests { pool_commands::test_utils::{ find_input_boxes, generate_token_ids, make_wallet_unspent_box, WalletDataMock, }, - spec_token::{SpecToken, TokenIdKind}, + spec_token::{RewardTokenId, SpecToken, TokenIdKind}, wallet::WalletDataSource, }; @@ -319,12 +305,14 @@ mod tests { change_address: change_address.clone(), }; - let new_reward_token_id = force_any_val::(); + let new_reward_token = SpecToken { + token_id: RewardTokenId::from_token_id_unchecked(force_any_val::()), + amount: 100_000.try_into().unwrap(), + }; let unsigned_tx = build_tx_for_first_ballot_box( &wallet_mock, new_pool_box_address_hash, - new_reward_token_id, - 100_000, + Some(new_reward_token), BlockHeight(height.0) - 3, change_address.address(), ballot_contract_inputs.contract_parameters(), @@ -378,10 +366,10 @@ mod tests { height - EpochLength(2), ballot_token, new_pool_box_address_hash, - Token { - token_id: token_ids.reward_token_id.token_id(), + Some(SpecToken { + token_id: token_ids.reward_token_id.clone(), amount: 100_000.try_into().unwrap(), - }, + }), BoxValue::new(10_000_000).unwrap(), height - EpochLength(2), ) @@ -409,8 +397,10 @@ mod tests { ballot_box, &wallet_mock, new_pool_box_address_hash, - token_ids.reward_token_id.token_id(), - 100_000, + Some(SpecToken { + token_id: token_ids.reward_token_id, + amount: 100_000.try_into().unwrap(), + }), height - EpochLength(3), height, change_address.address(), diff --git a/core/src/main.rs b/core/src/main.rs index fcd5f31b..ca415c0a 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -51,7 +51,7 @@ use ergo_lib::ergo_chain_types::Digest32; 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::ergotree_ir::chain::token::Token; +use ergo_lib::ergotree_ir::chain::token::TokenAmount; use ergo_lib::ergotree_ir::chain::token::TokenId; use log::error; use log::LevelFilter; @@ -68,10 +68,12 @@ use pool_commands::PoolCommandError; use pool_config::DEFAULT_POOL_CONFIG_FILE_NAME; use pool_config::POOL_CONFIG; use scans::get_scans_file_path; +use spec_token::RewardTokenId; +use spec_token::SpecToken; +use spec_token::TokenIdKind; use state::process; use state::PoolState; use std::convert::TryFrom; -use std::convert::TryInto; use std::env; use std::path::Path; use std::path::PathBuf; @@ -162,9 +164,9 @@ enum Command { /// The base16-encoded blake2b hash of the serialized pool box contract for the new pool box. new_pool_box_address_hash_str: String, /// The base16-encoded reward token id of the new pool box (use existing if unchanged) - reward_token_id_str: String, + reward_token_id_str: Option, /// The reward token amount in the pool box at the time of update transaction is committed. - reward_token_amount: u32, + reward_token_amount: Option, /// The creation height of the existing update box. update_box_creation_height: u32, }, @@ -382,14 +384,14 @@ fn handle_pool_command( reward_token_amount, update_box_creation_height, } => { + let reward_token_opt = check_reward_token_opt(reward_token_id_str, reward_token_amount); 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_id_str, - reward_token_amount, + reward_token_opt, BlockHeight(update_box_creation_height), height, ) { @@ -402,20 +404,14 @@ fn handle_pool_command( reward_token_id, reward_token_amount, } => { - let new_reward_tokens = - reward_token_id - .zip(reward_token_amount) - .map(|(token_id, amount)| Token { - token_id: TokenId::from(Digest32::try_from(token_id).unwrap()), - amount: amount.try_into().unwrap(), - }); + 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, &node_api.node, &node_api.node, new_pool_box_hash, - new_reward_tokens, + reward_token_opt, height, ) { error!("Fatal update-pool error: {}", e); @@ -533,3 +529,26 @@ fn log_on_launch() { log::info!("Oracle address: {}", config.oracle_address.to_base58()); } } + +fn check_reward_token_opt( + reward_token_id_str: Option, + reward_token_amount: Option, +) -> Option> { + let reward_token_opt = match (reward_token_id_str, reward_token_amount) { + (None, None) => None, + (None, Some(_)) => { + panic!("reward_token_amount is set, but reward_token_id is not set") + } + (Some(_), None) => { + panic!("reward_token_id is set, but reward_token_amount is not set") + } + (Some(reward_token_id_str), Some(reward_token_amount)) => Some({ + let reward_token_id: TokenId = Digest32::try_from(reward_token_id_str).unwrap().into(); + SpecToken { + token_id: RewardTokenId::from_token_id_unchecked(reward_token_id), + amount: TokenAmount::try_from(reward_token_amount as u64).unwrap(), + } + }), + }; + reward_token_opt +} diff --git a/core/src/pool_config.rs b/core/src/pool_config.rs index 9aa7c3c1..b914bacc 100644 --- a/core/src/pool_config.rs +++ b/core/src/pool_config.rs @@ -3,8 +3,6 @@ use std::path::PathBuf; use anyhow::anyhow; use derive_more::From; -use ergo_lib::ergo_chain_types::Digest32; -use ergo_lib::ergotree_ir::chain::token::TokenId; use once_cell::sync; use serde::Deserialize; use serde::Serialize; @@ -51,14 +49,6 @@ pub struct PoolConfig { pub token_ids: TokenIds, } -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] -pub struct CastBallotBoxVoteParameters { - pub pool_box_address_hash: Digest32, - pub reward_token_id: TokenId, - pub reward_token_quantity: u64, - pub update_box_creation_height: i32, -} - #[derive(serde::Serialize, serde::Deserialize, Debug, Copy, Clone)] #[allow(clippy::enum_variant_names)] pub enum PredefinedDataPointSource { From ba25be7660a31d06a89007fb1c437b4caf62a436 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Wed, 15 Feb 2023 14:38:20 +0200 Subject: [PATCH 02/15] move optional args of VoteUpdatePool to the end; --- core/src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main.rs b/core/src/main.rs index ca415c0a..c347a5db 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -163,12 +163,12 @@ enum Command { VoteUpdatePool { /// The base16-encoded blake2b hash of the serialized pool box contract for the new pool box. new_pool_box_address_hash_str: String, - /// The base16-encoded reward token id of the new pool box (use existing if unchanged) - reward_token_id_str: Option, - /// The reward token amount in the pool box at the time of update transaction is committed. - reward_token_amount: Option, /// The creation height of the existing update box. update_box_creation_height: u32, + /// The base16-encoded reward token id of the new pool box (if minted) + reward_token_id_str: Option, + /// The reward token amount in the pool box at the time of update transaction is committed (if minted). + reward_token_amount: Option, }, /// Initiate the Update Pool transaction. /// Run with no arguments to show diff between oracle_config.yaml and oracle_config_updated.yaml @@ -546,7 +546,7 @@ fn check_reward_token_opt( let reward_token_id: TokenId = Digest32::try_from(reward_token_id_str).unwrap().into(); SpecToken { token_id: RewardTokenId::from_token_id_unchecked(reward_token_id), - amount: TokenAmount::try_from(reward_token_amount as u64).unwrap(), + amount: TokenAmount::try_from(reward_token_amount).unwrap(), } }), }; From d532a9be27f95d2620665cb8b1cf7978e04c7db7 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Wed, 15 Feb 2023 15:08:23 +0200 Subject: [PATCH 03/15] update rust to 1.66.1; --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 5b6cd6b3..0403bed1 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.65 +1.66.1 From e9d109e885e1402d2a046afcfcae7bcaec2afd5c Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Wed, 15 Feb 2023 15:16:26 +0200 Subject: [PATCH 04/15] remove printing hints after prepare-update command; --- core/src/cli_commands/prepare_update.rs | 39 ++----------------------- 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/core/src/cli_commands/prepare_update.rs b/core/src/cli_commands/prepare_update.rs index cbd55657..7c4c774e 100644 --- a/core/src/cli_commands/prepare_update.rs +++ b/core/src/cli_commands/prepare_update.rs @@ -134,50 +134,15 @@ pub fn prepare_update( "Base16-encoded blake2b hash of the serialized new pool box contract(ErgoTree): {}", blake2b_pool_ergo_tree ); - print_hints_for_voting(height.0)?; + print_hints_for_voting()?; Ok(()) } -fn print_hints_for_voting(height: u32) -> Result<(), PrepareUpdateError> { - let epoch_length = POOL_CONFIG - .refresh_box_wrapper_inputs - .contract_inputs - .contract_parameters() - .epoch_length() - .0 as u32; +fn print_hints_for_voting() -> Result<(), PrepareUpdateError> { let op = OraclePool::new().unwrap(); - let oracle_boxes = op.datapoint_stage.stage.get_boxes().unwrap(); - let min_oracle_box_height = height - epoch_length; - let active_oracle_count = oracle_boxes - .into_iter() - .filter(|b| b.creation_height >= min_oracle_box_height) - .count() as u32; - let pool_box = op.get_pool_box_source().get_pool_box().unwrap(); - let pool_box_height = pool_box.get_box().creation_height; - let next_epoch_height = max(pool_box_height + epoch_length, height); - let reward_tokens_left = *pool_box.reward_token().amount.as_u64(); let update_box = op.get_update_box_source().get_update_box().unwrap(); let update_box_height = update_box.get_box().creation_height; info!("Update box height: {}", update_box_height); - info!( - "Reward token id in the pool box: {}", - String::from(pool_box.reward_token().token_id.token_id()) - ); - info!( - "Current height is {}, pool box height (epoch start) {}, epoch length is {}", - height, pool_box_height, epoch_length - ); - info!( - "Estimated active oracle count is {}, reward tokens in the pool box {}", - active_oracle_count, reward_tokens_left - ); - for i in 0..10 { - info!( - "On new epoch height {} estimating reward tokens in the pool box: {}", - next_epoch_height + i * (epoch_length + 1), - reward_tokens_left - ((i + 1) * (active_oracle_count * 2)) as u64 - ); - } Ok(()) } From 948ad7f4054f370fbab891c80e270c7472ee9a14 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 16 Feb 2023 13:00:16 +0200 Subject: [PATCH 05/15] debug print parsed ergo tree when building the contracts; --- core/src/contracts/ballot.rs | 22 ++++++++++++---------- core/src/contracts/oracle.rs | 6 ++++-- core/src/contracts/pool.rs | 22 ++++++++++++---------- core/src/contracts/refresh.rs | 4 +++- core/src/contracts/update.rs | 4 +++- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/core/src/contracts/ballot.rs b/core/src/contracts/ballot.rs index 77bd17d1..6aa2ca5e 100644 --- a/core/src/contracts/ballot.rs +++ b/core/src/contracts/ballot.rs @@ -98,16 +98,18 @@ impl BallotContract { fn build_with(inputs: &BallotContractInputs) -> Result { let parameters = inputs.contract_parameters.clone(); - let ergo_tree = - ErgoTree::sigma_parse_bytes(inputs.contract_parameters.ergo_tree_bytes.as_slice())? - .with_constant( - parameters.min_storage_rent_index, - parameters.min_storage_rent.into(), - )? - .with_constant( - parameters.update_nft_index, - inputs.update_nft_token_id.token_id().into(), - )?; + let ergo_tree_orig = + ErgoTree::sigma_parse_bytes(inputs.contract_parameters.ergo_tree_bytes.as_slice())?; + log::debug!("ballot contract ergo_tree_orig: {:#?}", ergo_tree_orig); + let ergo_tree = ergo_tree_orig + .with_constant( + parameters.min_storage_rent_index, + parameters.min_storage_rent.into(), + )? + .with_constant( + parameters.update_nft_index, + inputs.update_nft_token_id.token_id().into(), + )?; let contract = Self::from_ergo_tree(ergo_tree, inputs)?; Ok(contract) } diff --git a/core/src/contracts/oracle.rs b/core/src/contracts/oracle.rs index 31bcd360..edd8a813 100644 --- a/core/src/contracts/oracle.rs +++ b/core/src/contracts/oracle.rs @@ -254,8 +254,10 @@ impl OracleContractParameters { min_storage_rent_index: usize, min_storage_rent: BoxValue, ) -> Result { - let ergo_tree = ErgoTree::sigma_parse_bytes(ergo_tree_bytes.as_slice())? - .with_constant(min_storage_rent_index, min_storage_rent.into())?; + let ergo_tree_orig = ErgoTree::sigma_parse_bytes(ergo_tree_bytes.as_slice())?; + log::debug!("oracle contract ergo_tree_orig: {:#?}", ergo_tree_orig); + let ergo_tree = + ergo_tree_orig.with_constant(min_storage_rent_index, min_storage_rent.into())?; let _pool_nft = ergo_tree .get_constant(pool_nft_index) diff --git a/core/src/contracts/pool.rs b/core/src/contracts/pool.rs index d57c0227..d06df724 100644 --- a/core/src/contracts/pool.rs +++ b/core/src/contracts/pool.rs @@ -99,16 +99,18 @@ impl PoolContract { } pub fn build_with(inputs: &PoolContractInputs) -> Result { - let ergo_tree = - ErgoTree::sigma_parse_bytes(inputs.contract_parameters.ergo_tree_bytes.as_slice())? - .with_constant( - inputs.contract_parameters.refresh_nft_index, - inputs.refresh_nft_token_id.token_id().into(), - )? - .with_constant( - inputs.contract_parameters.update_nft_index, - inputs.update_nft_token_id.token_id().into(), - )?; + let ergo_tree_orig = + ErgoTree::sigma_parse_bytes(inputs.contract_parameters.ergo_tree_bytes.as_slice())?; + log::debug!("pool contract ergo_tree_orig: {:#?}", ergo_tree_orig); + let ergo_tree = ergo_tree_orig + .with_constant( + inputs.contract_parameters.refresh_nft_index, + inputs.refresh_nft_token_id.token_id().into(), + )? + .with_constant( + inputs.contract_parameters.update_nft_index, + inputs.update_nft_token_id.token_id().into(), + )?; let contract = Self::from_ergo_tree(ergo_tree, inputs)?; Ok(contract) } diff --git a/core/src/contracts/refresh.rs b/core/src/contracts/refresh.rs index cdba886d..0e2abf48 100644 --- a/core/src/contracts/refresh.rs +++ b/core/src/contracts/refresh.rs @@ -443,7 +443,9 @@ impl RefreshContractParameters { pub fn build_with( inputs: RefreshContractParametersInputs, ) -> Result { - let ergo_tree = ErgoTree::sigma_parse_bytes(inputs.ergo_tree_bytes.as_slice())? + let ergo_tree_orig = ErgoTree::sigma_parse_bytes(inputs.ergo_tree_bytes.as_slice())?; + log::debug!("refresh contract ergo_tree_orig: {:#?}", ergo_tree_orig); + let ergo_tree = ergo_tree_orig .with_constant( inputs.min_data_points_index, inputs.min_data_points.0.into(), diff --git a/core/src/contracts/update.rs b/core/src/contracts/update.rs index 8ab14a21..0af5ef73 100644 --- a/core/src/contracts/update.rs +++ b/core/src/contracts/update.rs @@ -260,7 +260,9 @@ impl UpdateContractParameters { min_votes_index: usize, min_votes: u64, ) -> Result { - let ergo_tree = ErgoTree::sigma_parse_bytes(ergo_tree_bytes.as_slice())? + let ergo_tree_orig = ErgoTree::sigma_parse_bytes(ergo_tree_bytes.as_slice())?; + log::debug!("update contract ergo_tree_orig: {:#?}", ergo_tree_orig); + let ergo_tree = ergo_tree_orig .with_constant(min_votes_index, (min_votes as i32).into()) .map_err(UpdateContractParametersError::ErgoTreeConstant)?; let _pool_nft = ergo_tree From 4b7fdfc25c6560e51f670c91113a7cdf56136210 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 23 Feb 2023 12:11:28 +0200 Subject: [PATCH 06/15] update README desc for vote-update-pool command; --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d5bc3282..d71d5974 100644 --- a/README.md +++ b/README.md @@ -154,19 +154,20 @@ The output shows the new pool box contract hash and reward tokens amounts for th Run ```console -oracle-core vote-update-pool +oracle-core vote-update-pool ``` Where: - - base16-encoded blake2b hash of the serialized pool box contract for the new pool box -- - base16-encoded reward token id in the new pool box (use existing if unchanged) -- - reward token amount in the pool box at the time of update transaction is committed - - The creation height of the existing update box. -and are printed in the output of the `prepare-update` command. +are required parameters, with optinal (in case of minting a new reward token): + +- - base16-encoded reward token id in the new pool box (use existing if unchanged) +- - reward token amount in the pool box at the time of update transaction is committed -Keep in mind the REWARD_TOKEN_AMOUNT depends on when(in which epoch) the final `update-pool` command will be run. +They are printed in the output of the `prepare-update` command. ### Update the pool box contract with `update-pool` command From 9f442ea6a8856d2a9472453a32f73e39dfae1883 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 24 Feb 2023 13:30:10 +0200 Subject: [PATCH 07/15] remove new pool box hash parameter from update-pool command; restore hint after prepare-update command; --- core/src/cli_commands/prepare_update.rs | 40 ++++++++++++++++++++++-- core/src/cli_commands/update_pool.rs | 41 ++++++++++++++++--------- core/src/main.rs | 12 +++----- 3 files changed, 69 insertions(+), 24 deletions(-) diff --git a/core/src/cli_commands/prepare_update.rs b/core/src/cli_commands/prepare_update.rs index 7c4c774e..8d065c6c 100644 --- a/core/src/cli_commands/prepare_update.rs +++ b/core/src/cli_commands/prepare_update.rs @@ -134,15 +134,51 @@ pub fn prepare_update( "Base16-encoded blake2b hash of the serialized new pool box contract(ErgoTree): {}", blake2b_pool_ergo_tree ); - print_hints_for_voting()?; + print_hints_for_voting(height)?; Ok(()) } -fn print_hints_for_voting() -> Result<(), PrepareUpdateError> { +fn print_hints_for_voting(height: BlockHeight) -> Result<(), PrepareUpdateError> { + let epoch_length = POOL_CONFIG + .refresh_box_wrapper_inputs + .contract_inputs + .contract_parameters() + .epoch_length() + .0 as u32; let op = OraclePool::new().unwrap(); + let oracle_boxes = op.datapoint_stage.stage.get_boxes().unwrap(); + let min_oracle_box_height = height - epoch_length; + let active_oracle_count = oracle_boxes + .into_iter() + .filter(|b| b.creation_height >= min_oracle_box_height.0) + .count() as u32; + let pool_box = op.get_pool_box_source().get_pool_box().unwrap(); + let pool_box_height = pool_box.get_box().creation_height; + let next_epoch_height = max(pool_box_height + epoch_length, height.0); + let reward_tokens_left = *pool_box.reward_token().amount.as_u64(); let update_box = op.get_update_box_source().get_update_box().unwrap(); let update_box_height = update_box.get_box().creation_height; info!("Update box height: {}", update_box_height); + + info!( + "Reward token id in the pool box: {}", + String::from(pool_box.reward_token().token_id.token_id()) + ); + info!( + "Current height is {}, pool box height (epoch start) {}, epoch length is {}", + height, pool_box_height, epoch_length + ); + info!( + "Estimated active oracle count is {}, reward tokens in the pool box {}", + active_oracle_count, reward_tokens_left + ); + for i in 0..10 { + info!( + "On new epoch height {} estimating reward tokens in the pool box: {}", + next_epoch_height + i * (epoch_length + 1), + reward_tokens_left - ((i + 1) * (active_oracle_count * 2)) as u64 + ); + } Ok(()) } diff --git a/core/src/cli_commands/update_pool.rs b/core/src/cli_commands/update_pool.rs index 97c63d4a..10358308 100644 --- a/core/src/cli_commands/update_pool.rs +++ b/core/src/cli_commands/update_pool.rs @@ -76,7 +76,6 @@ pub fn update_pool( wallet: &dyn WalletDataSource, tx_signer: &dyn SignTransaction, tx_submit: &dyn SubmitTransaction, - new_pool_box_hash_str: Option, new_reward_tokens: Option>, height: BlockHeight, ) -> Result<(), UpdatePoolError> { @@ -104,32 +103,46 @@ pub fn update_pool( new_reward_tokens.clone(), ); - if new_pool_box_hash_str.is_none() { - println!( - "Run ./oracle-core --new_pool_box_hash {} to update pool", - String::from(new_pool_box_hash) - ); - return Ok(()); - } - let tx = build_update_pool_box_tx( op.get_pool_box_source(), op.get_ballot_boxes_source(), wallet, op.get_update_box_source(), new_pool_contract, - new_reward_tokens, + new_reward_tokens.clone(), height, change_address, )?; + log::debug!("Signing update pool box tx: {:#?}", tx); let signed_tx = tx_signer.sign_transaction(&tx.spending_tx)?; - let tx_id_str = tx_submit.submit_transaction(&signed_tx)?; - crate::explorer_api::wait_for_tx_confirmation(signed_tx.id()); + println!( - "Update pool box transaction submitted: view here, {}", - ergo_explorer_transaction_link(tx_id_str, network_prefix) + "YOU WILL BE SUBMITTING AN UPDATE TO THE POOL CONTRACT:\ + - Hash of new pool box contract: {}", + String::from(new_pool_box_hash), ); + if let Some(reward_token) = new_reward_tokens { + println!( + " - Reward token Id: {}\ + - Reward token amount: {}", + String::from(reward_token.token_id.token_id()), + reward_token.amount.as_u64(), + ); + } + println!("TYPE 'YES' TO SUBMIT THE TRANSACTION."); + 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)?; + crate::explorer_api::wait_for_tx_confirmation(signed_tx.id()); + println!( + "Update pool box transaction submitted: view here, {}", + ergo_explorer_transaction_link(tx_id_str, network_prefix) + ); + } else { + println!("Aborting the transaction.") + } Ok(()) } diff --git a/core/src/main.rs b/core/src/main.rs index c347a5db..0fdd3d76 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -171,14 +171,12 @@ enum Command { reward_token_amount: Option, }, /// Initiate the Update Pool transaction. - /// Run with no arguments to show diff between oracle_config.yaml and oracle_config_updated.yaml - /// Updated config file must be created using --prepare-update command first + /// Updated config file `pool_config_updated.yaml` is expected to be in the current directory + /// and must be created using --prepare-update command first UpdatePool { - /// New pool box hash. Must match hash of updated pool contract - new_pool_box_hash: Option, - /// New reward token id (optional, base64) + /// New reward token id (only if minted) reward_token_id: Option, - /// New reward token amount, required if new token id was voted for + /// New reward token amount (only if minted) reward_token_amount: Option, }, /// Prepare updating oracle pool with new contracts/parameters. @@ -400,7 +398,6 @@ fn handle_pool_command( } } Command::UpdatePool { - new_pool_box_hash, reward_token_id, reward_token_amount, } => { @@ -410,7 +407,6 @@ fn handle_pool_command( &node_api, &node_api.node, &node_api.node, - new_pool_box_hash, reward_token_opt, height, ) { From c6610f4462d53e33b96c79ee137235620901f3b1 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 24 Feb 2023 14:05:43 +0200 Subject: [PATCH 08/15] add explorer_url optional parameter to the oracle_config.yaml #219; --- core/src/explorer_api.rs | 32 ++++++++++++++++++++++---------- core/src/oracle_config.rs | 6 +++++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/core/src/explorer_api.rs b/core/src/explorer_api.rs index d7cffc46..b17d4466 100644 --- a/core/src/explorer_api.rs +++ b/core/src/explorer_api.rs @@ -7,11 +7,23 @@ use ergo_lib::ergotree_ir::chain::address::NetworkPrefix; use reqwest::blocking::RequestBuilder; use reqwest::blocking::Response; use reqwest::header::CONTENT_TYPE; +use reqwest::Url; use thiserror::Error; use url::ParseError; use crate::oracle_config::ORACLE_CONFIG; +pub const MAINNET_EXPLORER_URL: &str = "https://api.ergoplatform.com/"; +pub const TESTNET_EXPLORER_URL: &str = "https://api-testnet.ergoplatform.com/"; + +pub fn default_explorer_url(network_prefix: NetworkPrefix) -> Url { + let url_str = match network_prefix { + NetworkPrefix::Mainnet => MAINNET_EXPLORER_URL, + NetworkPrefix::Testnet => TESTNET_EXPLORER_URL, + }; + Url::parse(url_str).unwrap() +} + #[derive(Debug, From, Error)] pub enum ExplorerApiError { #[error("reqwest error: {0}")] @@ -27,10 +39,11 @@ pub struct ExplorerApi { } impl ExplorerApi { - pub fn new(url: &str) -> Result { - Ok(Self { - url: url::Url::parse(url)?, - }) + pub const MAINNET_EXPLORER_URL: &'static str = "https://api.ergoplatform.com/"; + pub const TESTNET_EXPLORER_URL: &'static str = "https://api-testnet.ergoplatform.com/"; + + pub fn new(url: Url) -> Self { + Self { url } } /// Sets required headers for a request @@ -70,12 +83,11 @@ pub fn wait_for_tx_confirmation(tx_id: TxId) { pub fn wait_for_txs_confirmation(tx_ids: Vec) { let network = ORACLE_CONFIG.oracle_address.network(); let timeout = Duration::from_secs(1200); - let explorer_api = match network { - NetworkPrefix::Mainnet => ExplorerApi::new("https://api.ergoplatform.com/").unwrap(), - NetworkPrefix::Testnet => { - ExplorerApi::new("https://api-testnet.ergoplatform.com/").unwrap() - } - }; + let explorer_url = ORACLE_CONFIG + .explorer_url + .clone() + .unwrap_or_else(|| default_explorer_url(network)); + let explorer_api = ExplorerApi::new(explorer_url); let start_time = std::time::Instant::now(); println!("Waiting for block confirmation from ExplorerApi for tx ids: {tx_ids:?} ..."); let mut remaining_txs = tx_ids.clone(); diff --git a/core/src/oracle_config.rs b/core/src/oracle_config.rs index c410c06b..897b2808 100644 --- a/core/src/oracle_config.rs +++ b/core/src/oracle_config.rs @@ -15,6 +15,8 @@ use reqwest::Url; use serde::{Deserialize, Serialize}; use thiserror::Error; +use crate::explorer_api::default_explorer_url; + pub const DEFAULT_ORACLE_CONFIG_FILE_NAME: &str = "oracle_config.yaml"; #[derive(Debug, Clone, Deserialize, Serialize)] @@ -26,6 +28,7 @@ pub struct OracleConfig { pub core_api_port: u16, pub oracle_address: NetworkAddress, pub data_point_source_custom_script: Option, + pub explorer_url: Option, } impl OracleConfig { @@ -73,13 +76,14 @@ impl Default for OracleConfig { ) .unwrap(); Self { - oracle_address: address, + oracle_address: address.clone(), node_api_key: "hello".into(), core_api_port: 9010, data_point_source_custom_script: None, base_fee: *tx_builder::SUGGESTED_TX_FEE().as_u64(), log_level: LevelFilter::Info.into(), node_url: Url::parse("http://127.0.0.1:9053").unwrap(), + explorer_url: Some(default_explorer_url(address.network())), } } } From 838a7d89c1f1554bd3779462a6de84eebc8cfeed Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 24 Feb 2023 15:15:23 +0200 Subject: [PATCH 09/15] fix exro_explorer_transaction_link, re-arrange code; --- core/src/cli_commands.rs | 15 ---------- .../src/cli_commands/extract_reward_tokens.rs | 2 +- .../src/cli_commands/transfer_oracle_token.rs | 2 +- core/src/cli_commands/update_pool.rs | 2 +- core/src/cli_commands/vote_update_pool.rs | 2 +- core/src/explorer_api.rs | 30 +++++++++++-------- core/src/explorer_api/explorer_url.rs | 24 +++++++++++++++ core/src/oracle_config.rs | 4 +-- 8 files changed, 47 insertions(+), 34 deletions(-) create mode 100644 core/src/explorer_api/explorer_url.rs diff --git a/core/src/cli_commands.rs b/core/src/cli_commands.rs index eb23e6d6..7bba8e8c 100644 --- a/core/src/cli_commands.rs +++ b/core/src/cli_commands.rs @@ -1,6 +1,3 @@ -use ergo_lib::chain::transaction::TxId; -use ergo_lib::ergotree_ir::chain::address::NetworkPrefix; - pub mod bootstrap; pub mod extract_reward_tokens; pub mod import_pool_update; @@ -9,15 +6,3 @@ pub mod print_reward_tokens; pub mod transfer_oracle_token; pub mod update_pool; pub mod vote_update_pool; - -pub(crate) fn ergo_explorer_transaction_link(tx_id: TxId, prefix: NetworkPrefix) -> String { - let prefix_str = match prefix { - NetworkPrefix::Mainnet => "explorer", - NetworkPrefix::Testnet => "testnet", - }; - let tx_id_str = String::from(tx_id); - format!( - "https://{}.ergoplatform.com/en/transactions/{}", - prefix_str, tx_id_str - ) -} diff --git a/core/src/cli_commands/extract_reward_tokens.rs b/core/src/cli_commands/extract_reward_tokens.rs index d991d428..1d31a706 100644 --- a/core/src/cli_commands/extract_reward_tokens.rs +++ b/core/src/cli_commands/extract_reward_tokens.rs @@ -26,7 +26,7 @@ use crate::{ box_kind::{ make_collected_oracle_box_candidate, make_oracle_box_candidate, OracleBox, OracleBoxWrapper, }, - cli_commands::ergo_explorer_transaction_link, + explorer_api::ergo_explorer_transaction_link, node_interface::{SignTransaction, SubmitTransaction}, oracle_config::BASE_FEE, oracle_state::{LocalDatapointBoxSource, StageError}, diff --git a/core/src/cli_commands/transfer_oracle_token.rs b/core/src/cli_commands/transfer_oracle_token.rs index 6b144528..d31967a2 100644 --- a/core/src/cli_commands/transfer_oracle_token.rs +++ b/core/src/cli_commands/transfer_oracle_token.rs @@ -23,7 +23,7 @@ use crate::{ box_kind::{ make_collected_oracle_box_candidate, make_oracle_box_candidate, OracleBox, OracleBoxWrapper, }, - cli_commands::ergo_explorer_transaction_link, + explorer_api::ergo_explorer_transaction_link, node_interface::{SignTransaction, SubmitTransaction}, oracle_config::BASE_FEE, oracle_state::{LocalDatapointBoxSource, StageError}, diff --git a/core/src/cli_commands/update_pool.rs b/core/src/cli_commands/update_pool.rs index 10358308..3b3af677 100644 --- a/core/src/cli_commands/update_pool.rs +++ b/core/src/cli_commands/update_pool.rs @@ -26,8 +26,8 @@ use crate::{ make_pool_box_candidate_unchecked, BallotBox, CastBallotBoxVoteParameters, PoolBox, PoolBoxWrapper, VoteBallotBoxWrapper, }, - cli_commands::ergo_explorer_transaction_link, contracts::pool::PoolContract, + explorer_api::ergo_explorer_transaction_link, node_interface::{SignTransaction, SubmitTransaction}, oracle_config::BASE_FEE, oracle_state::{OraclePool, PoolBoxSource, StageError, UpdateBoxSource, VoteBallotBoxesSource}, diff --git a/core/src/cli_commands/vote_update_pool.rs b/core/src/cli_commands/vote_update_pool.rs index 67a6ea88..278e56be 100644 --- a/core/src/cli_commands/vote_update_pool.rs +++ b/core/src/cli_commands/vote_update_pool.rs @@ -17,10 +17,10 @@ use ergo_node_interface::node_interface::NodeError; use crate::{ box_kind::{make_local_ballot_box_candidate, BallotBox, BallotBoxWrapper}, - cli_commands::ergo_explorer_transaction_link, contracts::ballot::{ BallotContract, BallotContractError, BallotContractInputs, BallotContractParameters, }, + explorer_api::ergo_explorer_transaction_link, node_interface::{SignTransaction, SubmitTransaction}, oracle_config::{BASE_FEE, ORACLE_CONFIG}, oracle_state::{LocalBallotBoxSource, StageError}, diff --git a/core/src/explorer_api.rs b/core/src/explorer_api.rs index b17d4466..acf56e3a 100644 --- a/core/src/explorer_api.rs +++ b/core/src/explorer_api.rs @@ -13,16 +13,10 @@ use url::ParseError; use crate::oracle_config::ORACLE_CONFIG; -pub const MAINNET_EXPLORER_URL: &str = "https://api.ergoplatform.com/"; -pub const TESTNET_EXPLORER_URL: &str = "https://api-testnet.ergoplatform.com/"; +use self::explorer_url::default_explorer_api_url; +use self::explorer_url::default_explorer_url; -pub fn default_explorer_url(network_prefix: NetworkPrefix) -> Url { - let url_str = match network_prefix { - NetworkPrefix::Mainnet => MAINNET_EXPLORER_URL, - NetworkPrefix::Testnet => TESTNET_EXPLORER_URL, - }; - Url::parse(url_str).unwrap() -} +pub mod explorer_url; #[derive(Debug, From, Error)] pub enum ExplorerApiError { @@ -39,9 +33,6 @@ pub struct ExplorerApi { } impl ExplorerApi { - pub const MAINNET_EXPLORER_URL: &'static str = "https://api.ergoplatform.com/"; - pub const TESTNET_EXPLORER_URL: &'static str = "https://api-testnet.ergoplatform.com/"; - pub fn new(url: Url) -> Self { Self { url } } @@ -76,6 +67,19 @@ impl ExplorerApi { } } +pub(crate) fn ergo_explorer_transaction_link(tx_id: TxId, prefix: NetworkPrefix) -> String { + let url = ORACLE_CONFIG + .explorer_url + .clone() + .unwrap_or_else(|| default_explorer_url(prefix)); + let tx_id_str = String::from(tx_id); + url.join("en/transactions/") + .unwrap() + .join(&tx_id_str) + .unwrap(); + url.to_string() +} + pub fn wait_for_tx_confirmation(tx_id: TxId) { wait_for_txs_confirmation(vec![tx_id]); } @@ -86,7 +90,7 @@ pub fn wait_for_txs_confirmation(tx_ids: Vec) { let explorer_url = ORACLE_CONFIG .explorer_url .clone() - .unwrap_or_else(|| default_explorer_url(network)); + .unwrap_or_else(|| default_explorer_api_url(network)); let explorer_api = ExplorerApi::new(explorer_url); let start_time = std::time::Instant::now(); println!("Waiting for block confirmation from ExplorerApi for tx ids: {tx_ids:?} ..."); diff --git a/core/src/explorer_api/explorer_url.rs b/core/src/explorer_api/explorer_url.rs new file mode 100644 index 00000000..0126e953 --- /dev/null +++ b/core/src/explorer_api/explorer_url.rs @@ -0,0 +1,24 @@ +use ergo_lib::ergotree_ir::chain::address::NetworkPrefix; +use reqwest::Url; + +pub const MAINNET_EXPLORER_API_URL: &str = "https://api.ergoplatform.com/"; +pub const TESTNET_EXPLORER_API_URL: &str = "https://api-testnet.ergoplatform.com/"; + +pub const MAINNET_EXPLORER_URL: &str = "https://explorer.ergoplatform.com/"; +pub const TESTNET_EXPLORER_URL: &str = "https://testnet.ergoplatform.com/"; + +pub fn default_explorer_api_url(network_prefix: NetworkPrefix) -> Url { + let url_str = match network_prefix { + NetworkPrefix::Mainnet => MAINNET_EXPLORER_API_URL, + NetworkPrefix::Testnet => TESTNET_EXPLORER_API_URL, + }; + Url::parse(url_str).unwrap() +} + +pub fn default_explorer_url(network_prefix: NetworkPrefix) -> Url { + let url_str = match network_prefix { + NetworkPrefix::Mainnet => MAINNET_EXPLORER_URL, + NetworkPrefix::Testnet => TESTNET_EXPLORER_URL, + }; + Url::parse(url_str).unwrap() +} diff --git a/core/src/oracle_config.rs b/core/src/oracle_config.rs index 897b2808..c72f3efd 100644 --- a/core/src/oracle_config.rs +++ b/core/src/oracle_config.rs @@ -15,7 +15,7 @@ use reqwest::Url; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::explorer_api::default_explorer_url; +use crate::explorer_api::explorer_url::default_explorer_api_url; pub const DEFAULT_ORACLE_CONFIG_FILE_NAME: &str = "oracle_config.yaml"; @@ -83,7 +83,7 @@ impl Default for OracleConfig { base_fee: *tx_builder::SUGGESTED_TX_FEE().as_u64(), log_level: LevelFilter::Info.into(), node_url: Url::parse("http://127.0.0.1:9053").unwrap(), - explorer_url: Some(default_explorer_url(address.network())), + explorer_url: Some(default_explorer_api_url(address.network())), } } } From 6a8ca8cdcf0c145b737e069d3a83ca6c783f6b66 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Mon, 27 Feb 2023 08:34:57 +0200 Subject: [PATCH 10/15] clean up base 64 token id encoding leftovers #226; --- core/src/cli_commands/bootstrap.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/src/cli_commands/bootstrap.rs b/core/src/cli_commands/bootstrap.rs index 53d75e39..9b1266d6 100644 --- a/core/src/cli_commands/bootstrap.rs +++ b/core/src/cli_commands/bootstrap.rs @@ -669,7 +669,6 @@ pub(crate) mod tests { ergotree_ir::chain::{ address::{AddressEncoder, NetworkAddress, NetworkPrefix}, ergo_box::{ErgoBox, NonMandatoryRegisters}, - token::TokenId, }, wallet::Wallet, }; @@ -781,12 +780,6 @@ pub(crate) mod tests { let s = serde_yaml::to_string(&oracle_config).unwrap(); println!("{}", s); - // Quickly check an encoding - let bytes: Vec = token_ids.ballot_token_id.token_id().into(); - let encoded = base64::encode(bytes); - let ballot_id = TokenId::from_base64(&encoded).unwrap(); - assert_eq!(token_ids.ballot_token_id.token_id(), ballot_id); - // Check that refresh contract is updated assert_ne!( oracle_config From 8d1315ce8b6f7385310afe49b755ed9311c39697 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Mon, 27 Feb 2023 11:09:34 +0200 Subject: [PATCH 11/15] update default parameters for update contract according to https://github.com/ergoplatform/eips/pull/89; --- core/src/default_parameters.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/default_parameters.rs b/core/src/default_parameters.rs index 4729e576..28f57a17 100644 --- a/core/src/default_parameters.rs +++ b/core/src/default_parameters.rs @@ -91,11 +91,11 @@ impl Default for RefreshContractParameters { impl Default for UpdateContractParameters { fn default() -> Self { // compiled via - // https://scastie.scala-lang.org/BxJFCRDcSCeiwf9ZgKdymQ - let ergo_tree_bytes = base16::decode("100e040004000400040204020e20472b4b6250655368566d597133743677397a24432646294a404d635166546a570400040004000e203f4428472d4b6150645367566b5970337336763979244226452948404d625165010005000400040cd806d601b2a4730000d602b2db63087201730100d603b2a5730200d604db63087203d605b2a5730300d606b27204730400d1ededed938c7202017305edededed937202b2720473060093c17201c1720393c672010405c67203040593c672010504c672030504efe6c672030661edededed93db63087205db6308a793c27205c2a792c17205c1a7918cc77205018cc7a701efe6c67205046192b0b5a4d9010763d801d609db630872079591b172097307edededed938cb2720973080001730993e4c6720705048cc7a70193e4c67207060ecbc2720393e4c67207070e8c72060193e4c6720708058c720602730a730bd9010741639a8c7207018cb2db63088c720702730c00027e730d05").unwrap(); - let pool_nft_index = 5; - let ballot_token_index = 9; - let min_votes_index = 13; + // https://scastie.scala-lang.org/LjGBkqExRc2vCjmDKGLxWg + let ergo_tree_bytes = base16::decode("100f0400040004000402040204020e20472b4b6250655368566d597133743677397a24432646294a404d635166546a570400040004000e203f4428472d4b6150645367566b5970337336763979244226452948404d625165010005000400040cd80ad601b2a4730000d602db63087201d603b27202730100d604b2a5730200d605db63087204d606b2a5730300d607b27205730400d6088c720701d6098c720702d60ab27202730500d1ededed938c7203017306edededed937203b2720573070093c17201c1720493c672010405c67204040593c672010504c672040504efe6c672040661edededed93db63087206db6308a793c27206c2a792c17206c1a7918cc77206018cc7a701efe6c67206046192b0b5a4d9010b63d801d60ddb6308720b9591b1720d7308d801d60ec6720b070eededed938cb2720d73090001730a93e4c6720b05048cc7a70193e4c6720b060ecbc2720495ede6720ee6c6720b0805ed93e4720e720893e4c6720b08057209ed938c720a017208938c720a027209730b730cd9010b41639a8c720b018cb2db63088c720b02730d00027e730e05").unwrap(); + let pool_nft_index = 6; + let ballot_token_index = 10; + let min_votes_index = 14; let min_votes = 6; UpdateContractParameters::checked_load( ergo_tree_bytes, @@ -161,7 +161,7 @@ mod tests { let expected_refresh_encoding = "cs5c5QEirstI4ZlTyrbTjlPwWYHRW+QsedtpyOSBnH4="; let expected_oracle_encoding = "fhOYLO3s+NJCqTQDWUz0E+ffy2T1VG7ZnhSFs0RP948="; let expected_ballot_encoding = "2DnK+72bh+TxviNk8XfuYzLKtuF5jnqUJOzimt30NvI="; - let expected_update_encoding = "3aIVTP5tRgCZHxaT3ZFw3XubRV5DJi0rKeo9bKVHlVw="; + let expected_update_encoding = "pQ7Dgjq1pUyISroP+RWEDf+kVNYAWjeFHzW+cpImhsQ="; println!("BASE 64 ENCODING OF BLAKE2B HASH OF CONTRACT ERGO-TREE BYTES"); println!("------------------------------------------------------------\n"); From f44d0d9d86c969725642e45ca10f259756fb8f7b Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Tue, 28 Feb 2023 11:23:49 +0200 Subject: [PATCH 12/15] ensure exaclty one reward token on transferring the oracle token #159; --- core/src/cli_commands/transfer_oracle_token.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/cli_commands/transfer_oracle_token.rs b/core/src/cli_commands/transfer_oracle_token.rs index d31967a2..cae241b1 100644 --- a/core/src/cli_commands/transfer_oracle_token.rs +++ b/core/src/cli_commands/transfer_oracle_token.rs @@ -113,7 +113,7 @@ fn build_transfer_oracle_token_tx( .get_local_oracle_datapoint_box()? .ok_or(TransferOracleTokenActionError::NoLocalDatapointBox)?; let num_reward_tokens = *in_oracle_box.reward_token().amount.as_u64(); - if num_reward_tokens <= 1 { + if num_reward_tokens != 1 { return Err( TransferOracleTokenActionError::IncorrectNumberOfRewardTokensInOracleBox( num_reward_tokens as usize, From f10d4d622f1707a667ce8a71217e2ddcc4b54240 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Tue, 28 Feb 2023 11:44:26 +0200 Subject: [PATCH 13/15] fix reward token amount in tests; --- core/src/cli_commands/extract_reward_tokens.rs | 3 ++- core/src/cli_commands/transfer_oracle_token.rs | 1 + core/src/pool_commands/publish_datapoint.rs | 1 + core/src/pool_commands/refresh.rs | 1 + core/src/pool_commands/test_utils.rs | 3 ++- 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/cli_commands/extract_reward_tokens.rs b/core/src/cli_commands/extract_reward_tokens.rs index 1d31a706..3921490b 100644 --- a/core/src/cli_commands/extract_reward_tokens.rs +++ b/core/src/cli_commands/extract_reward_tokens.rs @@ -226,7 +226,7 @@ mod tests { let wallet = Wallet::from_secrets(vec![secret.clone().into()]); let oracle_pub_key = secret.public_image().h; - let num_reward_tokens_in_box = 100_u64; + let num_reward_tokens_in_box = 2; let parameters = OracleContractParameters::default(); let oracle_box_wrapper_inputs = @@ -239,6 +239,7 @@ mod tests { &token_ids, BASE_FEE.checked_mul_u32(100).unwrap(), BlockHeight(height.0), + num_reward_tokens_in_box, ), &oracle_box_wrapper_inputs, ) diff --git a/core/src/cli_commands/transfer_oracle_token.rs b/core/src/cli_commands/transfer_oracle_token.rs index cae241b1..9f1c9a55 100644 --- a/core/src/cli_commands/transfer_oracle_token.rs +++ b/core/src/cli_commands/transfer_oracle_token.rs @@ -215,6 +215,7 @@ mod tests { &token_ids, BASE_FEE.checked_mul_u32(100).unwrap(), BlockHeight(height.0) - 9, + 1, ), &oracle_box_wrapper_inputs, ) diff --git a/core/src/pool_commands/publish_datapoint.rs b/core/src/pool_commands/publish_datapoint.rs index 3a8c8ea0..fcc07ea5 100644 --- a/core/src/pool_commands/publish_datapoint.rs +++ b/core/src/pool_commands/publish_datapoint.rs @@ -239,6 +239,7 @@ mod tests { .contract_parameters() .min_storage_rent, height - EpochLength(99), + 100, ), &oracle_box_wrapper_inputs, ) diff --git a/core/src/pool_commands/refresh.rs b/core/src/pool_commands/refresh.rs index 6158add6..6db602d7 100644 --- a/core/src/pool_commands/refresh.rs +++ b/core/src/pool_commands/refresh.rs @@ -427,6 +427,7 @@ mod tests { token_ids, value, creation_height, + 100, ), &oracle_box_wrapper_inputs, ) diff --git a/core/src/pool_commands/test_utils.rs b/core/src/pool_commands/test_utils.rs index e9e0ad53..ac01be81 100644 --- a/core/src/pool_commands/test_utils.rs +++ b/core/src/pool_commands/test_utils.rs @@ -195,6 +195,7 @@ pub(crate) fn make_datapoint_box( token_ids: &TokenIds, value: BoxValue, creation_height: BlockHeight, + reward_token_amount: u64, ) -> ErgoBox { let tokens = vec![ Token::from(( @@ -203,7 +204,7 @@ pub(crate) fn make_datapoint_box( )), Token::from(( token_ids.reward_token_id.token_id(), - 100u64.try_into().unwrap(), + reward_token_amount.try_into().unwrap(), )), ] .try_into() From 52827dd1e0fce157ac82079a811bb42828b0c85f Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Wed, 1 Mar 2023 10:33:20 +0200 Subject: [PATCH 14/15] move tx confirmation waiting to the end of prepare_update command; --- core/src/cli_commands/prepare_update.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/cli_commands/prepare_update.rs b/core/src/cli_commands/prepare_update.rs index 8d065c6c..ea37032e 100644 --- a/core/src/cli_commands/prepare_update.rs +++ b/core/src/cli_commands/prepare_update.rs @@ -111,8 +111,6 @@ pub fn prepare_update( let prepare = PrepareUpdate::new(update_bootstrap_input, &POOL_CONFIG, &ORACLE_CONFIG)?; let (new_config, submitted_tx_ids) = prepare.execute(config)?; - wait_for_txs_confirmation(submitted_tx_ids); - // let new_config = perform_update_chained_transaction(update_bootstrap_input)?; let blake2b_pool_ergo_tree: String = blake2b256_hash( new_config .pool_box_wrapper_inputs @@ -135,6 +133,7 @@ pub fn prepare_update( blake2b_pool_ergo_tree ); print_hints_for_voting(height)?; + wait_for_txs_confirmation(submitted_tx_ids); Ok(()) } From 42a8d66aad4b1035bfe4e604dccfc04d49112d30 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 2 Mar 2023 11:23:33 +0200 Subject: [PATCH 15/15] update `update-pool` command description in readme; add `import-pool-update` command description in the readme; --- README.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d71d5974..0043f8e7 100644 --- a/README.md +++ b/README.md @@ -175,24 +175,26 @@ Make sure the `pool_config_updated.yaml` config file generated during the `prepa Run ```console -oracle-core update-pool +oracle-core update-pool ``` -to see the diff for the tokens. +With optional(only if minted) parameters: + - base16-encoded reward token id in the new pool box (only if minted) + - 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. + +### Import update pool config with `import-pool-update` command +Make sure the `pool_config_updated.yaml` config file generated during the `prepare-update` command is at hand. Run ```console -oracle-core update-pool +oracle-core update-pool pool_config_updated.yaml ``` -Where: - - base16-encoded blake2b hash of the serialized pool box contract for the new pool box - - base16-encoded reward token id in the new pool box (use existing if unchanged) - - reward token amount in the pool box at the time of update transaction is committed - -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. +This will update the pool_config.yaml, removes `scanIds.json`. Restart the oracle afterwards. ## How to run as systemd daemon