Client IPC Interface (#1493)
* btree map serializer * serde tests * state diff serialization * basic layout * more missing serializaers * uncle returns rlp * block queue info * sorting with transaction result * sorting out util imports * transaction import result sorting also * sorting filters & ranges * error sorting out * deriving ipc service compiling * rpc & sync recompile * sorting rpc using uncles * fix compilation * fix merging bugs * fix unused imports * fix all warnings * tests stub * some merge bugs * ethcore compilation * fix rpc compilation * deriving attribute * tests (and fixes) * rpc test working * fix warnings again * rs.in -> rs * missing attribute * refactored tree changes * paste reformat mess fix * pub mod actually * intendation fix
This commit is contained in:
parent
d7e225c0af
commit
8282c7dd50
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -245,6 +245,7 @@ dependencies = [
|
|||||||
"ethcore-devtools 1.3.0",
|
"ethcore-devtools 1.3.0",
|
||||||
"ethcore-ipc 1.3.0",
|
"ethcore-ipc 1.3.0",
|
||||||
"ethcore-ipc-codegen 1.3.0",
|
"ethcore-ipc-codegen 1.3.0",
|
||||||
|
"ethcore-ipc-nano 1.3.0",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.0",
|
||||||
"ethjson 0.1.0",
|
"ethjson 0.1.0",
|
||||||
"ethstore 0.1.0",
|
"ethstore 0.1.0",
|
||||||
@ -256,6 +257,7 @@ dependencies = [
|
|||||||
"rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -330,6 +332,7 @@ dependencies = [
|
|||||||
"ethash 1.3.0",
|
"ethash 1.3.0",
|
||||||
"ethcore 1.3.0",
|
"ethcore 1.3.0",
|
||||||
"ethcore-devtools 1.3.0",
|
"ethcore-devtools 1.3.0",
|
||||||
|
"ethcore-ipc 1.3.0",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.0",
|
||||||
"ethjson 0.1.0",
|
"ethjson 0.1.0",
|
||||||
"ethsync 1.3.0",
|
"ethsync 1.3.0",
|
||||||
|
@ -31,6 +31,8 @@ bloomchain = "0.1"
|
|||||||
"ethcore-ipc" = { path = "../ipc/rpc" }
|
"ethcore-ipc" = { path = "../ipc/rpc" }
|
||||||
rayon = "0.3.1"
|
rayon = "0.3.1"
|
||||||
ethstore = { path = "../ethstore" }
|
ethstore = { path = "../ethstore" }
|
||||||
|
semver = "0.2"
|
||||||
|
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||||
|
|
||||||
[dependencies.hyper]
|
[dependencies.hyper]
|
||||||
git = "https://github.com/ethcore/hyper"
|
git = "https://github.com/ethcore/hyper"
|
||||||
|
@ -30,4 +30,18 @@ fn main() {
|
|||||||
codegen::register(&mut registry);
|
codegen::register(&mut registry);
|
||||||
registry.expand("", &src, &dst).unwrap();
|
registry.expand("", &src, &dst).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// client interface
|
||||||
|
{
|
||||||
|
let src = Path::new("src/client/client.rs");
|
||||||
|
let intermediate = Path::new(&out_dir).join("client.intermediate.rs.in");
|
||||||
|
let mut registry = syntex::Registry::new();
|
||||||
|
codegen::register(&mut registry);
|
||||||
|
registry.expand("", &src, &intermediate).unwrap();
|
||||||
|
|
||||||
|
let dst = Path::new(&out_dir).join("client.ipc.rs");
|
||||||
|
let mut registry = syntex::Registry::new();
|
||||||
|
codegen::register(&mut registry);
|
||||||
|
registry.expand("", &intermediate, &dst).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ use service::*;
|
|||||||
use client::BlockStatus;
|
use client::BlockStatus;
|
||||||
use util::panics::*;
|
use util::panics::*;
|
||||||
|
|
||||||
|
pub use types::block_queue_info::BlockQueueInfo;
|
||||||
|
|
||||||
known_heap_size!(0, UnverifiedBlock, VerifyingBlock, PreverifiedBlock);
|
known_heap_size!(0, UnverifiedBlock, VerifyingBlock, PreverifiedBlock);
|
||||||
|
|
||||||
const MIN_MEM_LIMIT: usize = 16384;
|
const MIN_MEM_LIMIT: usize = 16384;
|
||||||
@ -53,22 +55,6 @@ impl Default for BlockQueueConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block queue status
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct BlockQueueInfo {
|
|
||||||
/// Number of queued blocks pending verification
|
|
||||||
pub unverified_queue_size: usize,
|
|
||||||
/// Number of verified queued blocks pending import
|
|
||||||
pub verified_queue_size: usize,
|
|
||||||
/// Number of blocks being verified
|
|
||||||
pub verifying_queue_size: usize,
|
|
||||||
/// Configured maximum number of blocks in the queue
|
|
||||||
pub max_queue_size: usize,
|
|
||||||
/// Configured maximum number of bytes to use
|
|
||||||
pub max_mem_use: usize,
|
|
||||||
/// Heap memory used in bytes
|
|
||||||
pub mem_used: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlockQueueInfo {
|
impl BlockQueueInfo {
|
||||||
/// The total size of the queues.
|
/// The total size of the queues.
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
use util::numbers::{U256,H256};
|
use util::numbers::{U256,H256};
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
|
|
||||||
use util::bytes::{FromRawBytesVariable, FromBytesError, ToBytesWithMap};
|
|
||||||
|
|
||||||
/// Brief info about inserted block.
|
/// Brief info about inserted block.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BlockInfo {
|
pub struct BlockInfo {
|
||||||
@ -54,43 +52,3 @@ pub struct BranchBecomingCanonChainData {
|
|||||||
/// Hashes of the blocks which were invalidated.
|
/// Hashes of the blocks which were invalidated.
|
||||||
pub retracted: Vec<H256>,
|
pub retracted: Vec<H256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromRawBytesVariable for BranchBecomingCanonChainData {
|
|
||||||
fn from_bytes_variable(bytes: &[u8]) -> Result<BranchBecomingCanonChainData, FromBytesError> {
|
|
||||||
type Tuple = (Vec<H256>, Vec<H256>, H256);
|
|
||||||
let (enacted, retracted, ancestor) = try!(Tuple::from_bytes_variable(bytes));
|
|
||||||
Ok(BranchBecomingCanonChainData { ancestor: ancestor, enacted: enacted, retracted: retracted })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromRawBytesVariable for BlockLocation {
|
|
||||||
fn from_bytes_variable(bytes: &[u8]) -> Result<BlockLocation, FromBytesError> {
|
|
||||||
match bytes[0] {
|
|
||||||
0 => Ok(BlockLocation::CanonChain),
|
|
||||||
1 => Ok(BlockLocation::Branch),
|
|
||||||
2 => Ok(BlockLocation::BranchBecomingCanonChain(
|
|
||||||
try!(BranchBecomingCanonChainData::from_bytes_variable(&bytes[1..bytes.len()])))),
|
|
||||||
_ => Err(FromBytesError::UnknownMarker)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToBytesWithMap for BranchBecomingCanonChainData {
|
|
||||||
fn to_bytes_map(&self) -> Vec<u8> {
|
|
||||||
(&self.enacted, &self.retracted, &self.ancestor).to_bytes_map()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToBytesWithMap for BlockLocation {
|
|
||||||
fn to_bytes_map(&self) -> Vec<u8> {
|
|
||||||
match *self {
|
|
||||||
BlockLocation::CanonChain => vec![0u8],
|
|
||||||
BlockLocation::Branch => vec![1u8],
|
|
||||||
BlockLocation::BranchBecomingCanonChain(ref data) => {
|
|
||||||
let mut bytes = (&data.enacted, &data.retracted, &data.ancestor).to_bytes_map();
|
|
||||||
bytes.insert(0, 2u8);
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -14,16 +14,35 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Blockchain database client.
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::collections::{HashSet, HashMap};
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::mem;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::sync::*;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::fmt;
|
||||||
|
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering};
|
|
||||||
use util::*;
|
// util
|
||||||
|
use util::numbers::*;
|
||||||
use util::panics::*;
|
use util::panics::*;
|
||||||
|
use util::network::*;
|
||||||
|
use util::io::*;
|
||||||
|
use util::rlp;
|
||||||
|
use util::sha3::*;
|
||||||
|
use util::{Bytes};
|
||||||
|
use util::rlp::{RlpStream, Rlp, UntrustedRlp};
|
||||||
|
use util::journaldb;
|
||||||
|
use util::journaldb::JournalDB;
|
||||||
|
use util::kvdb::*;
|
||||||
|
use util::{Applyable, Stream, View, PerfTimer, Itertools, Colour};
|
||||||
|
|
||||||
|
// other
|
||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
use error::{ImportError, ExecutionError, BlockError, ImportResult};
|
use error::{ImportError, ExecutionError, BlockError, ImportResult};
|
||||||
use header::{BlockNumber};
|
use header::BlockNumber;
|
||||||
use state::State;
|
use state::State;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use engine::Engine;
|
use engine::Engine;
|
||||||
@ -35,24 +54,29 @@ use verification::{PreverifiedBlock, Verifier};
|
|||||||
use block::*;
|
use block::*;
|
||||||
use transaction::{LocalizedTransaction, SignedTransaction, Action};
|
use transaction::{LocalizedTransaction, SignedTransaction, Action};
|
||||||
use blockchain::extras::TransactionAddress;
|
use blockchain::extras::TransactionAddress;
|
||||||
use filter::Filter;
|
use types::filter::Filter;
|
||||||
use log_entry::LocalizedLogEntry;
|
use log_entry::LocalizedLogEntry;
|
||||||
use block_queue::{BlockQueue, BlockQueueInfo};
|
use block_queue::{BlockQueue, BlockQueueInfo};
|
||||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
||||||
use client::{BlockID, TransactionID, UncleID, TraceId, Mode, ClientConfig, DatabaseCompactionProfile,
|
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig,
|
||||||
BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics,
|
DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient,
|
||||||
BlockImportError};
|
TraceFilter, CallAnalytics, BlockImportError, Mode};
|
||||||
use client::Error as ClientError;
|
use client::Error as ClientError;
|
||||||
use env_info::EnvInfo;
|
use env_info::EnvInfo;
|
||||||
use executive::{Executive, Executed, TransactOptions, contract_address};
|
use executive::{Executive, Executed, TransactOptions, contract_address};
|
||||||
use receipt::LocalizedReceipt;
|
use receipt::LocalizedReceipt;
|
||||||
pub use blockchain::CacheSize as BlockChainCacheSize;
|
|
||||||
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
|
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
|
||||||
use trace;
|
use trace;
|
||||||
pub use types::blockchain_info::BlockChainInfo;
|
|
||||||
pub use types::block_status::BlockStatus;
|
|
||||||
use evm::Factory as EvmFactory;
|
use evm::Factory as EvmFactory;
|
||||||
use miner::{Miner, MinerService};
|
use miner::{Miner, MinerService};
|
||||||
|
use util::TrieFactory;
|
||||||
|
use ipc::IpcConfig;
|
||||||
|
use ipc::binary::{BinaryConvertError};
|
||||||
|
|
||||||
|
// re-export
|
||||||
|
pub use types::blockchain_info::BlockChainInfo;
|
||||||
|
pub use types::block_status::BlockStatus;
|
||||||
|
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||||
|
|
||||||
const MAX_TX_QUEUE_SIZE: usize = 4096;
|
const MAX_TX_QUEUE_SIZE: usize = 4096;
|
||||||
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
|
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
|
||||||
@ -452,7 +476,7 @@ impl Client {
|
|||||||
HeaderView::new(&self.best_block_header()).state_root(),
|
HeaderView::new(&self.best_block_header()).state_root(),
|
||||||
self.engine.account_start_nonce(),
|
self.engine.account_start_nonce(),
|
||||||
self.trie_factory.clone())
|
self.trie_factory.clone())
|
||||||
.expect("State root of best block header always valid.")
|
.expect("State root of best block header always valid.")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get info on the cache.
|
/// Get info on the cache.
|
||||||
@ -472,12 +496,12 @@ impl Client {
|
|||||||
pub fn tick(&self) {
|
pub fn tick(&self) {
|
||||||
self.chain.collect_garbage();
|
self.chain.collect_garbage();
|
||||||
self.block_queue.collect_garbage();
|
self.block_queue.collect_garbage();
|
||||||
|
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::Dark(timeout) => {
|
Mode::Dark(timeout) => {
|
||||||
let mut ss = self.sleep_state.lock().unwrap();
|
let mut ss = self.sleep_state.lock().unwrap();
|
||||||
if let Some(t) = ss.last_activity {
|
if let Some(t) = ss.last_activity {
|
||||||
if Instant::now() > t + timeout {
|
if Instant::now() > t + timeout {
|
||||||
self.sleep();
|
self.sleep();
|
||||||
ss.last_activity = None;
|
ss.last_activity = None;
|
||||||
}
|
}
|
||||||
@ -487,14 +511,14 @@ impl Client {
|
|||||||
let mut ss = self.sleep_state.lock().unwrap();
|
let mut ss = self.sleep_state.lock().unwrap();
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if let Some(t) = ss.last_activity {
|
if let Some(t) = ss.last_activity {
|
||||||
if now > t + timeout {
|
if now > t + timeout {
|
||||||
self.sleep();
|
self.sleep();
|
||||||
ss.last_activity = None;
|
ss.last_activity = None;
|
||||||
ss.last_autosleep = Some(now);
|
ss.last_autosleep = Some(now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(t) = ss.last_autosleep {
|
if let Some(t) = ss.last_autosleep {
|
||||||
if now > t + wakeup_after {
|
if now > t + wakeup_after {
|
||||||
self.wake_up();
|
self.wake_up();
|
||||||
ss.last_activity = Some(now);
|
ss.last_activity = Some(now);
|
||||||
ss.last_autosleep = None;
|
ss.last_autosleep = None;
|
||||||
@ -575,6 +599,8 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Ipc)]
|
||||||
|
#[ipc(client_ident="RemoteClient")]
|
||||||
impl BlockChainClient for Client {
|
impl BlockChainClient for Client {
|
||||||
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
|
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
|
||||||
let header = self.block_header(BlockID::Latest).unwrap();
|
let header = self.block_header(BlockID::Latest).unwrap();
|
||||||
@ -811,8 +837,8 @@ impl BlockChainClient for Client {
|
|||||||
receipt.logs.into_iter()
|
receipt.logs.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|tuple| filter.matches(&tuple.1))
|
.filter(|tuple| filter.matches(&tuple.1))
|
||||||
.map(|(i, log)| LocalizedLogEntry {
|
.map(|(i, log)| LocalizedLogEntry {
|
||||||
entry: log,
|
entry: log,
|
||||||
block_hash: hash.clone(),
|
block_hash: hash.clone(),
|
||||||
block_number: number,
|
block_number: number,
|
||||||
transaction_hash: hashes.get(index).cloned().unwrap_or_else(H256::new),
|
transaction_hash: hashes.get(index).cloned().unwrap_or_else(H256::new),
|
||||||
@ -822,7 +848,6 @@ impl BlockChainClient for Client {
|
|||||||
.collect::<Vec<LocalizedLogEntry>>()
|
.collect::<Vec<LocalizedLogEntry>>()
|
||||||
})
|
})
|
||||||
.collect::<Vec<LocalizedLogEntry>>()
|
.collect::<Vec<LocalizedLogEntry>>()
|
||||||
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@ -966,3 +991,5 @@ impl MayPanic for Client {
|
|||||||
self.panic_handler.on_panic(closure);
|
self.panic_handler.on_panic(closure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IpcConfig for Client { }
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
//! Blockchain database client.
|
//! Blockchain database client.
|
||||||
|
|
||||||
mod client;
|
|
||||||
mod config;
|
mod config;
|
||||||
mod error;
|
mod error;
|
||||||
mod test_client;
|
mod test_client;
|
||||||
@ -27,7 +26,7 @@ pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueue
|
|||||||
pub use self::error::Error;
|
pub use self::error::Error;
|
||||||
pub use types::ids::*;
|
pub use types::ids::*;
|
||||||
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
||||||
pub use self::trace::Filter as TraceFilter;
|
pub use types::trace_filter::Filter as TraceFilter;
|
||||||
pub use executive::{Executed, Executive, TransactOptions};
|
pub use executive::{Executed, Executive, TransactOptions};
|
||||||
pub use env_info::{LastHashes, EnvInfo};
|
pub use env_info::{LastHashes, EnvInfo};
|
||||||
|
|
||||||
@ -47,23 +46,21 @@ use error::{ImportResult, ExecutionError};
|
|||||||
use receipt::LocalizedReceipt;
|
use receipt::LocalizedReceipt;
|
||||||
use trace::LocalizedTrace;
|
use trace::LocalizedTrace;
|
||||||
use evm::Factory as EvmFactory;
|
use evm::Factory as EvmFactory;
|
||||||
|
pub use types::call_analytics::CallAnalytics;
|
||||||
pub use block_import_error::BlockImportError;
|
pub use block_import_error::BlockImportError;
|
||||||
pub use transaction_import::TransactionImportResult;
|
pub use transaction_import::TransactionImportResult;
|
||||||
|
pub use transaction_import::TransactionImportError;
|
||||||
|
|
||||||
/// Options concerning what analytics we run on the call.
|
pub mod client {
|
||||||
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug)]
|
//! Blockchain database client.
|
||||||
pub struct CallAnalytics {
|
|
||||||
/// Make a transaction trace.
|
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
||||||
pub transaction_tracing: bool,
|
include!(concat!(env!("OUT_DIR"), "/client.ipc.rs"));
|
||||||
/// Make a VM trace.
|
|
||||||
pub vm_tracing: bool,
|
|
||||||
/// Make a diff.
|
|
||||||
pub state_diffing: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||||
pub trait BlockChainClient : Sync + Send {
|
pub trait BlockChainClient : Sync + Send {
|
||||||
|
|
||||||
/// Should be called by any external-facing interface when actively using the client.
|
/// Should be called by any external-facing interface when actively using the client.
|
||||||
/// To minimise chatter, there's no need to call more than once every 30s.
|
/// To minimise chatter, there's no need to call more than once every 30s.
|
||||||
fn keep_alive(&self) {}
|
fn keep_alive(&self) {}
|
||||||
|
@ -36,7 +36,7 @@ use spec::Spec;
|
|||||||
use block_queue::BlockQueueInfo;
|
use block_queue::BlockQueueInfo;
|
||||||
use block::{OpenBlock, SealedBlock};
|
use block::{OpenBlock, SealedBlock};
|
||||||
use executive::Executed;
|
use executive::Executed;
|
||||||
use error::{ExecutionError};
|
use error::ExecutionError;
|
||||||
use trace::LocalizedTrace;
|
use trace::LocalizedTrace;
|
||||||
|
|
||||||
/// Test client.
|
/// Test client.
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
|
|
||||||
//! Bridge between Tracedb and Blockchain.
|
//! Bridge between Tracedb and Blockchain.
|
||||||
|
|
||||||
use std::ops::Range;
|
use util::{H256};
|
||||||
use util::{Address, H256};
|
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
use trace::DatabaseExtras as TraceDatabaseExtras;
|
use trace::DatabaseExtras as TraceDatabaseExtras;
|
||||||
use blockchain::{BlockChain, BlockProvider};
|
use blockchain::{BlockChain, BlockProvider};
|
||||||
use blockchain::extras::TransactionAddress;
|
use blockchain::extras::TransactionAddress;
|
||||||
use super::BlockID;
|
pub use types::trace_filter::Filter;
|
||||||
|
|
||||||
impl TraceDatabaseExtras for BlockChain {
|
impl TraceDatabaseExtras for BlockChain {
|
||||||
fn block_hash(&self, block_number: BlockNumber) -> Option<H256> {
|
fn block_hash(&self, block_number: BlockNumber) -> Option<H256> {
|
||||||
@ -26,13 +25,3 @@ impl TraceDatabaseExtras for BlockChain {
|
|||||||
.map(|tx| tx.hash())
|
.map(|tx| tx.hash())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Easy to use trace filter.
|
|
||||||
pub struct Filter {
|
|
||||||
/// Range of filtering.
|
|
||||||
pub range: Range<BlockID>,
|
|
||||||
/// From address.
|
|
||||||
pub from_address: Vec<Address>,
|
|
||||||
/// To address.
|
|
||||||
pub to_address: Vec<Address>,
|
|
||||||
}
|
|
||||||
|
@ -20,9 +20,9 @@ use util::*;
|
|||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
use basic_types::LogBloom;
|
use basic_types::LogBloom;
|
||||||
use client::Error as ClientError;
|
use client::Error as ClientError;
|
||||||
pub use types::executed::ExecutionError;
|
|
||||||
use ipc::binary::{BinaryConvertError, BinaryConvertable};
|
use ipc::binary::{BinaryConvertError, BinaryConvertable};
|
||||||
use types::block_import_error::BlockImportError;
|
use types::block_import_error::BlockImportError;
|
||||||
|
pub use types::executed::ExecutionError;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
/// Errors concerning transaction processing.
|
/// Errors concerning transaction processing.
|
||||||
@ -327,7 +327,6 @@ binary_fixed_size!(BlockError);
|
|||||||
binary_fixed_size!(ImportError);
|
binary_fixed_size!(ImportError);
|
||||||
binary_fixed_size!(TransactionError);
|
binary_fixed_size!(TransactionError);
|
||||||
|
|
||||||
|
|
||||||
// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
|
// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
|
||||||
/*#![feature(concat_idents)]
|
/*#![feature(concat_idents)]
|
||||||
macro_rules! assimilate {
|
macro_rules! assimilate {
|
||||||
|
@ -95,6 +95,8 @@ extern crate rayon;
|
|||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
extern crate ethash;
|
extern crate ethash;
|
||||||
pub extern crate ethstore;
|
pub extern crate ethstore;
|
||||||
|
extern crate semver;
|
||||||
|
extern crate ethcore_ipc_nano as nanoipc;
|
||||||
|
|
||||||
#[cfg(test)] extern crate ethcore_devtools as devtools;
|
#[cfg(test)] extern crate ethcore_devtools as devtools;
|
||||||
#[cfg(feature = "jit" )] extern crate evmjit;
|
#[cfg(feature = "jit" )] extern crate evmjit;
|
||||||
@ -106,7 +108,6 @@ pub mod block_queue;
|
|||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod ethereum;
|
pub mod ethereum;
|
||||||
pub mod filter;
|
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod service;
|
pub mod service;
|
||||||
pub mod trace;
|
pub mod trace;
|
||||||
|
@ -198,6 +198,7 @@ struct VerifiedTransaction {
|
|||||||
/// transaction origin
|
/// transaction origin
|
||||||
origin: TransactionOrigin,
|
origin: TransactionOrigin,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerifiedTransaction {
|
impl VerifiedTransaction {
|
||||||
fn new(transaction: SignedTransaction, origin: TransactionOrigin) -> Result<Self, Error> {
|
fn new(transaction: SignedTransaction, origin: TransactionOrigin) -> Result<Self, Error> {
|
||||||
try!(transaction.sender());
|
try!(transaction.sender());
|
||||||
|
@ -72,7 +72,7 @@ impl fmt::Display for PodState {
|
|||||||
|
|
||||||
/// Calculate and return diff between `pre` state and `post` state.
|
/// Calculate and return diff between `pre` state and `post` state.
|
||||||
pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff {
|
pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff {
|
||||||
StateDiff(pre.get().keys().merge(post.get().keys()).filter_map(|acc| pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect())
|
StateDiff { raw: pre.get().keys().merge(post.get().keys()).filter_map(|acc| pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -86,22 +86,22 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn create_delete() {
|
fn create_delete() {
|
||||||
let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]);
|
let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]);
|
||||||
assert_eq!(super::diff_pod(&a, &PodState::new()), StateDiff(map![
|
assert_eq!(super::diff_pod(&a, &PodState::new()), StateDiff { raw: map![
|
||||||
1.into() => AccountDiff{
|
1.into() => AccountDiff{
|
||||||
balance: Diff::Died(69.into()),
|
balance: Diff::Died(69.into()),
|
||||||
nonce: Diff::Died(0.into()),
|
nonce: Diff::Died(0.into()),
|
||||||
code: Diff::Died(vec![]),
|
code: Diff::Died(vec![]),
|
||||||
storage: map![],
|
storage: map![],
|
||||||
}
|
}
|
||||||
]));
|
]});
|
||||||
assert_eq!(super::diff_pod(&PodState::new(), &a), StateDiff(map![
|
assert_eq!(super::diff_pod(&PodState::new(), &a), StateDiff{ raw: map![
|
||||||
1.into() => AccountDiff{
|
1.into() => AccountDiff{
|
||||||
balance: Diff::Born(69.into()),
|
balance: Diff::Born(69.into()),
|
||||||
nonce: Diff::Born(0.into()),
|
nonce: Diff::Born(0.into()),
|
||||||
code: Diff::Born(vec![]),
|
code: Diff::Born(vec![]),
|
||||||
storage: map![],
|
storage: map![],
|
||||||
}
|
}
|
||||||
]));
|
]});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -111,22 +111,22 @@ mod test {
|
|||||||
1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]),
|
1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]),
|
||||||
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
|
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
|
||||||
]);
|
]);
|
||||||
assert_eq!(super::diff_pod(&a, &b), StateDiff(map![
|
assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![
|
||||||
2.into() => AccountDiff{
|
2.into() => AccountDiff{
|
||||||
balance: Diff::Born(69.into()),
|
balance: Diff::Born(69.into()),
|
||||||
nonce: Diff::Born(0.into()),
|
nonce: Diff::Born(0.into()),
|
||||||
code: Diff::Born(vec![]),
|
code: Diff::Born(vec![]),
|
||||||
storage: map![],
|
storage: map![],
|
||||||
}
|
}
|
||||||
]));
|
]});
|
||||||
assert_eq!(super::diff_pod(&b, &a), StateDiff(map![
|
assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: map![
|
||||||
2.into() => AccountDiff{
|
2.into() => AccountDiff{
|
||||||
balance: Diff::Died(69.into()),
|
balance: Diff::Died(69.into()),
|
||||||
nonce: Diff::Died(0.into()),
|
nonce: Diff::Died(0.into()),
|
||||||
code: Diff::Died(vec![]),
|
code: Diff::Died(vec![]),
|
||||||
storage: map![],
|
storage: map![],
|
||||||
}
|
}
|
||||||
]));
|
]});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -139,14 +139,14 @@ mod test {
|
|||||||
1.into() => PodAccount::new(69.into(), 1.into(), vec![], map![]),
|
1.into() => PodAccount::new(69.into(), 1.into(), vec![], map![]),
|
||||||
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
|
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
|
||||||
]);
|
]);
|
||||||
assert_eq!(super::diff_pod(&a, &b), StateDiff(map![
|
assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![
|
||||||
1.into() => AccountDiff{
|
1.into() => AccountDiff{
|
||||||
balance: Diff::Same,
|
balance: Diff::Same,
|
||||||
nonce: Diff::Changed(0.into(), 1.into()),
|
nonce: Diff::Changed(0.into(), 1.into()),
|
||||||
code: Diff::Same,
|
code: Diff::Same,
|
||||||
storage: map![],
|
storage: map![],
|
||||||
}
|
}
|
||||||
]));
|
]});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,4 +15,5 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
mod client;
|
mod client;
|
||||||
|
mod rpc;
|
||||||
|
57
ethcore/src/tests/rpc.rs
Normal file
57
ethcore/src/tests/rpc.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Client RPC tests
|
||||||
|
|
||||||
|
use nanoipc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{Ordering, AtomicBool};
|
||||||
|
use client::{Client, ClientConfig, RemoteClient};
|
||||||
|
use tests::helpers::*;
|
||||||
|
use devtools::*;
|
||||||
|
use miner::Miner;
|
||||||
|
use crossbeam;
|
||||||
|
use common::IoChannel;
|
||||||
|
|
||||||
|
pub fn run_test_worker(scope: &crossbeam::Scope, stop: Arc<AtomicBool>, socket_path: &str) {
|
||||||
|
let socket_path = socket_path.to_owned();
|
||||||
|
scope.spawn(move || {
|
||||||
|
let temp = RandomTempPath::create_dir();
|
||||||
|
let client = Client::new(
|
||||||
|
ClientConfig::default(),
|
||||||
|
get_test_spec(),
|
||||||
|
temp.as_path(),
|
||||||
|
Arc::new(Miner::with_spec(get_test_spec())),
|
||||||
|
IoChannel::disconnected()).unwrap();
|
||||||
|
let mut worker = nanoipc::Worker::new(&client);
|
||||||
|
worker.add_reqrep(&socket_path).unwrap();
|
||||||
|
while !stop.load(Ordering::Relaxed) {
|
||||||
|
worker.poll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_handshake() {
|
||||||
|
crossbeam::scope(|scope| {
|
||||||
|
let stop_guard = StopGuard::new();
|
||||||
|
let socket_path = "ipc:///tmp/parity-client-rpc-10.ipc";
|
||||||
|
run_test_worker(scope, stop_guard.share(), socket_path);
|
||||||
|
let remote_client = nanoipc::init_client::<RemoteClient<_>>(socket_path).unwrap();
|
||||||
|
|
||||||
|
assert!(remote_client.handshake().is_ok());
|
||||||
|
})
|
||||||
|
}
|
@ -16,11 +16,17 @@
|
|||||||
|
|
||||||
//! Diff between two accounts.
|
//! Diff between two accounts.
|
||||||
|
|
||||||
use util::*;
|
use util::numbers::*;
|
||||||
|
use std::cmp::*;
|
||||||
|
use std::fmt;
|
||||||
|
use ipc::binary::{BinaryConvertError, BinaryConvertable};
|
||||||
|
use util::Bytes;
|
||||||
|
use std::collections::{VecDeque, BTreeMap};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone, Binary)]
|
||||||
/// Diff type for specifying a change (or not).
|
/// Diff type for specifying a change (or not).
|
||||||
pub enum Diff<T> where T: Eq {
|
pub enum Diff<T> where T: Eq + BinaryConvertable {
|
||||||
/// Both sides are the same.
|
/// Both sides are the same.
|
||||||
Same,
|
Same,
|
||||||
/// Left (pre, source) side doesn't include value, right side (post, destination) does.
|
/// Left (pre, source) side doesn't include value, right side (post, destination) does.
|
||||||
@ -31,7 +37,7 @@ pub enum Diff<T> where T: Eq {
|
|||||||
Died(T),
|
Died(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Diff<T> where T: Eq {
|
impl<T> Diff<T> where T: Eq + BinaryConvertable {
|
||||||
/// Construct new object with given `pre` and `post`.
|
/// Construct new object with given `pre` and `post`.
|
||||||
pub fn new(pre: T, post: T) -> Self { if pre == post { Diff::Same } else { Diff::Changed(pre, post) } }
|
pub fn new(pre: T, post: T) -> Self { if pre == post { Diff::Same } else { Diff::Changed(pre, post) } }
|
||||||
|
|
||||||
@ -45,7 +51,7 @@ impl<T> Diff<T> where T: Eq {
|
|||||||
pub fn is_same(&self) -> bool { match *self { Diff::Same => true, _ => false }}
|
pub fn is_same(&self) -> bool { match *self { Diff::Same => true, _ => false }}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone, Binary)]
|
||||||
/// Account diff.
|
/// Account diff.
|
||||||
pub struct AccountDiff {
|
pub struct AccountDiff {
|
||||||
/// Change in balance, allowed to be `Diff::Same`.
|
/// Change in balance, allowed to be `Diff::Same`.
|
||||||
@ -58,8 +64,8 @@ pub struct AccountDiff {
|
|||||||
pub storage: BTreeMap<H256, Diff<H256>>,
|
pub storage: BTreeMap<H256, Diff<H256>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone, Binary)]
|
||||||
/// Change in existance type.
|
/// Change in existance type.
|
||||||
// TODO: include other types of change.
|
// TODO: include other types of change.
|
||||||
pub enum Existance {
|
pub enum Existance {
|
||||||
/// Item came into existance.
|
/// Item came into existance.
|
||||||
@ -94,6 +100,8 @@ impl AccountDiff {
|
|||||||
|
|
||||||
// TODO: refactor into something nicer.
|
// TODO: refactor into something nicer.
|
||||||
fn interpreted_hash(u: &H256) -> String {
|
fn interpreted_hash(u: &H256) -> String {
|
||||||
|
use util::bytes::*;
|
||||||
|
|
||||||
if u <= &H256::from(0xffffffff) {
|
if u <= &H256::from(0xffffffff) {
|
||||||
format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u32(), U256::from(u.as_slice()).low_u32())
|
format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u32(), U256::from(u.as_slice()).low_u32())
|
||||||
} else if u <= &H256::from(u64::max_value()) {
|
} else if u <= &H256::from(u64::max_value()) {
|
||||||
@ -107,6 +115,8 @@ fn interpreted_hash(u: &H256) -> String {
|
|||||||
|
|
||||||
impl fmt::Display for AccountDiff {
|
impl fmt::Display for AccountDiff {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use util::bytes::*;
|
||||||
|
|
||||||
match self.nonce {
|
match self.nonce {
|
||||||
Diff::Born(ref x) => try!(write!(f, " non {}", x)),
|
Diff::Born(ref x) => try!(write!(f, " non {}", x)),
|
||||||
Diff::Changed(ref pre, ref post) => try!(write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))),
|
Diff::Changed(ref pre, ref post) => try!(write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))),
|
||||||
|
38
ethcore/src/types/block_queue_info.rs
Normal file
38
ethcore/src/types/block_queue_info.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Block queue info types
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
use ipc::binary::BinaryConvertError;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
/// Block queue status
|
||||||
|
#[derive(Debug, Binary)]
|
||||||
|
pub struct BlockQueueInfo {
|
||||||
|
/// Number of queued blocks pending verification
|
||||||
|
pub unverified_queue_size: usize,
|
||||||
|
/// Number of verified queued blocks pending import
|
||||||
|
pub verified_queue_size: usize,
|
||||||
|
/// Number of blocks being verified
|
||||||
|
pub verifying_queue_size: usize,
|
||||||
|
/// Configured maximum number of blocks in the queue
|
||||||
|
pub max_queue_size: usize,
|
||||||
|
/// Configured maximum number of bytes to use
|
||||||
|
pub max_mem_use: usize,
|
||||||
|
/// Heap memory used in bytes
|
||||||
|
pub mem_used: usize,
|
||||||
|
}
|
32
ethcore/src/types/call_analytics.rs
Normal file
32
ethcore/src/types/call_analytics.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Call analytics related types
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
use ipc::binary::{BinaryConvertError};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
/// Options concerning what analytics we run on the call.
|
||||||
|
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug, Binary)]
|
||||||
|
pub struct CallAnalytics {
|
||||||
|
/// Make a transaction trace.
|
||||||
|
pub transaction_tracing: bool,
|
||||||
|
/// Make a VM trace.
|
||||||
|
pub vm_tracing: bool,
|
||||||
|
/// Make a diff.
|
||||||
|
pub state_diffing: bool,
|
||||||
|
}
|
@ -27,7 +27,7 @@ use std::mem;
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
/// Transaction execution receipt.
|
/// Transaction execution receipt.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone, Binary)]
|
||||||
pub struct Executed {
|
pub struct Executed {
|
||||||
/// Gas paid up front for execution of transaction.
|
/// Gas paid up front for execution of transaction.
|
||||||
pub gas: U256,
|
pub gas: U256,
|
||||||
|
@ -20,8 +20,12 @@ use util::hash::*;
|
|||||||
use util::sha3::*;
|
use util::sha3::*;
|
||||||
use client::BlockID;
|
use client::BlockID;
|
||||||
use log_entry::LogEntry;
|
use log_entry::LogEntry;
|
||||||
|
use ipc::binary::BinaryConvertError;
|
||||||
|
use std::mem;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
/// Blockchain Filter.
|
/// Blockchain Filter.
|
||||||
|
#[derive(Binary)]
|
||||||
pub struct Filter {
|
pub struct Filter {
|
||||||
/// Blockchain will be searched from this block.
|
/// Blockchain will be searched from this block.
|
||||||
pub from_block: BlockID,
|
pub from_block: BlockID,
|
||||||
@ -29,22 +33,27 @@ pub struct Filter {
|
|||||||
/// Till this block.
|
/// Till this block.
|
||||||
pub to_block: BlockID,
|
pub to_block: BlockID,
|
||||||
|
|
||||||
/// Search addresses.
|
/// Search addresses.
|
||||||
///
|
///
|
||||||
/// If None, match all.
|
/// If None, match all.
|
||||||
/// If specified, log must be produced by one of these addresses.
|
/// If specified, log must be produced by one of these addresses.
|
||||||
pub address: Option<Vec<Address>>,
|
pub address: Option<Vec<Address>>,
|
||||||
|
|
||||||
/// Search topics.
|
/// Search topics.
|
||||||
///
|
///
|
||||||
/// If None, match all.
|
/// If None, match all.
|
||||||
/// If specified, log must contain one of these topics.
|
/// If specified, log must contain one of these topics.
|
||||||
pub topics: [Option<Vec<H256>>; 4],
|
pub topics: Vec<Option<Vec<H256>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Filter {
|
impl Clone for Filter {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
let mut topics = [None, None, None, None];
|
let mut topics = [
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
];
|
||||||
for i in 0..4 {
|
for i in 0..4 {
|
||||||
topics[i] = self.topics[i].clone();
|
topics[i] = self.topics[i].clone();
|
||||||
}
|
}
|
||||||
@ -53,13 +62,13 @@ impl Clone for Filter {
|
|||||||
from_block: self.from_block.clone(),
|
from_block: self.from_block.clone(),
|
||||||
to_block: self.to_block.clone(),
|
to_block: self.to_block.clone(),
|
||||||
address: self.address.clone(),
|
address: self.address.clone(),
|
||||||
topics: topics
|
topics: topics[..].to_vec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Filter {
|
impl Filter {
|
||||||
/// Returns combinations of each address and topic.
|
/// Returns combinations of each address and topic.
|
||||||
pub fn bloom_possibilities(&self) -> Vec<H2048> {
|
pub fn bloom_possibilities(&self) -> Vec<H2048> {
|
||||||
let blooms = match self.address {
|
let blooms = match self.address {
|
||||||
Some(ref addresses) if !addresses.is_empty() =>
|
Some(ref addresses) if !addresses.is_empty() =>
|
||||||
@ -71,7 +80,7 @@ impl Filter {
|
|||||||
_ => vec![H2048::new()]
|
_ => vec![H2048::new()]
|
||||||
};
|
};
|
||||||
|
|
||||||
self.topics.iter().fold(blooms, | bs, topic | match *topic {
|
self.topics.iter().fold(blooms, |bs, topic| match *topic {
|
||||||
None => bs,
|
None => bs,
|
||||||
Some(ref topics) => bs.into_iter().flat_map(|bloom| {
|
Some(ref topics) => bs.into_iter().flat_map(|bloom| {
|
||||||
topics.into_iter().map(|topic| {
|
topics.into_iter().map(|topic| {
|
||||||
@ -111,7 +120,7 @@ mod tests {
|
|||||||
from_block: BlockID::Earliest,
|
from_block: BlockID::Earliest,
|
||||||
to_block: BlockID::Latest,
|
to_block: BlockID::Latest,
|
||||||
address: None,
|
address: None,
|
||||||
topics: [None, None, None, None]
|
topics: vec![None, None, None, None],
|
||||||
};
|
};
|
||||||
|
|
||||||
let possibilities = none_filter.bloom_possibilities();
|
let possibilities = none_filter.bloom_possibilities();
|
||||||
@ -126,9 +135,11 @@ mod tests {
|
|||||||
from_block: BlockID::Earliest,
|
from_block: BlockID::Earliest,
|
||||||
to_block: BlockID::Latest,
|
to_block: BlockID::Latest,
|
||||||
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
|
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
|
||||||
topics: [
|
topics: vec![
|
||||||
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
|
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
|
||||||
None, None, None
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -142,10 +153,11 @@ mod tests {
|
|||||||
from_block: BlockID::Earliest,
|
from_block: BlockID::Earliest,
|
||||||
to_block: BlockID::Latest,
|
to_block: BlockID::Latest,
|
||||||
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
|
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
|
||||||
topics: [
|
topics: vec![
|
||||||
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
|
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
|
||||||
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
|
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
|
||||||
None, None
|
None,
|
||||||
|
None,
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -162,7 +174,7 @@ mod tests {
|
|||||||
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
|
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
|
||||||
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
|
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
|
||||||
]),
|
]),
|
||||||
topics: [
|
topics: vec![
|
||||||
Some(vec![
|
Some(vec![
|
||||||
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(),
|
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(),
|
||||||
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()
|
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()
|
||||||
@ -188,10 +200,11 @@ mod tests {
|
|||||||
from_block: BlockID::Earliest,
|
from_block: BlockID::Earliest,
|
||||||
to_block: BlockID::Latest,
|
to_block: BlockID::Latest,
|
||||||
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
|
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
|
||||||
topics: [
|
topics: vec![
|
||||||
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
|
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
|
||||||
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap()]),
|
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap()]),
|
||||||
None, None
|
None,
|
||||||
|
None,
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
@ -47,6 +47,7 @@ pub enum TransactionID {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Uniquely identifies Trace.
|
/// Uniquely identifies Trace.
|
||||||
|
#[derive(Binary)]
|
||||||
pub struct TraceId {
|
pub struct TraceId {
|
||||||
/// Transaction
|
/// Transaction
|
||||||
pub transaction: TransactionID,
|
pub transaction: TransactionID,
|
||||||
|
@ -25,5 +25,9 @@ pub mod executed;
|
|||||||
pub mod block_status;
|
pub mod block_status;
|
||||||
pub mod account_diff;
|
pub mod account_diff;
|
||||||
pub mod state_diff;
|
pub mod state_diff;
|
||||||
|
pub mod block_queue_info;
|
||||||
|
pub mod filter;
|
||||||
|
pub mod trace_filter;
|
||||||
|
pub mod call_analytics;
|
||||||
pub mod transaction_import;
|
pub mod transaction_import;
|
||||||
pub mod block_import_error;
|
pub mod block_import_error;
|
||||||
|
@ -16,24 +16,32 @@
|
|||||||
|
|
||||||
//! State diff module.
|
//! State diff module.
|
||||||
|
|
||||||
use util::*;
|
use util::numbers::*;
|
||||||
use account_diff::*;
|
use account_diff::*;
|
||||||
|
use ipc::binary::BinaryConvertError;
|
||||||
|
use std::mem;
|
||||||
|
use std::fmt;
|
||||||
|
use std::ops::*;
|
||||||
|
use std::collections::{VecDeque, BTreeMap};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone, Binary)]
|
||||||
/// Expression for the delta between two system states. Encoded the
|
/// Expression for the delta between two system states. Encoded the
|
||||||
/// delta of every altered account.
|
/// delta of every altered account.
|
||||||
pub struct StateDiff (pub BTreeMap<Address, AccountDiff>);
|
pub struct StateDiff {
|
||||||
|
/// Raw diff key-value
|
||||||
|
pub raw: BTreeMap<Address, AccountDiff>
|
||||||
|
}
|
||||||
|
|
||||||
impl StateDiff {
|
impl StateDiff {
|
||||||
/// Get the actual data.
|
/// Get the actual data.
|
||||||
pub fn get(&self) -> &BTreeMap<Address, AccountDiff> {
|
pub fn get(&self) -> &BTreeMap<Address, AccountDiff> {
|
||||||
&self.0
|
&self.raw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for StateDiff {
|
impl fmt::Display for StateDiff {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
for (add, acc) in &self.0 {
|
for (add, acc) in &self.raw {
|
||||||
try!(write!(f, "{} {}: {}", acc.existance(), add, acc));
|
try!(write!(f, "{} {}: {}", acc.existance(), add, acc));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -44,6 +52,6 @@ impl Deref for StateDiff {
|
|||||||
type Target = BTreeMap<Address, AccountDiff>;
|
type Target = BTreeMap<Address, AccountDiff>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.raw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
ethcore/src/types/trace_filter.rs
Normal file
35
ethcore/src/types/trace_filter.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Trace filter related types
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
use ipc::binary::{BinaryConvertError};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::ops::Range;
|
||||||
|
use util::{Address};
|
||||||
|
use types::ids::BlockID;
|
||||||
|
|
||||||
|
/// Easy to use trace filter.
|
||||||
|
#[derive(Binary)]
|
||||||
|
pub struct Filter {
|
||||||
|
/// Range of filtering.
|
||||||
|
pub range: Range<BlockID>,
|
||||||
|
/// From address.
|
||||||
|
pub from_address: Vec<Address>,
|
||||||
|
/// To address.
|
||||||
|
pub to_address: Vec<Address>,
|
||||||
|
}
|
@ -17,9 +17,12 @@
|
|||||||
//! Tree route info type definition
|
//! Tree route info type definition
|
||||||
|
|
||||||
use util::numbers::H256;
|
use util::numbers::H256;
|
||||||
|
use ipc::BinaryConvertError;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
/// Represents a tree route between `from` block and `to` block:
|
/// Represents a tree route between `from` block and `to` block:
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Binary)]
|
||||||
pub struct TreeRoute {
|
pub struct TreeRoute {
|
||||||
/// A vector of hashes of all blocks, ordered from `from` to `to`.
|
/// A vector of hashes of all blocks, ordered from `from` to `to`.
|
||||||
pub blocks: Vec<H256>,
|
pub blocks: Vec<H256>,
|
||||||
|
@ -315,6 +315,31 @@ impl BinaryConvertable for String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> BinaryConvertable for Range<T> where T: BinaryConvertable {
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
mem::size_of::<T>() * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_empty_bytes() -> Result<Self, BinaryConvertError> {
|
||||||
|
Err(BinaryConvertError)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_bytes(&self, buffer: &mut[u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> {
|
||||||
|
try!(self.start.to_bytes(&mut buffer[..mem::size_of::<T>()], length_stack));
|
||||||
|
try!(self.end.to_bytes(&mut buffer[mem::size_of::<T>() + 1..], length_stack));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> {
|
||||||
|
Ok(try!(T::from_bytes(&buffer[..mem::size_of::<T>()], length_stack))..try!(T::from_bytes(&buffer[mem::size_of::<T>()+1..], length_stack)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len_params() -> usize {
|
||||||
|
assert_eq!(0, T::len_params());
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> BinaryConvertable for ::std::cell::RefCell<T> where T: BinaryConvertable {
|
impl<T> BinaryConvertable for ::std::cell::RefCell<T> where T: BinaryConvertable {
|
||||||
fn size(&self) -> usize {
|
fn size(&self) -> usize {
|
||||||
self.borrow().size()
|
self.borrow().size()
|
||||||
@ -539,8 +564,6 @@ binary_fixed_size!(U512);
|
|||||||
binary_fixed_size!(H256);
|
binary_fixed_size!(H256);
|
||||||
binary_fixed_size!(H2048);
|
binary_fixed_size!(H2048);
|
||||||
binary_fixed_size!(Address);
|
binary_fixed_size!(Address);
|
||||||
binary_fixed_size!(Range<usize>);
|
|
||||||
binary_fixed_size!(Range<u64>);
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn vec_serialize() {
|
fn vec_serialize() {
|
||||||
|
@ -91,7 +91,7 @@ pub fn invoke<W>(method_num: u16, params: &Option<Vec<u8>>, w: &mut W) where W:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// IpcSocket, read/write generalization
|
/// IpcSocket, read/write generalization
|
||||||
pub trait IpcSocket: Read + Write + Sync {
|
pub trait IpcSocket: Read + Write + Sync + Send {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Basically something that needs only socket to be spawned
|
/// Basically something that needs only socket to be spawned
|
||||||
|
@ -25,6 +25,7 @@ transient-hashmap = "0.1"
|
|||||||
serde_macros = { version = "0.7.0", optional = true }
|
serde_macros = { version = "0.7.0", optional = true }
|
||||||
clippy = { version = "0.0.78", optional = true}
|
clippy = { version = "0.0.78", optional = true}
|
||||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
||||||
|
ethcore-ipc = { path = "../ipc/rpc" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde_codegen = { version = "0.7.0", optional = true }
|
serde_codegen = { version = "0.7.0", optional = true }
|
||||||
|
@ -32,6 +32,7 @@ extern crate ethcore;
|
|||||||
extern crate ethsync;
|
extern crate ethsync;
|
||||||
extern crate transient_hashmap;
|
extern crate transient_hashmap;
|
||||||
extern crate json_ipc_server as ipc;
|
extern crate json_ipc_server as ipc;
|
||||||
|
extern crate ethcore_ipc;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate ethjson;
|
extern crate ethjson;
|
||||||
|
@ -30,6 +30,7 @@ use util::sha3::*;
|
|||||||
use util::rlp::{encode, decode, UntrustedRlp, View};
|
use util::rlp::{encode, decode, UntrustedRlp, View};
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID};
|
use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID};
|
||||||
|
use ethcore::header::Header as BlockHeader;
|
||||||
use ethcore::block::IsBlock;
|
use ethcore::block::IsBlock;
|
||||||
use ethcore::views::*;
|
use ethcore::views::*;
|
||||||
use ethcore::ethereum::Ethash;
|
use ethcore::ethereum::Ethash;
|
||||||
@ -42,7 +43,6 @@ use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncIn
|
|||||||
use v1::helpers::CallRequest as CRequest;
|
use v1::helpers::CallRequest as CRequest;
|
||||||
use v1::impls::{default_gas_price, dispatch_transaction, error_codes};
|
use v1::impls::{default_gas_price, dispatch_transaction, error_codes};
|
||||||
use serde;
|
use serde;
|
||||||
use ethcore::header::Header as BlockHeader;
|
|
||||||
|
|
||||||
/// Eth rpc implementation.
|
/// Eth rpc implementation.
|
||||||
pub struct EthClient<C, S, M, EM> where
|
pub struct EthClient<C, S, M, EM> where
|
||||||
|
@ -84,7 +84,7 @@ impl Into<EthFilter> for Filter {
|
|||||||
VariadicValue::Single(t) => Some(vec![t.into()]),
|
VariadicValue::Single(t) => Some(vec![t.into()]),
|
||||||
VariadicValue::Multiple(t) => Some(t.into_iter().map(Into::into).collect())
|
VariadicValue::Multiple(t) => Some(t.into_iter().map(Into::into).collect())
|
||||||
}).filter_map(|m| m).collect()).into_iter();
|
}).filter_map(|m| m).collect()).into_iter();
|
||||||
[iter.next(), iter.next(), iter.next(), iter.next()]
|
vec![iter.next(), iter.next(), iter.next(), iter.next()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ pub enum Diff<T> where T: Serialize {
|
|||||||
Changed(ChangedType<T>),
|
Changed(ChangedType<T>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> From<account_diff::Diff<T>> for Diff<U> where T: Eq, U: Serialize + From<T> {
|
impl<T, U> From<account_diff::Diff<T>> for Diff<U> where T: Eq + ::ethcore_ipc::BinaryConvertable, U: Serialize + From<T> {
|
||||||
fn from(c: account_diff::Diff<T>) -> Self {
|
fn from(c: account_diff::Diff<T>) -> Self {
|
||||||
match c {
|
match c {
|
||||||
account_diff::Diff::Same => Diff::Same,
|
account_diff::Diff::Same => Diff::Same,
|
||||||
@ -205,7 +205,7 @@ impl Serialize for StateDiff {
|
|||||||
|
|
||||||
impl From<state_diff::StateDiff> for StateDiff {
|
impl From<state_diff::StateDiff> for StateDiff {
|
||||||
fn from(c: state_diff::StateDiff) -> Self {
|
fn from(c: state_diff::StateDiff) -> Self {
|
||||||
StateDiff(c.0.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
|
StateDiff(c.raw.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user