Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make vote-update reward token parameter optional #228

Merged
merged 15 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 19 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,44 +154,47 @@ The output shows the new pool box contract hash and reward tokens amounts for th
Run

```console
oracle-core vote-update-pool <NEW_POOL_BOX_ADDRESS_HASH_STR> <REWARD_TOKEN_ID_STR> <REWARD_TOKEN_AMOUNT> <UPDATE_BOX_CREATION_HEIGHT>
oracle-core vote-update-pool <NEW_POOL_BOX_ADDRESS_HASH_STR> <UPDATE_BOX_CREATION_HEIGHT>
```

Where:

- <NEW_POOL_BOX_ADDRESS_HASH_STR> - base16-encoded blake2b hash of the serialized pool box contract for the new pool box
- <REWARD_TOKEN_ID_STR> - base16-encoded reward token id in the new pool box (use existing if unchanged)
- <REWARD_TOKEN_AMOUNT> - reward token amount in the pool box at the time of update transaction is committed
- <UPDATE_BOX_CREATION_HEIGHT> - 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):

- <REWARD_TOKEN_ID_STR> - base16-encoded reward token id in the new pool box (use existing if unchanged)
- <REWARD_TOKEN_AMOUNT> - 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

Make sure the `pool_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
oracle-core update-pool
```

to see the diff for the tokens.
With optional(only if minted) parameters:
<REWARD_TOKEN_ID_STR> - base16-encoded reward token id in the new pool box (only if minted)
<REWARD_TOKEN_AMOUNT> - 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 <NEW_POOL_BOX_ADDRESS_HASH_STR> <REWARD_TOKEN_ID_STR> <REWARD_TOKEN_AMOUNT>
oracle-core update-pool pool_config_updated.yaml
```

Where:
<NEW_POOL_BOX_ADDRESS_HASH_STR> - base16-encoded blake2b hash of the serialized pool box contract for the new pool box
<REWARD_TOKEN_ID_STR> - base16-encoded reward token id in the new pool box (use existing if unchanged)
<REWARD_TOKEN_AMOUNT> - 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

Expand Down
61 changes: 40 additions & 21 deletions core/src/box_kind/ballot_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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,
Expand All @@ -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")]
Expand All @@ -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 {
Expand Down Expand Up @@ -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<SpecToken<RewardTokenId>>,
pub update_box_creation_height: i32,
}

/// A Ballot Box with vote parameters guaranteed to be set
#[derive(Clone, Debug)]
pub struct VoteBallotBoxWrapper {
Expand Down Expand Up @@ -169,21 +173,31 @@ impl VoteBallotBoxWrapper {
.ok_or(BallotBoxError::NoPoolBoxAddressInR6)?
.try_extract_into::<Digest32>()?;

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::<TokenId>()?;
let reward_token_quantity = ergo_box
.map(|c| c.try_extract_into::<TokenId>());
let reward_token_quantity_opt = ergo_box
.get_register(NonMandatoryRegisterId::R8.into())
.ok_or(BallotBoxError::NoRewardTokenQuantityInR8)?
.try_extract_into::<i64>()? as u64;
.map(|c| c.try_extract_into::<i64>());

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 {
Expand Down Expand Up @@ -269,7 +283,7 @@ pub fn make_local_ballot_box_candidate(
update_box_creation_height: BlockHeight,
ballot_token: SpecToken<BallotTokenId>,
pool_box_address_hash: Digest32,
reward_tokens: Token,
reward_token_opt: Option<SpecToken<RewardTokenId>>,
value: BoxValue,
creation_height: BlockHeight,
) -> Result<ErgoBoxCandidate, ErgoBoxCandidateBuilderError> {
Expand All @@ -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()
}
5 changes: 2 additions & 3 deletions core/src/box_kind/pool_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -229,14 +228,14 @@ pub fn make_pool_box_candidate_unchecked(
datapoint: i64,
epoch_counter: EpochCounter,
pool_nft_token: SpecToken<PoolTokenId>,
reward_token: Token,
reward_token: SpecToken<RewardTokenId>,
value: BoxValue,
creation_height: BlockHeight,
) -> Result<ErgoBoxCandidate, ErgoBoxCandidateBuilderError> {
let mut builder = ErgoBoxCandidateBuilder::new(value, contract.ergo_tree(), creation_height.0);
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()
}
15 changes: 0 additions & 15 deletions core/src/cli_commands.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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
)
}
7 changes: 0 additions & 7 deletions core/src/cli_commands/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,6 @@ pub(crate) mod tests {
ergotree_ir::chain::{
address::{AddressEncoder, NetworkAddress, NetworkPrefix},
ergo_box::{ErgoBox, NonMandatoryRegisters},
token::TokenId,
},
wallet::Wallet,
};
Expand Down Expand Up @@ -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<u8> = 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
Expand Down
5 changes: 3 additions & 2 deletions core/src/cli_commands/extract_reward_tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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 =
Expand All @@ -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,
)
Expand Down
12 changes: 6 additions & 6 deletions core/src/cli_commands/prepare_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -134,11 +132,12 @@ 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(height)?;
wait_for_txs_confirmation(submitted_tx_ids);
Ok(())
}

fn print_hints_for_voting(height: u32) -> Result<(), PrepareUpdateError> {
fn print_hints_for_voting(height: BlockHeight) -> Result<(), PrepareUpdateError> {
let epoch_length = POOL_CONFIG
.refresh_box_wrapper_inputs
.contract_inputs
Expand All @@ -150,15 +149,16 @@ fn print_hints_for_voting(height: u32) -> Result<(), PrepareUpdateError> {
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)
.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);
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())
Expand Down
5 changes: 3 additions & 2 deletions core/src/cli_commands/transfer_oracle_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -215,6 +215,7 @@ mod tests {
&token_ids,
BASE_FEE.checked_mul_u32(100).unwrap(),
BlockHeight(height.0) - 9,
1,
),
&oracle_box_wrapper_inputs,
)
Expand Down
Loading