Merge branch 'master' of github.com:ethcore/parity into ark
This commit is contained in:
commit
aa9fb98a74
3
.gitignore
vendored
3
.gitignore
vendored
@ -23,4 +23,5 @@ Cargo.lock
|
|||||||
|
|
||||||
/json-tests/target/
|
/json-tests/target/
|
||||||
|
|
||||||
|
# jetbrains ide stuff
|
||||||
|
.idea
|
11
Cargo.toml
11
Cargo.toml
@ -17,18 +17,13 @@ heapsize = "0.2.0"
|
|||||||
rust-crypto = "0.2.34"
|
rust-crypto = "0.2.34"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
#interpolate_idents = { git = "https://github.com/SkylerLipthay/interpolate_idents" }
|
#interpolate_idents = { git = "https://github.com/SkylerLipthay/interpolate_idents" }
|
||||||
evmjit = { path = "rust-evmjit", optional = true }
|
evmjit = { path = "evmjit", optional = true }
|
||||||
ethash = { path = "ethash" }
|
ethash = { path = "ethash" }
|
||||||
num_cpus = "0.2"
|
num_cpus = "0.2"
|
||||||
docopt = "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"
|
|
||||||
|
21
bin/Cargo.toml
Normal file
21
bin/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[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 = ".." }
|
||||||
|
clippy = "0.0.37"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
rpc = ["ethcore-rpc"]
|
@ -1,7 +1,9 @@
|
|||||||
#![feature(plugin)]
|
//! Ethcore client application.
|
||||||
// TODO: uncomment once this can be made to work.
|
|
||||||
//#![plugin(docopt_macros)]
|
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![feature(plugin)]
|
||||||
|
#![plugin(docopt_macros)]
|
||||||
|
#![plugin(clippy)]
|
||||||
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;
|
||||||
@ -10,6 +12,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;
|
||||||
@ -20,9 +25,8 @@ use ethcore::service::{ClientService, NetSyncMessage};
|
|||||||
use ethcore::ethereum;
|
use ethcore::ethereum;
|
||||||
use ethcore::blockchain::CacheSize;
|
use ethcore::blockchain::CacheSize;
|
||||||
use ethcore::sync::EthSync;
|
use ethcore::sync::EthSync;
|
||||||
use docopt::Docopt;
|
|
||||||
|
|
||||||
const USAGE: &'static str = "
|
docopt!(Args derive Debug, "
|
||||||
Parity. Ethereum Client.
|
Parity. Ethereum Client.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
@ -32,15 +36,9 @@ Usage:
|
|||||||
Options:
|
Options:
|
||||||
-l --logging LOGGING Specify the logging level
|
-l --logging LOGGING Specify the logging level
|
||||||
-h --help Show this screen.
|
-h --help Show this screen.
|
||||||
";
|
");
|
||||||
|
|
||||||
#[derive(Debug, RustcDecodable)]
|
fn setup_log(init: &str) {
|
||||||
struct Args {
|
|
||||||
arg_enode: Option<Vec<String>>,
|
|
||||||
flag_logging: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_log(init: &Option<String>) {
|
|
||||||
let mut builder = LogBuilder::new();
|
let mut builder = LogBuilder::new();
|
||||||
builder.filter(None, LogLevelFilter::Info);
|
builder.filter(None, LogLevelFilter::Info);
|
||||||
|
|
||||||
@ -48,26 +46,42 @@ fn setup_log(init: &Option<String>) {
|
|||||||
builder.parse(&env::var("RUST_LOG").unwrap());
|
builder.parse(&env::var("RUST_LOG").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let &Some(ref x) = init {
|
builder.parse(init);
|
||||||
builder.parse(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.init().unwrap();
|
builder.init().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(feature = "rpc")]
|
||||||
|
fn setup_rpc_server(client: Arc<Client>) {
|
||||||
|
use rpc::v1::*;
|
||||||
|
|
||||||
|
let mut server = rpc::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 = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
|
let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
|
||||||
|
|
||||||
setup_log(&args.flag_logging);
|
setup_log(&args.flag_logging);
|
||||||
|
|
||||||
let spec = ethereum::new_frontier();
|
let spec = ethereum::new_frontier();
|
||||||
let init_nodes = match &args.arg_enode {
|
let init_nodes = match args.arg_enode.len() {
|
||||||
&None => spec.nodes().clone(),
|
0 => spec.nodes().clone(),
|
||||||
&Some(ref enodes) => enodes.clone(),
|
_ => args.arg_enode.clone(),
|
||||||
};
|
};
|
||||||
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");
|
||||||
|
|
19
rpc/Cargo.toml
Normal file
19
rpc/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
description = "Ethcore jsonrpc"
|
||||||
|
name = "ethcore-rpc"
|
||||||
|
version = "0.1.0"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
authors = ["Ethcore <admin@ethcore.io"]
|
||||||
|
|
||||||
|
[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 = ".." }
|
||||||
|
clippy = "0.0.37"
|
||||||
|
|
47
rpc/src/lib.rs
Normal file
47
rpc/src/lib.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//! Ethcore rpc.
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![feature(custom_derive, custom_attribute, plugin)]
|
||||||
|
#![plugin(serde_macros)]
|
||||||
|
#![plugin(clippy)]
|
||||||
|
|
||||||
|
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 v1;
|
||||||
|
|
||||||
|
/// Http server.
|
||||||
|
pub struct HttpServer {
|
||||||
|
handler: IoHandler,
|
||||||
|
threads: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpServer {
|
||||||
|
/// Construct new http server object with given number of threads.
|
||||||
|
pub fn new(threads: usize) -> HttpServer {
|
||||||
|
HttpServer {
|
||||||
|
handler: IoHandler::new(),
|
||||||
|
threads: threads
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add io delegate.
|
||||||
|
pub fn add_delegate<D>(&mut self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
||||||
|
self.handler.add_delegate(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start server asynchronously in new thread
|
||||||
|
pub fn start_async(self, addr: &str) {
|
||||||
|
let server = jsonrpc_http_server::Server::new(self.handler, self.threads);
|
||||||
|
server.start_async(addr)
|
||||||
|
}
|
||||||
|
}
|
132
rpc/src/v1/impls/eth.rs
Normal file
132
rpc/src/v1/impls/eth.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
//! Eth rpc implementation.
|
||||||
|
use std::sync::Arc;
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
use util::hash::*;
|
||||||
|
use util::uint::*;
|
||||||
|
use util::sha3::*;
|
||||||
|
use ethcore::client::*;
|
||||||
|
use ethcore::views::*;
|
||||||
|
use v1::traits::{Eth, EthFilter};
|
||||||
|
use v1::types::Block;
|
||||||
|
|
||||||
|
/// Eth rpc implementation.
|
||||||
|
pub struct EthClient {
|
||||||
|
client: Arc<Client>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EthClient {
|
||||||
|
/// Creates new 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Eth filter rpc implementation.
|
||||||
|
pub struct EthFilterClient {
|
||||||
|
client: Arc<Client>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EthFilterClient {
|
||||||
|
/// Creates new Eth filter client.
|
||||||
|
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/v1/impls/mod.rs
Normal file
8
rpc/src/v1/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;
|
21
rpc/src/v1/impls/net.rs
Normal file
21
rpc/src/v1/impls/net.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//! Net rpc implementation.
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
use v1::traits::Net;
|
||||||
|
|
||||||
|
/// Net rpc implementation.
|
||||||
|
pub struct NetClient;
|
||||||
|
|
||||||
|
impl NetClient {
|
||||||
|
/// Creates new 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))
|
||||||
|
}
|
||||||
|
}
|
21
rpc/src/v1/impls/web3.rs
Normal file
21
rpc/src/v1/impls/web3.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//! Web3 rpc implementation.
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
use v1::traits::Web3;
|
||||||
|
|
||||||
|
/// Web3 rpc implementation.
|
||||||
|
pub struct Web3Client;
|
||||||
|
|
||||||
|
impl Web3Client {
|
||||||
|
/// Creates new 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
rpc/src/v1/mod.rs
Normal file
10
rpc/src/v1/mod.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
//! Ethcore rpc v1.
|
||||||
|
//!
|
||||||
|
//! Compliant with ethereum rpc.
|
||||||
|
|
||||||
|
pub mod traits;
|
||||||
|
mod impls;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
pub use self::traits::{Web3, Eth, EthFilter, Net};
|
||||||
|
pub use self::impls::*;
|
67
rpc/src/v1/traits/eth.rs
Normal file
67
rpc/src/v1/traits/eth.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
//! 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Eth filters rpc api (polling).
|
||||||
|
// 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/v1/traits/mod.rs
Normal file
8
rpc/src/v1/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/v1/traits/net.rs
Normal file
20
rpc/src/v1/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/v1/traits/web3.rs
Normal file
16
rpc/src/v1/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/v1/types/block.rs
Normal file
36
rpc/src/v1/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/v1/types/mod.rs
Normal file
3
rpc/src/v1/types/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod block;
|
||||||
|
|
||||||
|
pub use self::block::Block;
|
@ -149,11 +149,15 @@ impl Account {
|
|||||||
/// Provide a database to lookup `code_hash`. Should not be called if it is a contract without code.
|
/// Provide a database to lookup `code_hash`. Should not be called if it is a contract without code.
|
||||||
pub fn cache_code(&mut self, db: &HashDB) -> bool {
|
pub fn cache_code(&mut self, db: &HashDB) -> bool {
|
||||||
// TODO: fill out self.code_cache;
|
// TODO: fill out self.code_cache;
|
||||||
|
trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
|
||||||
self.is_cached() ||
|
self.is_cached() ||
|
||||||
match self.code_hash {
|
match self.code_hash {
|
||||||
Some(ref h) => match db.lookup(h) {
|
Some(ref h) => match db.lookup(h) {
|
||||||
Some(x) => { self.code_cache = x.to_vec(); true },
|
Some(x) => { self.code_cache = x.to_vec(); true },
|
||||||
_ => false,
|
_ => {
|
||||||
|
warn!("Failed reverse lookup of {}", h);
|
||||||
|
false
|
||||||
|
},
|
||||||
},
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,16 @@ use util::hash::*;
|
|||||||
use util::uint::*;
|
use util::uint::*;
|
||||||
use util::bytes::*;
|
use util::bytes::*;
|
||||||
|
|
||||||
// TODO: should be a trait, possible to avoid cloning everything from a Transaction(/View).
|
/// Transaction value
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ActionValue {
|
||||||
|
/// Value that should be transfered
|
||||||
|
Transfer(U256),
|
||||||
|
/// Apparent value for transaction (not transfered)
|
||||||
|
Apparent(U256)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: should be a trait, possible to avoid cloning everything from a Transaction(/View).
|
||||||
/// Action (call/create) input params. Everything else should be specified in Externalities.
|
/// Action (call/create) input params. Everything else should be specified in Externalities.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ActionParams {
|
pub struct ActionParams {
|
||||||
@ -22,16 +30,16 @@ pub struct ActionParams {
|
|||||||
/// Gas price.
|
/// Gas price.
|
||||||
pub gas_price: U256,
|
pub gas_price: U256,
|
||||||
/// Transaction value.
|
/// Transaction value.
|
||||||
pub value: U256,
|
pub value: ActionValue,
|
||||||
/// Code being executed.
|
/// Code being executed.
|
||||||
pub code: Option<Bytes>,
|
pub code: Option<Bytes>,
|
||||||
/// Input data.
|
/// Input data.
|
||||||
pub data: Option<Bytes>
|
pub data: Option<Bytes>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActionParams {
|
impl Default for ActionParams {
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Returns default ActionParams initialized with zeros
|
||||||
pub fn new() -> ActionParams {
|
fn default() -> ActionParams {
|
||||||
ActionParams {
|
ActionParams {
|
||||||
code_address: Address::new(),
|
code_address: Address::new(),
|
||||||
address: Address::new(),
|
address: Address::new(),
|
||||||
@ -39,7 +47,7 @@ impl ActionParams {
|
|||||||
origin: Address::new(),
|
origin: Address::new(),
|
||||||
gas: U256::zero(),
|
gas: U256::zero(),
|
||||||
gas_price: U256::zero(),
|
gas_price: U256::zero(),
|
||||||
value: U256::zero(),
|
value: ActionValue::Transfer(U256::zero()),
|
||||||
code: None,
|
code: None,
|
||||||
data: None
|
data: None
|
||||||
}
|
}
|
||||||
|
199
src/block.rs
199
src/block.rs
@ -5,80 +5,124 @@ use engine::*;
|
|||||||
use state::*;
|
use state::*;
|
||||||
use verification::PreVerifiedBlock;
|
use verification::PreVerifiedBlock;
|
||||||
|
|
||||||
/// A transaction/receipt execution entry.
|
/// A block, encoded as it is on the block chain.
|
||||||
pub struct Entry {
|
// TODO: rename to Block
|
||||||
transaction: Transaction,
|
#[derive(Default, Debug, Clone)]
|
||||||
receipt: Receipt,
|
pub struct Block {
|
||||||
|
/// The header of this block.
|
||||||
|
pub header: Header,
|
||||||
|
/// The transactions in this block.
|
||||||
|
pub transactions: Vec<Transaction>,
|
||||||
|
/// The uncles of this block.
|
||||||
|
pub uncles: Vec<Header>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Block {
|
||||||
|
/// Returns true iff the given bytes form a valid encoding of a block in RLP.
|
||||||
|
// TODO: implement Decoder for this and have this use that.
|
||||||
|
pub fn is_good(b: &[u8]) -> bool {
|
||||||
|
/*
|
||||||
|
let urlp = UntrustedRlp::new(&b);
|
||||||
|
if !urlp.is_list() || urlp.item_count() != 3 || urlp.size() != b.len() { return false; }
|
||||||
|
if urlp.val_at::<Header>(0).is_err() { return false; }
|
||||||
|
|
||||||
|
if !urlp.at(1).unwrap().is_list() { return false; }
|
||||||
|
if urlp.at(1).unwrap().iter().find(|i| i.as_val::<Transaction>().is_err()).is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !urlp.at(2).unwrap().is_list() { return false; }
|
||||||
|
if urlp.at(2).unwrap().iter().find(|i| i.as_val::<Header>().is_err()).is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true*/
|
||||||
|
UntrustedRlp::new(b).as_val::<Block>().is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for Block {
|
||||||
|
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||||
|
if decoder.as_raw().len() != try!(decoder.as_rlp().payload_info()).total() {
|
||||||
|
return Err(DecoderError::RlpIsTooBig);
|
||||||
|
}
|
||||||
|
let d = try!(decoder.as_list());
|
||||||
|
if d.len() != 3 {
|
||||||
|
return Err(DecoderError::RlpIncorrectListLen);
|
||||||
|
}
|
||||||
|
Ok(Block {
|
||||||
|
header: try!(Decodable::decode(&d[0])),
|
||||||
|
transactions: try!(Decodable::decode(&d[1])),
|
||||||
|
uncles: try!(Decodable::decode(&d[2])),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal type for a block's common elements.
|
/// Internal type for a block's common elements.
|
||||||
pub struct Block {
|
// TODO: rename to ExecutedBlock
|
||||||
header: Header,
|
// TODO: use BareBlock
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ExecutedBlock {
|
||||||
|
base: Block,
|
||||||
|
|
||||||
/// State is the most final state in the block.
|
receipts: Vec<Receipt>,
|
||||||
|
transactions_set: HashSet<H256>,
|
||||||
state: State,
|
state: State,
|
||||||
|
|
||||||
archive: Vec<Entry>,
|
|
||||||
archive_set: HashSet<H256>,
|
|
||||||
|
|
||||||
uncles: Vec<Header>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of references to `Block` fields that are publicly accessible.
|
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
|
||||||
pub struct BlockRefMut<'a> {
|
pub struct BlockRefMut<'a> {
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub header: &'a Header,
|
pub header: &'a Header,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub state: &'a mut State,
|
pub transactions: &'a Vec<Transaction>,
|
||||||
/// TODO [Gav Wood] Please document me
|
|
||||||
pub archive: &'a Vec<Entry>,
|
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub uncles: &'a Vec<Header>,
|
pub uncles: &'a Vec<Header>,
|
||||||
|
|
||||||
|
/// TODO [Gav Wood] Please document me
|
||||||
|
pub receipts: &'a Vec<Receipt>,
|
||||||
|
/// TODO [Gav Wood] Please document me
|
||||||
|
pub state: &'a mut State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Block {
|
impl ExecutedBlock {
|
||||||
/// Create a new block from the given `state`.
|
/// Create a new block from the given `state`.
|
||||||
fn new(state: State) -> Block {
|
fn new(state: State) -> ExecutedBlock { ExecutedBlock { base: Default::default(), receipts: Default::default(), transactions_set: Default::default(), state: state } }
|
||||||
Block {
|
|
||||||
header: Header::new(),
|
|
||||||
state: state,
|
|
||||||
archive: Vec::new(),
|
|
||||||
archive_set: HashSet::new(),
|
|
||||||
uncles: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a structure containing individual references to all public fields.
|
/// Get a structure containing individual references to all public fields.
|
||||||
pub fn fields(&mut self) -> BlockRefMut {
|
pub fn fields(&mut self) -> BlockRefMut {
|
||||||
BlockRefMut {
|
BlockRefMut {
|
||||||
header: &self.header,
|
header: &self.base.header,
|
||||||
|
transactions: &self.base.transactions,
|
||||||
|
uncles: &self.base.uncles,
|
||||||
state: &mut self.state,
|
state: &mut self.state,
|
||||||
archive: &self.archive,
|
receipts: &self.receipts,
|
||||||
uncles: &self.uncles,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for a object that is_a `Block`.
|
/// Trait for a object that is_a `ExecutedBlock`.
|
||||||
pub trait IsBlock {
|
pub trait IsBlock {
|
||||||
/// Get the block associated with this object.
|
/// Get the block associated with this object.
|
||||||
fn block(&self) -> &Block;
|
fn block(&self) -> &ExecutedBlock;
|
||||||
|
|
||||||
/// Get the header associated with this object's block.
|
/// Get the header associated with this object's block.
|
||||||
fn header(&self) -> &Header { &self.block().header }
|
fn header(&self) -> &Header { &self.block().base.header }
|
||||||
|
|
||||||
/// Get the final state associated with this object's block.
|
/// Get the final state associated with this object's block.
|
||||||
fn state(&self) -> &State { &self.block().state }
|
fn state(&self) -> &State { &self.block().state }
|
||||||
|
|
||||||
/// Get all information on transactions in this block.
|
/// Get all information on transactions in this block.
|
||||||
fn archive(&self) -> &Vec<Entry> { &self.block().archive }
|
fn transactions(&self) -> &Vec<Transaction> { &self.block().base.transactions }
|
||||||
|
|
||||||
|
/// Get all information on receipts in this block.
|
||||||
|
fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts }
|
||||||
|
|
||||||
/// Get all uncles in this block.
|
/// Get all uncles in this block.
|
||||||
fn uncles(&self) -> &Vec<Header> { &self.block().uncles }
|
fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IsBlock for Block {
|
impl IsBlock for ExecutedBlock {
|
||||||
fn block(&self) -> &Block { self }
|
fn block(&self) -> &ExecutedBlock { self }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block that is ready for transactions to be added.
|
/// Block that is ready for transactions to be added.
|
||||||
@ -86,7 +130,7 @@ impl IsBlock for Block {
|
|||||||
/// It's a bit like a Vec<Transaction>, eccept that whenever a transaction is pushed, we execute it and
|
/// It's a bit like a Vec<Transaction>, eccept that whenever a transaction is pushed, we execute it and
|
||||||
/// maintain the system `state()`. We also archive execution receipts in preparation for later block creation.
|
/// maintain the system `state()`. We also archive execution receipts in preparation for later block creation.
|
||||||
pub struct OpenBlock<'x, 'y> {
|
pub struct OpenBlock<'x, 'y> {
|
||||||
block: Block,
|
block: ExecutedBlock,
|
||||||
engine: &'x Engine,
|
engine: &'x Engine,
|
||||||
last_hashes: &'y LastHashes,
|
last_hashes: &'y LastHashes,
|
||||||
}
|
}
|
||||||
@ -104,7 +148,7 @@ pub struct ClosedBlock<'x, 'y> {
|
|||||||
///
|
///
|
||||||
/// The block's header has valid seal arguments. The block cannot be reversed into a ClosedBlock or OpenBlock.
|
/// The block's header has valid seal arguments. The block cannot be reversed into a ClosedBlock or OpenBlock.
|
||||||
pub struct SealedBlock {
|
pub struct SealedBlock {
|
||||||
block: Block,
|
block: ExecutedBlock,
|
||||||
uncle_bytes: Bytes,
|
uncle_bytes: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,42 +156,42 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
|
|||||||
/// Create a new OpenBlock ready for transaction pushing.
|
/// Create a new OpenBlock ready for transaction pushing.
|
||||||
pub fn new<'a, 'b>(engine: &'a Engine, db: JournalDB, parent: &Header, last_hashes: &'b LastHashes, author: Address, extra_data: Bytes) -> OpenBlock<'a, 'b> {
|
pub fn new<'a, 'b>(engine: &'a Engine, db: JournalDB, parent: &Header, last_hashes: &'b LastHashes, author: Address, extra_data: Bytes) -> OpenBlock<'a, 'b> {
|
||||||
let mut r = OpenBlock {
|
let mut r = OpenBlock {
|
||||||
block: Block::new(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce())),
|
block: ExecutedBlock::new(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce())),
|
||||||
engine: engine,
|
engine: engine,
|
||||||
last_hashes: last_hashes,
|
last_hashes: last_hashes,
|
||||||
};
|
};
|
||||||
|
|
||||||
r.block.header.set_number(parent.number() + 1);
|
r.block.base.header.set_number(parent.number() + 1);
|
||||||
r.block.header.set_author(author);
|
r.block.base.header.set_author(author);
|
||||||
r.block.header.set_extra_data(extra_data);
|
r.block.base.header.set_extra_data(extra_data);
|
||||||
r.block.header.set_timestamp_now();
|
r.block.base.header.set_timestamp_now();
|
||||||
|
|
||||||
engine.populate_from_parent(&mut r.block.header, parent);
|
engine.populate_from_parent(&mut r.block.base.header, parent);
|
||||||
engine.on_new_block(&mut r.block);
|
engine.on_new_block(&mut r.block);
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Alter the author for the block.
|
/// Alter the author for the block.
|
||||||
pub fn set_author(&mut self, author: Address) { self.block.header.set_author(author); }
|
pub fn set_author(&mut self, author: Address) { self.block.base.header.set_author(author); }
|
||||||
|
|
||||||
/// Alter the timestamp of the block.
|
/// Alter the timestamp of the block.
|
||||||
pub fn set_timestamp(&mut self, timestamp: u64) { self.block.header.set_timestamp(timestamp); }
|
pub fn set_timestamp(&mut self, timestamp: u64) { self.block.base.header.set_timestamp(timestamp); }
|
||||||
|
|
||||||
/// Alter the difficulty for the block.
|
/// Alter the difficulty for the block.
|
||||||
pub fn set_difficulty(&mut self, a: U256) { self.block.header.set_difficulty(a); }
|
pub fn set_difficulty(&mut self, a: U256) { self.block.base.header.set_difficulty(a); }
|
||||||
|
|
||||||
/// Alter the gas limit for the block.
|
/// Alter the gas limit for the block.
|
||||||
pub fn set_gas_limit(&mut self, a: U256) { self.block.header.set_gas_limit(a); }
|
pub fn set_gas_limit(&mut self, a: U256) { self.block.base.header.set_gas_limit(a); }
|
||||||
|
|
||||||
/// Alter the gas limit for the block.
|
/// Alter the gas limit for the block.
|
||||||
pub fn set_gas_used(&mut self, a: U256) { self.block.header.set_gas_used(a); }
|
pub fn set_gas_used(&mut self, a: U256) { self.block.base.header.set_gas_used(a); }
|
||||||
|
|
||||||
/// Alter the extra_data for the block.
|
/// Alter the extra_data for the block.
|
||||||
pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> {
|
pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> {
|
||||||
if extra_data.len() > self.engine.maximum_extra_data_size() {
|
if extra_data.len() > self.engine.maximum_extra_data_size() {
|
||||||
Err(BlockError::ExtraDataOutOfBounds(OutOfBounds{min: None, max: Some(self.engine.maximum_extra_data_size()), found: extra_data.len()}))
|
Err(BlockError::ExtraDataOutOfBounds(OutOfBounds{min: None, max: Some(self.engine.maximum_extra_data_size()), found: extra_data.len()}))
|
||||||
} else {
|
} else {
|
||||||
self.block.header.set_extra_data(extra_data);
|
self.block.base.header.set_extra_data(extra_data);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,12 +201,12 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
|
|||||||
/// NOTE Will check chain constraints and the uncle number but will NOT check
|
/// NOTE Will check chain constraints and the uncle number but will NOT check
|
||||||
/// that the header itself is actually valid.
|
/// that the header itself is actually valid.
|
||||||
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
|
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
|
||||||
if self.block.uncles.len() >= self.engine.maximum_uncle_count() {
|
if self.block.base.uncles.len() >= self.engine.maximum_uncle_count() {
|
||||||
return Err(BlockError::TooManyUncles(OutOfBounds{min: None, max: Some(self.engine.maximum_uncle_count()), found: self.block.uncles.len()}));
|
return Err(BlockError::TooManyUncles(OutOfBounds{min: None, max: Some(self.engine.maximum_uncle_count()), found: self.block.base.uncles.len()}));
|
||||||
}
|
}
|
||||||
// TODO: check number
|
// TODO: check number
|
||||||
// TODO: check not a direct ancestor (use last_hashes for that)
|
// TODO: check not a direct ancestor (use last_hashes for that)
|
||||||
self.block.uncles.push(valid_uncle_header);
|
self.block.base.uncles.push(valid_uncle_header);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,13 +214,13 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
|
|||||||
pub fn env_info(&self) -> EnvInfo {
|
pub fn env_info(&self) -> EnvInfo {
|
||||||
// TODO: memoise.
|
// TODO: memoise.
|
||||||
EnvInfo {
|
EnvInfo {
|
||||||
number: self.block.header.number,
|
number: self.block.base.header.number,
|
||||||
author: self.block.header.author.clone(),
|
author: self.block.base.header.author.clone(),
|
||||||
timestamp: self.block.header.timestamp,
|
timestamp: self.block.base.header.timestamp,
|
||||||
difficulty: self.block.header.difficulty.clone(),
|
difficulty: self.block.base.header.difficulty.clone(),
|
||||||
last_hashes: self.last_hashes.clone(), // TODO: should be a reference.
|
last_hashes: self.last_hashes.clone(), // TODO: should be a reference.
|
||||||
gas_used: self.block.archive.last().map_or(U256::zero(), |t| t.receipt.gas_used),
|
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
|
||||||
gas_limit: self.block.header.gas_limit.clone(),
|
gas_limit: self.block.base.header.gas_limit.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,9 +232,10 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
|
|||||||
// info!("env_info says gas_used={}", env_info.gas_used);
|
// info!("env_info says gas_used={}", env_info.gas_used);
|
||||||
match self.block.state.apply(&env_info, self.engine, &t) {
|
match self.block.state.apply(&env_info, self.engine, &t) {
|
||||||
Ok(receipt) => {
|
Ok(receipt) => {
|
||||||
self.block.archive_set.insert(h.unwrap_or_else(||t.hash()));
|
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
|
||||||
self.block.archive.push(Entry { transaction: t, receipt: receipt });
|
self.block.base.transactions.push(t);
|
||||||
Ok(&self.block.archive.last().unwrap().receipt)
|
self.block.receipts.push(receipt);
|
||||||
|
Ok(&self.block.receipts.last().unwrap())
|
||||||
}
|
}
|
||||||
Err(x) => Err(From::from(x))
|
Err(x) => Err(From::from(x))
|
||||||
}
|
}
|
||||||
@ -200,25 +245,25 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
|
|||||||
pub fn close(self) -> ClosedBlock<'x, 'y> {
|
pub fn close(self) -> ClosedBlock<'x, 'y> {
|
||||||
let mut s = self;
|
let mut s = self;
|
||||||
s.engine.on_close_block(&mut s.block);
|
s.engine.on_close_block(&mut s.block);
|
||||||
s.block.header.transactions_root = ordered_trie_root(s.block.archive.iter().map(|ref e| e.transaction.rlp_bytes().to_vec()).collect());
|
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|ref e| e.rlp_bytes().to_vec()).collect());
|
||||||
let uncle_bytes = s.block.uncles.iter().fold(RlpStream::new_list(s.block.uncles.len()), |mut s, u| {s.append(&u.rlp(Seal::With)); s} ).out();
|
let uncle_bytes = s.block.base.uncles.iter().fold(RlpStream::new_list(s.block.base.uncles.len()), |mut s, u| {s.append(&u.rlp(Seal::With)); s} ).out();
|
||||||
s.block.header.uncles_hash = uncle_bytes.sha3();
|
s.block.base.header.uncles_hash = uncle_bytes.sha3();
|
||||||
s.block.header.state_root = s.block.state.root().clone();
|
s.block.base.header.state_root = s.block.state.root().clone();
|
||||||
s.block.header.receipts_root = ordered_trie_root(s.block.archive.iter().map(|ref e| e.receipt.rlp_bytes().to_vec()).collect());
|
s.block.base.header.receipts_root = ordered_trie_root(s.block.receipts.iter().map(|ref r| r.rlp_bytes().to_vec()).collect());
|
||||||
s.block.header.log_bloom = s.block.archive.iter().fold(LogBloom::zero(), |mut b, e| {b |= &e.receipt.log_bloom; b});
|
s.block.base.header.log_bloom = s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b |= &r.log_bloom; b});
|
||||||
s.block.header.gas_used = s.block.archive.last().map_or(U256::zero(), |t| t.receipt.gas_used);
|
s.block.base.header.gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used);
|
||||||
s.block.header.note_dirty();
|
s.block.base.header.note_dirty();
|
||||||
|
|
||||||
ClosedBlock::new(s, uncle_bytes)
|
ClosedBlock::new(s, uncle_bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'x, 'y> IsBlock for OpenBlock<'x, 'y> {
|
impl<'x, 'y> IsBlock for OpenBlock<'x, 'y> {
|
||||||
fn block(&self) -> &Block { &self.block }
|
fn block(&self) -> &ExecutedBlock { &self.block }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'x, 'y> IsBlock for ClosedBlock<'x, 'y> {
|
impl<'x, 'y> IsBlock for ClosedBlock<'x, 'y> {
|
||||||
fn block(&self) -> &Block { &self.open_block.block }
|
fn block(&self) -> &ExecutedBlock { &self.open_block.block }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'x, 'y> ClosedBlock<'x, 'y> {
|
impl<'x, 'y> ClosedBlock<'x, 'y> {
|
||||||
@ -240,7 +285,7 @@ impl<'x, 'y> ClosedBlock<'x, 'y> {
|
|||||||
if seal.len() != s.open_block.engine.seal_fields() {
|
if seal.len() != s.open_block.engine.seal_fields() {
|
||||||
return Err(BlockError::InvalidSealArity(Mismatch{expected: s.open_block.engine.seal_fields(), found: seal.len()}));
|
return Err(BlockError::InvalidSealArity(Mismatch{expected: s.open_block.engine.seal_fields(), found: seal.len()}));
|
||||||
}
|
}
|
||||||
s.open_block.block.header.set_seal(seal);
|
s.open_block.block.base.header.set_seal(seal);
|
||||||
Ok(SealedBlock { block: s.open_block.block, uncle_bytes: s.uncle_bytes })
|
Ok(SealedBlock { block: s.open_block.block, uncle_bytes: s.uncle_bytes })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,9 +300,9 @@ impl SealedBlock {
|
|||||||
/// Get the RLP-encoding of the block.
|
/// Get the RLP-encoding of the block.
|
||||||
pub fn rlp_bytes(&self) -> Bytes {
|
pub fn rlp_bytes(&self) -> Bytes {
|
||||||
let mut block_rlp = RlpStream::new_list(3);
|
let mut block_rlp = RlpStream::new_list(3);
|
||||||
self.block.header.stream_rlp(&mut block_rlp, Seal::With);
|
self.block.base.header.stream_rlp(&mut block_rlp, Seal::With);
|
||||||
block_rlp.begin_list(self.block.archive.len());
|
block_rlp.begin_list(self.block.receipts.len());
|
||||||
for e in &self.block.archive { e.transaction.rlp_append(&mut block_rlp); }
|
for t in &self.block.base.transactions { t.rlp_append(&mut block_rlp); }
|
||||||
block_rlp.append_raw(&self.uncle_bytes, 1);
|
block_rlp.append_raw(&self.uncle_bytes, 1);
|
||||||
block_rlp.out()
|
block_rlp.out()
|
||||||
}
|
}
|
||||||
@ -267,7 +312,7 @@ impl SealedBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IsBlock for SealedBlock {
|
impl IsBlock for SealedBlock {
|
||||||
fn block(&self) -> &Block { &self.block }
|
fn block(&self) -> &ExecutedBlock { &self.block }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enact the block given by block header, transactions and uncles
|
/// Enact the block given by block header, transactions and uncles
|
||||||
|
@ -19,6 +19,16 @@ pub struct BlockQueueInfo {
|
|||||||
pub unverified_queue_size: usize,
|
pub unverified_queue_size: usize,
|
||||||
/// Number of verified queued blocks pending import
|
/// Number of verified queued blocks pending import
|
||||||
pub verified_queue_size: usize,
|
pub verified_queue_size: usize,
|
||||||
|
/// Number of blocks being verified
|
||||||
|
pub verifying_queue_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockQueueInfo {
|
||||||
|
/// The total size of the queues.
|
||||||
|
pub fn total_queue_size(&self) -> usize { self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size }
|
||||||
|
|
||||||
|
/// The size of the unverified and verifying queues.
|
||||||
|
pub fn incomplete_queue_size(&self) -> usize { self.unverified_queue_size + self.verifying_queue_size }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A queue of blocks. Sits between network or other I/O and the BlockChain.
|
/// A queue of blocks. Sits between network or other I/O and the BlockChain.
|
||||||
@ -30,6 +40,7 @@ pub struct BlockQueue {
|
|||||||
verifiers: Vec<JoinHandle<()>>,
|
verifiers: Vec<JoinHandle<()>>,
|
||||||
deleting: Arc<AtomicBool>,
|
deleting: Arc<AtomicBool>,
|
||||||
ready_signal: Arc<QueueSignal>,
|
ready_signal: Arc<QueueSignal>,
|
||||||
|
empty: Arc<Condvar>,
|
||||||
processing: HashSet<H256>
|
processing: HashSet<H256>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +85,7 @@ impl BlockQueue {
|
|||||||
let more_to_verify = Arc::new(Condvar::new());
|
let more_to_verify = Arc::new(Condvar::new());
|
||||||
let ready_signal = Arc::new(QueueSignal { signalled: AtomicBool::new(false), message_channel: message_channel });
|
let ready_signal = Arc::new(QueueSignal { signalled: AtomicBool::new(false), message_channel: message_channel });
|
||||||
let deleting = Arc::new(AtomicBool::new(false));
|
let deleting = Arc::new(AtomicBool::new(false));
|
||||||
|
let empty = Arc::new(Condvar::new());
|
||||||
|
|
||||||
let mut verifiers: Vec<JoinHandle<()>> = Vec::new();
|
let mut verifiers: Vec<JoinHandle<()>> = Vec::new();
|
||||||
let thread_count = max(::num_cpus::get(), 3) - 2;
|
let thread_count = max(::num_cpus::get(), 3) - 2;
|
||||||
@ -82,8 +94,9 @@ impl BlockQueue {
|
|||||||
let engine = engine.clone();
|
let engine = engine.clone();
|
||||||
let more_to_verify = more_to_verify.clone();
|
let more_to_verify = more_to_verify.clone();
|
||||||
let ready_signal = ready_signal.clone();
|
let ready_signal = ready_signal.clone();
|
||||||
|
let empty = empty.clone();
|
||||||
let deleting = deleting.clone();
|
let deleting = deleting.clone();
|
||||||
verifiers.push(thread::Builder::new().name(format!("Verifier #{}", i)).spawn(move || BlockQueue::verify(verification, engine, more_to_verify, ready_signal, deleting))
|
verifiers.push(thread::Builder::new().name(format!("Verifier #{}", i)).spawn(move || BlockQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty))
|
||||||
.expect("Error starting block verification thread"));
|
.expect("Error starting block verification thread"));
|
||||||
}
|
}
|
||||||
BlockQueue {
|
BlockQueue {
|
||||||
@ -94,13 +107,19 @@ impl BlockQueue {
|
|||||||
verifiers: verifiers,
|
verifiers: verifiers,
|
||||||
deleting: deleting.clone(),
|
deleting: deleting.clone(),
|
||||||
processing: HashSet::new(),
|
processing: HashSet::new(),
|
||||||
|
empty: empty.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify(verification: Arc<Mutex<Verification>>, engine: Arc<Box<Engine>>, wait: Arc<Condvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>) {
|
fn verify(verification: Arc<Mutex<Verification>>, engine: Arc<Box<Engine>>, wait: Arc<Condvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<Condvar>) {
|
||||||
while !deleting.load(AtomicOrdering::Relaxed) {
|
while !deleting.load(AtomicOrdering::Relaxed) {
|
||||||
{
|
{
|
||||||
let mut lock = verification.lock().unwrap();
|
let mut lock = verification.lock().unwrap();
|
||||||
|
|
||||||
|
if lock.unverified.is_empty() && lock.verifying.is_empty() {
|
||||||
|
empty.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
while lock.unverified.is_empty() && !deleting.load(AtomicOrdering::Relaxed) {
|
while lock.unverified.is_empty() && !deleting.load(AtomicOrdering::Relaxed) {
|
||||||
lock = wait.wait(lock).unwrap();
|
lock = wait.wait(lock).unwrap();
|
||||||
}
|
}
|
||||||
@ -169,36 +188,46 @@ impl BlockQueue {
|
|||||||
verification.verifying.clear();
|
verification.verifying.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wait for queue to be empty
|
||||||
|
pub fn flush(&mut self) {
|
||||||
|
let mut verification = self.verification.lock().unwrap();
|
||||||
|
while !verification.unverified.is_empty() || !verification.verifying.is_empty() {
|
||||||
|
verification = self.empty.wait(verification).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a block to the queue.
|
/// Add a block to the queue.
|
||||||
pub fn import_block(&mut self, bytes: Bytes) -> ImportResult {
|
pub fn import_block(&mut self, bytes: Bytes) -> ImportResult {
|
||||||
let header = BlockView::new(&bytes).header();
|
let header = BlockView::new(&bytes).header();
|
||||||
if self.processing.contains(&header.hash()) {
|
let h = header.hash();
|
||||||
|
if self.processing.contains(&h) {
|
||||||
return Err(ImportError::AlreadyQueued);
|
return Err(ImportError::AlreadyQueued);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let mut verification = self.verification.lock().unwrap();
|
let mut verification = self.verification.lock().unwrap();
|
||||||
if verification.bad.contains(&header.hash()) {
|
if verification.bad.contains(&h) {
|
||||||
return Err(ImportError::Bad(None));
|
return Err(ImportError::Bad(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
if verification.bad.contains(&header.parent_hash) {
|
if verification.bad.contains(&header.parent_hash) {
|
||||||
verification.bad.insert(header.hash());
|
verification.bad.insert(h.clone());
|
||||||
return Err(ImportError::Bad(None));
|
return Err(ImportError::Bad(None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
|
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
self.processing.insert(header.hash());
|
self.processing.insert(h.clone());
|
||||||
self.verification.lock().unwrap().unverified.push_back(UnVerifiedBlock { header: header, bytes: bytes });
|
self.verification.lock().unwrap().unverified.push_back(UnVerifiedBlock { header: header, bytes: bytes });
|
||||||
self.more_to_verify.notify_all();
|
self.more_to_verify.notify_all();
|
||||||
|
Ok(h)
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
|
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
|
||||||
self.verification.lock().unwrap().bad.insert(header.hash());
|
self.verification.lock().unwrap().bad.insert(h.clone());
|
||||||
|
Err(From::from(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark given block and all its children as bad. Stops verification.
|
/// Mark given block and all its children as bad. Stops verification.
|
||||||
@ -242,6 +271,7 @@ impl BlockQueue {
|
|||||||
full: false,
|
full: false,
|
||||||
verified_queue_size: verification.verified.len(),
|
verified_queue_size: verification.verified.len(),
|
||||||
unverified_queue_size: verification.unverified.len(),
|
unverified_queue_size: verification.unverified.len(),
|
||||||
|
verifying_queue_size: verification.verifying.len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,11 @@ pub trait BlockProvider {
|
|||||||
fn genesis_hash(&self) -> H256 {
|
fn genesis_hash(&self) -> H256 {
|
||||||
self.block_hash(0).expect("Genesis hash should always exist")
|
self.block_hash(0).expect("Genesis hash should always exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the header of the genesis block.
|
||||||
|
fn genesis_header(&self) -> Header {
|
||||||
|
self.block_header(&self.genesis_hash()).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
|
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
|
||||||
@ -283,13 +288,6 @@ impl BlockChain {
|
|||||||
bc
|
bc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure that the best block does indeed have a state_root in the state DB.
|
|
||||||
/// If it doesn't, then rewind down until we find one that does and delete data to ensure that
|
|
||||||
/// later blocks will be reimported.
|
|
||||||
pub fn ensure_good(&mut self, _state: &JournalDB) {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a tree route between `from` and `to`, which is a tuple of:
|
/// Returns a tree route between `from` and `to`, which is a tuple of:
|
||||||
///
|
///
|
||||||
/// - a vector of hashes of all blocks, ordered from `from` to `to`.
|
/// - a vector of hashes of all blocks, ordered from `from` to `to`.
|
||||||
@ -392,7 +390,6 @@ impl BlockChain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Inserts the block into backing cache database.
|
/// Inserts the block into backing cache database.
|
||||||
/// Expects the block to be valid and already verified.
|
/// Expects the block to be valid and already verified.
|
||||||
/// If the block is already known, does nothing.
|
/// If the block is already known, does nothing.
|
||||||
@ -568,15 +565,6 @@ impl BlockChain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to squeeze the cache if its too big.
|
|
||||||
pub fn squeeze_to_fit(&self, size: CacheSize) {
|
|
||||||
self.blocks.write().unwrap().squeeze(size.blocks);
|
|
||||||
self.block_details.write().unwrap().squeeze(size.block_details);
|
|
||||||
self.transaction_addresses.write().unwrap().squeeze(size.transaction_addresses);
|
|
||||||
self.block_logs.write().unwrap().squeeze(size.block_logs);
|
|
||||||
self.blocks_blooms.write().unwrap().squeeze(size.blocks_blooms);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Let the cache system know that a cacheable item has been used.
|
/// Let the cache system know that a cacheable item has been used.
|
||||||
fn note_used(&self, id: CacheID) {
|
fn note_used(&self, id: CacheID) {
|
||||||
let mut cache_man = self.cache_man.write().unwrap();
|
let mut cache_man = self.cache_man.write().unwrap();
|
||||||
|
@ -4,8 +4,10 @@ use blockchain::{BlockChain, BlockProvider, CacheSize};
|
|||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
use error::*;
|
use error::*;
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
|
use state::State;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use engine::Engine;
|
use engine::Engine;
|
||||||
|
use views::HeaderView;
|
||||||
use block_queue::{BlockQueue, BlockQueueInfo};
|
use block_queue::{BlockQueue, BlockQueueInfo};
|
||||||
use service::NetSyncMessage;
|
use service::NetSyncMessage;
|
||||||
use env_info::LastHashes;
|
use env_info::LastHashes;
|
||||||
@ -64,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>;
|
||||||
|
|
||||||
@ -77,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>;
|
||||||
@ -98,6 +106,11 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
|
|
||||||
/// Get blockchain information.
|
/// Get blockchain information.
|
||||||
fn chain_info(&self) -> BlockChainInfo;
|
fn chain_info(&self) -> BlockChainInfo;
|
||||||
|
|
||||||
|
/// Get the best block header.
|
||||||
|
fn best_block_header(&self) -> Bytes {
|
||||||
|
self.block_header(&self.chain_info().best_block_hash).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug, Eq, PartialEq)]
|
#[derive(Default, Clone, Debug, Eq, PartialEq)]
|
||||||
@ -121,6 +134,7 @@ impl ClientReport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
|
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
|
||||||
|
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
chain: Arc<RwLock<BlockChain>>,
|
chain: Arc<RwLock<BlockChain>>,
|
||||||
engine: Arc<Box<Engine>>,
|
engine: Arc<Box<Engine>>,
|
||||||
@ -136,11 +150,13 @@ const HISTORY: u64 = 1000;
|
|||||||
impl Client {
|
impl Client {
|
||||||
/// Create a new client with given spec and DB path.
|
/// Create a new client with given spec and DB path.
|
||||||
pub fn new(spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, Error> {
|
pub fn new(spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, Error> {
|
||||||
let chain = Arc::new(RwLock::new(BlockChain::new(&spec.genesis_block(), path)));
|
let gb = spec.genesis_block();
|
||||||
|
let chain = Arc::new(RwLock::new(BlockChain::new(&gb, path)));
|
||||||
let mut opts = Options::new();
|
let mut opts = Options::new();
|
||||||
opts.set_max_open_files(256);
|
opts.set_max_open_files(256);
|
||||||
opts.create_if_missing(true);
|
opts.create_if_missing(true);
|
||||||
/*opts.set_use_fsync(false);
|
opts.set_use_fsync(false);
|
||||||
|
/*
|
||||||
opts.set_bytes_per_sync(8388608);
|
opts.set_bytes_per_sync(8388608);
|
||||||
opts.set_disable_data_sync(false);
|
opts.set_disable_data_sync(false);
|
||||||
opts.set_block_cache_size_mb(1024);
|
opts.set_block_cache_size_mb(1024);
|
||||||
@ -177,15 +193,17 @@ impl Client {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Flush the block import queue.
|
||||||
|
pub fn flush_queue(&self) {
|
||||||
|
self.block_queue.write().unwrap().flush();
|
||||||
|
}
|
||||||
|
|
||||||
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
||||||
pub fn import_verified_blocks(&self, _io: &IoChannel<NetSyncMessage>) {
|
pub fn import_verified_blocks(&self, _io: &IoChannel<NetSyncMessage>) -> usize {
|
||||||
|
let mut ret = 0;
|
||||||
let mut bad = HashSet::new();
|
let mut bad = HashSet::new();
|
||||||
let _import_lock = self.import_lock.lock();
|
let _import_lock = self.import_lock.lock();
|
||||||
let blocks = self.block_queue.write().unwrap().drain(128);
|
let blocks = self.block_queue.write().unwrap().drain(128);
|
||||||
if blocks.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
if bad.contains(&block.header.parent_hash) {
|
if bad.contains(&block.header.parent_hash) {
|
||||||
self.block_queue.write().unwrap().mark_as_bad(&block.header.hash());
|
self.block_queue.write().unwrap().mark_as_bad(&block.header.hash());
|
||||||
@ -198,7 +216,7 @@ impl Client {
|
|||||||
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||||
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
|
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
|
||||||
bad.insert(block.header.hash());
|
bad.insert(block.header.hash());
|
||||||
return;
|
break;
|
||||||
};
|
};
|
||||||
let parent = match self.chain.read().unwrap().block_header(&header.parent_hash) {
|
let parent = match self.chain.read().unwrap().block_header(&header.parent_hash) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
@ -206,7 +224,7 @@ impl Client {
|
|||||||
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
|
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
|
||||||
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
|
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
|
||||||
bad.insert(block.header.hash());
|
bad.insert(block.header.hash());
|
||||||
return;
|
break;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// build last hashes
|
// build last hashes
|
||||||
@ -227,15 +245,15 @@ impl Client {
|
|||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||||
bad.insert(block.header.hash());
|
bad.insert(block.header.hash());
|
||||||
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
|
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Err(e) = verify_block_final(&header, result.block().header()) {
|
if let Err(e) = verify_block_final(&header, result.block().header()) {
|
||||||
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||||
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
|
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.chain.write().unwrap().insert_block(&block.bytes); //TODO: err here?
|
self.chain.write().unwrap().insert_block(&block.bytes); //TODO: err here?
|
||||||
@ -244,12 +262,14 @@ impl Client {
|
|||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!(target: "client", "State DB commit failed: {:?}", e);
|
warn!(target: "client", "State DB commit failed: {:?}", e);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.report.write().unwrap().accrue_block(&block);
|
self.report.write().unwrap().accrue_block(&block);
|
||||||
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
|
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
|
||||||
|
ret += 1;
|
||||||
}
|
}
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear cached state overlay
|
/// Clear cached state overlay
|
||||||
@ -257,6 +277,11 @@ impl Client {
|
|||||||
self.uncommited_states.write().unwrap().remove(hash);
|
self.uncommited_states.write().unwrap().remove(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a copy of the best block's state.
|
||||||
|
pub fn state(&self) -> State {
|
||||||
|
State::from_existing(self.state_db.clone(), HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce())
|
||||||
|
}
|
||||||
|
|
||||||
/// Get info on the cache.
|
/// Get info on the cache.
|
||||||
pub fn cache_info(&self) -> CacheSize {
|
pub fn cache_info(&self) -> CacheSize {
|
||||||
self.chain.read().unwrap().cache_size()
|
self.chain.read().unwrap().cache_size()
|
||||||
@ -295,6 +320,10 @@ impl BlockChainClient for Client {
|
|||||||
fn block_status(&self, hash: &H256) -> BlockStatus {
|
fn block_status(&self, hash: &H256) -> BlockStatus {
|
||||||
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))
|
||||||
@ -315,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())
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use common::*;
|
use common::*;
|
||||||
use block::Block;
|
use block::ExecutedBlock;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use evm::Schedule;
|
use evm::Schedule;
|
||||||
use evm::Factory;
|
use evm::Factory;
|
||||||
@ -37,9 +37,9 @@ pub trait Engine : Sync + Send {
|
|||||||
fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) }
|
fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) }
|
||||||
|
|
||||||
/// Block transformation functions, before and after the transactions.
|
/// Block transformation functions, before and after the transactions.
|
||||||
fn on_new_block(&self, _block: &mut Block) {}
|
fn on_new_block(&self, _block: &mut ExecutedBlock) {}
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
fn on_close_block(&self, _block: &mut Block) {}
|
fn on_close_block(&self, _block: &mut ExecutedBlock) {}
|
||||||
|
|
||||||
// TODO: consider including State in the params for verification functions.
|
// TODO: consider including State in the params for verification functions.
|
||||||
/// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block)
|
/// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block)
|
||||||
|
@ -120,7 +120,9 @@ pub enum BlockError {
|
|||||||
/// TODO [arkpar] Please document me
|
/// TODO [arkpar] Please document me
|
||||||
InvalidParentHash(Mismatch<H256>),
|
InvalidParentHash(Mismatch<H256>),
|
||||||
/// TODO [arkpar] Please document me
|
/// TODO [arkpar] Please document me
|
||||||
InvalidNumber(OutOfBounds<BlockNumber>),
|
InvalidNumber(Mismatch<BlockNumber>),
|
||||||
|
/// Block number isn't sensible.
|
||||||
|
RidiculousNumber(OutOfBounds<BlockNumber>),
|
||||||
/// TODO [arkpar] Please document me
|
/// TODO [arkpar] Please document me
|
||||||
UnknownParent(H256),
|
UnknownParent(H256),
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
@ -145,7 +147,7 @@ impl From<Error> for ImportError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Result of import block operation.
|
/// Result of import block operation.
|
||||||
pub type ImportResult = Result<(), ImportError>;
|
pub type ImportResult = Result<H256, ImportError>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// General error type which should be capable of representing all errors in ethcore.
|
/// General error type which should be capable of representing all errors in ethcore.
|
||||||
|
@ -83,7 +83,7 @@ impl Engine for Ethash {
|
|||||||
|
|
||||||
/// Apply the block reward on finalisation of the block.
|
/// Apply the block reward on finalisation of the block.
|
||||||
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
|
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
|
||||||
fn on_close_block(&self, block: &mut Block) {
|
fn on_close_block(&self, block: &mut ExecutedBlock) {
|
||||||
let reward = self.spec().engine_params.get("blockReward").map_or(U256::from(0u64), |a| decode(&a));
|
let reward = self.spec().engine_params.get("blockReward").map_or(U256::from(0u64), |a| decode(&a));
|
||||||
let fields = block.fields();
|
let fields = block.fields();
|
||||||
|
|
||||||
@ -99,13 +99,17 @@ impl Engine for Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
|
// check the seal fields.
|
||||||
|
try!(UntrustedRlp::new(&header.seal[0]).as_val::<H256>());
|
||||||
|
try!(UntrustedRlp::new(&header.seal[1]).as_val::<H64>());
|
||||||
|
|
||||||
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
|
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||||
if header.difficulty < min_difficulty {
|
if header.difficulty < min_difficulty {
|
||||||
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: min_difficulty, found: header.difficulty })))
|
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: min_difficulty, found: header.difficulty })))
|
||||||
}
|
}
|
||||||
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(quick_get_difficulty(
|
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(quick_get_difficulty(
|
||||||
&Ethash::to_ethash(header.bare_hash()),
|
&Ethash::to_ethash(header.bare_hash()),
|
||||||
header.nonce(),
|
header.nonce().low_u64(),
|
||||||
&Ethash::to_ethash(header.mix_hash()))));
|
&Ethash::to_ethash(header.mix_hash()))));
|
||||||
if difficulty < header.difficulty {
|
if difficulty < header.difficulty {
|
||||||
return Err(From::from(BlockError::InvalidEthashDifficulty(Mismatch { expected: header.difficulty, found: difficulty })));
|
return Err(From::from(BlockError::InvalidEthashDifficulty(Mismatch { expected: header.difficulty, found: difficulty })));
|
||||||
@ -114,7 +118,7 @@ impl Engine for Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
let result = self.pow.compute_light(header.number as u64, &Ethash::to_ethash(header.bare_hash()), header.nonce());
|
let result = self.pow.compute_light(header.number as u64, &Ethash::to_ethash(header.bare_hash()), header.nonce().low_u64());
|
||||||
let mix = Ethash::from_ethash(result.mix_hash);
|
let mix = Ethash::from_ethash(result.mix_hash);
|
||||||
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(result.value));
|
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(result.value));
|
||||||
if mix != header.mix_hash() {
|
if mix != header.mix_hash() {
|
||||||
@ -204,7 +208,7 @@ impl Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Header {
|
impl Header {
|
||||||
fn nonce(&self) -> u64 {
|
fn nonce(&self) -> H64 {
|
||||||
decode(&self.seal()[1])
|
decode(&self.seal()[1])
|
||||||
}
|
}
|
||||||
fn mix_hash(&self) -> H256 {
|
fn mix_hash(&self) -> H256 {
|
||||||
|
@ -26,7 +26,7 @@ pub enum MessageCallResult {
|
|||||||
Failed
|
Failed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO [debris] Please document me
|
/// Externalities interface for EVMs
|
||||||
pub trait Ext {
|
pub trait Ext {
|
||||||
/// Returns a value for given key.
|
/// Returns a value for given key.
|
||||||
fn storage_at(&self, key: &H256) -> H256;
|
fn storage_at(&self, key: &H256) -> H256;
|
||||||
@ -55,8 +55,9 @@ pub trait Ext {
|
|||||||
/// and true if subcall was successfull.
|
/// and true if subcall was successfull.
|
||||||
fn call(&mut self,
|
fn call(&mut self,
|
||||||
gas: &U256,
|
gas: &U256,
|
||||||
address: &Address,
|
sender_address: &Address,
|
||||||
value: &U256,
|
receive_address: &Address,
|
||||||
|
value: Option<U256>,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
code_address: &Address,
|
code_address: &Address,
|
||||||
output: &mut [u8]) -> MessageCallResult;
|
output: &mut [u8]) -> MessageCallResult;
|
||||||
|
@ -571,16 +571,10 @@ impl Interpreter {
|
|||||||
let call_gas = stack.pop_back();
|
let call_gas = stack.pop_back();
|
||||||
let code_address = stack.pop_back();
|
let code_address = stack.pop_back();
|
||||||
let code_address = u256_to_address(&code_address);
|
let code_address = u256_to_address(&code_address);
|
||||||
let is_delegatecall = instruction == instructions::DELEGATECALL;
|
|
||||||
|
|
||||||
let value = match is_delegatecall {
|
let value = match instruction == instructions::DELEGATECALL {
|
||||||
true => params.value,
|
true => None,
|
||||||
false => stack.pop_back()
|
false => Some(stack.pop_back())
|
||||||
};
|
|
||||||
|
|
||||||
let address = match instruction == instructions::CALL {
|
|
||||||
true => &code_address,
|
|
||||||
false => ¶ms.address
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let in_off = stack.pop_back();
|
let in_off = stack.pop_back();
|
||||||
@ -588,13 +582,27 @@ impl Interpreter {
|
|||||||
let out_off = stack.pop_back();
|
let out_off = stack.pop_back();
|
||||||
let out_size = stack.pop_back();
|
let out_size = stack.pop_back();
|
||||||
|
|
||||||
let call_gas = call_gas + match !is_delegatecall && value > U256::zero() {
|
// Add stipend (only CALL|CALLCODE when value > 0)
|
||||||
|
let call_gas = call_gas + value.map_or_else(U256::zero, |val| match val > U256::zero() {
|
||||||
true => U256::from(ext.schedule().call_stipend),
|
true => U256::from(ext.schedule().call_stipend),
|
||||||
false => U256::zero()
|
false => U256::zero()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get sender & receive addresses, check if we have balance
|
||||||
|
let (sender_address, receive_address, has_balance) = match instruction {
|
||||||
|
instructions::CALL => {
|
||||||
|
let has_balance = ext.balance(¶ms.address) >= value.unwrap();
|
||||||
|
(¶ms.address, &code_address, has_balance)
|
||||||
|
},
|
||||||
|
instructions::CALLCODE => {
|
||||||
|
let has_balance = ext.balance(¶ms.address) >= value.unwrap();
|
||||||
|
(¶ms.address, ¶ms.address, has_balance)
|
||||||
|
},
|
||||||
|
instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true),
|
||||||
|
_ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction))
|
||||||
};
|
};
|
||||||
|
|
||||||
let can_call = (is_delegatecall || ext.balance(¶ms.address) >= value) && ext.depth() < ext.schedule().max_depth;
|
let can_call = has_balance && ext.depth() < ext.schedule().max_depth;
|
||||||
|
|
||||||
if !can_call {
|
if !can_call {
|
||||||
stack.push(U256::zero());
|
stack.push(U256::zero());
|
||||||
return Ok(InstructionResult::UnusedGas(call_gas));
|
return Ok(InstructionResult::UnusedGas(call_gas));
|
||||||
@ -605,7 +613,7 @@ impl Interpreter {
|
|||||||
// and we don't want to copy
|
// and we don't want to copy
|
||||||
let input = unsafe { ::std::mem::transmute(mem.read_slice(in_off, in_size)) };
|
let input = unsafe { ::std::mem::transmute(mem.read_slice(in_off, in_size)) };
|
||||||
let output = mem.writeable_slice(out_off, out_size);
|
let output = mem.writeable_slice(out_off, out_size);
|
||||||
ext.call(&call_gas, address, &value, input, &code_address, output)
|
ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output)
|
||||||
};
|
};
|
||||||
|
|
||||||
return match call_result {
|
return match call_result {
|
||||||
@ -712,7 +720,10 @@ impl Interpreter {
|
|||||||
stack.push(address_to_u256(params.sender.clone()));
|
stack.push(address_to_u256(params.sender.clone()));
|
||||||
},
|
},
|
||||||
instructions::CALLVALUE => {
|
instructions::CALLVALUE => {
|
||||||
stack.push(params.value.clone());
|
stack.push(match params.value {
|
||||||
|
ActionValue::Transfer(val) => val,
|
||||||
|
ActionValue::Apparent(val) => val,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
instructions::CALLDATALOAD => {
|
instructions::CALLDATALOAD => {
|
||||||
let big_id = stack.pop_back();
|
let big_id = stack.pop_back();
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,8 +61,9 @@ impl Ext for FakeExt {
|
|||||||
|
|
||||||
fn call(&mut self,
|
fn call(&mut self,
|
||||||
_gas: &U256,
|
_gas: &U256,
|
||||||
_address: &Address,
|
_sender_address: &Address,
|
||||||
_value: &U256,
|
_receive_address: &Address,
|
||||||
|
_value: Option<U256>,
|
||||||
_data: &[u8],
|
_data: &[u8],
|
||||||
_code_address: &Address,
|
_code_address: &Address,
|
||||||
_output: &mut [u8]) -> MessageCallResult {
|
_output: &mut [u8]) -> MessageCallResult {
|
||||||
@ -110,7 +111,7 @@ fn test_stack_underflow() {
|
|||||||
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
let code = "01600055".from_hex().unwrap();
|
let code = "01600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
@ -137,7 +138,7 @@ fn test_add(factory: super::Factory) {
|
|||||||
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055".from_hex().unwrap();
|
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
@ -157,7 +158,7 @@ fn test_sha3(factory: super::Factory) {
|
|||||||
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
let code = "6000600020600055".from_hex().unwrap();
|
let code = "6000600020600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
@ -177,7 +178,7 @@ fn test_address(factory: super::Factory) {
|
|||||||
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
let code = "30600055".from_hex().unwrap();
|
let code = "30600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
@ -198,7 +199,7 @@ fn test_origin(factory: super::Factory) {
|
|||||||
let origin = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
|
let origin = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
|
||||||
let code = "32600055".from_hex().unwrap();
|
let code = "32600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.origin = origin.clone();
|
params.origin = origin.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
@ -214,13 +215,14 @@ 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();
|
||||||
let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
|
let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
|
||||||
let code = "33600055".from_hex().unwrap();
|
let code = "33600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.sender = sender.clone();
|
params.sender = sender.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
@ -254,7 +256,7 @@ fn test_extcodecopy(factory: super::Factory) {
|
|||||||
let code = "333b60006000333c600051600055".from_hex().unwrap();
|
let code = "333b60006000333c600051600055".from_hex().unwrap();
|
||||||
let sender_code = "6005600055".from_hex().unwrap();
|
let sender_code = "6005600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.sender = sender.clone();
|
params.sender = sender.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
@ -276,7 +278,7 @@ fn test_log_empty(factory: super::Factory) {
|
|||||||
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
let code = "60006000a0".from_hex().unwrap();
|
let code = "60006000a0".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
@ -307,7 +309,7 @@ fn test_log_sender(factory: super::Factory) {
|
|||||||
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
||||||
let code = "60ff6000533360206000a1".from_hex().unwrap();
|
let code = "60ff6000533360206000a1".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.sender = sender.clone();
|
params.sender = sender.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
@ -332,7 +334,7 @@ fn test_blockhash(factory: super::Factory) {
|
|||||||
let code = "600040600055".from_hex().unwrap();
|
let code = "600040600055".from_hex().unwrap();
|
||||||
let blockhash = H256::from_str("123400000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
|
let blockhash = H256::from_str("123400000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
@ -354,7 +356,7 @@ fn test_calldataload(factory: super::Factory) {
|
|||||||
let code = "600135600055".from_hex().unwrap();
|
let code = "600135600055".from_hex().unwrap();
|
||||||
let data = "0123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23".from_hex().unwrap();
|
let data = "0123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
@ -376,7 +378,7 @@ fn test_author(factory: super::Factory) {
|
|||||||
let author = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
let author = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
let code = "41600055".from_hex().unwrap();
|
let code = "41600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
let mut ext = FakeExt::new();
|
let mut ext = FakeExt::new();
|
||||||
@ -396,7 +398,7 @@ fn test_timestamp(factory: super::Factory) {
|
|||||||
let timestamp = 0x1234;
|
let timestamp = 0x1234;
|
||||||
let code = "42600055".from_hex().unwrap();
|
let code = "42600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
let mut ext = FakeExt::new();
|
let mut ext = FakeExt::new();
|
||||||
@ -416,7 +418,7 @@ fn test_number(factory: super::Factory) {
|
|||||||
let number = 0x1234;
|
let number = 0x1234;
|
||||||
let code = "43600055".from_hex().unwrap();
|
let code = "43600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
let mut ext = FakeExt::new();
|
let mut ext = FakeExt::new();
|
||||||
@ -436,7 +438,7 @@ fn test_difficulty(factory: super::Factory) {
|
|||||||
let difficulty = U256::from(0x1234);
|
let difficulty = U256::from(0x1234);
|
||||||
let code = "44600055".from_hex().unwrap();
|
let code = "44600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
let mut ext = FakeExt::new();
|
let mut ext = FakeExt::new();
|
||||||
@ -456,7 +458,7 @@ fn test_gas_limit(factory: super::Factory) {
|
|||||||
let gas_limit = U256::from(0x1234);
|
let gas_limit = U256::from(0x1234);
|
||||||
let code = "45600055".from_hex().unwrap();
|
let code = "45600055".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code);
|
params.code = Some(code);
|
||||||
let mut ext = FakeExt::new();
|
let mut ext = FakeExt::new();
|
||||||
|
@ -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 {
|
||||||
@ -133,7 +139,7 @@ impl<'a> Executive<'a> {
|
|||||||
origin: sender.clone(),
|
origin: sender.clone(),
|
||||||
gas: init_gas,
|
gas: init_gas,
|
||||||
gas_price: t.gas_price,
|
gas_price: t.gas_price,
|
||||||
value: t.value,
|
value: ActionValue::Transfer(t.value),
|
||||||
code: Some(t.data.clone()),
|
code: Some(t.data.clone()),
|
||||||
data: None,
|
data: None,
|
||||||
};
|
};
|
||||||
@ -147,7 +153,7 @@ impl<'a> Executive<'a> {
|
|||||||
origin: sender.clone(),
|
origin: sender.clone(),
|
||||||
gas: init_gas,
|
gas: init_gas,
|
||||||
gas_price: t.gas_price,
|
gas_price: t.gas_price,
|
||||||
value: t.value,
|
value: ActionValue::Transfer(t.value),
|
||||||
code: self.state.code(address),
|
code: self.state.code(address),
|
||||||
data: Some(t.data.clone()),
|
data: Some(t.data.clone()),
|
||||||
};
|
};
|
||||||
@ -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.
|
||||||
@ -170,7 +197,9 @@ impl<'a> Executive<'a> {
|
|||||||
let backup = self.state.clone();
|
let backup = self.state.clone();
|
||||||
|
|
||||||
// at first, transfer value to destination
|
// at first, transfer value to destination
|
||||||
self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
|
if let ActionValue::Transfer(val) = params.value {
|
||||||
|
self.state.transfer_balance(¶ms.sender, ¶ms.address, &val);
|
||||||
|
}
|
||||||
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
||||||
|
|
||||||
if self.engine.is_builtin(¶ms.code_address) {
|
if self.engine.is_builtin(¶ms.code_address) {
|
||||||
@ -198,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);
|
||||||
@ -227,11 +255,12 @@ impl<'a> Executive<'a> {
|
|||||||
self.state.new_contract(¶ms.address);
|
self.state.new_contract(¶ms.address);
|
||||||
|
|
||||||
// then transfer value to it
|
// then transfer value to it
|
||||||
self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
|
if let ActionValue::Transfer(val) = params.value {
|
||||||
|
self.state.transfer_balance(¶ms.sender, ¶ms.address, &val);
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
@ -266,13 +295,11 @@ impl<'a> Executive<'a> {
|
|||||||
|
|
||||||
// perform suicides
|
// perform suicides
|
||||||
for address in &substate.suicides {
|
for address in &substate.suicides {
|
||||||
trace!("Killing {}", address);
|
|
||||||
self.state.kill_account(address);
|
self.state.kill_account(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
@ -297,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 {..})
|
||||||
@ -363,12 +389,12 @@ mod tests {
|
|||||||
fn test_sender_balance(factory: Factory) {
|
fn test_sender_balance(factory: Factory) {
|
||||||
let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
let address = contract_address(&sender, &U256::zero());
|
let address = contract_address(&sender, &U256::zero());
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.sender = sender.clone();
|
params.sender = sender.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some("3331600055".from_hex().unwrap());
|
params.code = Some("3331600055".from_hex().unwrap());
|
||||||
params.value = U256::from(0x7);
|
params.value = ActionValue::Transfer(U256::from(0x7));
|
||||||
let mut state = State::new_temp();
|
let mut state = State::new_temp();
|
||||||
state.add_balance(&sender, &U256::from(0x100u64));
|
state.add_balance(&sender, &U256::from(0x100u64));
|
||||||
let info = EnvInfo::new();
|
let info = EnvInfo::new();
|
||||||
@ -420,13 +446,13 @@ mod tests {
|
|||||||
let address = contract_address(&sender, &U256::zero());
|
let address = contract_address(&sender, &U256::zero());
|
||||||
// TODO: add tests for 'callcreate'
|
// TODO: add tests for 'callcreate'
|
||||||
//let next_address = contract_address(&address, &U256::zero());
|
//let next_address = contract_address(&address, &U256::zero());
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.sender = sender.clone();
|
params.sender = sender.clone();
|
||||||
params.origin = sender.clone();
|
params.origin = sender.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code.clone());
|
params.code = Some(code.clone());
|
||||||
params.value = U256::from(100);
|
params.value = ActionValue::Transfer(U256::from(100));
|
||||||
let mut state = State::new_temp();
|
let mut state = State::new_temp();
|
||||||
state.add_balance(&sender, &U256::from(100));
|
state.add_balance(&sender, &U256::from(100));
|
||||||
let info = EnvInfo::new();
|
let info = EnvInfo::new();
|
||||||
@ -473,13 +499,13 @@ mod tests {
|
|||||||
let address = contract_address(&sender, &U256::zero());
|
let address = contract_address(&sender, &U256::zero());
|
||||||
// TODO: add tests for 'callcreate'
|
// TODO: add tests for 'callcreate'
|
||||||
//let next_address = contract_address(&address, &U256::zero());
|
//let next_address = contract_address(&address, &U256::zero());
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.sender = sender.clone();
|
params.sender = sender.clone();
|
||||||
params.origin = sender.clone();
|
params.origin = sender.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code.clone());
|
params.code = Some(code.clone());
|
||||||
params.value = U256::from(100);
|
params.value = ActionValue::Transfer(U256::from(100));
|
||||||
let mut state = State::new_temp();
|
let mut state = State::new_temp();
|
||||||
state.add_balance(&sender, &U256::from(100));
|
state.add_balance(&sender, &U256::from(100));
|
||||||
let info = EnvInfo::new();
|
let info = EnvInfo::new();
|
||||||
@ -524,13 +550,13 @@ mod tests {
|
|||||||
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
||||||
let address = contract_address(&sender, &U256::zero());
|
let address = contract_address(&sender, &U256::zero());
|
||||||
let next_address = contract_address(&address, &U256::zero());
|
let next_address = contract_address(&address, &U256::zero());
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.sender = sender.clone();
|
params.sender = sender.clone();
|
||||||
params.origin = sender.clone();
|
params.origin = sender.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code.clone());
|
params.code = Some(code.clone());
|
||||||
params.value = U256::from(100);
|
params.value = ActionValue::Transfer(U256::from(100));
|
||||||
let mut state = State::new_temp();
|
let mut state = State::new_temp();
|
||||||
state.add_balance(&sender, &U256::from(100));
|
state.add_balance(&sender, &U256::from(100));
|
||||||
let info = EnvInfo::new();
|
let info = EnvInfo::new();
|
||||||
@ -580,12 +606,12 @@ mod tests {
|
|||||||
let address_b = Address::from_str("945304eb96065b2a98b57a48a06ae28d285a71b5" ).unwrap();
|
let address_b = Address::from_str("945304eb96065b2a98b57a48a06ae28d285a71b5" ).unwrap();
|
||||||
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address_a.clone();
|
params.address = address_a.clone();
|
||||||
params.sender = sender.clone();
|
params.sender = sender.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code_a.clone());
|
params.code = Some(code_a.clone());
|
||||||
params.value = U256::from(100_000);
|
params.value = ActionValue::Transfer(U256::from(100_000));
|
||||||
|
|
||||||
let mut state = State::new_temp();
|
let mut state = State::new_temp();
|
||||||
state.init_code(&address_a, code_a.clone());
|
state.init_code(&address_a, code_a.clone());
|
||||||
@ -629,7 +655,7 @@ mod tests {
|
|||||||
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
||||||
let code = "600160005401600055600060006000600060003060e05a03f1600155".from_hex().unwrap();
|
let code = "600160005401600055600060006000600060003060e05a03f1600155".from_hex().unwrap();
|
||||||
let address = contract_address(&sender, &U256::zero());
|
let address = contract_address(&sender, &U256::zero());
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code.clone());
|
params.code = Some(code.clone());
|
||||||
@ -785,13 +811,13 @@ mod tests {
|
|||||||
let address = contract_address(&sender, &U256::zero());
|
let address = contract_address(&sender, &U256::zero());
|
||||||
// TODO: add tests for 'callcreate'
|
// TODO: add tests for 'callcreate'
|
||||||
//let next_address = contract_address(&address, &U256::zero());
|
//let next_address = contract_address(&address, &U256::zero());
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
params.address = address.clone();
|
params.address = address.clone();
|
||||||
params.sender = sender.clone();
|
params.sender = sender.clone();
|
||||||
params.origin = sender.clone();
|
params.origin = sender.clone();
|
||||||
params.gas = U256::from(0x0186a0);
|
params.gas = U256::from(0x0186a0);
|
||||||
params.code = Some(code.clone());
|
params.code = Some(code.clone());
|
||||||
params.value = U256::from_str("0de0b6b3a7640000").unwrap();
|
params.value = ActionValue::Transfer(U256::from_str("0de0b6b3a7640000").unwrap());
|
||||||
let mut state = State::new_temp();
|
let mut state = State::new_temp();
|
||||||
state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap());
|
state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap());
|
||||||
let info = EnvInfo::new();
|
let info = EnvInfo::new();
|
||||||
|
@ -19,7 +19,8 @@ pub enum OutputPolicy<'a> {
|
|||||||
pub struct OriginInfo {
|
pub struct OriginInfo {
|
||||||
address: Address,
|
address: Address,
|
||||||
origin: Address,
|
origin: Address,
|
||||||
gas_price: U256
|
gas_price: U256,
|
||||||
|
value: U256
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OriginInfo {
|
impl OriginInfo {
|
||||||
@ -28,7 +29,11 @@ impl OriginInfo {
|
|||||||
OriginInfo {
|
OriginInfo {
|
||||||
address: params.address.clone(),
|
address: params.address.clone(),
|
||||||
origin: params.origin.clone(),
|
origin: params.origin.clone(),
|
||||||
gas_price: params.gas_price.clone()
|
gas_price: params.gas_price.clone(),
|
||||||
|
value: match params.value {
|
||||||
|
ActionValue::Transfer(val) => val,
|
||||||
|
ActionValue::Apparent(val) => val,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +116,7 @@ impl<'a> Ext for Externalities<'a> {
|
|||||||
origin: self.origin_info.origin.clone(),
|
origin: self.origin_info.origin.clone(),
|
||||||
gas: *gas,
|
gas: *gas,
|
||||||
gas_price: self.origin_info.gas_price.clone(),
|
gas_price: self.origin_info.gas_price.clone(),
|
||||||
value: value.clone(),
|
value: ActionValue::Transfer(value.clone()),
|
||||||
code: Some(code.to_vec()),
|
code: Some(code.to_vec()),
|
||||||
data: None,
|
data: None,
|
||||||
};
|
};
|
||||||
@ -131,24 +136,29 @@ impl<'a> Ext for Externalities<'a> {
|
|||||||
|
|
||||||
fn call(&mut self,
|
fn call(&mut self,
|
||||||
gas: &U256,
|
gas: &U256,
|
||||||
address: &Address,
|
sender_address: &Address,
|
||||||
value: &U256,
|
receive_address: &Address,
|
||||||
|
value: Option<U256>,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
code_address: &Address,
|
code_address: &Address,
|
||||||
output: &mut [u8]) -> MessageCallResult {
|
output: &mut [u8]) -> MessageCallResult {
|
||||||
|
|
||||||
let params = ActionParams {
|
let mut params = ActionParams {
|
||||||
|
sender: sender_address.clone(),
|
||||||
|
address: receive_address.clone(),
|
||||||
|
value: ActionValue::Apparent(self.origin_info.value.clone()),
|
||||||
code_address: code_address.clone(),
|
code_address: code_address.clone(),
|
||||||
address: address.clone(),
|
|
||||||
sender: self.origin_info.address.clone(),
|
|
||||||
origin: self.origin_info.origin.clone(),
|
origin: self.origin_info.origin.clone(),
|
||||||
gas: *gas,
|
gas: *gas,
|
||||||
gas_price: self.origin_info.gas_price.clone(),
|
gas_price: self.origin_info.gas_price.clone(),
|
||||||
value: value.clone(),
|
|
||||||
code: self.state.code(code_address),
|
code: self.state.code(code_address),
|
||||||
data: Some(data.to_vec()),
|
data: Some(data.to_vec()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(value) = value {
|
||||||
|
params.value = ActionValue::Transfer(value);
|
||||||
|
}
|
||||||
|
|
||||||
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth);
|
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth);
|
||||||
|
|
||||||
match ex.call(params, self.substate, BytesRef::Fixed(output)) {
|
match ex.call(params, self.substate, BytesRef::Fixed(output)) {
|
||||||
|
@ -11,7 +11,7 @@ pub type BlockNumber = u64;
|
|||||||
/// which is non-specific.
|
/// which is non-specific.
|
||||||
///
|
///
|
||||||
/// Doesn't do all that much on its own.
|
/// Doesn't do all that much on its own.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
// TODO: make all private.
|
// TODO: make all private.
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use util::*;
|
|||||||
use basic_types::LogBloom;
|
use basic_types::LogBloom;
|
||||||
|
|
||||||
/// A single log's entry.
|
/// A single log's entry.
|
||||||
#[derive(Debug,PartialEq,Eq)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct LogEntry {
|
pub struct LogEntry {
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub address: Address,
|
pub address: Address,
|
||||||
|
@ -31,7 +31,7 @@ impl PodAccount {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO [Gav Wood] Please document me
|
/// Returns the RLP for this account.
|
||||||
pub fn rlp(&self) -> Bytes {
|
pub fn rlp(&self) -> Bytes {
|
||||||
let mut stream = RlpStream::new_list(4);
|
let mut stream = RlpStream::new_list(4);
|
||||||
stream.append(&self.nonce);
|
stream.append(&self.nonce);
|
||||||
@ -40,6 +40,18 @@ impl PodAccount {
|
|||||||
stream.append(&self.code.sha3());
|
stream.append(&self.code.sha3());
|
||||||
stream.out()
|
stream.out()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Place additional data into given hash DB.
|
||||||
|
pub fn insert_additional(&self, db: &mut HashDB) {
|
||||||
|
if !self.code.is_empty() {
|
||||||
|
db.insert(&self.code);
|
||||||
|
}
|
||||||
|
let mut r = H256::new();
|
||||||
|
let mut t = SecTrieDBMut::new(db, &mut r);
|
||||||
|
for (k, v) in &self.storage {
|
||||||
|
t.insert(k, &encode(&U256::from(v.as_slice())));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for PodAccount {
|
impl fmt::Display for PodAccount {
|
||||||
|
@ -1,17 +1,25 @@
|
|||||||
use util::*;
|
use util::*;
|
||||||
use pod_account::*;
|
use pod_account::*;
|
||||||
|
|
||||||
#[derive(Debug,Clone,PartialEq,Eq)]
|
#[derive(Debug,Clone,PartialEq,Eq,Default)]
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub struct PodState (BTreeMap<Address, PodAccount>);
|
pub struct PodState (BTreeMap<Address, PodAccount>);
|
||||||
|
|
||||||
impl PodState {
|
impl PodState {
|
||||||
/// Contruct a new object from the `m`.
|
/// Contruct a new object from the `m`.
|
||||||
pub fn new(m: BTreeMap<Address, PodAccount>) -> PodState { PodState(m) }
|
pub fn new() -> PodState { Default::default() }
|
||||||
|
|
||||||
|
/// Contruct a new object from the `m`.
|
||||||
|
pub fn from(m: BTreeMap<Address, PodAccount>) -> PodState { PodState(m) }
|
||||||
|
|
||||||
/// Get the underlying map.
|
/// Get the underlying map.
|
||||||
pub fn get(&self) -> &BTreeMap<Address, PodAccount> { &self.0 }
|
pub fn get(&self) -> &BTreeMap<Address, PodAccount> { &self.0 }
|
||||||
|
|
||||||
|
/// Get the root hash of the trie of the RLP of this.
|
||||||
|
pub fn root(&self) -> H256 {
|
||||||
|
sec_trie_root(self.0.iter().map(|(k, v)| (k.to_vec(), v.rlp())).collect())
|
||||||
|
}
|
||||||
|
|
||||||
/// Drain object to get the underlying map.
|
/// Drain object to get the underlying map.
|
||||||
pub fn drain(self) -> BTreeMap<Address, PodAccount> { self.0 }
|
pub fn drain(self) -> BTreeMap<Address, PodAccount> { self.0 }
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use basic_types::LogBloom;
|
|||||||
use log_entry::LogEntry;
|
use log_entry::LogEntry;
|
||||||
|
|
||||||
/// Information describing execution of a transaction.
|
/// Information describing execution of a transaction.
|
||||||
#[derive(Debug)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct Receipt {
|
pub struct Receipt {
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub state_root: H256,
|
pub state_root: H256,
|
||||||
|
89
src/spec.rs
89
src/spec.rs
@ -1,6 +1,7 @@
|
|||||||
use common::*;
|
use common::*;
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use engine::*;
|
use engine::*;
|
||||||
|
use pod_state::*;
|
||||||
use null_engine::*;
|
use null_engine::*;
|
||||||
|
|
||||||
/// Converts file from base64 gzipped bytes to json
|
/// Converts file from base64 gzipped bytes to json
|
||||||
@ -40,28 +41,6 @@ fn json_to_rlp_map(json: &Json) -> HashMap<String, Bytes> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: add code and data
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// Genesis account data. Does no thave a DB overlay cache
|
|
||||||
pub struct GenesisAccount {
|
|
||||||
// Balance of the account.
|
|
||||||
balance: U256,
|
|
||||||
// Nonce of the account.
|
|
||||||
nonce: U256,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GenesisAccount {
|
|
||||||
/// TODO [arkpar] Please document me
|
|
||||||
pub fn rlp(&self) -> Bytes {
|
|
||||||
let mut stream = RlpStream::new_list(4);
|
|
||||||
stream.append(&self.nonce);
|
|
||||||
stream.append(&self.balance);
|
|
||||||
stream.append(&SHA3_NULL_RLP);
|
|
||||||
stream.append(&SHA3_EMPTY);
|
|
||||||
stream.out()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parameters for a block chain; includes both those intrinsic to the design of the
|
/// Parameters for a block chain; includes both those intrinsic to the design of the
|
||||||
/// chain and those to be interpreted by the active chain engine.
|
/// chain and those to be interpreted by the active chain engine.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -83,7 +62,7 @@ pub struct Spec {
|
|||||||
|
|
||||||
// Builtin-contracts are here for now but would like to abstract into Engine API eventually.
|
// Builtin-contracts are here for now but would like to abstract into Engine API eventually.
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub builtins: HashMap<Address, Builtin>,
|
pub builtins: BTreeMap<Address, Builtin>,
|
||||||
|
|
||||||
// Genesis params.
|
// Genesis params.
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
@ -101,7 +80,7 @@ pub struct Spec {
|
|||||||
/// TODO [arkpar] Please document me
|
/// TODO [arkpar] Please document me
|
||||||
pub extra_data: Bytes,
|
pub extra_data: Bytes,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub genesis_state: HashMap<Address, GenesisAccount>,
|
genesis_state: PodState,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub seal_fields: usize,
|
pub seal_fields: usize,
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
@ -126,7 +105,7 @@ impl Spec {
|
|||||||
/// Return the state root for the genesis state, memoising accordingly.
|
/// Return the state root for the genesis state, memoising accordingly.
|
||||||
pub fn state_root(&self) -> H256 {
|
pub fn state_root(&self) -> H256 {
|
||||||
if self.state_root_memo.read().unwrap().is_none() {
|
if self.state_root_memo.read().unwrap().is_none() {
|
||||||
*self.state_root_memo.write().unwrap() = Some(sec_trie_root(self.genesis_state.iter().map(|(k, v)| (k.to_vec(), v.rlp())).collect()));
|
*self.state_root_memo.write().unwrap() = Some(self.genesis_state.root());
|
||||||
}
|
}
|
||||||
self.state_root_memo.read().unwrap().as_ref().unwrap().clone()
|
self.state_root_memo.read().unwrap().as_ref().unwrap().clone()
|
||||||
}
|
}
|
||||||
@ -174,6 +153,46 @@ impl Spec {
|
|||||||
ret.append_raw(&empty_list, 1);
|
ret.append_raw(&empty_list, 1);
|
||||||
ret.out()
|
ret.out()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Overwrite the genesis components with the given JSON, assuming standard Ethereum test format.
|
||||||
|
pub fn overwrite_genesis(&mut self, genesis: &Json) {
|
||||||
|
let (seal_fields, seal_rlp) = {
|
||||||
|
if genesis.find("mixHash").is_some() && genesis.find("nonce").is_some() {
|
||||||
|
let mut s = RlpStream::new();
|
||||||
|
s.append(&H256::from_json(&genesis["mixHash"]));
|
||||||
|
s.append(&H64::from_json(&genesis["nonce"]));
|
||||||
|
(2, s.out())
|
||||||
|
} else {
|
||||||
|
// backup algo that will work with sealFields/sealRlp (and without).
|
||||||
|
(
|
||||||
|
u64::from_json(&genesis["sealFields"]) as usize,
|
||||||
|
Bytes::from_json(&genesis["sealRlp"])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.parent_hash = H256::from_json(&genesis["parentHash"]);
|
||||||
|
self.author = Address::from_json(&genesis["coinbase"]);
|
||||||
|
self.difficulty = U256::from_json(&genesis["difficulty"]);
|
||||||
|
self.gas_limit = U256::from_json(&genesis["gasLimit"]);
|
||||||
|
self.gas_used = U256::from_json(&genesis["gasUsed"]);
|
||||||
|
self.timestamp = u64::from_json(&genesis["timestamp"]);
|
||||||
|
self.extra_data = Bytes::from_json(&genesis["extraData"]);
|
||||||
|
self.seal_fields = seal_fields;
|
||||||
|
self.seal_rlp = seal_rlp;
|
||||||
|
self.state_root_memo = RwLock::new(genesis.find("stateRoot").and_then(|_| Some(H256::from_json(&genesis["stateRoot"]))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alter the value of the genesis state.
|
||||||
|
pub fn set_genesis_state(&mut self, s: PodState) {
|
||||||
|
self.genesis_state = s;
|
||||||
|
*self.state_root_memo.write().unwrap() = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `false` if the memoized state root is invalid. `true` otherwise.
|
||||||
|
pub fn is_state_root_valid(&self) -> bool {
|
||||||
|
self.state_root_memo.read().unwrap().clone().map_or(true, |sr| sr == self.genesis_state.root())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromJson for Spec {
|
impl FromJson for Spec {
|
||||||
@ -181,8 +200,8 @@ impl FromJson for Spec {
|
|||||||
fn from_json(json: &Json) -> Spec {
|
fn from_json(json: &Json) -> Spec {
|
||||||
// once we commit ourselves to some json parsing library (serde?)
|
// once we commit ourselves to some json parsing library (serde?)
|
||||||
// move it to proper data structure
|
// move it to proper data structure
|
||||||
let mut state = HashMap::new();
|
let mut builtins = BTreeMap::new();
|
||||||
let mut builtins = HashMap::new();
|
let mut state = PodState::new();
|
||||||
|
|
||||||
if let Some(&Json::Object(ref accounts)) = json.find("accounts") {
|
if let Some(&Json::Object(ref accounts)) = json.find("accounts") {
|
||||||
for (address, acc) in accounts.iter() {
|
for (address, acc) in accounts.iter() {
|
||||||
@ -192,15 +211,8 @@ impl FromJson for Spec {
|
|||||||
builtins.insert(addr.clone(), builtin);
|
builtins.insert(addr.clone(), builtin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let balance = acc.find("balance").and_then(|x| match *x { Json::String(ref b) => U256::from_dec_str(b).ok(), _ => None });
|
|
||||||
let nonce = acc.find("nonce").and_then(|x| match *x { Json::String(ref b) => U256::from_dec_str(b).ok(), _ => None });
|
|
||||||
// let balance = if let Some(&Json::String(ref b)) = acc.find("balance") {U256::from_dec_str(b).unwrap_or(U256::from(0))} else {U256::from(0)};
|
|
||||||
// let nonce = if let Some(&Json::String(ref n)) = acc.find("nonce") {U256::from_dec_str(n).unwrap_or(U256::from(0))} else {U256::from(0)};
|
|
||||||
// TODO: handle code & data if they exist.
|
|
||||||
if balance.is_some() || nonce.is_some() {
|
|
||||||
state.insert(addr, GenesisAccount { balance: balance.unwrap_or_else(U256::zero), nonce: nonce.unwrap_or_else(U256::zero) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
state = xjson!(&json["accounts"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let nodes = if let Some(&Json::Array(ref ns)) = json.find("nodes") {
|
let nodes = if let Some(&Json::Array(ref ns)) = json.find("nodes") {
|
||||||
@ -249,16 +261,17 @@ impl Spec {
|
|||||||
/// Ensure that the given state DB has the trie nodes in for the genesis state.
|
/// Ensure that the given state DB has the trie nodes in for the genesis state.
|
||||||
pub fn ensure_db_good(&self, db: &mut HashDB) -> bool {
|
pub fn ensure_db_good(&self, db: &mut HashDB) -> bool {
|
||||||
if !db.contains(&self.state_root()) {
|
if !db.contains(&self.state_root()) {
|
||||||
info!("Populating genesis state...");
|
|
||||||
let mut root = H256::new();
|
let mut root = H256::new();
|
||||||
{
|
{
|
||||||
let mut t = SecTrieDBMut::new(db, &mut root);
|
let mut t = SecTrieDBMut::new(db, &mut root);
|
||||||
for (address, account) in &self.genesis_state {
|
for (address, account) in self.genesis_state.get().iter() {
|
||||||
t.insert(address.as_slice(), &account.rlp());
|
t.insert(address.as_slice(), &account.rlp());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (_, account) in self.genesis_state.get().iter() {
|
||||||
|
account.insert_additional(db);
|
||||||
|
}
|
||||||
assert!(db.contains(&self.state_root()));
|
assert!(db.contains(&self.state_root()));
|
||||||
info!("Genesis state is ready");
|
|
||||||
true
|
true
|
||||||
} else { false }
|
} else { false }
|
||||||
}
|
}
|
||||||
|
12
src/state.rs
12
src/state.rs
@ -3,7 +3,7 @@ use engine::Engine;
|
|||||||
use executive::Executive;
|
use executive::Executive;
|
||||||
use pod_account::*;
|
use pod_account::*;
|
||||||
use pod_state::*;
|
use pod_state::*;
|
||||||
use state_diff::*;
|
//use state_diff::*; // TODO: uncomment once to_pod() works correctly.
|
||||||
|
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub type ApplyResult = Result<Receipt, Error>;
|
pub type ApplyResult = Result<Receipt, Error>;
|
||||||
@ -145,16 +145,16 @@ impl State {
|
|||||||
/// Execute a given transaction.
|
/// Execute a given transaction.
|
||||||
/// This will change the state accordingly.
|
/// This will change the state accordingly.
|
||||||
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &Transaction) -> ApplyResult {
|
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &Transaction) -> ApplyResult {
|
||||||
|
// let old = self.to_pod();
|
||||||
let old = self.to_pod();
|
|
||||||
|
|
||||||
let e = try!(Executive::new(self, env_info, engine).transact(t));
|
let e = try!(Executive::new(self, env_info, engine).transact(t));
|
||||||
//println!("Executed: {:?}", e);
|
//println!("Executed: {:?}", e);
|
||||||
|
|
||||||
trace!("Applied transaction. Diff:\n{}\n", StateDiff::diff_pod(&old, &self.to_pod()));
|
// TODO uncomment once to_pod() works correctly.
|
||||||
|
// trace!("Applied transaction. Diff:\n{}\n", StateDiff::diff_pod(&old, &self.to_pod()));
|
||||||
self.commit();
|
self.commit();
|
||||||
let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs);
|
let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs);
|
||||||
trace!("Transaction receipt: {:?}", receipt);
|
// trace!("Transaction receipt: {:?}", receipt);
|
||||||
Ok(receipt)
|
Ok(receipt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ impl State {
|
|||||||
/// Populate a PodAccount map from this state.
|
/// Populate a PodAccount map from this state.
|
||||||
pub fn to_pod(&self) -> PodState {
|
pub fn to_pod(&self) -> PodState {
|
||||||
// TODO: handle database rather than just the cache.
|
// TODO: handle database rather than just the cache.
|
||||||
PodState::new(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
|
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
|
||||||
if let Some(ref acc) = *opt {
|
if let Some(ref acc) = *opt {
|
||||||
m.insert(add.clone(), PodAccount::from_account(acc));
|
m.insert(add.clone(), PodAccount::from_account(acc));
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_delete() {
|
fn create_delete() {
|
||||||
let a = PodState::new(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]);
|
let a = PodState::from(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]);
|
||||||
assert_eq!(StateDiff::diff_pod(&a, &PodState::new(map![])), StateDiff(map![
|
assert_eq!(StateDiff::diff_pod(&a, &PodState::new()), StateDiff(map![
|
||||||
x!(1) => AccountDiff{
|
x!(1) => AccountDiff{
|
||||||
balance: Diff::Died(x!(69)),
|
balance: Diff::Died(x!(69)),
|
||||||
nonce: Diff::Died(x!(0)),
|
nonce: Diff::Died(x!(0)),
|
||||||
@ -41,7 +41,7 @@ mod test {
|
|||||||
storage: map![],
|
storage: map![],
|
||||||
}
|
}
|
||||||
]));
|
]));
|
||||||
assert_eq!(StateDiff::diff_pod(&PodState::new(map![]), &a), StateDiff(map![
|
assert_eq!(StateDiff::diff_pod(&PodState::new(), &a), StateDiff(map![
|
||||||
x!(1) => AccountDiff{
|
x!(1) => AccountDiff{
|
||||||
balance: Diff::Born(x!(69)),
|
balance: Diff::Born(x!(69)),
|
||||||
nonce: Diff::Born(x!(0)),
|
nonce: Diff::Born(x!(0)),
|
||||||
@ -53,8 +53,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_delete_with_unchanged() {
|
fn create_delete_with_unchanged() {
|
||||||
let a = PodState::new(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]);
|
let a = PodState::from(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]);
|
||||||
let b = PodState::new(map![
|
let b = PodState::from(map![
|
||||||
x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]),
|
x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]),
|
||||||
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
|
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
|
||||||
]);
|
]);
|
||||||
@ -78,11 +78,11 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn change_with_unchanged() {
|
fn change_with_unchanged() {
|
||||||
let a = PodState::new(map![
|
let a = PodState::from(map![
|
||||||
x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]),
|
x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]),
|
||||||
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
|
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
|
||||||
]);
|
]);
|
||||||
let b = PodState::new(map![
|
let b = PodState::from(map![
|
||||||
x!(1) => PodAccount::new(x!(69), x!(1), vec![], map![]),
|
x!(1) => PodAccount::new(x!(69), x!(1), vec![], map![]),
|
||||||
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
|
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
|
||||||
]);
|
]);
|
||||||
|
@ -415,7 +415,7 @@ impl ChainSync {
|
|||||||
Err(ImportError::AlreadyQueued) => {
|
Err(ImportError::AlreadyQueued) => {
|
||||||
trace!(target: "sync", "New block already queued {:?}", h);
|
trace!(target: "sync", "New block already queued {:?}", h);
|
||||||
},
|
},
|
||||||
Ok(()) => {
|
Ok(_) => {
|
||||||
trace!(target: "sync", "New block queued {:?}", h);
|
trace!(target: "sync", "New block queued {:?}", h);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -680,7 +680,7 @@ impl ChainSync {
|
|||||||
self.last_imported_block = headers.0 + i as BlockNumber;
|
self.last_imported_block = headers.0 + i as BlockNumber;
|
||||||
self.last_imported_hash = h.clone();
|
self.last_imported_hash = h.clone();
|
||||||
},
|
},
|
||||||
Ok(()) => {
|
Ok(_) => {
|
||||||
trace!(target: "sync", "Block queued {:?}", h);
|
trace!(target: "sync", "Block queued {:?}", h);
|
||||||
self.last_imported_block = headers.0 + i as BlockNumber;
|
self.last_imported_block = headers.0 + i as BlockNumber;
|
||||||
self.last_imported_hash = h.clone();
|
self.last_imported_hash = h.clone();
|
||||||
|
@ -9,13 +9,13 @@
|
|||||||
/// extern crate ethcore;
|
/// extern crate ethcore;
|
||||||
/// use std::env;
|
/// use std::env;
|
||||||
/// use std::sync::Arc;
|
/// use std::sync::Arc;
|
||||||
/// use util::network::NetworkService;
|
/// 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;
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let mut service = NetworkService::start().unwrap();
|
/// let mut service = NetworkService::start(NetworkConfiguration::new()).unwrap();
|
||||||
/// let dir = env::temp_dir();
|
/// let dir = env::temp_dir();
|
||||||
/// let client = Client::new(ethereum::new_frontier(), &dir, service.io().channel()).unwrap();
|
/// let client = Client::new(ethereum::new_frontier(), &dir, service.io().channel()).unwrap();
|
||||||
/// EthSync::register(&mut service, client);
|
/// EthSync::register(&mut service, client);
|
||||||
|
@ -51,6 +51,10 @@ impl TestBlockChainClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BlockChainClient for TestBlockChainClient {
|
impl BlockChainClient for TestBlockChainClient {
|
||||||
|
fn block_total_difficulty(&self, _h: &H256) -> Option<U256> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
fn block_header(&self, h: &H256) -> Option<Bytes> {
|
fn block_header(&self, h: &H256) -> Option<Bytes> {
|
||||||
self.blocks.read().unwrap().get(h).map(|r| Rlp::new(r).at(0).as_raw().to_vec())
|
self.blocks.read().unwrap().get(h).map(|r| Rlp::new(r).at(0).as_raw().to_vec())
|
||||||
|
|
||||||
@ -76,6 +80,10 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_total_difficulty_at(&self, _number: BlockNumber) -> Option<U256> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes> {
|
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes> {
|
||||||
self.numbers.read().unwrap().get(&(n as usize)).and_then(|h| self.block_header(h))
|
self.numbers.read().unwrap().get(&(n as usize)).and_then(|h| self.block_header(h))
|
||||||
}
|
}
|
||||||
@ -114,6 +122,7 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
|
|
||||||
fn import_block(&self, b: Bytes) -> ImportResult {
|
fn import_block(&self, b: Bytes) -> ImportResult {
|
||||||
let header = Rlp::new(&b).val_at::<BlockHeader>(0);
|
let header = Rlp::new(&b).val_at::<BlockHeader>(0);
|
||||||
|
let h = header.hash();
|
||||||
let number: usize = header.number as usize;
|
let number: usize = header.number as usize;
|
||||||
if number > self.blocks.read().unwrap().len() {
|
if number > self.blocks.read().unwrap().len() {
|
||||||
panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().unwrap().len(), number);
|
panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().unwrap().len(), number);
|
||||||
@ -134,9 +143,9 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
let len = self.numbers.read().unwrap().len();
|
let len = self.numbers.read().unwrap().len();
|
||||||
if number == len {
|
if number == len {
|
||||||
*self.difficulty.write().unwrap().deref_mut() += header.difficulty;
|
*self.difficulty.write().unwrap().deref_mut() += header.difficulty;
|
||||||
mem::replace(self.last_hash.write().unwrap().deref_mut(), header.hash());
|
mem::replace(self.last_hash.write().unwrap().deref_mut(), h.clone());
|
||||||
self.blocks.write().unwrap().insert(header.hash(), b);
|
self.blocks.write().unwrap().insert(h.clone(), b);
|
||||||
self.numbers.write().unwrap().insert(number, header.hash());
|
self.numbers.write().unwrap().insert(number, h.clone());
|
||||||
let mut parent_hash = header.parent_hash;
|
let mut parent_hash = header.parent_hash;
|
||||||
if number > 0 {
|
if number > 0 {
|
||||||
let mut n = number - 1;
|
let mut n = number - 1;
|
||||||
@ -148,9 +157,9 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.blocks.write().unwrap().insert(header.hash(), b.to_vec());
|
self.blocks.write().unwrap().insert(h.clone(), b.to_vec());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queue_info(&self) -> BlockQueueInfo {
|
fn queue_info(&self) -> BlockQueueInfo {
|
||||||
@ -158,6 +167,7 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
full: false,
|
full: false,
|
||||||
verified_queue_size: 0,
|
verified_queue_size: 0,
|
||||||
unverified_queue_size: 0,
|
unverified_queue_size: 0,
|
||||||
|
verifying_queue_size: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
69
src/tests/chain.rs
Normal file
69
src/tests/chain.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
use std::env;
|
||||||
|
use super::test_common::*;
|
||||||
|
use client::{BlockChainClient,Client};
|
||||||
|
use pod_state::*;
|
||||||
|
use block::Block;
|
||||||
|
use ethereum;
|
||||||
|
|
||||||
|
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||||
|
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
|
||||||
|
let mut failed = Vec::new();
|
||||||
|
|
||||||
|
for (name, test) in json.as_object().unwrap() {
|
||||||
|
let mut fail = false;
|
||||||
|
{
|
||||||
|
let mut fail_unless = |cond: bool| if !cond && !fail {
|
||||||
|
failed.push(name.clone());
|
||||||
|
flush(format!("FAIL\n"));
|
||||||
|
fail = true;
|
||||||
|
true
|
||||||
|
} else {false};
|
||||||
|
|
||||||
|
flush(format!(" - {}...", name));
|
||||||
|
|
||||||
|
let blocks: Vec<(Bytes, bool)> = test["blocks"].as_array().unwrap().iter().map(|e| (xjson!(&e["rlp"]), e.find("blockHeader").is_some())).collect();
|
||||||
|
let mut spec = ethereum::new_frontier_like_test();
|
||||||
|
let s = PodState::from_json(test.find("pre").unwrap());
|
||||||
|
spec.set_genesis_state(s);
|
||||||
|
spec.overwrite_genesis(test.find("genesisBlockHeader").unwrap());
|
||||||
|
assert!(spec.is_state_root_valid());
|
||||||
|
|
||||||
|
let mut dir = env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
{
|
||||||
|
let client = Client::new(spec, &dir, IoChannel::disconnected()).unwrap();
|
||||||
|
for (b, is_valid) in blocks.into_iter() {
|
||||||
|
if Block::is_good(&b) {
|
||||||
|
let _ = client.import_block(b.clone());
|
||||||
|
}
|
||||||
|
client.flush_queue();
|
||||||
|
let imported_ok = client.import_verified_blocks(&IoChannel::disconnected()) > 0;
|
||||||
|
assert_eq!(imported_ok, is_valid);
|
||||||
|
}
|
||||||
|
fail_unless(client.chain_info().best_block_hash == H256::from_json(&test["lastblockhash"]));
|
||||||
|
}
|
||||||
|
fs::remove_dir_all(&dir).unwrap();
|
||||||
|
}
|
||||||
|
if !fail {
|
||||||
|
flush(format!("ok\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("!!! {:?} tests from failed.", failed.len());
|
||||||
|
failed
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_test!{BlockchainTests_bcBlockGasLimitTest, "BlockchainTests/bcBlockGasLimitTest"}
|
||||||
|
declare_test!{BlockchainTests_bcForkBlockTest, "BlockchainTests/bcForkBlockTest"}
|
||||||
|
declare_test!{BlockchainTests_bcForkStressTest, "BlockchainTests/bcForkStressTest"}
|
||||||
|
declare_test!{BlockchainTests_bcForkUncle, "BlockchainTests/bcForkUncle"}
|
||||||
|
declare_test!{BlockchainTests_bcGasPricerTest, "BlockchainTests/bcGasPricerTest"}
|
||||||
|
declare_test!{BlockchainTests_bcInvalidHeaderTest, "BlockchainTests/bcInvalidHeaderTest"}
|
||||||
|
declare_test!{BlockchainTests_bcInvalidRLPTest, "BlockchainTests/bcInvalidRLPTest"}
|
||||||
|
declare_test!{BlockchainTests_bcMultiChainTest, "BlockchainTests/bcMultiChainTest"}
|
||||||
|
declare_test!{BlockchainTests_bcRPC_API_Test, "BlockchainTests/bcRPC_API_Test"}
|
||||||
|
declare_test!{BlockchainTests_bcStateTest, "BlockchainTests/bcStateTest"}
|
||||||
|
declare_test!{BlockchainTests_bcTotalDifficultyTest, "BlockchainTests/bcTotalDifficultyTest"}
|
||||||
|
declare_test!{BlockchainTests_bcUncleHeaderValiditiy, "BlockchainTests/bcUncleHeaderValiditiy"}
|
||||||
|
declare_test!{BlockchainTests_bcUncleTest, "BlockchainTests/bcUncleTest"}
|
||||||
|
declare_test!{BlockchainTests_bcValidBlockTest, "BlockchainTests/bcValidBlockTest"}
|
||||||
|
declare_test!{BlockchainTests_bcWalletTest, "BlockchainTests/bcWalletTest"}
|
117
src/tests/client.rs
Normal file
117
src/tests/client.rs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
use client::{BlockChainClient,Client};
|
||||||
|
use super::test_common::*;
|
||||||
|
use super::helpers::*;
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> Arc<Client> {
|
||||||
|
let dir = RandomTempPath::new();
|
||||||
|
let client = Client::new(get_test_spec(), dir.as_path(), 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 dir = RandomTempPath::new();
|
||||||
|
let client_result = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected());
|
||||||
|
assert!(client_result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn imports_from_empty() {
|
||||||
|
let dir = RandomTempPath::new();
|
||||||
|
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||||
|
client.import_verified_blocks(&IoChannel::disconnected());
|
||||||
|
client.flush_queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn imports_good_block() {
|
||||||
|
let dir = RandomTempPath::new();
|
||||||
|
let client = Client::new(get_test_spec(), dir.as_path(), 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 dir = RandomTempPath::new();
|
||||||
|
let client = Client::new(get_test_spec(), dir.as_path(), 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn imports_block_sequence() {
|
||||||
|
let client = generate_dummy_client(6);
|
||||||
|
let block = client.block_header_at(5).unwrap();
|
||||||
|
|
||||||
|
assert!(!block.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_collect_garbage() {
|
||||||
|
let client = generate_dummy_client(100);
|
||||||
|
client.tick();
|
||||||
|
assert!(client.cache_info().blocks < 100 * 1024);
|
||||||
|
}
|
@ -101,8 +101,9 @@ impl<'a> Ext for TestExt<'a> {
|
|||||||
|
|
||||||
fn call(&mut self,
|
fn call(&mut self,
|
||||||
gas: &U256,
|
gas: &U256,
|
||||||
|
_sender_address: &Address,
|
||||||
receive_address: &Address,
|
receive_address: &Address,
|
||||||
value: &U256,
|
value: Option<U256>,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
_code_address: &Address,
|
_code_address: &Address,
|
||||||
_output: &mut [u8]) -> MessageCallResult {
|
_output: &mut [u8]) -> MessageCallResult {
|
||||||
@ -110,7 +111,7 @@ impl<'a> Ext for TestExt<'a> {
|
|||||||
data: data.to_vec(),
|
data: data.to_vec(),
|
||||||
destination: Some(receive_address.clone()),
|
destination: Some(receive_address.clone()),
|
||||||
gas_limit: *gas,
|
gas_limit: *gas,
|
||||||
value: *value
|
value: value.unwrap()
|
||||||
});
|
});
|
||||||
MessageCallResult::Success(*gas)
|
MessageCallResult::Success(*gas)
|
||||||
}
|
}
|
||||||
@ -194,7 +195,7 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
|
|||||||
let engine = TestEngine::new(1, vm.clone());
|
let engine = TestEngine::new(1, vm.clone());
|
||||||
|
|
||||||
// params
|
// params
|
||||||
let mut params = ActionParams::new();
|
let mut params = ActionParams::default();
|
||||||
test.find("exec").map(|exec| {
|
test.find("exec").map(|exec| {
|
||||||
params.address = xjson!(&exec["address"]);
|
params.address = xjson!(&exec["address"]);
|
||||||
params.sender = xjson!(&exec["caller"]);
|
params.sender = xjson!(&exec["caller"]);
|
||||||
@ -203,7 +204,7 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
|
|||||||
params.data = xjson!(&exec["data"]);
|
params.data = xjson!(&exec["data"]);
|
||||||
params.gas = xjson!(&exec["gas"]);
|
params.gas = xjson!(&exec["gas"]);
|
||||||
params.gas_price = xjson!(&exec["gasPrice"]);
|
params.gas_price = xjson!(&exec["gasPrice"]);
|
||||||
params.value = xjson!(&exec["value"]);
|
params.value = ActionValue::Transfer(xjson!(&exec["value"]));
|
||||||
});
|
});
|
||||||
|
|
||||||
let out_of_gas = test.find("callcreates").map(|_calls| {
|
let out_of_gas = test.find("callcreates").map(|_calls| {
|
||||||
@ -270,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"}
|
||||||
|
80
src/tests/helpers.rs
Normal file
80
src/tests/helpers.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use client::{BlockChainClient,Client};
|
||||||
|
use std::env;
|
||||||
|
use super::test_common::*;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use spec::*;
|
||||||
|
use std::fs::{remove_dir_all};
|
||||||
|
|
||||||
|
|
||||||
|
pub struct RandomTempPath {
|
||||||
|
path: PathBuf
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RandomTempPath {
|
||||||
|
pub fn new() -> RandomTempPath {
|
||||||
|
let mut dir = env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
RandomTempPath {
|
||||||
|
path: dir.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_path(&self) -> &PathBuf {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for RandomTempPath {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Err(e) = remove_dir_all(self.as_path()) {
|
||||||
|
panic!("failed to remove temp directory, probably something failed to destroyed ({})", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_test_spec() -> Spec {
|
||||||
|
Spec::new_test()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_dummy_client(block_number: usize) -> Arc<Client> {
|
||||||
|
let dir = RandomTempPath::new();
|
||||||
|
|
||||||
|
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||||
|
let test_spec = get_test_spec();
|
||||||
|
let test_engine = test_spec.to_engine().unwrap();
|
||||||
|
let state_root = test_engine.spec().genesis_header().state_root;
|
||||||
|
let mut rolling_hash = test_engine.spec().genesis_header().hash();
|
||||||
|
let mut rolling_block_number = 1;
|
||||||
|
let mut rolling_timestamp = 40;
|
||||||
|
|
||||||
|
for _ in 0..block_number {
|
||||||
|
let mut header = Header::new();
|
||||||
|
|
||||||
|
header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||||
|
header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||||
|
header.timestamp = rolling_timestamp;
|
||||||
|
header.number = rolling_block_number;
|
||||||
|
header.parent_hash = rolling_hash;
|
||||||
|
header.state_root = state_root.clone();
|
||||||
|
|
||||||
|
rolling_hash = header.hash();
|
||||||
|
rolling_block_number = rolling_block_number + 1;
|
||||||
|
rolling_timestamp = rolling_timestamp + 10;
|
||||||
|
|
||||||
|
if let Err(_) = client.import_block(create_test_block(&header)) {
|
||||||
|
panic!("error importing block which is valid by definition");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
client.flush_queue();
|
||||||
|
client.import_verified_blocks(&IoChannel::disconnected());
|
||||||
|
client
|
||||||
|
}
|
@ -4,3 +4,6 @@ mod test_common;
|
|||||||
mod transaction;
|
mod transaction;
|
||||||
mod executive;
|
mod executive;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod client;
|
||||||
|
mod chain;
|
||||||
|
mod helpers;
|
@ -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"}
|
||||||
|
@ -3,7 +3,7 @@ use basic_types::*;
|
|||||||
use error::*;
|
use error::*;
|
||||||
use evm::Schedule;
|
use evm::Schedule;
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
@ -12,9 +12,13 @@ pub enum Action {
|
|||||||
Call(Address),
|
Call(Address),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Action {
|
||||||
|
fn default() -> Action { Action::Create }
|
||||||
|
}
|
||||||
|
|
||||||
/// A set of information describing an externally-originating message call
|
/// A set of information describing an externally-originating message call
|
||||||
/// or contract creation operation.
|
/// or contract creation operation.
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
/// TODO [debris] Please document me
|
/// TODO [debris] Please document me
|
||||||
pub nonce: U256,
|
pub nonce: U256,
|
||||||
|
@ -162,7 +162,7 @@ pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error>
|
|||||||
/// Check basic header parameters.
|
/// Check basic header parameters.
|
||||||
fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> {
|
fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> {
|
||||||
if header.number >= From::from(BlockNumber::max_value()) {
|
if header.number >= From::from(BlockNumber::max_value()) {
|
||||||
return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number })))
|
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number })))
|
||||||
}
|
}
|
||||||
if header.gas_used > header.gas_limit {
|
if header.gas_used > header.gas_limit {
|
||||||
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used })));
|
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used })));
|
||||||
@ -186,8 +186,8 @@ fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> {
|
|||||||
if header.timestamp <= parent.timestamp {
|
if header.timestamp <= parent.timestamp {
|
||||||
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp })))
|
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp })))
|
||||||
}
|
}
|
||||||
if header.number <= parent.number {
|
if header.number != parent.number + 1 {
|
||||||
return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: None, min: Some(parent.number + 1), found: header.number })));
|
return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number })));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -397,7 +397,7 @@ mod tests {
|
|||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.number = BlockNumber::max_value();
|
header.number = BlockNumber::max_value();
|
||||||
check_fail(basic_test(&create_test_block(&header), engine.deref()),
|
check_fail(basic_test(&create_test_block(&header), engine.deref()),
|
||||||
InvalidNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number }));
|
RidiculousNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.gas_used = header.gas_limit + From::from(1);
|
header.gas_used = header.gas_limit + From::from(1);
|
||||||
@ -440,7 +440,7 @@ mod tests {
|
|||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.number = 9;
|
header.number = 9;
|
||||||
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
|
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
|
||||||
InvalidNumber(OutOfBounds { max: None, min: Some(parent.number + 1), found: header.number }));
|
InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
let mut bad_uncles = good_uncles.clone();
|
let mut bad_uncles = good_uncles.clone();
|
||||||
|
@ -26,7 +26,8 @@ crossbeam = "0.2"
|
|||||||
smallvec = "0.1"
|
smallvec = "0.1"
|
||||||
slab = { git = "https://github.com/arkpar/slab.git" }
|
slab = { git = "https://github.com/arkpar/slab.git" }
|
||||||
sha3 = { path = "sha3" }
|
sha3 = { path = "sha3" }
|
||||||
clippy = "*" # Always newest, since we use nightly
|
serde = "0.6.7"
|
||||||
|
clippy = "0.0.37"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
json-tests = { path = "json-tests" }
|
json-tests = { path = "json-tests" }
|
||||||
|
@ -303,7 +303,9 @@ pub enum FromBytesError {
|
|||||||
/// TODO [debris] Please document me
|
/// TODO [debris] Please document me
|
||||||
DataIsTooShort,
|
DataIsTooShort,
|
||||||
/// TODO [debris] Please document me
|
/// TODO [debris] Please document me
|
||||||
DataIsTooLong
|
DataIsTooLong,
|
||||||
|
/// Integer-representation is non-canonically prefixed with zero byte(s).
|
||||||
|
ZeroPrefixedInt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StdError for FromBytesError {
|
impl StdError for FromBytesError {
|
||||||
@ -340,6 +342,9 @@ macro_rules! impl_uint_from_bytes {
|
|||||||
match bytes.len() {
|
match bytes.len() {
|
||||||
0 => Ok(0),
|
0 => Ok(0),
|
||||||
l if l <= mem::size_of::<$to>() => {
|
l if l <= mem::size_of::<$to>() => {
|
||||||
|
if bytes[0] == 0 {
|
||||||
|
return Err(FromBytesError::ZeroPrefixedInt)
|
||||||
|
}
|
||||||
let mut res = 0 as $to;
|
let mut res = 0 as $to;
|
||||||
for i in 0..l {
|
for i in 0..l {
|
||||||
let shift = (l - 1 - i) * 8;
|
let shift = (l - 1 - i) * 8;
|
||||||
@ -374,7 +379,9 @@ macro_rules! impl_uint_from_bytes {
|
|||||||
($name: ident) => {
|
($name: ident) => {
|
||||||
impl FromBytes for $name {
|
impl FromBytes for $name {
|
||||||
fn from_bytes(bytes: &[u8]) -> FromBytesResult<$name> {
|
fn from_bytes(bytes: &[u8]) -> FromBytesResult<$name> {
|
||||||
if bytes.len() <= $name::SIZE {
|
if !bytes.is_empty() && bytes[0] == 0 {
|
||||||
|
Err(FromBytesError::ZeroPrefixedInt)
|
||||||
|
} else if bytes.len() <= $name::SIZE {
|
||||||
Ok($name::from(bytes))
|
Ok($name::from(bytes))
|
||||||
} else {
|
} else {
|
||||||
Err(FromBytesError::DataIsTooLong)
|
Err(FromBytesError::DataIsTooLong)
|
||||||
|
@ -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.
|
||||||
///
|
///
|
||||||
@ -41,6 +43,8 @@ pub trait FixedHash: Sized + BytesConvertable + Populatable + FromStr + Default
|
|||||||
fn contains<'a>(&'a self, b: &'a Self) -> bool;
|
fn contains<'a>(&'a self, b: &'a Self) -> bool;
|
||||||
/// TODO [debris] Please document me
|
/// TODO [debris] Please document me
|
||||||
fn is_zero(&self) -> bool;
|
fn is_zero(&self) -> bool;
|
||||||
|
/// Return the lowest 8 bytes interpreted as a BigEndian integer.
|
||||||
|
fn low_u64(&self) -> u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_0x(s: &str) -> &str {
|
fn clean_0x(s: &str) -> &str {
|
||||||
@ -71,8 +75,8 @@ macro_rules! impl_hash {
|
|||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl DerefMut for $from {
|
|
||||||
|
|
||||||
|
impl DerefMut for $from {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
@ -190,6 +194,14 @@ macro_rules! impl_hash {
|
|||||||
fn is_zero(&self) -> bool {
|
fn is_zero(&self) -> bool {
|
||||||
self.eq(&Self::new())
|
self.eq(&Self::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn low_u64(&self) -> u64 {
|
||||||
|
let mut ret = 0u64;
|
||||||
|
for i in 0..min($size, 8) {
|
||||||
|
ret |= (self.0[$size - 1 - i] as u64) << (i * 8);
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for $from {
|
impl FromStr for $from {
|
||||||
@ -205,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 {
|
||||||
|
@ -55,7 +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 smallvec;
|
extern crate serde;
|
||||||
|
|
||||||
/// TODO [Gav Wood] Please document me
|
/// TODO [Gav Wood] Please document me
|
||||||
pub mod standard;
|
pub mod standard;
|
||||||
|
@ -7,6 +7,8 @@ use bytes::FromBytesError;
|
|||||||
pub enum DecoderError {
|
pub enum DecoderError {
|
||||||
/// TODO [debris] Please document me
|
/// TODO [debris] Please document me
|
||||||
FromBytesError(FromBytesError),
|
FromBytesError(FromBytesError),
|
||||||
|
/// Given data has additional bytes at the end of the valid RLP fragment.
|
||||||
|
RlpIsTooBig,
|
||||||
/// TODO [debris] Please document me
|
/// TODO [debris] Please document me
|
||||||
RlpIsTooShort,
|
RlpIsTooShort,
|
||||||
/// TODO [debris] Please document me
|
/// TODO [debris] Please document me
|
||||||
@ -21,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]
|
||||||
@ -355,3 +355,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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,9 @@ impl PayloadInfo {
|
|||||||
value_len: value_len,
|
value_len: value_len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Total size of the RLP.
|
||||||
|
pub fn total(&self) -> usize { self.header_len + self.value_len }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data-oriented view onto rlp-slice.
|
/// Data-oriented view onto rlp-slice.
|
||||||
@ -331,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)
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
pub use std::io;
|
pub use std::io;
|
||||||
|
pub use std::fs;
|
||||||
pub use std::str;
|
pub use std::str;
|
||||||
pub use std::fmt;
|
pub use std::fmt;
|
||||||
pub use std::slice;
|
|
||||||
pub use std::cmp;
|
pub use std::cmp;
|
||||||
pub use std::ptr;
|
pub use std::ptr;
|
||||||
pub use std::result;
|
|
||||||
pub use std::option;
|
|
||||||
pub use std::mem;
|
pub use std::mem;
|
||||||
pub use std::ops;
|
pub use std::ops;
|
||||||
|
pub use std::slice;
|
||||||
|
pub use std::result;
|
||||||
|
pub use std::option;
|
||||||
|
|
||||||
pub use std::path::Path;
|
pub use std::path::Path;
|
||||||
pub use std::str::{FromStr};
|
pub use std::str::{FromStr};
|
||||||
@ -15,9 +16,9 @@ pub use std::io::{Read,Write};
|
|||||||
pub use std::hash::{Hash, Hasher};
|
pub use std::hash::{Hash, Hasher};
|
||||||
pub use std::error::Error as StdError;
|
pub use std::error::Error as StdError;
|
||||||
|
|
||||||
pub use std::sync::*;
|
|
||||||
pub use std::ops::*;
|
pub use std::ops::*;
|
||||||
pub use std::cmp::*;
|
pub use std::cmp::*;
|
||||||
|
pub use std::sync::*;
|
||||||
pub use std::cell::*;
|
pub use std::cell::*;
|
||||||
pub use std::collections::*;
|
pub use std::collections::*;
|
||||||
|
|
||||||
|
@ -25,6 +25,12 @@ impl<'db> SecTrieDBMut<'db> {
|
|||||||
pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> Self {
|
pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> Self {
|
||||||
SecTrieDBMut { raw: TrieDBMut::from_existing(db, root) }
|
SecTrieDBMut { raw: TrieDBMut::from_existing(db, root) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the backing database.
|
||||||
|
pub fn db(&'db self) -> &'db HashDB { self.raw.db() }
|
||||||
|
|
||||||
|
/// Get the backing database.
|
||||||
|
pub fn db_mut(&'db mut self) -> &'db mut HashDB { self.raw.db_mut() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> Trie for SecTrieDBMut<'db> {
|
impl<'db> Trie for SecTrieDBMut<'db> {
|
||||||
|
@ -87,6 +87,11 @@ impl<'db> TrieDBMut<'db> {
|
|||||||
self.db
|
self.db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the backing database.
|
||||||
|
pub fn db_mut(&'db mut self) -> &'db mut HashDB {
|
||||||
|
self.db
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine all the keys in the backing database that belong to the trie.
|
/// Determine all the keys in the backing database that belong to the trie.
|
||||||
pub fn keys(&self) -> Vec<H256> {
|
pub fn keys(&self) -> Vec<H256> {
|
||||||
let mut ret: Vec<H256> = Vec::new();
|
let mut ret: Vec<H256> = Vec::new();
|
||||||
|
@ -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