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

Commit

Permalink
Try #651:
Browse files Browse the repository at this point in the history
  • Loading branch information
bors[bot] authored Dec 5, 2019
2 parents 0fee695 + 38da5bb commit 818e0c9
Show file tree
Hide file tree
Showing 27 changed files with 519 additions and 109 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

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

20 changes: 16 additions & 4 deletions chain-abci/src/app/app_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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};
Expand Down Expand Up @@ -248,13 +249,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,
write_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 write_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 +587,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
3 changes: 2 additions & 1 deletion chain-abci/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ use abci::*;
use log::info;

pub use self::app_init::{
get_validator_key, init_app_hash, ChainNodeApp, ChainNodeState, ValidatorState,
compute_accounts_root, get_validator_key, init_app_hash, ChainNodeApp, ChainNodeState,
ValidatorState,
};
use crate::enclave_bridge::EnclaveProxy;
use crate::slashing::SlashingSchedule;
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
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
1 change: 0 additions & 1 deletion chain-abci/tests/abci_app.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use abci::Application;
use abci::*;
use bit_vec::BitVec;
use chain_abci::app::*;
Expand Down
15 changes: 13 additions & 2 deletions client-cli/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ pub enum Command {
help = "Force synchronization from genesis"
)]
force: bool,
#[structopt(
name = "disable-fast-forward",
long,
help = "Disable fast forward, which is not secure when connecting to outside nodes"
)]
disable_fast_forward: bool,
},
}

Expand Down Expand Up @@ -224,6 +230,7 @@ impl Command {
name,
batch_size,
force,
disable_fast_forward,
} => {
let storage = SledStorage::new(storage_path())?;
let tendermint_client = WebsocketRpcClient::new(&tendermint_url())?;
Expand All @@ -236,8 +243,12 @@ impl Command {
storage.clone(),
);

let synchronizer =
ManualSynchronizer::new(storage, tendermint_client, block_handler);
let synchronizer = ManualSynchronizer::new(
storage,
tendermint_client,
block_handler,
!disable_fast_forward,
);

Self::resync(synchronizer, name, *batch_size, *force)
}
Expand Down
1 change: 1 addition & 0 deletions client-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2018"

[dependencies]
chain-core = { path = "../chain-core" }
chain-abci = { path = "../chain-abci" }
chain-tx-filter = { path = "../chain-tx-filter" }
secp256k1zkp = { git = "https://github.com/crypto-com/rust-secp256k1-zkp.git", rev = "8b9a38b870a7759fcdbd4a5d435b5ba873c70afd", features = ["serde", "zeroize", "rand", "recovery", "endomorphism", "musig"] }
rand = "0.7"
Expand Down
5 changes: 5 additions & 0 deletions client-common/src/tendermint/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::tendermint::lite;
use crate::tendermint::types::*;
use crate::Result;
use chain_abci::app::ChainNodeState;

/// Makes remote calls to tendermint (backend agnostic)
pub trait Client: Send + Sync {
Expand Down Expand Up @@ -37,4 +38,8 @@ pub trait Client: Send + Sync {

/// Makes `abci_query` call to tendermint
fn query(&self, path: &str, data: &[u8]) -> Result<AbciQuery>;

/// Match batch state `abci_query` call to tendermint
fn query_state_batch<T: Iterator<Item = u64>>(&self, heights: T)
-> Result<Vec<ChainNodeState>>;
}
41 changes: 41 additions & 0 deletions client-common/src/tendermint/http_rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use tendermint::{lite::verifier, validator};
use crate::tendermint::types::*;
use crate::tendermint::{lite, Client};
use crate::{Error, ErrorKind, Result, ResultExt};
use chain_abci::state::ChainNodeState;

/// Tendermint RPC Client
#[derive(Clone)]
Expand Down Expand Up @@ -257,4 +258,44 @@ impl Client for RpcClient {

Ok(result)
}

fn query_state_batch<T: Iterator<Item = u64>>(
&self,
heights: T,
) -> Result<Vec<ChainNodeState>> {
let params: Vec<(&str, Vec<Value>)> = heights
.map(|height| {
(
"abci_query",
vec![
json!("state"),
json!(null),
json!(height.to_string()),
json!(null),
],
)
})
.collect();
let rsps = self.call_batch::<AbciQueryResponse>(&params)?;

rsps.into_iter()
.map(|opt| {
let rsp =
opt.ok_or(Error::new(ErrorKind::InvalidInput, "chain state not found"))?;
if rsp.response.code.is_ok() {
let value = base64
.decode(&rsp.response.value)
.chain(ErrorKind::InvalidInput, "chain state decode failed")?;
let state = serde_json::from_str(String::from_utf8(value))
.chain(ErrorKind::InvalidInput, "chain state decode failed")?;
Ok(state)
} else {
Err(Error::new(
ErrorKind::InvalidInput,
"abci query return error",
))
}
})
.collect()
}
}
32 changes: 30 additions & 2 deletions client-common/src/tendermint/lite.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
//! Lite tendermint client
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use tendermint::{block::Header, validator};

use super::types::BlockResults;
use crate::tendermint::client::Client;
use crate::Result;
use crate::Result as CommonResult;
use chain_abci::app::ChainNodeState;
use chain_core::common::MerkleTree;
use chain_core::compute_app_hash;

///
#[derive(Clone, Debug, Deserialize, Serialize)]
Expand All @@ -15,9 +20,32 @@ pub struct TrustedState {
}

/// get genesis validator set
pub fn get_genesis_validators<C>(client: &C) -> Result<validator::Set>
pub fn get_genesis_validators<C>(client: &C) -> CommonResult<validator::Set>
where
C: Client,
{
Ok(validator::Set::new(client.genesis()?.validators))
}

/// Verify transaction ids in block_results
pub fn verify_block_results(
result: &BlockResults,
state: &ChainNodeState,
) -> Result<(), Cow<'static, str>> {
let txids = result
.transaction_ids()
.map_err(|e| format!("get transaction id failed: {}", e.to_string()))?;

if state.last_apphash
!= compute_app_hash(
&MerkleTree::new(txids),
&state.last_account_root_hash,
&state.rewards_pool,
&state.network_params,
)
{
return Err("computed app hash don't match state".into());
}

Ok(())
}
8 changes: 8 additions & 0 deletions client-common/src/tendermint/unauthorized_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
tendermint::{lite, types::*, Client},
ErrorKind, Result,
};
use chain_abci::app::ChainNodeState;

/// `Client` which returns `PermissionDenied` error for each function call.
#[derive(Debug, Default, Clone, Copy)]
Expand Down Expand Up @@ -50,4 +51,11 @@ impl Client for UnauthorizedClient {
) -> Result<(Vec<Block>, lite::TrustedState)> {
Err(ErrorKind::PermissionDenied.into())
}

fn query_state_batch<T: Iterator<Item = u64>>(
&self,
_heights: T,
) -> Result<Vec<ChainNodeState>> {
Err(ErrorKind::PermissionDenied.into())
}
}
Loading

0 comments on commit 818e0c9

Please sign in to comment.