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:
Nikolay Volf 2016-07-07 11:39:32 +04:00 committed by Gav Wood
parent d7e225c0af
commit 8282c7dd50
32 changed files with 362 additions and 158 deletions

3
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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();
}
} }

View File

@ -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.

View File

@ -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
}
}
}
}

View File

@ -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;
@ -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();
@ -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 { }

View File

@ -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,18 +46,16 @@ 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.

View File

@ -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.

View File

@ -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>,
}

View File

@ -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 {

View File

@ -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;

View File

@ -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());

View File

@ -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![],
} }
])); ]});
} }
} }

View File

@ -16,3 +16,4 @@
pub mod helpers; pub mod helpers;
mod client; mod client;
mod rpc;

57
ethcore/src/tests/rpc.rs Normal file
View 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());
})
}

View File

@ -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,7 +64,7 @@ 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 {
@ -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))),

View 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,
}

View 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,
}

View File

@ -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,

View File

@ -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,
@ -39,12 +43,17 @@ pub struct Filter {
/// ///
/// 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,7 +62,7 @@ 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()
} }
} }
} }
@ -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,
] ]
}; };

View File

@ -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,

View File

@ -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;

View File

@ -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
} }
} }

View 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>,
}

View File

@ -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>,

View File

@ -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() {

View File

@ -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

View File

@ -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 }

View File

@ -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;

View File

@ -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

View File

@ -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()]
} }
} }
} }

View File

@ -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())
} }
} }