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

TooManyWitnesses RAD error tally #2446

Closed
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
13 changes: 11 additions & 2 deletions data_structures/src/chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3589,6 +3589,8 @@ pub struct DataRequestInfo {
pub current_reveal_round: u16,
/// Current stage, or None if finished
pub current_stage: Option<DataRequestStage>,
/// Too many witnesses requested
pub too_many_witnesses: bool,
}

impl Default for DataRequestInfo {
Expand All @@ -3602,6 +3604,7 @@ impl Default for DataRequestInfo {
current_commit_round: 0,
current_reveal_round: 0,
current_stage: Some(DataRequestStage::COMMIT),
too_many_witnesses: false,
}
}
}
Expand Down Expand Up @@ -3693,14 +3696,20 @@ impl DataRequestState {
}
}

pub fn set_too_many_witnesses(&mut self) {
self.info.too_many_witnesses = true;
}

/// Advance to the next stage.
/// Since the data requests are updated by looking at the transactions from a valid block,
/// the only issue would be that there were no commits in that block.
pub fn update_stage(&mut self, extra_rounds: u16) {
pub fn update_stage(&mut self, extra_rounds: u16, too_many_witnesses: bool) {
self.stage = match self.stage {
DataRequestStage::COMMIT => {
if self.info.commits.is_empty() {
if self.info.current_commit_round <= extra_rounds {
if too_many_witnesses {
DataRequestStage::TALLY
} else if self.info.current_commit_round <= extra_rounds {
self.info.current_commit_round += 1;
DataRequestStage::COMMIT
} else {
Expand Down
22 changes: 20 additions & 2 deletions data_structures/src/data_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ impl DataRequestPool {
/// for reveal (the node should send a reveal transaction).
/// This function must be called after `add_data_requests_from_block`, in order to update
/// the stage of all the data requests.
pub fn update_data_request_stages(&mut self) -> Vec<RevealTransaction> {
pub fn update_data_request_stages(&mut self, validator_count: usize) -> Vec<RevealTransaction> {
let waiting_for_reveal = &mut self.waiting_for_reveal;
let data_requests_by_epoch = &mut self.data_requests_by_epoch;
let extra_rounds = self.extra_rounds;
Expand All @@ -252,7 +252,9 @@ impl DataRequestPool {
.filter_map(|(dr_pointer, dr_state)| {
// We can notify the user that a data request from "my_claims" is available
// for reveal.
dr_state.update_stage(extra_rounds);
let too_many_witnesses =
data_request_has_too_many_witnesses(&dr_state.data_request, validator_count);
dr_state.update_stage(extra_rounds, too_many_witnesses);
match dr_state.stage {
DataRequestStage::REVEAL => {
// When a data request changes from commit stage to reveal stage, it should
Expand Down Expand Up @@ -368,6 +370,14 @@ impl DataRequestPool {
self.data_request_pool.get(dr_pointer)
}

/// Get the detailed state of a data request.
pub fn data_request_state_mutable(
&mut self,
dr_pointer: &Hash,
) -> Option<&mut DataRequestState> {
self.data_request_pool.get_mut(dr_pointer)
}

/// Get the data request info of the finished data requests, to be persisted to the storage
pub fn finished_data_requests(&mut self) -> Vec<DataRequestInfo> {
std::mem::take(&mut self.to_be_stored)
Expand Down Expand Up @@ -525,6 +535,14 @@ pub fn calculate_reward_collateral_ratio(
saturating_div_ceil(dr_collateral, witness_reward)
}

/// Function to check if a data request requires too many witnesses
pub fn data_request_has_too_many_witnesses(
dr_output: &DataRequestOutput,
validator_count: usize,
) -> bool {
usize::from(dr_output.witnesses) > validator_count / 4
}

/// Saturating version of `u64::div_ceil`.
///
/// Calculates the quotient of `lhs` and `rhs`, rounding the result towards positive infinity.
Expand Down
2 changes: 2 additions & 0 deletions data_structures/src/radon_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ pub enum RadonErrors {
InsufficientCommits = 0x52,
/// Generic error during tally execution
TallyExecution = 0x53,
/// Insufficient stakers to resolve the data request
TooManyWitnesses = 0x54,
/// Invalid reveal serialization (malformed reveals are converted to this value)
MalformedReveal = 0x60,
/// Failed to encode reveal
Expand Down
5 changes: 5 additions & 0 deletions data_structures/src/staking/stakes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,11 @@ where
}
}

/// Return the total number of validators.
pub fn validator_count(&self) -> usize {
self.by_validator.len()
}

/// Query stakes by stake key.
#[inline(always)]
fn query_by_key(&self, key: StakeKey<Address>) -> StakingResult<Coins, Address, Coins, Epoch> {
Expand Down
66 changes: 60 additions & 6 deletions node/src/actors/chain_manager/mining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ use witnet_data_structures::{
tapi::{after_second_hard_fork, ActiveWips},
Block, BlockHeader, BlockMerkleRoots, BlockTransactions, Bn256PublicKey, CheckpointBeacon,
CheckpointVRF, DataRequestOutput, EpochConstants, Hash, Hashable, Input, PublicKeyHash,
TransactionsPool, ValueTransferOutput,
RADTally, TransactionsPool, ValueTransferOutput,
},
data_request::{
calculate_witness_reward, calculate_witness_reward_before_second_hard_fork, create_tally,
DataRequestPool,
data_request_has_too_many_witnesses, DataRequestPool,
},
error::TransactionError,
get_environment, get_protocol_version,
Expand All @@ -56,7 +56,8 @@ use witnet_util::timestamp::get_timestamp;
use witnet_validations::validations::{
block_reward, calculate_liars_and_errors_count_from_tally, calculate_mining_probability,
calculate_randpoe_threshold, calculate_reppoe_threshold, dr_transaction_fee, merkle_tree_root,
st_transaction_fee, tally_bytes_on_encode_error, update_utxo_diff, vt_transaction_fee,
run_tally, st_transaction_fee, tally_bytes_on_encode_error, update_utxo_diff,
vt_transaction_fee,
};

use crate::{
Expand Down Expand Up @@ -156,6 +157,8 @@ impl ChainManager {
block_epoch: current_epoch,
};

let validator_count = self.chain_state.stakes.validator_count();

// Create a VRF proof and if eligible build block
signature_mngr::vrf_prove(VrfMessage::block_mining(vrf_input))
.map(move |res| {
Expand Down Expand Up @@ -228,11 +231,12 @@ impl ChainManager {
};

// Build the block using the supplied beacon and eligibility proof
let block_number = act.chain_state.block_number();
let (block_header, txns) = build_block(
(
&mut act.transactions_pool,
&act.chain_state.unspent_outputs_pool,
&act.chain_state.data_request_pool,
&mut act.chain_state.data_request_pool,
),
max_vt_weight,
max_dr_weight,
Expand All @@ -242,7 +246,7 @@ impl ChainManager {
&tally_transactions,
own_pkh,
epoch_constants,
act.chain_state.block_number(),
block_number,
collateral_minimum,
bn256_public_key,
act.external_address,
Expand All @@ -251,6 +255,7 @@ impl ChainManager {
halving_period,
tapi_version,
&active_wips,
validator_count,
);

// Sign the block hash
Expand All @@ -271,6 +276,7 @@ impl ChainManager {
vrf_input,
beacon,
epoch_constants,
validator_count,
)
.map_ok(|_diff, act, _ctx| {
// Send AddCandidates message to self
Expand Down Expand Up @@ -756,6 +762,7 @@ impl ChainManager {
script: dr_state.data_request.data_request.tally.clone(),
commits_count,
active_wips: active_wips_inside_move.clone(),
too_many_witnesses: false,
})
.await
.unwrap_or_else(|e| {
Expand Down Expand Up @@ -822,7 +829,11 @@ impl ChainManager {
/// TODO: simplify function signature, e.g. through merging multiple related fields into new data structures.
#[allow(clippy::too_many_arguments)]
pub fn build_block(
pools_ref: (&mut TransactionsPool, &UnspentOutputsPool, &DataRequestPool),
pools_ref: (
&mut TransactionsPool,
&UnspentOutputsPool,
&mut DataRequestPool,
),
max_vt_weight: u32,
max_dr_weight: u32,
max_st_weight: u32,
Expand All @@ -840,6 +851,7 @@ pub fn build_block(
halving_period: u32,
tapi_signals: u32,
active_wips: &ActiveWips,
validator_count: usize,
) -> (BlockHeader, BlockTransactions) {
let (transactions_pool, unspent_outputs_pool, dr_pool) = pools_ref;
let epoch = beacon.checkpoint;
Expand Down Expand Up @@ -992,6 +1004,48 @@ pub fn build_block(
dr_tx.hash(),
);

// Number of data request witnesses should be at most the number of validators divided by four
if data_request_has_too_many_witnesses(&dr_tx.body.dr_output, validator_count) {
log::debug!("Data request {} has too many witnesses", dr_tx.hash());

// Temporarily insert the data request into the dr_pool
if let Err(e) = dr_pool.process_data_request(dr_tx, epoch, &Hash::default()) {
log::error!("Error adding data request to the data request pool: {}", e);
}
if let Some(dr_state) = dr_pool.data_request_state_mutable(&dr_tx.hash()) {
dr_state.update_stage(0, true);
} else {
log::error!("Could not find data request state");
}

// The result of `RunTally` will be published as tally
let tally_script = RADTally {
filters: vec![],
reducer: 0,
};
let tally_result = run_tally(
vec![], // reveals
&tally_script,
0.0, // minimum consensus percentage
0, // commit count
active_wips,
true, // too many witnesses
);

let pkh = dr_tx.signatures[0].public_key.pkh();
tally_txns.push(create_tally(
dr_tx.hash(),
&dr_tx.body.dr_output,
pkh,
&tally_result,
vec![], // No revealers
HashSet::new(), // No committers
collateral_minimum,
tally_bytes_on_encode_error(),
active_wips,
));
}

data_request_txns.push(dr_tx.clone());
transaction_fees = transaction_fees.saturating_add(transaction_fee);
dr_weight = new_dr_weight;
Expand Down
Loading
Loading