From 3b6d886860cbe89736ed4448d992b3b20572d990 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 27 Nov 2016 14:11:37 +0100 Subject: [PATCH 01/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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 ff7b918d82bf8c6bf8a51802794dc022b65cb8fa Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 5 Dec 2016 10:55:53 -0800 Subject: [PATCH 09/13] 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 be90245ecb68894f0ed11c31bbef77bac64b7fcf Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 7 Dec 2016 10:46:46 +0100 Subject: [PATCH 10/13] 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 8dbd56888d6627b9434c86bd601b616f6aa3c091 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 7 Dec 2016 12:47:44 +0100 Subject: [PATCH 11/13] 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 = (