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

feat: expose validator information as host functions #2504

Merged
merged 18 commits into from
May 21, 2020
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
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion chain/chain/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use chrono::{DateTime, Utc};
use failure::{Backtrace, Context, Fail};

use near_primitives::challenge::{ChunkProofs, ChunkState};
use near_primitives::errors::StorageError;
use near_primitives::errors::{EpochError, StorageError};
use near_primitives::hash::CryptoHash;
use near_primitives::serialize::to_base;
use near_primitives::sharding::{ChunkHash, ShardChunkHeader};
use near_primitives::types::ShardId;

Expand Down Expand Up @@ -284,3 +285,14 @@ impl From<String> for Error {
}

impl std::error::Error for Error {}

impl From<EpochError> for Error {
fn from(error: EpochError) -> Self {
match error {
EpochError::EpochOutOfBounds => ErrorKind::EpochOutOfBounds,
EpochError::MissingBlock(h) => ErrorKind::DBNotFoundErr(to_base(&h)),
err => ErrorKind::ValidatorError(err.to_string()),
}
.into()
}
}
10 changes: 5 additions & 5 deletions chain/epoch_manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use cached::{Cached, SizedCache};
use log::{debug, warn};
use primitive_types::U256;

use near_primitives::errors::EpochError;
use near_primitives::hash::CryptoHash;
use near_primitives::types::{
AccountId, ApprovalStake, Balance, BlockChunkValidatorStats, BlockHeight, EpochId, ShardId,
Expand All @@ -18,9 +19,8 @@ use near_store::{ColBlockInfo, ColEpochInfo, ColEpochStart, Store, StoreUpdate};

use crate::proposals::proposals_to_epoch_info;
pub use crate::reward_calculator::RewardCalculator;
use crate::types::EpochError::EpochOutOfBounds;
use crate::types::EpochSummary;
pub use crate::types::{BlockInfo, EpochConfig, EpochError, EpochInfo, RngSeed, SlashState};
pub use crate::types::{BlockInfo, EpochConfig, EpochInfo, RngSeed, SlashState};

mod proposals;
mod reward_calculator;
Expand Down Expand Up @@ -310,8 +310,8 @@ impl EpochManager {
minted_amount,
) {
Ok(next_next_epoch_info) => next_next_epoch_info,
Err(EpochError::ThresholdError(amount, num_seats)) => {
warn!(target: "epoch_manager", "Not enough stake for required number of seats (all validators tried to unstake?): amount = {} for {}", amount, num_seats);
Err(EpochError::ThresholdError { stake_sum, num_seats }) => {
warn!(target: "epoch_manager", "Not enough stake for required number of seats (all validators tried to unstake?): amount = {} for {}", stake_sum, num_seats);
let mut epoch_info = next_epoch_info.clone();
epoch_info.epoch_height += 1;
epoch_info
Expand Down Expand Up @@ -874,7 +874,7 @@ impl EpochManager {
(Ok(index1), Ok(index2)) => Ok(index1.cmp(&index2)),
(Ok(_), Err(_)) => self.get_epoch_info(other_epoch_id).map(|_| Ordering::Less),
(Err(_), Ok(_)) => self.get_epoch_info(epoch_id).map(|_| Ordering::Greater),
(Err(_), Err(_)) => Err(EpochOutOfBounds),
(Err(_), Err(_)) => Err(EpochError::EpochOutOfBounds),
}
}
}
Expand Down
11 changes: 6 additions & 5 deletions chain/epoch_manager/src/proposals.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::iter;

use near_primitives::errors::EpochError;
use near_primitives::types::{
AccountId, Balance, NumSeats, ValidatorId, ValidatorKickoutReason, ValidatorStake,
};

use crate::types::{EpochConfig, EpochError, EpochInfo, RngSeed};
use crate::types::{EpochConfig, EpochInfo, RngSeed};

/// Find threshold of stake per seat, given provided stakes and required number of seats.
fn find_threshold(stakes: &[Balance], num_seats: NumSeats) -> Result<Balance, EpochError> {
let stakes_sum: Balance = stakes.iter().sum();
if stakes_sum < num_seats.into() {
return Err(EpochError::ThresholdError(stakes_sum, num_seats));
let stake_sum: Balance = stakes.iter().sum();
if stake_sum < num_seats.into() {
return Err(EpochError::ThresholdError { stake_sum, num_seats });
}
let (mut left, mut right): (Balance, Balance) = (1, stakes_sum + 1);
let (mut left, mut right): (Balance, Balance) = (1, stake_sum + 1);
'outer: loop {
if left == right - 1 {
break Ok(left);
Expand Down
72 changes: 0 additions & 72 deletions chain/epoch_manager/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use std::collections::{BTreeMap, HashMap};
use std::{fmt, io};

use borsh::{BorshDeserialize, BorshSerialize};
use num_rational::Rational;
use serde::Serialize;

use near_primitives::challenge::SlashedValidator;
use near_primitives::hash::CryptoHash;
use near_primitives::serialize::to_base;
use near_primitives::types::{
AccountId, Balance, BlockChunkValidatorStats, BlockHeight, BlockHeightDelta, EpochHeight,
EpochId, NumSeats, NumShards, ShardId, ValidatorId, ValidatorKickoutReason, ValidatorStake,
Expand Down Expand Up @@ -190,76 +188,6 @@ impl BlockInfo {
}
}

#[derive(Eq, PartialEq)]
pub enum EpochError {
/// Error calculating threshold from given stakes for given number of seats.
/// Only should happened if calling code doesn't check for integer value of stake > number of seats.
ThresholdError(Balance, u64),
/// Requesting validators for an epoch that wasn't computed yet.
EpochOutOfBounds,
/// Missing block hash in the storage (means there is some structural issue).
MissingBlock(CryptoHash),
/// Error due to IO (DB read/write, serialization, etc.).
IOErr(String),
/// Given account ID is not a validator in the given epoch ID.
NotAValidator(AccountId, EpochId),
}

impl std::error::Error for EpochError {}

impl fmt::Debug for EpochError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EpochError::ThresholdError(stakes_sum, num_seats) => write!(
f,
"Total stake {} must be higher than the number of seats {}",
stakes_sum, num_seats
),
EpochError::EpochOutOfBounds => write!(f, "Epoch out of bounds"),
EpochError::MissingBlock(hash) => write!(f, "Missing block {}", hash),
EpochError::IOErr(err) => write!(f, "IO: {}", err),
EpochError::NotAValidator(account_id, epoch_id) => {
write!(f, "{} is not a validator in epoch {:?}", account_id, epoch_id)
}
}
}
}

impl fmt::Display for EpochError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EpochError::ThresholdError(stake, num_seats) => {
write!(f, "ThresholdError({}, {})", stake, num_seats)
}
EpochError::EpochOutOfBounds => write!(f, "EpochOutOfBounds"),
EpochError::MissingBlock(hash) => write!(f, "MissingBlock({})", hash),
EpochError::IOErr(err) => write!(f, "IOErr({})", err),
EpochError::NotAValidator(account_id, epoch_id) => {
write!(f, "NotAValidator({}, {:?})", account_id, epoch_id)
}
}
}
}

