Skip to content
This repository has been archived by the owner on Jul 27, 2022. It is now read-only.

Commit

Permalink
Problem (CRO-594): block results and "fast sync" are not verified in …
Browse files Browse the repository at this point in the history
…client

Solution:
- Store history hashes to `COL_APP_HASHS`, history state to `COL_APP_STATES`
- Moved ChainNodeState to chain-core because client needs to access it
- Add `query_state_batch` to rpc client, and verify block results with it.
- Make fast-forward syncing optional
- Improve BlockGenerator to pass manual synchronizer test
  • Loading branch information
yihuang committed Dec 4, 2019
1 parent 0fee695 commit 654dab6
Show file tree
Hide file tree
Showing 30 changed files with 624 additions and 234 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

124 changes: 20 additions & 104 deletions chain-abci/src/app/app_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ use std::convert::TryInto;

use abci::*;
use enclave_protocol::{EnclaveRequest, EnclaveResponse};
use integer_encoding::VarInt;
use kvdb::DBTransaction;
use log::{info, warn};
use parity_scale_codec::{Decode, Encode};
use protobuf::Message;
use serde::{Deserialize, Serialize};

use crate::enclave_bridge::EnclaveProxy;
use crate::liveness::LivenessTracker;
use crate::punishment::ValidatorPunishment;
use crate::storage::account::AccountWrapper;
use crate::storage::account::{pure_account_storage, AccountStorage};
use crate::storage::tx::get_account;
Expand All @@ -27,105 +25,12 @@ use chain_core::init::config::InitConfig;
use chain_core::init::config::NetworkParameters;
use chain_core::state::account::StakedStateDestination;
use chain_core::state::account::{CouncilNode, StakedState, StakedStateAddress};
use chain_core::state::tendermint::{BlockHeight, TendermintValidatorAddress, TendermintVotePower};
use chain_core::state::RewardsPoolState;
use chain_core::state::liveness::LivenessTracker;
use chain_core::state::punishment::ValidatorPunishment;
use chain_core::state::tendermint::{TendermintValidatorAddress, TendermintVotePower};
use chain_core::state::{ChainNodeState, ValidatorState};
use chain_core::tx::TxAux;

/// Validator state tracking
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Encode, Decode, Default)]
pub struct ValidatorState {
/// all nodes (current validator set + pending): TendermintVotePower == coin bonded amount if >= minimal
/// or TendermintVotePower == 0 if < minimal or was jailed
/// FIXME: delete node metadata if voting power == 0 for longer than unbonding time
pub council_nodes_by_power: BTreeMap<(TendermintVotePower, StakedStateAddress), CouncilNode>,
/// stores staking account address corresponding to tendermint validator addresses
/// FIXME: delete node metadata if voting power == 0 for longer than unbonding time
pub tendermint_validator_addresses: BTreeMap<TendermintValidatorAddress, StakedStateAddress>,
/// Runtime state for computing and executing validator punishment
pub punishment: ValidatorPunishment,
}

impl ValidatorState {
/// add validator for tracking if it wasn't added before
pub fn add_validator_for_tracking(
&mut self,
validator_address: TendermintValidatorAddress,
staking_address: StakedStateAddress,
block_signing_window: u16,
) {
if !self
.punishment
.validator_liveness
.contains_key(&validator_address)
{
self.tendermint_validator_addresses
.insert(validator_address.clone(), staking_address);
self.punishment.validator_liveness.insert(
validator_address,
LivenessTracker::new(block_signing_window),
);
}
}

/// remove from tracking liveness
pub fn remove_validator_from_tracking(
&mut self,
tendermint_address: &TendermintValidatorAddress,
) {
self.punishment
.validator_liveness
.remove(tendermint_address);
}
}

