Merge branch 'master' into blockchaintests
This commit is contained in:
commit
611890a902
3
.gitignore
vendored
3
.gitignore
vendored
@ -23,4 +23,5 @@ Cargo.lock
|
|||||||
|
|
||||||
/json-tests/target/
|
/json-tests/target/
|
||||||
|
|
||||||
|
# jetbrains ide stuff
|
||||||
|
.idea
|
10
Cargo.toml
10
Cargo.toml
@ -20,16 +20,10 @@ time = "0.1"
|
|||||||
evmjit = { path = "rust-evmjit", optional = true }
|
evmjit = { path = "rust-evmjit", optional = true }
|
||||||
ethash = { path = "ethash" }
|
ethash = { path = "ethash" }
|
||||||
num_cpus = "0.2"
|
num_cpus = "0.2"
|
||||||
docopt = "0.6"
|
|
||||||
docopt_macros = "0.6"
|
|
||||||
ctrlc = "1.0"
|
|
||||||
clippy = "0.0.37"
|
clippy = "0.0.37"
|
||||||
|
crossbeam = "0.1.5"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
jit = ["evmjit"]
|
jit = ["evmjit"]
|
||||||
|
evm_debug = []
|
||||||
test-heavy = []
|
test-heavy = []
|
||||||
evm-debug = []
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "client"
|
|
||||||
path = "src/bin/client/main.rs"
|
|
||||||
|
20
bin/Cargo.toml
Normal file
20
bin/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
description = "Ethcore client."
|
||||||
|
name = "ethcore-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
authors = ["Ethcore <admin@ethcore.io>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.3"
|
||||||
|
env_logger = "0.3"
|
||||||
|
rustc-serialize = "0.3"
|
||||||
|
docopt = "0.6"
|
||||||
|
docopt_macros = "0.6"
|
||||||
|
ctrlc = "1.0"
|
||||||
|
ethcore-util = { path = "../util" }
|
||||||
|
ethcore-rpc = { path = "../rpc", optional = true }
|
||||||
|
ethcore = { path = ".." }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
rpc = ["ethcore-rpc"]
|
@ -1,5 +1,6 @@
|
|||||||
#![feature(plugin)]
|
#![feature(plugin)]
|
||||||
#![plugin(docopt_macros)]
|
#![plugin(docopt_macros)]
|
||||||
|
// required for serde, move it to a separate library
|
||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
@ -8,6 +9,9 @@ extern crate log;
|
|||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
extern crate ctrlc;
|
extern crate ctrlc;
|
||||||
|
|
||||||
|
#[cfg(feature = "rpc")]
|
||||||
|
extern crate ethcore_rpc as rpc;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use log::{LogLevelFilter};
|
use log::{LogLevelFilter};
|
||||||
use env_logger::LogBuilder;
|
use env_logger::LogBuilder;
|
||||||
@ -44,6 +48,23 @@ fn setup_log(init: &String) {
|
|||||||
builder.init().unwrap();
|
builder.init().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(feature = "rpc")]
|
||||||
|
fn setup_rpc_server(client: Arc<Client>) {
|
||||||
|
use rpc::*;
|
||||||
|
|
||||||
|
let mut server = HttpServer::new(1);
|
||||||
|
server.add_delegate(Web3Client::new().to_delegate());
|
||||||
|
server.add_delegate(EthClient::new(client.clone()).to_delegate());
|
||||||
|
server.add_delegate(EthFilterClient::new(client).to_delegate());
|
||||||
|
server.add_delegate(NetClient::new().to_delegate());
|
||||||
|
server.start_async("127.0.0.1:3030");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "rpc"))]
|
||||||
|
fn setup_rpc_server(_client: Arc<Client>) {
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
|
let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
|
||||||
|
|
||||||
@ -57,6 +78,7 @@ fn main() {
|
|||||||
let mut net_settings = NetworkConfiguration::new();
|
let mut net_settings = NetworkConfiguration::new();
|
||||||
net_settings.boot_nodes = init_nodes;
|
net_settings.boot_nodes = init_nodes;
|
||||||
let mut service = ClientService::start(spec, net_settings).unwrap();
|
let mut service = ClientService::start(spec, net_settings).unwrap();
|
||||||
|
setup_rpc_server(service.client());
|
||||||
let io_handler = Arc::new(ClientIoHandler { client: service.client(), info: Default::default(), sync: service.sync() });
|
let io_handler = Arc::new(ClientIoHandler { client: service.client(), info: Default::default(), sync: service.sync() });
|
||||||
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
||||||
|
|
@ -1 +1 @@
|
|||||||
Subproject commit dc86e6359675440aea59ddb48648a01c799925d8
|
Subproject commit e838fd90998fc5502d0b7c9427a4c231f9a6953d
|
18
rpc/Cargo.toml
Normal file
18
rpc/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
description = "Ethcore jsonrpc"
|
||||||
|
name = "ethcore-rpc"
|
||||||
|
version = "0.1.0"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
authors = ["Marek Kotewicz <marek.kotewicz@gmail.com"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = "0.6.7"
|
||||||
|
serde_macros = "0.6.10"
|
||||||
|
serde_json = "0.6.0"
|
||||||
|
jsonrpc-core = "1.1"
|
||||||
|
jsonrpc-http-server = "1.1"
|
||||||
|
ethcore-util = { path = "../util" }
|
||||||
|
ethcore = { path = ".." }
|
||||||
|
|
127
rpc/src/impls/eth.rs
Normal file
127
rpc/src/impls/eth.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
use util::hash::*;
|
||||||
|
use util::uint::*;
|
||||||
|
use util::sha3::*;
|
||||||
|
use ethcore::client::*;
|
||||||
|
use ethcore::views::*;
|
||||||
|
use traits::{Eth, EthFilter};
|
||||||
|
use types::Block;
|
||||||
|
|
||||||
|
pub struct EthClient {
|
||||||
|
client: Arc<Client>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EthClient {
|
||||||
|
pub fn new(client: Arc<Client>) -> Self {
|
||||||
|
EthClient {
|
||||||
|
client: client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eth for EthClient {
|
||||||
|
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match params {
|
||||||
|
Params::None => Ok(Value::U64(63)),
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn author(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match params {
|
||||||
|
Params::None => to_value(&Address::new()),
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gas_price(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match params {
|
||||||
|
Params::None => Ok(Value::U64(0)),
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_number(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match params {
|
||||||
|
Params::None => Ok(Value::U64(self.client.chain_info().best_block_number)),
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_mining(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match params {
|
||||||
|
Params::None => Ok(Value::Bool(false)),
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hashrate(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match params {
|
||||||
|
Params::None => Ok(Value::U64(0)),
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_transaction_count(&self, _: Params) -> Result<Value, Error> {
|
||||||
|
Ok(Value::U64(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match from_params::<(H256, bool)>(params) {
|
||||||
|
Ok((hash, _include_txs)) => match (self.client.block_header(&hash), self.client.block_total_difficulty(&hash)) {
|
||||||
|
(Some(bytes), Some(total_difficulty)) => {
|
||||||
|
let view = HeaderView::new(&bytes);
|
||||||
|
let block = Block {
|
||||||
|
hash: view.sha3(),
|
||||||
|
parent_hash: view.parent_hash(),
|
||||||
|
uncles_hash: view.uncles_hash(),
|
||||||
|
author: view.author(),
|
||||||
|
miner: view.author(),
|
||||||
|
state_root: view.state_root(),
|
||||||
|
transactions_root: view.transactions_root(),
|
||||||
|
receipts_root: view.receipts_root(),
|
||||||
|
number: U256::from(view.number()),
|
||||||
|
gas_used: view.gas_used(),
|
||||||
|
gas_limit: view.gas_limit(),
|
||||||
|
logs_bloom: view.log_bloom(),
|
||||||
|
timestamp: U256::from(view.timestamp()),
|
||||||
|
difficulty: view.difficulty(),
|
||||||
|
total_difficulty: total_difficulty,
|
||||||
|
uncles: vec![],
|
||||||
|
transactions: vec![]
|
||||||
|
};
|
||||||
|
to_value(&block)
|
||||||
|
},
|
||||||
|
_ => Ok(Value::Null)
|
||||||
|
},
|
||||||
|
Err(err) => Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EthFilterClient {
|
||||||
|
client: Arc<Client>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EthFilterClient {
|
||||||
|
pub fn new(client: Arc<Client>) -> Self {
|
||||||
|
EthFilterClient {
|
||||||
|
client: client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EthFilter for EthFilterClient {
|
||||||
|
fn new_block_filter(&self, _params: Params) -> Result<Value, Error> {
|
||||||
|
Ok(Value::U64(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_pending_transaction_filter(&self, _params: Params) -> Result<Value, Error> {
|
||||||
|
Ok(Value::U64(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter_changes(&self, _: Params) -> Result<Value, Error> {
|
||||||
|
to_value(&self.client.chain_info().best_block_hash).map(|v| Value::Array(vec![v]))
|
||||||
|
}
|
||||||
|
}
|
8
rpc/src/impls/mod.rs
Normal file
8
rpc/src/impls/mod.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//! Ethereum rpc interface implementation.
|
||||||
|
mod web3;
|
||||||
|
mod eth;
|
||||||
|
mod net;
|
||||||
|
|
||||||
|
pub use self::web3::Web3Client;
|
||||||
|
pub use self::eth::{EthClient, EthFilterClient};
|
||||||
|
pub use self::net::NetClient;
|
19
rpc/src/impls/net.rs
Normal file
19
rpc/src/impls/net.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//! Net rpc implementation.
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
use traits::Net;
|
||||||
|
|
||||||
|
pub struct NetClient;
|
||||||
|
|
||||||
|
impl NetClient {
|
||||||
|
pub fn new() -> Self { NetClient }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Net for NetClient {
|
||||||
|
fn version(&self, _: Params) -> Result<Value, Error> {
|
||||||
|
Ok(Value::U64(63))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peer_count(&self, _params: Params) -> Result<Value, Error> {
|
||||||
|
Ok(Value::U64(0))
|
||||||
|
}
|
||||||
|
}
|
18
rpc/src/impls/web3.rs
Normal file
18
rpc/src/impls/web3.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use jsonrpc_core::*;
|
||||||
|
use traits::Web3;
|
||||||
|
|
||||||
|
pub struct Web3Client;
|
||||||
|
|
||||||
|
impl Web3Client {
|
||||||
|
pub fn new() -> Self { Web3Client }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Web3 for Web3Client {
|
||||||
|
fn client_version(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match params {
|
||||||
|
//Params::None => Ok(Value::String("parity/0.1.0/-/rust1.7-nightly".to_owned())),
|
||||||
|
Params::None => Ok(Value::String("surprise/0.1.0/surprise/surprise".to_owned())),
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
rpc/src/lib.rs
Normal file
46
rpc/src/lib.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#![feature(custom_derive, custom_attribute, plugin)]
|
||||||
|
#![feature(slice_patterns)]
|
||||||
|
#![plugin(serde_macros)]
|
||||||
|
|
||||||
|
extern crate serde;
|
||||||
|
extern crate serde_json;
|
||||||
|
extern crate jsonrpc_core;
|
||||||
|
extern crate jsonrpc_http_server;
|
||||||
|
extern crate ethcore_util as util;
|
||||||
|
extern crate ethcore;
|
||||||
|
|
||||||
|
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
||||||
|
|
||||||
|
macro_rules! rpcerr {
|
||||||
|
() => (Err(Error::internal_error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod traits;
|
||||||
|
mod impls;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
pub use self::traits::{Web3, Eth, EthFilter, Net};
|
||||||
|
pub use self::impls::*;
|
||||||
|
|
||||||
|
pub struct HttpServer {
|
||||||
|
handler: IoHandler,
|
||||||
|
threads: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpServer {
|
||||||
|
pub fn new(threads: usize) -> HttpServer {
|
||||||
|
HttpServer {
|
||||||
|
handler: IoHandler::new(),
|
||||||
|
threads: threads
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_delegate<D>(&mut self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
||||||
|
self.handler.add_delegate(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_async(self, addr: &str) {
|
||||||
|
let server = jsonrpc_http_server::Server::new(self.handler, self.threads);
|
||||||
|
server.start_async(addr)
|
||||||
|
}
|
||||||
|
}
|
66
rpc/src/traits/eth.rs
Normal file
66
rpc/src/traits/eth.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
//! Eth rpc interface.
|
||||||
|
use std::sync::Arc;
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
|
||||||
|
/// Eth rpc interface.
|
||||||
|
pub trait Eth: Sized + Send + Sync + 'static {
|
||||||
|
/// Returns protocol version.
|
||||||
|
fn protocol_version(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Returns block author.
|
||||||
|
fn author(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Returns current gas_price.
|
||||||
|
fn gas_price(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Returns highest block number.
|
||||||
|
fn block_number(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Returns block with given index / hash.
|
||||||
|
fn block(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Returns true if client is actively mining new blocks.
|
||||||
|
fn is_mining(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Returns the number of hashes per second that the node is mining with.
|
||||||
|
fn hashrate(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Returns the number of transactions in a block.
|
||||||
|
fn block_transaction_count(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Should be used to convert object to io delegate.
|
||||||
|
fn to_delegate(self) -> IoDelegate<Self> {
|
||||||
|
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||||
|
delegate.add_method("eth_protocolVersion", Eth::protocol_version);
|
||||||
|
delegate.add_method("eth_coinbase", Eth::author);
|
||||||
|
delegate.add_method("eth_gasPrice", Eth::gas_price);
|
||||||
|
delegate.add_method("eth_blockNumber", Eth::block_number);
|
||||||
|
delegate.add_method("eth_getBlockByHash", Eth::block);
|
||||||
|
delegate.add_method("eth_getBlockByNumber", Eth::block);
|
||||||
|
delegate.add_method("eth_mining", Eth::is_mining);
|
||||||
|
delegate.add_method("eth_hashrate", Eth::hashrate);
|
||||||
|
delegate.add_method("eth_getBlockTransactionCountByNumber", Eth::block_transaction_count);
|
||||||
|
delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: do filters api properly
|
||||||
|
pub trait EthFilter: Sized + Send + Sync + 'static {
|
||||||
|
/// Returns id of new block filter
|
||||||
|
fn new_block_filter(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Returns id of new block filter
|
||||||
|
fn new_pending_transaction_filter(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Returns filter changes since last poll
|
||||||
|
fn filter_changes(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Should be used to convert object to io delegate.
|
||||||
|
fn to_delegate(self) -> IoDelegate<Self> {
|
||||||
|
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||||
|
delegate.add_method("eth_newBlockFilter", EthFilter::new_block_filter);
|
||||||
|
delegate.add_method("eth_newPendingTransactionFilter", EthFilter::new_pending_transaction_filter);
|
||||||
|
delegate.add_method("eth_getFilterChanges", EthFilter::filter_changes);
|
||||||
|
delegate
|
||||||
|
}
|
||||||
|
}
|
8
rpc/src/traits/mod.rs
Normal file
8
rpc/src/traits/mod.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//! Ethereum rpc interfaces.
|
||||||
|
pub mod web3;
|
||||||
|
pub mod eth;
|
||||||
|
pub mod net;
|
||||||
|
|
||||||
|
pub use self::web3::Web3;
|
||||||
|
pub use self::eth::{Eth, EthFilter};
|
||||||
|
pub use self::net::Net;
|
20
rpc/src/traits/net.rs
Normal file
20
rpc/src/traits/net.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//! Net rpc interface.
|
||||||
|
use std::sync::Arc;
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
|
||||||
|
/// Net rpc interface.
|
||||||
|
pub trait Net: Sized + Send + Sync + 'static {
|
||||||
|
/// Returns protocol version.
|
||||||
|
fn version(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Returns number of peers connected to node.
|
||||||
|
fn peer_count(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Should be used to convert object to io delegate.
|
||||||
|
fn to_delegate(self) -> IoDelegate<Self> {
|
||||||
|
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||||
|
delegate.add_method("net_version", Net::version);
|
||||||
|
delegate.add_method("net_peerCount", Net::peer_count);
|
||||||
|
delegate
|
||||||
|
}
|
||||||
|
}
|
16
rpc/src/traits/web3.rs
Normal file
16
rpc/src/traits/web3.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//! Web3 rpc interface.
|
||||||
|
use std::sync::Arc;
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
|
||||||
|
/// Web3 rpc interface.
|
||||||
|
pub trait Web3: Sized + Send + Sync + 'static {
|
||||||
|
/// Returns current client version.
|
||||||
|
fn client_version(&self, _: Params) -> Result<Value, Error> { rpcerr!() }
|
||||||
|
|
||||||
|
/// Should be used to convert object to io delegate.
|
||||||
|
fn to_delegate(self) -> IoDelegate<Self> {
|
||||||
|
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||||
|
delegate.add_method("web3_clientVersion", Web3::client_version);
|
||||||
|
delegate
|
||||||
|
}
|
||||||
|
}
|
36
rpc/src/types/block.rs
Normal file
36
rpc/src/types/block.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
use util::hash::*;
|
||||||
|
use util::uint::*;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize)]
|
||||||
|
pub struct Block {
|
||||||
|
pub hash: H256,
|
||||||
|
#[serde(rename="parentHash")]
|
||||||
|
pub parent_hash: H256,
|
||||||
|
#[serde(rename="sha3Uncles")]
|
||||||
|
pub uncles_hash: H256,
|
||||||
|
pub author: Address,
|
||||||
|
// TODO: get rid of this one
|
||||||
|
pub miner: Address,
|
||||||
|
#[serde(rename="stateRoot")]
|
||||||
|
pub state_root: H256,
|
||||||
|
#[serde(rename="transactionsRoot")]
|
||||||
|
pub transactions_root: H256,
|
||||||
|
#[serde(rename="receiptsRoot")]
|
||||||
|
pub receipts_root: H256,
|
||||||
|
pub number: U256,
|
||||||
|
#[serde(rename="gasUsed")]
|
||||||
|
pub gas_used: U256,
|
||||||
|
#[serde(rename="gasLimit")]
|
||||||
|
pub gas_limit: U256,
|
||||||
|
// TODO: figure out how to properly serialize bytes
|
||||||
|
//#[serde(rename="extraData")]
|
||||||
|
//extra_data: Vec<u8>,
|
||||||
|
#[serde(rename="logsBloom")]
|
||||||
|
pub logs_bloom: H2048,
|
||||||
|
pub timestamp: U256,
|
||||||
|
pub difficulty: U256,
|
||||||
|
#[serde(rename="totalDifficulty")]
|
||||||
|
pub total_difficulty: U256,
|
||||||
|
pub uncles: Vec<U256>,
|
||||||
|
pub transactions: Vec<U256>
|
||||||
|
}
|
3
rpc/src/types/mod.rs
Normal file
3
rpc/src/types/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod block;
|
||||||
|
|
||||||
|
pub use self::block::Block;
|
@ -66,6 +66,9 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// Get block status by block header hash.
|
/// Get block status by block header hash.
|
||||||
fn block_status(&self, hash: &H256) -> BlockStatus;
|
fn block_status(&self, hash: &H256) -> BlockStatus;
|
||||||
|
|
||||||
|
/// Get block total difficulty.
|
||||||
|
fn block_total_difficulty(&self, hash: &H256) -> Option<U256>;
|
||||||
|
|
||||||
/// Get raw block header data by block number.
|
/// Get raw block header data by block number.
|
||||||
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes>;
|
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes>;
|
||||||
|
|
||||||
@ -79,6 +82,9 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// Get block status by block number.
|
/// Get block status by block number.
|
||||||
fn block_status_at(&self, n: BlockNumber) -> BlockStatus;
|
fn block_status_at(&self, n: BlockNumber) -> BlockStatus;
|
||||||
|
|
||||||
|
/// Get block total difficulty.
|
||||||
|
fn block_total_difficulty_at(&self, n: BlockNumber) -> Option<U256>;
|
||||||
|
|
||||||
/// Get a tree route between `from` and `to`.
|
/// Get a tree route between `from` and `to`.
|
||||||
/// See `BlockChain::tree_route`.
|
/// See `BlockChain::tree_route`.
|
||||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
|
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
|
||||||
@ -315,6 +321,10 @@ impl BlockChainClient for Client {
|
|||||||
if self.chain.read().unwrap().is_known(&hash) { BlockStatus::InChain } else { BlockStatus::Unknown }
|
if self.chain.read().unwrap().is_known(&hash) { BlockStatus::InChain } else { BlockStatus::Unknown }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_total_difficulty(&self, hash: &H256) -> Option<U256> {
|
||||||
|
self.chain.read().unwrap().block_details(hash).map(|d| d.total_difficulty)
|
||||||
|
}
|
||||||
|
|
||||||
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes> {
|
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes> {
|
||||||
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_header(&h))
|
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_header(&h))
|
||||||
}
|
}
|
||||||
@ -334,6 +344,10 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_total_difficulty_at(&self, n: BlockNumber) -> Option<U256> {
|
||||||
|
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_total_difficulty(&h))
|
||||||
|
}
|
||||||
|
|
||||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
||||||
self.chain.read().unwrap().tree_route(from.clone(), to.clone())
|
self.chain.read().unwrap().tree_route(from.clone(), to.clone())
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ impl IntoJit<evmjit::I256> for H256 {
|
|||||||
for i in 0..self.bytes().len() {
|
for i in 0..self.bytes().len() {
|
||||||
let rev = self.bytes().len() - 1 - i;
|
let rev = self.bytes().len() - 1 - i;
|
||||||
let pos = rev / 8;
|
let pos = rev / 8;
|
||||||
ret[pos] += (self.bytes()[i] as u64) << (rev % 8) * 8;
|
ret[pos] += (self.bytes()[i] as u64) << ((rev % 8) * 8);
|
||||||
}
|
}
|
||||||
evmjit::I256 { words: ret }
|
evmjit::I256 { words: ret }
|
||||||
}
|
}
|
||||||
@ -218,9 +218,11 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.ext.call(&call_gas,
|
match self.ext.call(
|
||||||
|
&call_gas,
|
||||||
|
&self.address,
|
||||||
&receive_address,
|
&receive_address,
|
||||||
&value,
|
Some(value),
|
||||||
unsafe { slice::from_raw_parts(in_beg, in_size as usize) },
|
unsafe { slice::from_raw_parts(in_beg, in_size as usize) },
|
||||||
&code_address,
|
&code_address,
|
||||||
unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) }) {
|
unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) }) {
|
||||||
@ -262,7 +264,7 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let bytes_ref: &[u8] = slice::from_raw_parts(beg, size as usize);
|
let bytes_ref: &[u8] = slice::from_raw_parts(beg, size as usize);
|
||||||
self.ext.log(topics, bytes_ref.to_vec());
|
self.ext.log(topics, bytes_ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,8 +289,8 @@ impl evm::Evm for JitEvm {
|
|||||||
assert!(params.gas <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63");
|
assert!(params.gas <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63");
|
||||||
assert!(params.gas_price <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63");
|
assert!(params.gas_price <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63");
|
||||||
|
|
||||||
let call_data = params.data.unwrap_or(vec![]);
|
let call_data = params.data.unwrap_or_else(Vec::new);
|
||||||
let code = params.code.unwrap_or(vec![]);
|
let code = params.code.unwrap_or_else(Vec::new);
|
||||||
|
|
||||||
let mut data = evmjit::RuntimeDataHandle::new();
|
let mut data = evmjit::RuntimeDataHandle::new();
|
||||||
data.gas = params.gas.low_u64() as i64;
|
data.gas = params.gas.low_u64() as i64;
|
||||||
@ -303,7 +305,10 @@ impl evm::Evm for JitEvm {
|
|||||||
data.address = params.address.into_jit();
|
data.address = params.address.into_jit();
|
||||||
data.caller = params.sender.into_jit();
|
data.caller = params.sender.into_jit();
|
||||||
data.origin = params.origin.into_jit();
|
data.origin = params.origin.into_jit();
|
||||||
data.call_value = params.value.into_jit();
|
data.call_value = match params.value {
|
||||||
|
ActionValue::Transfer(val) => val.into_jit(),
|
||||||
|
ActionValue::Apparent(val) => val.into_jit()
|
||||||
|
};
|
||||||
|
|
||||||
data.author = ext.env_info().author.clone().into_jit();
|
data.author = ext.env_info().author.clone().into_jit();
|
||||||
data.difficulty = ext.env_info().difficulty.into_jit();
|
data.difficulty = ext.env_info().difficulty.into_jit();
|
||||||
|
@ -2,67 +2,67 @@
|
|||||||
|
|
||||||
/// Definition of the cost schedule and other parameterisations for the EVM.
|
/// Definition of the cost schedule and other parameterisations for the EVM.
|
||||||
pub struct Schedule {
|
pub struct Schedule {
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Does it support exceptional failed code deposit
|
||||||
pub exceptional_failed_code_deposit: bool,
|
pub exceptional_failed_code_deposit: bool,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Does it have a delegate cal
|
||||||
pub have_delegate_call: bool,
|
pub have_delegate_call: bool,
|
||||||
/// TODO [Tomusdrw] Please document me
|
/// VM stack limit
|
||||||
pub stack_limit: usize,
|
pub stack_limit: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Max number of nested calls/creates
|
||||||
pub max_depth: usize,
|
pub max_depth: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas prices for instructions in all tiers
|
||||||
pub tier_step_gas: [usize; 8],
|
pub tier_step_gas: [usize; 8],
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas price for `EXP` opcode
|
||||||
pub exp_gas: usize,
|
pub exp_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Additional gas for `EXP` opcode for each byte of exponent
|
||||||
pub exp_byte_gas: usize,
|
pub exp_byte_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas price for `SHA3` opcode
|
||||||
pub sha3_gas: usize,
|
pub sha3_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Additional gas for `SHA3` opcode for each word of hashed memory
|
||||||
pub sha3_word_gas: usize,
|
pub sha3_word_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas price for loading from storage
|
||||||
pub sload_gas: usize,
|
pub sload_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas price for setting new value to storage (`storage==0`, `new!=0`)
|
||||||
pub sstore_set_gas: usize,
|
pub sstore_set_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas price for altering value in storage
|
||||||
pub sstore_reset_gas: usize,
|
pub sstore_reset_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas refund for `SSTORE` clearing (when `storage!=0`, `new==0`)
|
||||||
pub sstore_refund_gas: usize,
|
pub sstore_refund_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas price for `JUMPDEST` opcode
|
||||||
pub jumpdest_gas: usize,
|
pub jumpdest_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas price for `LOG*`
|
||||||
pub log_gas: usize,
|
pub log_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Additional gas for data in `LOG*`
|
||||||
pub log_data_gas: usize,
|
pub log_data_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Additional gas for each topic in `LOG*`
|
||||||
pub log_topic_gas: usize,
|
pub log_topic_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas price for `CREATE` opcode
|
||||||
pub create_gas: usize,
|
pub create_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas price for `*CALL*` opcodes
|
||||||
pub call_gas: usize,
|
pub call_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Stipend for transfer for `CALL|CALLCODE` opcode when `value>0`
|
||||||
pub call_stipend: usize,
|
pub call_stipend: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Additional gas required for value transfer (`CALL|CALLCODE`)
|
||||||
pub call_value_transfer_gas: usize,
|
pub call_value_transfer_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Additional gas for creating new account (`CALL|CALLCODE`)
|
||||||
pub call_new_account_gas: usize,
|
pub call_new_account_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Refund for SUICIDE
|
||||||
pub suicide_refund_gas: usize,
|
pub suicide_refund_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas for used memory
|
||||||
pub memory_gas: usize,
|
pub memory_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Coefficient used to convert memory size to gas price for memory
|
||||||
pub quad_coeff_div: usize,
|
pub quad_coeff_div: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Cost for contract length when executing `CREATE`
|
||||||
pub create_data_gas: usize,
|
pub create_data_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Transaction cost
|
||||||
pub tx_gas: usize,
|
pub tx_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// `CREATE` transaction cost
|
||||||
pub tx_create_gas: usize,
|
pub tx_create_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Additional cost for empty data transaction
|
||||||
pub tx_data_zero_gas: usize,
|
pub tx_data_zero_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Aditional cost for non-empty data transaction
|
||||||
pub tx_data_non_zero_gas: usize,
|
pub tx_data_non_zero_gas: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Gas price for copying memory
|
||||||
pub copy_gas: usize,
|
pub copy_gas: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +215,7 @@ fn test_origin(factory: super::Factory) {
|
|||||||
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap());
|
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO [todr] Fails with Signal 11 on JIT
|
||||||
evm_test!{test_sender: test_sender_jit, test_sender_int}
|
evm_test!{test_sender: test_sender_jit, test_sender_int}
|
||||||
fn test_sender(factory: super::Factory) {
|
fn test_sender(factory: super::Factory) {
|
||||||
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
@ -5,6 +5,12 @@ use engine::*;
|
|||||||
use evm::{self, Ext};
|
use evm::{self, Ext};
|
||||||
use externalities::*;
|
use externalities::*;
|
||||||
use substate::*;
|
use substate::*;
|
||||||
|
use crossbeam;
|
||||||
|
|
||||||
|
/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM)
|
||||||
|
/// TODO [todr] We probably need some more sophisticated calculations here (limit on my machine 132)
|
||||||
|
/// Maybe something like here: https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp
|
||||||
|
const MAX_VM_DEPTH_FOR_THREAD: usize = 128;
|
||||||
|
|
||||||
/// Returns new address created from address and given nonce.
|
/// Returns new address created from address and given nonce.
|
||||||
pub fn contract_address(address: &Address, nonce: &U256) -> Address {
|
pub fn contract_address(address: &Address, nonce: &U256) -> Address {
|
||||||
@ -161,6 +167,27 @@ impl<'a> Executive<'a> {
|
|||||||
Ok(try!(self.finalize(t, substate, res)))
|
Ok(try!(self.finalize(t, substate, res)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn exec_vm(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy) -> evm::Result {
|
||||||
|
// Ordinary execution - keep VM in same thread
|
||||||
|
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
|
||||||
|
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy);
|
||||||
|
let vm_factory = self.engine.vm_factory();
|
||||||
|
return vm_factory.create().exec(params, &mut ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start in new thread to reset stack
|
||||||
|
// TODO [todr] No thread builder yet, so we need to reset once for a while
|
||||||
|
// https://github.com/aturon/crossbeam/issues/16
|
||||||
|
crossbeam::scope(|scope| {
|
||||||
|
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy);
|
||||||
|
let vm_factory = self.engine.vm_factory();
|
||||||
|
|
||||||
|
scope.spawn(move || {
|
||||||
|
vm_factory.create().exec(params, &mut ext)
|
||||||
|
})
|
||||||
|
}).join()
|
||||||
|
}
|
||||||
|
|
||||||
/// Calls contract function with given contract params.
|
/// Calls contract function with given contract params.
|
||||||
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
||||||
/// Modifies the substate and the output.
|
/// Modifies the substate and the output.
|
||||||
@ -200,8 +227,7 @@ impl<'a> Executive<'a> {
|
|||||||
let mut unconfirmed_substate = Substate::new();
|
let mut unconfirmed_substate = Substate::new();
|
||||||
|
|
||||||
let res = {
|
let res = {
|
||||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), &mut unconfirmed_substate, OutputPolicy::Return(output));
|
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output))
|
||||||
self.engine.vm_factory().create().exec(params, &mut ext)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!("exec: sstore-clears={}\n", unconfirmed_substate.sstore_clears_count);
|
trace!("exec: sstore-clears={}\n", unconfirmed_substate.sstore_clears_count);
|
||||||
@ -234,8 +260,7 @@ impl<'a> Executive<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let res = {
|
let res = {
|
||||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), &mut unconfirmed_substate, OutputPolicy::InitContract);
|
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract)
|
||||||
self.engine.vm_factory().create().exec(params, &mut ext)
|
|
||||||
};
|
};
|
||||||
self.enact_result(&res, substate, unconfirmed_substate, backup);
|
self.enact_result(&res, substate, unconfirmed_substate, backup);
|
||||||
res
|
res
|
||||||
@ -275,7 +300,6 @@ impl<'a> Executive<'a> {
|
|||||||
|
|
||||||
match result {
|
match result {
|
||||||
Err(evm::Error::Internal) => Err(ExecutionError::Internal),
|
Err(evm::Error::Internal) => Err(ExecutionError::Internal),
|
||||||
// TODO [ToDr] BadJumpDestination @debris - how to handle that?
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
Ok(Executed {
|
Ok(Executed {
|
||||||
gas: t.gas,
|
gas: t.gas,
|
||||||
@ -300,7 +324,6 @@ impl<'a> Executive<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate, backup: State) {
|
fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate, backup: State) {
|
||||||
// TODO: handle other evm::Errors same as OutOfGas once they are implemented
|
|
||||||
match *result {
|
match *result {
|
||||||
Err(evm::Error::OutOfGas)
|
Err(evm::Error::OutOfGas)
|
||||||
| Err(evm::Error::BadJumpDestination {..})
|
| Err(evm::Error::BadJumpDestination {..})
|
||||||
|
@ -215,7 +215,6 @@ impl<'a> Ext for Externalities<'a> {
|
|||||||
fn suicide(&mut self, refund_address: &Address) {
|
fn suicide(&mut self, refund_address: &Address) {
|
||||||
let address = self.origin_info.address.clone();
|
let address = self.origin_info.address.clone();
|
||||||
let balance = self.balance(&address);
|
let balance = self.balance(&address);
|
||||||
trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
|
|
||||||
self.state.transfer_balance(&address, refund_address, &balance);
|
self.state.transfer_balance(&address, refund_address, &balance);
|
||||||
self.substate.suicides.insert(address);
|
self.substate.suicides.insert(address);
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,7 @@ extern crate num_cpus;
|
|||||||
extern crate evmjit;
|
extern crate evmjit;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
|
extern crate crossbeam;
|
||||||
|
|
||||||
// NOTE: Add doc parser exception for these pub declarations.
|
// NOTE: Add doc parser exception for these pub declarations.
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
/// extern crate ethcore;
|
/// extern crate ethcore;
|
||||||
/// use std::env;
|
/// use std::env;
|
||||||
/// use std::sync::Arc;
|
/// use std::sync::Arc;
|
||||||
/// use util::network::{NetworkService,NetworkConfiguration};
|
/// use util::network::{NetworkService, NetworkConfiguration};
|
||||||
/// use ethcore::client::Client;
|
/// use ethcore::client::Client;
|
||||||
/// use ethcore::sync::EthSync;
|
/// use ethcore::sync::EthSync;
|
||||||
/// use ethcore::ethereum;
|
/// use ethcore::ethereum;
|
||||||
|
122
src/tests/client.rs
Normal file
122
src/tests/client.rs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
use client::{BlockChainClient,Client};
|
||||||
|
use std::env;
|
||||||
|
use super::test_common::*;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use spec::*;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn get_random_temp_dir() -> PathBuf {
|
||||||
|
let mut dir = env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
dir
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn get_test_spec() -> Spec {
|
||||||
|
Spec::new_test()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn get_good_dummy_block() -> Bytes {
|
||||||
|
let mut block_header = Header::new();
|
||||||
|
let test_spec = get_test_spec();
|
||||||
|
let test_engine = test_spec.to_engine().unwrap();
|
||||||
|
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||||
|
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||||
|
block_header.timestamp = 40;
|
||||||
|
block_header.number = 1;
|
||||||
|
block_header.parent_hash = test_engine.spec().genesis_header().hash();
|
||||||
|
block_header.state_root = test_engine.spec().genesis_header().state_root;
|
||||||
|
|
||||||
|
create_test_block(&block_header)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn get_bad_state_dummy_block() -> Bytes {
|
||||||
|
let mut block_header = Header::new();
|
||||||
|
let test_spec = get_test_spec();
|
||||||
|
let test_engine = test_spec.to_engine().unwrap();
|
||||||
|
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||||
|
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||||
|
block_header.timestamp = 40;
|
||||||
|
block_header.number = 1;
|
||||||
|
block_header.parent_hash = test_engine.spec().genesis_header().hash();
|
||||||
|
block_header.state_root = x!(0xbad);
|
||||||
|
|
||||||
|
create_test_block(&block_header)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn create_test_block(header: &Header) -> Bytes {
|
||||||
|
let mut rlp = RlpStream::new_list(3);
|
||||||
|
rlp.append(header);
|
||||||
|
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
|
||||||
|
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
|
||||||
|
rlp.out()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> Arc<Client> {
|
||||||
|
let client = Client::new(get_test_spec(), &get_random_temp_dir(), IoChannel::disconnected()).unwrap();
|
||||||
|
for block in &blocks {
|
||||||
|
if let Err(_) = client.import_block(block.clone()) {
|
||||||
|
panic!("panic importing block which is well-formed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.flush_queue();
|
||||||
|
client.import_verified_blocks(&IoChannel::disconnected());
|
||||||
|
client
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn created() {
|
||||||
|
let client_result = Client::new(get_test_spec(), &get_random_temp_dir(), IoChannel::disconnected());
|
||||||
|
assert!(client_result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn imports_from_empty() {
|
||||||
|
let client = Client::new(get_test_spec(), &get_random_temp_dir(), IoChannel::disconnected()).unwrap();
|
||||||
|
client.import_verified_blocks(&IoChannel::disconnected());
|
||||||
|
client.flush_queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn imports_good_block() {
|
||||||
|
let client = Client::new(get_test_spec(), &get_random_temp_dir(), IoChannel::disconnected()).unwrap();
|
||||||
|
let good_block = get_good_dummy_block();
|
||||||
|
if let Err(_) = client.import_block(good_block) {
|
||||||
|
panic!("error importing block being good by definition");
|
||||||
|
}
|
||||||
|
client.flush_queue();
|
||||||
|
client.import_verified_blocks(&IoChannel::disconnected());
|
||||||
|
|
||||||
|
let block = client.block_header_at(1).unwrap();
|
||||||
|
assert!(!block.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn query_none_block() {
|
||||||
|
let client = Client::new(get_test_spec(), &get_random_temp_dir(), IoChannel::disconnected()).unwrap();
|
||||||
|
|
||||||
|
let non_existant = client.block_header_at(188);
|
||||||
|
assert!(non_existant.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn query_bad_block() {
|
||||||
|
let client = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]);
|
||||||
|
let bad_block:Option<Bytes> = client.block_header_at(1);
|
||||||
|
|
||||||
|
assert!(bad_block.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn returns_chain_info() {
|
||||||
|
let dummy_block = get_good_dummy_block();
|
||||||
|
let client = get_test_client_with_blocks(vec![dummy_block.clone()]);
|
||||||
|
let block = BlockView::new(&dummy_block);
|
||||||
|
let info = client.chain_info();
|
||||||
|
assert_eq!(info.best_block_hash, block.header().hash());
|
||||||
|
}
|
@ -271,8 +271,8 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
|
|||||||
|
|
||||||
declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"}
|
declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"}
|
||||||
declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"}
|
declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"}
|
||||||
// this one crashes with some vm internal error. Separately they pass.
|
declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"}
|
||||||
declare_test!{ignore => ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"}
|
// TODO [todr] Fails with Signal 11 when using JIT
|
||||||
declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"}
|
declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"}
|
||||||
declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"}
|
declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"}
|
||||||
declare_test!{heavy => ExecutiveTests_vmInputLimits, "VMTests/vmInputLimits"}
|
declare_test!{heavy => ExecutiveTests_vmInputLimits, "VMTests/vmInputLimits"}
|
||||||
|
@ -4,4 +4,5 @@ mod test_common;
|
|||||||
mod transaction;
|
mod transaction;
|
||||||
mod executive;
|
mod executive;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod client;
|
||||||
mod chain;
|
mod chain;
|
||||||
|
@ -73,7 +73,7 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
|||||||
|
|
||||||
declare_test!{StateTests_stBlockHashTest, "StateTests/stBlockHashTest"}
|
declare_test!{StateTests_stBlockHashTest, "StateTests/stBlockHashTest"}
|
||||||
declare_test!{StateTests_stCallCodes, "StateTests/stCallCodes"}
|
declare_test!{StateTests_stCallCodes, "StateTests/stCallCodes"}
|
||||||
declare_test!{ignore => StateTests_stCallCreateCallCodeTest, "StateTests/stCallCreateCallCodeTest"} //<< Out of stack
|
declare_test!{StateTests_stCallCreateCallCodeTest, "StateTests/stCallCreateCallCodeTest"}
|
||||||
declare_test!{StateTests_stDelegatecallTest, "StateTests/stDelegatecallTest"}
|
declare_test!{StateTests_stDelegatecallTest, "StateTests/stDelegatecallTest"}
|
||||||
declare_test!{StateTests_stExample, "StateTests/stExample"}
|
declare_test!{StateTests_stExample, "StateTests/stExample"}
|
||||||
declare_test!{StateTests_stInitCodeTest, "StateTests/stInitCodeTest"}
|
declare_test!{StateTests_stInitCodeTest, "StateTests/stInitCodeTest"}
|
||||||
@ -81,12 +81,12 @@ declare_test!{StateTests_stLogTests, "StateTests/stLogTests"}
|
|||||||
declare_test!{heavy => StateTests_stMemoryStressTest, "StateTests/stMemoryStressTest"}
|
declare_test!{heavy => StateTests_stMemoryStressTest, "StateTests/stMemoryStressTest"}
|
||||||
declare_test!{heavy => StateTests_stMemoryTest, "StateTests/stMemoryTest"}
|
declare_test!{heavy => StateTests_stMemoryTest, "StateTests/stMemoryTest"}
|
||||||
declare_test!{StateTests_stPreCompiledContracts, "StateTests/stPreCompiledContracts"}
|
declare_test!{StateTests_stPreCompiledContracts, "StateTests/stPreCompiledContracts"}
|
||||||
declare_test!{heavy => StateTests_stQuadraticComplexityTest, "StateTests/stQuadraticComplexityTest"} //<< Too long
|
declare_test!{heavy => StateTests_stQuadraticComplexityTest, "StateTests/stQuadraticComplexityTest"}
|
||||||
declare_test!{ignore => StateTests_stRecursiveCreate, "StateTests/stRecursiveCreate"} //<< Out of stack
|
declare_test!{StateTests_stRecursiveCreate, "StateTests/stRecursiveCreate"}
|
||||||
declare_test!{StateTests_stRefundTest, "StateTests/stRefundTest"}
|
declare_test!{StateTests_stRefundTest, "StateTests/stRefundTest"}
|
||||||
declare_test!{StateTests_stSolidityTest, "StateTests/stSolidityTest"}
|
declare_test!{StateTests_stSolidityTest, "StateTests/stSolidityTest"}
|
||||||
declare_test!{ignore => StateTests_stSpecialTest, "StateTests/stSpecialTest"} //<< Out of Stack
|
declare_test!{StateTests_stSpecialTest, "StateTests/stSpecialTest"}
|
||||||
declare_test!{ignore => StateTests_stSystemOperationsTest, "StateTests/stSystemOperationsTest"} //<< Out of stack
|
declare_test!{StateTests_stSystemOperationsTest, "StateTests/stSystemOperationsTest"}
|
||||||
declare_test!{StateTests_stTransactionTest, "StateTests/stTransactionTest"}
|
declare_test!{StateTests_stTransactionTest, "StateTests/stTransactionTest"}
|
||||||
declare_test!{StateTests_stTransitionTest, "StateTests/stTransitionTest"}
|
declare_test!{StateTests_stTransitionTest, "StateTests/stTransitionTest"}
|
||||||
declare_test!{StateTests_stWalletTest, "StateTests/stWalletTest"}
|
declare_test!{StateTests_stWalletTest, "StateTests/stWalletTest"}
|
||||||
|
@ -25,6 +25,7 @@ itertools = "0.4"
|
|||||||
crossbeam = "0.2"
|
crossbeam = "0.2"
|
||||||
slab = { git = "https://github.com/arkpar/slab.git" }
|
slab = { git = "https://github.com/arkpar/slab.git" }
|
||||||
sha3 = { path = "sha3" }
|
sha3 = { path = "sha3" }
|
||||||
|
serde = "0.6.7"
|
||||||
clippy = "*" # Always newest, since we use nightly
|
clippy = "*" # Always newest, since we use nightly
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -8,6 +8,8 @@ use rand::os::OsRng;
|
|||||||
use bytes::{BytesConvertable,Populatable};
|
use bytes::{BytesConvertable,Populatable};
|
||||||
use from_json::*;
|
use from_json::*;
|
||||||
use uint::{Uint, U256};
|
use uint::{Uint, U256};
|
||||||
|
use rustc_serialize::hex::ToHex;
|
||||||
|
use serde;
|
||||||
|
|
||||||
/// Trait for a fixed-size byte array to be used as the output of hash functions.
|
/// Trait for a fixed-size byte array to be used as the output of hash functions.
|
||||||
///
|
///
|
||||||
@ -215,6 +217,41 @@ macro_rules! impl_hash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl serde::Serialize for $from {
|
||||||
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||||
|
where S: serde::Serializer {
|
||||||
|
let mut hex = "0x".to_owned();
|
||||||
|
hex.push_str(self.to_hex().as_ref());
|
||||||
|
serializer.visit_str(hex.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl serde::Deserialize for $from {
|
||||||
|
fn deserialize<D>(deserializer: &mut D) -> Result<$from, D::Error>
|
||||||
|
where D: serde::Deserializer {
|
||||||
|
struct HashVisitor;
|
||||||
|
|
||||||
|
impl serde::de::Visitor for HashVisitor {
|
||||||
|
type Value = $from;
|
||||||
|
|
||||||
|
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: serde::Error {
|
||||||
|
// 0x + len
|
||||||
|
if value.len() != 2 + $size * 2 {
|
||||||
|
return Err(serde::Error::syntax("Invalid length."));
|
||||||
|
}
|
||||||
|
|
||||||
|
value[2..].from_hex().map(|ref v| $from::from_slice(v)).map_err(|_| serde::Error::syntax("Invalid valid hex."))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: serde::Error {
|
||||||
|
self.visit_str(value.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.visit(HashVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromJson for $from {
|
impl FromJson for $from {
|
||||||
fn from_json(json: &Json) -> Self {
|
fn from_json(json: &Json) -> Self {
|
||||||
match *json {
|
match *json {
|
||||||
@ -479,6 +516,18 @@ impl<'_> From<&'_ U256> for H256 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<H256> for U256 {
|
||||||
|
fn from(value: H256) -> U256 {
|
||||||
|
U256::from(value.bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'_> From<&'_ H256> for U256 {
|
||||||
|
fn from(value: &'_ H256) -> U256 {
|
||||||
|
U256::from(value.bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<H256> for Address {
|
impl From<H256> for Address {
|
||||||
fn from(value: H256) -> Address {
|
fn from(value: H256) -> Address {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -572,6 +621,7 @@ pub static ZERO_H256: H256 = H256([0x00; 32]);
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use hash::*;
|
use hash::*;
|
||||||
|
use uint::*;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -645,5 +695,18 @@ mod tests {
|
|||||||
// too short.
|
// too short.
|
||||||
assert_eq!(H64::from(0), H64::from("0x34567890abcdef"));
|
assert_eq!(H64::from(0), H64::from("0x34567890abcdef"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_and_to_u256() {
|
||||||
|
let u: U256 = x!(0x123456789abcdef0u64);
|
||||||
|
let h = H256::from(u);
|
||||||
|
assert_eq!(H256::from(u), H256::from("000000000000000000000000000000000000000000000000123456789abcdef0"));
|
||||||
|
let h_ref = H256::from(&u);
|
||||||
|
assert_eq!(h, h_ref);
|
||||||
|
let r_ref: U256 = From::from(&h);
|
||||||
|
assert_eq!(r_ref, u);
|
||||||
|
let r: U256 = From::from(h);
|
||||||
|
assert_eq!(r, u);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ extern crate secp256k1;
|
|||||||
extern crate arrayvec;
|
extern crate arrayvec;
|
||||||
extern crate elastic_array;
|
extern crate elastic_array;
|
||||||
extern crate crossbeam;
|
extern crate crossbeam;
|
||||||
|
extern crate serde;
|
||||||
|
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub mod standard;
|
pub mod standard;
|
||||||
|
@ -23,6 +23,8 @@ pub enum DecoderError {
|
|||||||
RlpListLenWithZeroPrefix,
|
RlpListLenWithZeroPrefix,
|
||||||
/// TODO [debris] Please document me
|
/// TODO [debris] Please document me
|
||||||
RlpInvalidIndirection,
|
RlpInvalidIndirection,
|
||||||
|
/// Returned when declared length is inconsistent with data specified after
|
||||||
|
RlpInconsistentLengthAndData
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StdError for DecoderError {
|
impl StdError for DecoderError {
|
||||||
|
@ -4,7 +4,7 @@ use self::json_tests::rlp as rlptest;
|
|||||||
use std::{fmt, cmp};
|
use std::{fmt, cmp};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use rlp;
|
use rlp;
|
||||||
use rlp::{UntrustedRlp, RlpStream, View, Stream};
|
use rlp::{UntrustedRlp, RlpStream, View, Stream, DecoderError};
|
||||||
use uint::U256;
|
use uint::U256;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -351,3 +351,56 @@ fn test_decoding_array() {
|
|||||||
assert_eq!(arr[0], 5);
|
assert_eq!(arr[0], 5);
|
||||||
assert_eq!(arr[1], 2);
|
assert_eq!(arr[1], 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rlp_data_length_check()
|
||||||
|
{
|
||||||
|
let data = vec![0x84, b'c', b'a', b't'];
|
||||||
|
let rlp = UntrustedRlp::new(&data);
|
||||||
|
|
||||||
|
let as_val: Result<String, DecoderError> = rlp.as_val();
|
||||||
|
assert_eq!(Err(DecoderError::RlpInconsistentLengthAndData), as_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rlp_long_data_length_check()
|
||||||
|
{
|
||||||
|
let mut data: Vec<u8> = vec![0xb8, 255];
|
||||||
|
for _ in 0..253 {
|
||||||
|
data.push(b'c');
|
||||||
|
}
|
||||||
|
|
||||||
|
let rlp = UntrustedRlp::new(&data);
|
||||||
|
|
||||||
|
let as_val: Result<String, DecoderError> = rlp.as_val();
|
||||||
|
assert_eq!(Err(DecoderError::RlpInconsistentLengthAndData), as_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_the_exact_long_string()
|
||||||
|
{
|
||||||
|
let mut data: Vec<u8> = vec![0xb8, 255];
|
||||||
|
for _ in 0..255 {
|
||||||
|
data.push(b'c');
|
||||||
|
}
|
||||||
|
|
||||||
|
let rlp = UntrustedRlp::new(&data);
|
||||||
|
|
||||||
|
let as_val: Result<String, DecoderError> = rlp.as_val();
|
||||||
|
assert!(as_val.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rlp_2bytes_data_length_check()
|
||||||
|
{
|
||||||
|
let mut data: Vec<u8> = vec![0xb9, 2, 255]; // 512+255
|
||||||
|
for _ in 0..700 {
|
||||||
|
data.push(b'c');
|
||||||
|
}
|
||||||
|
|
||||||
|
let rlp = UntrustedRlp::new(&data);
|
||||||
|
|
||||||
|
let as_val: Result<String, DecoderError> = rlp.as_val();
|
||||||
|
assert_eq!(Err(DecoderError::RlpInconsistentLengthAndData), as_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -334,18 +334,31 @@ impl<'a> Decoder for BasicDecoder<'a> {
|
|||||||
Some(l @ 0...0x7f) => Ok(try!(f(&[l]))),
|
Some(l @ 0...0x7f) => Ok(try!(f(&[l]))),
|
||||||
// 0-55 bytes
|
// 0-55 bytes
|
||||||
Some(l @ 0x80...0xb7) => {
|
Some(l @ 0x80...0xb7) => {
|
||||||
let d = &bytes[1..(1 + l as usize - 0x80)];
|
let last_index_of = 1 + l as usize - 0x80;
|
||||||
|
if bytes.len() < last_index_of {
|
||||||
|
return Err(DecoderError::RlpInconsistentLengthAndData);
|
||||||
|
}
|
||||||
|
let d = &bytes[1..last_index_of];
|
||||||
if l == 0x81 && d[0] < 0x80 {
|
if l == 0x81 && d[0] < 0x80 {
|
||||||
return Err(DecoderError::RlpInvalidIndirection);
|
return Err(DecoderError::RlpInvalidIndirection);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(try!(f(d)))
|
Ok(try!(f(d)))
|
||||||
},
|
},
|
||||||
// longer than 55 bytes
|
// longer than 55 bytes
|
||||||
Some(l @ 0xb8...0xbf) => {
|
Some(l @ 0xb8...0xbf) => {
|
||||||
let len_of_len = l as usize - 0xb7;
|
let len_of_len = l as usize - 0xb7;
|
||||||
let begin_of_value = 1 as usize + len_of_len;
|
let begin_of_value = 1 as usize + len_of_len;
|
||||||
|
if bytes.len() < begin_of_value {
|
||||||
|
return Err(DecoderError::RlpInconsistentLengthAndData);
|
||||||
|
}
|
||||||
let len = try!(usize::from_bytes(&bytes[1..begin_of_value]));
|
let len = try!(usize::from_bytes(&bytes[1..begin_of_value]));
|
||||||
Ok(try!(f(&bytes[begin_of_value..begin_of_value + len])))
|
|
||||||
|
let last_index_of_value = begin_of_value + len;
|
||||||
|
if bytes.len() < last_index_of_value {
|
||||||
|
return Err(DecoderError::RlpInconsistentLengthAndData);
|
||||||
|
}
|
||||||
|
Ok(try!(f(&bytes[begin_of_value..last_index_of_value])))
|
||||||
}
|
}
|
||||||
// we are reading value, not a list!
|
// we are reading value, not a list!
|
||||||
_ => Err(DecoderError::RlpExpectedToBeData)
|
_ => Err(DecoderError::RlpExpectedToBeData)
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
use standard::*;
|
use standard::*;
|
||||||
use from_json::*;
|
use from_json::*;
|
||||||
|
use rustc_serialize::hex::ToHex;
|
||||||
|
use serde;
|
||||||
|
|
||||||
macro_rules! impl_map_from {
|
macro_rules! impl_map_from {
|
||||||
($thing:ident, $from:ty, $to:ty) => {
|
($thing:ident, $from:ty, $to:ty) => {
|
||||||
@ -436,6 +438,17 @@ macro_rules! construct_uint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl serde::Serialize for $name {
|
||||||
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||||
|
where S: serde::Serializer {
|
||||||
|
let mut hex = "0x".to_owned();
|
||||||
|
let mut bytes = [0u8; 8 * $n_words];
|
||||||
|
self.to_bytes(&mut bytes);
|
||||||
|
hex.push_str(bytes.to_hex().as_ref());
|
||||||
|
serializer.visit_str(hex.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<u64> for $name {
|
impl From<u64> for $name {
|
||||||
fn from(value: u64) -> $name {
|
fn from(value: u64) -> $name {
|
||||||
let mut ret = [0; $n_words];
|
let mut ret = [0; $n_words];
|
||||||
|
Loading…
Reference in New Issue
Block a user