impl From<io::Error> for EpochError {
fn from(error: io::Error) -> Self {
EpochError::IOErr(error.to_string())
}
}

impl From<EpochError> for near_chain::Error {
fn from(error: EpochError) -> Self {
match error {
EpochError::EpochOutOfBounds => near_chain::ErrorKind::EpochOutOfBounds,
EpochError::MissingBlock(h) => near_chain::ErrorKind::DBNotFoundErr(to_base(&h)),
EpochError::IOErr(err) => near_chain::ErrorKind::IOErr(err),
EpochError::NotAValidator(_, _) => near_chain::ErrorKind::NotAValidator,
err => near_chain::ErrorKind::ValidatorError(err.to_string()),
}
.into()
}
}

pub struct EpochSummary {
pub prev_epoch_last_block_hash: CryptoHash,
// Proposals from the epoch, only the latest one per account
Expand Down
2 changes: 1 addition & 1 deletion core/chain-configs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ pub use genesis_config::{
};

/// Current latest version of the protocol
pub const PROTOCOL_VERSION: u32 = 15;
pub const PROTOCOL_VERSION: u32 = 16;
76 changes: 72 additions & 4 deletions core/primitives/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use crate::serialize::u128_dec_format;
use crate::types::{AccountId, Balance, Gas, Nonce};
use crate::types::{AccountId, Balance, EpochId, Gas, Nonce};
use borsh::{BorshDeserialize, BorshSerialize};
use near_crypto::PublicKey;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::fmt::{Debug, Display};

use crate::hash::CryptoHash;
use near_rpc_error_macro::RpcError;
use near_vm_errors::FunctionCallError;
use near_vm_errors::{FunctionCallError, VMLogicError};

/// Error returned in the ExecutionOutcome in case of failure
#[derive(
Expand Down Expand Up @@ -55,6 +56,8 @@ pub enum RuntimeError {
BalanceMismatchError(BalanceMismatchError),
/// The incoming receipt didn't pass the validation, it's likely a malicious behaviour.
ReceiptValidationError(ReceiptValidationError),
/// Error when accessing validator information. Happens inside epoch manager.
ValidatorError(EpochError),
}

/// Error used by `RuntimeExt`. This error has to be serializable, because it's transferred through
Expand All @@ -64,6 +67,14 @@ pub enum ExternalError {
/// Unexpected error which is typically related to the node storage corruption.
/// It's possible the input state is invalid or malicious.
StorageError(StorageError),
/// Error when accessing validator information. Happens inside epoch manager.
ValidatorError(EpochError),
}

impl From<ExternalError> for VMLogicError {
fn from(err: ExternalError) -> Self {
VMLogicError::ExternalError(err.try_to_vec().expect("Borsh serialize cannot fail"))
}
}

/// Internal
Expand Down Expand Up @@ -368,7 +379,7 @@ impl Display for InvalidTxError {
InvalidTxError::SignerDoesNotExist { signer_id } => {
write!(f, "Signer {:?} does not exist", signer_id)
}
InvalidTxError::InvalidAccessKeyError(access_key_error) => access_key_error.fmt(f),
InvalidTxError::InvalidAccessKeyError(access_key_error) => Display::fmt(&access_key_error, f),
InvalidTxError::InvalidNonce { tx_nonce, ak_nonce } => write!(
f,
"Transaction nonce {} must be larger than nonce of the used access key {}",
Expand Down Expand Up @@ -634,3 +645,60 @@ impl Display for ActionErrorKind {
}
}
}

#[derive(Eq, PartialEq, BorshSerialize, BorshDeserialize, Clone)]
pub enum EpochError {
/// Error calculating threshold from given stakes for given number of seats.
/// Only should happened if calling code doesn't check for integer value of stake > number of seats.
ThresholdError { stake_sum: Balance, num_seats: u64 },
/// Requesting validators for an epoch that wasn't computed yet.
EpochOutOfBounds,
/// Missing block hash in the storage (means there is some structural issue).
MissingBlock(CryptoHash),
/// Error due to IO (DB read/write, serialization, etc.).
IOErr(String),
/// Given account ID is not a validator in the given epoch ID.
NotAValidator(AccountId, EpochId),
}

impl std::error::Error for EpochError {}

impl Display for EpochError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EpochError::ThresholdError { stake_sum, num_seats } => write!(
f,
"Total stake {} must be higher than the number of seats {}",
stake_sum, num_seats
),
EpochError::EpochOutOfBounds => write!(f, "Epoch out of bounds"),
EpochError::MissingBlock(hash) => write!(f, "Missing block {}", hash),
EpochError::IOErr(err) => write!(f, "IO: {}", err),
EpochError::NotAValidator(account_id, epoch_id) => {
write!(f, "{} is not a validator in epoch {:?}", account_id, epoch_id)
}
}
}
}

impl Debug for EpochError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EpochError::ThresholdError { stake_sum, num_seats } => {
write!(f, "ThresholdError({}, {})", stake_sum, num_seats)
bowenwang1996 marked this conversation as resolved.
Show resolved Hide resolved
}
EpochError::EpochOutOfBounds => write!(f, "EpochOutOfBounds"),
EpochError::MissingBlock(hash) => write!(f, "MissingBlock({})", hash),
EpochError::IOErr(err) => write!(f, "IOErr({})", err),
EpochError::NotAValidator(account_id, epoch_id) => {
write!(f, "NotAValidator({}, {:?})", account_id, epoch_id)
}
}
}
}

impl From<std::io::Error> for EpochError {
fn from(error: std::io::Error) -> Self {
EpochError::IOErr(error.to_string())
}
}
Loading