/// ABCI app state snapshot
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Encode, Decode)]
pub struct ChainNodeState {
/// last processed block height
pub last_block_height: BlockHeight,
/// last committed merkle root
pub last_apphash: H256,
/// time in previous block's header or genesis time
pub block_time: Timespec,
/// root hash of the sparse merkle patricia trie of staking account states
pub last_account_root_hash: StarlingFixedKey,
/// last rewards pool state
pub rewards_pool: RewardsPoolState,
/// Record how many block each validator proposed, used for rewards distribution,
/// cleared after rewards distributed
pub proposer_stats: BTreeMap<StakedStateAddress, u64>,
/// network parameters (fee policy, staking configuration etc.)
pub network_params: NetworkParameters,
/// state of validators (keys, voting power, punishments, rewards...)
#[serde(skip)]
pub validators: ValidatorState,
/// genesis time
pub genesis_time: Timespec,
}

impl ChainNodeState {
pub fn genesis(
genesis_apphash: H256,
genesis_time: Timespec,
last_account_root_hash: StarlingFixedKey,
rewards_pool: RewardsPoolState,
network_params: NetworkParameters,
validators: ValidatorState,
) -> Self {
ChainNodeState {
last_block_height: 0,
last_apphash: genesis_apphash,
block_time: genesis_time,
last_account_root_hash,
rewards_pool,
proposer_stats: BTreeMap::new(),
network_params,
validators,
genesis_time,
}
}
}

/// The global ABCI state
pub struct ChainNodeApp<T: EnclaveProxy> {
/// the underlying key-value storage (+ possibly some info in the future)
Expand Down Expand Up @@ -248,13 +153,24 @@ fn get_voting_power(
}
}

