From 3b6d886860cbe89736ed4448d992b3b20572d990 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 27 Nov 2016 14:11:37 +0100 Subject: [PATCH 01/43] Fix up the transaction JSON serialisation for RPC. --- ethcore/src/types/transaction.rs | 3 +++ rpc/src/v1/types/transaction.rs | 22 ++++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 8289c5864..d7e06790b 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -304,6 +304,9 @@ impl SignedTransaction { /// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if invalid. pub fn standard_v(&self) -> u8 { match self.v { v if v == 27 || v == 28 || v > 36 => (v - 1) % 2, _ => 4 } } + /// The `v` value that appears in the RLP. + pub fn original_v(&self) -> u8 { self.v } + /// The network ID, or `None` if this is a global transaction. pub fn network_id(&self) -> Option { match self.v { diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index f566f9b20..561843246 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -57,12 +57,18 @@ pub struct Transaction { /// Public key of the signer. #[serde(rename="publicKey")] pub public_key: Option, - /// The V field of the signature. + /// The network id of the transaction, if any. + #[serde(rename="networkId")] + pub network_id: Option, + /// The standardised V field of the signature (0 or 1). + #[serde(rename="standardV")] + pub standard_v: u8, + /// The standardised V field of the signature. pub v: u8, /// The R field of the signature. - pub r: H256, + pub r: U256, /// The S field of the signature. - pub s: H256, + pub s: U256, } /// Local Transaction Status @@ -176,7 +182,9 @@ impl From for Transaction { }, raw: ::rlp::encode(&t.signed).to_vec().into(), public_key: t.public_key().ok().map(Into::into), - v: signature.v(), + network_id: t.network_id(), + standard_v: t.standard_v(), + v: t.original_v(), r: signature.r().into(), s: signature.s().into(), } @@ -207,7 +215,9 @@ impl From for Transaction { }, raw: ::rlp::encode(&t).to_vec().into(), public_key: t.public_key().ok().map(Into::into), - v: signature.v(), + network_id: t.network_id(), + standard_v: t.standard_v(), + v: t.original_v(), r: signature.r().into(), s: signature.s().into(), } @@ -238,7 +248,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"v":0,"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000"}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"v":0,"r":"0x","s":"0x"}"#); } #[test] From 0cf8db58b8c06c12b6ea6f9ca1dd6a68d4a543b6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 27 Nov 2016 14:49:30 +0100 Subject: [PATCH 02/43] Fix tests. --- ethcore/res/ethereum/tests | 2 +- rpc/src/v1/tests/mocked/eth.rs | 9 ++++++--- rpc/src/v1/tests/mocked/signing.rs | 7 +++++-- rpc/src/v1/types/block.rs | 2 +- rpc/src/v1/types/transaction.rs | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index d509c7593..e8f4624b7 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit d509c75936ec6cbba683ee1916aa0bca436bc376 +Subproject commit e8f4624b7f1a15c63674eecf577c7ab76c3b16be diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 861bb5234..d1d419719 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -495,7 +495,7 @@ fn rpc_eth_pending_transaction_by_hash() { tester.miner.pending_transactions.lock().insert(H256::zero(), tx); } - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":0,"value":"0xa"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":0,"to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":27,"value":"0xa"},"id":1}"#; let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", @@ -810,13 +810,16 @@ fn rpc_eth_sign_transaction() { &format!("\"from\":\"0x{:?}\",", &address) + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + &format!("\"hash\":\"0x{:?}\",", t.hash()) + - r#""input":"0x","nonce":"0x1","# + + r#""input":"0x","# + + &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + &format!("\"r\":\"0x{}\",", signature.r().to_hex()) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + &format!("\"s\":\"0x{}\",", signature.s().to_hex()) + + &format!("\"standardV\":{},", t.standard_v()) + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + - &format!("\"v\":{},", signature.v()) + + &format!("\"v\":{},", t.original_v()) + r#""value":"0x9184e72a""# + r#"}},"id":1}"#; diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 7431bc45e..3166f0436 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -278,13 +278,16 @@ fn should_add_sign_transaction_to_the_queue() { &format!("\"from\":\"0x{:?}\",", &address) + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + &format!("\"hash\":\"0x{:?}\",", t.hash()) + - r#""input":"0x","nonce":"0x1","# + + r#""input":"0x","# + + &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + &format!("\"r\":\"0x{}\",", signature.r().to_hex()) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + &format!("\"s\":\"0x{}\",", signature.s().to_hex()) + + &format!("\"standardV\":{},", t.standard_v()) + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + - &format!("\"v\":{},", signature.v()) + + &format!("\"v\":{},", t.original_v()) + r#""value":"0x9184e72a""# + r#"}},"id":1}"#; diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index f52785e90..270b077be 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -139,7 +139,7 @@ mod tests { fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"v":0,"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000"}]"#); + assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":0,"v":0,"r":"0x0","s":"0x0"}]"#); let t = BlockTransactions::Hashes(vec![H256::default().into()]); let serialized = serde_json::to_string(&t).unwrap(); diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 561843246..bd56e6a30 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -248,7 +248,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"v":0,"r":"0x","s":"0x"}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":0,"v":0,"r":"0x0","s":"0x0"}"#); } #[test] From 436016ef026c9444f8e2b1fcb1fd0df1255c462d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 29 Nov 2016 13:46:06 +0100 Subject: [PATCH 03/43] Introduce to_hex() utility in bigint. Fix tests. --- rpc/src/v1/tests/mocked/eth.rs | 10 ++++---- rpc/src/v1/tests/mocked/signing.rs | 8 +++---- rpc/src/v1/types/block.rs | 2 +- rpc/src/v1/types/transaction.rs | 14 +++++------ util/bigint/src/uint.rs | 37 +++++++++++++++++++++++++----- 5 files changed, 48 insertions(+), 23 deletions(-) diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index d1d419719..c25ed881b 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -495,7 +495,7 @@ fn rpc_eth_pending_transaction_by_hash() { tester.miner.pending_transactions.lock().insert(H256::zero(), tx); } - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":0,"to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":27,"value":"0xa"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#; let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", @@ -814,12 +814,12 @@ fn rpc_eth_sign_transaction() { &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + - &format!("\"r\":\"0x{}\",", signature.r().to_hex()) + + &format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + - &format!("\"s\":\"0x{}\",", signature.s().to_hex()) + - &format!("\"standardV\":{},", t.standard_v()) + + &format!("\"s\":\"0x{}\",", U256::from(signature.s()).to_hex()) + + &format!("\"standardV\":\"0x{}\",", U256::from(t.standard_v()).to_hex()) + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + - &format!("\"v\":{},", t.original_v()) + + &format!("\"v\":\"0x{}\",", U256::from(t.original_v()).to_hex()) + r#""value":"0x9184e72a""# + r#"}},"id":1}"#; diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 3166f0436..4b1314853 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -282,12 +282,12 @@ fn should_add_sign_transaction_to_the_queue() { &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + - &format!("\"r\":\"0x{}\",", signature.r().to_hex()) + + &format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + - &format!("\"s\":\"0x{}\",", signature.s().to_hex()) + - &format!("\"standardV\":{},", t.standard_v()) + + &format!("\"s\":\"0x{}\",", U256::from(signature.s()).to_hex()) + + &format!("\"standardV\":\"0x{}\",", U256::from(t.standard_v()).to_hex()) + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + - &format!("\"v\":{},", t.original_v()) + + &format!("\"v\":\"0x{}\",", U256::from(t.original_v()).to_hex()) + r#""value":"0x9184e72a""# + r#"}},"id":1}"#; diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 270b077be..5aab6ced8 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -139,7 +139,7 @@ mod tests { fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":0,"v":0,"r":"0x0","s":"0x0"}]"#); + assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0"}]"#); let t = BlockTransactions::Hashes(vec![H256::default().into()]); let serialized = serde_json::to_string(&t).unwrap(); diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index bd56e6a30..7f26adf41 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -62,9 +62,9 @@ pub struct Transaction { pub network_id: Option, /// The standardised V field of the signature (0 or 1). #[serde(rename="standardV")] - pub standard_v: u8, + pub standard_v: U256, /// The standardised V field of the signature. - pub v: u8, + pub v: U256, /// The R field of the signature. pub r: U256, /// The S field of the signature. @@ -183,8 +183,8 @@ impl From for Transaction { raw: ::rlp::encode(&t.signed).to_vec().into(), public_key: t.public_key().ok().map(Into::into), network_id: t.network_id(), - standard_v: t.standard_v(), - v: t.original_v(), + standard_v: t.standard_v().into(), + v: t.original_v().into(), r: signature.r().into(), s: signature.s().into(), } @@ -216,8 +216,8 @@ impl From for Transaction { raw: ::rlp::encode(&t).to_vec().into(), public_key: t.public_key().ok().map(Into::into), network_id: t.network_id(), - standard_v: t.standard_v(), - v: t.original_v(), + standard_v: t.standard_v().into(), + v: t.original_v().into(), r: signature.r().into(), s: signature.s().into(), } @@ -248,7 +248,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":0,"v":0,"r":"0x0","s":"0x0"}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0"}"#); } #[test] diff --git a/util/bigint/src/uint.rs b/util/bigint/src/uint.rs index f4dd91140..49aa06d91 100644 --- a/util/bigint/src/uint.rs +++ b/util/bigint/src/uint.rs @@ -37,12 +37,12 @@ //! implementations for even more speed, hidden behind the `x64_arithmetic` //! feature flag. -use std::{mem, fmt}; +use std::{mem, fmt, cmp}; use std::str::{FromStr}; use std::hash::Hash; use std::ops::{Shr, Shl, BitAnd, BitOr, BitXor, Not, Div, Rem, Mul, Add, Sub}; use std::cmp::Ordering; -use rustc_serialize::hex::{FromHex, FromHexError}; +use rustc_serialize::hex::{ToHex, FromHex, FromHexError}; /// Conversion from decimal string error #[derive(Debug, PartialEq)] @@ -520,8 +520,10 @@ pub trait Uint: Sized + Default + FromStr + From + fmt::Debug + fmt::Displa fn bit(&self, index: usize) -> bool; /// Return single byte fn byte(&self, index: usize) -> u8; - /// Convert U256 to the sequence of bytes with a big endian + /// Convert to the sequence of bytes with a big endian fn to_big_endian(&self, bytes: &mut[u8]); + /// Convert to a non-zero-prefixed hex representation prefixed by `0x`. + fn to_hex(&self) -> String; /// Create `Uint(10**n)` fn exp10(n: usize) -> Self; /// Return eponentation `self**other`. Panic on overflow. @@ -684,6 +686,17 @@ macro_rules! construct_uint { } } + #[inline] + fn to_hex(&self) -> String { + if self.is_zero() { return "0".to_owned(); } // special case. + let mut bytes = [0u8; 8 * $n_words]; + self.to_big_endian(&mut bytes); + let bp7 = self.bits() + 7; + let len = cmp::max(bp7 / 8, 1); + let bytes_hex = bytes[bytes.len() - len..].to_hex(); + (&bytes_hex[1 - bp7 % 8 / 4..]).to_owned() + } + #[inline] fn exp10(n: usize) -> Self { match n { @@ -1637,7 +1650,7 @@ mod tests { } #[test] - fn uint256_pow () { + fn uint256_pow() { assert_eq!(U256::from(10).pow(U256::from(0)), U256::from(1)); assert_eq!(U256::from(10).pow(U256::from(1)), U256::from(10)); assert_eq!(U256::from(10).pow(U256::from(2)), U256::from(100)); @@ -1647,12 +1660,24 @@ mod tests { #[test] #[should_panic] - fn uint256_pow_overflow_panic () { + fn uint256_pow_overflow_panic() { U256::from(2).pow(U256::from(0x100)); } #[test] - fn uint256_overflowing_pow () { + fn should_format_hex_correctly() { + assert_eq!(&U256::from(0).to_hex(), &"0"); + assert_eq!(&U256::from(0x1).to_hex(), &"1"); + assert_eq!(&U256::from(0xf).to_hex(), &"f"); + assert_eq!(&U256::from(0x10).to_hex(), &"10"); + assert_eq!(&U256::from(0xff).to_hex(), &"ff"); + assert_eq!(&U256::from(0x100).to_hex(), &"100"); + assert_eq!(&U256::from(0xfff).to_hex(), &"fff"); + assert_eq!(&U256::from(0x1000).to_hex(), &"1000"); + } + + #[test] + fn uint256_overflowing_pow() { // assert_eq!( // U256::from(2).overflowing_pow(U256::from(0xff)), // (U256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap(), false) From 0e0bdc8ae93aaa3889693cf10ec438fae4e67bf5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 29 Nov 2016 13:48:28 +0100 Subject: [PATCH 04/43] Deduplicate code. --- rpc/src/v1/types/uint.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/rpc/src/v1/types/uint.rs b/rpc/src/v1/types/uint.rs index ce0fa49a2..e513d23db 100644 --- a/rpc/src/v1/types/uint.rs +++ b/rpc/src/v1/types/uint.rs @@ -14,9 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::cmp; use std::str::FromStr; -use rustc_serialize::hex::ToHex; use serde; use util::{U256 as EthU256, Uint}; @@ -50,18 +48,7 @@ macro_rules! impl_uint { impl serde::Serialize for $name { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer { - let mut hex = "0x".to_owned(); - let mut bytes = [0u8; 8 * $size]; - self.0.to_big_endian(&mut bytes); - let len = cmp::max((self.0.bits() + 7) / 8, 1); - let bytes_hex = bytes[bytes.len() - len..].to_hex(); - - if bytes_hex.starts_with('0') { - hex.push_str(&bytes_hex[1..]); - } else { - hex.push_str(&bytes_hex); - } - serializer.serialize_str(&hex) + serializer.serialize_str(&format!("0x{}", self.0.to_hex())) } } From d58905ae13f781139380f3611bd6d137eeb64208 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 29 Nov 2016 17:59:17 +0100 Subject: [PATCH 05/43] fix comment --- util/bigint/src/uint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/bigint/src/uint.rs b/util/bigint/src/uint.rs index 49aa06d91..c0b6e0987 100644 --- a/util/bigint/src/uint.rs +++ b/util/bigint/src/uint.rs @@ -522,7 +522,7 @@ pub trait Uint: Sized + Default + FromStr + From + fmt::Debug + fmt::Displa fn byte(&self, index: usize) -> u8; /// Convert to the sequence of bytes with a big endian fn to_big_endian(&self, bytes: &mut[u8]); - /// Convert to a non-zero-prefixed hex representation prefixed by `0x`. + /// Convert to a non-zero-prefixed hex representation (not prefixed by `0x`). fn to_hex(&self) -> String; /// Create `Uint(10**n)` fn exp10(n: usize) -> Self; From cd5b6fdf592654e83b229104c4263e770d9202a0 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 2 Dec 2016 18:21:54 +0100 Subject: [PATCH 06/43] queue: CLI for auto-scaling and num verifiers --- ethcore/src/verification/queue/mod.rs | 58 ++++++++++++++++++++++----- parity/blockchain.rs | 18 ++++++++- parity/cli/mod.rs | 2 + parity/cli/usage.txt | 7 +++- parity/configuration.rs | 15 +++++++ parity/run.rs | 6 ++- 6 files changed, 92 insertions(+), 14 deletions(-) diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index 686a1d093..de4428f02 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -53,6 +53,8 @@ pub struct Config { /// Maximum heap memory to use. /// When the limit is reached, is_full returns true. pub max_mem_use: usize, + /// Settings for the number of verifiers and adaptation strategy. + pub verifier_settings: VerifierSettings, } impl Default for Config { @@ -60,6 +62,26 @@ impl Default for Config { Config { max_queue_size: 30000, max_mem_use: 50 * 1024 * 1024, + verifier_settings: VerifierSettings::default(), + } + } +} + +/// Verifier settings. +#[derive(Debug, PartialEq, Clone)] +pub struct VerifierSettings { + /// Whether to scale amount of verifiers according to load. + // Todo: replace w/ strategy enum? + pub scale_verifiers: bool, + /// Beginning amount of verifiers. + pub num_verifiers: usize, +} + +impl Default for VerifierSettings { + fn default() -> Self { + VerifierSettings { + scale_verifiers: false, + num_verifiers: MAX_VERIFIERS, } } } @@ -139,6 +161,7 @@ pub struct VerificationQueue { ticks_since_adjustment: AtomicUsize, max_queue_size: usize, max_mem_use: usize, + scale_verifiers: bool, } struct QueueSignal { @@ -221,12 +244,15 @@ impl VerificationQueue { }); let empty = Arc::new(SCondvar::new()); let panic_handler = PanicHandler::new_in_arc(); + let scale_verifiers = config.verifier_settings.scale_verifiers; - let max_verifiers = min(::num_cpus::get(), MAX_VERIFIERS); - let default_amount = max(::num_cpus::get(), 3) - 2; + let num_cpus = ::num_cpus::get(); + let max_verifiers = min(num_cpus, MAX_VERIFIERS); + let default_amount = max(1, min(max_verifiers, config.verifier_settings.num_verifiers)); let mut verifiers = Vec::with_capacity(max_verifiers); debug!(target: "verification", "Allocating {} verifiers, {} initially active", max_verifiers, default_amount); + debug!(target: "verification", "Verifier auto-scaling {}", if scale_verifiers { "enabled" } else { "disabled" }); for i in 0..max_verifiers { debug!(target: "verification", "Adding verification thread #{}", i); @@ -273,6 +299,7 @@ impl VerificationQueue { ticks_since_adjustment: AtomicUsize::new(0), max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT), max_mem_use: max(config.max_mem_use, MIN_MEM_LIMIT), + scale_verifiers: scale_verifiers, } } @@ -598,6 +625,8 @@ impl VerificationQueue { self.processing.write().shrink_to_fit(); + if !self.scale_verifiers { return } + if self.ticks_since_adjustment.fetch_add(1, AtomicOrdering::SeqCst) + 1 >= READJUSTMENT_PERIOD { self.ticks_since_adjustment.store(0, AtomicOrdering::SeqCst); } else { @@ -693,10 +722,15 @@ mod tests { use error::*; use views::*; - fn get_test_queue() -> BlockQueue { + // create a test block queue. + // auto_scaling enables verifier adjustment. + fn get_test_queue(auto_scale: bool) -> BlockQueue { let spec = get_test_spec(); let engine = spec.engine; - BlockQueue::new(Config::default(), engine, IoChannel::disconnected(), true) + + let mut config = Config::default(); + config.verifier_settings.scale_verifiers = auto_scale; + BlockQueue::new(config, engine, IoChannel::disconnected(), true) } #[test] @@ -709,7 +743,7 @@ mod tests { #[test] fn can_import_blocks() { - let queue = get_test_queue(); + let queue = get_test_queue(false); if let Err(e) = queue.import(Unverified::new(get_good_dummy_block())) { panic!("error importing block that is valid by definition({:?})", e); } @@ -717,7 +751,7 @@ mod tests { #[test] fn returns_error_for_duplicates() { - let queue = get_test_queue(); + let queue = get_test_queue(false); if let Err(e) = queue.import(Unverified::new(get_good_dummy_block())) { panic!("error importing block that is valid by definition({:?})", e); } @@ -736,7 +770,7 @@ mod tests { #[test] fn returns_ok_for_drained_duplicates() { - let queue = get_test_queue(); + let queue = get_test_queue(false); let block = get_good_dummy_block(); let hash = BlockView::new(&block).header().hash().clone(); if let Err(e) = queue.import(Unverified::new(block)) { @@ -753,7 +787,7 @@ mod tests { #[test] fn returns_empty_once_finished() { - let queue = get_test_queue(); + let queue = get_test_queue(false); queue.import(Unverified::new(get_good_dummy_block())) .expect("error importing block that is valid by definition"); queue.flush(); @@ -781,7 +815,7 @@ mod tests { fn scaling_limits() { use super::MAX_VERIFIERS; - let queue = get_test_queue(); + let queue = get_test_queue(true); queue.scale_verifiers(MAX_VERIFIERS + 1); assert!(queue.verifiers.lock().1 < MAX_VERIFIERS + 1); @@ -793,7 +827,7 @@ mod tests { #[test] fn readjust_verifiers() { - let queue = get_test_queue(); + let queue = get_test_queue(true); // put all the verifiers to sleep to ensure // the test isn't timing sensitive. @@ -806,13 +840,15 @@ mod tests { verifiers.1 }; + queue.scale_verifiers(num_verifiers - 1); + for block in get_good_dummy_block_seq(5000) { queue.import(Unverified::new(block)).expect("Block good by definition; qed"); } // almost all unverified == bump verifier count. queue.collect_garbage(); - assert_eq!(queue.verifiers.lock().1, num_verifiers + 1); + assert_eq!(queue.verifiers.lock().1, num_verifiers); // wake them up again and verify everything. { diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 02d3e39fb..0750d369d 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -28,6 +28,7 @@ use ethcore::service::ClientService; use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, BlockImportError, BlockChainClient, BlockID}; use ethcore::error::ImportError; use ethcore::miner::Miner; +use ethcore::verification::queue::VerifierSettings; use cache::CacheConfig; use informant::{Informant, MillisecondDuration}; use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_bool}; @@ -84,6 +85,7 @@ pub struct ImportBlockchain { pub vm_type: VMType, pub check_seal: bool, pub with_color: bool, + pub verifier_settings: VerifierSettings, } #[derive(Debug, PartialEq)] @@ -175,7 +177,21 @@ fn execute_import(cmd: ImportBlockchain) -> Result { try!(execute_upgrades(&db_dirs, algorithm, cmd.compaction.compaction_profile(db_dirs.fork_path().as_path()))); // prepare client config - let client_config = to_client_config(&cmd.cache_config, Mode::Active, tracing, fat_db, cmd.compaction, cmd.wal, cmd.vm_type, "".into(), algorithm, cmd.pruning_history, cmd.check_seal); + let mut client_config = to_client_config( + &cmd.cache_config, + Mode::Active, + tracing, + fat_db, + cmd.compaction, + cmd.wal, + cmd.vm_type, + "".into(), + algorithm, + cmd.pruning_history, + cmd.check_seal + ); + + client_config.queue.verifier_settings = cmd.verifier_settings; // build client let service = try!(ClientService::start( diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index d33c58d9d..30b273b38 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -242,6 +242,8 @@ usage! { or |c: &Config| otry!(c.footprint).db_compaction.clone(), flag_fat_db: String = "auto", or |c: &Config| otry!(c.footprint).fat_db.clone(), + flag_scale_verifiers: bool = false, or |_| None, + flag_num_verifiers: Option = None, or |_| None, // -- Import/Export Options flag_from: String = "1", or |_| None, diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index b67af6110..02e7e00ec 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -250,7 +250,7 @@ Footprint Options: the state cache (default: {flag_cache_size_state}). --cache-size MB Set total amount of discretionary memory to use for the entire system, overrides other cache and queue - options.a (default: {flag_cache_size:?}) + options. (default: {flag_cache_size:?}) --fast-and-loose Disables DB WAL, which gives a significant speed up but means an unclean exit is unrecoverable. (default: {flag_fast_and_loose}) --db-compaction TYPE Database compaction type. TYPE may be one of: @@ -261,6 +261,11 @@ Footprint Options: of all accounts and storage keys. Doubles the size of the state database. BOOL may be one of on, off or auto. (default: {flag_fat_db}) + --scale-verifiers Automatically scale amount of verifier threads based on + workload. Not guaranteed to be faster. + (default: {flag_scale_verifiers}) + --num-verifiers INT Amount of verifier threads to use or to begin with, if verifier + auto-scaling is enabled. (default: {flag_num_verifiers:?}) Import/Export Options: --from BLOCK Export from block BLOCK, which may be an index or diff --git a/parity/configuration.rs b/parity/configuration.rs index c4a54f747..7b8100bbb 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -25,6 +25,7 @@ use util::log::Colour; use ethsync::{NetworkConfiguration, is_valid_node_url, AllowIP}; use ethcore::client::VMType; use ethcore::miner::{MinerOptions, Banning}; +use ethcore::verification::queue::VerifierSettings; use rpc::{IpcConfiguration, HttpConfiguration}; use ethcore_rpc::NetworkSettings; @@ -158,6 +159,7 @@ impl Configuration { vm_type: vm_type, check_seal: !self.args.flag_no_seal_check, with_color: logger_config.color, + verifier_settings: self.verifier_settings(), }; Cmd::Blockchain(BlockchainCmd::Import(import_cmd)) } else if self.args.cmd_export { @@ -241,6 +243,8 @@ impl Configuration { None }; + let verifier_settings = self.verifier_settings(); + let run_cmd = RunCmd { cache_config: cache_config, dirs: dirs, @@ -275,6 +279,7 @@ impl Configuration { no_periodic_snapshot: self.args.flag_no_periodic_snapshot, check_seal: !self.args.flag_no_seal_check, download_old_blocks: !self.args.flag_no_ancient_blocks, + verifier_settings: verifier_settings, }; Cmd::Run(run_cmd) }; @@ -702,6 +707,16 @@ impl Configuration { !ui_disabled } + + fn verifier_settings(&self) -> VerifierSettings { + let mut settings = VerifierSettings::default(); + settings.scale_verifiers = self.args.flag_scale_verifiers; + if let Some(num_verifiers) = self.args.flag_num_verifiers { + settings.num_verifiers = num_verifiers; + } + + settings + } } #[cfg(test)] diff --git a/parity/run.rs b/parity/run.rs index f977c450c..20a372424 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -28,6 +28,7 @@ use ethcore::service::ClientService; use ethcore::account_provider::AccountProvider; use ethcore::miner::{Miner, MinerService, ExternalMiner, MinerOptions}; use ethcore::snapshot; +use ethcore::verification::queue::VerifierSettings; use ethsync::SyncConfig; use informant::Informant; @@ -92,6 +93,7 @@ pub struct RunCmd { pub no_periodic_snapshot: bool, pub check_seal: bool, pub download_old_blocks: bool, + pub verifier_settings: VerifierSettings, } pub fn open_ui(dapps_conf: &dapps::Configuration, signer_conf: &signer::Configuration) -> Result<(), String> { @@ -217,7 +219,7 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { miner.set_transactions_limit(cmd.miner_extras.transactions_limit); // create client config - let client_config = to_client_config( + let mut client_config = to_client_config( &cmd.cache_config, mode.clone(), tracing, @@ -231,6 +233,8 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { cmd.check_seal, ); + client_config.queue.verifier_settings = cmd.verifier_settings; + // set up bootnodes let mut net_conf = cmd.net_conf; if !cmd.custom_bootnodes { From 80f98bc8b7a43275ef172119b17693e6b534850d Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 2 Dec 2016 18:36:00 +0100 Subject: [PATCH 07/43] fix tests --- parity/cli/config.full.toml | 2 ++ parity/cli/config.toml | 1 + parity/cli/mod.rs | 12 ++++++++++-- parity/configuration.rs | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index fcd9a9712..ffb0848ea 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -94,6 +94,8 @@ cache_size = 128 # Overrides above caches with total size fast_and_loose = false db_compaction = "ssd" fat_db = "auto" +scale_verifiers = true +num_verifiers = 6 [snapshots] disable_periodic = false diff --git a/parity/cli/config.toml b/parity/cli/config.toml index c9bd563a8..02b4a9577 100644 --- a/parity/cli/config.toml +++ b/parity/cli/config.toml @@ -57,6 +57,7 @@ cache_size_queue = 100 cache_size_state = 25 db_compaction = "ssd" fat_db = "off" +scale_verifiers = false, [snapshots] disable_periodic = true diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 30b273b38..1d1bcc4c0 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -242,8 +242,10 @@ usage! { or |c: &Config| otry!(c.footprint).db_compaction.clone(), flag_fat_db: String = "auto", or |c: &Config| otry!(c.footprint).fat_db.clone(), - flag_scale_verifiers: bool = false, or |_| None, - flag_num_verifiers: Option = None, or |_| None, + flag_scale_verifiers: bool = false, + or |c: &Config| otry!(c.footprint).scale_verifiers.clone(), + flag_num_verifiers: Option = None, + or |c: &Config| otry!(c.footprint).num_verifiers.clone().map(Some), // -- Import/Export Options flag_from: String = "1", or |_| None, @@ -404,6 +406,8 @@ struct Footprint { cache_size_state: Option, db_compaction: Option, fat_db: Option, + scale_verifiers: Option, + num_verifiers: Option, } #[derive(Default, Debug, PartialEq, RustcDecodable)] @@ -604,6 +608,8 @@ mod tests { flag_fast_and_loose: false, flag_db_compaction: "ssd".into(), flag_fat_db: "auto".into(), + flag_scale_verifiers: true, + flag_num_verifiers: Some(6), // -- Import/Export Options flag_from: "1".into(), @@ -773,6 +779,8 @@ mod tests { cache_size_state: Some(25), db_compaction: Some("ssd".into()), fat_db: Some("off".into()), + scale_verifiers: Some(false), + num_verifiers: None, }), snapshots: Some(Snapshots { disable_periodic: Some(true), diff --git a/parity/configuration.rs b/parity/configuration.rs index 7b8100bbb..55783e5b2 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -813,6 +813,7 @@ mod tests { vm_type: VMType::Interpreter, check_seal: true, with_color: !cfg!(windows), + verifier_settings: Default::default(), }))); } @@ -936,6 +937,7 @@ mod tests { no_periodic_snapshot: false, check_seal: true, download_old_blocks: true, + verifier_settings: Default::default(), })); } From 767d48601429931c2b5b846818860d712c889b72 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 5 Dec 2016 14:12:40 +0100 Subject: [PATCH 08/43] fix config test --- parity/cli/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parity/cli/config.toml b/parity/cli/config.toml index 02b4a9577..74f3477f3 100644 --- a/parity/cli/config.toml +++ b/parity/cli/config.toml @@ -57,7 +57,7 @@ cache_size_queue = 100 cache_size_state = 25 db_compaction = "ssd" fat_db = "off" -scale_verifiers = false, +scale_verifiers = false [snapshots] disable_periodic = true From 529a7fc33c61a88298a63fc205585538b219f4f5 Mon Sep 17 00:00:00 2001 From: keorn Date: Mon, 5 Dec 2016 17:08:16 +0000 Subject: [PATCH 09/43] add password and AccountProvider --- ethcore/src/engines/authority_round.rs | 46 +++++++++++------------ ethcore/src/engines/basic_authority.rs | 13 +++++-- ethcore/src/engines/instant_seal.rs | 12 ++---- ethcore/src/engines/mod.rs | 14 +++++-- ethcore/src/miner/miner.rs | 43 ++++++++++++++------- ethcore/src/miner/mod.rs | 3 ++ parity/run.rs | 5 ++- rpc/src/v1/impls/parity_set.rs | 6 +++ rpc/src/v1/tests/eth.rs | 1 + rpc/src/v1/tests/helpers/miner_service.rs | 10 +++++ rpc/src/v1/tests/helpers/sync_provider.rs | 2 +- rpc/src/v1/tests/mocked/parity_set.rs | 17 +++++++++ rpc/src/v1/traits/parity_set.rs | 4 ++ 13 files changed, 119 insertions(+), 57 deletions(-) diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index f28a06117..646c107e0 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -21,13 +21,15 @@ use std::sync::Weak; use std::time::{UNIX_EPOCH, Duration}; use util::*; use ethkey::{verify_address, Signature}; -use rlp::{Rlp, UntrustedRlp, View, encode}; +use rlp::{UntrustedRlp, View, encode}; use account_provider::AccountProvider; use block::*; use spec::CommonParams; use engines::Engine; use header::Header; use error::{Error, BlockError}; +use blockchain::extras::BlockDetails; +use views::HeaderView; use evm::Schedule; use ethjson; use io::{IoContext, IoHandler, TimerToken, IoService, IoChannel}; @@ -35,8 +37,6 @@ use service::ClientIoMessage; use transaction::SignedTransaction; use env_info::EnvInfo; use builtin::Builtin; -use blockchain::extras::BlockDetails; -use views::HeaderView; /// `AuthorityRound` params. #[derive(Debug, PartialEq)] @@ -72,6 +72,7 @@ pub struct AuthorityRound { message_channel: Mutex>>, step: AtomicUsize, proposed: AtomicBool, + account_provider: Mutex>>, } fn header_step(header: &Header) -> Result { @@ -104,7 +105,8 @@ impl AuthorityRound { transition_service: try!(IoService::::start()), message_channel: Mutex::new(None), step: AtomicUsize::new(initial_step), - proposed: AtomicBool::new(false) + proposed: AtomicBool::new(false), + account_provider: Mutex::new(None), }); let handler = TransitionHandler { engine: Arc::downgrade(&engine) }; try!(engine.transition_service.register_handler(Arc::new(handler))); @@ -196,7 +198,6 @@ impl Engine for AuthorityRound { } fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { - header.set_difficulty(parent.difficulty().clone()); header.set_gas_limit({ let gas_limit = parent.gas_limit().clone(); let bound_divisor = self.our_params.gas_limit_bound_divisor; @@ -221,12 +222,12 @@ impl Engine for AuthorityRound { /// /// This operation is synchronous and may (quite reasonably) not be available, in which `false` will /// be returned. - fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option> { + fn generate_seal(&self, block: &ExecutedBlock) -> Option> { if self.proposed.load(AtomicOrdering::SeqCst) { return None; } let header = block.header(); let step = self.step(); if self.is_step_proposer(step, header.author()) { - if let Some(ap) = accounts { + if let Some(ref ap) = *self.account_provider.lock() { // Account should be permanently unlocked, otherwise sealing will fail. if let Ok(signature) = ap.sign(*header.author(), None, header.bare_hash()) { trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step); @@ -303,22 +304,16 @@ impl Engine for AuthorityRound { t.sender().map(|_|()) // Perform EC recovery and cache sender } - fn register_message_channel(&self, message_channel: IoChannel) { - let mut guard = self.message_channel.lock(); - *guard = Some(message_channel); + fn is_new_best_block(&self, _best_total_difficulty: U256, best_header: HeaderView, _parent_details: &BlockDetails, new_header: &HeaderView) -> bool { + new_header.number() > best_header.number() } - fn is_new_best_block(&self, _best_total_difficulty: U256, best_header: HeaderView, _parent_details: &BlockDetails, new_header: &HeaderView) -> bool { - let new_number = new_header.number(); - let best_number = best_header.number(); - if new_number != best_number { - new_number > best_number - } else { - // Take the oldest step at given height. - let new_step: usize = Rlp::new(&new_header.seal()[0]).as_val(); - let best_step: usize = Rlp::new(&best_header.seal()[0]).as_val(); - new_step < best_step - } + fn register_message_channel(&self, message_channel: IoChannel) { + *self.message_channel.lock() = Some(message_channel); + } + + fn register_account_provider(&self, account_provider: Arc) { + *self.account_provider.lock() = Some(account_provider); } } @@ -393,6 +388,7 @@ mod tests { let spec = Spec::new_test_round(); let engine = &*spec.engine; + engine.register_account_provider(Arc::new(tap)); let genesis_header = spec.genesis_header(); let mut db1 = get_temp_state_db().take(); spec.ensure_db_good(&mut db1, &TrieFactory::new(TrieSpec::Secure)).unwrap(); @@ -404,16 +400,16 @@ mod tests { let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b2 = b2.close_and_lock(); - if let Some(seal) = engine.generate_seal(b1.block(), Some(&tap)) { + if let Some(seal) = engine.generate_seal(b1.block()) { assert!(b1.clone().try_seal(engine, seal).is_ok()); // Second proposal is forbidden. - assert!(engine.generate_seal(b1.block(), Some(&tap)).is_none()); + assert!(engine.generate_seal(b1.block()).is_none()); } - if let Some(seal) = engine.generate_seal(b2.block(), Some(&tap)) { + if let Some(seal) = engine.generate_seal(b2.block()) { assert!(b2.clone().try_seal(engine, seal).is_ok()); // Second proposal is forbidden. - assert!(engine.generate_seal(b2.block(), Some(&tap)).is_none()); + assert!(engine.generate_seal(b2.block()).is_none()); } } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index fb2f9bde6..0b9040a11 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -58,6 +58,7 @@ pub struct BasicAuthority { params: CommonParams, our_params: BasicAuthorityParams, builtins: BTreeMap, + account_provider: Mutex>>, } impl BasicAuthority { @@ -67,6 +68,7 @@ impl BasicAuthority { params: params, our_params: our_params, builtins: builtins, + account_provider: Mutex::new(None) } } } @@ -113,8 +115,8 @@ impl Engine for BasicAuthority { /// /// This operation is synchronous and may (quite reasonably) not be available, in which `false` will /// be returned. - fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option> { - if let Some(ap) = accounts { + fn generate_seal(&self, block: &ExecutedBlock) -> Option> { + if let Some(ref ap) = *self.account_provider.lock() { let header = block.header(); let message = header.bare_hash(); // account should be pernamently unlocked, otherwise sealing will fail @@ -179,6 +181,10 @@ impl Engine for BasicAuthority { fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> { t.sender().map(|_|()) // Perform EC recovery and cache sender } + + fn register_account_provider(&self, ap: Arc) { + *self.account_provider.lock() = Some(ap); + } } #[cfg(test)] @@ -254,6 +260,7 @@ mod tests { let spec = new_test_authority(); let engine = &*spec.engine; + engine.register_account_provider(Arc::new(tap)); let genesis_header = spec.genesis_header(); let mut db_result = get_temp_state_db(); let mut db = db_result.take(); @@ -261,7 +268,7 @@ mod tests { let last_hashes = Arc::new(vec![genesis_header.hash()]); let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close_and_lock(); - let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); + let seal = engine.generate_seal(b.block()).unwrap(); assert!(b.try_seal(engine, seal).is_ok()); } diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index f50f7344b..83335fb03 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -23,7 +23,6 @@ use spec::CommonParams; use evm::Schedule; use block::ExecutedBlock; use util::Bytes; -use account_provider::AccountProvider; /// An engine which does not provide any consensus mechanism, just seals blocks internally. pub struct InstantSeal { @@ -60,7 +59,7 @@ impl Engine for InstantSeal { fn is_sealer(&self, _author: &Address) -> Option { Some(true) } - fn generate_seal(&self, _block: &ExecutedBlock, _accounts: Option<&AccountProvider>) -> Option> { + fn generate_seal(&self, _block: &ExecutedBlock) -> Option> { Some(Vec::new()) } } @@ -70,16 +69,12 @@ mod tests { use util::*; use util::trie::TrieSpec; use tests::helpers::*; - use account_provider::AccountProvider; use spec::Spec; use header::Header; use block::*; #[test] fn instant_can_seal() { - let tap = AccountProvider::transient_provider(); - let addr = tap.insert_account("".sha3(), "").unwrap(); - let spec = Spec::new_instant(); let engine = &*spec.engine; let genesis_header = spec.genesis_header(); @@ -87,10 +82,9 @@ mod tests { let mut db = db_result.take(); spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close_and_lock(); - // Seal with empty AccountProvider. - let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); + let seal = engine.generate_seal(b.block()).unwrap(); assert!(b.try_seal(engine, seal).is_ok()); } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 163901edc..0401ba667 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -94,7 +94,7 @@ pub trait Engine : Sync + Send { /// /// This operation is synchronous and may (quite reasonably) not be available, in which None will /// be returned. - fn generate_seal(&self, _block: &ExecutedBlock, _accounts: Option<&AccountProvider>) -> Option> { None } + fn generate_seal(&self, _block: &ExecutedBlock) -> Option> { None } /// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block) /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. @@ -147,11 +147,17 @@ pub trait Engine : Sync + Send { self.builtins().get(a).expect("attempted to execute nonexistent builtin").execute(input, output); } - /// Add a channel for communication with Client which can be used for sealing. - fn register_message_channel(&self, _message_channel: IoChannel) {} - /// Check if new block should be chosen as the one in chain. fn is_new_best_block(&self, best_total_difficulty: U256, _best_header: HeaderView, parent_details: &BlockDetails, new_header: &HeaderView) -> bool { ethash::is_new_best_block(best_total_difficulty, parent_details, new_header) } + + /// Register an account which signs consensus messages. + fn set_signer(&self, _address: Address, _password: String) {} + + /// Add a channel for communication with Client which can be used for sealing. + fn register_message_channel(&self, _message_channel: IoChannel) {} + + /// Add an account provider useful for Engines that sign stuff. + fn register_account_provider(&self, _account_provider: Arc) {} } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index a543e608d..4b38a7e07 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -19,7 +19,7 @@ use std::time::{Instant, Duration}; use util::*; use util::using_queue::{UsingQueue, GetAction}; -use account_provider::AccountProvider; +use account_provider::{AccountProvider, Error as AccountError}; use views::{BlockView, HeaderView}; use header::Header; use state::{State, CleanupMode}; @@ -464,15 +464,12 @@ impl Miner { /// Attempts to perform internal sealing (one that does not require work) to return Ok(sealed), /// Err(Some(block)) returns for unsuccesful sealing while Err(None) indicates misspecified engine. fn seal_block_internally(&self, block: ClosedBlock) -> Result> { - trace!(target: "miner", "seal_block_internally: block has transaction - attempting internal seal."); - let s = self.engine.generate_seal(block.block(), match self.accounts { - Some(ref x) => Some(&**x), - None => None, - }); + trace!(target: "miner", "seal_block_internally: attempting internal seal."); + let s = self.engine.generate_seal(block.block()); if let Some(seal) = s { trace!(target: "miner", "seal_block_internally: managed internal seal. importing..."); - block.lock().try_seal(&*self.engine, seal).or_else(|_| { - warn!("prepare_sealing: ERROR: try_seal failed when given internally generated seal. WTF?"); + block.lock().try_seal(&*self.engine, seal).or_else(|(e, _)| { + warn!("prepare_sealing: ERROR: try_seal failed when given internally generated seal: {}", e); Err(None) }) } else { @@ -485,7 +482,7 @@ impl Miner { fn seal_and_import_block_internally(&self, chain: &MiningBlockChainClient, block: ClosedBlock) -> bool { if !block.transactions().is_empty() || self.forced_sealing() { if let Ok(sealed) = self.seal_block_internally(block) { - if chain.import_block(sealed.rlp_bytes()).is_ok() { + if chain.import_sealed_block(sealed).is_ok() { trace!(target: "miner", "import_block_internally: imported internally sealed block"); return true } @@ -740,6 +737,19 @@ impl MinerService for Miner { *self.author.write() = author; } + fn set_consensus_signer(&self, address: Address, password: String) -> Result<(), AccountError> { + if self.seals_internally { + if let Some(ref ap) = self.accounts { + try!(ap.sign(address.clone(), Some(password.clone()), Default::default())); + } + let mut sealing_work = self.sealing_work.lock(); + sealing_work.enabled = self.engine.is_sealer(&address).unwrap_or(false); + *self.author.write() = address; + self.engine.set_signer(address, password); + } + Ok(()) + } + fn set_extra_data(&self, extra_data: Bytes) { *self.extra_data.write() = extra_data; } @@ -1020,6 +1030,11 @@ impl MinerService for Miner { let (block, original_work_hash) = self.prepare_block(chain); if self.seals_internally { trace!(target: "miner", "update_sealing: engine indicates internal sealing"); + { + let mut sealing_work = self.sealing_work.lock(); + sealing_work.queue.push(block.clone()); + sealing_work.queue.use_last_ref(); + } self.seal_and_import_block_internally(chain, block); } else { trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work"); @@ -1042,7 +1057,7 @@ impl MinerService for Miner { ret.map(f) } - fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec) -> Result<(), Error> { + fn submit_seal(&self, chain: &MiningBlockChainClient, block_hash: H256, seal: Vec) -> Result<(), Error> { let result = if let Some(b) = self.sealing_work.lock().queue.get_used_if( if self.options.enable_resubmission { @@ -1050,22 +1065,22 @@ impl MinerService for Miner { } else { GetAction::Take }, - |b| &b.hash() == &pow_hash + |b| &b.hash() == &block_hash ) { - trace!(target: "miner", "Sealing block {}={}={} with seal {:?}", pow_hash, b.hash(), b.header().bare_hash(), seal); + trace!(target: "miner", "Submitted block {}={}={} with seal {:?}", block_hash, b.hash(), b.header().bare_hash(), seal); b.lock().try_seal(&*self.engine, seal).or_else(|(e, _)| { warn!(target: "miner", "Mined solution rejected: {}", e); Err(Error::PowInvalid) }) } else { - warn!(target: "miner", "Mined solution rejected: Block unknown or out of date."); + warn!(target: "miner", "Submitted solution rejected: Block unknown or out of date."); Err(Error::PowHashInvalid) }; result.and_then(|sealed| { let n = sealed.header().number(); let h = sealed.header().hash(); try!(chain.import_sealed_block(sealed)); - info!(target: "miner", "Mined block imported OK. #{}: {}", Colour::White.bold().paint(format!("{}", n)), Colour::White.bold().paint(h.hex())); + info!(target: "miner", "Submitted block imported OK. #{}: {}", Colour::White.bold().paint(format!("{}", n)), Colour::White.bold().paint(h.hex())); Ok(()) }) } diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 1fb2244fd..26cefb295 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -76,6 +76,9 @@ pub trait MinerService : Send + Sync { /// Set the author that we will seal blocks as. fn set_author(&self, author: Address); + /// Set info necessary to sign consensus messages. + fn set_consensus_signer(&self, address: Address, password: String) -> Result<(), ::account_provider::Error>; + /// Get the extra_data that we will seal blocks with. fn extra_data(&self) -> Bytes; diff --git a/parity/run.rs b/parity/run.rs index f977c450c..2509d1347 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -70,7 +70,7 @@ pub struct RunCmd { pub http_conf: HttpConfiguration, pub ipc_conf: IpcConfiguration, pub net_conf: NetworkConfiguration, - pub network_id: Option, + pub network_id: Option, pub warp_sync: bool, pub acc_conf: AccountsConfig, pub gas_pricer: GasPricerConfig, @@ -208,6 +208,9 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { // prepare account provider let account_provider = Arc::new(try!(prepare_account_provider(&cmd.dirs, cmd.acc_conf))); + // let the Engine access the accounts + spec.engine.register_account_provider(account_provider.clone()); + // create miner let miner = Miner::new(cmd.miner_options, cmd.gas_pricer.into(), &spec, Some(account_provider.clone())); miner.set_author(cmd.miner_extras.author); diff --git a/rpc/src/v1/impls/parity_set.rs b/rpc/src/v1/impls/parity_set.rs index 47634d518..e28f9a573 100644 --- a/rpc/src/v1/impls/parity_set.rs +++ b/rpc/src/v1/impls/parity_set.rs @@ -116,6 +116,12 @@ impl ParitySet for ParitySetClient where Ok(true) } + fn set_consensus_signer(&self, address: H160, password: String) -> Result { + try!(self.active()); + try!(take_weak!(self.miner).set_consensus_signer(address.into(), password).map_err(Into::into).map_err(errors::from_password_error)); + Ok(true) + } + fn set_transactions_limit(&self, limit: usize) -> Result { try!(self.active()); diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 7894fa111..3aa83fec9 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -116,6 +116,7 @@ impl EthTester { fn from_spec(spec: Spec) -> Self { let dir = RandomTempPath::new(); let account_provider = account_provider(); + spec.engine.register_account_provider(account_provider.clone()); let miner_service = miner_service(&spec, account_provider.clone()); let snapshot_service = snapshot_service(); diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index ad55faa7b..6037cdd4a 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -25,6 +25,7 @@ use ethcore::header::BlockNumber; use ethcore::transaction::SignedTransaction; use ethcore::receipt::{Receipt, RichReceipt}; use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult, LocalTransactionStatus}; +use ethcore::account_provider::Error as AccountError; /// Test miner service. pub struct TestMinerService { @@ -40,6 +41,8 @@ pub struct TestMinerService { pub pending_receipts: Mutex>, /// Last nonces. pub last_nonces: RwLock>, + /// Password held by Engine. + pub password: RwLock, min_gas_price: RwLock, gas_range_target: RwLock<(U256, U256)>, @@ -61,6 +64,7 @@ impl Default for TestMinerService { min_gas_price: RwLock::new(U256::from(20_000_000)), gas_range_target: RwLock::new((U256::from(12345), U256::from(54321))), author: RwLock::new(Address::zero()), + password: RwLock::new(String::new()), extra_data: RwLock::new(vec![1, 2, 3, 4]), limit: RwLock::new(1024), tx_gas_limit: RwLock::new(!U256::zero()), @@ -83,6 +87,12 @@ impl MinerService for TestMinerService { *self.author.write() = author; } + fn set_consensus_signer(&self, address: Address, password: String) -> Result<(), AccountError> { + *self.author.write() = address; + *self.password.write() = password; + Ok(()) + } + fn set_extra_data(&self, extra_data: Bytes) { *self.extra_data.write() = extra_data; } diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index 24be33417..8800d926a 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -23,7 +23,7 @@ use ethsync::{SyncProvider, SyncStatus, SyncState, PeerInfo, TransactionStats}; /// TestSyncProvider config. pub struct Config { /// Protocol version. - pub network_id: usize, + pub network_id: u64, /// Number of peers. pub num_peers: usize, } diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index 01f33e251..fdf3f2d0f 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -106,6 +106,23 @@ fn rpc_parity_set_author() { assert_eq!(miner.author(), Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); } +#[test] +fn rpc_parity_set_consensus_signer() { + let miner = miner_service(); + let client = client_service(); + let network = network_service(); + let io = IoHandler::new(); + io.add_delegate(parity_set_client(&client, &miner, &network).to_delegate()); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_setConsensusSigner", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681", "password"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(miner.author(), Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); + assert_eq!(*miner.password.read(), "password".to_string()); +} + + #[test] fn rpc_parity_set_transactions_limit() { let miner = miner_service(); diff --git a/rpc/src/v1/traits/parity_set.rs b/rpc/src/v1/traits/parity_set.rs index c83eff022..e196a7d21 100644 --- a/rpc/src/v1/traits/parity_set.rs +++ b/rpc/src/v1/traits/parity_set.rs @@ -44,6 +44,10 @@ build_rpc_trait! { #[rpc(name = "parity_setAuthor")] fn set_author(&self, H160) -> Result; + /// Sets account for signing consensus messages. + #[rpc(name = "parity_setConsensusSigner")] + fn set_consensus_signer(&self, H160, String) -> Result; + /// Sets the limits for transaction queue. #[rpc(name = "parity_setTransactionsLimit")] fn set_transactions_limit(&self, usize) -> Result; From 01bf483b63152b3e5fb1a5cf4ea8285a033cfc6b Mon Sep 17 00:00:00 2001 From: keorn Date: Mon, 5 Dec 2016 17:29:47 +0000 Subject: [PATCH 10/43] remove unnecessary impls --- ethcore/src/engines/authority_round.rs | 4 ---- ethcore/src/engines/basic_authority.rs | 5 ----- ethcore/src/miner/miner.rs | 5 ----- 3 files changed, 14 deletions(-) diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 646c107e0..02993fe83 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -209,10 +209,6 @@ impl Engine for AuthorityRound { }); } - /// 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). - fn on_close_block(&self, _block: &mut ExecutedBlock) {} - fn is_sealer(&self, author: &Address) -> Option { let p = &self.our_params; Some(p.authorities.contains(author)) diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 0b9040a11..264a2d55a 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -100,13 +100,8 @@ impl Engine for BasicAuthority { max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into()) } }); -// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit); } - /// 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). - fn on_close_block(&self, _block: &mut ExecutedBlock) {} - fn is_sealer(&self, author: &Address) -> Option { Some(self.our_params.authorities.contains(author)) } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 4b38a7e07..3b4223c68 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1030,11 +1030,6 @@ impl MinerService for Miner { let (block, original_work_hash) = self.prepare_block(chain); if self.seals_internally { trace!(target: "miner", "update_sealing: engine indicates internal sealing"); - { - let mut sealing_work = self.sealing_work.lock(); - sealing_work.queue.push(block.clone()); - sealing_work.queue.use_last_ref(); - } self.seal_and_import_block_internally(chain, block); } else { trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work"); From ff7b918d82bf8c6bf8a51802794dc022b65cb8fa Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 5 Dec 2016 10:55:53 -0800 Subject: [PATCH 11/43] Fix build. --- ethcore/src/types/transaction.rs | 2 +- rpc/src/v1/types/transaction.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 2cc25a745..1c6ef92e3 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -305,7 +305,7 @@ impl SignedTransaction { pub fn standard_v(&self) -> u8 { match self.v { v if v == 27 || v == 28 || v > 36 => ((v - 1) % 2) as u8, _ => 4 } } /// The `v` value that appears in the RLP. - pub fn original_v(&self) -> u8 { self.v } + pub fn original_v(&self) -> u64 { self.v } /// The network ID, or `None` if this is a global transaction. pub fn network_id(&self) -> Option { diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 7f26adf41..933a4a482 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -59,7 +59,7 @@ pub struct Transaction { pub public_key: Option, /// The network id of the transaction, if any. #[serde(rename="networkId")] - pub network_id: Option, + pub network_id: Option, /// The standardised V field of the signature (0 or 1). #[serde(rename="standardV")] pub standard_v: U256, From 5e7c21ad4aa613038d30715fc5b55f02e50a904b Mon Sep 17 00:00:00 2001 From: keorn Date: Mon, 5 Dec 2016 19:23:03 +0000 Subject: [PATCH 12/43] add a cli flag --- parity/cli/config.full.toml | 1 + parity/cli/config.toml | 1 + parity/cli/mod.rs | 5 +++++ parity/cli/usage.txt | 4 ++++ parity/configuration.rs | 5 +++++ parity/helpers.rs | 4 ++-- parity/params.rs | 2 ++ parity/run.rs | 14 +++++++++----- 8 files changed, 29 insertions(+), 7 deletions(-) diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index fcd9a9712..e2e4aeb5a 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -61,6 +61,7 @@ pass = "test_pass" [mining] author = "0xdeadbeefcafe0000000000000000000000000001" +consensus_signer = "0xdeadbeefcafe0000000000000000000000000001" force_sealing = true reseal_on_txs = "all" reseal_min_period = 4000 diff --git a/parity/cli/config.toml b/parity/cli/config.toml index c9bd563a8..b84c10b22 100644 --- a/parity/cli/config.toml +++ b/parity/cli/config.toml @@ -40,6 +40,7 @@ pass = "password" [mining] author = "0xdeadbeefcafe0000000000000000000000000001" +consensus_signer = "0xdeadbeefcafe0000000000000000000000000001" force_sealing = true reseal_on_txs = "all" reseal_min_period = 4000 diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 7fcdd2209..8939b22e1 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -178,6 +178,8 @@ usage! { // -- Sealing/Mining Options flag_author: Option = None, or |c: &Config| otry!(c.mining).author.clone().map(Some), + flag_engine_signer: Option = None, + or |c: &Config| otry!(c.mining).consensus_signer.clone().map(Some), flag_force_sealing: bool = false, or |c: &Config| otry!(c.mining).force_sealing.clone(), flag_reseal_on_txs: String = "own", @@ -367,6 +369,7 @@ struct Dapps { #[derive(Default, Debug, PartialEq, RustcDecodable)] struct Mining { author: Option, + consensus_signer: Option, force_sealing: Option, reseal_on_txs: Option, reseal_min_period: Option, @@ -569,6 +572,7 @@ mod tests { // -- Sealing/Mining Options flag_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), + flag_engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()), flag_force_sealing: true, flag_reseal_on_txs: "all".into(), flag_reseal_min_period: 4000u64, @@ -738,6 +742,7 @@ mod tests { }), mining: Some(Mining { author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), + consensus_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()), force_sealing: Some(true), reseal_on_txs: Some("all".into()), reseal_min_period: Some(4000), diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index b67af6110..2829f4fe1 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -149,6 +149,10 @@ Sealing/Mining Options: for sending block rewards from sealed blocks. NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION. (default: {flag_author:?}) + --engine-signer ADDRESS Specify the address which should be used to + sign consensus messages and issue blocks. + Relevant only to non-PoW chains. + (default: {flag_engine_signer:?}) --force-sealing Force the node to author new blocks as if it were always sealing/mining. (default: {flag_force_sealing}) diff --git a/parity/configuration.rs b/parity/configuration.rs index 37c699521..0ba084673 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -300,6 +300,7 @@ impl Configuration { gas_floor_target: try!(to_u256(&self.args.flag_gas_floor_target)), gas_ceil_target: try!(to_u256(&self.args.flag_gas_cap)), transactions_limit: self.args.flag_tx_queue_size, + consensus_signer: try!(self.consensus_signer()), }; Ok(extras) @@ -309,6 +310,10 @@ impl Configuration { to_address(self.args.flag_etherbase.clone().or(self.args.flag_author.clone())) } + fn consensus_signer(&self) -> Result { + to_address(self.args.flag_engine_signer.clone()) + } + fn format(&self) -> Result, String> { match self.args.flag_format { Some(ref f) => Ok(Some(try!(f.parse()))), diff --git a/parity/helpers.rs b/parity/helpers.rs index ab0dafddd..654270b64 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -300,14 +300,14 @@ pub fn password_prompt() -> Result { /// Read a password from password file. pub fn password_from_file(path: String) -> Result { - let passwords = try!(passwords_from_files(vec![path])); + let passwords = try!(passwords_from_files(&[path])); // use only first password from the file passwords.get(0).map(String::to_owned) .ok_or_else(|| "Password file seems to be empty.".to_owned()) } /// Reads passwords from files. Treats each line as a separate password. -pub fn passwords_from_files(files: Vec) -> Result, String> { +pub fn passwords_from_files(files: &[String]) -> Result, String> { let passwords = files.iter().map(|filename| { let file = try!(File::open(filename).map_err(|_| format!("{} Unable to read password file. Ensure it exists and permissions are correct.", filename))); let reader = BufReader::new(&file); diff --git a/parity/params.rs b/parity/params.rs index 3ce07e889..97a5e7b08 100644 --- a/parity/params.rs +++ b/parity/params.rs @@ -204,6 +204,7 @@ pub struct MinerExtras { pub gas_floor_target: U256, pub gas_ceil_target: U256, pub transactions_limit: usize, + pub consensus_signer: Address, } impl Default for MinerExtras { @@ -214,6 +215,7 @@ impl Default for MinerExtras { gas_floor_target: U256::from(4_700_000), gas_ceil_target: U256::from(6_283_184), transactions_limit: 1024, + consensus_signer: Default::default(), } } } diff --git a/parity/run.rs b/parity/run.rs index 2509d1347..332f296c1 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -205,8 +205,10 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { sync_config.warp_sync = cmd.warp_sync; sync_config.download_old_blocks = cmd.download_old_blocks; + let passwords = try!(passwords_from_files(&cmd.acc_conf.password_files)); + // prepare account provider - let account_provider = Arc::new(try!(prepare_account_provider(&cmd.dirs, cmd.acc_conf))); + let account_provider = Arc::new(try!(prepare_account_provider(&cmd.dirs, cmd.acc_conf, &passwords))); // let the Engine access the accounts spec.engine.register_account_provider(account_provider.clone()); @@ -218,6 +220,10 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { miner.set_gas_ceil_target(cmd.miner_extras.gas_ceil_target); miner.set_extra_data(cmd.miner_extras.extra_data); miner.set_transactions_limit(cmd.miner_extras.transactions_limit); + let consensus_signer = cmd.miner_extras.consensus_signer; + if passwords.into_iter().any(|p| miner.set_consensus_signer(consensus_signer, p).is_ok()) { + return Err(format!("No password found for the consensus signer {}. Make sure valid password is present in files passed using `--password`.", cmd.miner_extras.consensus_signer)); + } // create client config let client_config = to_client_config( @@ -424,19 +430,17 @@ fn daemonize(_pid_file: String) -> Result<(), String> { Err("daemon is no supported on windows".into()) } -fn prepare_account_provider(dirs: &Directories, cfg: AccountsConfig) -> Result { +fn prepare_account_provider(dirs: &Directories, cfg: AccountsConfig, passwords: &[String]) -> Result { use ethcore::ethstore::EthStore; use ethcore::ethstore::dir::DiskDirectory; - let passwords = try!(passwords_from_files(cfg.password_files)); - let dir = Box::new(try!(DiskDirectory::create(dirs.keys.clone()).map_err(|e| format!("Could not open keys directory: {}", e)))); let account_service = AccountProvider::new(Box::new( try!(EthStore::open_with_iterations(dir, cfg.iterations).map_err(|e| format!("Could not open keys directory: {}", e))) )); for a in cfg.unlocked_accounts { - if passwords.iter().find(|p| account_service.unlock_account_permanently(a, (*p).clone()).is_ok()).is_none() { + if passwords.iter().any(|p| account_service.unlock_account_permanently(a, (*p).clone()).is_ok()) { return Err(format!("No password found to unlock account {}. Make sure valid password is present in files passed using `--password`.", a)); } } From 46dd2543e5b104a58bd4dd2aae32c1c4158aae62 Mon Sep 17 00:00:00 2001 From: keorn Date: Mon, 5 Dec 2016 19:27:24 +0000 Subject: [PATCH 13/43] rename rpc to match cli --- rpc/src/v1/tests/mocked/parity_set.rs | 2 +- rpc/src/v1/traits/parity_set.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index fdf3f2d0f..5c249f08b 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -114,7 +114,7 @@ fn rpc_parity_set_consensus_signer() { let io = IoHandler::new(); io.add_delegate(parity_set_client(&client, &miner, &network).to_delegate()); - let request = r#"{"jsonrpc": "2.0", "method": "parity_setConsensusSigner", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681", "password"], "id": 1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_setEngineSigner", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681", "password"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); diff --git a/rpc/src/v1/traits/parity_set.rs b/rpc/src/v1/traits/parity_set.rs index e196a7d21..c41a88033 100644 --- a/rpc/src/v1/traits/parity_set.rs +++ b/rpc/src/v1/traits/parity_set.rs @@ -45,7 +45,7 @@ build_rpc_trait! { fn set_author(&self, H160) -> Result; /// Sets account for signing consensus messages. - #[rpc(name = "parity_setConsensusSigner")] + #[rpc(name = "parity_setEngineSigner")] fn set_consensus_signer(&self, H160, String) -> Result; /// Sets the limits for transaction queue. From 0a2ec319ac5be557493de6df53f381c09fde6108 Mon Sep 17 00:00:00 2001 From: keorn Date: Mon, 5 Dec 2016 21:31:38 +0000 Subject: [PATCH 14/43] rename to engine_signer --- ethcore/src/miner/miner.rs | 2 +- ethcore/src/miner/mod.rs | 2 +- parity/cli/config.full.toml | 2 +- parity/cli/config.toml | 2 +- parity/cli/mod.rs | 6 +++--- parity/configuration.rs | 4 ++-- parity/params.rs | 4 ++-- parity/run.rs | 6 +++--- rpc/src/v1/impls/parity_set.rs | 4 ++-- rpc/src/v1/tests/helpers/miner_service.rs | 2 +- rpc/src/v1/tests/mocked/parity_set.rs | 2 +- rpc/src/v1/traits/parity_set.rs | 2 +- 12 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 3b4223c68..8d1f55567 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -737,7 +737,7 @@ impl MinerService for Miner { *self.author.write() = author; } - fn set_consensus_signer(&self, address: Address, password: String) -> Result<(), AccountError> { + fn set_engine_signer(&self, address: Address, password: String) -> Result<(), AccountError> { if self.seals_internally { if let Some(ref ap) = self.accounts { try!(ap.sign(address.clone(), Some(password.clone()), Default::default())); diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 26cefb295..8c466581b 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -77,7 +77,7 @@ pub trait MinerService : Send + Sync { fn set_author(&self, author: Address); /// Set info necessary to sign consensus messages. - fn set_consensus_signer(&self, address: Address, password: String) -> Result<(), ::account_provider::Error>; + fn set_engine_signer(&self, address: Address, password: String) -> Result<(), ::account_provider::Error>; /// Get the extra_data that we will seal blocks with. fn extra_data(&self) -> Bytes; diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index e2e4aeb5a..4f3e4fa07 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -61,7 +61,7 @@ pass = "test_pass" [mining] author = "0xdeadbeefcafe0000000000000000000000000001" -consensus_signer = "0xdeadbeefcafe0000000000000000000000000001" +engine_signer = "0xdeadbeefcafe0000000000000000000000000001" force_sealing = true reseal_on_txs = "all" reseal_min_period = 4000 diff --git a/parity/cli/config.toml b/parity/cli/config.toml index b84c10b22..34bab5b46 100644 --- a/parity/cli/config.toml +++ b/parity/cli/config.toml @@ -40,7 +40,7 @@ pass = "password" [mining] author = "0xdeadbeefcafe0000000000000000000000000001" -consensus_signer = "0xdeadbeefcafe0000000000000000000000000001" +engine_signer = "0xdeadbeefcafe0000000000000000000000000001" force_sealing = true reseal_on_txs = "all" reseal_min_period = 4000 diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 8939b22e1..1f6a266b3 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -179,7 +179,7 @@ usage! { flag_author: Option = None, or |c: &Config| otry!(c.mining).author.clone().map(Some), flag_engine_signer: Option = None, - or |c: &Config| otry!(c.mining).consensus_signer.clone().map(Some), + or |c: &Config| otry!(c.mining).engine_signer.clone().map(Some), flag_force_sealing: bool = false, or |c: &Config| otry!(c.mining).force_sealing.clone(), flag_reseal_on_txs: String = "own", @@ -369,7 +369,7 @@ struct Dapps { #[derive(Default, Debug, PartialEq, RustcDecodable)] struct Mining { author: Option, - consensus_signer: Option, + engine_signer: Option, force_sealing: Option, reseal_on_txs: Option, reseal_min_period: Option, @@ -742,7 +742,7 @@ mod tests { }), mining: Some(Mining { author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), - consensus_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()), + engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()), force_sealing: Some(true), reseal_on_txs: Some("all".into()), reseal_min_period: Some(4000), diff --git a/parity/configuration.rs b/parity/configuration.rs index 0ba084673..bcca3aa58 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -300,7 +300,7 @@ impl Configuration { gas_floor_target: try!(to_u256(&self.args.flag_gas_floor_target)), gas_ceil_target: try!(to_u256(&self.args.flag_gas_cap)), transactions_limit: self.args.flag_tx_queue_size, - consensus_signer: try!(self.consensus_signer()), + engine_signer: try!(self.engine_signer()), }; Ok(extras) @@ -310,7 +310,7 @@ impl Configuration { to_address(self.args.flag_etherbase.clone().or(self.args.flag_author.clone())) } - fn consensus_signer(&self) -> Result { + fn engine_signer(&self) -> Result { to_address(self.args.flag_engine_signer.clone()) } diff --git a/parity/params.rs b/parity/params.rs index 97a5e7b08..25ddc8814 100644 --- a/parity/params.rs +++ b/parity/params.rs @@ -204,7 +204,7 @@ pub struct MinerExtras { pub gas_floor_target: U256, pub gas_ceil_target: U256, pub transactions_limit: usize, - pub consensus_signer: Address, + pub engine_signer: Address, } impl Default for MinerExtras { @@ -215,7 +215,7 @@ impl Default for MinerExtras { gas_floor_target: U256::from(4_700_000), gas_ceil_target: U256::from(6_283_184), transactions_limit: 1024, - consensus_signer: Default::default(), + engine_signer: Default::default(), } } } diff --git a/parity/run.rs b/parity/run.rs index 332f296c1..774dd6271 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -220,9 +220,9 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { miner.set_gas_ceil_target(cmd.miner_extras.gas_ceil_target); miner.set_extra_data(cmd.miner_extras.extra_data); miner.set_transactions_limit(cmd.miner_extras.transactions_limit); - let consensus_signer = cmd.miner_extras.consensus_signer; - if passwords.into_iter().any(|p| miner.set_consensus_signer(consensus_signer, p).is_ok()) { - return Err(format!("No password found for the consensus signer {}. Make sure valid password is present in files passed using `--password`.", cmd.miner_extras.consensus_signer)); + let engine_signer = cmd.miner_extras.engine_signer; + if passwords.into_iter().any(|p| miner.set_engine_signer(engine_signer, p).is_ok()) { + return Err(format!("No password found for the consensus signer {}. Make sure valid password is present in files passed using `--password`.", cmd.miner_extras.engine_signer)); } // create client config diff --git a/rpc/src/v1/impls/parity_set.rs b/rpc/src/v1/impls/parity_set.rs index e28f9a573..92de99a1f 100644 --- a/rpc/src/v1/impls/parity_set.rs +++ b/rpc/src/v1/impls/parity_set.rs @@ -116,9 +116,9 @@ impl ParitySet for ParitySetClient where Ok(true) } - fn set_consensus_signer(&self, address: H160, password: String) -> Result { + fn set_engine_signer(&self, address: H160, password: String) -> Result { try!(self.active()); - try!(take_weak!(self.miner).set_consensus_signer(address.into(), password).map_err(Into::into).map_err(errors::from_password_error)); + try!(take_weak!(self.miner).set_engine_signer(address.into(), password).map_err(Into::into).map_err(errors::from_password_error)); Ok(true) } diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 6037cdd4a..39fba8406 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -87,7 +87,7 @@ impl MinerService for TestMinerService { *self.author.write() = author; } - fn set_consensus_signer(&self, address: Address, password: String) -> Result<(), AccountError> { + fn set_engine_signer(&self, address: Address, password: String) -> Result<(), AccountError> { *self.author.write() = address; *self.password.write() = password; Ok(()) diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index 5c249f08b..55f155693 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -107,7 +107,7 @@ fn rpc_parity_set_author() { } #[test] -fn rpc_parity_set_consensus_signer() { +fn rpc_parity_set_engine_signer() { let miner = miner_service(); let client = client_service(); let network = network_service(); diff --git a/rpc/src/v1/traits/parity_set.rs b/rpc/src/v1/traits/parity_set.rs index c41a88033..c40abd01f 100644 --- a/rpc/src/v1/traits/parity_set.rs +++ b/rpc/src/v1/traits/parity_set.rs @@ -46,7 +46,7 @@ build_rpc_trait! { /// Sets account for signing consensus messages. #[rpc(name = "parity_setEngineSigner")] - fn set_consensus_signer(&self, H160, String) -> Result; + fn set_engine_signer(&self, H160, String) -> Result; /// Sets the limits for transaction queue. #[rpc(name = "parity_setTransactionsLimit")] From b089aa7a6b47d955a0d66503c98f239b6eb3dc62 Mon Sep 17 00:00:00 2001 From: keorn Date: Tue, 6 Dec 2016 08:38:41 +0000 Subject: [PATCH 15/43] fix password loading --- parity/run.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parity/run.rs b/parity/run.rs index 774dd6271..cd2392ae6 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -221,7 +221,7 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { miner.set_extra_data(cmd.miner_extras.extra_data); miner.set_transactions_limit(cmd.miner_extras.transactions_limit); let engine_signer = cmd.miner_extras.engine_signer; - if passwords.into_iter().any(|p| miner.set_engine_signer(engine_signer, p).is_ok()) { + if !passwords.into_iter().any(|p| miner.set_engine_signer(engine_signer, p).is_ok()) { return Err(format!("No password found for the consensus signer {}. Make sure valid password is present in files passed using `--password`.", cmd.miner_extras.engine_signer)); } @@ -440,7 +440,7 @@ fn prepare_account_provider(dirs: &Directories, cfg: AccountsConfig, passwords: )); for a in cfg.unlocked_accounts { - if passwords.iter().any(|p| account_service.unlock_account_permanently(a, (*p).clone()).is_ok()) { + if !passwords.iter().any(|p| account_service.unlock_account_permanently(a, (*p).clone()).is_ok()) { return Err(format!("No password found to unlock account {}. Make sure valid password is present in files passed using `--password`.", a)); } } From e2bb8ef6d18611118fbefffa5958a46b30079d69 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 7 Dec 2016 10:25:57 +0100 Subject: [PATCH 16/43] Unify proptypes in util/proptypes.js (#3728) * Unify proptypes in util/proptypes.js * Add missing use of nodeOrStringProptype --- .../dapps/registry/Application/application.js | 9 ++++---- js/src/dapps/registry/Container.js | 10 ++++----- js/src/dapps/registry/Lookup/lookup.js | 7 +++--- .../SMSVerification/GatherData/gatherData.js | 6 ++--- .../SendConfirmation/sendConfirmation.js | 4 ++-- .../SendRequest/sendRequest.js | 4 ++-- js/src/modals/Transfer/Details/details.js | 3 +-- js/src/modals/Transfer/transfer.js | 2 +- js/src/ui/ConfirmDialog/confirmDialog.js | 22 ++++++++++++++++--- js/src/ui/Container/Title/title.js | 10 ++++----- js/src/ui/Container/container.js | 6 ++--- js/src/ui/Form/InputInline/inputInline.js | 6 ++--- js/src/ui/Modal/Title/title.js | 6 ++--- js/src/ui/Modal/modal.js | 6 ++--- .../{nullable-proptype.js => proptypes.js} | 14 ++++++++++-- 15 files changed, 70 insertions(+), 45 deletions(-) rename js/src/util/{nullable-proptype.js => proptypes.js} (75%) diff --git a/js/src/dapps/registry/Application/application.js b/js/src/dapps/registry/Application/application.js index 5102e5d57..45290937e 100644 --- a/js/src/dapps/registry/Application/application.js +++ b/js/src/dapps/registry/Application/application.js @@ -21,6 +21,9 @@ const muiTheme = getMuiTheme(lightBaseTheme); import CircularProgress from 'material-ui/CircularProgress'; import { Card, CardText } from 'material-ui/Card'; + +import { nullableProptype } from '~/util/proptypes'; + import styles from './application.css'; import Accounts from '../Accounts'; import Events from '../Events'; @@ -28,8 +31,6 @@ import Lookup from '../Lookup'; import Names from '../Names'; import Records from '../Records'; -const nullable = (type) => React.PropTypes.oneOfType([ React.PropTypes.oneOf([ null ]), type ]); - export default class Application extends Component { static childContextTypes = { muiTheme: PropTypes.object.isRequired, @@ -44,8 +45,8 @@ export default class Application extends Component { actions: PropTypes.object.isRequired, accounts: PropTypes.object.isRequired, contacts: PropTypes.object.isRequired, - contract: nullable(PropTypes.object.isRequired), - fee: nullable(PropTypes.object.isRequired), + contract: nullableProptype(PropTypes.object.isRequired), + fee: nullableProptype(PropTypes.object.isRequired), lookup: PropTypes.object.isRequired, events: PropTypes.object.isRequired, names: PropTypes.object.isRequired, diff --git a/js/src/dapps/registry/Container.js b/js/src/dapps/registry/Container.js index 1464320e6..e379038fa 100644 --- a/js/src/dapps/registry/Container.js +++ b/js/src/dapps/registry/Container.js @@ -18,19 +18,19 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { nullableProptype } from '~/util/proptypes'; + import Application from './Application'; import * as actions from './actions'; -const nullable = (type) => React.PropTypes.oneOfType([ React.PropTypes.oneOf([ null ]), type ]); - class Container extends Component { static propTypes = { actions: PropTypes.object.isRequired, accounts: PropTypes.object.isRequired, contacts: PropTypes.object.isRequired, - contract: nullable(PropTypes.object.isRequired), - owner: nullable(PropTypes.string.isRequired), - fee: nullable(PropTypes.object.isRequired), + contract: nullableProptype(PropTypes.object.isRequired), + owner: nullableProptype(PropTypes.string.isRequired), + fee: nullableProptype(PropTypes.object.isRequired), lookup: PropTypes.object.isRequired, events: PropTypes.object.isRequired }; diff --git a/js/src/dapps/registry/Lookup/lookup.js b/js/src/dapps/registry/Lookup/lookup.js index 436d113b9..b8b6207cc 100644 --- a/js/src/dapps/registry/Lookup/lookup.js +++ b/js/src/dapps/registry/Lookup/lookup.js @@ -19,21 +19,22 @@ import { Card, CardHeader, CardText } from 'material-ui/Card'; import TextField from 'material-ui/TextField'; import RaisedButton from 'material-ui/RaisedButton'; import SearchIcon from 'material-ui/svg-icons/action/search'; + +import { nullableProptype } from '~/util/proptypes'; + import renderAddress from '../ui/address.js'; import renderImage from '../ui/image.js'; import recordTypeSelect from '../ui/record-type-select.js'; import styles from './lookup.css'; -const nullable = (type) => React.PropTypes.oneOfType([ React.PropTypes.oneOf([ null ]), type ]); - export default class Lookup extends Component { static propTypes = { actions: PropTypes.object.isRequired, name: PropTypes.string.isRequired, type: PropTypes.string.isRequired, - result: nullable(PropTypes.string.isRequired), + result: nullableProptype(PropTypes.string.isRequired), accounts: PropTypes.object.isRequired, contacts: PropTypes.object.isRequired } diff --git a/js/src/modals/SMSVerification/GatherData/gatherData.js b/js/src/modals/SMSVerification/GatherData/gatherData.js index 87213b1fc..f5f09578e 100644 --- a/js/src/modals/SMSVerification/GatherData/gatherData.js +++ b/js/src/modals/SMSVerification/GatherData/gatherData.js @@ -15,7 +15,6 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; -import nullable from '../../../util/nullable-proptype'; import BigNumber from 'bignumber.js'; import { Checkbox } from 'material-ui'; import InfoIcon from 'material-ui/svg-icons/action/info-outline'; @@ -24,6 +23,7 @@ import ErrorIcon from 'material-ui/svg-icons/navigation/close'; import { fromWei } from '~/api/util/wei'; import { Form, Input } from '~/ui'; +import { nullableProptype } from '~/util/proptypes'; import { termsOfService } from '../../../3rdparty/sms-verification'; import styles from './gatherData.css'; @@ -32,8 +32,8 @@ export default class GatherData extends Component { static propTypes = { fee: React.PropTypes.instanceOf(BigNumber), isNumberValid: PropTypes.bool.isRequired, - isVerified: nullable(PropTypes.bool.isRequired), - hasRequested: nullable(PropTypes.bool.isRequired), + isVerified: nullableProptype(PropTypes.bool.isRequired), + hasRequested: nullableProptype(PropTypes.bool.isRequired), setNumber: PropTypes.func.isRequired, setConsentGiven: PropTypes.func.isRequired } diff --git a/js/src/modals/SMSVerification/SendConfirmation/sendConfirmation.js b/js/src/modals/SMSVerification/SendConfirmation/sendConfirmation.js index 50c3aa17a..a0cc13689 100644 --- a/js/src/modals/SMSVerification/SendConfirmation/sendConfirmation.js +++ b/js/src/modals/SMSVerification/SendConfirmation/sendConfirmation.js @@ -15,8 +15,8 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; -import nullable from '../../../util/nullable-proptype'; +import { nullableProptype } from '~/util/proptypes'; import TxHash from '~/ui/TxHash'; import { POSTING_CONFIRMATION, POSTED_CONFIRMATION @@ -27,7 +27,7 @@ import styles from './sendConfirmation.css'; export default class SendConfirmation extends Component { static propTypes = { step: PropTypes.any.isRequired, - tx: nullable(PropTypes.any.isRequired) + tx: nullableProptype(PropTypes.any.isRequired) } render () { diff --git a/js/src/modals/SMSVerification/SendRequest/sendRequest.js b/js/src/modals/SMSVerification/SendRequest/sendRequest.js index 9ba24f0e8..933de9265 100644 --- a/js/src/modals/SMSVerification/SendRequest/sendRequest.js +++ b/js/src/modals/SMSVerification/SendRequest/sendRequest.js @@ -15,8 +15,8 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; -import nullable from '../../../util/nullable-proptype'; +import { nullableProptype } from '~/util/proptypes'; import TxHash from '~/ui/TxHash'; import { POSTING_REQUEST, POSTED_REQUEST, REQUESTING_SMS @@ -27,7 +27,7 @@ import styles from './sendRequest.css'; export default class SendRequest extends Component { static propTypes = { step: PropTypes.any.isRequired, - tx: nullable(PropTypes.any.isRequired) + tx: nullableProptype(PropTypes.any.isRequired) } render () { diff --git a/js/src/modals/Transfer/Details/details.js b/js/src/modals/Transfer/Details/details.js index b14d15add..ad2abf9b0 100644 --- a/js/src/modals/Transfer/Details/details.js +++ b/js/src/modals/Transfer/Details/details.js @@ -17,11 +17,10 @@ import BigNumber from 'bignumber.js'; import React, { Component, PropTypes } from 'react'; import { Checkbox, MenuItem } from 'material-ui'; - import { isEqual } from 'lodash'; import Form, { Input, InputAddressSelect, AddressSelect, Select } from '~/ui/Form'; -import nullableProptype from '~/util/nullable-proptype'; +import { nullableProptype } from '~/util/proptypes'; import imageUnknown from '../../../../assets/images/contracts/unknown-64x64.png'; import styles from '../transfer.css'; diff --git a/js/src/modals/Transfer/transfer.js b/js/src/modals/Transfer/transfer.js index 42a68e37e..54e712c12 100644 --- a/js/src/modals/Transfer/transfer.js +++ b/js/src/modals/Transfer/transfer.js @@ -26,7 +26,7 @@ import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forwa import { newError } from '~/ui/Errors/actions'; import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '~/ui'; -import nullableProptype from '~/util/nullable-proptype'; +import { nullableProptype } from '~/util/proptypes'; import Details from './Details'; import Extras from './Extras'; diff --git a/js/src/ui/ConfirmDialog/confirmDialog.js b/js/src/ui/ConfirmDialog/confirmDialog.js index 7a0be650a..31c4024bc 100644 --- a/js/src/ui/ConfirmDialog/confirmDialog.js +++ b/js/src/ui/ConfirmDialog/confirmDialog.js @@ -1,7 +1,25 @@ +// 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 . + import React, { Component, PropTypes } from 'react'; import ActionDone from 'material-ui/svg-icons/action/done'; import ContentClear from 'material-ui/svg-icons/content/clear'; +import { nodeOrStringProptype } from '~/util/proptypes'; + import Button from '../Button'; import Modal from '../Modal'; @@ -15,9 +33,7 @@ export default class ConfirmDialog extends Component { iconDeny: PropTypes.node, labelConfirm: PropTypes.string, labelDeny: PropTypes.string, - title: PropTypes.oneOfType([ - PropTypes.node, PropTypes.string - ]).isRequired, + title: nodeOrStringProptype.isRequired, visible: PropTypes.bool.isRequired, onConfirm: PropTypes.func.isRequired, onDeny: PropTypes.func.isRequired diff --git a/js/src/ui/Container/Title/title.js b/js/src/ui/Container/Title/title.js index 794653130..127db24f4 100644 --- a/js/src/ui/Container/Title/title.js +++ b/js/src/ui/Container/Title/title.js @@ -16,17 +16,15 @@ import React, { Component, PropTypes } from 'react'; +import { nodeOrStringProptype } from '~/util/proptypes'; + import styles from './title.css'; export default class Title extends Component { static propTypes = { className: PropTypes.string, - title: PropTypes.oneOfType([ - PropTypes.string, PropTypes.node - ]), - byline: PropTypes.oneOfType([ - PropTypes.string, PropTypes.node - ]) + title: nodeOrStringProptype, + byline: nodeOrStringProptype } state = { diff --git a/js/src/ui/Container/container.js b/js/src/ui/Container/container.js index 55b0b838e..fd4d608fe 100644 --- a/js/src/ui/Container/container.js +++ b/js/src/ui/Container/container.js @@ -17,6 +17,8 @@ import React, { Component, PropTypes } from 'react'; import { Card } from 'material-ui/Card'; +import { nodeOrStringProptype } from '~/util/proptypes'; + import Title from './Title'; import styles from './container.css'; @@ -28,9 +30,7 @@ export default class Container extends Component { compact: PropTypes.bool, light: PropTypes.bool, style: PropTypes.object, - title: PropTypes.oneOfType([ - PropTypes.string, PropTypes.node - ]) + title: nodeOrStringProptype } render () { diff --git a/js/src/ui/Form/InputInline/inputInline.js b/js/src/ui/Form/InputInline/inputInline.js index bdf5102f0..d27b8138d 100644 --- a/js/src/ui/Form/InputInline/inputInline.js +++ b/js/src/ui/Form/InputInline/inputInline.js @@ -16,6 +16,8 @@ import React, { Component, PropTypes } from 'react'; +import { nodeOrStringProptype } from '~/util/proptypes'; + import Input from '../Input'; import styles from './inputInline.css'; @@ -33,9 +35,7 @@ export default class InputInline extends Component { value: PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]), - static: PropTypes.oneOfType([ - PropTypes.node, PropTypes.string - ]) + static: nodeOrStringProptype } state = { diff --git a/js/src/ui/Modal/Title/title.js b/js/src/ui/Modal/Title/title.js index 58db8487a..03b431df6 100644 --- a/js/src/ui/Modal/Title/title.js +++ b/js/src/ui/Modal/Title/title.js @@ -18,6 +18,8 @@ import React, { Component, PropTypes } from 'react'; import { LinearProgress } from 'material-ui'; import { Step, Stepper, StepLabel } from 'material-ui/Stepper'; +import { nodeOrStringProptype } from '~/util/proptypes'; + import styles from '../modal.css'; export default class Title extends Component { @@ -26,9 +28,7 @@ export default class Title extends Component { current: PropTypes.number, steps: PropTypes.array, waiting: PropTypes.array, - title: React.PropTypes.oneOfType([ - PropTypes.node, PropTypes.string - ]) + title: nodeOrStringProptype } render () { diff --git a/js/src/ui/Modal/modal.js b/js/src/ui/Modal/modal.js index 4e036a1e1..66f94d806 100644 --- a/js/src/ui/Modal/modal.js +++ b/js/src/ui/Modal/modal.js @@ -19,6 +19,8 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { Dialog } from 'material-ui'; +import { nodeOrStringProptype } from '~/util/proptypes'; + import Container from '../Container'; import Title from './Title'; @@ -42,9 +44,7 @@ class Modal extends Component { current: PropTypes.number, waiting: PropTypes.array, steps: PropTypes.array, - title: PropTypes.oneOfType([ - PropTypes.node, PropTypes.string - ]), + title: nodeOrStringProptype, visible: PropTypes.bool.isRequired, settings: PropTypes.object.isRequired } diff --git a/js/src/util/nullable-proptype.js b/js/src/util/proptypes.js similarity index 75% rename from js/src/util/nullable-proptype.js rename to js/src/util/proptypes.js index 78c486cb6..f322c629c 100644 --- a/js/src/util/nullable-proptype.js +++ b/js/src/util/proptypes.js @@ -16,6 +16,16 @@ import { PropTypes } from 'react'; -export default function nullableProptype (type) { - return PropTypes.oneOfType([ PropTypes.oneOf([ null ]), type ]); +export function nullableProptype (type) { + return PropTypes.oneOfType([ + PropTypes.oneOf([ null ]), + type + ]); +} + +export function nodeOrStringProptype () { + return PropTypes.oneOfType([ + PropTypes.node, + PropTypes.string + ]); } From 71a96588b0c48436af549efa3cf891a67e79512a Mon Sep 17 00:00:00 2001 From: keorn Date: Wed, 7 Dec 2016 10:34:06 +0100 Subject: [PATCH 17/43] add the methods to engines --- ethcore/src/engines/authority_round.rs | 13 ++++++++++--- ethcore/src/engines/basic_authority.rs | 10 ++++++++-- parity/run.rs | 6 ++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 02993fe83..a478df6bb 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -73,14 +73,15 @@ pub struct AuthorityRound { step: AtomicUsize, proposed: AtomicBool, account_provider: Mutex>>, + password: RwLock>, } fn header_step(header: &Header) -> Result { - UntrustedRlp::new(&header.seal()[0]).as_val() + UntrustedRlp::new(&header.seal().get(0).expect("was either checked with verify_block_basic or is genesis; has 2 fields; qed (Make sure the spec file has a correct genesis seal)")).as_val() } fn header_signature(header: &Header) -> Result { - UntrustedRlp::new(&header.seal()[1]).as_val::().map(Into::into) + UntrustedRlp::new(&header.seal().get(1).expect("was checked with verify_block_basic; has 2 fields; qed")).as_val::().map(Into::into) } trait AsMillis { @@ -107,6 +108,7 @@ impl AuthorityRound { step: AtomicUsize::new(initial_step), proposed: AtomicBool::new(false), account_provider: Mutex::new(None), + password: RwLock::new(None), }); let handler = TransitionHandler { engine: Arc::downgrade(&engine) }; try!(engine.transition_service.register_handler(Arc::new(handler))); @@ -198,6 +200,7 @@ impl Engine for AuthorityRound { } fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { + header.set_difficulty(parent.difficulty().clone()); header.set_gas_limit({ let gas_limit = parent.gas_limit().clone(); let bound_divisor = self.our_params.gas_limit_bound_divisor; @@ -225,7 +228,7 @@ impl Engine for AuthorityRound { if self.is_step_proposer(step, header.author()) { if let Some(ref ap) = *self.account_provider.lock() { // Account should be permanently unlocked, otherwise sealing will fail. - if let Ok(signature) = ap.sign(*header.author(), None, header.bare_hash()) { + if let Ok(signature) = ap.sign(*header.author(), self.password.read().clone(), header.bare_hash()) { trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step); self.proposed.store(true, AtomicOrdering::SeqCst); return Some(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); @@ -308,6 +311,10 @@ impl Engine for AuthorityRound { *self.message_channel.lock() = Some(message_channel); } + fn set_signer(&self, _address: Address, password: String) { + *self.password.write() = Some(password); + } + fn register_account_provider(&self, account_provider: Arc) { *self.account_provider.lock() = Some(account_provider); } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 264a2d55a..4db8357e6 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -59,6 +59,7 @@ pub struct BasicAuthority { our_params: BasicAuthorityParams, builtins: BTreeMap, account_provider: Mutex>>, + password: RwLock>, } impl BasicAuthority { @@ -68,7 +69,8 @@ impl BasicAuthority { params: params, our_params: our_params, builtins: builtins, - account_provider: Mutex::new(None) + account_provider: Mutex::new(None), + password: RwLock::new(None), } } } @@ -115,7 +117,7 @@ impl Engine for BasicAuthority { let header = block.header(); let message = header.bare_hash(); // account should be pernamently unlocked, otherwise sealing will fail - if let Ok(signature) = ap.sign(*block.header().author(), None, message) { + if let Ok(signature) = ap.sign(*block.header().author(), self.password.read().clone(), message) { return Some(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]); } else { trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable"); @@ -177,6 +179,10 @@ impl Engine for BasicAuthority { t.sender().map(|_|()) // Perform EC recovery and cache sender } + fn set_signer(&self, _address: Address, password: String) { + *self.password.write() = Some(password); + } + fn register_account_provider(&self, ap: Arc) { *self.account_provider.lock() = Some(ap); } diff --git a/parity/run.rs b/parity/run.rs index cd2392ae6..d535b63ee 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -221,8 +221,10 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { miner.set_extra_data(cmd.miner_extras.extra_data); miner.set_transactions_limit(cmd.miner_extras.transactions_limit); let engine_signer = cmd.miner_extras.engine_signer; - if !passwords.into_iter().any(|p| miner.set_engine_signer(engine_signer, p).is_ok()) { - return Err(format!("No password found for the consensus signer {}. Make sure valid password is present in files passed using `--password`.", cmd.miner_extras.engine_signer)); + if engine_signer != Default::default() { + if !passwords.into_iter().any(|p| miner.set_engine_signer(engine_signer, p).is_ok()) { + return Err(format!("No password found for the consensus signer {}. Make sure valid password is present in files passed using `--password`.", engine_signer)); + } } // create client config From be90245ecb68894f0ed11c31bbef77bac64b7fcf Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 7 Dec 2016 10:46:46 +0100 Subject: [PATCH 18/43] PropTypes as function call (#3731) --- js/src/ui/ConfirmDialog/confirmDialog.js | 2 +- js/src/ui/Container/Title/title.js | 4 ++-- js/src/ui/Container/container.js | 2 +- js/src/ui/Form/InputInline/inputInline.js | 2 +- js/src/ui/Modal/Title/title.js | 2 +- js/src/ui/Modal/modal.js | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/js/src/ui/ConfirmDialog/confirmDialog.js b/js/src/ui/ConfirmDialog/confirmDialog.js index 31c4024bc..f1b8c6902 100644 --- a/js/src/ui/ConfirmDialog/confirmDialog.js +++ b/js/src/ui/ConfirmDialog/confirmDialog.js @@ -33,7 +33,7 @@ export default class ConfirmDialog extends Component { iconDeny: PropTypes.node, labelConfirm: PropTypes.string, labelDeny: PropTypes.string, - title: nodeOrStringProptype.isRequired, + title: nodeOrStringProptype().isRequired, visible: PropTypes.bool.isRequired, onConfirm: PropTypes.func.isRequired, onDeny: PropTypes.func.isRequired diff --git a/js/src/ui/Container/Title/title.js b/js/src/ui/Container/Title/title.js index 127db24f4..485340d2a 100644 --- a/js/src/ui/Container/Title/title.js +++ b/js/src/ui/Container/Title/title.js @@ -23,8 +23,8 @@ import styles from './title.css'; export default class Title extends Component { static propTypes = { className: PropTypes.string, - title: nodeOrStringProptype, - byline: nodeOrStringProptype + title: nodeOrStringProptype(), + byline: nodeOrStringProptype() } state = { diff --git a/js/src/ui/Container/container.js b/js/src/ui/Container/container.js index fd4d608fe..8d30a3963 100644 --- a/js/src/ui/Container/container.js +++ b/js/src/ui/Container/container.js @@ -30,7 +30,7 @@ export default class Container extends Component { compact: PropTypes.bool, light: PropTypes.bool, style: PropTypes.object, - title: nodeOrStringProptype + title: nodeOrStringProptype() } render () { diff --git a/js/src/ui/Form/InputInline/inputInline.js b/js/src/ui/Form/InputInline/inputInline.js index d27b8138d..92a54e2a3 100644 --- a/js/src/ui/Form/InputInline/inputInline.js +++ b/js/src/ui/Form/InputInline/inputInline.js @@ -35,7 +35,7 @@ export default class InputInline extends Component { value: PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]), - static: nodeOrStringProptype + static: nodeOrStringProptype() } state = { diff --git a/js/src/ui/Modal/Title/title.js b/js/src/ui/Modal/Title/title.js index 03b431df6..b483af061 100644 --- a/js/src/ui/Modal/Title/title.js +++ b/js/src/ui/Modal/Title/title.js @@ -28,7 +28,7 @@ export default class Title extends Component { current: PropTypes.number, steps: PropTypes.array, waiting: PropTypes.array, - title: nodeOrStringProptype + title: nodeOrStringProptype() } render () { diff --git a/js/src/ui/Modal/modal.js b/js/src/ui/Modal/modal.js index 66f94d806..522684140 100644 --- a/js/src/ui/Modal/modal.js +++ b/js/src/ui/Modal/modal.js @@ -44,7 +44,7 @@ class Modal extends Component { current: PropTypes.number, waiting: PropTypes.array, steps: PropTypes.array, - title: nodeOrStringProptype, + title: nodeOrStringProptype(), visible: PropTypes.bool.isRequired, settings: PropTypes.object.isRequired } From 3ae09de0190ededd318fc50e35d2974f85400bc9 Mon Sep 17 00:00:00 2001 From: keorn Date: Wed, 7 Dec 2016 11:40:46 +0100 Subject: [PATCH 19/43] restore is_new_best --- ethcore/src/engines/authority_round.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index a478df6bb..c78193e3f 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -304,7 +304,16 @@ impl Engine for AuthorityRound { } fn is_new_best_block(&self, _best_total_difficulty: U256, best_header: HeaderView, _parent_details: &BlockDetails, new_header: &HeaderView) -> bool { - new_header.number() > best_header.number() + let new_number = new_header.number(); + let best_number = best_header.number(); + if new_number != best_number { + new_number > best_number + } else { + // Take the oldest step at given height. + let new_step: usize = Rlp::new(&new_header.seal()[0]).as_val(); + let best_step: usize = Rlp::new(&best_header.seal()[0]).as_val(); + new_step < best_step + } } fn register_message_channel(&self, message_channel: IoChannel) { From 21a3cf7b6586d3c14cc8de8dcc5a0e92b65c1d39 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 7 Dec 2016 11:44:45 +0100 Subject: [PATCH 20/43] Swap order of addressbook & acocunts merge --- rpc/src/v1/impls/parity_accounts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index ea927de4e..2631bc3ce 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -57,7 +57,7 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock let info = try!(store.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))); let other = store.addresses_info().expect("addresses_info always returns Ok; qed"); - Ok(info.into_iter().chain(other.into_iter()).map(|(a, v)| { + Ok(other.into_iter().chain(info.into_iter()).map(|(a, v)| { let m = map![ "name".to_owned() => to_value(&v.name), "meta".to_owned() => to_value(&v.meta), From f7586109df8ed3cc1c20bd9774a2c4359ed1a903 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 7 Dec 2016 11:48:08 +0100 Subject: [PATCH 21/43] Ignore meta.deleted for accounts --- js/src/redux/providers/personalActions.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/js/src/redux/providers/personalActions.js b/js/src/redux/providers/personalActions.js index a1e9845db..7b51683b0 100644 --- a/js/src/redux/providers/personalActions.js +++ b/js/src/redux/providers/personalActions.js @@ -27,17 +27,18 @@ export function personalAccountsInfo (accountsInfo) { Object.keys(accountsInfo || {}) .map((address) => Object.assign({}, accountsInfo[address], { address })) - .filter((account) => !account.meta.deleted) .forEach((account) => { if (account.uuid) { accounts[account.address] = account; - } else if (account.meta.wallet) { - account.wallet = true; - wallets[account.address] = account; - } else if (account.meta.contract) { - contracts[account.address] = account; - } else { - contacts[account.address] = account; + } else if (!account.meta.deleted) { + if (account.meta.wallet) { + account.wallet = true; + wallets[account.address] = account; + } else if (account.meta.contract) { + contracts[account.address] = account; + } else { + contacts[account.address] = account; + } } }); From 5bdb6e4f22a9ab6d47b3a7dbcebccd1f4b0bac5b Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 7 Dec 2016 11:51:48 +0100 Subject: [PATCH 22/43] Ignore meta.deleted for account name display --- js/src/redux/providers/personalActions.js | 17 ++++++++--------- js/src/ui/IdentityName/identityName.js | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/js/src/redux/providers/personalActions.js b/js/src/redux/providers/personalActions.js index 7b51683b0..47056af2f 100644 --- a/js/src/redux/providers/personalActions.js +++ b/js/src/redux/providers/personalActions.js @@ -27,18 +27,17 @@ export function personalAccountsInfo (accountsInfo) { Object.keys(accountsInfo || {}) .map((address) => Object.assign({}, accountsInfo[address], { address })) + .filter((account) => account.uuid || !account.meta.deleted) .forEach((account) => { if (account.uuid) { accounts[account.address] = account; - } else if (!account.meta.deleted) { - if (account.meta.wallet) { - account.wallet = true; - wallets[account.address] = account; - } else if (account.meta.contract) { - contracts[account.address] = account; - } else { - contacts[account.address] = account; - } + } else if (account.meta.wallet) { + account.wallet = true; + wallets[account.address] = account; + } else if (account.meta.contract) { + contracts[account.address] = account; + } else { + contacts[account.address] = account; } }); diff --git a/js/src/ui/IdentityName/identityName.js b/js/src/ui/IdentityName/identityName.js index 85ff34a35..8f95ce116 100644 --- a/js/src/ui/IdentityName/identityName.js +++ b/js/src/ui/IdentityName/identityName.js @@ -37,7 +37,7 @@ class IdentityName extends Component { render () { const { address, accountsInfo, tokens, empty, name, shorten, unknown, className } = this.props; const account = accountsInfo[address] || tokens[address]; - const hasAccount = account && (!account.meta || !account.meta.deleted); + const hasAccount = account && (account.uuid || !account.meta || !account.meta.deleted); if (!hasAccount && empty) { return null; From 8dbd56888d6627b9434c86bd601b616f6aa3c091 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 7 Dec 2016 12:47:44 +0100 Subject: [PATCH 23/43] Add functionalities to multi-sig wallet (#3729) * WIP Sending tokens in multi-sig wallet * Working Token transfer for multi-sig wallet #3282 * Add operation hash to transfer modal * Add existing wallet from address #3282 * Wallet delete redirect to Wallets/Accounts #3282 * Rightly check balance in Transfer // Get all accounts balances #3282 * Fix linting * Better Header UI for Wallet * Use the `~` webpack alias * Use Webpack `~` alias --- js/src/api/contract/contract.js | 18 +- js/src/dapps/registry/Records/records.js | 16 ++ .../WalletDetails/walletDetails.js | 89 +++++++--- .../CreateWallet/WalletInfo/walletInfo.js | 18 +- .../modals/CreateWallet/WalletType/index.js | 17 ++ .../CreateWallet/WalletType/walletType.js | 58 +++++++ js/src/modals/CreateWallet/createWallet.css | 19 +++ js/src/modals/CreateWallet/createWallet.js | 52 ++++-- .../modals/CreateWallet/createWalletStore.js | 157 +++++++++++++----- js/src/modals/DeleteAccount/deleteAccount.js | 2 +- js/src/modals/Transfer/Details/details.js | 6 +- js/src/modals/Transfer/store.js | 136 ++++++++++++--- js/src/modals/Transfer/transfer.js | 30 +++- js/src/redux/providers/balancesActions.js | 8 +- js/src/redux/providers/walletActions.js | 102 +++--------- js/src/ui/Form/AutoComplete/autocomplete.js | 4 +- js/src/ui/Form/Input/input.js | 11 +- js/src/ui/Form/RadioButtons/radioButtons.js | 5 +- js/src/ui/Form/TypedInput/typedInput.js | 30 +++- js/src/util/wallets.js | 107 ++++++++++++ js/src/views/Account/Header/header.js | 18 +- js/src/views/Accounts/accounts.js | 50 +++--- js/src/views/Address/Delete/delete.js | 9 +- js/src/views/Contract/contract.js | 2 +- js/src/views/Dapps/dappsStore.js | 2 +- .../TransactionMainDetails.js | 2 +- .../TransactionPending/TransactionPending.js | 3 +- js/src/views/Status/actions/clipboard.js | 2 +- js/src/views/Status/actions/logger.js | 2 +- js/src/views/Status/actions/rpc.js | 2 +- .../containers/StatusPage/StatusPage.js | 2 +- .../Wallet/Confirmations/confirmations.js | 10 +- js/src/views/Wallet/Details/details.js | 35 ++-- .../views/Wallet/Transactions/transactions.js | 8 +- js/src/views/Wallet/wallet.css | 33 +++- js/src/views/Wallet/wallet.js | 114 ++++++++++--- 36 files changed, 857 insertions(+), 322 deletions(-) create mode 100644 js/src/modals/CreateWallet/WalletType/index.js create mode 100644 js/src/modals/CreateWallet/WalletType/walletType.js create mode 100644 js/src/util/wallets.js diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index d9ec9b03e..ed922a02c 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -189,15 +189,21 @@ export default class Contract { }); } - _encodeOptions (func, options, values) { + getCallData = (func, options, values) => { + let data = options.data; + const tokens = func ? this._abi.encodeTokens(func.inputParamTypes(), values) : null; const call = tokens ? func.encodeCall(tokens) : null; - if (options.data && options.data.substr(0, 2) === '0x') { - options.data = options.data.substr(2); + if (data && data.substr(0, 2) === '0x') { + data = data.substr(2); } - options.data = `0x${options.data || ''}${call || ''}`; + return `0x${data || ''}${call || ''}`; + } + + _encodeOptions (func, options, values) { + options.data = this.getCallData(func, options, values); return options; } @@ -209,10 +215,10 @@ export default class Contract { _bindFunction = (func) => { func.call = (options, values = []) => { - const callData = this._encodeOptions(func, this._addOptionsTo(options), values); + const callParams = this._encodeOptions(func, this._addOptionsTo(options), values); return this._api.eth - .call(callData) + .call(callParams) .then((encoded) => func.decodeOutput(encoded)) .then((tokens) => tokens.map((token) => token.value)) .then((returns) => returns.length === 1 ? returns[0] : returns); diff --git a/js/src/dapps/registry/Records/records.js b/js/src/dapps/registry/Records/records.js index 89c751c36..4837290a3 100644 --- a/js/src/dapps/registry/Records/records.js +++ b/js/src/dapps/registry/Records/records.js @@ -1,3 +1,19 @@ +// 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 . + import React, { Component, PropTypes } from 'react'; import { Card, CardHeader, CardText } from 'material-ui/Card'; import TextField from 'material-ui/TextField'; diff --git a/js/src/modals/CreateWallet/WalletDetails/walletDetails.js b/js/src/modals/CreateWallet/WalletDetails/walletDetails.js index 9126fdb72..5d581b81d 100644 --- a/js/src/modals/CreateWallet/WalletDetails/walletDetails.js +++ b/js/src/modals/CreateWallet/WalletDetails/walletDetails.js @@ -16,18 +16,62 @@ import React, { Component, PropTypes } from 'react'; -import { Form, TypedInput, Input, AddressSelect } from '../../../ui'; -import { parseAbiType } from '../../../util/abi'; +import { Form, TypedInput, Input, AddressSelect, InputAddress } from '~/ui'; +import { parseAbiType } from '~/util/abi'; + +import styles from '../createWallet.css'; export default class WalletDetails extends Component { static propTypes = { accounts: PropTypes.object.isRequired, wallet: PropTypes.object.isRequired, errors: PropTypes.object.isRequired, - onChange: PropTypes.func.isRequired + onChange: PropTypes.func.isRequired, + walletType: PropTypes.string.isRequired }; render () { + const { walletType } = this.props; + + if (walletType === 'WATCH') { + return this.renderWatchDetails(); + } + + return this.renderMultisigDetails(); + } + + renderWatchDetails () { + const { wallet, errors } = this.props; + + return ( +
+ + + + + + + ); + } + + renderMultisigDetails () { const { accounts, wallet, errors } = this.props; return ( @@ -64,27 +108,34 @@ export default class WalletDetails extends Component { param={ parseAbiType('address[]') } /> - +
+ - + +
); } + onAddressChange = (_, address) => { + this.props.onChange({ address }); + } + onAccoutChange = (_, account) => { this.props.onChange({ account }); } diff --git a/js/src/modals/CreateWallet/WalletInfo/walletInfo.js b/js/src/modals/CreateWallet/WalletInfo/walletInfo.js index 301263314..344f4e09a 100644 --- a/js/src/modals/CreateWallet/WalletInfo/walletInfo.js +++ b/js/src/modals/CreateWallet/WalletInfo/walletInfo.js @@ -16,7 +16,7 @@ import React, { Component, PropTypes } from 'react'; -import { CompletedStep, IdentityIcon, CopyToClipboard } from '../../../ui'; +import { CompletedStep, IdentityIcon, CopyToClipboard } from '~/ui'; import styles from '../createWallet.css'; @@ -34,15 +34,21 @@ export default class WalletInfo extends Component { daylimit: PropTypes.oneOfType([ PropTypes.string, PropTypes.number - ]).isRequired + ]).isRequired, + + deployed: PropTypes.bool }; render () { - const { address, required, daylimit, name } = this.props; + const { address, required, daylimit, name, deployed } = this.props; return ( -
{ name } has been deployed at
+
+ { name } + has been + { deployed ? 'deployed' : 'added' } at +
@@ -63,9 +69,9 @@ export default class WalletInfo extends Component { } renderOwners () { - const { account, owners } = this.props; + const { account, owners, deployed } = this.props; - return [].concat(account, owners).map((address, id) => ( + return [].concat(deployed ? account : null, owners).filter((a) => a).map((address, id) => (
{ this.addressToString(address) }
diff --git a/js/src/modals/CreateWallet/WalletType/index.js b/js/src/modals/CreateWallet/WalletType/index.js new file mode 100644 index 000000000..525e35495 --- /dev/null +++ b/js/src/modals/CreateWallet/WalletType/index.js @@ -0,0 +1,17 @@ +// 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 . + +export default from './walletType.js'; diff --git a/js/src/modals/CreateWallet/WalletType/walletType.js b/js/src/modals/CreateWallet/WalletType/walletType.js new file mode 100644 index 000000000..e77d58fc6 --- /dev/null +++ b/js/src/modals/CreateWallet/WalletType/walletType.js @@ -0,0 +1,58 @@ +// 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 . + +import React, { Component, PropTypes } from 'react'; + +import { RadioButtons } from '~/ui'; + +// import styles from '../createWallet.css'; + +export default class WalletType extends Component { + static propTypes = { + onChange: PropTypes.func.isRequired, + type: PropTypes.string.isRequired + }; + + render () { + const { type } = this.props; + + return ( + + ); + } + + getTypes () { + return [ + { + label: 'Multi-Sig wallet', key: 'MULTISIG', + description: 'A standard multi-signature Wallet' + }, + { + label: 'Watch a wallet', key: 'WATCH', + description: 'Add an existing wallet to your accounts' + } + ]; + } + + onTypeChange = (type) => { + this.props.onChange(type.key); + } +} diff --git a/js/src/modals/CreateWallet/createWallet.css b/js/src/modals/CreateWallet/createWallet.css index a450466f0..01c079260 100644 --- a/js/src/modals/CreateWallet/createWallet.css +++ b/js/src/modals/CreateWallet/createWallet.css @@ -37,3 +37,22 @@ height: 24px; } } + +.splitInput { + display: flex; + flex-direction: row; + + > * { + flex: 1; + + margin: 0 0.25em; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } +} diff --git a/js/src/modals/CreateWallet/createWallet.js b/js/src/modals/CreateWallet/createWallet.js index 8f38fec12..d99ef05b4 100644 --- a/js/src/modals/CreateWallet/createWallet.js +++ b/js/src/modals/CreateWallet/createWallet.js @@ -21,8 +21,9 @@ import ActionDone from 'material-ui/svg-icons/action/done'; import ContentClear from 'material-ui/svg-icons/content/clear'; import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward'; -import { Button, Modal, TxHash, BusyStep } from '../../ui'; +import { Button, Modal, TxHash, BusyStep } from '~/ui'; +import WalletType from './WalletType'; import WalletDetails from './WalletDetails'; import WalletInfo from './WalletInfo'; import CreateWalletStore from './createWalletStore'; @@ -64,7 +65,7 @@ export default class CreateWallet extends Component { visible actions={ this.renderDialogActions() } current={ stage } - steps={ steps } + steps={ steps.map((s) => s.title) } waiting={ waiting } > { this.renderPage() } @@ -98,24 +99,35 @@ export default class CreateWallet extends Component { required={ this.store.wallet.required } daylimit={ this.store.wallet.daylimit } name={ this.store.wallet.name } + + deployed={ this.store.deployed } /> ); - default: case 'DETAILS': return ( ); + + default: + case 'TYPE': + return ( + + ); } } renderDialogActions () { - const { step, hasErrors, rejected, onCreate } = this.store; + const { step, hasErrors, rejected, onCreate, onNext, onAdd } = this.store; const cancelBtn = (
diff --git a/js/src/modals/CreateWallet/WalletInfo/walletInfo.js b/js/src/modals/CreateWallet/WalletInfo/walletInfo.js index 344f4e09a..bbbe5877f 100644 --- a/js/src/modals/CreateWallet/WalletInfo/walletInfo.js +++ b/js/src/modals/CreateWallet/WalletInfo/walletInfo.js @@ -17,6 +17,7 @@ import React, { Component, PropTypes } from 'react'; import { CompletedStep, IdentityIcon, CopyToClipboard } from '~/ui'; +import { fromWei } from '~/api/util/wei'; import styles from '../createWallet.css'; @@ -62,7 +63,7 @@ export default class WalletInfo extends Component { { required } owners are required to confirm a transaction.

- The daily limit is set to { daylimit }. + The daily limit is set to { fromWei(daylimit).toFormat() } ETH.

); diff --git a/js/src/modals/CreateWallet/WalletType/walletType.js b/js/src/modals/CreateWallet/WalletType/walletType.js index e77d58fc6..868c6ad9b 100644 --- a/js/src/modals/CreateWallet/WalletType/walletType.js +++ b/js/src/modals/CreateWallet/WalletType/walletType.js @@ -43,7 +43,13 @@ export default class WalletType extends Component { return [ { label: 'Multi-Sig wallet', key: 'MULTISIG', - description: 'A standard multi-signature Wallet' + description: ( + + Create/Deploy a + standard multi-signature + Wallet + + ) }, { label: 'Watch a wallet', key: 'WATCH', diff --git a/js/src/modals/CreateWallet/createWalletStore.js b/js/src/modals/CreateWallet/createWalletStore.js index f5e2f1855..d8c308a12 100644 --- a/js/src/modals/CreateWallet/createWalletStore.js +++ b/js/src/modals/CreateWallet/createWalletStore.js @@ -16,7 +16,7 @@ import { observable, computed, action, transaction } from 'mobx'; -import { validateUint, validateAddress, validateName } from '../../util/validation'; +import { validateUint, validateAddress, validateName } from '~/util/validation'; import { ERROR_CODES } from '~/api/transport/error'; import Contract from '~/api/contract'; diff --git a/js/src/modals/Transfer/store.js b/js/src/modals/Transfer/store.js index a0c7967b9..8f8baf55f 100644 --- a/js/src/modals/Transfer/store.js +++ b/js/src/modals/Transfer/store.js @@ -23,7 +23,7 @@ import { bytesToHex } from '~/api/util/format'; import Contract from '~/api/contract'; import ERRORS from './errors'; import { ERROR_CODES } from '~/api/transport/error'; -import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '../../util/constants'; +import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '~/util/constants'; const TITLES = { transfer: 'transfer details', @@ -116,7 +116,6 @@ export default class TransferStore { this.api = api; const { account, balance, gasLimit, senders, onClose, newError, sendersBalances } = props; - this.account = account; this.balance = balance; this.gasLimit = gasLimit; @@ -412,34 +411,38 @@ export default class TransferStore { return; } - const { gas, gasPrice, tag, valueAll, isEth } = this; + const { gas, gasPrice, tag, valueAll, isEth, isWallet } = this; const gasTotal = new BigNumber(gasPrice || 0).mul(new BigNumber(gas || 0)); const availableEth = new BigNumber(balance.tokens[0].value); const senderBalance = this.balance.tokens.find((b) => tag === b.token.tag); - const available = new BigNumber(senderBalance.value); const format = new BigNumber(senderBalance.token.format || 1); + const available = isWallet + ? this.api.util.fromWei(new BigNumber(senderBalance.value)) + : (new BigNumber(senderBalance.value)).div(format); let { value, valueError } = this; let totalEth = gasTotal; let totalError = null; if (valueAll) { - if (isEth) { + if (isEth && !isWallet) { const bn = this.api.util.fromWei(availableEth.minus(gasTotal)); value = (bn.lt(0) ? new BigNumber(0.0) : bn).toString(); + } else if (isEth) { + value = (available.lt(0) ? new BigNumber(0.0) : available).toString(); } else { - value = available.div(format).toString(); + value = available.toString(); } } - if (isEth) { + if (isEth && !isWallet) { totalEth = totalEth.plus(this.api.util.toWei(value || 0)); } - if (new BigNumber(value || 0).gt(available.div(format))) { + if (new BigNumber(value || 0).gt(available)) { valueError = ERRORS.largeAmount; } else if (valueError === ERRORS.largeAmount) { valueError = null; diff --git a/js/src/modals/Transfer/transfer.js b/js/src/modals/Transfer/transfer.js index 402e3ae4f..e1e2a0951 100644 --- a/js/src/modals/Transfer/transfer.js +++ b/js/src/modals/Transfer/transfer.js @@ -139,8 +139,8 @@ class Transfer extends Component { ? (

-

- This transaction needs confirmation from other owners. +

+

This transaction needs confirmation from other owners.

-

+
) : null @@ -298,7 +298,6 @@ function mapStateToProps (initState, initProps) { return (state) => { const { gasLimit } = state.nodeStatus; const sendersBalances = senders ? pick(state.balances.balances, Object.keys(senders)) : null; - return { gasLimit, wallet, senders, sendersBalances }; }; } diff --git a/js/src/modals/WalletSettings/index.js b/js/src/modals/WalletSettings/index.js new file mode 100644 index 000000000..9b15a5f3b --- /dev/null +++ b/js/src/modals/WalletSettings/index.js @@ -0,0 +1,17 @@ +// 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 . + +export default from './walletSettings'; diff --git a/js/src/modals/WalletSettings/walletSettings.css b/js/src/modals/WalletSettings/walletSettings.css new file mode 100644 index 000000000..ae52013cd --- /dev/null +++ b/js/src/modals/WalletSettings/walletSettings.css @@ -0,0 +1,63 @@ +/* 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 . +*/ + +.splitInput { + display: flex; + flex-direction: row; + + > * { + flex: 1; + + margin: 0 0.25em; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } +} + +.change { + background-color: rgba(255, 255, 255, 0.1); + padding: 0.75em 1.75em; + margin-bottom: 1em; + + &.add { + background-color: rgba(139, 195, 74, 0.5); + } + + &.remove { + background-color: rgba(244, 67, 54, 0.5); + } + + .label { + text-transform: uppercase; + margin-bottom: 0.5em; + margin-left: -1em; + font-size: 0.8em; + } +} + +.eth:after { + content: 'ETH'; + font-size: 0.75em; + margin-left: 0.125em; +} + diff --git a/js/src/modals/WalletSettings/walletSettings.js b/js/src/modals/WalletSettings/walletSettings.js new file mode 100644 index 000000000..1b2b2cc1a --- /dev/null +++ b/js/src/modals/WalletSettings/walletSettings.js @@ -0,0 +1,321 @@ +// 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 . + +import React, { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; +import { observer } from 'mobx-react'; +import { pick } from 'lodash'; + +import ActionDone from 'material-ui/svg-icons/action/done'; +import ContentClear from 'material-ui/svg-icons/content/clear'; +import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward'; +import { parseAbiType } from '~/util/abi'; + +import { Button, Modal, TxHash, BusyStep, Form, TypedInput, InputAddress, AddressSelect } from '~/ui'; +import { fromWei } from '~/api/util/wei'; + +import WalletSettingsStore from './walletSettingsStore.js'; +import styles from './walletSettings.css'; + +@observer +class WalletSettings extends Component { + static contextTypes = { + api: PropTypes.object.isRequired + }; + + static propTypes = { + accounts: PropTypes.object.isRequired, + wallet: PropTypes.object.isRequired, + onClose: PropTypes.func.isRequired, + senders: PropTypes.object.isRequired + }; + + store = new WalletSettingsStore(this.context.api, this.props.wallet); + + render () { + const { stage, steps, waiting, rejected } = this.store; + + if (rejected) { + return ( + + + + ); + } + + return ( + s.title) } + waiting={ waiting } + > + { this.renderPage() } + + ); + } + + renderPage () { + const { step } = this.store; + + switch (step) { + case 'SENDING': + return ( + + { + this.store.requests.map((req) => { + const key = req.id; + + if (req.txhash) { + return (); + } + + if (req.rejected) { + return (

The transaction #{parseInt(key, 16)} has been rejected

); + } + }) + } +
+ ); + + case 'CONFIRMATION': + const { changes } = this.store; + + return ( +
+

You are about to make the following modifications

+
+ { this.renderChanges(changes) } +
+
+ ); + + default: + case 'EDIT': + const { wallet, errors } = this.store; + const { accounts, senders } = this.props; + + return ( +
+

+ In order to edit this contract's settings, at + least { this.store.initialWallet.require.toNumber() } owners have to + send the very same modifications. + Otherwise, no modification will be taken into account... +

+ + + + + +
+ + + +
+ + ); + } + } + + renderChanges (changes) { + return changes.map((change, index) => ( +
+ { this.renderChange(change) } +
+ )); + } + + renderChange (change) { + const { accounts } = this.props; + + switch (change.type) { + case 'dailylimit': + return ( +
+
Change Daily Limit
+
+ from + { fromWei(change.initial).toFormat() } + + to + { fromWei(change.value).toFormat() } + +
+
+ ); + + case 'require': + return ( +
+
Change Required Owners
+
+ from + { change.initial.toNumber() } + to + { change.value.toNumber() } +
+
+ ); + + case 'add_owner': + return ( +
+
Add Owner
+
+ +
+
+ ); + + case 'remove_owner': + return ( +
+
Remove Owner
+
+ +
+
+ ); + } + } + + renderDialogActions () { + const { onClose } = this.props; + const { step, hasErrors, rejected, onNext, send, done } = this.store; + + const cancelBtn = ( +