From 27dcc89286e59f2c295e28736279769e75e6b9ad Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 24 Nov 2022 12:24:27 +0200 Subject: [PATCH 1/5] issue #176 remove pool box height preservation requirement from the update contract; update off-chain code to create new pool box with the current height; --- core/src/cli_commands/update_pool.rs | 2 +- core/src/default_parameters.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/cli_commands/update_pool.rs b/core/src/cli_commands/update_pool.rs index 920a0a84..ecc6d709 100644 --- a/core/src/cli_commands/update_pool.rs +++ b/core/src/cli_commands/update_pool.rs @@ -248,7 +248,7 @@ fn build_update_pool_box_tx( old_pool_box.pool_nft_token(), reward_tokens.clone(), old_pool_box.get_box().value, - old_pool_box.get_box().creation_height, // creation info must be preserved + height, )?; let mut update_box_candidate = ErgoBoxCandidateBuilder::new(update_box.get_box().value, update_box.ergo_tree(), height); diff --git a/core/src/default_parameters.rs b/core/src/default_parameters.rs index 2e984388..b0a61075 100644 --- a/core/src/default_parameters.rs +++ b/core/src/default_parameters.rs @@ -88,8 +88,8 @@ impl Default for RefreshContractParameters { impl Default for UpdateContractParameters { fn default() -> Self { // compiled via - // https://scastie.scala-lang.org/epRkAqc1Tl6oDut01uSsgg - let ergo_tree_bytes = base16::decode("100e040004000400040204020e20472b4b6250655368566d597133743677397a24432646294a404d635166546a570400040004000e203f4428472d4b6150645367566b5970337336763979244226452948404d625165010005000400040cd806d601b2a4730000d602b2db63087201730100d603b2a5730200d604db63087203d605b2a5730300d606b27204730400d1ededed938c7202017305ededededed937202b27204730600938cc77201018cc772030193c17201c1720393c672010405c67203040593c672010504c672030504efe6c672030661edededed93db63087205db6308a793c27205c2a792c17205c1a7918cc77205018cc7a701efe6c67205046192b0b5a4d9010763d801d609db630872079591b172097307edededed938cb2720973080001730993e4c6720705048cc7a70193e4c67207060ecbc2720393e4c67207070e8c72060193e4c6720708058c720602730a730bd9010741639a8c7207018cb2db63088c720702730c00027e730d05").unwrap(); + // 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; @@ -158,7 +158,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 = "0wFmk/1TNpgTsbzpWND3WLPbwQdD8E+TWDzZLaYv3nE="; + let expected_update_encoding = "3aIVTP5tRgCZHxaT3ZFw3XubRV5DJi0rKeo9bKVHlVw="; println!("BASE 64 ENCODING OF BLAKE2B HASH OF CONTRACT ERGO-TREE BYTES"); println!("------------------------------------------------------------\n"); From 0403ef1bef59e8684324b7f734e8636bfd38b37e Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 25 Nov 2022 14:47:47 +0200 Subject: [PATCH 2/5] add `update-pool` command description to update_epoch_length.md; --- docs/update_epoch_length.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/update_epoch_length.md b/docs/update_epoch_length.md index 95e32946..2334e64a 100644 --- a/docs/update_epoch_length.md +++ b/docs/update_epoch_length.md @@ -39,7 +39,7 @@ oracle-core prepare-update prepare_update_conf.yaml and check that `oracle_config_updated.yaml` with new refresh contract, pool contract and refresh NFT is generated. The new pool contract hash is printed along with current reward token amount and guesstimated reward token amounts for the upcoming epochs. -### Vote for the change. +### Vote for the change with `vote-update-pool` command. Run ```console oracle-core vote-update-pool @@ -53,3 +53,19 @@ Where: are printed in the output of the `prepare-update` command. Keep in mind the REWARD_TOKEN_AMOUNT depends on when(in which epoch) the final `update-pool` command will be run. + +### Commit the update to the pool box contract with `update-pool` command. +Make sure the `oracle_config_updated.yaml` config file generated during the `prepare-update` command is in the same folder as the oracle-core binary. +Run +```console +oracle-core 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 + +were printed at the end of the `prepare-update` command. + +This will submit an update tx. +After the update tx is confirmed, use `oracle_config_updated.yaml` to run the oracle (i.e., rename it to `oracle_config.yaml` and restart the oracle) From 92fb78d626da729c920f7bbafc9da6d54b4d2f6f Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Mon, 28 Nov 2022 09:27:44 +0200 Subject: [PATCH 3/5] update `update-pool` command instructions in update_epoch_length.md; --- docs/update_epoch_length.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/update_epoch_length.md b/docs/update_epoch_length.md index 2334e64a..0fe13de1 100644 --- a/docs/update_epoch_length.md +++ b/docs/update_epoch_length.md @@ -68,4 +68,4 @@ Where: were printed at the end of the `prepare-update` command. This will submit an update tx. -After the update tx is confirmed, use `oracle_config_updated.yaml` to run the oracle (i.e., rename it to `oracle_config.yaml` and restart the oracle) +After the update tx is confirmed, remove `scanIds.json` and use `oracle_config_updated.yaml` to run the oracle (i.e., rename it to `oracle_config.yaml` and restart the oracle). Distribute the new oracle config file (with zeroed credentials - node_api_key, node_ip, oracle_address, etc) to all the oracles and keep in mind that they have to set their own requisites in the received config. Be sure they delete `scanIds.json` before restart. From ec919a72b5513ebbb9a10760c54090efbec0f2af Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Mon, 28 Nov 2022 09:32:44 +0200 Subject: [PATCH 4/5] update README with new instructions from update_epoch_length.md; --- README.md | 11 +++++++++-- docs/update_epoch_length.md | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6ad162fb..b9ea360f 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,9 @@ Changes to the contract(parameters)/tokens can be done in three steps: Each of the step is described below. See also a detailed instruction on [Updating the epoch length](docs/update_epoch_length.md) ### Create a new refresh box with `prepare-update` command -Create a YAML file describing what contract parameters should be updated and run +Create a YAML file describing what contract parameters should be updated. +See also an example of such YAML file at [Updating the epoch length](docs/update_epoch_length.md) +Run: ```console oracle-core prepare-update ``` @@ -119,6 +121,10 @@ Where: - - 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. + +Keep in mind the REWARD_TOKEN_AMOUNT depends on when(in which epoch) the final `update-pool` command will be run. + ### Update the pool box contract with `update-pool` command Make sure the `oracle_config_updated.yaml` config file generated during the `prepare-update` command is in the same folder as the oracle-core binary. Run @@ -135,7 +141,8 @@ Where: - 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, use `oracle_config_updated.yaml` to run the oracle (i.e., rename it to `oracle_config.yaml` and restart the oracle) +This will submit an update tx. +After the update tx is confirmed, remove `scanIds.json` and use `oracle_config_updated.yaml` to run the oracle (i.e., rename it to `oracle_config.yaml` and restart the oracle). Distribute the new oracle config file (with zeroed credentials - node_api_key, node_ip, oracle_address, etc) to all the oracles and keep in mind that they have to set their own requisites in the received config. Be sure they delete `scanIds.json` before restart. ## How to run as systemd daemon To run oracle-core as a systemd unit, the unit file in [systemd/oracle-core.service](systemd/oracle-core.service) should be installed. diff --git a/docs/update_epoch_length.md b/docs/update_epoch_length.md index 0fe13de1..509d0b29 100644 --- a/docs/update_epoch_length.md +++ b/docs/update_epoch_length.md @@ -50,7 +50,7 @@ Where: - - reward token amount in the pool box at the time of update transaction is committed - - The creation height of the existing update box. -are printed in the output of the `prepare-update` command. +and are printed in the output of the `prepare-update` command. Keep in mind the REWARD_TOKEN_AMOUNT depends on when(in which epoch) the final `update-pool` command will be run. From 34cfeaad6f11611fb0737e2db985bc45b4ca50b0 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Tue, 29 Nov 2022 12:14:02 +0200 Subject: [PATCH 5/5] mint refresh NFT in a separate tx on prepare-update #180; --- core/src/cli_commands/prepare_update.rs | 83 ++++++++++++++++++++----- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/core/src/cli_commands/prepare_update.rs b/core/src/cli_commands/prepare_update.rs index 6c815e53..0a230c5b 100644 --- a/core/src/cli_commands/prepare_update.rs +++ b/core/src/cli_commands/prepare_update.rs @@ -36,7 +36,10 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use crate::{ - box_kind::{PoolBox, PoolBoxWrapperInputs, RefreshBoxWrapperInputs, UpdateBoxWrapperInputs}, + box_kind::{ + make_refresh_box_candidate, PoolBox, PoolBoxWrapperInputs, RefreshBoxWrapperInputs, + UpdateBoxWrapperInputs, + }, contracts::{ pool::{PoolContractError, PoolContractParameters}, refresh::{ @@ -203,7 +206,7 @@ fn perform_update_chained_transaction( let mut num_transactions_left = 1; if config.refresh_contract_parameters.is_some() { - num_transactions_left += 1; + num_transactions_left += 2; // refresh NFT mint tx + new refresh box tx } if config.update_contract_parameters.is_some() { num_transactions_left += 1; @@ -341,34 +344,84 @@ fn perform_update_chained_transaction( transactions.push(tx); } if let Some(ref contract_parameters) = config.refresh_contract_parameters { - info!("Creating new refresh NFT"); - let refresh_contract_inputs = RefreshContractInputs::build_with( - contract_parameters.clone(), - new_oracle_config.token_ids.oracle_token_id.clone(), - old_config.token_ids.pool_nft_token_id, - )?; - let refresh_contract = RefreshContract::checked_load(&refresh_contract_inputs)?; + info!("Creating new refresh NFT and refresh box"); let refresh_nft_details = config .tokens_to_mint .refresh_nft .ok_or(PrepareUpdateError::NoMintDetails)?; - let (token, tx) = mint_token( + let (refresh_nft_token, mint_refresh_nft_tx) = mint_token( inputs.into(), &mut num_transactions_left, refresh_nft_details.name.clone(), refresh_nft_details.description.clone(), 1.try_into().unwrap(), - Some(refresh_contract.ergo_tree()), + None, )?; new_oracle_config.token_ids.refresh_nft_token_id = - RefreshTokenId::from_token_id_unchecked(token.token_id.clone()); + RefreshTokenId::from_token_id_unchecked(refresh_nft_token.token_id.clone()); + inputs = filter_tx_outputs(mint_refresh_nft_tx.outputs.clone()) + .try_into() + .unwrap(); + info!("Refresh NFT minting tx id: {:?}", mint_refresh_nft_tx.id()); + transactions.push(mint_refresh_nft_tx); + + // Create refresh box -------------------------------------------------------------------------- + info!("Create and sign refresh box tx"); + + let refresh_contract_inputs = RefreshContractInputs::build_with( + contract_parameters.clone(), + new_oracle_config.token_ids.oracle_token_id.clone(), + old_config.token_ids.pool_nft_token_id, + )?; + let refresh_contract = RefreshContract::checked_load(&refresh_contract_inputs)?; new_oracle_config.refresh_box_wrapper_inputs = RefreshBoxWrapperInputs { contract_inputs: refresh_contract_inputs, refresh_nft_token_id: new_oracle_config.token_ids.refresh_nft_token_id.clone(), }; - inputs = filter_tx_outputs(tx.outputs.clone()).try_into().unwrap(); - info!("Refresh contract tx id: {:?}", tx.id()); - transactions.push(tx); + + let refresh_box_candidate = make_refresh_box_candidate( + &refresh_contract, + refresh_nft_token.clone(), + erg_value_per_box, + height, + )?; + + let target_balance = calc_target_balance(num_transactions_left)?; + + let box_selection = SimpleBoxSelector::new().select( + inputs.as_vec().clone(), + target_balance, + &[refresh_nft_token.clone()], + )?; + + let mut output_candidates = vec![refresh_box_candidate]; + let remaining_funds = ErgoBoxCandidateBuilder::new( + calc_target_balance(num_transactions_left - 1)?, + wallet_pk_ergo_tree.clone(), + height, + ) + .build()?; + output_candidates.push(remaining_funds.clone()); + + let tx_builder = TxBuilder::new( + box_selection.clone(), + output_candidates, + height, + tx_fee, + change_address.clone(), + ); + let refresh_box_tx = tx_builder.build()?; + let signed_refresh_box_tx = wallet_sign.sign_transaction_with_inputs( + &refresh_box_tx, + box_selection.boxes.clone(), + None, + )?; + inputs = filter_tx_outputs(signed_refresh_box_tx.outputs.clone()) + .try_into() + .unwrap(); + info!("Refresh box tx id: {:?}", signed_refresh_box_tx.id()); + transactions.push(signed_refresh_box_tx); + num_transactions_left -= 1; } if let Some(ref contract_parameters) = config.update_contract_parameters { info!("Creating new update NFT");