Merge remote-tracking branch 'origin/master' into bettermining
This commit is contained in:
commit
d150529730
@ -19,6 +19,7 @@ use tests::helpers::*;
|
|||||||
use pod_state::*;
|
use pod_state::*;
|
||||||
use state_diff::*;
|
use state_diff::*;
|
||||||
use ethereum;
|
use ethereum;
|
||||||
|
use ethjson;
|
||||||
|
|
||||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||||
json_chain_test(json_data, ChainEra::Frontier)
|
json_chain_test(json_data, ChainEra::Frontier)
|
||||||
@ -26,18 +27,14 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
|||||||
|
|
||||||
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
||||||
init_log();
|
init_log();
|
||||||
|
let tests = ethjson::state::Test::load(json_data).unwrap();
|
||||||
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
|
|
||||||
let mut failed = Vec::new();
|
let mut failed = Vec::new();
|
||||||
|
|
||||||
let engine = match era {
|
let engine = match era {
|
||||||
ChainEra::Frontier => ethereum::new_mainnet_like(),
|
ChainEra::Frontier => ethereum::new_mainnet_like(),
|
||||||
ChainEra::Homestead => ethereum::new_homestead_test(),
|
ChainEra::Homestead => ethereum::new_homestead_test(),
|
||||||
}.to_engine().unwrap();
|
}.to_engine().unwrap();
|
||||||
|
|
||||||
flushln!("");
|
for (name, test) in tests.into_iter() {
|
||||||
|
|
||||||
for (name, test) in json.as_object().unwrap() {
|
|
||||||
let mut fail = false;
|
let mut fail = false;
|
||||||
{
|
{
|
||||||
let mut fail_unless = |cond: bool| if !cond && !fail {
|
let mut fail_unless = |cond: bool| if !cond && !fail {
|
||||||
@ -49,16 +46,13 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
|||||||
|
|
||||||
flush!(" - {}...", name);
|
flush!(" - {}...", name);
|
||||||
|
|
||||||
let t = SignedTransaction::from_json(&test["transaction"]);
|
let transaction = test.transaction.into();
|
||||||
let env = EnvInfo::from_json(&test["env"]);
|
let post_state_root = test.post_state_root.into();
|
||||||
let _out = Bytes::from_json(&test["out"]);
|
let env = test.env.into();
|
||||||
let post_state_root = xjson!(&test["postStateRoot"]);
|
let pre: PodState = test.pre_state.into();
|
||||||
let pre = PodState::from_json(&test["pre"]);
|
let post: PodState = test.post_state.into();
|
||||||
let post = PodState::from_json(&test["post"]);
|
let logs: Vec<LogEntry> = test.logs.into_iter().map(Into::into).collect();
|
||||||
let logs: Vec<_> = test["logs"].as_array().unwrap().iter().map(&LogEntry::from_json).collect();
|
|
||||||
|
|
||||||
//println!("Transaction: {:?}", t);
|
|
||||||
//println!("Env: {:?}", env);
|
|
||||||
let calc_post = sec_trie_root(post.get().iter().map(|(k, v)| (k.to_vec(), v.rlp())).collect());
|
let calc_post = sec_trie_root(post.get().iter().map(|(k, v)| (k.to_vec(), v.rlp())).collect());
|
||||||
|
|
||||||
if fail_unless(post_state_root == calc_post) {
|
if fail_unless(post_state_root == calc_post) {
|
||||||
@ -69,7 +63,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
|||||||
let mut state = state_result.reference_mut();
|
let mut state = state_result.reference_mut();
|
||||||
state.populate_from(pre);
|
state.populate_from(pre);
|
||||||
state.commit();
|
state.commit();
|
||||||
let res = state.apply(&env, engine.deref(), &t, false);
|
let res = state.apply(&env, engine.deref(), &transaction, false);
|
||||||
|
|
||||||
if fail_unless(state.root() == &post_state_root) {
|
if fail_unless(state.root() == &post_state_root) {
|
||||||
println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root);
|
println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root);
|
||||||
@ -88,12 +82,12 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fail {
|
if !fail {
|
||||||
flushln!("ok");
|
flushln!("ok");
|
||||||
}
|
}
|
||||||
// TODO: Add extra APIs for output
|
|
||||||
//if fail_unless(out == r.)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("!!! {:?} tests from failed.", failed.len());
|
println!("!!! {:?} tests from failed.", failed.len());
|
||||||
failed
|
failed
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
use util::*;
|
use util::*;
|
||||||
use basic_types::LogBloom;
|
use basic_types::LogBloom;
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
|
use ethjson;
|
||||||
|
|
||||||
/// A record of execution for a `LOG` operation.
|
/// A record of execution for a `LOG` operation.
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||||
@ -65,6 +66,16 @@ impl LogEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ethjson::state::Log> for LogEntry {
|
||||||
|
fn from(l: ethjson::state::Log) -> Self {
|
||||||
|
LogEntry {
|
||||||
|
address: l.address.into(),
|
||||||
|
topics: l.topics.into_iter().map(Into::into).collect(),
|
||||||
|
data: l.data.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromJson for LogEntry {
|
impl FromJson for LogEntry {
|
||||||
/// Convert given JSON object to a LogEntry.
|
/// Convert given JSON object to a LogEntry.
|
||||||
fn from_json(json: &Json) -> LogEntry {
|
fn from_json(json: &Json) -> LogEntry {
|
||||||
|
@ -20,6 +20,7 @@ use util::*;
|
|||||||
use error::*;
|
use error::*;
|
||||||
use evm::Schedule;
|
use evm::Schedule;
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
|
use ethjson;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
/// Transaction action type.
|
/// Transaction action type.
|
||||||
@ -79,6 +80,23 @@ impl Transaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ethjson::state::Transaction> for SignedTransaction {
|
||||||
|
fn from(t: ethjson::state::Transaction) -> Self {
|
||||||
|
let to: Option<_> = t.to.into();
|
||||||
|
Transaction {
|
||||||
|
nonce: t.nonce.into(),
|
||||||
|
gas_price: t.gas_price.into(),
|
||||||
|
gas: t.gas_limit.into(),
|
||||||
|
action: match to {
|
||||||
|
Some(to) => Action::Call(to.into()),
|
||||||
|
None => Action::Create
|
||||||
|
},
|
||||||
|
value: t.value.into(),
|
||||||
|
data: t.data.into(),
|
||||||
|
}.sign(&t.secret.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromJson for SignedTransaction {
|
impl FromJson for SignedTransaction {
|
||||||
#[cfg_attr(feature="dev", allow(single_char_pattern))]
|
#[cfg_attr(feature="dev", allow(single_char_pattern))]
|
||||||
fn from_json(json: &Json) -> SignedTransaction {
|
fn from_json(json: &Json) -> SignedTransaction {
|
||||||
|
@ -26,3 +26,4 @@ pub mod blockchain;
|
|||||||
pub mod spec;
|
pub mod spec;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
pub mod maybe;
|
pub mod maybe;
|
||||||
|
pub mod state;
|
||||||
|
52
json/src/state/log.rs
Normal file
52
json/src/state/log.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! State test log deserialization.
|
||||||
|
use hash::{Address, H256, Bloom};
|
||||||
|
use bytes::Bytes;
|
||||||
|
|
||||||
|
/// State test log deserialization.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct Log {
|
||||||
|
/// Address.
|
||||||
|
pub address: Address,
|
||||||
|
/// Topics.
|
||||||
|
pub topics: Vec<H256>,
|
||||||
|
/// Data.
|
||||||
|
pub data: Bytes,
|
||||||
|
/// Bloom.
|
||||||
|
pub bloom: Bloom,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use state::Log;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn log_deserialization() {
|
||||||
|
let s = r#"{
|
||||||
|
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
|
||||||
|
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"data" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"topics" : [
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
]
|
||||||
|
}"#;
|
||||||
|
let _deserialized: Log = serde_json::from_str(s).unwrap();
|
||||||
|
// TODO: validate all fields
|
||||||
|
}
|
||||||
|
}
|
29
json/src/state/mod.rs
Normal file
29
json/src/state/mod.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! State test deserialization.
|
||||||
|
|
||||||
|
pub mod state;
|
||||||
|
pub mod transaction;
|
||||||
|
pub mod test;
|
||||||
|
pub mod log;
|
||||||
|
|
||||||
|
pub use self::state::State;
|
||||||
|
pub use self::transaction::Transaction;
|
||||||
|
pub use self::test::Test;
|
||||||
|
pub use self::log::Log;
|
||||||
|
pub use vm::Env as Env;
|
||||||
|
pub use blockchain::State as AccountState;
|
156
json/src/state/state.rs
Normal file
156
json/src/state/state.rs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! State test deserialization.
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
use hash::H256;
|
||||||
|
use state::{Env, AccountState, Transaction, Log};
|
||||||
|
|
||||||
|
/// State test deserialization.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct State {
|
||||||
|
/// Environment.
|
||||||
|
pub env: Env,
|
||||||
|
/// Output.
|
||||||
|
#[serde(rename="out")]
|
||||||
|
pub output: Bytes,
|
||||||
|
/// Pre state.
|
||||||
|
#[serde(rename="pre")]
|
||||||
|
pub pre_state: AccountState,
|
||||||
|
/// Post state.
|
||||||
|
#[serde(rename="post")]
|
||||||
|
pub post_state: AccountState,
|
||||||
|
/// Post state root.
|
||||||
|
#[serde(rename="postStateRoot")]
|
||||||
|
pub post_state_root: H256,
|
||||||
|
/// Transaction.
|
||||||
|
pub transaction: Transaction,
|
||||||
|
/// Logs.
|
||||||
|
pub logs: Vec<Log>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use state::State;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn state_deserialization() {
|
||||||
|
let s = r#"{
|
||||||
|
"env" : {
|
||||||
|
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||||
|
"currentDifficulty" : "0x0100",
|
||||||
|
"currentGasLimit" : "0x01c9c380",
|
||||||
|
"currentNumber" : "0x00",
|
||||||
|
"currentTimestamp" : "0x01",
|
||||||
|
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
|
||||||
|
},
|
||||||
|
"logs" : [
|
||||||
|
],
|
||||||
|
"out" : "0x",
|
||||||
|
"post" : {
|
||||||
|
"1000000000000000000000000000000000000000" : {
|
||||||
|
"balance" : "0x0de0b6b3a763ffff",
|
||||||
|
"code" : "0x6040600060406000600173100000000000000000000000000000000000000162055730f1600055",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
"0x00" : "0x01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"1000000000000000000000000000000000000001" : {
|
||||||
|
"balance" : "0x0de0b6b3a763ffff",
|
||||||
|
"code" : "0x604060006040600060027310000000000000000000000000000000000000026203d090f1600155",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
"0x01" : "0x01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"1000000000000000000000000000000000000002" : {
|
||||||
|
"balance" : "0x02",
|
||||||
|
"code" : "0x600160025533600455346007553060e6553260e8553660ec553860ee553a60f055",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
"0x02" : "0x01",
|
||||||
|
"0x04" : "0x1000000000000000000000000000000000000001",
|
||||||
|
"0x07" : "0x02",
|
||||||
|
"0xe6" : "0x1000000000000000000000000000000000000002",
|
||||||
|
"0xe8" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||||
|
"0xec" : "0x40",
|
||||||
|
"0xee" : "0x21",
|
||||||
|
"0xf0" : "0x01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
|
||||||
|
"balance" : "0x039455",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "0x0de0b6b3a7606bab",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0x01",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postStateRoot" : "8f8ed2aed2973e159fa5486f47c6ebf15c5058f8e2350286b84b569bc6ce2d25",
|
||||||
|
"pre" : {
|
||||||
|
"1000000000000000000000000000000000000000" : {
|
||||||
|
"balance" : "0x0de0b6b3a7640000",
|
||||||
|
"code" : "0x6040600060406000600173100000000000000000000000000000000000000162055730f1600055",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"1000000000000000000000000000000000000001" : {
|
||||||
|
"balance" : "0x0de0b6b3a7640000",
|
||||||
|
"code" : "0x604060006040600060027310000000000000000000000000000000000000026203d090f1600155",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"1000000000000000000000000000000000000002" : {
|
||||||
|
"balance" : "0x00",
|
||||||
|
"code" : "0x600160025533600455346007553060e6553260e8553660ec553860ee553a60f055",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "0x0de0b6b3a7640000",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transaction" : {
|
||||||
|
"data" : "",
|
||||||
|
"gasLimit" : "0x2dc6c0",
|
||||||
|
"gasPrice" : "0x01",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||||
|
"to" : "1000000000000000000000000000000000000000",
|
||||||
|
"value" : "0x00"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
let _deserialized: State = serde_json::from_str(s).unwrap();
|
||||||
|
// TODO: validate all fields
|
||||||
|
}
|
||||||
|
}
|
43
json/src/state/test.rs
Normal file
43
json/src/state/test.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! State test deserializer.
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::io::Read;
|
||||||
|
use serde_json;
|
||||||
|
use serde_json::Error;
|
||||||
|
use state::State;
|
||||||
|
|
||||||
|
/// State test deserializer.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct Test(BTreeMap<String, State>);
|
||||||
|
|
||||||
|
impl IntoIterator for Test {
|
||||||
|
type Item = <BTreeMap<String, State> as IntoIterator>::Item;
|
||||||
|
type IntoIter = <BTreeMap<String, State> as IntoIterator>::IntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.0.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Test {
|
||||||
|
/// Loads test from json.
|
||||||
|
pub fn load<R>(reader: R) -> Result<Self, Error> where R: Read {
|
||||||
|
serde_json::from_reader(reader)
|
||||||
|
}
|
||||||
|
}
|
65
json/src/state/transaction.rs
Normal file
65
json/src/state/transaction.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! State test transaction deserialization.
|
||||||
|
|
||||||
|
use uint::Uint;
|
||||||
|
use bytes::Bytes;
|
||||||
|
use hash::{Address, H256};
|
||||||
|
use maybe::MaybeEmpty;
|
||||||
|
|
||||||
|
/// State test transaction deserialization.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct Transaction {
|
||||||
|
/// Transaction data.
|
||||||
|
pub data: Bytes,
|
||||||
|
/// Gas limit.
|
||||||
|
#[serde(rename="gasLimit")]
|
||||||
|
pub gas_limit: Uint,
|
||||||
|
/// Gas price.
|
||||||
|
#[serde(rename="gasPrice")]
|
||||||
|
pub gas_price: Uint,
|
||||||
|
/// Nonce.
|
||||||
|
pub nonce: Uint,
|
||||||
|
/// Secret key.
|
||||||
|
#[serde(rename="secretKey")]
|
||||||
|
pub secret: H256,
|
||||||
|
/// To.
|
||||||
|
pub to: MaybeEmpty<Address>,
|
||||||
|
/// Value.
|
||||||
|
pub value: Uint
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use state::Transaction;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_deserialization() {
|
||||||
|
let s = r#"{
|
||||||
|
"data" : "",
|
||||||
|
"gasLimit" : "0x2dc6c0",
|
||||||
|
"gasPrice" : "0x01",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||||
|
"to" : "1000000000000000000000000000000000000000",
|
||||||
|
"value" : "0x00"
|
||||||
|
}"#;
|
||||||
|
let _deserialized: Transaction = serde_json::from_str(s).unwrap();
|
||||||
|
// TODO: validate all fields
|
||||||
|
}
|
||||||
|
}
|
@ -104,6 +104,9 @@ pub trait MinerService : Send + Sync {
|
|||||||
|
|
||||||
/// Get the sealing work package and if `Some`, apply some transform.
|
/// Get the sealing work package and if `Some`, apply some transform.
|
||||||
fn map_sealing_work<F, T>(&self, chain: &BlockChainClient, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T;
|
fn map_sealing_work<F, T>(&self, chain: &BlockChainClient, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T;
|
||||||
|
|
||||||
|
/// Query pending transactions for hash
|
||||||
|
fn transaction(&self, hash: &H256) -> Option<SignedTransaction>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mining status
|
/// Mining status
|
||||||
|
@ -217,6 +217,11 @@ impl MinerService for Miner {
|
|||||||
transaction_queue.pending_hashes()
|
transaction_queue.pending_hashes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
|
||||||
|
let queue = self.transaction_queue.lock().unwrap();
|
||||||
|
queue.find(hash)
|
||||||
|
}
|
||||||
|
|
||||||
fn update_sealing(&self, chain: &BlockChainClient) {
|
fn update_sealing(&self, chain: &BlockChainClient) {
|
||||||
if self.sealing_enabled.load(atomic::Ordering::Relaxed) {
|
if self.sealing_enabled.load(atomic::Ordering::Relaxed) {
|
||||||
let current_no = chain.chain_info().best_block_number;
|
let current_no = chain.chain_info().best_block_number;
|
||||||
|
@ -504,6 +504,11 @@ impl TransactionQueue {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds transaction in the queue by hash (if any)
|
||||||
|
pub fn find(&self, hash: &H256) -> Option<SignedTransaction> {
|
||||||
|
match self.by_hash.get(hash) { Some(transaction_ref) => Some(transaction_ref.transaction.clone()), None => None }
|
||||||
|
}
|
||||||
|
|
||||||
/// Removes all elements (in any state) from the queue
|
/// Removes all elements (in any state) from the queue
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.current.clear();
|
self.current.clear();
|
||||||
|
@ -39,6 +39,8 @@ extern crate rpassword;
|
|||||||
#[cfg(feature = "rpc")]
|
#[cfg(feature = "rpc")]
|
||||||
extern crate ethcore_rpc as rpc;
|
extern crate ethcore_rpc as rpc;
|
||||||
|
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
use std::fs::File;
|
||||||
use std::net::{SocketAddr, IpAddr};
|
use std::net::{SocketAddr, IpAddr};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
@ -89,6 +91,11 @@ Protocol Options:
|
|||||||
[default: $HOME/.web3/keys].
|
[default: $HOME/.web3/keys].
|
||||||
--identity NAME Specify your node's name.
|
--identity NAME Specify your node's name.
|
||||||
|
|
||||||
|
Account Options:
|
||||||
|
--unlock ACCOUNT Unlock ACCOUNT for the duration of the execution.
|
||||||
|
--password FILE Provide a file containing a password for unlocking
|
||||||
|
an account.
|
||||||
|
|
||||||
Networking Options:
|
Networking Options:
|
||||||
--port PORT Override the port on which the node should listen
|
--port PORT Override the port on which the node should listen
|
||||||
[default: 30303].
|
[default: 30303].
|
||||||
@ -176,6 +183,8 @@ struct Args {
|
|||||||
flag_chain: String,
|
flag_chain: String,
|
||||||
flag_db_path: String,
|
flag_db_path: String,
|
||||||
flag_identity: String,
|
flag_identity: String,
|
||||||
|
flag_unlock: Vec<String>,
|
||||||
|
flag_password: Vec<String>,
|
||||||
flag_cache: Option<usize>,
|
flag_cache: Option<usize>,
|
||||||
flag_keys_path: String,
|
flag_keys_path: String,
|
||||||
flag_bootnodes: Option<String>,
|
flag_bootnodes: Option<String>,
|
||||||
@ -490,6 +499,28 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn account_service(&self) -> AccountService {
|
||||||
|
// Secret Store
|
||||||
|
let passwords = self.args.flag_password.iter().flat_map(|filename| {
|
||||||
|
BufReader::new(&File::open(filename).unwrap_or_else(|_| die!("{} Unable to read password file. Ensure it exists and permissions are correct.", filename)))
|
||||||
|
.lines()
|
||||||
|
.map(|l| l.unwrap())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let account_service = AccountService::new();
|
||||||
|
for d in &self.args.flag_unlock {
|
||||||
|
let a = Address::from_str(clean_0x(&d)).unwrap_or_else(|_| {
|
||||||
|
die!("{}: Invalid address for --unlock. Must be 40 hex characters, without the 0x at the beginning.", d)
|
||||||
|
});
|
||||||
|
if passwords.iter().find(|p| account_service.unlock_account_no_expire(&a, p).is_ok()).is_none() {
|
||||||
|
die!("No password given to unlock account {}. Pass the password using `--password`.", a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
account_service
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(useless_format))]
|
#[cfg_attr(feature="dev", allow(useless_format))]
|
||||||
fn execute_client(&self) {
|
fn execute_client(&self) {
|
||||||
// Setup panic handler
|
// Setup panic handler
|
||||||
@ -504,6 +535,9 @@ impl Configuration {
|
|||||||
let net_settings = self.net_settings(&spec);
|
let net_settings = self.net_settings(&spec);
|
||||||
let sync_config = self.sync_config(&spec);
|
let sync_config = self.sync_config(&spec);
|
||||||
|
|
||||||
|
// Secret Store
|
||||||
|
let account_service = Arc::new(self.account_service());
|
||||||
|
|
||||||
// Build client
|
// Build client
|
||||||
let mut service = ClientService::start(self.client_config(), spec, net_settings, &Path::new(&self.path())).unwrap();
|
let mut service = ClientService::start(self.client_config(), spec, net_settings, &Path::new(&self.path())).unwrap();
|
||||||
panic_handler.forward_from(&service);
|
panic_handler.forward_from(&service);
|
||||||
@ -519,9 +553,6 @@ impl Configuration {
|
|||||||
// Sync
|
// Sync
|
||||||
let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone());
|
let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone());
|
||||||
|
|
||||||
// Secret Store
|
|
||||||
let account_service = Arc::new(AccountService::new());
|
|
||||||
|
|
||||||
// Setup rpc
|
// Setup rpc
|
||||||
if self.args.flag_jsonrpc || self.args.flag_rpc {
|
if self.args.flag_jsonrpc || self.args.flag_rpc {
|
||||||
let url = format!("{}:{}",
|
let url = format!("{}:{}",
|
||||||
|
@ -348,7 +348,13 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
|
|||||||
|
|
||||||
fn transaction_by_hash(&self, params: Params) -> Result<Value, Error> {
|
fn transaction_by_hash(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(H256,)>(params)
|
from_params::<(H256,)>(params)
|
||||||
.and_then(|(hash,)| self.transaction(TransactionId::Hash(hash)))
|
.and_then(|(hash,)| {
|
||||||
|
let miner = take_weak!(self.miner);
|
||||||
|
match miner.transaction(&hash) {
|
||||||
|
Some(pending_tx) => to_value(&Transaction::from(pending_tx)),
|
||||||
|
None => self.transaction(TransactionId::Hash(hash))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_by_block_hash_and_index(&self, params: Params) -> Result<Value, Error> {
|
fn transaction_by_block_hash_and_index(&self, params: Params) -> Result<Value, Error> {
|
||||||
@ -471,11 +477,11 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
|
|||||||
Ok(_) => to_value(&hash),
|
Ok(_) => to_value(&hash),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Error sending transaction: {:?}", e);
|
warn!("Error sending transaction: {:?}", e);
|
||||||
to_value(&U256::zero())
|
to_value(&H256::zero())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => { to_value(&U256::zero()) }
|
Err(_) => { to_value(&H256::zero()) }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -498,11 +504,11 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
|
|||||||
Ok(_) => to_value(&hash),
|
Ok(_) => to_value(&hash),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Error sending transaction: {:?}", e);
|
warn!("Error sending transaction: {:?}", e);
|
||||||
to_value(&U256::zero())
|
to_value(&H256::zero())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => { to_value(&U256::zero()) }
|
Err(_) => { to_value(&H256::zero()) }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ struct EthTester {
|
|||||||
pub client: Arc<TestBlockChainClient>,
|
pub client: Arc<TestBlockChainClient>,
|
||||||
pub sync: Arc<TestSyncProvider>,
|
pub sync: Arc<TestSyncProvider>,
|
||||||
_accounts_provider: Arc<TestAccountProvider>,
|
_accounts_provider: Arc<TestAccountProvider>,
|
||||||
_miner: Arc<TestMinerService>,
|
miner: Arc<TestMinerService>,
|
||||||
hashrates: Arc<RwLock<HashMap<H256, U256>>>,
|
hashrates: Arc<RwLock<HashMap<H256, U256>>>,
|
||||||
pub io: IoHandler,
|
pub io: IoHandler,
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ impl Default for EthTester {
|
|||||||
client: client,
|
client: client,
|
||||||
sync: sync,
|
sync: sync,
|
||||||
_accounts_provider: ap,
|
_accounts_provider: ap,
|
||||||
_miner: miner,
|
miner: miner,
|
||||||
io: io,
|
io: io,
|
||||||
hashrates: hashrates,
|
hashrates: hashrates,
|
||||||
}
|
}
|
||||||
@ -258,6 +258,27 @@ fn rpc_eth_transaction_count_by_number_pending() {
|
|||||||
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_pending_transaction_by_hash() {
|
||||||
|
use util::*;
|
||||||
|
use ethcore::transaction::*;
|
||||||
|
|
||||||
|
let tester = EthTester::default();
|
||||||
|
{
|
||||||
|
let tx: SignedTransaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap());
|
||||||
|
tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#;
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_getTransactionByHash",
|
||||||
|
"params": ["0x0000000000000000000000000000000000000000000000000000000000000000"],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rpc_eth_uncle_count_by_block_hash() {
|
fn rpc_eth_uncle_count_by_block_hash() {
|
||||||
|
@ -30,6 +30,8 @@ pub struct TestMinerService {
|
|||||||
pub imported_transactions: RwLock<Vec<H256>>,
|
pub imported_transactions: RwLock<Vec<H256>>,
|
||||||
/// Latest closed block.
|
/// Latest closed block.
|
||||||
pub latest_closed_block: Mutex<Option<ClosedBlock>>,
|
pub latest_closed_block: Mutex<Option<ClosedBlock>>,
|
||||||
|
/// Pre-existed pending transactions
|
||||||
|
pub pending_transactions: Mutex<HashMap<H256, SignedTransaction>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TestMinerService {
|
impl Default for TestMinerService {
|
||||||
@ -37,6 +39,7 @@ impl Default for TestMinerService {
|
|||||||
TestMinerService {
|
TestMinerService {
|
||||||
imported_transactions: RwLock::new(Vec::new()),
|
imported_transactions: RwLock::new(Vec::new()),
|
||||||
latest_closed_block: Mutex::new(None),
|
latest_closed_block: Mutex::new(None),
|
||||||
|
pending_transactions: Mutex::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,6 +73,10 @@ impl MinerService for TestMinerService {
|
|||||||
|
|
||||||
fn map_sealing_work<F, T>(&self, _chain: &BlockChainClient, _f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T { unimplemented!(); }
|
fn map_sealing_work<F, T>(&self, _chain: &BlockChainClient, _f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T { unimplemented!(); }
|
||||||
|
|
||||||
|
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
|
||||||
|
self.pending_transactions.lock().unwrap().get(hash).and_then(|tx_ref| Some(tx_ref.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||||
/// Will check the seal, but not actually insert the block into the chain.
|
/// Will check the seal, but not actually insert the block into the chain.
|
||||||
fn submit_seal(&self, _chain: &BlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> { unimplemented!(); }
|
fn submit_seal(&self, _chain: &BlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> { unimplemented!(); }
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use ethcore::transaction::{LocalizedTransaction, Action};
|
use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
|
||||||
use v1::types::{Bytes, OptionalValue};
|
use v1::types::{Bytes, OptionalValue};
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize)]
|
#[derive(Debug, Default, Serialize)]
|
||||||
@ -58,6 +58,27 @@ impl From<LocalizedTransaction> for Transaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<SignedTransaction> for Transaction {
|
||||||
|
fn from(t: SignedTransaction) -> Transaction {
|
||||||
|
Transaction {
|
||||||
|
hash: t.hash(),
|
||||||
|
nonce: t.nonce,
|
||||||
|
block_hash: OptionalValue::Null,
|
||||||
|
block_number: OptionalValue::Null,
|
||||||
|
transaction_index: OptionalValue::Null,
|
||||||
|
from: t.sender().unwrap(),
|
||||||
|
to: match t.action {
|
||||||
|
Action::Create => OptionalValue::Null,
|
||||||
|
Action::Call(ref address) => OptionalValue::Value(address.clone())
|
||||||
|
},
|
||||||
|
value: t.value,
|
||||||
|
gas_price: t.gas_price,
|
||||||
|
gas: t.gas,
|
||||||
|
input: Bytes::new(t.data.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -102,4 +102,27 @@ mod tests {
|
|||||||
nonce: None,
|
nonce: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_request_deserialize_test() {
|
||||||
|
let s = r#"{
|
||||||
|
"from":"0xb5f7502a2807cb23615c7456055e1d65b2508625",
|
||||||
|
"to":"0x895d32f2db7d01ebb50053f9e48aacf26584fe40",
|
||||||
|
"data":"0x8595bab1",
|
||||||
|
"gas":"0x2fd618",
|
||||||
|
"gasPrice":"0x0ba43b7400"
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let deserialized: TransactionRequest = serde_json::from_str(s).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(deserialized, TransactionRequest {
|
||||||
|
from: Address::from_str("b5f7502a2807cb23615c7456055e1d65b2508625").unwrap(),
|
||||||
|
to: Some(Address::from_str("895d32f2db7d01ebb50053f9e48aacf26584fe40").unwrap()),
|
||||||
|
gas_price: Some(U256::from_str("0ba43b7400").unwrap()),
|
||||||
|
gas: Some(U256::from_str("2fd618").unwrap()),
|
||||||
|
value: None,
|
||||||
|
data: Some(Bytes::new(vec![0x85, 0x95, 0xba, 0xb1])),
|
||||||
|
nonce: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,8 @@ pub struct SecretStore {
|
|||||||
|
|
||||||
struct AccountUnlock {
|
struct AccountUnlock {
|
||||||
secret: H256,
|
secret: H256,
|
||||||
expires: DateTime<UTC>,
|
/// expiration datetime (None - never)
|
||||||
|
expires: Option<DateTime<UTC>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Basic account management trait
|
/// Basic account management trait
|
||||||
@ -148,6 +149,11 @@ impl AccountService {
|
|||||||
pub fn tick(&self) {
|
pub fn tick(&self) {
|
||||||
self.secret_store.write().unwrap().collect_garbage();
|
self.secret_store.write().unwrap().collect_garbage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unlocks account for use (no expiration of unlock)
|
||||||
|
pub fn unlock_account_no_expire(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> {
|
||||||
|
self.secret_store.write().unwrap().unlock_account_with_expiration(account, pass, None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -226,14 +232,23 @@ impl SecretStore {
|
|||||||
|
|
||||||
/// Unlocks account for use
|
/// Unlocks account for use
|
||||||
pub fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> {
|
pub fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> {
|
||||||
|
self.unlock_account_with_expiration(account, pass, Some(UTC::now() + Duration::minutes(20)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unlocks account for use (no expiration of unlock)
|
||||||
|
pub fn unlock_account_no_expire(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> {
|
||||||
|
self.unlock_account_with_expiration(account, pass, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unlock_account_with_expiration(&self, account: &Address, pass: &str, expiration: Option<DateTime<UTC>>) -> Result<(), EncryptedHashMapError> {
|
||||||
let secret_id = try!(self.account(&account).ok_or(EncryptedHashMapError::UnknownIdentifier));
|
let secret_id = try!(self.account(&account).ok_or(EncryptedHashMapError::UnknownIdentifier));
|
||||||
let secret = try!(self.get(&secret_id, pass));
|
let secret = try!(self.get(&secret_id, pass));
|
||||||
{
|
{
|
||||||
let mut write_lock = self.unlocks.write().unwrap();
|
let mut write_lock = self.unlocks.write().unwrap();
|
||||||
let mut unlock = write_lock.entry(*account)
|
let mut unlock = write_lock.entry(*account)
|
||||||
.or_insert_with(|| AccountUnlock { secret: secret, expires: UTC::now() });
|
.or_insert_with(|| AccountUnlock { secret: secret, expires: Some(UTC::now()) });
|
||||||
unlock.secret = secret;
|
unlock.secret = secret;
|
||||||
unlock.expires = UTC::now() + Duration::minutes(20);
|
unlock.expires = expiration;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -277,7 +292,7 @@ impl SecretStore {
|
|||||||
self.directory.collect_garbage();
|
self.directory.collect_garbage();
|
||||||
let utc = UTC::now();
|
let utc = UTC::now();
|
||||||
let expired_addresses = garbage_lock.iter()
|
let expired_addresses = garbage_lock.iter()
|
||||||
.filter(|&(_, unlock)| unlock.expires < utc)
|
.filter(|&(_, unlock)| match unlock.expires { Some(ref expire_val) => expire_val < &utc, _ => false })
|
||||||
.map(|(address, _)| address.clone()).collect::<Vec<Address>>();
|
.map(|(address, _)| address.clone()).collect::<Vec<Address>>();
|
||||||
|
|
||||||
for expired in expired_addresses { garbage_lock.remove(&expired); }
|
for expired in expired_addresses { garbage_lock.remove(&expired); }
|
||||||
@ -629,7 +644,7 @@ mod tests {
|
|||||||
let ss_rw = svc.secret_store.write().unwrap();
|
let ss_rw = svc.secret_store.write().unwrap();
|
||||||
let mut ua_rw = ss_rw.unlocks.write().unwrap();
|
let mut ua_rw = ss_rw.unlocks.write().unwrap();
|
||||||
let entry = ua_rw.entry(address);
|
let entry = ua_rw.entry(address);
|
||||||
if let Entry::Occupied(mut occupied) = entry { occupied.get_mut().expires = UTC::now() - Duration::minutes(1); }
|
if let Entry::Occupied(mut occupied) = entry { occupied.get_mut().expires = Some(UTC::now() - Duration::minutes(1)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
svc.tick();
|
svc.tick();
|
||||||
|
Loading…
Reference in New Issue
Block a user