fn store_valid_genesis_state(genesis_state: &ChainNodeState, inittx: &mut DBTransaction) {
fn store_valid_genesis_state(
genesis_state: &ChainNodeState,
inittx: &mut DBTransaction,
save_history_states: bool,
) {
let encoded = genesis_state.encode();
inittx.put(COL_NODE_INFO, LAST_STATE_KEY, &encoded);
inittx.put(COL_EXTRA, b"init_chain_state", &encoded);
let encoded_height = i64::encode_var_vec(0);
inittx.put(COL_APP_HASHS, &encoded_height, &genesis_state.last_apphash);
if save_history_states {
inittx.put(COL_APP_STATES, &encoded_height, &encoded);
}
}

fn compute_accounts_root(account_storage: &mut AccountStorage, accounts: &[StakedState]) -> H256 {
pub fn compute_accounts_root(
account_storage: &mut AccountStorage,
accounts: &[StakedState],
) -> H256 {
let mut keys: Vec<_> = accounts.iter().map(StakedState::key).collect();
let wrapped: Vec<_> = accounts.iter().cloned().map(AccountWrapper).collect();
account_storage
Expand Down Expand Up @@ -575,7 +491,7 @@ impl<T: EnclaveProxy> ChainNodeApp<T> {
},
},
);
store_valid_genesis_state(&genesis_state, &mut inittx);
store_valid_genesis_state(&genesis_state, &mut inittx, self.tx_query_address.is_some());

let wr = self.storage.db.write(inittx);
if wr.is_err() {
Expand Down
13 changes: 7 additions & 6 deletions chain-abci/src/app/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,13 @@ impl<T: EnclaveProxy> ChainNodeApp<T> {
}
}

inittx.put(
COL_APP_STATES,
&i64::encode_var_vec(new_state.last_block_height),
&new_state.last_apphash,
);
inittx.put(COL_NODE_INFO, LAST_STATE_KEY, &new_state.encode());
let encoded_state = new_state.encode();
inittx.put(COL_NODE_INFO, LAST_STATE_KEY, &encoded_state);
let encoded_height = i64::encode_var_vec(new_state.last_block_height);
inittx.put(COL_APP_HASHS, &encoded_height, &new_state.last_apphash);
if self.tx_query_address.is_some() {
inittx.put(COL_APP_STATES, &encoded_height, &encoded_state);
}
let wr = self.storage.db.write(inittx);
if wr.is_err() {
panic!("db write error: {}", wr.err().unwrap());
Expand Down
8 changes: 4 additions & 4 deletions chain-abci/src/app/end_block.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::app::app_init::{get_validator_key, ChainNodeApp, ChainNodeState};
use crate::app::app_init::{get_validator_key, ChainNodeApp};
use crate::enclave_bridge::EnclaveProxy;
use abci::{Event, KVPair, RequestEndBlock, ResponseEndBlock, ValidatorUpdate};
use chain_core::common::TendermintEventType;
use chain_core::state::account::{CouncilNode, StakedStateAddress};
use chain_core::state::tendermint::{TendermintValidatorAddress, TendermintVotePower};
use chain_core::state::ChainNodeState;
use chain_tx_filter::BlockFilter;
use enclave_protocol::{EnclaveRequest, EnclaveResponse};
use protobuf::RepeatedField;
Expand Down Expand Up @@ -180,8 +181,6 @@ fn get_validator_updates(
#[cfg(test)]
mod tests {
use super::*;
use crate::app::app_init::ValidatorState;
use crate::punishment::ValidatorPunishment;
use crate::storage::tx::StarlingFixedKey;
use chain_core::common::H256;
use chain_core::init::address::RedeemAddress;
Expand All @@ -190,8 +189,9 @@ mod tests {
use chain_core::init::params::{
JailingParameters, NetworkParameters, RewardsParameters, SlashRatio, SlashingParameters,
};
use chain_core::state::punishment::ValidatorPunishment;
use chain_core::state::tendermint::TendermintValidatorPubKey;
use chain_core::state::RewardsPoolState;
use chain_core::state::{ChainNodeState, RewardsPoolState, ValidatorState};
use chain_core::tx::fee::{LinearFee, Milli};
use std::str::FromStr;

Expand Down
7 changes: 3 additions & 4 deletions chain-abci/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,18 @@ mod validate_tx;
use abci::*;
use log::info;

pub use self::app_init::{
get_validator_key, init_app_hash, ChainNodeApp, ChainNodeState, ValidatorState,
};
pub use self::app_init::{compute_accounts_root, get_validator_key, init_app_hash, ChainNodeApp};
use crate::enclave_bridge::EnclaveProxy;
use crate::slashing::SlashingSchedule;
use crate::storage::account::AccountStorage;
use crate::storage::account::AccountWrapper;
use crate::storage::tx::StarlingFixedKey;
use crate::storage::COL_TX_META;
use bit_vec::BitVec;
use chain_core::common::{TendermintEventKey, TendermintEventType};
use chain_core::state::account::{PunishmentKind, StakedState};
use chain_core::state::slashing::SlashingSchedule;
use chain_core::state::tendermint::{BlockHeight, TendermintValidatorAddress, TendermintVotePower};
use chain_core::state::ChainNodeState;
use chain_core::tx::data::input::TxoPointer;
use chain_core::tx::{TxAux, TxEnclaveAux};
use kvdb::{DBTransaction, KeyValueDB};
Expand Down
34 changes: 32 additions & 2 deletions chain-abci/src/app/query.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::ChainNodeApp;
use crate::app::ChainNodeState;
use crate::enclave_bridge::{mock::handle_enc_dec, EnclaveProxy};
use crate::storage::tx::get_account;
use crate::storage::*;
Expand All @@ -8,6 +9,7 @@ use chain_core::state::account::StakedStateAddress;
use chain_core::tx::data::{txid_hash, TXID_HASH_ID};
use integer_encoding::VarInt;
use parity_scale_codec::{Decode, Encode};
use serde_json;
use std::convert::TryFrom;

/// Generate generic ABCI ProofOp for the witness
Expand Down Expand Up @@ -82,13 +84,13 @@ impl<T: EnclaveProxy> ChainNodeApp<T> {
let app_hash = self
.storage
.db
.get(COL_APP_STATES, &i64::encode_var_vec(height))
.get(COL_APP_HASHS, &i64::encode_var_vec(height))
.unwrap()
.unwrap();
let data = self
.storage
.db
.get(COL_MERKLE_PROOFS, &app_hash[..])
.get(COL_MERKLE_PROOFS, &app_hash)
.unwrap()
.unwrap()
.to_vec();
Expand Down Expand Up @@ -152,6 +154,34 @@ impl<T: EnclaveProxy> ChainNodeApp<T> {
resp.code = 3;
}
}
"state" => {
if self.tx_query_address.is_none() {
resp.code = 1;
resp.log += "tx query address not set";
} else {
let value = self
.storage
.db
.get(COL_APP_STATES, &i64::encode_var_vec(_req.height));
match value {
Ok(Some(value)) => {
if let Ok(state) =
ChainNodeState::decode(&mut value.to_vec().as_slice())
{
resp.value =
serde_json::to_string(&state).unwrap().as_bytes().to_owned();
} else {
resp.log += "state decode failed";
resp.code = 2;
}
}
_ => {
resp.log += "state not found";
resp.code = 2;
}
}
}
}
_ => {
resp.log += "invalid path";
resp.code = 1;
Expand Down
3 changes: 0 additions & 3 deletions chain-abci/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
pub mod app;
pub mod enclave_bridge;
pub mod liveness;
pub mod punishment;
pub mod slashing;
pub mod storage;
8 changes: 5 additions & 3 deletions chain-abci/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ pub const COL_EXTRA: Option<u32> = Some(3);
pub const COL_NODE_INFO: Option<u32> = Some(4);
/// Column for seriliazed merkle tree: root hash => MerkleTree
pub const COL_MERKLE_PROOFS: Option<u32> = Some(5);
/// Column for tracking app states: height => root hash
pub const COL_APP_STATES: Option<u32> = Some(6);
/// Column for tracking app hashes: height => app hash
pub const COL_APP_HASHS: Option<u32> = Some(6);
/// Column for tracking app states: height => ChainNodeState, only available when tx_query_address set
pub const COL_APP_STATES: Option<u32> = Some(7);
/// Number of columns in DB
pub const NUM_COLUMNS: Option<u32> = Some(7);
pub const NUM_COLUMNS: Option<u32> = Some(8);

pub const CHAIN_ID_KEY: &[u8] = b"chain_id";
pub const GENESIS_APP_HASH_KEY: &[u8] = b"genesis_app_hash";
Expand Down
6 changes: 3 additions & 3 deletions chain-abci/tests/abci_app.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use abci::Application;
use abci::*;
use bit_vec::BitVec;
use chain_abci::app::*;
use chain_abci::enclave_bridge::mock::MockClient;
use chain_abci::punishment::ValidatorPunishment;
use chain_abci::storage::account::AccountStorage;
use chain_abci::storage::account::AccountWrapper;
use chain_abci::storage::tx::StarlingFixedKey;
Expand All @@ -24,11 +22,12 @@ use chain_core::state::account::{
StakedStateDestination, StakedStateOpAttributes, StakedStateOpWitness, UnbondTx,
WithdrawUnbondedTx,
};
use chain_core::state::punishment::ValidatorPunishment;
use chain_core::state::tendermint::{
TendermintValidatorAddress, TendermintValidatorPubKey, TendermintVotePower,
};
use chain_core::state::validator::NodeJoinRequestTx;
use chain_core::state::RewardsPoolState;
use chain_core::state::{ChainNodeState, RewardsPoolState, ValidatorState};
use chain_core::tx::fee::{LinearFee, Milli};
use chain_core::tx::witness::tree::RawPubkey;
use chain_core::tx::witness::EcdsaSignature;
Expand Down Expand Up @@ -212,6 +211,7 @@ fn previously_stored_hash_should_match() {
let decoded_gah = decode(example_hash).unwrap();
let mut genesis_app_hash = [0u8; HASH_SIZE_256];
genesis_app_hash.copy_from_slice(&decoded_gah[..]);

let mut inittx = db.transaction();
inittx.put(COL_NODE_INFO, GENESIS_APP_HASH_KEY, &genesis_app_hash);
inittx.put(
Expand Down
1 change: 1 addition & 0 deletions chain-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ static_assertions = { version = "1.1.0", default-features = false}
bech32 = { version = "0.7.1", optional = true }
aead = "0.2"
fixed = "0.4.6"
bit-vec = { version = "0.6.1", features = ["serde_no_std"] }

[dev-dependencies]
quickcheck = "0.9"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use bit_vec::BitVec;
use parity_scale_codec::{Decode, Encode, Error, Input, Output};
use serde::{Deserialize, Serialize};

use chain_core::state::tendermint::BlockHeight;
use super::tendermint::BlockHeight;

/// Liveness tracker for a validator
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand Down
Loading

0 comments on commit 654dab6

Please sign in to comment.