From 3d2e9efb1ed856f68a42662875e4e9df97908958 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Mon, 27 Mar 2017 18:39:21 +0300 Subject: [PATCH 001/117] P1, P2 definition --- Cargo.lock | 2 +- ethcore/src/builtin.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 1a25c8553..3b5eded0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,7 +180,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bn" version = "0.4.3" -source = "git+https://github.com/paritytech/bn#59d848e642ad1ff0d60e39348576a6f11ee123b8" +source = "git+https://github.com/paritytech/bn#5df8d83c19f3fb97ae3fdefa5bad5279babe795e" dependencies = [ "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index bdedd5739..c240250df 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -191,6 +191,9 @@ struct Bn128AddImpl; #[derive(Debug)] struct Bn128MulImpl; +#[derive(Debug)] +struct Bn128ParingImpl; + impl Impl for Identity { fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { output.write(0, input); @@ -393,6 +396,31 @@ impl Impl for Bn128MulImpl { } } +impl Impl for Bn128ParingImpl { + // Can fail if any of the 2 points does not belong the bn128 curve + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { + use bn::{Fq, Fq2, AffineG1, AffineG2}; + + let p1 = AffineG1::new( + Fq::from_str("1").expect("1 is a valid field element"), + Fq::from_str("2").expect("2 is a valid field element"), + ).expect("Generator P1(1, 2) is a valid curve point"); + + let p2 = AffineG2::new( + Fq2::new( + Fq::from_str("1").expect("1 is a valid field element"), + Fq::from_str("2").expect("2 is a valid field element"), + ), + Fq2::new( + Fq::from_str("1").expect("1 is a valid field element"), + Fq::from_str("2").expect("2 is a valid field element"), + ), + ).expect("Generator P2(i+2b, i+2b) is a valid curve point"); + + Ok(()) + } +} + #[cfg(test)] mod tests { use super::{Builtin, Linear, ethereum_builtin, Pricer, Modexp}; From 9271dd0cc7f6d2185d720378494f6872ff616b75 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Mon, 27 Mar 2017 19:44:35 +0300 Subject: [PATCH 002/117] wip --- ethcore/src/builtin.rs | 93 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 8 deletions(-) diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index c240250df..f5e500154 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -396,17 +396,18 @@ impl Impl for Bn128MulImpl { } } -impl Impl for Bn128ParingImpl { - // Can fail if any of the 2 points does not belong the bn128 curve - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - use bn::{Fq, Fq2, AffineG1, AffineG2}; +mod bn128_gen { + use bn::{AffineG1, AffineG2, Fq, Fq2, G1, G2, Gt, pairing}; - let p1 = AffineG1::new( + lazy_static! { + pub static ref P1: G1 = G1::from(AffineG1::new( Fq::from_str("1").expect("1 is a valid field element"), Fq::from_str("2").expect("2 is a valid field element"), - ).expect("Generator P1(1, 2) is a valid curve point"); + ).expect("Generator P1(1, 2) is a valid curve point")); + } - let p2 = AffineG2::new( + lazy_static! { + pub static ref P2: G2 = G2::from(AffineG2::new( Fq2::new( Fq::from_str("1").expect("1 is a valid field element"), Fq::from_str("2").expect("2 is a valid field element"), @@ -415,7 +416,83 @@ impl Impl for Bn128ParingImpl { Fq::from_str("1").expect("1 is a valid field element"), Fq::from_str("2").expect("2 is a valid field element"), ), - ).expect("Generator P2(i+2b, i+2b) is a valid curve point"); + ).expect("Generator P2(i+2b, i+2b) is a valid curve point")); + } + + + lazy_static! { + pub static ref P1xP2: Gt = pairing(P1.clone(), P2.clone()); + } +} + +impl Impl for Bn128ParingImpl { + // Can fail if any of the 2 points does not belong the bn128 curve + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { + use bn::{AffineG1, AffineG2, Fq, Fq2, pairing, G1, G2}; + + let elements = input.len() / 192; // (a, b_a, b_b - each 64-byte affine coordinates) + if input.len() % 192 != 0 { + return Err("Invalid input length, must be multiple of 192 (3 * (32*2))".into()) + } + let ret_val = if input.len() == 0 { + U256::one() + } else { + let mut vals = Vec::new(); + for idx in 0..elements { + let a_x = Fq::from_slice(&input[idx*192..idx*192+32]) + .map_err(|_| Error::from("Invalid a argument x coordinate"))?; + + let a_y = Fq::from_slice(&input[idx*192+32..idx*192+64]) + .map_err(|_| Error::from("Invalid a argument y coordinate"))?; + + let b_b_x = Fq::from_slice(&input[idx*192+64..idx*192+96]) + .map_err(|_| Error::from("Invalid b argument imaginary coeff x coordinate"))?; + + let b_b_y = Fq::from_slice(&input[idx*192+96..idx*192+128]) + .map_err(|_| Error::from("Invalid b argument imaginary coeff y coordinate"))?; + + let b_a_x = Fq::from_slice(&input[idx*192+128..idx*192+160]) + .map_err(|_| Error::from("Invalid b argument real coeff x coordinate"))?; + + let b_a_y = Fq::from_slice(&input[idx*192+160..idx*192+192]) + .map_err(|_| Error::from("Invalid b argument real coeff y coordinate"))?; + + vals.push(( + G1::from( + AffineG1::new(a_x, a_y).map_err(|_| Error::from("Invalid a argument - not on curve"))? + ), + G2::from( + AffineG2::new( + Fq2::new(b_a_x, b_a_y), + Fq2::new(b_b_x, b_b_y), + ).map_err(|_| Error::from("Invalid b argument - not on curve"))? + ), + )); + }; + let mul = if elements == 1 { + let (a, b) = vals[0]; + pairing(a, b) + } else { + let mut drain = vals.drain(..); + let mut mul = { + let (a, b) = drain.next() + .expect("Checked above that elements > 1, so 0th element should exist; qed"); + pairing(a, b) + }; + for _ in 1..elements { + let (a, b) = drain.next() + .expect("idx-th element should exist, because we do next() no more than elements-1 times; qed"); + mul = mul * pairing(a, b); + } + mul + }; + + if mul == *bn128_gen::P1xP2 { + U256::one() + } else { + U256::zero() + } + }; Ok(()) } From d34aec29ed412792db18ac20aaf5090bdd8a3ffa Mon Sep 17 00:00:00 2001 From: NikVolf Date: Mon, 27 Mar 2017 21:34:02 +0300 Subject: [PATCH 003/117] finalize --- ethcore/res/ethereum/foundation.json | 1 + ethcore/src/builtin.rs | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index 54f10b70c..68b74a0f5 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -192,6 +192,7 @@ "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x7fffffffffffff", "pricing": { "modexp": { "divisor": 20 } } } }, "0000000000000000000000000000000000000006": { "builtin": { "name": "bn128_add", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 999999, "word": 0 } } } }, "0000000000000000000000000000000000000007": { "builtin": { "name": "bn128_mul", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 999999, "word": 0 } } } }, + "0000000000000000000000000000000000000008": { "builtin": { "name": "bn128_pairing", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 999999, "word": 0 } } } }, "3282791d6fd713f1e94f4bfd565eaa78b3a0599d": { "balance": "1337000000000000000000" }, diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index f5e500154..cc214f2a3 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -158,6 +158,7 @@ fn ethereum_builtin(name: &str) -> Box { "modexp" => Box::new(ModexpImpl) as Box, "bn128_add" => Box::new(Bn128AddImpl) as Box, "bn128_mul" => Box::new(Bn128MulImpl) as Box, + "bn128_pairing" => Box::new(Bn128ParingImpl) as Box, _ => panic!("invalid builtin name: {}", name), } } @@ -409,19 +410,18 @@ mod bn128_gen { lazy_static! { pub static ref P2: G2 = G2::from(AffineG2::new( Fq2::new( - Fq::from_str("1").expect("1 is a valid field element"), - Fq::from_str("2").expect("2 is a valid field element"), + Fq::from_str("10857046999023057135944570762232829481370756359578518086990519993285655852781").expect("1 is a valid field element"), + Fq::from_str("11559732032986387107991004021392285783925812861821192530917403151452391805634").expect("2 is a valid field element"), ), Fq2::new( - Fq::from_str("1").expect("1 is a valid field element"), - Fq::from_str("2").expect("2 is a valid field element"), + Fq::from_str("8495653923123431417604973247489272438418190587263600148770280649306958101930").expect("1 is a valid field element"), + Fq::from_str("4082367875863433681332203403145435568316851327593401208105741076214120093531").expect("2 is a valid field element"), ), - ).expect("Generator P2(i+2b, i+2b) is a valid curve point")); + ).expect("Generator P2(10857046999023057135944570762232829481370756359578518086990519993285655852781 + 11559732032986387107991004021392285783925812861821192530917403151452391805634i, 8495653923123431417604973247489272438418190587263600148770280649306958101930 + 4082367875863433681332203403145435568316851327593401208105741076214120093531i) is a valid curve point")); } - lazy_static! { - pub static ref P1xP2: Gt = pairing(P1.clone(), P2.clone()); + pub static ref P1_P2_PAIRING: Gt = pairing(P1.clone(), P2.clone()); } } @@ -481,19 +481,23 @@ impl Impl for Bn128ParingImpl { }; for _ in 1..elements { let (a, b) = drain.next() - .expect("idx-th element should exist, because we do next() no more than elements-1 times; qed"); + .expect("this element should exist, because we do next() no more than elements-1 times; qed"); mul = mul * pairing(a, b); } mul }; - if mul == *bn128_gen::P1xP2 { + if mul == *bn128_gen::P1_P2_PAIRING { U256::one() } else { U256::zero() } }; + let mut buf = [0u8; 32]; + ret_val.to_big_endian(&mut buf); + output.write(0, &buf); + Ok(()) } } From 2f30b030605ec17c0c6feecb926c9a34d78404b1 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 28 Mar 2017 01:38:04 +0300 Subject: [PATCH 004/117] some tests --- ethcore/src/builtin.rs | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index cc214f2a3..0113e94f2 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -507,6 +507,7 @@ mod tests { use super::{Builtin, Linear, ethereum_builtin, Pricer, Modexp}; use ethjson; use util::{U256, BytesRef}; + use rustc_serialize::hex::FromHex; #[test] fn identity() { @@ -822,7 +823,82 @@ mod tests { assert!(res.is_err(), "There should be built-in error here"); } } + + fn builitin_pairing() -> Builtin { + Builtin { + pricer: Box::new(Linear { base: 0, word: 0 }), + native: ethereum_builtin("bn128_pairing"), + activate_at: 0, + } + } + + fn empty_test(f: Builtin, expected: Vec) { + let mut empty = [0u8; 0]; + let input = BytesRef::Fixed(&mut empty); + + let mut output = vec![0u8; expected.len()]; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); + assert_eq!(output, expected); + } + + fn error_test(f: Builtin, input: &[u8], msg_contains: Option<&str>) { + let mut output = vec![0u8; 64]; + let res = f.execute(input, &mut BytesRef::Fixed(&mut output[..])); + if let Some(msg) = msg_contains { + if let Err(e) = res { + if !e.0.contains(msg) { + panic!("There should be error containing '{}' here, but got: '{}'", msg, e.0); + } + } + } else { + assert!(res.is_err(), "There should be built-in error here"); + } + } + + fn bytes(s: &'static str) -> Vec { + FromHex::from_hex(s).expect("static str should contain valid hex bytes") + } + #[test] + fn bn128_pairing_empty() { + // should not fail, because empty input is a valid input of 0 elements + empty_test( + builitin_pairing(), + bytes("0000000000000000000000000000000000000000000000000000000000000001"), + ); + } + + #[test] + fn bn128_pairing_notcurve() { + // should fail - point not on curve + error_test( + builitin_pairing(), + &bytes("\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111" + ), + Some("not on curve"), + ); + } + + #[test] + fn bn128_pairing_fragmented() { + // should fail - input length is invalid + error_test( + builitin_pairing(), + &bytes("\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 111111111111111111111111111111" + ), + Some("Invalid input length"), + ); + } #[test] #[should_panic] From a529427bc935303890dbec53868858bf8715c20a Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 28 Mar 2017 01:42:37 +0300 Subject: [PATCH 005/117] bump bn lib --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b5eded0a..26fec8a73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,8 +179,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bn" -version = "0.4.3" -source = "git+https://github.com/paritytech/bn#5df8d83c19f3fb97ae3fdefa5bad5279babe795e" +version = "0.4.4" +source = "git+https://github.com/paritytech/bn#b97e95a45f4484a41a515338c4f0e093bf6675e0" dependencies = [ "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -387,7 +387,7 @@ version = "1.7.0" dependencies = [ "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bn 0.4.3 (git+https://github.com/paritytech/bn)", + "bn 0.4.4 (git+https://github.com/paritytech/bn)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2642,7 +2642,7 @@ dependencies = [ "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum blastfig 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09640e0509d97d5cdff03a9f5daf087a8e04c735c3b113a75139634a19cfc7b2" "checksum bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f421095d2a76fc24cd3fb3f912b90df06be7689912b1bdb423caefae59c258d" -"checksum bn 0.4.3 (git+https://github.com/paritytech/bn)" = "" +"checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "" "checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" "checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27" "checksum bytes 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46112a0060ae15e3a3f9a445428a53e082b91215b744fa27a1948842f4a64b96" From 569d63d0c99db7abe6215bb9ec2394dfdf1f2c82 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 31 Mar 2017 17:46:44 +0300 Subject: [PATCH 006/117] fix assertions --- ethcore/src/builtin.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index 0113e94f2..a2677abc7 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -410,14 +410,18 @@ mod bn128_gen { lazy_static! { pub static ref P2: G2 = G2::from(AffineG2::new( Fq2::new( - Fq::from_str("10857046999023057135944570762232829481370756359578518086990519993285655852781").expect("1 is a valid field element"), - Fq::from_str("11559732032986387107991004021392285783925812861821192530917403151452391805634").expect("2 is a valid field element"), + Fq::from_str("10857046999023057135944570762232829481370756359578518086990519993285655852781") + .expect("a valid field element"), + Fq::from_str("11559732032986387107991004021392285783925812861821192530917403151452391805634") + .expect("a valid field element"), ), Fq2::new( - Fq::from_str("8495653923123431417604973247489272438418190587263600148770280649306958101930").expect("1 is a valid field element"), - Fq::from_str("4082367875863433681332203403145435568316851327593401208105741076214120093531").expect("2 is a valid field element"), + Fq::from_str("8495653923123431417604973247489272438418190587263600148770280649306958101930") + .expect("a valid field element"), + Fq::from_str("4082367875863433681332203403145435568316851327593401208105741076214120093531") + .expect("a valid field element"), ), - ).expect("Generator P2(10857046999023057135944570762232829481370756359578518086990519993285655852781 + 11559732032986387107991004021392285783925812861821192530917403151452391805634i, 8495653923123431417604973247489272438418190587263600148770280649306958101930 + 4082367875863433681332203403145435568316851327593401208105741076214120093531i) is a valid curve point")); + ).expect("the generator P2(10857046999023057135944570762232829481370756359578518086990519993285655852781 + 11559732032986387107991004021392285783925812861821192530917403151452391805634i, 8495653923123431417604973247489272438418190587263600148770280649306958101930 + 4082367875863433681332203403145435568316851327593401208105741076214120093531i) is a valid curve point")); } lazy_static! { From d6baadbaf2b24660066c30d41b8915e769ad7682 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 31 Mar 2017 17:53:26 +0300 Subject: [PATCH 007/117] fix doc comment about failing --- ethcore/src/builtin.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index a2677abc7..efe59d43f 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -430,11 +430,14 @@ mod bn128_gen { } impl Impl for Bn128ParingImpl { - // Can fail if any of the 2 points does not belong the bn128 curve + /// Can fail if: + /// - input length is not a multiple of 192 + /// - any of odd points does not belong to bn128 curve + /// - any of even points does not belong to the twisted bn128 curve over the field F_p^2 = F_p[i] / (i^2 + 1) fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { use bn::{AffineG1, AffineG2, Fq, Fq2, pairing, G1, G2}; - let elements = input.len() / 192; // (a, b_a, b_b - each 64-byte affine coordinates) + let elements = input.len() / 192; // (a, b_a, b_b - each 64-byte affine coordinate) if input.len() % 192 != 0 { return Err("Invalid input length, must be multiple of 192 (3 * (32*2))".into()) } From df72c9c6cc77ad32a97b85afc1722e3ea35e5eba Mon Sep 17 00:00:00 2001 From: NikVolf Date: Mon, 3 Apr 2017 12:57:18 +0300 Subject: [PATCH 008/117] fold multiplication --- ethcore/src/builtin.rs | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index efe59d43f..88cf357a3 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -435,9 +435,9 @@ impl Impl for Bn128ParingImpl { /// - any of odd points does not belong to bn128 curve /// - any of even points does not belong to the twisted bn128 curve over the field F_p^2 = F_p[i] / (i^2 + 1) fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - use bn::{AffineG1, AffineG2, Fq, Fq2, pairing, G1, G2}; + use bn::{AffineG1, AffineG2, Fq, Fq2, pairing, G1, G2, Gt}; - let elements = input.len() / 192; // (a, b_a, b_b - each 64-byte affine coordinate) + let elements = input.len() / 192; // (a, b_a, b_b - each 64-byte affine coordinates) if input.len() % 192 != 0 { return Err("Invalid input length, must be multiple of 192 (3 * (32*2))".into()) } @@ -476,23 +476,8 @@ impl Impl for Bn128ParingImpl { ), )); }; - let mul = if elements == 1 { - let (a, b) = vals[0]; - pairing(a, b) - } else { - let mut drain = vals.drain(..); - let mut mul = { - let (a, b) = drain.next() - .expect("Checked above that elements > 1, so 0th element should exist; qed"); - pairing(a, b) - }; - for _ in 1..elements { - let (a, b) = drain.next() - .expect("this element should exist, because we do next() no more than elements-1 times; qed"); - mul = mul * pairing(a, b); - } - mul - }; + + let mul = vals.into_iter().fold(Gt::one(), |s, (a, b)| s * pairing(a, b)); if mul == *bn128_gen::P1_P2_PAIRING { U256::one() From 9bcd1245d9d669c436a0a397adc6c0f1d4b89282 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 6 Apr 2017 11:04:28 +0300 Subject: [PATCH 009/117] fix typo --- ethcore/src/builtin.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index 88cf357a3..32087be96 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -158,7 +158,7 @@ fn ethereum_builtin(name: &str) -> Box { "modexp" => Box::new(ModexpImpl) as Box, "bn128_add" => Box::new(Bn128AddImpl) as Box, "bn128_mul" => Box::new(Bn128MulImpl) as Box, - "bn128_pairing" => Box::new(Bn128ParingImpl) as Box, + "bn128_pairing" => Box::new(Bn128PairingImpl) as Box, _ => panic!("invalid builtin name: {}", name), } } @@ -193,7 +193,7 @@ struct Bn128AddImpl; struct Bn128MulImpl; #[derive(Debug)] -struct Bn128ParingImpl; +struct Bn128PairingImpl; impl Impl for Identity { fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { @@ -429,7 +429,7 @@ mod bn128_gen { } } -impl Impl for Bn128ParingImpl { +impl Impl for Bn128PairingImpl { /// Can fail if: /// - input length is not a multiple of 192 /// - any of odd points does not belong to bn128 curve From a254b2098fd029ee03724e3898a69562e2787bcb Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 11 Apr 2017 17:07:04 +0200 Subject: [PATCH 010/117] more useful Engine::verify_seal --- ethcore/src/block.rs | 11 +++++--- ethcore/src/engines/authority_round.rs | 4 +-- ethcore/src/engines/basic_authority.rs | 2 +- ethcore/src/engines/instant_seal.rs | 2 +- ethcore/src/engines/mod.rs | 36 ++++++++++++++++++++++---- ethcore/src/engines/tendermint/mod.rs | 2 +- ethcore/src/miner/miner.rs | 2 +- ethcore/src/snapshot/mod.rs | 2 +- 8 files changed, 46 insertions(+), 15 deletions(-) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 12e77d41b..6ec2584ec 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -26,7 +26,7 @@ use util::error::{Mismatch, OutOfBounds}; use basic_types::{LogBloom, Seal}; use env_info::{EnvInfo, LastHashes}; -use engines::Engine; +use engines::{Engine, ValidationProof}; use error::{Error, BlockError, TransactionError}; use factory::Factories; use header::Header; @@ -484,10 +484,15 @@ impl LockedBlock { /// Provide a valid seal in order to turn this into a `SealedBlock`. /// This does check the validity of `seal` with the engine. /// Returns the `ClosedBlock` back again if the seal is no good. - pub fn try_seal(self, engine: &Engine, seal: Vec) -> Result { + pub fn try_seal( + self, + engine: &Engine, + seal: Vec, + proof: Option, + ) -> Result { let mut s = self; s.block.header.set_seal(seal); - match engine.verify_block_seal(&s.block.header) { + match engine.verify_block_seal(&s.block.header, proof) { Err(e) => Err((e, s)), _ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }), } diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index e6cbdb531..acc276d21 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -464,14 +464,14 @@ mod tests { engine.set_signer(tap.clone(), addr1, "1".into()); if let Seal::Regular(seal) = engine.generate_seal(b1.block()) { - assert!(b1.clone().try_seal(engine, seal).is_ok()); + assert!(b1.clone().try_seal(engine, seal, None).is_ok()); // Second proposal is forbidden. assert!(engine.generate_seal(b1.block()) == Seal::None); } engine.set_signer(tap, addr2, "2".into()); if let Seal::Regular(seal) = engine.generate_seal(b2.block()) { - assert!(b2.clone().try_seal(engine, seal).is_ok()); + assert!(b2.clone().try_seal(engine, seal, None).is_ok()); // Second proposal is forbidden. assert!(engine.generate_seal(b2.block()) == Seal::None); } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index e5a53d4e9..fe27994e4 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -258,7 +258,7 @@ mod tests { 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(); if let Seal::Regular(seal) = engine.generate_seal(b.block()) { - assert!(b.try_seal(engine, seal).is_ok()); + assert!(b.try_seal(engine, seal, None).is_ok()); } } diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index 45bede9f4..5d05dd83f 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -89,7 +89,7 @@ mod tests { 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(); if let Seal::Regular(seal) = engine.generate_seal(b.block()) { - assert!(b.try_seal(engine, seal).is_ok()); + assert!(b.try_seal(engine, seal, None).is_ok()); } } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 438b9bda0..76a0d790b 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -33,18 +33,20 @@ pub use self::authority_round::AuthorityRound; pub use self::tendermint::Tendermint; use std::sync::Weak; -use util::*; -use ethkey::Signature; + use account_provider::AccountProvider; use block::ExecutedBlock; use builtin::Builtin; +use client::Client; use env_info::EnvInfo; use error::{Error, TransactionError}; -use spec::CommonParams; use evm::Schedule; use header::Header; +use spec::CommonParams; use transaction::{UnverifiedTransaction, SignedTransaction}; -use client::Client; + +use ethkey::Signature; +use util::*; /// Voting errors. #[derive(Debug)] @@ -59,6 +61,8 @@ pub enum EngineError { UnexpectedMessage, /// Seal field has an unexpected size. BadSealFieldSize(OutOfBounds), + /// Needs a validation proof for the given block hash before verification can continue. + NeedsValidationProof(H256), } impl fmt::Display for EngineError { @@ -70,6 +74,7 @@ impl fmt::Display for EngineError { NotAuthorized(ref address) => format!("Signer {} is not authorized.", address), UnexpectedMessage => "This Engine should not be fed messages.".into(), BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob), + NeedsValidationProof(ref hash) => format!("Needs validation proof of block {} to verify seal.", hash), }; f.write_fmt(format_args!("Engine error ({})", msg)) @@ -87,6 +92,12 @@ pub enum Seal { None, } +/// A validation proof, required for validation of a block header. +pub type ValidationProof = Vec; + +/// Type alias for a function we can make calls through synchronously. +pub type Call = Fn(Address, Bytes) -> Result; + /// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. /// Provides hooks into each of the major parts of block import. pub trait Engine : Sync + Send { @@ -180,10 +191,25 @@ pub trait Engine : Sync + Send { /// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods /// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer /// methods are needed for an Engine, this may be overridden. - fn verify_block_seal(&self, header: &Header) -> Result<(), Error> { + fn verify_block_seal(&self, header: &Header, _proof: Option) -> Result<(), Error> { self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None)) } + /// Generate a validation proof for the given block header. + /// + /// All values queried during execution of given will go into the proof. + /// This may only be called for blocks indicated in "needs validation proof" + /// errors. + /// + /// Engines which don't draw consensus information from the state (e.g. PoW) + /// don't need to change anything here. + /// + /// Engines which do draw consensus information from the state may only do so + /// here. + fn generate_validation_proof(&self, _call: &Call) -> ValidationProof { + ValidationProof::default() + } + /// Populate a header's fields based on its parent's header. /// Usually implements the chain scoring rule based on weight. /// The gas floor target must not be lower than the engine's minimum gas limit. diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 8c8094117..e88803ce2 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -864,7 +864,7 @@ mod tests { let proposer = insert_and_register(&tap, spec.engine.as_ref(), "1"); let (b, seal) = propose_default(&spec, proposer); - assert!(b.lock().try_seal(spec.engine.as_ref(), seal).is_ok()); + assert!(b.lock().try_seal(spec.engine.as_ref(), seal, None).is_ok()); } #[test] diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index ac1695b52..8337ef2d8 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1133,7 +1133,7 @@ impl MinerService for Miner { |b| &b.hash() == &block_hash ) { 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, _)| { + b.lock().try_seal(&*self.engine, seal, None).or_else(|(e, _)| { warn!(target: "miner", "Mined solution rejected: {}", e); Err(Error::PowInvalid) }) diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 69dbc943d..0ef263476 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -567,7 +567,7 @@ pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &Engine, chain if always || rng.gen::() <= POW_VERIFY_RATE { match chain.block_header(header.parent_hash()) { Some(parent) => engine.verify_block_family(header, &parent, body), - None => engine.verify_block_seal(header), + None => engine.verify_block_seal(header, None), // TODO: fetch validation proof as necessary. } } else { engine.verify_block_basic(header, body) From 7723d6281bcd11c7bd4da101c02fc70e78bbf303 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 12 Apr 2017 12:46:25 +0200 Subject: [PATCH 011/117] starting memoized validatorset --- ethcore/src/engines/mod.rs | 2 +- ethcore/src/engines/validator_set/mod.rs | 41 +++++++++++++++++++ ethcore/src/engines/validator_set/multi.rs | 19 ++++++++- .../src/engines/validator_set/simple_list.rs | 9 ++-- 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 76a0d790b..0ae353a56 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -206,7 +206,7 @@ pub trait Engine : Sync + Send { /// /// Engines which do draw consensus information from the state may only do so /// here. - fn generate_validation_proof(&self, _call: &Call) -> ValidationProof { + fn generate_validation_proof(&self, header: &Header, _call: &Call) -> ValidationProof { ValidationProof::default() } diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index cbbedfb33..a2f6d1075 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -25,11 +25,15 @@ use std::sync::Weak; use util::{Address, H256}; use ethjson::spec::ValidatorSet as ValidatorSpec; use client::Client; +use receipt::Receipt; + use self::simple_list::SimpleList; use self::contract::ValidatorContract; use self::safe_contract::ValidatorSafeContract; use self::multi::Multi; +pub type Call = Fn(Address, Vec) -> Result, String>; + /// Creates a validator set from spec. pub fn new_validator_set(spec: ValidatorSpec) -> Box { match spec { @@ -42,7 +46,12 @@ pub fn new_validator_set(spec: ValidatorSpec) -> Box { } } +/// A validator set. pub trait ValidatorSet: Send + Sync { + /// Get this validator set as a flexible validator set. + /// Returning `None` indicates this is only usable for + /// full nodes. + fn as_memoized(&self) -> Option<&Memoized> { None } /// Checks if a given address is a validator. fn contains(&self, parent_block_hash: &H256, address: &Address) -> bool; /// Draws an validator nonce modulo number of validators. @@ -56,3 +65,35 @@ pub trait ValidatorSet: Send + Sync { /// Allows blockchain state access. fn register_contract(&self, _client: Weak) {} } + +/// A flexible validator set can track its changes. +pub trait FlexibleValidatorSet: Send + Sync { + /// Whether a validator set may have changed at this header. + fn has_possibly_changed(&self, header: &Header) -> bool; + + /// Whether a validator set has changed at this header, given the block receipts. + /// Called after `has_possibly_changed`. + /// `Some` indicates the validator set has changed at this header and the new + /// expected validator set. + /// `None` indicates no change. + fn has_changed(&self, header: &Header, receipts: &[Receipt]) -> Option; + + /// Fetch validators at a block synchronously. + fn fetch_validators(&self, header: &Header, call: &Call) -> Result; +} + +/// A memoized flexible validator set +pub struct Memoized { + inner: Box, + memo: Mutex<(SimpleList, (H256, u64))>, +} + +impl Memoized { + /// Denote that the + pub fn use_memo_at(&self, list: SimpleList) +} + +impl ValidatorSet for Memoized { + fn as_memoized(&self) -> Option<&Memoized> { Some(self) } +} + diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index 5027f23cd..8527a8aa9 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -40,7 +40,7 @@ impl Multi { } } - fn correct_set(&self, bh: &H256) -> Option<&Box> { + fn correct_set(&self, bh: &H256) -> Option<&ValidatorSet> { match self .block_number .read()(bh) @@ -55,7 +55,7 @@ impl Multi { ) { Ok((block, set)) => { trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block); - Some(set) + Some(&*set) }, Err(e) => { debug!(target: "engine", "ValidatorSet could not be recovered: {}", e); @@ -66,6 +66,21 @@ impl Multi { } impl ValidatorSet for Multi { + fn has_possibly_changed(&self, header: &Header) -> bool { + // if the sets are the same for each header, compare those. + // otherwise, the sets have almost certainly changed. + match (self.correct_set(&header.hash()), self.correct_set(header.parent_hash())) { + (Some(a), Some(b)) if a as *const _ == b as *const _ => { a.has_possibly_changed(header) }, + _ => true, + } + } + + fn has_changed(&self, header: &Header, receipts: &[Receipt]) -> Option> { + + } + + fn fetch(&self) -> + fn contains(&self, bh: &H256, address: &Address) -> bool { self.correct_set(bh).map_or(false, |set| set.contains(bh, address)) } diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index 2d7687979..cc4259fac 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -22,13 +22,11 @@ use super::ValidatorSet; #[derive(Debug, PartialEq, Eq, Default)] pub struct SimpleList { validators: Vec
, - validator_n: usize, } impl SimpleList { pub fn new(validators: Vec
) -> Self { SimpleList { - validator_n: validators.len(), validators: validators, } } @@ -36,7 +34,7 @@ impl SimpleList { impl HeapSizeOf for SimpleList { fn heap_size_of_children(&self) -> usize { - self.validators.heap_size_of_children() + self.validator_n.heap_size_of_children() + self.validators.heap_size_of_children() } } @@ -46,11 +44,12 @@ impl ValidatorSet for SimpleList { } fn get(&self, _bh: &H256, nonce: usize) -> Address { - self.validators.get(nonce % self.validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone() + let validator_n = self.validators.len(); + self.validators.get(nonce % validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone() } fn count(&self, _bh: &H256) -> usize { - self.validator_n + self.validators.len() } } From 2d8a8bd3e5fcd77e3c81165268e6b4729a85e5bf Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 12 Apr 2017 14:41:19 +0200 Subject: [PATCH 012/117] engine changes --- ethcore/src/block.rs | 5 +-- ethcore/src/engines/authority_round.rs | 4 +- ethcore/src/engines/basic_authority.rs | 2 +- ethcore/src/engines/instant_seal.rs | 2 +- ethcore/src/engines/mod.rs | 52 ++++++++++++++-------- ethcore/src/engines/tendermint/mod.rs | 2 +- ethcore/src/engines/validator_set/mod.rs | 39 ---------------- ethcore/src/engines/validator_set/multi.rs | 17 +------ ethcore/src/miner/miner.rs | 2 +- ethcore/src/snapshot/mod.rs | 2 +- 10 files changed, 43 insertions(+), 84 deletions(-) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 6ec2584ec..8da7c14b2 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -26,7 +26,7 @@ use util::error::{Mismatch, OutOfBounds}; use basic_types::{LogBloom, Seal}; use env_info::{EnvInfo, LastHashes}; -use engines::{Engine, ValidationProof}; +use engines::Engine; use error::{Error, BlockError, TransactionError}; use factory::Factories; use header::Header; @@ -488,11 +488,10 @@ impl LockedBlock { self, engine: &Engine, seal: Vec, - proof: Option, ) -> Result { let mut s = self; s.block.header.set_seal(seal); - match engine.verify_block_seal(&s.block.header, proof) { + match engine.verify_block_seal(&s.block.header) { Err(e) => Err((e, s)), _ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }), } diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index acc276d21..e6cbdb531 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -464,14 +464,14 @@ mod tests { engine.set_signer(tap.clone(), addr1, "1".into()); if let Seal::Regular(seal) = engine.generate_seal(b1.block()) { - assert!(b1.clone().try_seal(engine, seal, None).is_ok()); + assert!(b1.clone().try_seal(engine, seal).is_ok()); // Second proposal is forbidden. assert!(engine.generate_seal(b1.block()) == Seal::None); } engine.set_signer(tap, addr2, "2".into()); if let Seal::Regular(seal) = engine.generate_seal(b2.block()) { - assert!(b2.clone().try_seal(engine, seal, None).is_ok()); + assert!(b2.clone().try_seal(engine, seal).is_ok()); // Second proposal is forbidden. assert!(engine.generate_seal(b2.block()) == Seal::None); } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index fe27994e4..e5a53d4e9 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -258,7 +258,7 @@ mod tests { 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(); if let Seal::Regular(seal) = engine.generate_seal(b.block()) { - assert!(b.try_seal(engine, seal, None).is_ok()); + 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 5d05dd83f..45bede9f4 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -89,7 +89,7 @@ mod tests { 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(); if let Seal::Regular(seal) = engine.generate_seal(b.block()) { - assert!(b.try_seal(engine, seal, None).is_ok()); + assert!(b.try_seal(engine, seal).is_ok()); } } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 0ae353a56..e4bc514b7 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -44,6 +44,7 @@ use evm::Schedule; use header::Header; use spec::CommonParams; use transaction::{UnverifiedTransaction, SignedTransaction}; +use receipt::Receipt; use ethkey::Signature; use util::*; @@ -61,8 +62,6 @@ pub enum EngineError { UnexpectedMessage, /// Seal field has an unexpected size. BadSealFieldSize(OutOfBounds), - /// Needs a validation proof for the given block hash before verification can continue. - NeedsValidationProof(H256), } impl fmt::Display for EngineError { @@ -74,7 +73,6 @@ impl fmt::Display for EngineError { NotAuthorized(ref address) => format!("Signer {} is not authorized.", address), UnexpectedMessage => "This Engine should not be fed messages.".into(), BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob), - NeedsValidationProof(ref hash) => format!("Needs validation proof of block {} to verify seal.", hash), }; f.write_fmt(format_args!("Engine error ({})", msg)) @@ -92,12 +90,29 @@ pub enum Seal { None, } -/// A validation proof, required for validation of a block header. -pub type ValidationProof = Vec; - /// Type alias for a function we can make calls through synchronously. pub type Call = Fn(Address, Bytes) -> Result; +/// Results of a query of whether a validation proof is necessary at a block. +pub enum RequiresProof { + /// Cannot determine until more data is passed. + Unsure(Unsure), + /// Validation proof not required. + No, + /// Validation proof required. + Yes, +} + +/// More data required to determine if a validation proof is required. +pub enum Unsure { + /// Needs the body. + NeedsBody, + /// Needs the receipts. + NeedsReceipts, + /// Needs both body and receipts. + NeedsBoth, +} + /// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. /// Provides hooks into each of the major parts of block import. pub trait Engine : Sync + Send { @@ -191,23 +206,22 @@ pub trait Engine : Sync + Send { /// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods /// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer /// methods are needed for an Engine, this may be overridden. - fn verify_block_seal(&self, header: &Header, _proof: Option) -> Result<(), Error> { + fn verify_block_seal(&self, header: &Header) -> Result<(), Error> { self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None)) } - /// Generate a validation proof for the given block header. + /// Re-do all verification for a header with the given contract-calling interface /// - /// All values queried during execution of given will go into the proof. - /// This may only be called for blocks indicated in "needs validation proof" - /// errors. - /// - /// Engines which don't draw consensus information from the state (e.g. PoW) - /// don't need to change anything here. - /// - /// Engines which do draw consensus information from the state may only do so - /// here. - fn generate_validation_proof(&self, header: &Header, _call: &Call) -> ValidationProof { - ValidationProof::default() + /// This will be used to generate proofs of validation as well as verify them. + fn verify_with_state(&self, _header: &Header, _call: &Call) -> Result<(), Error> { + Ok(()) + } + + /// Whether a proof is required for the given header. + fn proof_required(&self, _header: Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>) + -> RequiresProof + { + RequiresProof::No } /// Populate a header's fields based on its parent's header. diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index e88803ce2..8c8094117 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -864,7 +864,7 @@ mod tests { let proposer = insert_and_register(&tap, spec.engine.as_ref(), "1"); let (b, seal) = propose_default(&spec, proposer); - assert!(b.lock().try_seal(spec.engine.as_ref(), seal, None).is_ok()); + assert!(b.lock().try_seal(spec.engine.as_ref(), seal).is_ok()); } #[test] diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index a2f6d1075..2c00c43b0 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -25,15 +25,12 @@ use std::sync::Weak; use util::{Address, H256}; use ethjson::spec::ValidatorSet as ValidatorSpec; use client::Client; -use receipt::Receipt; use self::simple_list::SimpleList; use self::contract::ValidatorContract; use self::safe_contract::ValidatorSafeContract; use self::multi::Multi; -pub type Call = Fn(Address, Vec) -> Result, String>; - /// Creates a validator set from spec. pub fn new_validator_set(spec: ValidatorSpec) -> Box { match spec { @@ -48,10 +45,6 @@ pub fn new_validator_set(spec: ValidatorSpec) -> Box { /// A validator set. pub trait ValidatorSet: Send + Sync { - /// Get this validator set as a flexible validator set. - /// Returning `None` indicates this is only usable for - /// full nodes. - fn as_memoized(&self) -> Option<&Memoized> { None } /// Checks if a given address is a validator. fn contains(&self, parent_block_hash: &H256, address: &Address) -> bool; /// Draws an validator nonce modulo number of validators. @@ -65,35 +58,3 @@ pub trait ValidatorSet: Send + Sync { /// Allows blockchain state access. fn register_contract(&self, _client: Weak) {} } - -/// A flexible validator set can track its changes. -pub trait FlexibleValidatorSet: Send + Sync { - /// Whether a validator set may have changed at this header. - fn has_possibly_changed(&self, header: &Header) -> bool; - - /// Whether a validator set has changed at this header, given the block receipts. - /// Called after `has_possibly_changed`. - /// `Some` indicates the validator set has changed at this header and the new - /// expected validator set. - /// `None` indicates no change. - fn has_changed(&self, header: &Header, receipts: &[Receipt]) -> Option; - - /// Fetch validators at a block synchronously. - fn fetch_validators(&self, header: &Header, call: &Call) -> Result; -} - -/// A memoized flexible validator set -pub struct Memoized { - inner: Box, - memo: Mutex<(SimpleList, (H256, u64))>, -} - -impl Memoized { - /// Denote that the - pub fn use_memo_at(&self, list: SimpleList) -} - -impl ValidatorSet for Memoized { - fn as_memoized(&self) -> Option<&Memoized> { Some(self) } -} - diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index 8527a8aa9..3ae93b0f5 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -55,7 +55,7 @@ impl Multi { ) { Ok((block, set)) => { trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block); - Some(&*set) + Some(&**set) }, Err(e) => { debug!(target: "engine", "ValidatorSet could not be recovered: {}", e); @@ -66,21 +66,6 @@ impl Multi { } impl ValidatorSet for Multi { - fn has_possibly_changed(&self, header: &Header) -> bool { - // if the sets are the same for each header, compare those. - // otherwise, the sets have almost certainly changed. - match (self.correct_set(&header.hash()), self.correct_set(header.parent_hash())) { - (Some(a), Some(b)) if a as *const _ == b as *const _ => { a.has_possibly_changed(header) }, - _ => true, - } - } - - fn has_changed(&self, header: &Header, receipts: &[Receipt]) -> Option> { - - } - - fn fetch(&self) -> - fn contains(&self, bh: &H256, address: &Address) -> bool { self.correct_set(bh).map_or(false, |set| set.contains(bh, address)) } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 8337ef2d8..ac1695b52 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1133,7 +1133,7 @@ impl MinerService for Miner { |b| &b.hash() == &block_hash ) { trace!(target: "miner", "Submitted block {}={}={} with seal {:?}", block_hash, b.hash(), b.header().bare_hash(), seal); - b.lock().try_seal(&*self.engine, seal, None).or_else(|(e, _)| { + b.lock().try_seal(&*self.engine, seal).or_else(|(e, _)| { warn!(target: "miner", "Mined solution rejected: {}", e); Err(Error::PowInvalid) }) diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 0ef263476..1c3b4366b 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -567,7 +567,7 @@ pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &Engine, chain if always || rng.gen::() <= POW_VERIFY_RATE { match chain.block_header(header.parent_hash()) { Some(parent) => engine.verify_block_family(header, &parent, body), - None => engine.verify_block_seal(header, None), // TODO: fetch validation proof as necessary. + None => engine.verify_block_seal(header), // TODO: fetch validation proof as necessary. } } else { engine.verify_block_basic(header, body) From 2f5a774325f59da2f647ac336abf8aabccd58c93 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 12 Apr 2017 15:54:23 +0200 Subject: [PATCH 013/117] add validator contracts to native_contracts bin --- ethcore/native_contracts/build.rs | 8 ++++++++ ethcore/native_contracts/generator/src/lib.rs | 4 ++-- ethcore/native_contracts/src/lib.rs | 4 ++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ethcore/native_contracts/build.rs b/ethcore/native_contracts/build.rs index 91eaa86cd..0a080e801 100644 --- a/ethcore/native_contracts/build.rs +++ b/ethcore/native_contracts/build.rs @@ -22,9 +22,15 @@ use std::io::Write; // TODO: `include!` these from files where they're pretty-printed? const REGISTRY_ABI: &'static str = r#"[{"constant":true,"inputs":[{"name":"_data","type":"address"}],"name":"canReverse","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"bytes32"}],"name":"setData","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"}],"name":"confirmReverse","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[{"name":"success","type":"bool"}],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"drop","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"setFee","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_to","type":"address"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getData","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserved","outputs":[{"name":"reserved","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"drain","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_who","type":"address"}],"name":"proposeReverse","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"hasReverse","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"getOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"getReverse","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_data","type":"address"}],"name":"reverse","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"uint256"}],"name":"setUint","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_who","type":"address"}],"name":"confirmReverseAs","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"removeReverse","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"address"}],"name":"setAddress","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}]"#; + const SERVICE_TRANSACTION_ABI: &'static str = r#"[{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"certify","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"revoke","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"delegate","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setDelegate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"}],"name":"certified","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"}]"#; + const SECRETSTORE_ACL_STORAGE_ABI: &'static str = r#"[{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"document","type":"bytes32"}],"name":"checkPermissions","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"}]"#; +const VALIDATOR_SET_ABI: &'static str = r#"[{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]"#; + +const VALIDATOR_REPORT_ABI: &'static str = r#"[{"constant":false,"inputs":[{"name":"validator","type":"address"}],"name":"reportMalicious","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"validator","type":"address"}],"name":"reportBenign","outputs":[],"payable":false,"type":"function"}]"#; + fn build_file(name: &str, abi: &str, filename: &str) { let code = ::native_contract_generator::generate_module(name, abi).unwrap(); @@ -39,4 +45,6 @@ fn main() { build_file("Registry", REGISTRY_ABI, "registry.rs"); build_file("ServiceTransactionChecker", SERVICE_TRANSACTION_ABI, "service_transaction.rs"); build_file("SecretStoreAclStorage", SECRETSTORE_ACL_STORAGE_ABI, "secretstore_acl_storage.rs"); + build_file("ValidatorSet", VALIDATOR_SET_ABI, "validator_set.rs"); + build_file("ValidatorReport", VALIDATOR_REPORT_ABI, "validator_report.rs"); } diff --git a/ethcore/native_contracts/generator/src/lib.rs b/ethcore/native_contracts/generator/src/lib.rs index f49caf227..363e76ea1 100644 --- a/ethcore/native_contracts/generator/src/lib.rs +++ b/ethcore/native_contracts/generator/src/lib.rs @@ -299,10 +299,10 @@ fn detokenize(name: &str, output_type: ParamType) -> String { ParamType::Bool => format!("{}.to_bool()", name), ParamType::String => format!("{}.to_string()", name), ParamType::Array(kind) => { - let read_array = format!("x.into_iter().map(|a| {{ {} }}).collect::>()", + let read_array = format!("x.into_iter().map(|a| {{ {} }}).collect::>>()", detokenize("a", *kind)); - format!("{}.to_array().and_then(|x| {})", + format!("{}.to_array().and_then(|x| {{ {} }})", name, read_array) } ParamType::FixedArray(_, _) => panic!("Fixed-length arrays not supported.") diff --git a/ethcore/native_contracts/src/lib.rs b/ethcore/native_contracts/src/lib.rs index e894a636f..1aaaf07b1 100644 --- a/ethcore/native_contracts/src/lib.rs +++ b/ethcore/native_contracts/src/lib.rs @@ -26,7 +26,11 @@ extern crate ethcore_util as util; mod registry; mod service_transaction; mod secretstore_acl_storage; +mod validator_set; +mod validator_report; pub use self::registry::Registry; pub use self::service_transaction::ServiceTransactionChecker; pub use self::secretstore_acl_storage::SecretStoreAclStorage; +pub use self::validator_set::ValidatorSet; +pub use self::validator_report::ValidatorReport; From 0f80c57dcaac179b29b077ece375ee91d0886ba2 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 12 Apr 2017 16:15:35 +0200 Subject: [PATCH 014/117] use native contracts in `ValidatorSet` --- ethcore/native_contracts/generator/src/lib.rs | 8 +- .../native_contracts/src/validator_report.rs | 22 ++++ ethcore/native_contracts/src/validator_set.rs | 22 ++++ ethcore/src/engines/validator_set/contract.rs | 104 +++++------------- .../engines/validator_set/safe_contract.rs | 96 +++++----------- 5 files changed, 107 insertions(+), 145 deletions(-) create mode 100644 ethcore/native_contracts/src/validator_report.rs create mode 100644 ethcore/native_contracts/src/validator_set.rs diff --git a/ethcore/native_contracts/generator/src/lib.rs b/ethcore/native_contracts/generator/src/lib.rs index 363e76ea1..28d658cb3 100644 --- a/ethcore/native_contracts/generator/src/lib.rs +++ b/ethcore/native_contracts/generator/src/lib.rs @@ -46,7 +46,7 @@ pub fn generate_module(struct_name: &str, abi: &str) -> Result { Ok(format!(r##" use byteorder::{{BigEndian, ByteOrder}}; -use futures::{{future, Future, BoxFuture}}; +use futures::{{future, Future, IntoFuture, BoxFuture}}; use ethabi::{{Contract, Interface, Token}}; use util::{{self, Uint}}; @@ -99,7 +99,10 @@ fn generate_functions(contract: &Contract) -> Result { /// Inputs: {abi_inputs:?} /// Outputs: {abi_outputs:?} pub fn {snake_name}(&self, call: F, {params}) -> BoxFuture<{output_type}, String> - where F: Fn(util::Address, Vec) -> U, U: Future, Error=String> + Send + 'static + where + F: Fn(util::Address, Vec) -> U, + U: IntoFuture, Error=String>, + U::Future: Send + 'static {{ let function = self.contract.function(r#"{abi_name}"#.to_string()) .expect("function existence checked at compile-time; qed"); @@ -111,6 +114,7 @@ pub fn {snake_name}(&self, call: F, {params}) -> BoxFuture<{output_type}, }}; call_future + .into_future() .and_then(move |out| function.decode_output(out).map_err(|e| format!("{{:?}}", e))) .map(::std::collections::VecDeque::from) .and_then(|mut outputs| {decode_outputs}) diff --git a/ethcore/native_contracts/src/validator_report.rs b/ethcore/native_contracts/src/validator_report.rs new file mode 100644 index 000000000..d77b07c71 --- /dev/null +++ b/ethcore/native_contracts/src/validator_report.rs @@ -0,0 +1,22 @@ +// Copyright 2015-2017 Parity Technologies (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 . + +#![allow(unused_mut, unused_variables, unused_imports)] + +//! Validator reporting. +// TODO: testing. + +include!(concat!(env!("OUT_DIR"), "/validator_report.rs")); diff --git a/ethcore/native_contracts/src/validator_set.rs b/ethcore/native_contracts/src/validator_set.rs new file mode 100644 index 000000000..0a69913e0 --- /dev/null +++ b/ethcore/native_contracts/src/validator_set.rs @@ -0,0 +1,22 @@ +// Copyright 2015-2017 Parity Technologies (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 . + +#![allow(unused_mut, unused_variables, unused_imports)] + +//! Validator set contract. +// TODO: testing. + +include!(concat!(env!("OUT_DIR"), "/validator_set.rs")); diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index adfdd1225..c45e8c479 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -18,27 +18,46 @@ /// It can also report validators for misbehaviour with two levels: `reportMalicious` and `reportBenign`. use std::sync::Weak; +use futures::Future; use util::*; use client::{Client, BlockChainClient}; + +use native_contracts::ValidatorReport as Provider; + use super::ValidatorSet; use super::safe_contract::ValidatorSafeContract; -/// The validator contract should have the following interface: -/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}] +/// A validator contract with reporting. pub struct ValidatorContract { validators: ValidatorSafeContract, - provider: RwLock>, + provider: Provider, + client: RwLock>>, // TODO [keorn]: remove } impl ValidatorContract { pub fn new(contract_address: Address) -> Self { ValidatorContract { validators: ValidatorSafeContract::new(contract_address), - provider: RwLock::new(None), + provider: Provider::new(contract_address), + client: RwLock::new(None), } } } +impl ValidatorContract { + // could be `impl Trait`. + // note: dispatches transactions to network as well as execute. + // TODO [keorn]: Make more general. + fn transact(&self) -> Box) -> Result, String>> { + let client = self.client.read().clone(); + Box::new(move |a, d| client.as_ref() + .and_then(Weak::upgrade) + .ok_or("No client!".into()) + .and_then(|c| c.transact_contract(a, d).map_err(|e| format!("Transaction import error: {}", e))) + .map(|_| Default::default())) + } +} + impl ValidatorSet for ValidatorContract { fn contains(&self, bh: &H256, address: &Address) -> bool { self.validators.contains(bh, address) @@ -53,85 +72,22 @@ impl ValidatorSet for ValidatorContract { } fn report_malicious(&self, address: &Address) { - if let Some(ref provider) = *self.provider.read() { - match provider.report_malicious(address) { - Ok(_) => warn!(target: "engine", "Reported malicious validator {}", address), - Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s), - } - } else { - warn!(target: "engine", "Malicious behaviour could not be reported: no provider contract.") + match self.provider.report_malicious(&*self.transact(), *address).wait() { + Ok(_) => warn!(target: "engine", "Reported malicious validator {}", address), + Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s), } } fn report_benign(&self, address: &Address) { - if let Some(ref provider) = *self.provider.read() { - match provider.report_benign(address) { - Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address), - Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s), - } - } else { - warn!(target: "engine", "Benign misbehaviour could not be reported: no provider contract.") + match self.provider.report_benign(&*self.transact(), *address).wait() { + Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address), + Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s), } } fn register_contract(&self, client: Weak) { self.validators.register_contract(client.clone()); - let transact = move |a, d| client - .upgrade() - .ok_or("No client!".into()) - .and_then(|c| c.transact_contract(a, d).map_err(|e| format!("Transaction import error: {}", e))) - .map(|_| Default::default()); - *self.provider.write() = Some(provider::Contract::new(self.validators.address, transact)); - } -} - -mod provider { - // Autogenerated from JSON contract definition using Rust contract convertor. - #![allow(unused_imports)] - use std::string::String; - use std::result::Result; - use std::fmt; - use {util, ethabi}; - use util::{Uint}; - - pub struct Contract { - contract: ethabi::Contract, - address: util::Address, - do_call: Box) -> Result, String> + Send + Sync + 'static>, - } - impl Contract { - pub fn new(address: util::Address, do_call: F) -> Self where F: Fn(util::Address, Vec) -> Result, String> + Send + Sync + 'static { - Contract { - contract: ethabi::Contract::new(ethabi::Interface::load(b"[{\"constant\":false,\"inputs\":[{\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"reportMalicious\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"reportBenign\",\"outputs\":[],\"payable\":false,\"type\":\"function\"}]").expect("JSON is autogenerated; qed")), - address: address, - do_call: Box::new(do_call), - } - } - fn as_string(e: T) -> String { format!("{:?}", e) } - - /// Auto-generated from: `{"constant":false,"inputs":[{"name":"validator","type":"address"}],"name":"reportMalicious","outputs":[],"payable":false,"type":"function"}` - #[allow(dead_code)] - pub fn report_malicious(&self, validator: &util::Address) -> Result<(), String> { - let call = self.contract.function("reportMalicious".into()).map_err(Self::as_string)?; - let data = call.encode_call( - vec![ethabi::Token::Address(validator.clone().0)] - ).map_err(Self::as_string)?; - call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; - - Ok(()) - } - - /// Auto-generated from: `{"constant":false,"inputs":[{"name":"validator","type":"address"}],"name":"reportBenign","outputs":[],"payable":false,"type":"function"}` - #[allow(dead_code)] - pub fn report_benign(&self, validator: &util::Address) -> Result<(), String> { - let call = self.contract.function("reportBenign".into()).map_err(Self::as_string)?; - let data = call.encode_call( - vec![ethabi::Token::Address(validator.clone().0)] - ).map_err(Self::as_string)?; - call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; - - Ok(()) - } + *self.client.write() = Some(client); } } diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 0a0eaecfd..958f3e0a0 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -17,24 +17,26 @@ /// Validator set maintained in a contract, updated using `getValidators` method. use std::sync::Weak; -use ethabi; +use futures::Future; +use native_contracts::ValidatorSet as Provider; + use util::*; use util::cache::MemoryLruCache; + use types::ids::BlockId; use client::{Client, BlockChainClient}; + use super::ValidatorSet; use super::simple_list::SimpleList; const MEMOIZE_CAPACITY: usize = 500; -const CONTRACT_INTERFACE: &'static [u8] = b"[{\"constant\":true,\"inputs\":[],\"name\":\"getValidators\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"type\":\"function\"}]"; -const GET_VALIDATORS: &'static str = "getValidators"; /// The validator contract should have the following interface: -/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}] pub struct ValidatorSafeContract { pub address: Address, validators: RwLock>, - provider: RwLock>, + provider: Provider, + client: RwLock>>, // TODO [keorn]: remove } impl ValidatorSafeContract { @@ -42,26 +44,30 @@ impl ValidatorSafeContract { ValidatorSafeContract { address: contract_address, validators: RwLock::new(MemoryLruCache::new(MEMOIZE_CAPACITY)), - provider: RwLock::new(None), + provider: Provider::new(contract_address), + client: RwLock::new(None), } } + fn do_call(&self, id: BlockId) -> Box) -> Result, String>> { + let client = self.client.read().clone(); + Box::new(move |addr, data| client.as_ref() + .and_then(Weak::upgrade) + .ok_or("No client!".into()) + .and_then(|c| c.call_contract(id, addr, data))) + } + /// Queries the state and gets the set of validators. fn get_list(&self, block_hash: H256) -> Option { - if let Some(ref provider) = *self.provider.read() { - match provider.get_validators(BlockId::Hash(block_hash)) { - Ok(new) => { - debug!(target: "engine", "Set of validators obtained: {:?}", new); - Some(SimpleList::new(new)) - }, - Err(s) => { - debug!(target: "engine", "Set of validators could not be updated: {}", s); - None - }, - } - } else { - warn!(target: "engine", "Set of validators could not be updated: no provider contract."); - None + match self.provider.get_validators(&*self.do_call(BlockId::Hash(block_hash))).wait() { + Ok(new) => { + debug!(target: "engine", "Set of validators obtained: {:?}", new); + Some(SimpleList::new(new)) + }, + Err(s) => { + debug!(target: "engine", "Set of validators could not be updated: {}", s); + None + }, } } } @@ -114,55 +120,7 @@ impl ValidatorSet for ValidatorSafeContract { fn register_contract(&self, client: Weak) { trace!(target: "engine", "Setting up contract caller."); - let contract = ethabi::Contract::new(ethabi::Interface::load(CONTRACT_INTERFACE).expect("JSON interface is valid; qed")); - let call = contract.function(GET_VALIDATORS.into()).expect("Method name is valid; qed"); - let data = call.encode_call(vec![]).expect("get_validators does not take any arguments; qed"); - let contract_address = self.address.clone(); - let do_call = move |id| client - .upgrade() - .ok_or("No client!".into()) - .and_then(|c| c.call_contract(id, contract_address.clone(), data.clone())) - .map(|raw_output| call.decode_output(raw_output).expect("ethabi is correct; qed")); - *self.provider.write() = Some(provider::Contract::new(do_call)); - } -} - -mod provider { - use std::string::String; - use std::result::Result; - use {util, ethabi}; - use types::ids::BlockId; - - pub struct Contract { - do_call: Box Result, String> + Send + Sync + 'static>, - } - - impl Contract { - pub fn new(do_call: F) -> Self where F: Fn(BlockId) -> Result, String> + Send + Sync + 'static { - Contract { - do_call: Box::new(do_call), - } - } - - /// Gets validators from contract with interface: `{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}` - pub fn get_validators(&self, id: BlockId) -> Result, String> { - Ok((self.do_call)(id)? - .into_iter() - .rev() - .collect::>() - .pop() - .expect("get_validators returns one argument; qed") - .to_array() - .and_then(|v| v - .into_iter() - .map(|a| a.to_address()) - .collect::>>()) - .expect("get_validators returns a list of addresses; qed") - .into_iter() - .map(util::Address::from) - .collect::>() - ) - } + *self.client.write() = Some(client); } } From ec922ee5e4d954a5acffe44e62c8a6ba85954e4d Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 12 Apr 2017 16:42:19 +0200 Subject: [PATCH 015/117] supply optional call context to validator sets --- ethcore/src/engines/validator_set/contract.rs | 24 +++++++----- ethcore/src/engines/validator_set/mod.rs | 37 +++++++++++++++++-- ethcore/src/engines/validator_set/multi.rs | 31 ++++++++++------ .../engines/validator_set/safe_contract.rs | 33 +++++++++-------- .../src/engines/validator_set/simple_list.rs | 12 ++++-- 5 files changed, 94 insertions(+), 43 deletions(-) diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index c45e8c479..12d816246 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -18,12 +18,14 @@ /// It can also report validators for misbehaviour with two levels: `reportMalicious` and `reportBenign`. use std::sync::Weak; -use futures::Future; use util::*; -use client::{Client, BlockChainClient}; +use futures::Future; use native_contracts::ValidatorReport as Provider; +use client::{Client, BlockChainClient}; +use engines::Call; + use super::ValidatorSet; use super::safe_contract::ValidatorSafeContract; @@ -48,7 +50,7 @@ impl ValidatorContract { // could be `impl Trait`. // note: dispatches transactions to network as well as execute. // TODO [keorn]: Make more general. - fn transact(&self) -> Box) -> Result, String>> { + fn transact(&self) -> Box { let client = self.client.read().clone(); Box::new(move |a, d| client.as_ref() .and_then(Weak::upgrade) @@ -59,16 +61,20 @@ impl ValidatorContract { } impl ValidatorSet for ValidatorContract { - fn contains(&self, bh: &H256, address: &Address) -> bool { - self.validators.contains(bh, address) + fn default_caller(&self, id: ::ids::BlockId) -> Box { + self.validators.default_caller(id) } - fn get(&self, bh: &H256, nonce: usize) -> Address { - self.validators.get(bh, nonce) + fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { + self.validators.contains_with_caller(bh, address, caller) } - fn count(&self, bh: &H256) -> usize { - self.validators.count(bh) + fn get_with_caller(&self, bh: &H256, nonce: usize, caller: &Call) -> Address { + self.validators.get_with_caller(bh, nonce, caller) + } + + fn count_with_caller(&self, bh: &H256, caller: &Call) -> usize { + self.validators.count_with_caller(bh, caller) } fn report_malicious(&self, address: &Address) { diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index 2c00c43b0..707b5adf5 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -22,6 +22,7 @@ mod contract; mod multi; use std::sync::Weak; +use ids::BlockId; use util::{Address, H256}; use ethjson::spec::ValidatorSet as ValidatorSpec; use client::Client; @@ -31,6 +32,8 @@ use self::contract::ValidatorContract; use self::safe_contract::ValidatorSafeContract; use self::multi::Multi; +use super::Call; + /// Creates a validator set from spec. pub fn new_validator_set(spec: ValidatorSpec) -> Box { match spec { @@ -44,13 +47,39 @@ pub fn new_validator_set(spec: ValidatorSpec) -> Box { } /// A validator set. +// TODO [keorn]: remove internal callers. pub trait ValidatorSet: Send + Sync { - /// Checks if a given address is a validator. - fn contains(&self, parent_block_hash: &H256, address: &Address) -> bool; + /// Get the default "Call" helper, for use in general operation. + fn default_caller(&self, block_id: BlockId) -> Box; + + /// Checks if a given address is a validator, + /// using underlying, default call mechanism. + fn contains(&self, parent: &H256, address: &Address) -> bool { + let default = self.default_caller(BlockId::Hash(*parent)); + self.contains_with_caller(parent, address, &*default) + } /// Draws an validator nonce modulo number of validators. - fn get(&self, parent_block_hash: &H256, nonce: usize) -> Address; + fn get(&self, parent: &H256, nonce: usize) -> Address { + let default = self.default_caller(BlockId::Hash(*parent)); + self.get_with_caller(parent, nonce, &*default) + } /// Returns the current number of validators. - fn count(&self, parent_block_hash: &H256) -> usize; + fn count(&self, parent: &H256) -> usize { + let default = self.default_caller(BlockId::Hash(*parent)); + self.count_with_caller(parent, &*default) + } + + /// Checks if a given address is a validator, with the given function + /// for executing synchronous calls to contracts. + fn contains_with_caller(&self, parent_block_hash: &H256, address: &Address, caller: &Call) -> bool; + + /// Draws an validator nonce modulo number of validators. + /// + fn get_with_caller(&self, parent_block_hash: &H256, nonce: usize, caller: &Call) -> Address; + + /// Returns the current number of validators. + fn count_with_caller(&self, parent_block_hash: &H256, caller: &Call) -> usize; + /// Notifies about malicious behaviour. fn report_malicious(&self, _validator: &Address) {} /// Notifies about benign misbehaviour. diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index 3ae93b0f5..f098ca343 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -18,13 +18,14 @@ use std::collections::BTreeMap; use std::sync::Weak; +use engines::Call; use util::{H256, Address, RwLock}; use ids::BlockId; use header::BlockNumber; use client::{Client, BlockChainClient}; use super::ValidatorSet; -type BlockNumberLookup = Box Result + Send + Sync + 'static>; +type BlockNumberLookup = Box Result + Send + Sync + 'static>; pub struct Multi { sets: BTreeMap>, @@ -40,10 +41,10 @@ impl Multi { } } - fn correct_set(&self, bh: &H256) -> Option<&ValidatorSet> { + fn correct_set(&self, id: BlockId) -> Option<&ValidatorSet> { match self .block_number - .read()(bh) + .read()(id) .map(|parent_block| self .sets .iter() @@ -66,16 +67,24 @@ impl Multi { } impl ValidatorSet for Multi { - fn contains(&self, bh: &H256, address: &Address) -> bool { - self.correct_set(bh).map_or(false, |set| set.contains(bh, address)) + fn default_caller(&self, block_id: BlockId) -> Box { + self.correct_set(block_id).map(|set| set.default_caller(block_id)) + .unwrap_or(Box::new(|_, _| Err("No validator set for given ID.".into()))) } - fn get(&self, bh: &H256, nonce: usize) -> Address { - self.correct_set(bh).map_or_else(Default::default, |set| set.get(bh, nonce)) + fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { + self.correct_set(BlockId::Hash(*bh)) + .map_or(false, |set| set.contains_with_caller(bh, address, caller)) } - fn count(&self, bh: &H256) -> usize { - self.correct_set(bh).map_or_else(usize::max_value, |set| set.count(bh)) + fn get_with_caller(&self, bh: &H256, nonce: usize, caller: &Call) -> Address { + self.correct_set(BlockId::Hash(*bh)) + .map_or_else(Default::default, |set| set.get_with_caller(bh, nonce, caller)) + } + + fn count_with_caller(&self, bh: &H256, caller: &Call) -> usize { + self.correct_set(BlockId::Hash(*bh)) + .map_or_else(usize::max_value, |set| set.count_with_caller(bh, caller)) } fn report_malicious(&self, validator: &Address) { @@ -94,10 +103,10 @@ impl ValidatorSet for Multi { for set in self.sets.values() { set.register_contract(client.clone()); } - *self.block_number.write() = Box::new(move |hash| client + *self.block_number.write() = Box::new(move |id| client .upgrade() .ok_or("No client!".into()) - .and_then(|c| c.block_number(BlockId::Hash(*hash)).ok_or("Unknown block".into()))); + .and_then(|c| c.block_number(id).ok_or("Unknown block".into()))); } } diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 958f3e0a0..8a0599dde 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -23,6 +23,7 @@ use native_contracts::ValidatorSet as Provider; use util::*; use util::cache::MemoryLruCache; +use engines::Call; use types::ids::BlockId; use client::{Client, BlockChainClient}; @@ -49,17 +50,9 @@ impl ValidatorSafeContract { } } - fn do_call(&self, id: BlockId) -> Box) -> Result, String>> { - let client = self.client.read().clone(); - Box::new(move |addr, data| client.as_ref() - .and_then(Weak::upgrade) - .ok_or("No client!".into()) - .and_then(|c| c.call_contract(id, addr, data))) - } - /// Queries the state and gets the set of validators. - fn get_list(&self, block_hash: H256) -> Option { - match self.provider.get_validators(&*self.do_call(BlockId::Hash(block_hash))).wait() { + fn get_list(&self, caller: &Call) -> Option { + match self.provider.get_validators(caller).wait() { Ok(new) => { debug!(target: "engine", "Set of validators obtained: {:?}", new); Some(SimpleList::new(new)) @@ -73,14 +66,22 @@ impl ValidatorSafeContract { } impl ValidatorSet for ValidatorSafeContract { - fn contains(&self, block_hash: &H256, address: &Address) -> bool { + fn default_caller(&self, id: BlockId) -> Box { + let client = self.client.read().clone(); + Box::new(move |addr, data| client.as_ref() + .and_then(Weak::upgrade) + .ok_or("No client!".into()) + .and_then(|c| c.call_contract(id, addr, data))) + } + + fn contains_with_caller(&self, block_hash: &H256, address: &Address, caller: &Call) -> bool { let mut guard = self.validators.write(); let maybe_existing = guard .get_mut(block_hash) .map(|list| list.contains(block_hash, address)); maybe_existing .unwrap_or_else(|| self - .get_list(block_hash.clone()) + .get_list(caller) .map_or(false, |list| { let contains = list.contains(block_hash, address); guard.insert(block_hash.clone(), list); @@ -88,14 +89,14 @@ impl ValidatorSet for ValidatorSafeContract { })) } - fn get(&self, block_hash: &H256, nonce: usize) -> Address { + fn get_with_caller(&self, block_hash: &H256, nonce: usize, caller: &Call) -> Address { let mut guard = self.validators.write(); let maybe_existing = guard .get_mut(block_hash) .map(|list| list.get(block_hash, nonce)); maybe_existing .unwrap_or_else(|| self - .get_list(block_hash.clone()) + .get_list(caller) .map_or_else(Default::default, |list| { let address = list.get(block_hash, nonce); guard.insert(block_hash.clone(), list); @@ -103,14 +104,14 @@ impl ValidatorSet for ValidatorSafeContract { })) } - fn count(&self, block_hash: &H256) -> usize { + fn count_with_caller(&self, block_hash: &H256, caller: &Call) -> usize { let mut guard = self.validators.write(); let maybe_existing = guard .get_mut(block_hash) .map(|list| list.count(block_hash)); maybe_existing .unwrap_or_else(|| self - .get_list(block_hash.clone()) + .get_list(caller) .map_or_else(usize::max_value, |list| { let address = list.count(block_hash); guard.insert(block_hash.clone(), list); diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index cc4259fac..ac82cf76f 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -17,6 +17,8 @@ /// Preconfigured validator list. use util::{H256, Address, HeapSizeOf}; + +use engines::Call; use super::ValidatorSet; #[derive(Debug, PartialEq, Eq, Default)] @@ -39,16 +41,20 @@ impl HeapSizeOf for SimpleList { } impl ValidatorSet for SimpleList { - fn contains(&self, _bh: &H256, address: &Address) -> bool { + fn default_caller(&self, _block_id: ::ids::BlockId) -> Box { + Box::new(|_, _| Err("Simple list doesn't require calls.".into())) + } + + fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool { self.validators.contains(address) } - fn get(&self, _bh: &H256, nonce: usize) -> Address { + fn get_with_caller(&self, _bh: &H256, nonce: usize, _: &Call) -> Address { let validator_n = self.validators.len(); self.validators.get(nonce % validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone() } - fn count(&self, _bh: &H256) -> usize { + fn count_with_caller(&self, _bh: &H256, _: &Call) -> usize { self.validators.len() } } From 34a1512ff07b74993ec45206e4a5af9612425fc0 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 12 Apr 2017 18:55:38 +0200 Subject: [PATCH 016/117] skeleton for proof checking --- ethcore/src/engines/authority_round.rs | 14 +++++- ethcore/src/engines/basic_authority.rs | 18 ++++++-- ethcore/src/engines/mod.rs | 17 ++++--- ethcore/src/engines/validator_set/contract.rs | 11 +++++ ethcore/src/engines/validator_set/mod.rs | 23 +++++++++- ethcore/src/engines/validator_set/multi.rs | 44 ++++++++++++------- .../engines/validator_set/safe_contract.rs | 14 ++++++ .../src/engines/validator_set/simple_list.rs | 17 +++++++ 8 files changed, 131 insertions(+), 27 deletions(-) diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index e6cbdb531..e57fb27bc 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -25,7 +25,7 @@ use rlp::{UntrustedRlp, encode}; use account_provider::AccountProvider; use block::*; use spec::CommonParams; -use engines::{Engine, Seal, EngineError}; +use engines::{Call, Engine, Seal, EngineError}; use header::Header; use error::{Error, TransactionError, BlockError}; use evm::Schedule; @@ -358,6 +358,18 @@ impl Engine for AuthorityRound { Ok(()) } + // the proofs we need just allow us to get the full validator set. + fn prove_with_caller(&self, header: &Header, caller: &Call) -> Result { + self.validators.generate_proof(header, caller) + .map_err(|e| EngineError::InsufficientProof(e).into()) + } + + fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> super::RequiresProof + { + self.validators.proof_required(header, block, receipts) + } + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> result::Result<(), Error> { t.check_low_s()?; diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index e5a53d4e9..a785b360a 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -23,7 +23,7 @@ use account_provider::AccountProvider; use block::*; use builtin::Builtin; use spec::CommonParams; -use engines::{Engine, Seal}; +use engines::{Engine, EngineError, Seal, Call, RequiresProof}; use env_info::EnvInfo; use error::{BlockError, Error}; use evm::Schedule; @@ -51,8 +51,7 @@ impl From for BasicAuthorityParams { } } -/// Engine using `BasicAuthority` proof-of-work consensus algorithm, suitable for Ethereum -/// mainnet chains in the Olympic, Frontier and Homestead eras. +/// Engine using `BasicAuthority`, trivial proof-of-authority consensus. pub struct BasicAuthority { params: CommonParams, gas_limit_bound_divisor: U256, @@ -139,6 +138,7 @@ impl Engine for BasicAuthority { fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { use rlp::UntrustedRlp; + // Check if the signature belongs to a validator, can depend on parent state. let sig = UntrustedRlp::new(&header.seal()[0]).as_val::()?; let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?); @@ -164,6 +164,18 @@ impl Engine for BasicAuthority { Ok(()) } + // the proofs we need just allow us to get the full validator set. + fn prove_with_caller(&self, header: &Header, caller: &Call) -> Result { + self.validators.generate_proof(header, caller) + .map_err(|e| EngineError::InsufficientProof(e).into()) + } + + fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> RequiresProof + { + self.validators.proof_required(header, block, receipts) + } + fn register_client(&self, client: Weak) { self.validators.register_contract(client); } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index e4bc514b7..f1e0a4d43 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -62,6 +62,8 @@ pub enum EngineError { UnexpectedMessage, /// Seal field has an unexpected size. BadSealFieldSize(OutOfBounds), + /// Validation proof insufficient. + InsufficientProof(String), } impl fmt::Display for EngineError { @@ -73,6 +75,7 @@ impl fmt::Display for EngineError { NotAuthorized(ref address) => format!("Signer {} is not authorized.", address), UnexpectedMessage => "This Engine should not be fed messages.".into(), BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob), + InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg), }; f.write_fmt(format_args!("Engine error ({})", msg)) @@ -99,8 +102,9 @@ pub enum RequiresProof { Unsure(Unsure), /// Validation proof not required. No, - /// Validation proof required. - Yes, + /// Validation proof required, and the expected output + /// if it can be recovered. + Yes(Option), } /// More data required to determine if a validation proof is required. @@ -213,12 +217,15 @@ pub trait Engine : Sync + Send { /// Re-do all verification for a header with the given contract-calling interface /// /// This will be used to generate proofs of validation as well as verify them. - fn verify_with_state(&self, _header: &Header, _call: &Call) -> Result<(), Error> { - Ok(()) + /// Must be called on blocks that have already passed basic verification. + /// + /// Return the "validation proof" generated. + fn prove_with_caller(&self, _header: &Header, _caller: &Call) -> Result { + Ok(Vec::new()) } /// Whether a proof is required for the given header. - fn proof_required(&self, _header: Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>) + fn proof_required(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>) -> RequiresProof { RequiresProof::No diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 12d816246..5c4af4ba7 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -25,6 +25,7 @@ use native_contracts::ValidatorReport as Provider; use client::{Client, BlockChainClient}; use engines::Call; +use header::Header; use super::ValidatorSet; use super::safe_contract::ValidatorSafeContract; @@ -65,6 +66,16 @@ impl ValidatorSet for ValidatorContract { self.validators.default_caller(id) } + fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> ::engines::RequiresProof + { + self.validators.proof_required(header, block, receipts) + } + + fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String> { + self.validators.generate_proof(header, caller) + } + fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { self.validators.contains_with_caller(bh, address, caller) } diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index 707b5adf5..d5abf430d 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -26,6 +26,7 @@ use ids::BlockId; use util::{Address, H256}; use ethjson::spec::ValidatorSet as ValidatorSpec; use client::Client; +use header::Header; use self::simple_list::SimpleList; use self::contract::ValidatorContract; @@ -47,9 +48,10 @@ pub fn new_validator_set(spec: ValidatorSpec) -> Box { } /// A validator set. -// TODO [keorn]: remove internal callers. pub trait ValidatorSet: Send + Sync { /// Get the default "Call" helper, for use in general operation. + // TODO [keorn]: this is a hack intended to migrate off of + // a strict dependency on state always being available. fn default_caller(&self, block_id: BlockId) -> Box; /// Checks if a given address is a validator, @@ -63,12 +65,31 @@ pub trait ValidatorSet: Send + Sync { let default = self.default_caller(BlockId::Hash(*parent)); self.get_with_caller(parent, nonce, &*default) } + /// Returns the current number of validators. fn count(&self, parent: &H256) -> usize { let default = self.default_caller(BlockId::Hash(*parent)); self.count_with_caller(parent, &*default) } + /// Whether a validation proof is required at the given block. + /// Usually indicates that the validator set changed at the given block. + /// + /// Should not inspect state! This is used in situations where + /// state is not generally available. + /// + /// Return `Ok` with a flag indicating whether it changed at the given header, + /// or `Unsure` indicating a need for more information. + /// + /// This may or may not be called in a loop. + fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> super::RequiresProof; + + /// Generate a validation proof at the given header. + /// Must interact with state only through the given caller! + /// Otherwise, generated proofs may be wrong. + fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String>; + /// Checks if a given address is a validator, with the given function /// for executing synchronous calls to contracts. fn contains_with_caller(&self, parent_block_hash: &H256, address: &Address, caller: &Call) -> bool; diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index f098ca343..78270c4af 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -18,10 +18,10 @@ use std::collections::BTreeMap; use std::sync::Weak; -use engines::Call; +use engines::{Call, RequiresProof}; use util::{H256, Address, RwLock}; use ids::BlockId; -use header::BlockNumber; +use header::{BlockNumber, Header}; use client::{Client, BlockChainClient}; use super::ValidatorSet; @@ -42,21 +42,9 @@ impl Multi { } fn correct_set(&self, id: BlockId) -> Option<&ValidatorSet> { - match self - .block_number - .read()(id) - .map(|parent_block| self - .sets - .iter() - .rev() - .find(|&(block, _)| *block <= parent_block + 1) - .expect("constructor validation ensures that there is at least one validator set for block 0; - block 0 is less than any uint; - qed") - ) { - Ok((block, set)) => { - trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block); - Some(&**set) + match self.block_number.read()(id).map(|parent_block| self.correct_set_by_number(parent_block)) { + Ok(set) => { + Some(set) }, Err(e) => { debug!(target: "engine", "ValidatorSet could not be recovered: {}", e); @@ -64,6 +52,18 @@ impl Multi { }, } } + + fn correct_set_by_number(&self, parent_block: BlockNumber) -> &ValidatorSet { + let (block, set) = self.sets.iter() + .rev() + .find(|&(block, _)| *block <= parent_block + 1) + .expect("constructor validation ensures that there is at least one validator set for block 0; + block 0 is less than any uint; + qed"); + + trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block); + &**set + } } impl ValidatorSet for Multi { @@ -72,6 +72,16 @@ impl ValidatorSet for Multi { .unwrap_or(Box::new(|_, _| Err("No validator set for given ID.".into()))) } + fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> RequiresProof + { + self.correct_set_by_number(header.number()).proof_required(header, block, receipts) + } + + fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String> { + self.correct_set_by_number(header.number()).generate_proof(header, caller) + } + fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { self.correct_set(BlockId::Hash(*bh)) .map_or(false, |set| set.contains_with_caller(bh, address, caller)) diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 8a0599dde..61e16e198 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -26,6 +26,7 @@ use util::cache::MemoryLruCache; use engines::Call; use types::ids::BlockId; use client::{Client, BlockChainClient}; +use header::Header; use super::ValidatorSet; use super::simple_list::SimpleList; @@ -74,6 +75,19 @@ impl ValidatorSet for ValidatorSafeContract { .and_then(|c| c.call_contract(id, addr, data))) } + fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> ::engines::RequiresProof + { + // TODO: check blooms first and then logs for the + // ValidatorsChanged([parent_hash, nonce], new_validators) log event. + ::engines::RequiresProof::No + } + + fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String> { + self.get_list(caller).map(|list| ::rlp::encode_list(&list.into_inner()).to_vec()) + .ok_or_else(|| "Caller insufficient to get validator list.".into()) + } + fn contains_with_caller(&self, block_hash: &H256, address: &Address, caller: &Call) -> bool { let mut guard = self.validators.write(); let maybe_existing = guard diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index ac82cf76f..e513212a6 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -19,6 +19,7 @@ use util::{H256, Address, HeapSizeOf}; use engines::Call; +use header::Header; use super::ValidatorSet; #[derive(Debug, PartialEq, Eq, Default)] @@ -27,11 +28,17 @@ pub struct SimpleList { } impl SimpleList { + /// Create a new `SimpleList`. pub fn new(validators: Vec
) -> Self { SimpleList { validators: validators, } } + + /// Convert into inner representation. + pub fn into_inner(self) -> Vec
{ + self.validators + } } impl HeapSizeOf for SimpleList { @@ -45,6 +52,16 @@ impl ValidatorSet for SimpleList { Box::new(|_, _| Err("Simple list doesn't require calls.".into())) } + fn proof_required(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[::receipt::Receipt]>) + -> ::engines::RequiresProof + { + ::engines::RequiresProof::No + } + + fn generate_proof(&self, _header: &Header, _caller: &Call) -> Result, String> { + Ok(Vec::new()) + } + fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool { self.validators.contains(address) } From b4f3e30cd6827d404182aad45dc748712fbef150 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 12 Apr 2017 22:10:18 +0200 Subject: [PATCH 017/117] detect changes in safe_contract --- ethcore/native_contracts/build.rs | 4 +- ethcore/native_contracts/generator/src/lib.rs | 7 +- .../engines/validator_set/safe_contract.rs | 132 ++++++++++++++++-- 3 files changed, 132 insertions(+), 11 deletions(-) diff --git a/ethcore/native_contracts/build.rs b/ethcore/native_contracts/build.rs index 0a080e801..7da55a977 100644 --- a/ethcore/native_contracts/build.rs +++ b/ethcore/native_contracts/build.rs @@ -27,7 +27,9 @@ const SERVICE_TRANSACTION_ABI: &'static str = r#"[{"constant":false,"inputs":[{" const SECRETSTORE_ACL_STORAGE_ABI: &'static str = r#"[{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"document","type":"bytes32"}],"name":"checkPermissions","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"}]"#; -const VALIDATOR_SET_ABI: &'static str = r#"[{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]"#; +// be very careful changing these: ensure `ethcore/engines` validator sets have corresponding +// changes. +const VALIDATOR_SET_ABI: &'static str = r#"[{"constant":true,"inputs":[],"name":"transitionNonce","outputs":[{"name":"nonce","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"validators","type":"address[]"}],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_parent_hash","type":"bytes32"},{"indexed":true,"name":"_nonce","type":"uint256"},{"indexed":false,"name":"_new_set","type":"address[]"}],"name":"ValidatorsChanged","type":"event"}]"#; const VALIDATOR_REPORT_ABI: &'static str = r#"[{"constant":false,"inputs":[{"name":"validator","type":"address"}],"name":"reportMalicious","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"validator","type":"address"}],"name":"reportBenign","outputs":[],"payable":false,"type":"function"}]"#; diff --git a/ethcore/native_contracts/generator/src/lib.rs b/ethcore/native_contracts/generator/src/lib.rs index 28d658cb3..76985e682 100644 --- a/ethcore/native_contracts/generator/src/lib.rs +++ b/ethcore/native_contracts/generator/src/lib.rs @@ -47,7 +47,7 @@ pub fn generate_module(struct_name: &str, abi: &str) -> Result { Ok(format!(r##" use byteorder::{{BigEndian, ByteOrder}}; use futures::{{future, Future, IntoFuture, BoxFuture}}; -use ethabi::{{Contract, Interface, Token}}; +use ethabi::{{Contract, Interface, Token, Event}}; use util::{{self, Uint}}; pub struct {name} {{ @@ -70,6 +70,11 @@ impl {name} {{ }} }} + /// Access the underlying `ethabi` contract. + pub fn contract(this: &Self) -> &Contract {{ + &this.contract + }} + {functions} }} "##, diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 61e16e198..de7ecb3a2 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -23,16 +23,25 @@ use native_contracts::ValidatorSet as Provider; use util::*; use util::cache::MemoryLruCache; -use engines::Call; -use types::ids::BlockId; +use basic_types::LogBloom; use client::{Client, BlockChainClient}; +use engines::Call; use header::Header; +use ids::BlockId; +use log_entry::LogEntry; use super::ValidatorSet; use super::simple_list::SimpleList; const MEMOIZE_CAPACITY: usize = 500; +// TODO: ethabi should be able to generate this. +const EVENT_NAME: &'static [u8] = &*b"ValidatorsChanged(bytes32,uint256,address[])"; + +lazy_static! { + static ref EVENT_NAME_HASH: H256 = EVENT_NAME.sha3(); +} + /// The validator contract should have the following interface: pub struct ValidatorSafeContract { pub address: Address, @@ -41,6 +50,14 @@ pub struct ValidatorSafeContract { client: RwLock>>, // TODO [keorn]: remove } +fn encode_proof(nonce: U256, validators: &[Address]) -> Bytes { + use rlp::RlpStream; + + let mut stream = RlpStream::new_list(2); + stream.append(&nonce).append_list(validators); + stream.drain().to_vec() +} + impl ValidatorSafeContract { pub fn new(contract_address: Address) -> Self { ValidatorSafeContract { @@ -64,6 +81,40 @@ impl ValidatorSafeContract { }, } } + + /// Queries for the current validator set transition nonce. + fn get_nonce(&self, caller: &Call) -> Option<::util::U256> { + match self.provider.transition_nonce(caller).wait() { + Ok(nonce) => Some(nonce), + Err(s) => { + debug!(target: "engine", "Unable to fetch transition nonce: {}", s); + None + } + } + } + + // Whether the header matches the expected bloom. + // + // The expected log should have 3 topics: + // 1. ETHABI-encoded log name. + // 2. the block's parent hash. + // 3. the "nonce": n for the nth transition in history. + // + // We can only search for the first 2, since we don't have the third + // just yet. + // + // The parent hash is included to prevent + // malicious actors from brute forcing other logs that would + // produce the same bloom. + // + // The log data is an array of all new validator addresses. + fn expected_bloom(&self, header: &Header) -> LogBloom { + LogEntry { + address: self.address, + topics: vec![*EVENT_NAME_HASH, *header.parent_hash()], + data: Vec::new(), // irrelevant for bloom. + }.bloom() + } } impl ValidatorSet for ValidatorSafeContract { @@ -75,17 +126,80 @@ impl ValidatorSet for ValidatorSafeContract { .and_then(|c| c.call_contract(id, addr, data))) } - fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + fn proof_required(&self, header: &Header, _block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) -> ::engines::RequiresProof { - // TODO: check blooms first and then logs for the - // ValidatorsChanged([parent_hash, nonce], new_validators) log event. - ::engines::RequiresProof::No + let bloom = self.expected_bloom(header); + let header_bloom = header.log_bloom(); + + if &bloom & header_bloom != bloom { return ::engines::RequiresProof::No } + + match receipts { + None => ::engines::RequiresProof::Unsure(::engines::Unsure::NeedsReceipts), + Some(receipts) => { + let check_log = |log: &LogEntry| { + log.address == self.address && + log.topics.len() == 3 && + log.topics[0] == *EVENT_NAME_HASH && + log.topics[1] == *header.parent_hash() + // don't have anything to compare nonce to yet. + }; + + let event = Provider::contract(&self.provider) + .event("ValidatorsChanged".into()) + .expect("Contract known ahead of time to have `ValidatorsChanged` event; qed"); + + let mut decoded_events = receipts.iter() + .filter(|r| &bloom & &r.log_bloom == bloom) + .flat_map(|r| r.logs.iter()) + .filter(move |l| check_log(l)) + .filter_map(|log| { + let topics = log.topics.iter().map(|x| x.0.clone()).collect(); + match event.decode_log(topics, log.data.clone()) { + Ok(decoded) => Some(decoded), + Err(_) => None, + } + }); + + + // TODO: are multiple transitions per block possible? + match decoded_events.next() { + None => ::engines::RequiresProof::No, + Some(matched_event) => { + // decode log manually until the native contract generator is + // good enough to do it for us. + let &(_, _, ref nonce_token) = &matched_event.params[2]; + let &(_, _, ref validators_token) = &matched_event.params[3]; + + let nonce: Option = nonce_token.clone().to_uint() + .map(H256).map(Into::into); + let validators = validators_token.clone().to_array() + .and_then(|a| a.into_iter() + .map(|x| x.to_address().map(H160)) + .collect::>>() + ); + + match (nonce, validators) { + (Some(nonce), Some(validators)) => + ::engines::RequiresProof::Yes(Some(encode_proof(nonce, &validators))), + _ => { + debug!(target: "engine", "Successfully decoded log turned out to be bad."); + ::engines::RequiresProof::No + } + } + } + } + } + } } - fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String> { - self.get_list(caller).map(|list| ::rlp::encode_list(&list.into_inner()).to_vec()) - .ok_or_else(|| "Caller insufficient to get validator list.".into()) + // the proof we generate is an RLP list containing two parts. + // (nonce, validators) + fn generate_proof(&self, _header: &Header, caller: &Call) -> Result, String> { + match (self.get_nonce(caller), self.get_list(caller)) { + (Some(nonce), Some(list)) => Ok(encode_proof(nonce, &list.into_inner())), + _ => Err("Caller insufficient to generate validator proof.".into()), + } } fn contains_with_caller(&self, block_hash: &H256, address: &Address, caller: &Call) -> bool { From 314af4cdae217355cd52d70fc3ea81b77ded498b Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 6 Apr 2017 19:26:17 +0200 Subject: [PATCH 018/117] Use in-memory database in tests --- ethcore/src/block.rs | 15 ++-- ethcore/src/blockchain/blockchain.rs | 12 +-- ethcore/src/client/client.rs | 2 +- ethcore/src/engines/authority_round.rs | 4 +- ethcore/src/engines/basic_authority.rs | 3 +- ethcore/src/engines/instant_seal.rs | 3 +- ethcore/src/engines/tendermint/mod.rs | 4 +- ethcore/src/ethereum/ethash.rs | 6 +- ethcore/src/ethereum/mod.rs | 3 +- ethcore/src/executive.rs | 39 +++----- ethcore/src/externalities.rs | 15 ++-- ethcore/src/json_tests/executive.rs | 3 +- ethcore/src/json_tests/state.rs | 2 +- ethcore/src/miner/miner.rs | 11 ++- ethcore/src/spec/spec.rs | 3 +- ethcore/src/state/mod.rs | 97 +++++++------------- ethcore/src/state_db.rs | 3 +- ethcore/src/tests/client.rs | 63 +++++-------- ethcore/src/tests/helpers.rs | 118 ++++++------------------- 19 files changed, 130 insertions(+), 276 deletions(-) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 12e77d41b..46a0db733 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -667,8 +667,7 @@ mod tests { use spec::*; let spec = Spec::new_test(); let genesis_header = spec.genesis_header(); - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close_and_lock(); @@ -682,16 +681,14 @@ mod tests { let engine = &*spec.engine; let genesis_header = spec.genesis_header(); - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap() .close_and_lock().seal(engine, vec![]).unwrap(); let orig_bytes = b.rlp_bytes(); let orig_db = b.drain(); - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap(); assert_eq!(e.rlp_bytes(), orig_bytes); @@ -708,8 +705,7 @@ mod tests { let engine = &*spec.engine; let genesis_header = spec.genesis_header(); - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let mut uncle1_header = Header::new(); @@ -723,8 +719,7 @@ mod tests { let orig_bytes = b.rlp_bytes(); let orig_db = b.drain(); - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap(); let bytes = e.rlp_bytes(); diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 8986dc6b8..39c1039ca 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -1791,15 +1791,13 @@ mod tests { #[test] fn can_contain_arbitrary_block_sequence() { - let bc_result = generate_dummy_blockchain(50); - let bc = bc_result.reference(); + let bc = generate_dummy_blockchain(50); assert_eq!(bc.best_block_number(), 49); } #[test] fn can_collect_garbage() { - let bc_result = generate_dummy_blockchain(3000); - let bc = bc_result.reference(); + let bc = generate_dummy_blockchain(3000); assert_eq!(bc.best_block_number(), 2999); let best_hash = bc.best_block_hash(); @@ -1818,15 +1816,13 @@ mod tests { #[test] fn can_contain_arbitrary_block_sequence_with_extra() { - let bc_result = generate_dummy_blockchain_with_extra(25); - let bc = bc_result.reference(); + let bc = generate_dummy_blockchain_with_extra(25); assert_eq!(bc.best_block_number(), 24); } #[test] fn can_contain_only_genesis_block() { - let bc_result = generate_dummy_empty_blockchain(); - let bc = bc_result.reference(); + let bc = generate_dummy_empty_blockchain(); assert_eq!(bc.best_block_number(), 0); } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 4bd29d100..2ad4fa8d6 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1717,7 +1717,7 @@ mod tests { // Separate thread uncommited transaction let go = Arc::new(AtomicBool::new(false)); let go_thread = go.clone(); - let another_client = client.reference().clone(); + let another_client = client.clone(); thread::spawn(move || { let mut batch = DBTransaction::new(); another_client.chain.read().insert_block(&mut batch, &new_block, Vec::new()); diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index e6cbdb531..2e8e3932c 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -454,8 +454,8 @@ mod tests { let spec = Spec::new_test_round(); let engine = &*spec.engine; let genesis_header = spec.genesis_header(); - let db1 = spec.ensure_db_good(get_temp_state_db().take(), &Default::default()).unwrap(); - let db2 = spec.ensure_db_good(get_temp_state_db().take(), &Default::default()).unwrap(); + let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); + let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b1 = b1.close_and_lock(); diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index e5a53d4e9..19867a648 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -252,8 +252,7 @@ mod tests { let engine = &*spec.engine; engine.set_signer(Arc::new(tap), addr, "".into()); let genesis_header = spec.genesis_header(); - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).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 = b.close_and_lock(); diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index 45bede9f4..88672c6f4 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -82,8 +82,7 @@ mod tests { fn instant_can_seal() { let spec = Spec::new_instant(); let engine = &*spec.engine; - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let genesis_header = spec.genesis_header(); let last_hashes = Arc::new(vec![genesis_header.hash()]); let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap(); diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 8c8094117..cb284d3c4 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -676,8 +676,8 @@ mod tests { } fn propose_default(spec: &Spec, proposer: Address) -> (ClosedBlock, Vec) { - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = get_temp_state_db(); + let db = spec.ensure_db_good(db, &Default::default()).unwrap(); let genesis_header = spec.genesis_header(); let last_hashes = Arc::new(vec![genesis_header.hash()]); let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![]).unwrap(); diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 5bc31c5d9..88655fa86 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -524,8 +524,7 @@ mod tests { let spec = new_morden(); let engine = &*spec.engine; let genesis_header = spec.genesis_header(); - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close(); @@ -537,8 +536,7 @@ mod tests { let spec = new_morden(); let engine = &*spec.engine; let genesis_header = spec.genesis_header(); - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let mut uncle = Header::new(); diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index ce8b84b31..b54401572 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -94,8 +94,7 @@ mod tests { let spec = new_morden(); let engine = &spec.engine; let genesis_header = spec.genesis_header(); - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let s = State::from_existing(db, genesis_header.state_root().clone(), engine.account_start_nonce(), Default::default()).unwrap(); assert_eq!(s.balance(&"0000000000000000000000000000000000000001".into()).unwrap(), 1u64.into()); assert_eq!(s.balance(&"0000000000000000000000000000000000000002".into()).unwrap(), 1u64.into()); diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 4183a5005..5e0ee5662 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -554,8 +554,7 @@ mod tests { params.gas = U256::from(100_000); params.code = Some(Arc::new("3331600055".from_hex().unwrap())); params.value = ActionValue::Transfer(U256::from(0x7)); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(0x100u64), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); let engine = TestEngine::new(0); @@ -613,8 +612,7 @@ mod tests { params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from(100)); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); let engine = TestEngine::new(0); @@ -672,8 +670,7 @@ mod tests { params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from(100)); params.call_type = CallType::Call; - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); let engine = TestEngine::new(5); @@ -783,8 +780,7 @@ mod tests { params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(100.into()); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); let engine = TestEngine::new(5); @@ -871,8 +867,7 @@ mod tests { params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from(100)); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); let engine = TestEngine::new(0); @@ -923,8 +918,7 @@ mod tests { params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from(100)); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); let engine = TestEngine::new(1024); @@ -981,8 +975,7 @@ mod tests { params.code = Some(Arc::new(code_a.clone())); params.value = ActionValue::Transfer(U256::from(100_000)); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.init_code(&address_a, code_a.clone()).unwrap(); state.init_code(&address_b, code_b.clone()).unwrap(); state.add_balance(&sender, &U256::from(100_000), CleanupMode::NoEmpty).unwrap(); @@ -1029,8 +1022,7 @@ mod tests { params.address = address.clone(); params.gas = U256::from(100_000); params.code = Some(Arc::new(code.clone())); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.init_code(&address, code).unwrap(); let info = EnvInfo::default(); let engine = TestEngine::new(0); @@ -1062,8 +1054,7 @@ mod tests { let sender = t.sender(); let contract = contract_address(&sender, &U256::zero()); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(18), CleanupMode::NoEmpty).unwrap(); let mut info = EnvInfo::default(); info.gas_limit = U256::from(100_000); @@ -1100,8 +1091,7 @@ mod tests { }.sign(keypair.secret(), None); let sender = t.sender(); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty).unwrap(); let mut info = EnvInfo::default(); info.gas_limit = U256::from(100_000); @@ -1133,8 +1123,7 @@ mod tests { }.sign(keypair.secret(), None); let sender = t.sender(); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty).unwrap(); let mut info = EnvInfo::default(); info.gas_used = U256::from(20_000); @@ -1168,8 +1157,7 @@ mod tests { }.sign(keypair.secret(), None); let sender = t.sender(); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(100_017), CleanupMode::NoEmpty).unwrap(); let mut info = EnvInfo::default(); info.gas_limit = U256::from(100_000); @@ -1203,8 +1191,7 @@ mod tests { params.gas = U256::from(0x0186a0); params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from_str("0de0b6b3a7640000").unwrap()); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap(), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); let engine = TestEngine::new(0); diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 893ba03be..8591f15fd 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -346,7 +346,6 @@ mod tests { use evm::Ext; use state::{State, Substate}; use tests::helpers::*; - use devtools::GuardedTempResult; use super::*; use trace::{NoopTracer, NoopVMTracer}; use types::executed::CallType; @@ -373,7 +372,7 @@ mod tests { } struct TestSetup { - state: GuardedTempResult>, + state: State<::state_db::StateDB>, engine: Arc, sub_state: Substate, env_info: EnvInfo @@ -399,7 +398,7 @@ mod tests { #[test] fn can_be_created() { let mut setup = TestSetup::new(); - let state = setup.state.reference_mut(); + let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; @@ -412,7 +411,7 @@ mod tests { #[test] fn can_return_block_hash_no_env() { let mut setup = TestSetup::new(); - let state = setup.state.reference_mut(); + let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; @@ -437,7 +436,7 @@ mod tests { last_hashes.push(test_hash.clone()); env_info.last_hashes = Arc::new(last_hashes); } - let state = setup.state.reference_mut(); + let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; @@ -453,7 +452,7 @@ mod tests { #[should_panic] fn can_call_fail_empty() { let mut setup = TestSetup::new(); - let state = setup.state.reference_mut(); + let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; @@ -481,7 +480,7 @@ mod tests { let log_topics = vec![H256::from("af0fa234a6af46afa23faf23bcbc1c1cb4bcb7bcbe7e7e7ee3ee2edddddddddd")]; let mut setup = TestSetup::new(); - let state = setup.state.reference_mut(); + let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; @@ -499,7 +498,7 @@ mod tests { let refund_account = &Address::new(); let mut setup = TestSetup::new(); - let state = setup.state.reference_mut(); + let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 844fa08f5..9526e5ec2 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -215,8 +215,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { } let out_of_gas = vm.out_of_gas(); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.populate_from(From::from(vm.pre_state.clone())); let info = From::from(vm.env); let engine = TestEngine::new(1); diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index e4b9390db..d0b18d99c 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -56,7 +56,7 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec { let post_root: H256 = state.hash.into(); let transaction = multitransaction.select(&state.indexes).into(); - let mut state = get_temp_mem_state(); + let mut state = get_temp_state(); state.populate_from(pre.clone()); state.commit().expect(&format!("State test {} failed due to internal error.", name)); let _res = state.apply(&env, &**engine, &transaction, false); diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index ac1695b52..0347e984f 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1413,19 +1413,18 @@ mod tests { fn internal_seals_without_work() { let miner = Miner::with_spec(&Spec::new_instant()); - let c = generate_dummy_client(2); - let client = c.reference().as_ref(); + let client = generate_dummy_client(2); - assert_eq!(miner.import_external_transactions(client, vec![transaction().into()]).pop().unwrap().unwrap(), TransactionImportResult::Current); + assert_eq!(miner.import_external_transactions(&*client, vec![transaction().into()]).pop().unwrap().unwrap(), TransactionImportResult::Current); - miner.update_sealing(client); + miner.update_sealing(&*client); client.flush_queue(); assert!(miner.pending_block().is_none()); assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber); - assert_eq!(miner.import_own_transaction(client, PendingTransaction::new(transaction().into(), None)).unwrap(), TransactionImportResult::Current); + assert_eq!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction().into(), None)).unwrap(), TransactionImportResult::Current); - miner.update_sealing(client); + miner.update_sealing(&*client); client.flush_queue(); assert!(miner.pending_block().is_none()); assert_eq!(client.chain_info().best_block_number, 4 as BlockNumber); diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 9051bcbcf..35f1ca983 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -392,8 +392,7 @@ mod tests { #[test] fn genesis_constructor() { let spec = Spec::new_test_constructor(); - let mut db_result = get_temp_state_db(); - let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let state = State::from_existing(db.boxed_clone(), spec.state_root(), spec.engine.account_start_nonce(), Default::default()).unwrap(); let expected = H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(); assert_eq!(state.storage_at(&Address::from_str("0000000000000000000000000000000000000005").unwrap(), &H256::zero()).unwrap(), expected); diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index b1594dd9b..12d7718cc 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -939,7 +939,6 @@ mod tests { use ethkey::Secret; use util::{U256, H256, Address, Hashable}; use tests::helpers::*; - use devtools::*; use env_info::EnvInfo; use spec::*; use transaction::*; @@ -955,8 +954,7 @@ mod tests { fn should_apply_create_transaction() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -998,9 +996,8 @@ mod tests { let a = Address::zero(); - let temp = RandomTempPath::new(); let mut state = { - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); assert_eq!(state.exists(&a).unwrap(), false); state.inc_nonce(&a).unwrap(); state.commit().unwrap(); @@ -1015,8 +1012,7 @@ mod tests { fn should_trace_failed_create_transaction() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1052,8 +1048,7 @@ mod tests { fn should_trace_call_transaction() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1095,8 +1090,7 @@ mod tests { fn should_trace_basic_call_transaction() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1137,8 +1131,7 @@ mod tests { fn should_trace_call_transaction_to_builtin() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1179,8 +1172,7 @@ mod tests { fn should_not_trace_subcall_transaction_to_builtin() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1222,8 +1214,7 @@ mod tests { fn should_not_trace_callcode() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1281,8 +1272,7 @@ mod tests { fn should_not_trace_delegatecall() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1343,8 +1333,7 @@ mod tests { fn should_trace_failed_call_transaction() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1383,8 +1372,7 @@ mod tests { fn should_trace_call_with_subcall_transaction() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1443,8 +1431,7 @@ mod tests { fn should_trace_call_with_basic_subcall_transaction() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1498,8 +1485,7 @@ mod tests { fn should_not_trace_call_with_invalid_basic_subcall_transaction() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1541,8 +1527,7 @@ mod tests { fn should_trace_failed_subcall_transaction() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1597,8 +1582,7 @@ mod tests { fn should_trace_call_with_subcall_with_subcall_transaction() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1672,8 +1656,7 @@ mod tests { fn should_trace_failed_subcall_with_subcall_transaction() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1745,8 +1728,7 @@ mod tests { fn should_trace_suicide() { init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); let mut info = EnvInfo::default(); info.gas_limit = 1_000_000.into(); @@ -1797,9 +1779,8 @@ mod tests { #[test] fn code_from_database() { let a = Address::zero(); - let temp = RandomTempPath::new(); let (root, db) = { - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); state.require_or_from(&a, false, ||Account::new_contract(42.into(), 0.into()), |_|{}).unwrap(); state.init_code(&a, vec![1, 2, 3]).unwrap(); assert_eq!(state.code(&a).unwrap(), Some(Arc::new([1u8, 2, 3].to_vec()))); @@ -1815,9 +1796,8 @@ mod tests { #[test] fn storage_at_from_database() { let a = Address::zero(); - let temp = RandomTempPath::new(); let (root, db) = { - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(69u64))).unwrap(); state.commit().unwrap(); state.drop() @@ -1830,9 +1810,8 @@ mod tests { #[test] fn get_from_database() { let a = Address::zero(); - let temp = RandomTempPath::new(); let (root, db) = { - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); state.inc_nonce(&a).unwrap(); state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); state.commit().unwrap(); @@ -1848,8 +1827,7 @@ mod tests { #[test] fn remove() { let a = Address::zero(); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); assert_eq!(state.exists(&a).unwrap(), false); assert_eq!(state.exists_and_not_null(&a).unwrap(), false); state.inc_nonce(&a).unwrap(); @@ -1865,8 +1843,7 @@ mod tests { #[test] fn empty_account_is_not_created() { let a = Address::zero(); - let path = RandomTempPath::new(); - let db = get_temp_state_db_in(path.as_path()); + let db = get_temp_state_db(); let (root, db) = { let mut state = State::new(db, U256::from(0), Default::default()); state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty).unwrap(); // create an empty account @@ -1881,8 +1858,7 @@ mod tests { #[test] fn empty_account_exists_when_creation_forced() { let a = Address::zero(); - let path = RandomTempPath::new(); - let db = get_temp_state_db_in(path.as_path()); + let db = get_temp_state_db(); let (root, db) = { let mut state = State::new(db, U256::from(0), Default::default()); state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate).unwrap(); // create an empty account @@ -1897,9 +1873,8 @@ mod tests { #[test] fn remove_from_database() { let a = Address::zero(); - let temp = RandomTempPath::new(); let (root, db) = { - let mut state = get_temp_state_in(temp.as_path()); + let mut state = get_temp_state(); state.inc_nonce(&a).unwrap(); state.commit().unwrap(); assert_eq!(state.exists(&a).unwrap(), true); @@ -1925,8 +1900,7 @@ mod tests { #[test] fn alter_balance() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); let a = Address::zero(); let b = 1u64.into(); state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); @@ -1947,8 +1921,7 @@ mod tests { #[test] fn alter_nonce() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); let a = Address::zero(); state.inc_nonce(&a).unwrap(); assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); @@ -1964,8 +1937,7 @@ mod tests { #[test] fn balance_nonce() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); let a = Address::zero(); assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); @@ -1976,8 +1948,7 @@ mod tests { #[test] fn ensure_cached() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); let a = Address::zero(); state.require(&a, false).unwrap(); state.commit().unwrap(); @@ -1986,8 +1957,7 @@ mod tests { #[test] fn checkpoint_basic() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); let a = Address::zero(); state.checkpoint(); state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); @@ -2003,8 +1973,7 @@ mod tests { #[test] fn checkpoint_nested() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); let a = Address::zero(); state.checkpoint(); state.checkpoint(); @@ -2018,16 +1987,14 @@ mod tests { #[test] fn create_empty() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); + let mut state = get_temp_state(); state.commit().unwrap(); assert_eq!(state.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); } #[test] fn should_not_panic_on_state_diff_with_storage() { - let state = get_temp_state(); - let mut state = state.reference().clone(); + let mut state = get_temp_state(); let a: Address = 0xa.into(); state.init_code(&a, b"abcdefg".to_vec()).unwrap();; diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index 2831d2f9b..6bf8bbd8e 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -463,8 +463,7 @@ mod tests { fn state_db_smoke() { init_log(); - let mut state_db_result = get_temp_state_db(); - let state_db = state_db_result.take(); + let state_db = get_temp_state_db(); let root_parent = H256::random(); let address = Address::random(); let h0 = H256::random(); diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index e61edd478..6f0d7bc26 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -72,8 +72,7 @@ fn should_return_registrar() { #[test] fn returns_state_root_basic() { - let client_result = generate_dummy_client(6); - let client = client_result.reference(); + let client = generate_dummy_client(6); let test_spec = get_test_spec(); let genesis_header = test_spec.genesis_header(); @@ -125,8 +124,7 @@ fn query_none_block() { #[test] fn query_bad_block() { - let client_result = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]); - let client = client_result.reference(); + let client = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]); let bad_block: Option<_> = client.block_header(BlockId::Number(1)); assert!(bad_block.is_none()); @@ -135,8 +133,7 @@ fn query_bad_block() { #[test] fn returns_chain_info() { let dummy_block = get_good_dummy_block(); - let client_result = get_test_client_with_blocks(vec![dummy_block.clone()]); - let client = client_result.reference(); + let client = get_test_client_with_blocks(vec![dummy_block.clone()]); let block = BlockView::new(&dummy_block); let info = client.chain_info(); assert_eq!(info.best_block_hash, block.header().hash()); @@ -145,8 +142,7 @@ fn returns_chain_info() { #[test] fn returns_logs() { let dummy_block = get_good_dummy_block(); - let client_result = get_test_client_with_blocks(vec![dummy_block.clone()]); - let client = client_result.reference(); + let client = get_test_client_with_blocks(vec![dummy_block.clone()]); let logs = client.logs(Filter { from_block: BlockId::Earliest, to_block: BlockId::Latest, @@ -160,8 +156,7 @@ fn returns_logs() { #[test] fn returns_logs_with_limit() { let dummy_block = get_good_dummy_block(); - let client_result = get_test_client_with_blocks(vec![dummy_block.clone()]); - let client = client_result.reference(); + let client = get_test_client_with_blocks(vec![dummy_block.clone()]); let logs = client.logs(Filter { from_block: BlockId::Earliest, to_block: BlockId::Latest, @@ -175,8 +170,7 @@ fn returns_logs_with_limit() { #[test] fn returns_block_body() { let dummy_block = get_good_dummy_block(); - let client_result = get_test_client_with_blocks(vec![dummy_block.clone()]); - let client = client_result.reference(); + let client = get_test_client_with_blocks(vec![dummy_block.clone()]); let block = BlockView::new(&dummy_block); let body = client.block_body(BlockId::Hash(block.header().hash())).unwrap(); let body = body.rlp(); @@ -187,8 +181,7 @@ fn returns_block_body() { #[test] fn imports_block_sequence() { - let client_result = generate_dummy_client(6); - let client = client_result.reference(); + let client = generate_dummy_client(6); let block = client.block_header(BlockId::Number(5)).unwrap(); assert!(!block.into_inner().is_empty()); @@ -196,8 +189,7 @@ fn imports_block_sequence() { #[test] fn can_collect_garbage() { - let client_result = generate_dummy_client(100); - let client = client_result.reference(); + let client = generate_dummy_client(100); client.tick(); assert!(client.blockchain_cache_info().blocks < 100 * 1024); } @@ -205,19 +197,16 @@ fn can_collect_garbage() { #[test] fn can_generate_gas_price_median() { - let client_result = generate_dummy_client_with_data(3, 1, slice_into![1, 2, 3]); - let client = client_result.reference(); + let client = generate_dummy_client_with_data(3, 1, slice_into![1, 2, 3]); assert_eq!(Some(&U256::from(2)), client.gas_price_corpus(3).median()); - let client_result = generate_dummy_client_with_data(4, 1, slice_into![1, 4, 3, 2]); - let client = client_result.reference(); + let client = generate_dummy_client_with_data(4, 1, slice_into![1, 4, 3, 2]); assert_eq!(Some(&U256::from(3)), client.gas_price_corpus(3).median()); } #[test] fn can_generate_gas_price_histogram() { - let client_result = generate_dummy_client_with_data(20, 1, slice_into![6354,8593,6065,4842,7845,7002,689,4958,4250,6098,5804,4320,643,8895,2296,8589,7145,2000,2512,1408]); - let client = client_result.reference(); + let client = generate_dummy_client_with_data(20, 1, slice_into![6354,8593,6065,4842,7845,7002,689,4958,4250,6098,5804,4320,643,8895,2296,8589,7145,2000,2512,1408]); let hist = client.gas_price_corpus(20).histogram(5).unwrap(); let correct_hist = ::stats::Histogram { bucket_bounds: vec_into![643, 2294, 3945, 5596, 7247, 8898], counts: vec![4,2,4,6,4] }; @@ -226,32 +215,29 @@ fn can_generate_gas_price_histogram() { #[test] fn empty_gas_price_histogram() { - let client_result = generate_dummy_client_with_data(20, 0, slice_into![]); - let client = client_result.reference(); + let client = generate_dummy_client_with_data(20, 0, slice_into![]); assert!(client.gas_price_corpus(20).histogram(5).is_none()); } #[test] fn corpus_is_sorted() { - let client_result = generate_dummy_client_with_data(2, 1, slice_into![U256::from_str("11426908979").unwrap(), U256::from_str("50426908979").unwrap()]); - let client = client_result.reference(); + let client = generate_dummy_client_with_data(2, 1, slice_into![U256::from_str("11426908979").unwrap(), U256::from_str("50426908979").unwrap()]); let corpus = client.gas_price_corpus(20); assert!(corpus[0] < corpus[1]); } #[test] fn can_handle_long_fork() { - let client_result = generate_dummy_client(1200); - let client = client_result.reference(); + let client = generate_dummy_client(1200); for _ in 0..20 { client.import_verified_blocks(); } assert_eq!(1200, client.chain_info().best_block_number); - push_blocks_to_client(client, 45, 1201, 800); - push_blocks_to_client(client, 49, 1201, 800); - push_blocks_to_client(client, 53, 1201, 600); + push_blocks_to_client(&client, 45, 1201, 800); + push_blocks_to_client(&client, 49, 1201, 800); + push_blocks_to_client(&client, 53, 1201, 600); for _ in 0..400 { client.import_verified_blocks(); @@ -262,8 +248,7 @@ fn can_handle_long_fork() { #[test] fn can_mine() { let dummy_blocks = get_good_dummy_block_seq(2); - let client_result = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]); - let client = client_result.reference(); + let client = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]); let b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]).close(); @@ -329,14 +314,13 @@ fn does_not_propagate_delayed_transactions() { value: 0.into(), data: Vec::new(), }.sign(secret, None), None); - let client_result = generate_dummy_client(1); - let client = client_result.reference(); + let client = generate_dummy_client(1); - client.miner().import_own_transaction(&**client, tx0).unwrap(); - client.miner().import_own_transaction(&**client, tx1).unwrap(); + client.miner().import_own_transaction(&*client, tx0).unwrap(); + client.miner().import_own_transaction(&*client, tx1).unwrap(); assert_eq!(0, client.ready_transactions().len()); assert_eq!(2, client.miner().pending_transactions().len()); - push_blocks_to_client(client, 53, 2, 2); + push_blocks_to_client(&client, 53, 2, 2); client.flush_queue(); assert_eq!(2, client.ready_transactions().len()); assert_eq!(2, client.miner().pending_transactions().len()); @@ -346,8 +330,7 @@ fn does_not_propagate_delayed_transactions() { fn transaction_proof() { use ::client::ProvingBlockChainClient; - let client_result = generate_dummy_client(0); - let client = client_result.reference(); + let client = generate_dummy_client(0); let address = Address::random(); let test_spec = Spec::new_test(); for _ in 0..20 { diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 70269b6e4..1391c37f4 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -30,7 +30,6 @@ use engines::Engine; use env_info::EnvInfo; use ethereum; use ethereum::ethash::EthashParams; -use devtools::*; use miner::Miner; use header::Header; use transaction::{Action, Transaction, SignedTransaction}; @@ -133,28 +132,26 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[SignedTransa rlp.out() } -pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult> { +pub fn generate_dummy_client(block_number: u32) -> Arc { generate_dummy_client_with_spec_and_data(Spec::new_test, block_number, 0, &[]) } -pub fn generate_dummy_client_with_data(block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> GuardedTempResult> { +pub fn generate_dummy_client_with_data(block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> Arc { generate_dummy_client_with_spec_and_data(Spec::new_null, block_number, txs_per_block, tx_gas_prices) } -pub fn generate_dummy_client_with_spec_and_data(get_test_spec: F, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> GuardedTempResult> where F: Fn()->Spec { +pub fn generate_dummy_client_with_spec_and_data(get_test_spec: F, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> Arc where F: Fn()->Spec { generate_dummy_client_with_spec_accounts_and_data(get_test_spec, None, block_number, txs_per_block, tx_gas_prices) } -pub fn generate_dummy_client_with_spec_and_accounts(get_test_spec: F, accounts: Option>) -> GuardedTempResult> where F: Fn()->Spec { +pub fn generate_dummy_client_with_spec_and_accounts(get_test_spec: F, accounts: Option>) -> Arc where F: Fn()->Spec { generate_dummy_client_with_spec_accounts_and_data(get_test_spec, accounts, 0, 0, &[]) } -pub fn generate_dummy_client_with_spec_accounts_and_data(get_test_spec: F, accounts: Option>, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> GuardedTempResult> where F: Fn()->Spec { - let dir = RandomTempPath::new(); +pub fn generate_dummy_client_with_spec_accounts_and_data(get_test_spec: F, accounts: Option>, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> Arc where F: Fn()->Spec { let test_spec = get_test_spec(); - let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); - let client_db = Arc::new(Database::open(&db_config, dir.as_path().to_str().unwrap()).unwrap()); + let client_db = new_db(); let client = Client::new( ClientConfig::default(), @@ -165,8 +162,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(get_test_spec: F, ac ).unwrap(); let test_engine = &*test_spec.engine; - let mut db_result = get_temp_state_db(); - let mut db = test_spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); + let mut db = test_spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let genesis_header = test_spec.genesis_header(); let mut rolling_timestamp = 40; @@ -220,11 +216,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(get_test_spec: F, ac } client.flush_queue(); client.import_verified_blocks(); - - GuardedTempResult::> { - _temp: dir, - result: Some(client) - } + client } pub fn push_blocks_to_client(client: &Arc, timestamp_salt: u64, starting_number: usize, block_number: usize) { @@ -256,11 +248,9 @@ pub fn push_blocks_to_client(client: &Arc, timestamp_salt: u64, starting } } -pub fn get_test_client_with_blocks(blocks: Vec) -> GuardedTempResult> { - let dir = RandomTempPath::new(); +pub fn get_test_client_with_blocks(blocks: Vec) -> Arc { let test_spec = get_test_spec(); - let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); - let client_db = Arc::new(Database::open(&db_config, dir.as_path().to_str().unwrap()).unwrap()); + let client_db = new_db(); let client = Client::new( ClientConfig::default(), @@ -277,23 +267,15 @@ pub fn get_test_client_with_blocks(blocks: Vec) -> GuardedTempResult> { - _temp: dir, - result: Some(client) - } + client } -fn new_db(path: &str) -> Arc { - Arc::new( - Database::open(&DatabaseConfig::with_columns(::db::NUM_COLUMNS), path) - .expect("Opening database for tests should always work.") - ) +fn new_db() -> Arc { + Arc::new(::util::kvdb::in_memory(::db::NUM_COLUMNS.unwrap_or(0))) } -pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult { - let temp = RandomTempPath::new(); - let db = new_db(temp.as_str()); +pub fn generate_dummy_blockchain(block_number: u32) -> BlockChain { + let db = new_db(); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); let mut batch = db.transaction(); @@ -302,16 +284,11 @@ pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult { - _temp: temp, - result: Some(bc) - } + bc } -pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempResult { - let temp = RandomTempPath::new(); - let db = new_db(temp.as_str()); +pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> BlockChain { + let db = new_db(); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); @@ -321,69 +298,29 @@ pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempRes bc.commit(); } db.write(batch).unwrap(); - - GuardedTempResult:: { - _temp: temp, - result: Some(bc) - } + bc } -pub fn generate_dummy_empty_blockchain() -> GuardedTempResult { - let temp = RandomTempPath::new(); - let db = new_db(temp.as_str()); +pub fn generate_dummy_empty_blockchain() -> BlockChain { + let db = new_db(); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); - - GuardedTempResult:: { - _temp: temp, - result: Some(bc) - } + bc } -pub fn get_temp_state_db() -> GuardedTempResult { - let temp = RandomTempPath::new(); - let journal_db = get_temp_state_db_in(temp.as_path()); - - GuardedTempResult { - _temp: temp, - result: Some(journal_db) - } -} - -pub fn get_temp_state() -> GuardedTempResult> { - let temp = RandomTempPath::new(); - let journal_db = get_temp_state_db_in(temp.as_path()); - - GuardedTempResult { - _temp: temp, - result: Some(State::new(journal_db, U256::from(0), Default::default())), - } -} - -pub fn get_temp_mem_state() -> State<::state_db::StateDB> { - let journal_db = get_temp_mem_state_db(); +pub fn get_temp_state() -> State<::state_db::StateDB> { + let journal_db = get_temp_state_db(); State::new(journal_db, U256::from(0), Default::default()) } -pub fn get_temp_state_db_in(path: &Path) -> StateDB { - let db = new_db(path.to_str().expect("Only valid utf8 paths for tests.")); - let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, ::db::COL_STATE); - StateDB::new(journal_db, 5 * 1024 * 1024) -} - -pub fn get_temp_mem_state_db() -> StateDB { - let db = Arc::new(::util::kvdb::in_memory(::db::NUM_COLUMNS.unwrap_or(0))); +pub fn get_temp_state_db() -> StateDB { + let db = new_db(); let journal_db = journaldb::new(db, journaldb::Algorithm::EarlyMerge, ::db::COL_STATE); StateDB::new(journal_db, 5 * 1024 * 1024) } -pub fn get_temp_state_in(path: &Path) -> State<::state_db::StateDB> { - let journal_db = get_temp_state_db_in(path); - State::new(journal_db, U256::from(0), Default::default()) -} - pub fn get_good_dummy_block_seq(count: usize) -> Vec { let test_spec = get_test_spec(); - get_good_dummy_block_fork_seq(1, count, &test_spec.genesis_header().hash()) + get_good_dummy_block_fork_seq(1, count, &test_spec.genesis_header().hash()) } pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_hash: &H256) -> Vec { @@ -405,7 +342,6 @@ pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_h rolling_timestamp = rolling_timestamp + 10; r.push(create_test_block(&block_header)); - } r } From ea09aa584d0f2a47105cf15875e7ec72097ea557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 13 Apr 2017 16:32:07 +0200 Subject: [PATCH 019/117] WebSockets RPC server (#5425) * Basic WS server. * CLI for WS server. * Bump jsonrpc * Fixing test. --- .gitlab-ci.yml | 2 +- Cargo.lock | 139 ++++++++++++++++++++------------- Cargo.toml | 10 +-- dapps/src/rpc.rs | 2 +- parity/cli/config.full.toml | 8 ++ parity/cli/config.toml | 3 + parity/cli/mod.rs | 43 +++++++++- parity/cli/usage.txt | 23 +++++- parity/configuration.rs | 38 ++++++++- parity/dapps.rs | 2 +- parity/informant.rs | 4 +- parity/main.rs | 8 +- parity/rpc.rs | 123 +++++++++++++++++++++++------ parity/rpc_apis.rs | 12 +-- parity/run.rs | 9 ++- parity/signer.rs | 12 +-- rpc/Cargo.toml | 5 +- rpc/rpctest/Cargo.toml | 4 +- rpc/rpctest/src/main.rs | 10 +-- rpc/src/lib.rs | 29 ++++++- rpc/src/v1/types/provenance.rs | 7 ++ rpc_cli/Cargo.toml | 2 +- rpc_cli/src/lib.rs | 8 +- rpc_client/Cargo.toml | 2 +- rpc_client/src/lib.rs | 12 +-- scripts/targets.sh | 2 +- signer/Cargo.toml | 2 +- signer/src/lib.rs | 8 +- 28 files changed, 388 insertions(+), 141 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5d90c5d4e..1c2ad13d6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -541,7 +541,7 @@ test-windows: - git submodule update --init --recursive script: - set RUST_BACKTRACE=1 - - echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p ethcore-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release + - echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p parity-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release tags: - rust-windows allow_failure: true diff --git a/Cargo.lock b/Cargo.lock index d8561cd11..8208f583e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,7 +18,6 @@ dependencies = [ "ethcore-ipc-tests 0.1.0", "ethcore-light 1.7.0", "ethcore-logger 1.7.0", - "ethcore-rpc 1.7.0", "ethcore-secretstore 1.0.0", "ethcore-signer 1.7.0", "ethcore-stratum 1.7.0", @@ -38,6 +37,7 @@ dependencies = [ "parity-ipfs-api 1.7.0", "parity-local-store 0.1.0", "parity-reactor 0.1.0", + "parity-rpc 1.7.0", "parity-rpc-client 1.4.0", "parity-updater 1.7.0", "path 0.1.0", @@ -592,49 +592,6 @@ dependencies = [ "tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "ethcore-rpc" -version = "1.7.0" -dependencies = [ - "cid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "ethash 1.7.0", - "ethcore 1.7.0", - "ethcore-devtools 1.7.0", - "ethcore-io 1.7.0", - "ethcore-ipc 1.7.0", - "ethcore-light 1.7.0", - "ethcore-logger 1.7.0", - "ethcore-util 1.7.0", - "ethcrypto 0.1.0", - "ethjson 0.1.0", - "ethkey 0.2.0", - "ethstore 0.1.0", - "ethsync 1.7.0", - "fetch 0.1.0", - "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", - "jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", - "jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", - "jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", - "jsonrpc-minihttp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", - "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "multihash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-reactor 0.1.0", - "parity-updater 1.7.0", - "rlp 0.1.0", - "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", - "stats 0.1.0", - "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "ethcore-secretstore" version = "1.0.0" @@ -674,12 +631,12 @@ dependencies = [ "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-devtools 1.7.0", "ethcore-io 1.7.0", - "ethcore-rpc 1.7.0", "ethcore-util 1.7.0", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-rpc 1.7.0", "parity-ui 1.7.0", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1080,7 +1037,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jsonrpc-core" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#ad0682785a693eba3069e48b57ec89abb62c3b60" dependencies = [ "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1092,7 +1049,7 @@ dependencies = [ [[package]] name = "jsonrpc-http-server" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#ad0682785a693eba3069e48b57ec89abb62c3b60" dependencies = [ "hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -1105,7 +1062,7 @@ dependencies = [ [[package]] name = "jsonrpc-ipc-server" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#ad0682785a693eba3069e48b57ec89abb62c3b60" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -1117,7 +1074,7 @@ dependencies = [ [[package]] name = "jsonrpc-macros" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#ad0682785a693eba3069e48b57ec89abb62c3b60" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -1127,7 +1084,7 @@ dependencies = [ [[package]] name = "jsonrpc-minihttp-server" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#ad0682785a693eba3069e48b57ec89abb62c3b60" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -1141,7 +1098,7 @@ dependencies = [ [[package]] name = "jsonrpc-pubsub" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#ad0682785a693eba3069e48b57ec89abb62c3b60" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1151,7 +1108,7 @@ dependencies = [ [[package]] name = "jsonrpc-server-utils" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#ad0682785a693eba3069e48b57ec89abb62c3b60" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1162,7 +1119,7 @@ dependencies = [ [[package]] name = "jsonrpc-tcp-server" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#ad0682785a693eba3069e48b57ec89abb62c3b60" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -1172,6 +1129,17 @@ dependencies = [ "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "jsonrpc-ws-server" +version = "7.0.0" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#ad0682785a693eba3069e48b57ec89abb62c3b60" +dependencies = [ + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "ws 0.6.0 (git+https://github.com/tomusdrw/ws-rs)", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -1708,16 +1676,60 @@ dependencies = [ "tokio-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-rpc" +version = "1.7.0" +dependencies = [ + "cid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "ethash 1.7.0", + "ethcore 1.7.0", + "ethcore-devtools 1.7.0", + "ethcore-io 1.7.0", + "ethcore-ipc 1.7.0", + "ethcore-light 1.7.0", + "ethcore-logger 1.7.0", + "ethcore-util 1.7.0", + "ethcrypto 0.1.0", + "ethjson 0.1.0", + "ethkey 0.2.0", + "ethstore 0.1.0", + "ethsync 1.7.0", + "fetch 0.1.0", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-minihttp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "multihash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-reactor 0.1.0", + "parity-updater 1.7.0", + "rlp 0.1.0", + "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", + "stats 0.1.0", + "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parity-rpc-client" version = "1.4.0" dependencies = [ - "ethcore-rpc 1.7.0", "ethcore-signer 1.7.0", "ethcore-util 1.7.0", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-rpc 1.7.0", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2081,9 +2093,9 @@ name = "rpc-cli" version = "1.4.0" dependencies = [ "ethcore-bigint 0.1.2", - "ethcore-rpc 1.7.0", "ethcore-util 1.7.0", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-rpc 1.7.0", "parity-rpc-client 1.4.0", "rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2710,6 +2722,21 @@ dependencies = [ "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ws" +version = "0.6.0" +source = "git+https://github.com/tomusdrw/ws-rs#3259e7ca906c848beae109eb32e492871f8f397d" +dependencies = [ + "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -2824,6 +2851,7 @@ dependencies = [ "checksum jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "" "checksum jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "" "checksum jsonrpc-tcp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "" +"checksum jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" @@ -2981,6 +3009,7 @@ dependencies = [ "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=parity-1.7)" = "" +"checksum ws 0.6.0 (git+https://github.com/tomusdrw/ws-rs)" = "" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xdg 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77b831a5ba77110f438f0ac5583aafeb087f70432998ba6b7dcb1d32185db453" "checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef" diff --git a/Cargo.toml b/Cargo.toml index 09af66e09..37effabb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,6 @@ ethcore = { path = "ethcore" } ethcore-util = { path = "util" } ethcore-io = { path = "util/io" } ethcore-devtools = { path = "devtools" } -ethcore-rpc = { path = "rpc" } ethcore-signer = { path = "signer" } ethcore-ipc = { path = "ipc/rpc" } ethcore-ipc-nano = { path = "ipc/nano" } @@ -45,12 +44,13 @@ ethkey = { path = "ethkey" } evmbin = { path = "evmbin" } rlp = { path = "util/rlp" } rpc-cli = { path = "rpc_cli" } -parity-rpc-client = { path = "rpc_client" } parity-hash-fetch = { path = "hash-fetch" } parity-ipfs-api = { path = "ipfs" } -parity-updater = { path = "updater" } -parity-reactor = { path = "util/reactor" } parity-local-store = { path = "local-store" } +parity-reactor = { path = "util/reactor" } +parity-rpc = { path = "rpc" } +parity-rpc-client = { path = "rpc_client" } +parity-updater = { path = "updater" } path = { path = "util/path" } parity-dapps = { path = "dapps", optional = true } @@ -85,7 +85,7 @@ ui-precompiled = [ dapps = ["parity-dapps"] ipc = ["ethcore/ipc", "ethsync/ipc"] jit = ["ethcore/jit"] -dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "parity-dapps/dev", "ethcore-signer/dev"] +dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "parity-rpc/dev", "parity-dapps/dev", "ethcore-signer/dev"] json-tests = ["ethcore/json-tests"] test-heavy = ["ethcore/test-heavy"] ethkey-cli = ["ethcore/ethkey-cli"] diff --git a/dapps/src/rpc.rs b/dapps/src/rpc.rs index b743408dc..74c6d8d89 100644 --- a/dapps/src/rpc.rs +++ b/dapps/src/rpc.rs @@ -17,7 +17,7 @@ use std::sync::Arc; use hyper; -use ethcore_rpc::{Metadata, Origin}; +use parity_rpc::{Metadata, Origin}; use jsonrpc_core::{Middleware, MetaIoHandler}; use jsonrpc_http_server::{self as http, AccessControlAllowOrigin, HttpMetaExtractor}; use jsonrpc_http_server::tokio_core::reactor::Remote; diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index 7301afa50..64ddd20e9 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -52,6 +52,14 @@ cors = "null" apis = ["web3", "eth", "net", "parity", "traces", "rpc"] hosts = ["none"] +[websockets] +disable = false +port = 8546 +interface = "local" +origins = ["none"] +apis = ["web3", "eth", "net", "parity", "traces", "rpc"] +hosts = ["none"] + [ipc] disable = false path = "$HOME/.parity/jsonrpc.ipc" diff --git a/parity/cli/config.toml b/parity/cli/config.toml index b7f2936a1..b6695f3f5 100644 --- a/parity/cli/config.toml +++ b/parity/cli/config.toml @@ -24,6 +24,9 @@ allow_ips = "public" reserved_only = true reserved_peers = "./path/to/reserved_peers" +[websockets] +disable = true +origins = ["none"] [rpc] disable = true diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 002ce6efb..ed1b6526f 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -170,6 +170,20 @@ usage! { flag_jsonrpc_threads: Option = None, or |c: &Config| otry!(c.rpc).threads.map(Some), + // WS + flag_no_ws: bool = false, + or |c: &Config| otry!(c.websockets).disable.clone(), + flag_ws_port: u16 = 8546u16, + or |c: &Config| otry!(c.websockets).port.clone(), + flag_ws_interface: String = "local", + or |c: &Config| otry!(c.websockets).interface.clone(), + flag_ws_apis: String = "web3,eth,net,parity,traces,rpc", + or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")), + flag_ws_origins: String = "none", + or |c: &Config| otry!(c.websockets).origins.as_ref().map(|vec| vec.join(",")), + flag_ws_hosts: String = "none", + or |c: &Config| otry!(c.websockets).hosts.as_ref().map(|vec| vec.join(",")), + // IPC flag_no_ipc: bool = false, or |c: &Config| otry!(c.ipc).disable.clone(), @@ -363,6 +377,7 @@ struct Config { ui: Option, network: Option, rpc: Option, + websockets: Option, ipc: Option, dapps: Option, secretstore: Option, @@ -440,6 +455,16 @@ struct Rpc { threads: Option, } +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Ws { + disable: Option, + port: Option, + interface: Option, + apis: Option>, + origins: Option>, + hosts: Option>, +} + #[derive(Default, Debug, PartialEq, RustcDecodable)] struct Ipc { disable: Option, @@ -554,7 +579,7 @@ struct Misc { mod tests { use super::{ Args, ArgsError, - Config, Operating, Account, Ui, Network, Rpc, Ipc, Dapps, Ipfs, Mining, Footprint, + Config, Operating, Account, Ui, Network, Ws, Rpc, Ipc, Dapps, Ipfs, Mining, Footprint, Snapshots, VM, Misc, SecretStore, }; use toml; @@ -699,6 +724,14 @@ mod tests { flag_jsonrpc_hosts: "none".into(), flag_jsonrpc_threads: None, + // WS + flag_no_ws: false, + flag_ws_port: 8546u16, + flag_ws_interface: "local".into(), + flag_ws_apis: "web3,eth,net,parity,traces,rpc".into(), + flag_ws_origins: "none".into(), + flag_ws_hosts: "none".into(), + // IPC flag_no_ipc: false, flag_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(), @@ -899,6 +932,14 @@ mod tests { reserved_only: Some(true), no_serve_light: None, }), + websockets: Some(Ws { + disable: Some(true), + port: None, + interface: None, + apis: None, + origins: Some(vec!["none".into()]), + hosts: None, + }), rpc: Some(Rpc { disable: Some(true), port: Some(8180), diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index 16f04e55e..17b6ce2b8 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -157,7 +157,28 @@ API and Console Options: vectors. Special options: "all", "none", (default: {flag_jsonrpc_hosts}). --jsonrpc-threads THREADS Enables experimental faster implementation of JSON-RPC server. - Requires Dapps server to be disabled using --no-dapps. (default: {flag_jsonrpc_threads:?}) + Requires Dapps server to be disabled + using --no-dapps. (default: {flag_jsonrpc_threads:?}) + + --no-ws Disable the WebSockets server. (default: {flag_no_ws}) + --ws-port PORT Specify the port portion of the WebSockets server + (default: {flag_ws_port}). + --ws-interface IP Specify the hostname portion of the WebSockets + server, IP should be an interface's IP address, or + all (all interfaces) or local (default: {flag_ws_interface}). + --ws-apis APIS Specify the APIs available through the WebSockets + interface. APIS is a comma-delimited list of API + name. Possible name are web3, eth, net, personal, + parity, parity_set, traces, rpc, parity_accounts. + (default: {flag_ws_apis}). + --ws-origins URL Specify Origin header values allowed to connect. + Special options: "all", "none". + (default: {flag_ws_origins}) + --ws-hosts HOSTS List of allowed Host header values. This option will + validate the Host header sent by the browser, it + is additional security against some attack + vectors. Special options: "all", "none", + (default: {flag_ws_hosts}). --no-ipc Disable JSON-RPC over IPC service. (default: {flag_no_ipc}) --ipc-path PATH Specify custom path for JSON-RPC over IPC service diff --git a/parity/configuration.rs b/parity/configuration.rs index fe26613d3..724633c28 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -30,9 +30,9 @@ use ethcore::client::{VMType}; use ethcore::miner::{MinerOptions, Banning, StratumOptions}; use ethcore::verification::queue::VerifierSettings; -use rpc::{IpcConfiguration, HttpConfiguration}; +use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration}; use rpc_apis::ApiSet; -use ethcore_rpc::NetworkSettings; +use parity_rpc::NetworkSettings; use cache::CacheConfig; use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, replace_home, replace_home_for_db, geth_ipc_path, parity_ipc_path, to_bootnodes, to_addresses, to_address, to_gas_limit, to_queue_strategy}; @@ -114,6 +114,7 @@ impl Configuration { }; let update_policy = self.update_policy()?; let logger_config = self.logger_config(); + let ws_conf = self.ws_config()?; let http_conf = self.http_config()?; let ipc_conf = self.ipc_config()?; let net_conf = self.net_config()?; @@ -352,6 +353,7 @@ impl Configuration { daemon: daemon, logger_config: logger_config.clone(), miner_options: miner_options, + ws_conf: ws_conf, http_conf: http_conf, ipc_conf: ipc_conf, net_conf: net_conf, @@ -757,6 +759,14 @@ impl Configuration { Self::hosts(&self.args.flag_jsonrpc_hosts) } + fn ws_hosts(&self) -> Option> { + Self::hosts(&self.args.flag_ws_hosts) + } + + fn ws_origins(&self) -> Option> { + Self::hosts(&self.args.flag_ws_origins) + } + fn ipfs_hosts(&self) -> Option> { Self::hosts(&self.args.flag_ipfs_api_hosts) } @@ -801,6 +811,19 @@ impl Configuration { Ok(conf) } + fn ws_config(&self) -> Result { + let conf = WsConfiguration { + enabled: self.ws_enabled(), + interface: self.ws_interface(), + port: self.args.flag_ws_port, + apis: self.args.flag_ws_apis.parse()?, + hosts: self.ws_hosts(), + origins: self.ws_origins() + }; + + Ok(conf) + } + fn network_settings(&self) -> NetworkSettings { NetworkSettings { name: self.args.flag_identity.clone(), @@ -913,6 +936,10 @@ impl Configuration { Self::interface(&self.network_settings().rpc_interface) } + fn ws_interface(&self) -> String { + Self::interface(&self.args.flag_ws_interface) + } + fn ipfs_interface(&self) -> String { Self::interface(&self.args.flag_ipfs_api_interface) } @@ -965,6 +992,10 @@ impl Configuration { !self.args.flag_jsonrpc_off && !self.args.flag_no_jsonrpc } + fn ws_enabled(&self) -> bool { + !self.args.flag_no_ws + } + fn dapps_enabled(&self) -> bool { !self.args.flag_dapps_off && !self.args.flag_no_dapps && self.rpc_enabled() && cfg!(feature = "dapps") } @@ -1000,7 +1031,7 @@ impl Configuration { mod tests { use super::*; use cli::Args; - use ethcore_rpc::NetworkSettings; + use parity_rpc::NetworkSettings; use ethcore::client::{VMType, BlockId}; use ethcore::miner::{MinerOptions, PrioritizationStrategy}; use helpers::{default_network_config}; @@ -1204,6 +1235,7 @@ mod tests { daemon: None, logger_config: Default::default(), miner_options: Default::default(), + ws_conf: Default::default(), http_conf: Default::default(), ipc_conf: Default::default(), net_conf: default_network_config(), diff --git a/parity/dapps.rs b/parity/dapps.rs index 28264e270..b2dfe16d4 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -109,7 +109,7 @@ pub use self::server::{SyncStatus, Middleware, dapps_middleware}; mod server { use super::Dependencies; use std::path::PathBuf; - use ethcore_rpc::{hyper, RequestMiddleware, RequestMiddlewareAction}; + use parity_rpc::{hyper, RequestMiddleware, RequestMiddlewareAction}; pub type SyncStatus = Fn() -> bool; diff --git a/parity/informant.rs b/parity/informant.rs index 4145b0282..6b21eefc0 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -30,8 +30,8 @@ use ethcore::service::ClientIoMessage; use ethcore::snapshot::service::Service as SnapshotService; use ethcore::snapshot::{RestorationStatus, SnapshotService as SS}; use number_prefix::{binary_prefix, Standalone, Prefixed}; -use ethcore_rpc::{is_major_importing}; -use ethcore_rpc::informant::RpcStats; +use parity_rpc::{is_major_importing}; +use parity_rpc::informant::RpcStats; pub struct Informant { report: RwLock>, diff --git a/parity/main.rs b/parity/main.rs index 0d55055da..c61d414be 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -51,18 +51,18 @@ extern crate ethcore_ipc_hypervisor as hypervisor; extern crate ethcore_ipc_nano as nanoipc; extern crate ethcore_light as light; extern crate ethcore_logger; -extern crate ethcore_rpc; extern crate ethcore_signer; extern crate ethcore_util as util; extern crate ethkey; extern crate ethsync; extern crate parity_hash_fetch as hash_fetch; extern crate parity_ipfs_api; -extern crate parity_reactor; -extern crate parity_updater as updater; extern crate parity_local_store as local_store; -extern crate rpc_cli; +extern crate parity_reactor; +extern crate parity_rpc; +extern crate parity_updater as updater; extern crate path; +extern crate rpc_cli; #[macro_use] extern crate log as rlog; diff --git a/parity/rpc.rs b/parity/rpc.rs index 70a91c851..1fc503767 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -14,19 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::{io, fmt}; +use std::io; use std::sync::Arc; use dapps; use dir::default_data_path; -use ethcore_rpc::informant::{RpcStats, Middleware}; -use ethcore_rpc::{self as rpc, HttpServerError, Metadata, Origin, AccessControlAllowOrigin, Host}; +use parity_rpc::informant::{RpcStats, Middleware}; +use parity_rpc::{self as rpc, HttpServerError, Metadata, Origin, DomainsValidation}; use helpers::parity_ipc_path; use jsonrpc_core::MetaIoHandler; use parity_reactor::TokioRemote; use rpc_apis::{self, ApiSet}; -pub use ethcore_rpc::{IpcServer, HttpServer, RequestMiddleware}; +pub use parity_rpc::{IpcServer, HttpServer, RequestMiddleware}; +pub use parity_rpc::ws::Server as WsServer; #[derive(Debug, Clone, PartialEq)] pub struct HttpConfiguration { @@ -71,12 +72,25 @@ impl Default for IpcConfiguration { } } -impl fmt::Display for IpcConfiguration { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.enabled { - write!(f, "endpoint address [{}], api list [{:?}]", self.socket_addr, self.apis) - } else { - write!(f, "disabled") +#[derive(Debug, PartialEq)] +pub struct WsConfiguration { + pub enabled: bool, + pub interface: String, + pub port: u16, + pub apis: ApiSet, + pub origins: Option>, + pub hosts: Option>, +} + +impl Default for WsConfiguration { + fn default() -> Self { + WsConfiguration { + enabled: true, + interface: "127.0.0.1".into(), + port: 8546, + apis: ApiSet::UnsafeContext, + origins: Some(Vec::new()), + hosts: Some(Vec::new()), } } } @@ -112,12 +126,71 @@ impl rpc::IpcMetaExtractor for RpcExtractor { } } +impl rpc::ws::MetaExtractor for RpcExtractor { + fn extract(&self, req: &rpc::ws::RequestContext) -> Metadata { + let mut metadata = Metadata::default(); + let id = req.session_id as u64; + metadata.origin = Origin::Ws(id.into()); + metadata + } +} + +struct WsStats { + stats: Arc, +} + +impl rpc::ws::SessionStats for WsStats { + fn open_session(&self, _id: rpc::ws::SessionId) { + self.stats.open_session() + } + + fn close_session(&self, _id: rpc::ws::SessionId) { + self.stats.close_session() + } +} + fn setup_apis(apis: ApiSet, deps: &Dependencies) -> MetaIoHandler> where D: rpc_apis::Dependencies { rpc_apis::setup_rpc(deps.stats.clone(), &*deps.apis, apis) } +pub fn new_ws( + conf: WsConfiguration, + deps: &Dependencies, +) -> Result, String> { + if !conf.enabled { + return Ok(None); + } + + let url = format!("{}:{}", conf.interface, conf.port); + let addr = url.parse().map_err(|_| format!("Invalid WebSockets listen host/port given: {}", url))?; + let handler = setup_apis(conf.apis, deps); + let remote = deps.remote.clone(); + let allowed_origins = into_domains(conf.origins); + let allowed_hosts = into_domains(conf.hosts); + + let start_result = rpc::start_ws( + &addr, + handler, + remote, + allowed_origins, + allowed_hosts, + RpcExtractor, + WsStats { + stats: deps.stats.clone(), + }, + ); + + match start_result { + Ok(server) => Ok(Some(server)), + Err(rpc::ws::Error::Io(ref err)) if err.kind() == io::ErrorKind::AddrInUse => Err( + format!("WebSockets address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --ws-port and --ws-interface options.", url) + ), + Err(e) => Err(format!("WebSockets error: {:?}", e)), + } +} + pub fn new_http( conf: HttpConfiguration, deps: &Dependencies, @@ -128,17 +201,17 @@ pub fn new_http( } let url = format!("{}:{}", conf.interface, conf.port); - let addr = url.parse().map_err(|_| format!("Invalid JSONRPC listen host/port given: {}", url))?; + let addr = url.parse().map_err(|_| format!("Invalid HTTP JSON-RPC listen host/port given: {}", url))?; let handler = setup_apis(conf.apis, deps); let remote = deps.remote.clone(); - let cors_domains: Option> = conf.cors.map(|domains| domains.into_iter().map(AccessControlAllowOrigin::from).collect()); - let allowed_hosts: Option> = conf.hosts.map(|hosts| hosts.into_iter().map(Host::from).collect()); + let cors_domains = into_domains(conf.cors); + let allowed_hosts = into_domains(conf.hosts); let start_result = rpc::start_http( &addr, - cors_domains.into(), - allowed_hosts.into(), + cors_domains, + allowed_hosts, handler, remote, RpcExtractor, @@ -153,16 +226,17 @@ pub fn new_http( match start_result { Ok(server) => Ok(Some(server)), - Err(HttpServerError::Io(err)) => match err.kind() { - io::ErrorKind::AddrInUse => Err( - format!("RPC address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --jsonrpc-port and --jsonrpc-interface options.", url) - ), - _ => Err(format!("RPC io error: {}", err)), - }, - Err(e) => Err(format!("RPC error: {:?}", e)), + Err(HttpServerError::Io(ref err)) if err.kind() == io::ErrorKind::AddrInUse => Err( + format!("HTTP address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --jsonrpc-port and --jsonrpc-interface options.", url) + ), + Err(e) => Err(format!("HTTP error: {:?}", e)), } } +fn into_domains>(items: Option>) -> DomainsValidation { + items.map(|vals| vals.into_iter().map(T::from).collect()).into() +} + pub fn new_ipc( conf: IpcConfiguration, dependencies: &Dependencies @@ -170,18 +244,19 @@ pub fn new_ipc( if !conf.enabled { return Ok(None); } + let handler = setup_apis(conf.apis, dependencies); let remote = dependencies.remote.clone(); match rpc::start_ipc(&conf.socket_addr, handler, remote, RpcExtractor) { Ok(server) => Ok(Some(server)), - Err(io_error) => Err(format!("RPC io error: {}", io_error)), + Err(io_error) => Err(format!("IPC error: {}", io_error)), } } #[cfg(test)] mod tests { use super::RpcExtractor; - use ethcore_rpc::{HttpMetaExtractor, Origin}; + use parity_rpc::{HttpMetaExtractor, Origin}; #[test] fn should_extract_rpc_origin() { diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 538808909..15fe660b2 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -20,15 +20,15 @@ use std::collections::HashSet; use std::str::FromStr; use std::sync::Arc; -pub use ethcore_rpc::SignerService; +pub use parity_rpc::SignerService; use ethcore::account_provider::AccountProvider; use ethcore::client::Client; use ethcore::miner::{Miner, ExternalMiner}; use ethcore::snapshot::SnapshotService; -use ethcore_rpc::{Metadata, NetworkSettings}; -use ethcore_rpc::informant::{ActivityNotifier, Middleware, RpcStats, ClientNotifier}; -use ethcore_rpc::dispatch::{FullDispatcher, LightDispatcher}; +use parity_rpc::{Metadata, NetworkSettings}; +use parity_rpc::informant::{ActivityNotifier, Middleware, RpcStats, ClientNotifier}; +use parity_rpc::dispatch::{FullDispatcher, LightDispatcher}; use ethsync::{ManageNetwork, SyncProvider, LightSync}; use hash_fetch::fetch::Client as FetchClient; use jsonrpc_core::{MetaIoHandler}; @@ -203,7 +203,7 @@ impl Dependencies for FullDependencies { } fn extend_with_set(&self, handler: &mut MetaIoHandler, apis: &[Api]) { - use ethcore_rpc::v1::*; + use parity_rpc::v1::*; macro_rules! add_signing_methods { ($namespace:ident, $handler:expr, $deps:expr) => { @@ -331,7 +331,7 @@ impl Dependencies for LightDependencies { fn activity_notifier(&self) -> Self::Notifier { LightClientNotifier } fn extend_with_set(&self, handler: &mut MetaIoHandler>, apis: &[Api]) { - use ethcore_rpc::v1::*; + use parity_rpc::v1::*; let dispatcher = LightDispatcher::new( self.sync.clone(), diff --git a/parity/run.rs b/parity/run.rs index 70a23f934..a76c6a4ca 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -18,7 +18,7 @@ use std::sync::Arc; use std::net::{TcpListener}; use ctrlc::CtrlC; use fdlimit::raise_fd_limit; -use ethcore_rpc::{NetworkSettings, informant, is_major_importing}; +use parity_rpc::{NetworkSettings, informant, is_major_importing}; use ethsync::NetworkConfiguration; use util::{Colour, version, Mutex, Condvar}; use io::{MayPanic, ForwardPanic, PanicHandler}; @@ -80,6 +80,7 @@ pub struct RunCmd { pub daemon: Option, pub logger_config: LogConfig, pub miner_options: MinerOptions, + pub ws_conf: rpc::WsConfiguration, pub http_conf: rpc::HttpConfiguration, pub ipc_conf: rpc::IpcConfiguration, pub net_conf: NetworkConfiguration, @@ -295,7 +296,8 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc) -> }; // start rpc servers - let _http_server = rpc::new_http(cmd.http_conf, &dependencies, None)?; + let _ws_server = rpc::new_ws(cmd.ws_conf, &dependencies)?; + let _http_server = rpc::new_http(cmd.http_conf.clone(), &dependencies, None)?; let _ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?; // the signer server @@ -636,6 +638,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps)?; // start rpc servers + let ws_server = rpc::new_ws(cmd.ws_conf, &dependencies)?; let http_server = rpc::new_http(cmd.http_conf.clone(), &dependencies, dapps_middleware)?; let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?; @@ -716,7 +719,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R let restart = wait_for_exit(panic_handler, Some(updater), Some(client), can_restart); // drop this stuff as soon as exit detected. - drop((http_server, ipc_server, signer_server, secretstore_key_server, ipfs_server, event_loop)); + drop((ws_server, http_server, ipc_server, signer_server, secretstore_key_server, ipfs_server, event_loop)); info!("Finishing work, please wait..."); diff --git a/parity/signer.rs b/parity/signer.rs index 29429311e..1ab53ea69 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -22,8 +22,8 @@ pub use ethcore_signer::Server as SignerServer; use ansi_term::Colour; use dir::default_data_path; -use ethcore_rpc::informant::RpcStats; -use ethcore_rpc::{self, ConfirmationsQueue}; +use parity_rpc::informant::RpcStats; +use parity_rpc::{self, ConfirmationsQueue}; use ethcore_signer as signer; use helpers::replace_home; use parity_reactor::TokioRemote; @@ -69,10 +69,10 @@ pub struct NewToken { #[derive(Debug, Default, Clone)] pub struct StandardExtractor; -impl signer::MetaExtractor for StandardExtractor { - fn extract_metadata(&self, session: &H256) -> ethcore_rpc::Metadata { - let mut metadata = ethcore_rpc::Metadata::default(); - metadata.origin = ethcore_rpc::Origin::Signer((*session).into()); +impl signer::MetaExtractor for StandardExtractor { + fn extract_metadata(&self, session: &H256) -> parity_rpc::Metadata { + let mut metadata = parity_rpc::Metadata::default(); + metadata.origin = parity_rpc::Origin::Signer((*session).into()); metadata } } diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 8b694b765..87e340b02 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] -description = "Ethcore jsonrpc" -name = "ethcore-rpc" +description = "Parity JSON-RPC servers." +name = "parity-rpc" version = "1.7.0" license = "GPL-3.0" authors = ["Parity Technologies "] @@ -25,6 +25,7 @@ rust-crypto = "0.2.36" jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } jsonrpc-minihttp-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } +jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } jsonrpc-ipc-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } diff --git a/rpc/rpctest/Cargo.toml b/rpc/rpctest/Cargo.toml index 369de1c99..258af2f39 100644 --- a/rpc/rpctest/Cargo.toml +++ b/rpc/rpctest/Cargo.toml @@ -8,10 +8,10 @@ authors = ["Parity Technologies "] [dependencies] ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" } docopt = "0.7" -rustc-serialize = "0.3" ethcore = { path = "../../ethcore" } ethcore-devtools = { path = "../../devtools" } -ethcore-rpc = { path = ".." } ethcore-util = { path = "../../util" } ethjson = { path = "../../json" } +parity-rpc = { path = ".." } +rustc-serialize = "0.3" serde_json = "0.8" diff --git a/rpc/rpctest/src/main.rs b/rpc/rpctest/src/main.rs index 6776a913a..d65d2462a 100644 --- a/rpc/rpctest/src/main.rs +++ b/rpc/rpctest/src/main.rs @@ -16,13 +16,13 @@ extern crate ctrlc; extern crate docopt; -extern crate rustc_serialize; -extern crate serde_json; -extern crate ethjson; -extern crate ethcore_util as util; extern crate ethcore; extern crate ethcore_devtools as devtools; -extern crate ethcore_rpc as rpc; +extern crate ethcore_util as util; +extern crate ethjson; +extern crate parity_rpc as rpc; +extern crate rustc_serialize; +extern crate serde_json; use std::collections::HashMap; use std::sync::{Arc, Mutex, Condvar}; diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 1d3f4285d..53b8f994c 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -66,6 +66,8 @@ extern crate ethjson; #[cfg(test)] extern crate ethcore_devtools as devtools; +pub extern crate jsonrpc_ws_server as ws; + mod metadata; pub mod v1; @@ -73,7 +75,7 @@ pub use ipc::{Server as IpcServer, MetaExtractor as IpcMetaExtractor, RequestCon pub use http::{ hyper, RequestMiddleware, RequestMiddlewareAction, - AccessControlAllowOrigin, Host, + AccessControlAllowOrigin, Host, DomainsValidation }; pub use v1::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, Metadata, Origin, informant, dispatch}; @@ -193,3 +195,28 @@ pub fn start_ipc( .session_metadata_extractor(extractor) .start(addr) } + +/// Start WS server and return `Server` handle. +pub fn start_ws( + addr: &SocketAddr, + handler: H, + remote: tokio_core::reactor::Remote, + allowed_origins: ws::DomainsValidation, + allowed_hosts: ws::DomainsValidation, + extractor: T, + stats: U, +) -> Result where + M: jsonrpc_core::Metadata, + S: jsonrpc_core::Middleware, + H: Into>, + T: ws::MetaExtractor, + U: ws::SessionStats, +{ + ws::ServerBuilder::new(handler) + .event_loop_remote(remote) + .allowed_origins(allowed_origins) + .allowed_hosts(allowed_hosts) + .session_meta_extractor(extractor) + .session_stats(stats) + .start(addr) +} diff --git a/rpc/src/v1/types/provenance.rs b/rpc/src/v1/types/provenance.rs index 1e014dd5f..81201f8e4 100644 --- a/rpc/src/v1/types/provenance.rs +++ b/rpc/src/v1/types/provenance.rs @@ -33,6 +33,9 @@ pub enum Origin { /// IPC server (includes session hash) #[serde(rename="ipc")] Ipc(H256), + /// WS server (includes session hash) + #[serde(rename="ws")] + Ws(H256), /// Signer (includes session hash) #[serde(rename="signer")] Signer(H256), @@ -53,6 +56,7 @@ impl fmt::Display for Origin { Origin::Rpc(ref origin) => write!(f, "RPC (service: {})", origin), Origin::Dapps(ref origin) => write!(f, "Dapp {}", origin), Origin::Ipc(ref session) => write!(f, "IPC (session: {})", session), + Origin::Ws(ref session) => write!(f, "WebSocket (session: {})", session), Origin::Signer(ref session) => write!(f, "UI (session: {})", session), Origin::Unknown => write!(f, "unknown origin"), } @@ -112,6 +116,7 @@ mod tests { let o3 = Origin::Ipc(5.into()); let o4 = Origin::Signer(10.into()); let o5 = Origin::Unknown; + let o6 = Origin::Ws(5.into()); // when let res1 = serde_json::to_string(&o1).unwrap(); @@ -119,6 +124,7 @@ mod tests { let res3 = serde_json::to_string(&o3).unwrap(); let res4 = serde_json::to_string(&o4).unwrap(); let res5 = serde_json::to_string(&o5).unwrap(); + let res6 = serde_json::to_string(&o6).unwrap(); // then assert_eq!(res1, r#"{"rpc":"test service"}"#); @@ -126,6 +132,7 @@ mod tests { assert_eq!(res3, r#"{"ipc":"0x0000000000000000000000000000000000000000000000000000000000000005"}"#); assert_eq!(res4, r#"{"signer":"0x000000000000000000000000000000000000000000000000000000000000000a"}"#); assert_eq!(res5, r#""unknown""#); + assert_eq!(res6, r#"{"ws":"0x0000000000000000000000000000000000000000000000000000000000000005"}"#); } #[test] diff --git a/rpc_cli/Cargo.toml b/rpc_cli/Cargo.toml index 8169d3b71..bb3588015 100644 --- a/rpc_cli/Cargo.toml +++ b/rpc_cli/Cargo.toml @@ -10,6 +10,6 @@ version = "1.4.0" futures = "0.1" rpassword = "0.3.0" ethcore-bigint = { path = "../util/bigint" } -ethcore-rpc = { path = "../rpc" } +parity-rpc = { path = "../rpc" } parity-rpc-client = { path = "../rpc_client" } ethcore-util = { path = "../util" } diff --git a/rpc_cli/src/lib.rs b/rpc_cli/src/lib.rs index 0cfb54353..8d00405b5 100644 --- a/rpc_cli/src/lib.rs +++ b/rpc_cli/src/lib.rs @@ -1,10 +1,10 @@ extern crate futures; - -extern crate ethcore_util as util; -extern crate ethcore_rpc as rpc; -extern crate ethcore_bigint as bigint; extern crate rpassword; +extern crate ethcore_util as util; +extern crate ethcore_bigint as bigint; + +extern crate parity_rpc as rpc; extern crate parity_rpc_client as client; use rpc::v1::types::{U256, ConfirmationRequest}; diff --git a/rpc_client/Cargo.toml b/rpc_client/Cargo.toml index 7b6be3bb2..c4e68acef 100644 --- a/rpc_client/Cargo.toml +++ b/rpc_client/Cargo.toml @@ -16,6 +16,6 @@ tempdir = "0.3.5" url = "1.2.0" jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } ws = { git = "https://github.com/paritytech/ws-rs.git", branch = "parity-1.7" } -ethcore-rpc = { path = "../rpc" } +parity-rpc = { path = "../rpc" } ethcore-signer = { path = "../signer" } ethcore-util = { path = "../util" } diff --git a/rpc_client/src/lib.rs b/rpc_client/src/lib.rs index 7bc265250..e2f53e606 100644 --- a/rpc_client/src/lib.rs +++ b/rpc_client/src/lib.rs @@ -1,17 +1,17 @@ pub mod client; pub mod signer_client; -extern crate ws; extern crate ethcore_signer; -extern crate url; -extern crate futures; extern crate ethcore_util as util; -extern crate ethcore_rpc as rpc; +extern crate futures; +extern crate jsonrpc_core; +extern crate parity_rpc as rpc; +extern crate rand; extern crate serde; extern crate serde_json; -extern crate rand; extern crate tempdir; -extern crate jsonrpc_core; +extern crate url; +extern crate ws; #[macro_use] extern crate log; diff --git a/scripts/targets.sh b/scripts/targets.sh index 040485d85..f3ae6a2d5 100644 --- a/scripts/targets.sh +++ b/scripts/targets.sh @@ -6,7 +6,7 @@ export TARGETS=" -p ethcore \ -p ethcore-bigint\ -p parity-dapps \ - -p ethcore-rpc \ + -p parity-rpc \ -p ethcore-signer \ -p ethcore-util \ -p ethcore-network \ diff --git a/signer/Cargo.toml b/signer/Cargo.toml index bed1c461a..4f7c0f179 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -20,7 +20,7 @@ ws = { git = "https://github.com/paritytech/ws-rs.git", branch = "parity-1.7" } parity-dapps-glue = { version = "1.7", optional = true } ethcore-util = { path = "../util" } ethcore-io = { path = "../util/io" } -ethcore-rpc = { path = "../rpc" } +parity-rpc = { path = "../rpc" } ethcore-devtools = { path = "../devtools" } parity-ui = { path = "../dapps/ui", version = "1.4", optional = true } diff --git a/signer/src/lib.rs b/signer/src/lib.rs index d211e2eac..a1c935c5b 100644 --- a/signer/src/lib.rs +++ b/signer/src/lib.rs @@ -32,13 +32,13 @@ //! extern crate jsonrpc_core; //! extern crate jsonrpc_server_utils; //! extern crate ethcore_signer; -//! extern crate ethcore_rpc; +//! extern crate parity_rpc; //! //! use std::sync::Arc; //! use jsonrpc_core::IoHandler; //! use jsonrpc_server_utils::reactor::RpcEventLoop; //! use ethcore_signer::ServerBuilder; -//! use ethcore_rpc::ConfirmationsQueue; +//! use parity_rpc::ConfirmationsQueue; //! //! fn main() { //! let queue = Arc::new(ConfirmationsQueue::default()); @@ -55,11 +55,11 @@ extern crate log; extern crate env_logger; extern crate rand; -extern crate ethcore_util as util; -extern crate ethcore_rpc as rpc; extern crate ethcore_io as io; +extern crate ethcore_util as util; extern crate jsonrpc_core; extern crate jsonrpc_server_utils; +extern crate parity_rpc as rpc; extern crate ws; extern crate ethcore_devtools as devtools; From 715d5daafe1f1c32878aea26ba32ad038bb4f9ca Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 13 Apr 2017 20:24:21 +0200 Subject: [PATCH 020/117] ChainVerifier for memoizing validator sets --- ethcore/res/ethereum/tests | 2 +- ethcore/src/client/client.rs | 22 ++ ethcore/src/engines/authority_round.rs | 197 ++++++++++++------ ethcore/src/engines/basic_authority.rs | 43 +++- ethcore/src/engines/chain_verifier.rs | 42 ++++ ethcore/src/engines/mod.rs | 45 ++-- ethcore/src/engines/validator_set/contract.rs | 6 +- ethcore/src/engines/validator_set/mod.rs | 5 +- ethcore/src/engines/validator_set/multi.rs | 4 + .../engines/validator_set/safe_contract.rs | 9 + .../src/engines/validator_set/simple_list.rs | 7 +- ethcore/src/ethereum/ethash.rs | 19 +- 12 files changed, 311 insertions(+), 90 deletions(-) create mode 100644 ethcore/src/engines/chain_verifier.rs diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index 9028c4801..d52059307 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit 9028c4801fd39fbb71a9796979182549a24e81c8 +Subproject commit d520593078fa0849dcd1f907e44ed0a616892e33 diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 4bd29d100..13cb95afe 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -569,11 +569,33 @@ impl Client { //let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new)); let mut batch = DBTransaction::new(); + + // generate validation proof if the engine requires them. + // TODO: make conditional? + let generate_proof = { + use engines::RequiresProof; + match self.engine.proof_required(block.header(), Some(block_data), Some(&receipts)) { + RequiresProof::Yes(_) => true, + RequiresProof::No => false, + RequiresProof::Unsure(_) => { + warn!(target: "client", "Detected invalid engine implementation."); + warn!(target: "client", "Engine claims to require more block data, but everything provided."); + false + } + } + }; + // CHECK! I *think* this is fine, even if the state_root is equal to another // already-imported block of the same number. // TODO: Prove it with a test. let mut state = block.drain(); + if generate_proof { + debug!(target: "client", "Generating validation proof for block {}", hash); + + // TODO + } + state.journal_under(&mut batch, number, hash).expect("DB commit failed"); let route = chain.insert_block(&mut batch, block_data, receipts); self.tracedb.read().import(&mut batch, TraceImportRequest { diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index e57fb27bc..8e8450768 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -37,7 +37,7 @@ use transaction::UnverifiedTransaction; use client::{Client, EngineClient}; use state::CleanupMode; use super::signer::EngineSigner; -use super::validator_set::{ValidatorSet, new_validator_set}; +use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; /// `AuthorityRound` params. #[derive(Debug, PartialEq)] @@ -75,27 +75,76 @@ impl From for AuthorityRoundParams { } } -/// Engine using `AuthorityRound` proof-of-work consensus algorithm, suitable for Ethereum -/// mainnet chains in the Olympic, Frontier and Homestead eras. +// Helper for managing the step. +#[derive(Debug)] +struct Step { + calibrate: bool, // whether calibration is enabled. + inner: AtomicUsize, + duration: Duration, +} + +impl Step { + fn load(&self) -> usize { self.inner.load(AtomicOrdering::SeqCst) } + fn duration_remaining(&self) -> Duration { + let now = unix_now(); + let step_end = self.duration * (self.load() as u32 + 1); + if step_end > now { + step_end - now + } else { + Duration::from_secs(0) + } + } + fn increment(&self) { + self.inner.fetch_add(1, AtomicOrdering::SeqCst); + } + fn calibrate(&self) { + if self.calibrate { + let new_step = unix_now().as_secs() / self.duration.as_secs(); + self.inner.store(new_step as usize, AtomicOrdering::SeqCst); + } + } + fn is_future(&self, given: usize) -> bool { + if given > self.load() + 1 { + // Make absolutely sure that the given step is correct. + self.calibrate(); + given > self.load() + 1 + } else { + false + } + } +} + +/// Engine using `AuthorityRound` proof-of-authority BFT consensus. pub struct AuthorityRound { params: CommonParams, gas_limit_bound_divisor: U256, block_reward: U256, registrar: Address, - step_duration: Duration, builtins: BTreeMap, transition_service: IoService<()>, - step: AtomicUsize, + step: Arc, proposed: AtomicBool, client: RwLock>>, signer: EngineSigner, validators: Box, - /// Is this Engine just for testing (prevents step calibration). - calibrate_step: bool, validate_score_transition: u64, eip155_transition: u64, } +// header-chain validator. +struct ChainVerifier { + step: Arc, + subchain_validators: SimpleList, +} + +impl super::ChainVerifier for ChainVerifier { + fn verify_light(&self, header: &Header) -> Result<(), Error> { + // always check the seal since it's fast. + // nothing heavier to do. + verify_external(header, &self.subchain_validators, &*self.step) + } +} + fn header_step(header: &Header) -> Result { 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() } @@ -104,6 +153,26 @@ fn header_signature(header: &Header) -> Result { UntrustedRlp::new(&header.seal().get(1).expect("was checked with verify_block_basic; has 2 fields; qed")).as_val::().map(Into::into) } +fn verify_external(header: &Header, validators: &ValidatorSet, step: &Step) -> Result<(), Error> { + let header_step = header_step(header)?; + + // Give one step slack if step is lagging, double vote is still not possible. + if step.is_future(header_step) { + trace!(target: "engine", "verify_block_unordered: block from the future"); + validators.report_benign(header.author()); + Err(BlockError::InvalidSeal)? + } else { + let proposer_signature = header_signature(header)?; + let correct_proposer = validators.get(header.parent_hash(), header_step); + if !verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? { + trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", header_step); + Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))? + } else { + Ok(()) + } + } +} + trait AsMillis { fn as_millis(&self) -> u64; } @@ -125,15 +194,17 @@ impl AuthorityRound { gas_limit_bound_divisor: our_params.gas_limit_bound_divisor, block_reward: our_params.block_reward, registrar: our_params.registrar, - step_duration: our_params.step_duration, builtins: builtins, transition_service: IoService::<()>::start()?, - step: AtomicUsize::new(initial_step), + step: Arc::new(Step { + inner: AtomicUsize::new(initial_step), + calibrate: our_params.start_step.is_none(), + duration: our_params.step_duration, + }), proposed: AtomicBool::new(false), client: RwLock::new(None), signer: Default::default(), validators: new_validator_set(our_params.validators), - calibrate_step: our_params.start_step.is_none(), validate_score_transition: our_params.validate_score_transition, eip155_transition: our_params.eip155_transition, }); @@ -145,22 +216,6 @@ impl AuthorityRound { Ok(engine) } - fn calibrate_step(&self) { - if self.calibrate_step { - self.step.store((unix_now().as_secs() / self.step_duration.as_secs()) as usize, AtomicOrdering::SeqCst); - } - } - - fn remaining_step_duration(&self) -> Duration { - let now = unix_now(); - let step_end = self.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1); - if step_end > now { - step_end - now - } else { - Duration::from_secs(0) - } - } - fn step_proposer(&self, bh: &H256, step: usize) -> Address { self.validators.get(bh, step) } @@ -168,16 +223,6 @@ impl AuthorityRound { fn is_step_proposer(&self, bh: &H256, step: usize, address: &Address) -> bool { self.step_proposer(bh, step) == *address } - - fn is_future_step(&self, step: usize) -> bool { - if step > self.step.load(AtomicOrdering::SeqCst) + 1 { - // Make absolutely sure that the step is correct. - self.calibrate_step(); - step > self.step.load(AtomicOrdering::SeqCst) + 1 - } else { - false - } - } } fn unix_now() -> Duration { @@ -193,7 +238,8 @@ const ENGINE_TIMEOUT_TOKEN: TimerToken = 23; impl IoHandler<()> for TransitionHandler { fn initialize(&self, io: &IoContext<()>) { if let Some(engine) = self.engine.upgrade() { - io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis()) + let remaining = engine.step.duration_remaining(); + io.register_timer_once(ENGINE_TIMEOUT_TOKEN, remaining.as_millis()) .unwrap_or_else(|e| warn!(target: "engine", "Failed to start consensus step timer: {}.", e)) } } @@ -202,7 +248,8 @@ impl IoHandler<()> for TransitionHandler { if timer == ENGINE_TIMEOUT_TOKEN { if let Some(engine) = self.engine.upgrade() { engine.step(); - io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis()) + let remaining = engine.step.duration_remaining(); + io.register_timer_once(ENGINE_TIMEOUT_TOKEN, remaining.as_millis()) .unwrap_or_else(|e| warn!(target: "engine", "Failed to restart consensus step timer: {}.", e)) } } @@ -224,7 +271,7 @@ impl Engine for AuthorityRound { fn builtins(&self) -> &BTreeMap { &self.builtins } fn step(&self) { - self.step.fetch_add(1, AtomicOrdering::SeqCst); + self.step.increment(); self.proposed.store(false, AtomicOrdering::SeqCst); if let Some(ref weak) = *self.client.read() { if let Some(c) = weak.upgrade() { @@ -247,7 +294,7 @@ impl Engine for AuthorityRound { fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { // Chain scoring: total weight is sqrt(U256::max_value())*height - step - let new_difficulty = U256::from(U128::max_value()) + header_step(parent).expect("Header has been verified; qed").into() - self.step.load(AtomicOrdering::SeqCst).into(); + let new_difficulty = U256::from(U128::max_value()) + header_step(parent).expect("Header has been verified; qed").into() - self.step.load().into(); header.set_difficulty(new_difficulty); header.set_gas_limit({ let gas_limit = parent.gas_limit().clone(); @@ -271,7 +318,7 @@ impl Engine for AuthorityRound { fn generate_seal(&self, block: &ExecutedBlock) -> Seal { if self.proposed.load(AtomicOrdering::SeqCst) { return Seal::None; } let header = block.header(); - let step = self.step.load(AtomicOrdering::SeqCst); + let step = self.step.load(); if self.is_step_proposer(header.parent_hash(), step, header.author()) { if let Ok(signature) = self.signer.sign(header.bare_hash()) { trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step); @@ -319,32 +366,19 @@ impl Engine for AuthorityRound { Ok(()) } - /// Do the validator and gas limit validation. + /// Do the step and gas limit validation. fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { let step = header_step(header)?; - // Give one step slack if step is lagging, double vote is still not possible. - if self.is_future_step(step) { - trace!(target: "engine", "verify_block_unordered: block from the future"); - self.validators.report_benign(header.author()); - Err(BlockError::InvalidSeal)? - } else { - let proposer_signature = header_signature(header)?; - let correct_proposer = self.step_proposer(header.parent_hash(), step); - if !verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? { - trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", step); - Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))? - } - } // Do not calculate difficulty for genesis blocks. if header.number() == 0 { return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); } - // Check if parent is from a previous step. + // Ensure header is from the step after parent. let parent_step = header_step(parent)?; - if step == parent_step { - trace!(target: "engine", "Multiple blocks proposed for step {}.", step); + if step <= parent_step { + trace!(target: "engine", "Multiple blocks proposed for step {}.", parent_step); self.validators.report_malicious(header.author()); Err(EngineError::DoubleVote(header.author().clone()))?; } @@ -358,6 +392,11 @@ impl Engine for AuthorityRound { Ok(()) } + // Check the validators. + fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + verify_external(header, &*self.validators, &*self.step) + } + // the proofs we need just allow us to get the full validator set. fn prove_with_caller(&self, header: &Header, caller: &Call) -> Result { self.validators.generate_proof(header, caller) @@ -370,6 +409,16 @@ impl Engine for AuthorityRound { self.validators.proof_required(header, block, receipts) } + fn chain_verifier(&self, header: &Header, proof: Bytes) -> Result, Error> { + // extract a simple list from the proof. + let simple_list = self.validators.chain_verifier(header, proof)?; + + Ok(Box::new(ChainVerifier { + step: self.step.clone(), + subchain_validators: simple_list, + })) + } + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> result::Result<(), Error> { t.check_low_s()?; @@ -453,7 +502,7 @@ mod tests { let mut header: Header = Header::default(); header.set_seal(vec![encode(&H520::default()).to_vec()]); - let verify_result = engine.verify_block_family(&header, &Default::default(), None); + let verify_result = engine.verify_block_external(&header, None); assert!(verify_result.is_err()); } @@ -507,9 +556,11 @@ mod tests { // Two validators. // Spec starts with step 2. header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_err()); + assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_external(&header, None).is_err()); header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_external(&header, None).is_ok()); } #[test] @@ -532,7 +583,33 @@ mod tests { // Spec starts with step 2. header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_external(&header, None).is_ok()); header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); + assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_external(&header, None).is_err()); + } + + #[test] + fn rejects_step_backwards() { + let tap = AccountProvider::transient_provider(); + let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap(); + + let mut parent_header: Header = Header::default(); + parent_header.set_seal(vec![encode(&4usize).to_vec()]); + parent_header.set_gas_limit(U256::from_str("222222").unwrap()); + let mut header: Header = Header::default(); + header.set_number(1); + header.set_gas_limit(U256::from_str("222222").unwrap()); + header.set_author(addr); + + let engine = Spec::new_test_round().engine; + + let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); + // Two validators. + // Spec starts with step 2. + header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); + assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + header.set_seal(vec![encode(&3usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); assert!(engine.verify_block_family(&header, &parent_header, None).is_err()); } } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index a785b360a..25cdd765f 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -31,7 +31,7 @@ use ethjson; use header::Header; use client::Client; use super::signer::EngineSigner; -use super::validator_set::{ValidatorSet, new_validator_set}; +use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; /// `BasicAuthority` params. #[derive(Debug, PartialEq)] @@ -51,6 +51,27 @@ impl From for BasicAuthorityParams { } } +struct ChainVerifier(SimpleList); + +impl super::ChainVerifier for ChainVerifier { + fn verify_light(&self, header: &Header) -> Result<(), Error> { + verify_external(header, &self.0) + } +} + +fn verify_external(header: &Header, validators: &ValidatorSet) -> Result<(), Error> { + use rlp::UntrustedRlp; + + // Check if the signature belongs to a validator, can depend on parent state. + let sig = UntrustedRlp::new(&header.seal()[0]).as_val::()?; + let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?); + + match validators.contains(header.parent_hash(), &signer) { + false => Err(BlockError::InvalidSeal.into()), + true => Ok(()) + } +} + /// Engine using `BasicAuthority`, trivial proof-of-authority consensus. pub struct BasicAuthority { params: CommonParams, @@ -137,15 +158,6 @@ impl Engine for BasicAuthority { } fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { - use rlp::UntrustedRlp; - - // Check if the signature belongs to a validator, can depend on parent state. - let sig = UntrustedRlp::new(&header.seal()[0]).as_val::()?; - let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?); - if !self.validators.contains(header.parent_hash(), &signer) { - return Err(BlockError::InvalidSeal)?; - } - // Do not calculate difficulty for genesis blocks. if header.number() == 0 { return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); @@ -164,6 +176,10 @@ impl Engine for BasicAuthority { Ok(()) } + fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + verify_external(header, &*self.validators) + } + // the proofs we need just allow us to get the full validator set. fn prove_with_caller(&self, header: &Header, caller: &Call) -> Result { self.validators.generate_proof(header, caller) @@ -176,6 +192,13 @@ impl Engine for BasicAuthority { self.validators.proof_required(header, block, receipts) } + fn chain_verifier(&self, header: &Header, proof: Bytes) -> Result, Error> { + // extract a simple list from the proof. + let simple_list = self.validators.chain_verifier(header, proof)?; + + Ok(Box::new(ChainVerifier(simple_list))) + } + fn register_client(&self, client: Weak) { self.validators.register_contract(client); } diff --git a/ethcore/src/engines/chain_verifier.rs b/ethcore/src/engines/chain_verifier.rs new file mode 100644 index 000000000..399832fe9 --- /dev/null +++ b/ethcore/src/engines/chain_verifier.rs @@ -0,0 +1,42 @@ +// Copyright 2015-2017 Parity Technologies (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 . + +// Chain verifiers. + +use error::Error; +use header::Header; + +/// Sequential chain verifier. +/// +/// See docs on `Engine` relating to proving functions for more details. +/// Headers here will have already passed "basic" verification. +pub trait ChainVerifier { + /// Lightly verify the next block header. + /// This may not be a header that requires a proof. + fn verify_light(&self, header: &Header) -> Result<(), Error>; + + /// Perform potentially heavier checks on the next block header. + fn verify_heavy(&self, header: &Header) -> Result<(), Error> { + self.verify_light(header) + } +} + +/// No-op chain verifier. +pub struct NoOp; + +impl ChainVerifier for NoOp { + fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } +} diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index f1e0a4d43..7d4906dd2 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -16,20 +16,22 @@ //! Consensus engine specification and basic implementations. -mod transition; -mod vote_collector; -mod null_engine; -mod instant_seal; -mod basic_authority; mod authority_round; -mod tendermint; -mod validator_set; +mod basic_authority; +mod chain_verifier; +mod instant_seal; +mod null_engine; mod signer; +mod tendermint; +mod transition; +mod validator_set; +mod vote_collector; -pub use self::null_engine::NullEngine; -pub use self::instant_seal::InstantSeal; -pub use self::basic_authority::BasicAuthority; pub use self::authority_round::AuthorityRound; +pub use self::basic_authority::BasicAuthority; +pub use self::chain_verifier::ChainVerifier; +pub use self::instant_seal::InstantSeal; +pub use self::null_engine::NullEngine; pub use self::tendermint::Tendermint; use std::sync::Weak; @@ -182,6 +184,9 @@ pub trait Engine : Sync + Send { /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. fn verify_block_family(&self, _header: &Header, _parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + /// Phase 4 verification. Verify block header against potentially external data. + fn verify_block_external(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + /// Additional verification for transactions in blocks. // TODO: Add flags for which bits of the transaction to check. // TODO: consider including State in the params. @@ -214,13 +219,21 @@ pub trait Engine : Sync + Send { self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None)) } - /// Re-do all verification for a header with the given contract-calling interface + /// Generate validation proof. /// /// This will be used to generate proofs of validation as well as verify them. /// Must be called on blocks that have already passed basic verification. /// /// Return the "validation proof" generated. - fn prove_with_caller(&self, _header: &Header, _caller: &Call) -> Result { + /// This must be usable to generate a `ChainVerifier` for verifying all blocks + /// from the supplied header up to the next one where proof is required. + /// + /// For example, for PoA chains the proof will be a validator set, + /// and the corresponding `ChainVerifier` can be used to correctly validate + /// all blocks produced under that `ValidatorSet` + fn prove_with_caller(&self, _header: &Header, _caller: &Call) + -> Result, Error> + { Ok(Vec::new()) } @@ -231,6 +244,14 @@ pub trait Engine : Sync + Send { RequiresProof::No } + /// Create a subchain verifier from validation proof. + /// + /// The proof should be one generated by `prove_with_caller`. + /// See docs of `prove_with_caller` for description. + fn chain_verifier(&self, _header: &Header, _proof: Bytes) -> Result, Error> { + Ok(Box::new(self::chain_verifier::NoOp)) + } + /// Populate a header's fields based on its parent's header. /// Usually implements the chain scoring rule based on weight. /// The gas floor target must not be lower than the engine's minimum gas limit. diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 5c4af4ba7..db6e8ed2c 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -76,6 +76,10 @@ impl ValidatorSet for ValidatorContract { self.validators.generate_proof(header, caller) } + fn chain_verifier(&self, header: &Header, proof: Vec) -> Result { + self.validators.chain_verifier(header, proof) + } + fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { self.validators.contains_with_caller(bh, address, caller) } @@ -153,7 +157,7 @@ mod tests { header.set_parent_hash(client.chain_info().best_block_hash); // `reportBenign` when the designated proposer releases block from the future (bad clock). - assert!(client.engine().verify_block_family(&header, &header, None).is_err()); + assert!(client.engine().verify_block_external(&header, None).is_err()); // Seal a block. client.engine().step(); assert_eq!(client.chain_info().best_block_number, 1); diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index d5abf430d..48269e20f 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -28,7 +28,7 @@ use ethjson::spec::ValidatorSet as ValidatorSpec; use client::Client; use header::Header; -use self::simple_list::SimpleList; +pub use self::simple_list::SimpleList; use self::contract::ValidatorContract; use self::safe_contract::ValidatorSafeContract; use self::multi::Multi; @@ -90,6 +90,9 @@ pub trait ValidatorSet: Send + Sync { /// Otherwise, generated proofs may be wrong. fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String>; + /// Create a fully self-contained validator set from the given proof. + fn chain_verifier(&self, header: &Header, proof: Vec) -> Result; + /// Checks if a given address is a validator, with the given function /// for executing synchronous calls to contracts. fn contains_with_caller(&self, parent_block_hash: &H256, address: &Address, caller: &Call) -> bool; diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index 78270c4af..b7a13c5e3 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -82,6 +82,10 @@ impl ValidatorSet for Multi { self.correct_set_by_number(header.number()).generate_proof(header, caller) } + fn chain_verifier(&self, header: &Header, proof: Vec) -> Result { + self.correct_set_by_number(header.number()).chain_verifier(header, proof) + } + fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { self.correct_set(BlockId::Hash(*bh)) .map_or(false, |set| set.contains_with_caller(bh, address, caller)) diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index de7ecb3a2..2af389484 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -202,6 +202,15 @@ impl ValidatorSet for ValidatorSafeContract { } } + fn chain_verifier(&self, _header: &Header, proof: Vec) -> Result { + use rlp::UntrustedRlp; + + let rlp = UntrustedRlp::new(&proof); + let validators: Vec
= rlp.list_at(1)?; + + Ok(SimpleList::new(validators)) + } + fn contains_with_caller(&self, block_hash: &H256, address: &Address, caller: &Call) -> bool { let mut guard = self.validators.write(); let maybe_existing = guard diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index e513212a6..339585d7d 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -22,7 +22,8 @@ use engines::Call; use header::Header; use super::ValidatorSet; -#[derive(Debug, PartialEq, Eq, Default)] +/// Validator set containing a known set of addresses. +#[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct SimpleList { validators: Vec
, } @@ -62,6 +63,10 @@ impl ValidatorSet for SimpleList { Ok(Vec::new()) } + fn chain_verifier(&self, _header: &Header, _: Vec) -> Result { + Ok(self.clone()) + } + fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool { self.validators.contains(address) } diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 5bc31c5d9..585fd29aa 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -139,17 +139,24 @@ pub struct Ethash { impl Ethash { /// Create a new instance of Ethash engine - pub fn new(params: CommonParams, ethash_params: EthashParams, builtins: BTreeMap) -> Self { - Ethash { + pub fn new(params: CommonParams, ethash_params: EthashParams, builtins: BTreeMap) -> Arc { + Arc::new(Ethash { params: params, ethash_params: ethash_params, builtins: builtins, pow: EthashManager::new(), - } + }) } } -impl Engine for Ethash { +impl ::engines::ChainVerifier for Arc { + fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } + fn verify_heavy(&self, header: &Header) -> Result<(), Error> { + self.verify_block_unordered(header, None) + } +} + +impl Engine for Arc { fn name(&self) -> &str { "Ethash" } fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) } // Two fields - mix @@ -385,6 +392,10 @@ impl Engine for Ethash { Ok(()) } + + fn chain_verifier(&self, _header: &Header, _proof: Vec) -> Result, Error> { + Ok(Box::new(self.clone())) + } } // Try to round gas_limit a bit so that: From e1fa4ab8ec3c1e86a16539c6ac96c07582112ee6 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 13 Apr 2017 20:31:29 +0200 Subject: [PATCH 021/117] verify_external in verifier trait --- ethcore/src/client/client.rs | 6 ++++++ ethcore/src/verification/canon_verifier.rs | 4 ++++ ethcore/src/verification/noop_verifier.rs | 4 ++++ ethcore/src/verification/verifier.rs | 2 ++ 4 files changed, 16 insertions(+) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 13cb95afe..03353a2a4 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -380,6 +380,12 @@ impl Client { return Err(()); }; + let verify_external_result = self.verifier.verify_block_external(header, &block.bytes, engine); + if let Err(e) = verify_external_result { + warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); + return Err(()); + }; + // Check if Parent is in chain let chain_has_parent = chain.block_header(header.parent_hash()); if let Some(parent) = chain_has_parent { diff --git a/ethcore/src/verification/canon_verifier.rs b/ethcore/src/verification/canon_verifier.rs index 849f7caad..a5d8fe73f 100644 --- a/ethcore/src/verification/canon_verifier.rs +++ b/ethcore/src/verification/canon_verifier.rs @@ -34,4 +34,8 @@ impl Verifier for CanonVerifier { fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> { verification::verify_block_final(expected, got) } + + fn verify_block_external(&self, header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> { + engine.verify_block_external(header, Some(bytes)) + } } diff --git a/ethcore/src/verification/noop_verifier.rs b/ethcore/src/verification/noop_verifier.rs index 2fcd877f5..8464ba1e2 100644 --- a/ethcore/src/verification/noop_verifier.rs +++ b/ethcore/src/verification/noop_verifier.rs @@ -34,4 +34,8 @@ impl Verifier for NoopVerifier { fn verify_block_final(&self, _expected: &Header, _got: &Header) -> Result<(), Error> { Ok(()) } + + fn verify_block_external(&self, _header: &Header, _bytes: &[u8], _engine: &Engine) -> Result<(), Error> { + Ok(()) + } } diff --git a/ethcore/src/verification/verifier.rs b/ethcore/src/verification/verifier.rs index e5dabd392..55b711c1c 100644 --- a/ethcore/src/verification/verifier.rs +++ b/ethcore/src/verification/verifier.rs @@ -27,4 +27,6 @@ pub trait Verifier: Send + Sync { fn verify_block_family(&self, header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error>; /// Do a final verification check for an enacted header vs its expected counterpart. fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error>; + /// Verify a block, inspecing external state. + fn verify_block_external(&self, header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error>; } From f6f9816ef4a35d5e3af0c8f59dd1e4df80260b54 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 18 Apr 2017 14:19:10 +0200 Subject: [PATCH 022/117] epoch verifier rename --- ethcore/src/client/client.rs | 10 ++-- ethcore/src/engines/authority_round.rs | 23 +++++---- ethcore/src/engines/basic_authority.rs | 31 +++++++----- .../{chain_verifier.rs => epoch_verifier.rs} | 18 ++++--- ethcore/src/engines/mod.rs | 48 ++++++++++--------- ethcore/src/engines/validator_set/contract.rs | 14 +++--- ethcore/src/engines/validator_set/mod.rs | 26 ++++++---- ethcore/src/engines/validator_set/multi.rs | 32 +++++++------ .../engines/validator_set/safe_contract.rs | 27 ++++++----- .../src/engines/validator_set/simple_list.rs | 14 +++--- ethcore/src/ethereum/ethash.rs | 13 ++++- 11 files changed, 148 insertions(+), 108 deletions(-) rename ethcore/src/engines/{chain_verifier.rs => epoch_verifier.rs} (75%) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 03353a2a4..a91294e27 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -579,11 +579,11 @@ impl Client { // generate validation proof if the engine requires them. // TODO: make conditional? let generate_proof = { - use engines::RequiresProof; - match self.engine.proof_required(block.header(), Some(block_data), Some(&receipts)) { - RequiresProof::Yes(_) => true, - RequiresProof::No => false, - RequiresProof::Unsure(_) => { + use engines::EpochChange; + match self.engine.is_epoch_end(block.header(), Some(block_data), Some(&receipts)) { + EpochChange::Yes(_) => true, + EpochChange::No => false, + EpochChange::Unsure(_) => { warn!(target: "client", "Detected invalid engine implementation."); warn!(target: "client", "Engine claims to require more block data, but everything provided."); false diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 8e8450768..1f3edf57c 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -132,12 +132,14 @@ pub struct AuthorityRound { } // header-chain validator. -struct ChainVerifier { +struct EpochVerifier { + epoch_number: U256, step: Arc, subchain_validators: SimpleList, } -impl super::ChainVerifier for ChainVerifier { +impl super::EpochVerifier for EpochVerifier { + fn epoch_number(&self) -> U256 { self.epoch_number.clone() } fn verify_light(&self, header: &Header) -> Result<(), Error> { // always check the seal since it's fast. // nothing heavier to do. @@ -398,22 +400,23 @@ impl Engine for AuthorityRound { } // the proofs we need just allow us to get the full validator set. - fn prove_with_caller(&self, header: &Header, caller: &Call) -> Result { - self.validators.generate_proof(header, caller) + fn epoch_proof(&self, header: &Header, caller: &Call) -> Result { + self.validators.epoch_proof(header, caller) .map_err(|e| EngineError::InsufficientProof(e).into()) } - fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> super::RequiresProof + fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> super::EpochChange { - self.validators.proof_required(header, block, receipts) + self.validators.is_epoch_end(header, block, receipts) } - fn chain_verifier(&self, header: &Header, proof: Bytes) -> Result, Error> { + fn epoch_verifier(&self, header: &Header, proof: &[u8]) -> Result, Error> { // extract a simple list from the proof. - let simple_list = self.validators.chain_verifier(header, proof)?; + let (num, simple_list) = self.validators.epoch_set(header, proof)?; - Ok(Box::new(ChainVerifier { + Ok(Box::new(EpochVerifier { + epoch_number: num, step: self.step.clone(), subchain_validators: simple_list, })) diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 25cdd765f..94c3fe578 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -23,7 +23,7 @@ use account_provider::AccountProvider; use block::*; use builtin::Builtin; use spec::CommonParams; -use engines::{Engine, EngineError, Seal, Call, RequiresProof}; +use engines::{Engine, EngineError, Seal, Call, EpochChange}; use env_info::EnvInfo; use error::{BlockError, Error}; use evm::Schedule; @@ -51,11 +51,15 @@ impl From for BasicAuthorityParams { } } -struct ChainVerifier(SimpleList); +struct EpochVerifier { + epoch_number: U256, + list: SimpleList, +} -impl super::ChainVerifier for ChainVerifier { +impl super::EpochVerifier for EpochVerifier { + fn epoch_number(&self) -> U256 { self.epoch_number.clone() } fn verify_light(&self, header: &Header) -> Result<(), Error> { - verify_external(header, &self.0) + verify_external(header, &self.list) } } @@ -181,22 +185,25 @@ impl Engine for BasicAuthority { } // the proofs we need just allow us to get the full validator set. - fn prove_with_caller(&self, header: &Header, caller: &Call) -> Result { - self.validators.generate_proof(header, caller) + fn epoch_proof(&self, header: &Header, caller: &Call) -> Result { + self.validators.epoch_proof(header, caller) .map_err(|e| EngineError::InsufficientProof(e).into()) } - fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> RequiresProof + fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> EpochChange { - self.validators.proof_required(header, block, receipts) + self.validators.is_epoch_end(header, block, receipts) } - fn chain_verifier(&self, header: &Header, proof: Bytes) -> Result, Error> { + fn epoch_verifier(&self, header: &Header, proof: &[u8]) -> Result, Error> { // extract a simple list from the proof. - let simple_list = self.validators.chain_verifier(header, proof)?; + let (num, simple_list) = self.validators.epoch_set(header, proof)?; - Ok(Box::new(ChainVerifier(simple_list))) + Ok(Box::new(EpochVerifier { + epoch_number: num, + list: simple_list, + })) } fn register_client(&self, client: Weak) { diff --git a/ethcore/src/engines/chain_verifier.rs b/ethcore/src/engines/epoch_verifier.rs similarity index 75% rename from ethcore/src/engines/chain_verifier.rs rename to ethcore/src/engines/epoch_verifier.rs index 399832fe9..fd0847e76 100644 --- a/ethcore/src/engines/chain_verifier.rs +++ b/ethcore/src/engines/epoch_verifier.rs @@ -14,18 +14,21 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -// Chain verifiers. +// Epoch verifiers. use error::Error; use header::Header; +use util::U256; -/// Sequential chain verifier. +/// Verifier for all blocks within an epoch without accessing /// /// See docs on `Engine` relating to proving functions for more details. -/// Headers here will have already passed "basic" verification. -pub trait ChainVerifier { +pub trait EpochVerifier: Sync { + /// Get the epoch number. + fn epoch_number(&self) -> U256; + /// Lightly verify the next block header. - /// This may not be a header that requires a proof. + /// This may not be a header belonging to a different epoch. fn verify_light(&self, header: &Header) -> Result<(), Error>; /// Perform potentially heavier checks on the next block header. @@ -34,9 +37,10 @@ pub trait ChainVerifier { } } -/// No-op chain verifier. +/// Special "no-op" verifier for stateless, epoch-less engines. pub struct NoOp; -impl ChainVerifier for NoOp { +impl EpochVerifier for NoOp { + fn epoch_number(&self) -> U256 { 0.into() } fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 7d4906dd2..b67afdf2f 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -18,7 +18,7 @@ mod authority_round; mod basic_authority; -mod chain_verifier; +mod epoch_verifier; mod instant_seal; mod null_engine; mod signer; @@ -29,7 +29,7 @@ mod vote_collector; pub use self::authority_round::AuthorityRound; pub use self::basic_authority::BasicAuthority; -pub use self::chain_verifier::ChainVerifier; +pub use self::epoch_verifier::EpochVerifier; pub use self::instant_seal::InstantSeal; pub use self::null_engine::NullEngine; pub use self::tendermint::Tendermint; @@ -98,18 +98,19 @@ pub enum Seal { /// Type alias for a function we can make calls through synchronously. pub type Call = Fn(Address, Bytes) -> Result; -/// Results of a query of whether a validation proof is necessary at a block. -pub enum RequiresProof { +/// Results of a query of whether an epoch change occurred at the given block. +#[derive(Debug, Clone, PartialEq)] +pub enum EpochChange { /// Cannot determine until more data is passed. Unsure(Unsure), - /// Validation proof not required. + /// No epoch change. No, /// Validation proof required, and the expected output - /// if it can be recovered. - Yes(Option), + Yes(Bytes), } -/// More data required to determine if a validation proof is required. +/// More data required to determine if an epoch change occurred at a given block. +#[derive(Debug, Clone, Copy, PartialEq)] pub enum Unsure { /// Needs the body. NeedsBody, @@ -219,37 +220,38 @@ pub trait Engine : Sync + Send { self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None)) } - /// Generate validation proof. + /// Generate epoch change proof. /// - /// This will be used to generate proofs of validation as well as verify them. + /// This will be used to generate proofs of epoch change as well as verify them. /// Must be called on blocks that have already passed basic verification. /// - /// Return the "validation proof" generated. - /// This must be usable to generate a `ChainVerifier` for verifying all blocks + /// Return the "epoch proof" generated. + /// This must be usable to generate a `EpochVerifier` for verifying all blocks /// from the supplied header up to the next one where proof is required. /// /// For example, for PoA chains the proof will be a validator set, - /// and the corresponding `ChainVerifier` can be used to correctly validate + /// and the corresponding `EpochVerifier` can be used to correctly validate /// all blocks produced under that `ValidatorSet` - fn prove_with_caller(&self, _header: &Header, _caller: &Call) + fn epoch_proof(&self, _header: &Header, _caller: &Call) -> Result, Error> { Ok(Vec::new()) } - /// Whether a proof is required for the given header. - fn proof_required(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>) - -> RequiresProof + /// Whether an epoch change occurred at the given header. + /// Should not interact with state. + fn is_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>) + -> EpochChange { - RequiresProof::No + EpochChange::No } - /// Create a subchain verifier from validation proof. + /// Create an epoch verifier from validation proof. /// - /// The proof should be one generated by `prove_with_caller`. - /// See docs of `prove_with_caller` for description. - fn chain_verifier(&self, _header: &Header, _proof: Bytes) -> Result, Error> { - Ok(Box::new(self::chain_verifier::NoOp)) + /// The proof should be one generated by `epoch_proof`. + /// See docs of `epoch_proof` for description. + fn epoch_verifier(&self, _header: &Header, _proof: &[u8]) -> Result, Error> { + Ok(Box::new(self::epoch_verifier::NoOp)) } /// Populate a header's fields based on its parent's header. diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index db6e8ed2c..0fdef84a3 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -66,18 +66,18 @@ impl ValidatorSet for ValidatorContract { self.validators.default_caller(id) } - fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> ::engines::RequiresProof + fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> ::engines::EpochChange { - self.validators.proof_required(header, block, receipts) + self.validators.is_epoch_end(header, block, receipts) } - fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String> { - self.validators.generate_proof(header, caller) + fn epoch_proof(&self, header: &Header, caller: &Call) -> Result, String> { + self.validators.epoch_proof(header, caller) } - fn chain_verifier(&self, header: &Header, proof: Vec) -> Result { - self.validators.chain_verifier(header, proof) + fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(U256, super::SimpleList), ::error::Error> { + self.validators.epoch_set(header, proof) } fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index 48269e20f..e9dc333c5 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -23,7 +23,7 @@ mod multi; use std::sync::Weak; use ids::BlockId; -use util::{Address, H256}; +use util::{Address, H256, U256}; use ethjson::spec::ValidatorSet as ValidatorSpec; use client::Client; use header::Header; @@ -72,26 +72,32 @@ pub trait ValidatorSet: Send + Sync { self.count_with_caller(parent, &*default) } - /// Whether a validation proof is required at the given block. + /// Whether this block is the last one in its epoch. /// Usually indicates that the validator set changed at the given block. /// /// Should not inspect state! This is used in situations where /// state is not generally available. /// - /// Return `Ok` with a flag indicating whether it changed at the given header, + /// Return `Yes` or `No` indicating whether it changed at the given header, /// or `Unsure` indicating a need for more information. /// - /// This may or may not be called in a loop. - fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> super::RequiresProof; + /// If block or receipts are provided, do not return `Unsure` indicating + /// need for them. + fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> super::EpochChange; - /// Generate a validation proof at the given header. + /// Generate epoch proof. /// Must interact with state only through the given caller! /// Otherwise, generated proofs may be wrong. - fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String>; + fn epoch_proof(&self, header: &Header, caller: &Call) -> Result, String>; - /// Create a fully self-contained validator set from the given proof. - fn chain_verifier(&self, header: &Header, proof: Vec) -> Result; + /// Recover the validator set for all + /// + /// May fail if the given header doesn't kick off an epoch or + /// the proof is invalid. + /// + /// Returns the epoch number and proof. + fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(U256, SimpleList), ::error::Error>; /// Checks if a given address is a validator, with the given function /// for executing synchronous calls to contracts. diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index b7a13c5e3..eecf88432 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -18,8 +18,8 @@ use std::collections::BTreeMap; use std::sync::Weak; -use engines::{Call, RequiresProof}; -use util::{H256, Address, RwLock}; +use engines::{Call, EpochChange}; +use util::{H256, Address, RwLock, U256}; use ids::BlockId; use header::{BlockNumber, Header}; use client::{Client, BlockChainClient}; @@ -43,9 +43,7 @@ impl Multi { fn correct_set(&self, id: BlockId) -> Option<&ValidatorSet> { match self.block_number.read()(id).map(|parent_block| self.correct_set_by_number(parent_block)) { - Ok(set) => { - Some(set) - }, + Ok((_, set)) => Some(set), Err(e) => { debug!(target: "engine", "ValidatorSet could not be recovered: {}", e); None @@ -53,7 +51,9 @@ impl Multi { } } - fn correct_set_by_number(&self, parent_block: BlockNumber) -> &ValidatorSet { + // get correct set by block number, along with block number at which + // this set was activated. + fn correct_set_by_number(&self, parent_block: BlockNumber) -> (BlockNumber, &ValidatorSet) { let (block, set) = self.sets.iter() .rev() .find(|&(block, _)| *block <= parent_block + 1) @@ -62,7 +62,7 @@ impl Multi { qed"); trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block); - &**set + (*block, &**set) } } @@ -72,18 +72,22 @@ impl ValidatorSet for Multi { .unwrap_or(Box::new(|_, _| Err("No validator set for given ID.".into()))) } - fn proof_required(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> RequiresProof + fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> EpochChange { - self.correct_set_by_number(header.number()).proof_required(header, block, receipts) + self.correct_set_by_number(header.number()).1.is_epoch_end(header, block, receipts) } - fn generate_proof(&self, header: &Header, caller: &Call) -> Result, String> { - self.correct_set_by_number(header.number()).generate_proof(header, caller) + fn epoch_proof(&self, header: &Header, caller: &Call) -> Result, String> { + self.correct_set_by_number(header.number()).1.epoch_proof(header, caller) } - fn chain_verifier(&self, header: &Header, proof: Vec) -> Result { - self.correct_set_by_number(header.number()).chain_verifier(header, proof) + fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(U256, super::SimpleList), ::error::Error> { + // "multi" epoch is the inner set's epoch plus the transition block to that set. + // ensures epoch increases monotonically. + let (set_block, set) = self.correct_set_by_number(header.number()); + let (inner_epoch, list) = set.epoch_set(header, proof)?; + Ok((U256::from(set_block) + inner_epoch, list)) } fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 2af389484..3284ec5ab 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -126,16 +126,16 @@ impl ValidatorSet for ValidatorSafeContract { .and_then(|c| c.call_contract(id, addr, data))) } - fn proof_required(&self, header: &Header, _block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) - -> ::engines::RequiresProof + fn is_epoch_end(&self, header: &Header, _block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) + -> ::engines::EpochChange { let bloom = self.expected_bloom(header); let header_bloom = header.log_bloom(); - if &bloom & header_bloom != bloom { return ::engines::RequiresProof::No } + if &bloom & header_bloom != bloom { return ::engines::EpochChange::No } match receipts { - None => ::engines::RequiresProof::Unsure(::engines::Unsure::NeedsReceipts), + None => ::engines::EpochChange::Unsure(::engines::Unsure::NeedsReceipts), Some(receipts) => { let check_log = |log: &LogEntry| { log.address == self.address && @@ -149,7 +149,11 @@ impl ValidatorSet for ValidatorSafeContract { .event("ValidatorsChanged".into()) .expect("Contract known ahead of time to have `ValidatorsChanged` event; qed"); + // iterate in reverse because only the _last_ change in a given + // block actually has any effect. + // the contract should only increment the nonce once. let mut decoded_events = receipts.iter() + .rev() .filter(|r| &bloom & &r.log_bloom == bloom) .flat_map(|r| r.logs.iter()) .filter(move |l| check_log(l)) @@ -164,7 +168,7 @@ impl ValidatorSet for ValidatorSafeContract { // TODO: are multiple transitions per block possible? match decoded_events.next() { - None => ::engines::RequiresProof::No, + None => ::engines::EpochChange::No, Some(matched_event) => { // decode log manually until the native contract generator is // good enough to do it for us. @@ -181,10 +185,10 @@ impl ValidatorSet for ValidatorSafeContract { match (nonce, validators) { (Some(nonce), Some(validators)) => - ::engines::RequiresProof::Yes(Some(encode_proof(nonce, &validators))), + ::engines::EpochChange::Yes(encode_proof(nonce, &validators)), _ => { debug!(target: "engine", "Successfully decoded log turned out to be bad."); - ::engines::RequiresProof::No + ::engines::EpochChange::No } } } @@ -195,20 +199,21 @@ impl ValidatorSet for ValidatorSafeContract { // the proof we generate is an RLP list containing two parts. // (nonce, validators) - fn generate_proof(&self, _header: &Header, caller: &Call) -> Result, String> { + fn epoch_proof(&self, _header: &Header, caller: &Call) -> Result, String> { match (self.get_nonce(caller), self.get_list(caller)) { (Some(nonce), Some(list)) => Ok(encode_proof(nonce, &list.into_inner())), _ => Err("Caller insufficient to generate validator proof.".into()), } } - fn chain_verifier(&self, _header: &Header, proof: Vec) -> Result { + fn epoch_set(&self, _header: &Header, proof: &[u8]) -> Result<(U256, SimpleList), ::error::Error> { use rlp::UntrustedRlp; - let rlp = UntrustedRlp::new(&proof); + let rlp = UntrustedRlp::new(proof); + let nonce: U256 = rlp.val_at(0)?; let validators: Vec
= rlp.list_at(1)?; - Ok(SimpleList::new(validators)) + Ok((nonce, SimpleList::new(validators))) } fn contains_with_caller(&self, block_hash: &H256, address: &Address, caller: &Call) -> bool { diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index 339585d7d..59209fac0 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -16,7 +16,7 @@ /// Preconfigured validator list. -use util::{H256, Address, HeapSizeOf}; +use util::{H256, Address, HeapSizeOf, U256}; use engines::Call; use header::Header; @@ -53,18 +53,18 @@ impl ValidatorSet for SimpleList { Box::new(|_, _| Err("Simple list doesn't require calls.".into())) } - fn proof_required(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[::receipt::Receipt]>) - -> ::engines::RequiresProof + fn is_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[::receipt::Receipt]>) + -> ::engines::EpochChange { - ::engines::RequiresProof::No + ::engines::EpochChange::No } - fn generate_proof(&self, _header: &Header, _caller: &Call) -> Result, String> { + fn epoch_proof(&self, _header: &Header, _caller: &Call) -> Result, String> { Ok(Vec::new()) } - fn chain_verifier(&self, _header: &Header, _: Vec) -> Result { - Ok(self.clone()) + fn epoch_set(&self, _header: &Header, _: &[u8]) -> Result<(U256, SimpleList), ::error::Error> { + Ok((0.into(), self.clone())) } fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool { diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 585fd29aa..867d4f775 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -149,7 +149,16 @@ impl Ethash { } } -impl ::engines::ChainVerifier for Arc { +// TODO [rphmeier] +// +// for now, this is different than Ethash's own epochs, and signal +// "consensus epochs". +// in this sense, `Ethash` is epochless: the same `EpochVerifier` can be used +// for any block in the chain. +// in the future, we might move the Ethash epoch +// caching onto this mechanism as well. +impl ::engines::EpochVerifier for Arc { + fn epoch_number(&self) -> U256 { 0.into() } fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } fn verify_heavy(&self, header: &Header) -> Result<(), Error> { self.verify_block_unordered(header, None) @@ -393,7 +402,7 @@ impl Engine for Arc { Ok(()) } - fn chain_verifier(&self, _header: &Header, _proof: Vec) -> Result, Error> { + fn epoch_verifier(&self, _header: &Header, _proof: &[u8]) -> Result, Error> { Ok(Box::new(self.clone())) } } From 0180b21dd17ce9caf921d6fb87aab314447a58f2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 18 Apr 2017 14:55:25 +0200 Subject: [PATCH 023/117] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f668f3218..c74b51a58 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Be sure to check out [our wiki][wiki-url] for more information. Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs. -Parity comes with a built-in wallet. To access [Parity Wallet](http://127.0.0.1:8080/) simply go to http://127.0.0.1:8080/. It +Parity comes with a built-in wallet. To access [Parity Wallet](http://web3.site/) simply go to http://web3.site/ (if you don't have access to the internet, but still want to use the service, you can also use http://127.0.0.1:8180/). It includes various functionality allowing you to: - create and manage your Ethereum accounts; - manage your Ether and any Ethereum tokens; From 6da6c755a5c862e3110552c2c22206161ff271a0 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 18 Apr 2017 15:45:15 +0200 Subject: [PATCH 024/117] iterate DB by prefix --- util/src/kvdb.rs | 73 +++++++++++++++++++++++++++++---------- util/src/migration/mod.rs | 7 +++- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/util/src/kvdb.rs b/util/src/kvdb.rs index 5d4c02152..056b49c65 100644 --- a/util/src/kvdb.rs +++ b/util/src/kvdb.rs @@ -167,6 +167,10 @@ pub trait KeyValueDB: Sync + Send { /// Iterate over flushed data for a given column. fn iter<'a>(&'a self, col: Option) -> Box, Box<[u8]>)> + 'a>; + /// Iterate over flushed data for a given column, starting from a given prefix. + fn iter_from_prefix<'a>(&'a self, col: Option, prefix: &'a [u8]) + -> Box, Box<[u8]>)> + 'a>; + /// Attempt to replace this database with a new one located at the given path. fn restore(&self, new_db: &str) -> Result<(), UtilError>; } @@ -247,7 +251,21 @@ impl KeyValueDB for InMemory { .into_iter() .map(|(k, v)| (k.into_boxed_slice(), v.to_vec().into_boxed_slice())) ), - None => Box::new(None.into_iter()) + None => Box::new(None.into_iter()), + } + } + + fn iter_from_prefix<'a>(&'a self, col: Option, prefix: &'a [u8]) + -> Box, Box<[u8]>)> + 'a> + { + match self.columns.read().get(&col) { + Some(map) => Box::new( + map.clone() + .into_iter() + .skip_while(move |&(ref k, _)| !k.starts_with(prefix)) + .map(|(k, v)| (k.into_boxed_slice(), v.to_vec().into_boxed_slice())) + ), + None => Box::new(None.into_iter()), } } @@ -691,23 +709,17 @@ impl Database { /// Get value by partial key. Prefix size should match configured prefix size. Only searches flushed values. // TODO: support prefix seek for unflushed data pub fn get_by_prefix(&self, col: Option, prefix: &[u8]) -> Option> { - match *self.db.read() { - Some(DBAndColumns { ref db, ref cfs }) => { - let mut iter = col.map_or_else(|| db.iterator_opt(IteratorMode::From(prefix, Direction::Forward), &self.read_opts), - |c| db.iterator_cf_opt(cfs[c as usize], IteratorMode::From(prefix, Direction::Forward), &self.read_opts) - .expect("iterator params are valid; qed")); - match iter.next() { - // TODO: use prefix_same_as_start read option (not availabele in C API currently) - Some((k, v)) => if k[0 .. prefix.len()] == prefix[..] { Some(v) } else { None }, - _ => None - } - }, - None => None, - } + self.iter_from_prefix(col, prefix).and_then(|mut iter| { + match iter.next() { + // TODO: use prefix_same_as_start read option (not availabele in C API currently) + Some((k, v)) => if k[0 .. prefix.len()] == prefix[..] { Some(v) } else { None }, + _ => None + } + }) } /// Get database iterator for flushed data. - pub fn iter(&self, col: Option) -> DatabaseIterator { + pub fn iter(&self, col: Option) -> Option { //TODO: iterate over overlay match *self.db.read() { Some(DBAndColumns { ref db, ref cfs }) => { @@ -717,12 +729,28 @@ impl Database { .expect("iterator params are valid; qed") ); - DatabaseIterator { + Some(DatabaseIterator { iter: iter, _marker: PhantomData, - } + }) }, - None => panic!("Not supported yet") //TODO: return an empty iterator or change return type + None => None, + } + } + + fn iter_from_prefix(&self, col: Option, prefix: &[u8]) -> Option { + match *self.db.read() { + Some(DBAndColumns { ref db, ref cfs }) => { + let iter = col.map_or_else(|| db.iterator_opt(IteratorMode::From(prefix, Direction::Forward), &self.read_opts), + |c| db.iterator_cf_opt(cfs[c as usize], IteratorMode::From(prefix, Direction::Forward), &self.read_opts) + .expect("iterator params are valid; qed")); + + Some(DatabaseIterator { + iter: iter, + _marker: PhantomData, + }) + }, + None => None, } } @@ -836,7 +864,14 @@ impl KeyValueDB for Database { fn iter<'a>(&'a self, col: Option) -> Box, Box<[u8]>)> + 'a> { let unboxed = Database::iter(self, col); - Box::new(unboxed) + Box::new(unboxed.into_iter().flat_map(|inner| inner)) + } + + fn iter_from_prefix<'a>(&'a self, col: Option, prefix: &'a [u8]) + -> Box, Box<[u8]>)> + 'a> + { + let unboxed = Database::iter_from_prefix(self, col, prefix); + Box::new(unboxed.into_iter().flat_map(|inner| inner)) } fn restore(&self, new_db: &str) -> Result<(), UtilError> { diff --git a/util/src/migration/mod.rs b/util/src/migration/mod.rs index 6ec465517..93a98f1dc 100644 --- a/util/src/migration/mod.rs +++ b/util/src/migration/mod.rs @@ -157,7 +157,12 @@ impl Migration for T { fn migrate(&mut self, source: Arc, config: &Config, dest: &mut Database, col: Option) -> Result<(), Error> { let mut batch = Batch::new(config, col); - for (key, value) in source.iter(col) { + let iter = match source.iter(col) { + Some(iter) => iter, + None => return Ok(()), + }; + + for (key, value) in iter { if let Some((key, value)) = self.simple_migrate(key.to_vec(), value.to_vec()) { batch.insert(key, value, dest)?; } From b50fb71dd1d29dfde2a6c7e1830447cf30896c31 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Wed, 19 Apr 2017 14:30:00 +0200 Subject: [PATCH 025/117] EIP-86 (#4697) * EIP-86 * Disable EIP-86 auto activation for now --- ethcore/light/src/client/mod.rs | 7 ++ ethcore/res/ethereum/classic.json | 3 +- ethcore/res/ethereum/eip150_test.json | 3 +- ethcore/res/ethereum/eip161_test.json | 3 +- ethcore/res/ethereum/expanse.json | 3 +- ethcore/res/ethereum/foundation.json | 3 +- ethcore/res/ethereum/frontier_like_test.json | 3 +- ethcore/res/ethereum/frontier_test.json | 3 +- ethcore/res/ethereum/homestead_test.json | 3 +- ethcore/res/ethereum/morden.json | 3 +- ethcore/res/ethereum/olympic.json | 3 +- ethcore/res/ethereum/ropsten.json | 3 +- ethcore/res/ethereum/tests | 2 +- ethcore/res/ethereum/transition_test.json | 5 +- ethcore/res/null.json | 3 +- ethcore/src/client/client.rs | 21 ++-- ethcore/src/client/test_client.rs | 4 +- ethcore/src/client/traits.rs | 3 + ethcore/src/engines/authority_round.rs | 19 +-- ethcore/src/engines/basic_authority.rs | 17 +-- ethcore/src/engines/instant_seal.rs | 7 +- ethcore/src/engines/mod.rs | 17 +-- ethcore/src/engines/null_engine.rs | 4 +- ethcore/src/engines/tendermint/mod.rs | 19 +-- .../engines/validator_set/safe_contract.rs | 7 +- ethcore/src/ethereum/ethash.rs | 53 +++----- ethcore/src/evm/ext.rs | 13 +- ethcore/src/evm/instructions.rs | 3 + ethcore/src/evm/interpreter/gasometer.rs | 2 +- ethcore/src/evm/interpreter/mod.rs | 12 +- ethcore/src/evm/mod.rs | 2 +- ethcore/src/evm/schedule.rs | 16 ++- ethcore/src/evm/tests.rs | 4 +- ethcore/src/executive.rs | 77 ++++++++---- ethcore/src/externalities.rs | 20 +-- ethcore/src/json_tests/executive.rs | 15 ++- ethcore/src/json_tests/state.rs | 19 ++- ethcore/src/json_tests/transaction.rs | 24 ++-- ethcore/src/lib.rs | 1 + ethcore/src/miner/miner.rs | 15 ++- ethcore/src/spec/spec.rs | 5 + ethcore/src/state/mod.rs | 2 +- ethcore/src/tests/client.rs | 2 +- ethcore/src/tests/helpers.rs | 5 +- ethcore/src/types/transaction.rs | 116 +++++++++++++----- evmbin/src/ext.rs | 6 +- json/src/spec/params.rs | 3 + json/src/state/test.rs | 2 +- json/src/state/transaction.rs | 2 +- parity/rpc_apis.rs | 1 + rpc/src/v1/impls/eth.rs | 9 +- rpc/src/v1/impls/light/eth.rs | 7 +- rpc/src/v1/impls/light/parity.rs | 9 +- rpc/src/v1/impls/parity.rs | 11 +- rpc/src/v1/impls/parity_set.rs | 9 +- rpc/src/v1/tests/mocked/parity_set.rs | 2 +- rpc/src/v1/tests/mocked/signing.rs | 2 +- rpc/src/v1/types/transaction.rs | 49 ++++---- sync/src/tests/consensus.rs | 36 +++--- 59 files changed, 433 insertions(+), 289 deletions(-) diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index 6d241f7fd..7e6213273 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -106,6 +106,9 @@ pub trait LightChainClient: Send + Sync { /// Get the `i`th CHT root. fn cht_root(&self, i: usize) -> Option; + + /// Get the EIP-86 transition block number. + fn eip86_transition(&self) -> u64; } /// Something which can be treated as a `LightChainClient`. @@ -384,4 +387,8 @@ impl LightChainClient for Client { fn cht_root(&self, i: usize) -> Option { Client::cht_root(self, i) } + + fn eip86_transition(&self) -> u64 { + self.engine().params().eip86_transition + } } diff --git a/ethcore/res/ethereum/classic.json b/ethcore/res/ethereum/classic.json index f6d30c4d6..f7cb4daca 100644 --- a/ethcore/res/ethereum/classic.json +++ b/ethcore/res/ethereum/classic.json @@ -30,7 +30,8 @@ "chainID": "0x3d", "forkBlock": "0x1d4c00", "forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/eip150_test.json b/ethcore/res/ethereum/eip150_test.json index 8331c8fc2..3091ee27b 100644 --- a/ethcore/res/ethereum/eip150_test.json +++ b/ethcore/res/ethereum/eip150_test.json @@ -24,7 +24,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x7fffffffffffffff" + "eip98Transition": "0x7fffffffffffffff", + "eip86Transition": "0x7fffffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/eip161_test.json b/ethcore/res/ethereum/eip161_test.json index 00885bae1..e6e8bf3bb 100644 --- a/ethcore/res/ethereum/eip161_test.json +++ b/ethcore/res/ethereum/eip161_test.json @@ -24,7 +24,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x7fffffffffffffff" + "eip98Transition": "0x7fffffffffffffff", + "eip86Transition": "0x7fffffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/expanse.json b/ethcore/res/ethereum/expanse.json index 3ec04052b..b7d22ac3e 100644 --- a/ethcore/res/ethereum/expanse.json +++ b/ethcore/res/ethereum/expanse.json @@ -30,7 +30,8 @@ "networkID": "0x1", "chainID": "0x2", "subprotocolName": "exp", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index 54f10b70c..8253987f3 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -147,7 +147,8 @@ "networkID" : "0x1", "forkBlock": "0x1d4c00", "forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/frontier_like_test.json b/ethcore/res/ethereum/frontier_like_test.json index 7b73faa30..aab433033 100644 --- a/ethcore/res/ethereum/frontier_like_test.json +++ b/ethcore/res/ethereum/frontier_like_test.json @@ -143,7 +143,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/frontier_test.json b/ethcore/res/ethereum/frontier_test.json index bf79729ba..6761c79f3 100644 --- a/ethcore/res/ethereum/frontier_test.json +++ b/ethcore/res/ethereum/frontier_test.json @@ -23,7 +23,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/homestead_test.json b/ethcore/res/ethereum/homestead_test.json index 95bd85a94..25b061857 100644 --- a/ethcore/res/ethereum/homestead_test.json +++ b/ethcore/res/ethereum/homestead_test.json @@ -23,7 +23,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index 22f253bf8..495849daf 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -30,7 +30,8 @@ "chainID": "0x3e", "forkBlock": "0x1b34d8", "forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/olympic.json b/ethcore/res/ethereum/olympic.json index baf1c7d05..3d2e7baf9 100644 --- a/ethcore/res/ethereum/olympic.json +++ b/ethcore/res/ethereum/olympic.json @@ -23,7 +23,8 @@ "maximumExtraDataSize": "0x0400", "minGasLimit": "125000", "networkID" : "0x0", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/ropsten.json b/ethcore/res/ethereum/ropsten.json index 539168279..1e350d636 100644 --- a/ethcore/res/ethereum/ropsten.json +++ b/ethcore/res/ethereum/ropsten.json @@ -27,7 +27,8 @@ "networkID" : "0x3", "forkBlock": 641350, "forkCanonHash": "0x8033403e9fe5811a7b6d6b469905915de1c59207ce2172cbcf5d6ff14fa6a2eb", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index d52059307..ef191fdc6 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit d520593078fa0849dcd1f907e44ed0a616892e33 +Subproject commit ef191fdc61cf76cdb9cdc147465fb447304b0ed2 diff --git a/ethcore/res/ethereum/transition_test.json b/ethcore/res/ethereum/transition_test.json index 1b502f087..41251dc8c 100644 --- a/ethcore/res/ethereum/transition_test.json +++ b/ethcore/res/ethereum/transition_test.json @@ -139,11 +139,12 @@ } }, "params": { - "eip98Transition": "0x7fffffffffffffff", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", - "networkID" : "0x1" + "networkID" : "0x1", + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/null.json b/ethcore/res/null.json index 3ec7ce75e..ffb0ea061 100644 --- a/ethcore/res/null.json +++ b/ethcore/res/null.json @@ -7,7 +7,8 @@ "accountStartNonce": "0x0", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", - "networkID" : "0x2" + "networkID" : "0x2", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 2ad4fa8d6..e7755e4d0 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -960,7 +960,7 @@ impl BlockChainClient for Client { return Err(err.into()) } } - let lower = t.gas_required(&self.engine.schedule(&env_info)).into(); + let lower = t.gas_required(&self.engine.schedule(env_info.number)).into(); if cond(lower)? { trace!(target: "estimate_gas", "estimate_gas succeeded with {}", lower); return Ok(lower) @@ -1259,7 +1259,8 @@ impl BlockChainClient for Client { .collect(); match (transaction, previous_receipts) { (Some(transaction), Some(previous_receipts)) => { - Some(transaction_receipt(transaction, previous_receipts)) + let schedule = self.engine().schedule(block_number); + Some(transaction_receipt(&schedule, transaction, previous_receipts)) }, _ => None, } @@ -1501,11 +1502,15 @@ impl BlockChainClient for Client { }) .and_then(|a| if a.is_zero() { None } else { Some(a) }) } + + fn eip86_transition(&self) -> u64 { + self.engine().params().eip86_transition + } } impl MiningBlockChainClient for Client { fn latest_schedule(&self) -> Schedule { - self.engine.schedule(&self.latest_env_info()) + self.engine.schedule(self.latest_env_info().number) } fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { @@ -1655,7 +1660,7 @@ impl Drop for Client { /// Returns `LocalizedReceipt` given `LocalizedTransaction` /// and a vector of receipts from given block up to transaction index. -fn transaction_receipt(mut tx: LocalizedTransaction, mut receipts: Vec) -> LocalizedReceipt { +fn transaction_receipt(schedule: &Schedule, mut tx: LocalizedTransaction, mut receipts: Vec) -> LocalizedReceipt { assert_eq!(receipts.len(), tx.transaction_index + 1, "All previous receipts are provided."); let sender = tx.sender(); @@ -1674,12 +1679,12 @@ fn transaction_receipt(mut tx: LocalizedTransaction, mut receipts: Vec) transaction_hash: transaction_hash, transaction_index: transaction_index, block_hash: block_hash, - block_number:block_number, + block_number: block_number, cumulative_gas_used: receipt.gas_used, gas_used: receipt.gas_used - prior_gas_used, contract_address: match tx.action { Action::Call(_) => None, - Action::Create => Some(contract_address(&sender, &tx.nonce)) + Action::Create => Some(contract_address(schedule.create_address, &sender, &tx.nonce, &tx.data.sha3())) }, logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry { entry: log, @@ -1734,6 +1739,7 @@ mod tests { #[test] fn should_return_correct_log_index() { use super::transaction_receipt; + use evm::schedule::Schedule; use ethkey::KeyPair; use log_entry::{LogEntry, LocalizedLogEntry}; use receipt::{Receipt, LocalizedReceipt}; @@ -1743,6 +1749,7 @@ mod tests { // given let key = KeyPair::from_secret_slice(&"test".sha3()).unwrap(); let secret = key.secret(); + let schedule = Schedule::new_homestead(); let block_number = 1; let block_hash = 5.into(); @@ -1786,7 +1793,7 @@ mod tests { }]; // when - let receipt = transaction_receipt(transaction, receipts); + let receipt = transaction_receipt(&schedule, transaction, receipts); // then assert_eq!(receipt, LocalizedReceipt { diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 16f38203f..feda29107 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -353,7 +353,7 @@ pub fn get_temp_state_db() -> GuardedTempResult { impl MiningBlockChainClient for TestBlockChainClient { fn latest_schedule(&self) -> Schedule { - Schedule::new_post_eip150(24576, true, true, true) + Schedule::new_post_eip150(24576, true, true, true, true) } fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { @@ -756,6 +756,8 @@ impl BlockChainClient for TestBlockChainClient { fn registrar_address(&self) -> Option
{ None } fn registry_address(&self, _name: String) -> Option
{ None } + + fn eip86_transition(&self) -> u64 { u64::max_value() } } impl ProvingBlockChainClient for TestBlockChainClient { diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index a612d8a77..8ccd022e2 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -272,6 +272,9 @@ pub trait BlockChainClient : Sync + Send { /// Get the address of a particular blockchain service, if available. fn registry_address(&self, name: String) -> Option
; + + /// Get the EIP-86 transition block number. + fn eip86_transition(&self) -> u64; } impl IpcConfig for BlockChainClient { } diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 2e8e3932c..85c848d16 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -26,12 +26,11 @@ use account_provider::AccountProvider; use block::*; use spec::CommonParams; use engines::{Engine, Seal, EngineError}; -use header::Header; +use header::{Header, BlockNumber}; use error::{Error, TransactionError, BlockError}; use evm::Schedule; use ethjson; use io::{IoContext, IoHandler, TimerToken, IoService}; -use env_info::EnvInfo; use builtin::Builtin; use transaction::UnverifiedTransaction; use client::{Client, EngineClient}; @@ -241,8 +240,9 @@ impl Engine for AuthorityRound { ] } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { - Schedule::new_post_eip150(usize::max_value(), true, true, true) + fn schedule(&self, block_number: BlockNumber) -> Schedule { + let eip86 = block_number >= self.params.eip86_transition; + Schedule::new_post_eip150(usize::max_value(), true, true, true, eip86) } fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { @@ -387,7 +387,6 @@ impl Engine for AuthorityRound { #[cfg(test)] mod tests { use util::*; - use env_info::EnvInfo; use header::Header; use error::{Error, BlockError}; use ethkey::Secret; @@ -408,15 +407,7 @@ mod tests { #[test] fn can_return_schedule() { let engine = Spec::new_test_round().engine; - let schedule = engine.schedule(&EnvInfo { - number: 10000000, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - }); + let schedule = engine.schedule(10000000); assert!(schedule.stack_limit > 0); } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 19867a648..43be5fe70 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -24,11 +24,10 @@ use block::*; use builtin::Builtin; use spec::CommonParams; use engines::{Engine, Seal}; -use env_info::EnvInfo; use error::{BlockError, Error}; use evm::Schedule; use ethjson; -use header::Header; +use header::{Header, BlockNumber}; use client::Client; use super::signer::EngineSigner; use super::validator_set::{ValidatorSet, new_validator_set}; @@ -86,7 +85,7 @@ impl Engine for BasicAuthority { /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, _header: &Header) -> BTreeMap { map!["signature".to_owned() => "TODO".to_owned()] } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { + fn schedule(&self, _block_number: BlockNumber) -> Schedule { Schedule::new_homestead() } @@ -181,7 +180,6 @@ impl Engine for BasicAuthority { mod tests { use util::*; use block::*; - use env_info::EnvInfo; use error::{BlockError, Error}; use tests::helpers::*; use account_provider::AccountProvider; @@ -206,16 +204,7 @@ mod tests { #[test] fn can_return_schedule() { let engine = new_test_authority().engine; - let schedule = engine.schedule(&EnvInfo { - number: 10000000, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - }); - + let schedule = engine.schedule(10000000); assert!(schedule.stack_limit > 0); } diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index 88672c6f4..702afde4f 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -18,10 +18,10 @@ use std::collections::BTreeMap; use util::{Address, HashMap}; use builtin::Builtin; use engines::{Engine, Seal}; -use env_info::EnvInfo; use spec::CommonParams; use evm::Schedule; use block::ExecutedBlock; +use header::BlockNumber; /// An engine which does not provide any consensus mechanism, just seals blocks internally. pub struct InstantSeal { @@ -58,8 +58,9 @@ impl Engine for InstantSeal { &self.builtins } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { - Schedule::new_post_eip150(usize::max_value(), true, true, true) + fn schedule(&self, block_number: BlockNumber) -> Schedule { + let eip86 = block_number >= self.params.eip86_transition; + Schedule::new_post_eip150(usize::max_value(), true, true, true, eip86) } fn seals_internally(&self) -> Option { Some(true) } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 438b9bda0..4de76f079 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -39,10 +39,10 @@ use account_provider::AccountProvider; use block::ExecutedBlock; use builtin::Builtin; use env_info::EnvInfo; -use error::{Error, TransactionError}; +use error::Error; use spec::CommonParams; use evm::Schedule; -use header::Header; +use header::{Header, BlockNumber}; use transaction::{UnverifiedTransaction, SignedTransaction}; use client::Client; @@ -107,8 +107,8 @@ pub trait Engine : Sync + Send { /// Get the general parameters of the chain. fn params(&self) -> &CommonParams; - /// Get the EVM schedule for the given `env_info`. - fn schedule(&self, env_info: &EnvInfo) -> Schedule; + /// Get the EVM schedule for the given `block_number`. + fn schedule(&self, block_number: BlockNumber) -> Schedule; /// Builtin-contracts we would like to see in the chain. /// (In principle these are just hints for the engine since that has the last word on them.) @@ -156,14 +156,7 @@ pub trait Engine : Sync + Send { // TODO: Add flags for which bits of the transaction to check. // TODO: consider including State in the params. fn verify_transaction_basic(&self, t: &UnverifiedTransaction, _header: &Header) -> Result<(), Error> { - t.check_low_s()?; - - if let Some(n) = t.network_id() { - if n != self.params().chain_id { - return Err(TransactionError::InvalidNetworkId.into()); - } - } - + t.verify_basic(true, Some(self.params().network_id), true)?; Ok(()) } diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index 0611fc08e..838ddf809 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -20,7 +20,7 @@ use builtin::Builtin; use engines::Engine; use spec::CommonParams; use evm::Schedule; -use env_info::EnvInfo; +use header::BlockNumber; /// An engine which does not provide any consensus mechanism and does not seal blocks. pub struct NullEngine { @@ -57,7 +57,7 @@ impl Engine for NullEngine { &self.builtins } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { + fn schedule(&self, _block_number: BlockNumber) -> Schedule { Schedule::new_homestead() } } diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index cb284d3c4..65e11d19e 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -30,9 +30,8 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use util::*; use client::{Client, EngineClient}; use error::{Error, BlockError}; -use header::Header; +use header::{Header, BlockNumber}; use builtin::Builtin; -use env_info::EnvInfo; use rlp::UntrustedRlp; use ethkey::{recover, public_to_address, Signature}; use account_provider::AccountProvider; @@ -405,8 +404,9 @@ impl Engine for Tendermint { ] } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { - Schedule::new_post_eip150(usize::max_value(), true, true, true) + fn schedule(&self, block_number: BlockNumber) -> Schedule { + let eip86 = block_number >= self.params.eip86_transition; + Schedule::new_post_eip150(usize::max_value(), true, true, true, eip86) } fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { @@ -658,7 +658,6 @@ mod tests { use block::*; use error::{Error, BlockError}; use header::Header; - use env_info::EnvInfo; use ethkey::Secret; use client::chain_notify::ChainNotify; use miner::MinerService; @@ -740,15 +739,7 @@ mod tests { #[test] fn can_return_schedule() { let engine = Spec::new_test_tendermint().engine; - let schedule = engine.schedule(&EnvInfo { - number: 10000000, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - }); + let schedule = engine.schedule(10000000); assert!(schedule.stack_limit > 0); } diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 0a0eaecfd..a9388bbbf 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -196,6 +196,7 @@ mod tests { let s0 = Secret::from_slice(&"1".sha3()).unwrap(); let v0 = tap.insert_account(s0.clone(), "").unwrap(); let v1 = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "").unwrap(); + let network_id = Spec::new_validator_safe_contract().network_id(); let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, Some(tap)); client.engine().register_client(Arc::downgrade(&client)); let validator_contract = Address::from_str("0000000000000000000000000000000000000005").unwrap(); @@ -209,7 +210,7 @@ mod tests { action: Action::Call(validator_contract), value: 0.into(), data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), - }.sign(&s0, None); + }.sign(&s0, Some(network_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); assert_eq!(client.chain_info().best_block_number, 1); @@ -221,7 +222,7 @@ mod tests { action: Action::Call(validator_contract), value: 0.into(), data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), - }.sign(&s0, None); + }.sign(&s0, Some(network_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); // The transaction is not yet included so still unable to seal. @@ -240,7 +241,7 @@ mod tests { action: Action::Call(Address::default()), value: 0.into(), data: Vec::new(), - }.sign(&s0, None); + }.sign(&s0, Some(network_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); // Able to seal again. diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 88655fa86..ae865da4f 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -19,8 +19,8 @@ use util::*; use block::*; use builtin::Builtin; use env_info::EnvInfo; -use error::{BlockError, TransactionError, Error}; -use header::Header; +use error::{BlockError, Error, TransactionError}; +use header::{Header, BlockNumber}; use state::CleanupMode; use spec::CommonParams; use transaction::UnverifiedTransaction; @@ -167,19 +167,20 @@ impl Engine for Ethash { map!["nonce".to_owned() => format!("0x{}", header.nonce().hex()), "mixHash".to_owned() => format!("0x{}", header.mix_hash().hex())] } - fn schedule(&self, env_info: &EnvInfo) -> Schedule { + fn schedule(&self, block_number: BlockNumber) -> Schedule { trace!(target: "client", "Creating schedule. fCML={}, bGCML={}", self.ethash_params.homestead_transition, self.ethash_params.eip150_transition); - if env_info.number < self.ethash_params.homestead_transition { + if block_number < self.ethash_params.homestead_transition { Schedule::new_frontier() - } else if env_info.number < self.ethash_params.eip150_transition { + } else if block_number < self.ethash_params.eip150_transition { Schedule::new_homestead() } else { Schedule::new_post_eip150( self.ethash_params.max_code_size as usize, - env_info.number >= self.ethash_params.eip160_transition, - env_info.number >= self.ethash_params.eip161abc_transition, - env_info.number >= self.ethash_params.eip161d_transition + block_number >= self.ethash_params.eip160_transition, + block_number >= self.ethash_params.eip161abc_transition, + block_number >= self.ethash_params.eip161d_transition, + block_number >= self.params.eip86_transition ) } } @@ -369,20 +370,13 @@ impl Engine for Ethash { } fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> result::Result<(), Error> { - if header.number() >= self.ethash_params.homestead_transition { - t.check_low_s()?; - } - - if let Some(n) = t.network_id() { - if header.number() < self.ethash_params.eip155_transition || n != self.params().chain_id { - return Err(TransactionError::InvalidNetworkId.into()) - } - } - if header.number() >= self.ethash_params.min_gas_price_transition && t.gas_price < self.ethash_params.min_gas_price { return Err(TransactionError::InsufficientGasPrice { minimal: self.ethash_params.min_gas_price, got: t.gas_price }.into()); } + let check_low_s = header.number() >= self.ethash_params.homestead_transition; + let network_id = if header.number() >= self.ethash_params.eip155_transition { Some(self.params().chain_id) } else { None }; + t.verify_basic(check_low_s, network_id, false)?; Ok(()) } } @@ -512,7 +506,6 @@ mod tests { use block::*; use tests::helpers::*; use engines::Engine; - use env_info::EnvInfo; use error::{BlockError, Error}; use header::Header; use super::super::{new_morden, new_homestead_test}; @@ -559,28 +552,10 @@ mod tests { #[test] fn can_return_schedule() { let engine = new_morden().engine; - let schedule = engine.schedule(&EnvInfo { - number: 10000000, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - }); - + let schedule = engine.schedule(10000000); assert!(schedule.stack_limit > 0); - let schedule = engine.schedule(&EnvInfo { - number: 100, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - }); - + let schedule = engine.schedule(100); assert!(!schedule.have_delegate_call); } diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 352ffb7d9..e6b644874 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -41,6 +41,17 @@ pub enum MessageCallResult { Failed } +/// Specifies how an address is calculated for a new contract. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum CreateContractAddress { + /// Address is calculated from nonce and sender. Pre EIP-86 (Metropolis) + FromSenderAndNonce, + /// Address is calculated from code hash. Default since EIP-86 + FromCodeHash, + /// Address is calculated from code hash and sender. Used by CREATE_P2SH instruction. + FromSenderAndCodeHash, +} + /// Externalities interface for EVMs // TODO: [rob] associated error type instead of `trie::Result`. Not all EVMs are trie powered. pub trait Ext { @@ -68,7 +79,7 @@ pub trait Ext { /// Creates new contract. /// /// Returns gas_left and contract address if contract creation was succesfull. - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult; + fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult; /// Message call. /// diff --git a/ethcore/src/evm/instructions.rs b/ethcore/src/evm/instructions.rs index d93ddc437..41c9e1ea1 100644 --- a/ethcore/src/evm/instructions.rs +++ b/ethcore/src/evm/instructions.rs @@ -278,6 +278,7 @@ lazy_static! { arr[RETURN as usize] = InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero); arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special); arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Special); + arr[CREATE_P2SH as usize] = InstructionInfo::new("CREATE_P2SH", 0, 3, 1, true, GasPriceTier::Special); arr }; } @@ -553,6 +554,8 @@ pub const CALLCODE: Instruction = 0xf2; pub const RETURN: Instruction = 0xf3; /// like CALLCODE but keeps caller's value and sender pub const DELEGATECALL: Instruction = 0xf4; +/// create a new account and set creation address to sha3(sender + sha3(init code)) % 2**160 +pub const CREATE_P2SH: Instruction = 0xfb; /// halt execution and register account for later deletion pub const SUICIDE: Instruction = 0xff; diff --git a/ethcore/src/evm/interpreter/gasometer.rs b/ethcore/src/evm/interpreter/gasometer.rs index 9086200fa..fb0c86d35 100644 --- a/ethcore/src/evm/interpreter/gasometer.rs +++ b/ethcore/src/evm/interpreter/gasometer.rs @@ -223,7 +223,7 @@ impl Gasometer { Request::GasMemProvide(gas, mem, Some(requested)) }, - instructions::CREATE => { + instructions::CREATE | instructions::CREATE_P2SH => { let gas = Gas::from(schedule.create_gas); let mem = mem_needed(stack.peek(1), stack.peek(2))?; diff --git a/ethcore/src/evm/interpreter/mod.rs b/ethcore/src/evm/interpreter/mod.rs index 79304793e..7fbab7ebc 100644 --- a/ethcore/src/evm/interpreter/mod.rs +++ b/ethcore/src/evm/interpreter/mod.rs @@ -32,7 +32,7 @@ use std::marker::PhantomData; use action_params::{ActionParams, ActionValue}; use types::executed::CallType; use evm::instructions::{self, Instruction, InstructionInfo}; -use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType}; +use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType, CreateContractAddress}; use bit_set::BitSet; use util::*; @@ -182,7 +182,9 @@ impl Interpreter { fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack) -> evm::Result<()> { let schedule = ext.schedule(); - if !schedule.have_delegate_call && instruction == instructions::DELEGATECALL { + if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call) || + (instruction == instructions::CREATE_P2SH && !schedule.have_create_p2sh) { + return Err(evm::Error::BadInstruction { instruction: instruction }); @@ -266,10 +268,12 @@ impl Interpreter { instructions::JUMPDEST => { // ignore }, - instructions::CREATE => { + instructions::CREATE | instructions::CREATE_P2SH => { let endowment = stack.pop_back(); let init_off = stack.pop_back(); let init_size = stack.pop_back(); + + let address_scheme = if instruction == instructions::CREATE { ext.schedule().create_address } else { CreateContractAddress::FromSenderAndCodeHash }; let create_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is `CREATE`; qed"); let contract_code = self.mem.read_slice(init_off, init_size); @@ -280,7 +284,7 @@ impl Interpreter { return Ok(InstructionResult::UnusedGas(create_gas)); } - let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code); + let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme); return match create_result { ContractCreateResult::Created(address, gas_left) => { stack.push(address_to_u256(address)); diff --git a/ethcore/src/evm/mod.rs b/ethcore/src/evm/mod.rs index cc707d6ef..7906b81ff 100644 --- a/ethcore/src/evm/mod.rs +++ b/ethcore/src/evm/mod.rs @@ -32,7 +32,7 @@ mod tests; mod benches; pub use self::evm::{Evm, Error, Finalize, GasLeft, Result, CostType}; -pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; +pub use self::ext::{Ext, ContractCreateResult, MessageCallResult, CreateContractAddress}; pub use self::factory::{Factory, VMType}; pub use self::schedule::Schedule; pub use types::executed::CallType; diff --git a/ethcore/src/evm/schedule.rs b/ethcore/src/evm/schedule.rs index 70801983d..97df4d784 100644 --- a/ethcore/src/evm/schedule.rs +++ b/ethcore/src/evm/schedule.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . //! Cost schedule and other parameterisations for the EVM. +use evm::CreateContractAddress; /// Definition of the cost schedule and other parameterisations for the EVM. pub struct Schedule { @@ -22,6 +23,8 @@ pub struct Schedule { pub exceptional_failed_code_deposit: bool, /// Does it have a delegate cal pub have_delegate_call: bool, + /// Does it have a CREATE_P2SH instruction + pub have_create_p2sh: bool, /// VM stack limit pub stack_limit: usize, /// Max number of nested calls/creates @@ -99,6 +102,8 @@ pub struct Schedule { pub no_empty: bool, /// Kill empty accounts if touched. pub kill_empty: bool, + /// Contract address generation scheme + pub create_address: CreateContractAddress, } impl Schedule { @@ -113,10 +118,11 @@ impl Schedule { } /// Schedule for the post-EIP-150-era of the Ethereum main net. - pub fn new_post_eip150(max_code_size: usize, fix_exp: bool, no_empty: bool, kill_empty: bool) -> Schedule { + pub fn new_post_eip150(max_code_size: usize, fix_exp: bool, no_empty: bool, kill_empty: bool, have_create_p2sh: bool) -> Schedule { Schedule { exceptional_failed_code_deposit: true, have_delegate_call: true, + have_create_p2sh: have_create_p2sh, stack_limit: 1024, max_depth: 1024, tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], @@ -155,13 +161,20 @@ impl Schedule { sub_gas_cap_divisor: Some(64), no_empty: no_empty, kill_empty: kill_empty, + create_address: if have_create_p2sh { CreateContractAddress::FromCodeHash } else { CreateContractAddress::FromSenderAndNonce }, } } + /// Schedule for the Metropolis of the Ethereum main net. + pub fn new_metropolis() -> Schedule { + Self::new_post_eip150(24576, true, true, true, true) + } + fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule { Schedule { exceptional_failed_code_deposit: efcd, have_delegate_call: hdc, + have_create_p2sh: false, stack_limit: 1024, max_depth: 1024, tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], @@ -200,6 +213,7 @@ impl Schedule { sub_gas_cap_divisor: None, no_empty: false, kill_empty: false, + create_address: CreateContractAddress::FromSenderAndNonce, } } } diff --git a/ethcore/src/evm/tests.rs b/ethcore/src/evm/tests.rs index 3002c170c..b5b2341aa 100644 --- a/ethcore/src/evm/tests.rs +++ b/ethcore/src/evm/tests.rs @@ -18,7 +18,7 @@ use util::*; use action_params::{ActionParams, ActionValue}; use env_info::EnvInfo; use types::executed::CallType; -use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult}; +use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult, CreateContractAddress}; use std::fmt::Debug; pub struct FakeLogEntry { @@ -111,7 +111,7 @@ impl Ext for FakeExt { self.blockhashes.get(number).unwrap_or(&H256::new()).clone() } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult { + fn create(&mut self, gas: &U256, value: &U256, code: &[u8], _address: CreateContractAddress) -> ContractCreateResult { self.calls.insert(FakeCall { call_type: FakeCallType::Create, gas: *gas, diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 5e0ee5662..dc9dcec17 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -22,7 +22,7 @@ use engines::Engine; use types::executed::CallType; use env_info::EnvInfo; use error::ExecutionError; -use evm::{self, Ext, Factory, Finalize}; +use evm::{self, Ext, Factory, Finalize, CreateContractAddress}; use externalities::*; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; use transaction::{Action, SignedTransaction}; @@ -34,14 +34,29 @@ pub use types::executed::{Executed, ExecutionResult}; /// Maybe something like here: `https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp` const STACK_SIZE_PER_DEPTH: usize = 24*1024; -/// Returns new address created from address and given nonce. -pub fn contract_address(address: &Address, nonce: &U256) -> Address { +/// Returns new address created from address, nonce, and code hash +pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address, nonce: &U256, code_hash: &H256) -> Address { use rlp::RlpStream; - let mut stream = RlpStream::new_list(2); - stream.append(address); - stream.append(nonce); - From::from(stream.out().sha3()) + match address_scheme { + CreateContractAddress::FromSenderAndNonce => { + let mut stream = RlpStream::new_list(2); + stream.append(sender); + stream.append(nonce); + From::from(stream.as_raw().sha3()) + }, + CreateContractAddress::FromCodeHash => { + let mut buffer = [0u8; 20 + 32]; + &mut buffer[20..].copy_from_slice(&code_hash[..]); + From::from((&buffer[..]).sha3()) + }, + CreateContractAddress::FromSenderAndCodeHash => { + let mut buffer = [0u8; 20 + 32]; + &mut buffer[..20].copy_from_slice(&sender[..]); + &mut buffer[20..].copy_from_slice(&code_hash[..]); + From::from((&buffer[..]).sha3()) + }, + } } /// Transaction execution options. @@ -125,7 +140,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { let sender = t.sender(); let nonce = self.state.nonce(&sender)?; - let schedule = self.engine.schedule(self.info); + let schedule = self.engine.schedule(self.info.number); let base_gas_required = U256::from(t.gas_required(&schedule)); if t.gas < base_gas_required { @@ -160,17 +175,20 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { } // NOTE: there can be no invalid transactions from this point. - self.state.inc_nonce(&sender)?; + if !t.is_unsigned() { + self.state.inc_nonce(&sender)?; + } self.state.sub_balance(&sender, &U256::from(gas_cost))?; let mut substate = Substate::new(); let (gas_left, output) = match t.action { Action::Create => { - let new_address = contract_address(&sender, &nonce); + let code_hash = t.data.sha3(); + let new_address = contract_address(schedule.create_address, &sender, &nonce, &code_hash); let params = ActionParams { code_address: new_address.clone(), - code_hash: t.data.sha3(), + code_hash: code_hash, address: new_address, sender: sender.clone(), origin: sender.clone(), @@ -253,7 +271,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { // backup used in case of running out of gas self.state.checkpoint(); - let schedule = self.engine.schedule(self.info); + let schedule = self.engine.schedule(self.info.number); // at first, transfer value to destination if let ActionValue::Transfer(val) = params.value { @@ -365,8 +383,14 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { params: ActionParams, substate: &mut Substate, tracer: &mut T, - vm_tracer: &mut V + vm_tracer: &mut V, ) -> evm::Result where T: Tracer, V: VMTracer { + + let schedule = self.engine.schedule(self.info.number); + if schedule.create_address != CreateContractAddress::FromSenderAndNonce && self.state.exists(¶ms.address)? { + return Err(evm::Error::OutOfGas); + } + // backup used in case of running out of gas self.state.checkpoint(); @@ -374,7 +398,6 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { let mut unconfirmed_substate = Substate::new(); // create contract and transfer value to it if necessary - let schedule = self.engine.schedule(self.info); let nonce_offset = if schedule.no_empty {1} else {0}.into(); let prev_bal = self.state.balance(¶ms.address)?; if let ActionValue::Transfer(val) = params.value { @@ -423,7 +446,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { trace: Vec, vm_trace: Option ) -> ExecutionResult { - let schedule = self.engine.schedule(self.info); + let schedule = self.engine.schedule(self.info.number); // refunds from SSTORE nonzero -> zero let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.sstore_clears_count; @@ -525,7 +548,7 @@ mod tests { use util::bytes::BytesRef; use action_params::{ActionParams, ActionValue}; use env_info::EnvInfo; - use evm::{Factory, VMType}; + use evm::{Factory, VMType, CreateContractAddress}; use error::ExecutionError; use state::{Substate, CleanupMode}; use tests::helpers::*; @@ -540,14 +563,14 @@ mod tests { fn test_contract_address() { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let expected_address = Address::from_str("3f09c73a5ed19289fb9bdc72f1742566df146f56").unwrap(); - assert_eq!(expected_address, contract_address(&address, &U256::from(88))); + assert_eq!(expected_address, contract_address(CreateContractAddress::FromSenderAndNonce, &address, &U256::from(88), &H256::default())); } // TODO: replace params with transactions! evm_test!{test_sender_balance: test_sender_balance_jit, test_sender_balance_int} fn test_sender_balance(factory: Factory) { let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); let mut params = ActionParams::default(); params.address = address.clone(); params.sender = sender.clone(); @@ -602,7 +625,7 @@ mod tests { let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); // TODO: add tests for 'callcreate' //let next_address = contract_address(&address, &U256::zero()); let mut params = ActionParams::default(); @@ -658,7 +681,7 @@ mod tests { let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); // TODO: add tests for 'callcreate' //let next_address = contract_address(&address, &U256::zero()); let mut params = ActionParams::default(); @@ -770,7 +793,7 @@ mod tests { let code = "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); // TODO: add tests for 'callcreate' //let next_address = contract_address(&address, &U256::zero()); let mut params = ActionParams::default(); @@ -857,7 +880,7 @@ mod tests { let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d600360e6f0600055".from_hex().unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); // TODO: add tests for 'callcreate' //let next_address = contract_address(&address, &U256::zero()); let mut params = ActionParams::default(); @@ -909,8 +932,8 @@ mod tests { let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0".from_hex().unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(&sender, &U256::zero()); - let next_address = contract_address(&address, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); + let next_address = contract_address(CreateContractAddress::FromSenderAndNonce, &address, &U256::zero(), &H256::default()); let mut params = ActionParams::default(); params.address = address.clone(); params.sender = sender.clone(); @@ -1017,7 +1040,7 @@ mod tests { // 55 - sstore let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let code = "600160005401600055600060006000600060003060e05a03f1600155".from_hex().unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); let mut params = ActionParams::default(); params.address = address.clone(); params.gas = U256::from(100_000); @@ -1052,7 +1075,7 @@ mod tests { nonce: U256::zero() }.sign(keypair.secret(), None); let sender = t.sender(); - let contract = contract_address(&sender, &U256::zero()); + let contract = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(18), CleanupMode::NoEmpty).unwrap(); @@ -1181,7 +1204,7 @@ mod tests { let code = "6064640fffffffff20600055".from_hex().unwrap(); let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); // TODO: add tests for 'callcreate' //let next_address = contract_address(&address, &U256::zero()); let mut params = ActionParams::default(); diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 8591f15fd..0b849033e 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -21,8 +21,9 @@ use state::{Backend as StateBackend, State, Substate}; use engines::Engine; use env_info::EnvInfo; use executive::*; -use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; +use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory, CreateContractAddress}; use types::executed::CallType; +use types::transaction::UNSIGNED_SENDER; use trace::{Tracer, VMTracer}; /// Policy for handling output data on `RETURN` opcode. @@ -97,7 +98,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B> depth: depth, origin_info: origin_info, substate: substate, - schedule: engine.schedule(env_info), + schedule: engine.schedule(env_info.number), output: output, tracer: tracer, vm_tracer: vm_tracer, @@ -147,10 +148,11 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> } } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult { + fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address_scheme: CreateContractAddress) -> ContractCreateResult { // create new contract address + let code_hash = code.sha3(); let address = match self.state.nonce(&self.origin_info.address) { - Ok(nonce) => contract_address(&self.origin_info.address, &nonce), + Ok(nonce) => contract_address(address_scheme, &self.origin_info.address, &nonce, &code_hash), Err(e) => { debug!(target: "ext", "Database corruption encountered: {:?}", e); return ContractCreateResult::Failed @@ -167,14 +169,16 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> gas_price: self.origin_info.gas_price, value: ActionValue::Transfer(*value), code: Some(Arc::new(code.to_vec())), - code_hash: code.sha3(), + code_hash: code_hash, data: None, call_type: CallType::None, }; - if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { - debug!(target: "ext", "Database corruption encountered: {:?}", e); - return ContractCreateResult::Failed + if params.sender != UNSIGNED_SENDER { + if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { + debug!(target: "ext", "Database corruption encountered: {:?}", e); + return ContractCreateResult::Failed + } } let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 9526e5ec2..c34ad69e3 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -21,7 +21,7 @@ use executive::*; use engines::Engine; use env_info::EnvInfo; use evm; -use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult}; +use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult, CreateContractAddress}; use externalities::*; use types::executed::CallType; use tests::helpers::*; @@ -56,7 +56,8 @@ struct TestExt<'a, T: 'a, V: 'a, B: 'a> { ext: Externalities<'a, T, V, B>, callcreates: Vec, - contract_address: Address + nonce: U256, + sender: Address, } impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B> @@ -76,9 +77,10 @@ impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B> vm_tracer: &'a mut V, ) -> trie::Result { Ok(TestExt { - contract_address: contract_address(&address, &state.nonce(&address)?), + nonce: state.nonce(&address)?, ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer, vm_tracer), - callcreates: vec![] + callcreates: vec![], + sender: address, }) } } @@ -114,14 +116,15 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> self.ext.blockhash(number) } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult { + fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult { self.callcreates.push(CallCreate { data: code.to_vec(), destination: None, gas_limit: *gas, value: *value }); - ContractCreateResult::Created(self.contract_address.clone(), *gas) + let contract_address = contract_address(address, &self.sender, &self.nonce, &code.sha3()); + ContractCreateResult::Created(contract_address, *gas) } fn call(&mut self, diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index d0b18d99c..c15847896 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -21,6 +21,8 @@ use ethereum; use spec::Spec; use ethjson; use ethjson::state::test::ForkSpec; +use types::transaction::SignedTransaction; +use env_info::EnvInfo; lazy_static! { pub static ref FRONTIER: Spec = ethereum::new_frontier_test(); @@ -37,7 +39,7 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec { for (name, test) in tests.into_iter() { { let multitransaction = test.transaction; - let env = test.env.into(); + let env: EnvInfo = test.env.into(); let pre: PodState = test.pre_state.into(); for (spec, states) in test.post_states { @@ -54,12 +56,15 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec { let info = format!(" - {} | {:?} ({}/{}) ...", name, spec, i + 1, total); let post_root: H256 = state.hash.into(); - let transaction = multitransaction.select(&state.indexes).into(); - + let transaction: SignedTransaction = multitransaction.select(&state.indexes).into(); let mut state = get_temp_state(); state.populate_from(pre.clone()); - state.commit().expect(&format!("State test {} failed due to internal error.", name)); - let _res = state.apply(&env, &**engine, &transaction, false); + if transaction.verify_basic(true, None, env.number >= engine.params().eip86_transition).is_ok() { + state.commit().expect(&format!("State test {} failed due to internal error.", name)); + let _res = state.apply(&env, &**engine, &transaction, false); + } else { + let _rest = state.commit(); + } if state.root() != &post_root { println!("{} !!! State mismatch (got: {}, expect: {}", info, state.root(), post_root); flushln!("{} fail", info); @@ -73,7 +78,9 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec { } - println!("!!! {:?} tests from failed.", failed.len()); + if !failed.is_empty() { + println!("!!! {:?} tests failed.", failed.len()); + } failed } diff --git a/ethcore/src/json_tests/transaction.rs b/ethcore/src/json_tests/transaction.rs index f400180ee..a3c3c889d 100644 --- a/ethcore/src/json_tests/transaction.rs +++ b/ethcore/src/json_tests/transaction.rs @@ -18,35 +18,37 @@ use super::test_common::*; use evm; use ethjson; use rlp::UntrustedRlp; -use transaction::{Action, UnverifiedTransaction}; -use ethstore::ethkey::public_to_address; +use transaction::{Action, UnverifiedTransaction, SignedTransaction}; fn do_json_test(json_data: &[u8]) -> Vec { let tests = ethjson::transaction::Test::load(json_data).unwrap(); let mut failed = Vec::new(); - let old_schedule = evm::Schedule::new_frontier(); - let new_schedule = evm::Schedule::new_homestead(); + let frontier_schedule = evm::Schedule::new_frontier(); + let homestead_schedule = evm::Schedule::new_homestead(); + let metropolis_schedule = evm::Schedule::new_metropolis(); for (name, test) in tests.into_iter() { let mut fail_unless = |cond: bool, title: &str| if !cond { failed.push(name.clone()); println!("Transaction failed: {:?}: {:?}", name, title); }; let number: Option = test.block_number.map(Into::into); let schedule = match number { - None => &old_schedule, - Some(x) if x < 1_150_000 => &old_schedule, - Some(_) => &new_schedule + None => &frontier_schedule, + Some(x) if x < 1_150_000 => &frontier_schedule, + Some(x) if x < 3_000_000 => &homestead_schedule, + Some(_) => &metropolis_schedule }; let allow_network_id_of_one = number.map_or(false, |n| n >= 2_675_000); + let allow_unsigned = number.map_or(false, |n| n >= 3_000_000); let rlp: Vec = test.rlp.into(); let res = UntrustedRlp::new(&rlp) .as_val() .map_err(From::from) - .and_then(|t: UnverifiedTransaction| t.validate(schedule, schedule.have_delegate_call, allow_network_id_of_one)); + .and_then(|t: UnverifiedTransaction| t.validate(schedule, schedule.have_delegate_call, allow_network_id_of_one, allow_unsigned)); fail_unless(test.transaction.is_none() == res.is_err(), "Validity different"); if let (Some(tx), Some(sender)) = (test.transaction, test.sender) { let t = res.unwrap(); - fail_unless(public_to_address(&t.recover_public().unwrap()) == sender.into(), "sender mismatch"); + fail_unless(SignedTransaction::new(t.clone()).unwrap().sender() == sender.into(), "sender mismatch"); let is_acceptable_network_id = match t.network_id() { None => true, Some(1) if allow_network_id_of_one => true, @@ -84,3 +86,7 @@ declare_test!{TransactionTests_Homestead_ttTransactionTestEip155VitaliksTests, " declare_test!{TransactionTests_EIP155_ttTransactionTest, "TransactionTests/EIP155/ttTransactionTest"} declare_test!{TransactionTests_EIP155_ttTransactionTestEip155VitaliksTests, "TransactionTests/EIP155/ttTransactionTestEip155VitaliksTests"} declare_test!{TransactionTests_EIP155_ttTransactionTestVRule, "TransactionTests/EIP155/ttTransactionTestVRule"} + +declare_test!{TransactionTests_Metropolis_ttMetropolisTest, "TransactionTests/Metropolis/ttMetropolisTest"} +declare_test!{TransactionTests_Metropolis_ttTransactionTest, "TransactionTests/Metropolis/ttTransactionTest"} +declare_test!{TransactionTests_Metropolis_ttTransactionTestZeroSig, "TransactionTests/Metropolis/ttTransactionTestZeroSig"} diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 8f9ed2e5f..980bdd25c 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -170,3 +170,4 @@ mod json_tests; pub use types::*; pub use executive::contract_address; +pub use evm::CreateContractAddress; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 0347e984f..286a89bc1 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1048,7 +1048,7 @@ impl MinerService for Miner { Action::Call(_) => None, Action::Create => { let sender = tx.sender(); - Some(contract_address(&sender, &tx.nonce)) + Some(contract_address(self.engine.schedule(pending.header().number()).create_address, &sender, &tx.nonce, &tx.data.sha3())) } }, logs: receipt.logs.clone(), @@ -1327,6 +1327,10 @@ mod tests { } fn transaction() -> SignedTransaction { + transaction_with_network_id(2) + } + + fn transaction_with_network_id(id: u64) -> SignedTransaction { let keypair = Random.generate().unwrap(); Transaction { action: Action::Create, @@ -1335,7 +1339,7 @@ mod tests { gas: U256::from(100_000), gas_price: U256::zero(), nonce: U256::zero(), - }.sign(keypair.secret(), None) + }.sign(keypair.secret(), Some(id)) } #[test] @@ -1411,18 +1415,19 @@ mod tests { #[test] fn internal_seals_without_work() { - let miner = Miner::with_spec(&Spec::new_instant()); + let spec = Spec::new_instant(); + let miner = Miner::with_spec(&spec); let client = generate_dummy_client(2); - assert_eq!(miner.import_external_transactions(&*client, vec![transaction().into()]).pop().unwrap().unwrap(), TransactionImportResult::Current); + assert_eq!(miner.import_external_transactions(&*client, vec![transaction_with_network_id(spec.network_id()).into()]).pop().unwrap().unwrap(), TransactionImportResult::Current); miner.update_sealing(&*client); client.flush_queue(); assert!(miner.pending_block().is_none()); assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber); - assert_eq!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction().into(), None)).unwrap(), TransactionImportResult::Current); + assert_eq!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction_with_network_id(spec.network_id()).into(), None)).unwrap(), TransactionImportResult::Current); miner.update_sealing(&*client); client.flush_queue(); diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 35f1ca983..c258d89cb 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -57,6 +57,8 @@ pub struct CommonParams { pub eip98_transition: BlockNumber, /// Validate block receipts root. pub validate_receipts_transition: u64, + /// Number of first block where EIP-86 (Metropolis) rules begin. + pub eip86_transition: BlockNumber, } impl From for CommonParams { @@ -71,6 +73,7 @@ impl From for CommonParams { fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None }, eip98_transition: p.eip98_transition.map_or(0, Into::into), validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into), + eip86_transition: p.eip86_transition.map_or(BlockNumber::max_value(), Into::into), } } } @@ -306,6 +309,7 @@ impl Spec { call_type: CallType::None, }; let mut substate = Substate::new(); + state.kill_account(address); { let mut exec = Executive::new(&mut state, &env_info, self.engine.as_ref(), &factories.vm); if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) { @@ -391,6 +395,7 @@ mod tests { #[test] fn genesis_constructor() { + ::ethcore_logger::init_log(); let spec = Spec::new_test_constructor(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let state = State::from_existing(db.boxed_clone(), spec.state_root(), spec.engine.account_start_nonce(), Default::default()).unwrap(); diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 12d7718cc..639fac053 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -1279,7 +1279,7 @@ mod tests { info.number = 0x789b0; let engine = &*Spec::new_test().engine; - println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call); + println!("schedule.have_delegate_call: {:?}", engine.schedule(info.number).have_delegate_call); let t = Transaction { nonce: 0.into(), diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 6f0d7bc26..755c9f3ef 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -162,7 +162,7 @@ fn returns_logs_with_limit() { to_block: BlockId::Latest, address: None, topics: vec![], - limit: Some(2), + limit: None, }); assert_eq!(logs.len(), 0); } diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 1391c37f4..548187e48 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -27,7 +27,6 @@ use builtin::Builtin; use state::*; use evm::Schedule; use engines::Engine; -use env_info::EnvInfo; use ethereum; use ethereum::ethash::EthashParams; use miner::Miner; @@ -72,7 +71,7 @@ impl Engine for TestEngine { self.engine.builtins() } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { + fn schedule(&self, _block_number: u64) -> Schedule { let mut schedule = Schedule::new_frontier(); schedule.max_depth = self.max_depth; schedule @@ -201,7 +200,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(get_test_spec: F, ac action: Action::Create, data: vec![], value: U256::zero(), - }.sign(kp.secret(), None), None).unwrap(); + }.sign(kp.secret(), Some(test_spec.network_id())), None).unwrap(); n += 1; } diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 79e27d97d..f9e9f2c47 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -19,13 +19,16 @@ use std::ops::Deref; use rlp::*; use util::sha3::Hashable; -use util::{H256, Address, U256, Bytes, HeapSizeOf}; +use util::{H256, Address, U256, Bytes, HeapSizeOf, Uint}; use ethkey::{Signature, Secret, Public, recover, public_to_address, Error as EthkeyError}; use error::*; use evm::Schedule; use header::BlockNumber; use ethjson; +/// Fake address for unsigned transactions as defined by EIP-86. +pub const UNSIGNED_SENDER: Address = ::util::H160([0xff; 20]); + #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "ipc", binary)] /// Transaction action type. @@ -110,8 +113,8 @@ impl HeapSizeOf for Transaction { impl From for SignedTransaction { fn from(t: ethjson::state::Transaction) -> Self { let to: Option = t.to.into(); - let secret = Secret::from_slice(&t.secret.0).expect("Valid secret expected."); - Transaction { + let secret = t.secret.map(|s| Secret::from_slice(&s.0).expect("Valid secret expected.")); + let tx = Transaction { nonce: t.nonce.into(), gas_price: t.gas_price.into(), gas: t.gas_limit.into(), @@ -121,7 +124,11 @@ impl From for SignedTransaction { }, value: t.value.into(), data: t.data.into(), - }.sign(&secret, None) + }; + match secret { + Some(s) => tx.sign(&s, None), + None => tx.null_sign(), + } } } @@ -180,8 +187,8 @@ impl Transaction { pub fn invalid_sign(self) -> UnverifiedTransaction { UnverifiedTransaction { unsigned: self, - r: U256::default(), - s: U256::default(), + r: U256::one(), + s: U256::one(), v: 0, hash: 0.into(), }.compute_hash() @@ -192,13 +199,28 @@ impl Transaction { SignedTransaction { transaction: UnverifiedTransaction { unsigned: self, - r: U256::default(), - s: U256::default(), + r: U256::one(), + s: U256::one(), v: 0, hash: 0.into(), }.compute_hash(), sender: from, - public: Public::default(), + public: None, + } + } + + /// Add EIP-86 compatible empty signature. + pub fn null_sign(self) -> SignedTransaction { + SignedTransaction { + transaction: UnverifiedTransaction { + unsigned: self, + r: U256::zero(), + s: U256::zero(), + v: 0, + hash: 0.into(), + }.compute_hash(), + sender: UNSIGNED_SENDER, + public: None, } } @@ -276,6 +298,11 @@ impl UnverifiedTransaction { self } + /// Checks is signature is empty. + pub fn is_unsigned(&self) -> bool { + self.r.is_zero() && self.s.is_zero() + } + /// Append object with a signature into RLP stream fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) { s.begin_list(9); @@ -307,6 +334,7 @@ impl UnverifiedTransaction { /// The network ID, or `None` if this is a global transaction. pub fn network_id(&self) -> Option { match self.v { + v if self.is_unsigned() => Some(v), v if v > 36 => Some((v - 35) / 2), _ => None, } @@ -340,21 +368,33 @@ impl UnverifiedTransaction { // TODO: consider use in block validation. #[cfg(test)] #[cfg(feature = "json-tests")] - pub fn validate(self, schedule: &Schedule, require_low: bool, allow_network_id_of_one: bool) -> Result { - if require_low && !self.signature().is_low_s() { + pub fn validate(self, schedule: &Schedule, require_low: bool, allow_network_id_of_one: bool, allow_empty_signature: bool) -> Result { + let chain_id = if allow_network_id_of_one { Some(1) } else { None }; + self.verify_basic(require_low, chain_id, allow_empty_signature)?; + if !allow_empty_signature || !self.is_unsigned() { + self.recover_public()?; + } + if self.gas < U256::from(self.gas_required(&schedule)) { + return Err(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}).into()) + } + Ok(self) + } + + /// Verify basic signature params. Does not attempt sender recovery. + pub fn verify_basic(&self, check_low_s: bool, chain_id: Option, allow_empty_signature: bool) -> Result<(), Error> { + if check_low_s && !(allow_empty_signature && self.is_unsigned()) { + self.check_low_s()?; + } + // EIP-86: Transactions of this form MUST have gasprice = 0, nonce = 0, value = 0, and do NOT increment the nonce of account 0. + if allow_empty_signature && self.is_unsigned() && !(self.gas_price.is_zero() && self.value.is_zero() && self.nonce.is_zero()) { return Err(EthkeyError::InvalidSignature.into()) } - match self.network_id() { - None => {}, - Some(1) if allow_network_id_of_one => {}, + match (self.network_id(), chain_id) { + (None, _) => {}, + (Some(n), Some(m)) if n == m => {}, _ => return Err(TransactionError::InvalidNetworkId.into()), - } - self.recover_public()?; - if self.gas < U256::from(self.gas_required(&schedule)) { - Err(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}).into()) - } else { - Ok(self) - } + }; + Ok(()) } } @@ -363,7 +403,7 @@ impl UnverifiedTransaction { pub struct SignedTransaction { transaction: UnverifiedTransaction, sender: Address, - public: Public, + public: Option, } impl HeapSizeOf for SignedTransaction { @@ -392,13 +432,21 @@ impl From for UnverifiedTransaction { impl SignedTransaction { /// Try to verify transaction and recover sender. pub fn new(transaction: UnverifiedTransaction) -> Result { - let public = transaction.recover_public()?; - let sender = public_to_address(&public); - Ok(SignedTransaction { - transaction: transaction, - sender: sender, - public: public, - }) + if transaction.is_unsigned() { + Ok(SignedTransaction { + transaction: transaction, + sender: UNSIGNED_SENDER, + public: None, + }) + } else { + let public = transaction.recover_public()?; + let sender = public_to_address(&public); + Ok(SignedTransaction { + transaction: transaction, + sender: sender, + public: Some(public), + }) + } } /// Returns transaction sender. @@ -407,9 +455,14 @@ impl SignedTransaction { } /// Returns a public key of the sender. - pub fn public_key(&self) -> Public { + pub fn public_key(&self) -> Option { self.public } + + /// Checks is signature is empty. + pub fn is_unsigned(&self) -> bool { + self.transaction.is_unsigned() + } } /// Signed Transaction that is a part of canon blockchain. @@ -435,6 +488,9 @@ impl LocalizedTransaction { if let Some(sender) = self.cached_sender { return sender; } + if self.is_unsigned() { + return UNSIGNED_SENDER.clone(); + } let sender = public_to_address(&self.recover_public() .expect("LocalizedTransaction is always constructed from transaction from blockchain; Blockchain only stores verified transactions; qed")); self.cached_sender = Some(sender); diff --git a/evmbin/src/ext.rs b/evmbin/src/ext.rs index a293ccc80..781120c36 100644 --- a/evmbin/src/ext.rs +++ b/evmbin/src/ext.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use std::collections::HashMap; use util::{U256, H256, Address, Bytes, trie}; use ethcore::client::EnvInfo; -use ethcore::evm::{self, Ext, ContractCreateResult, MessageCallResult, Schedule, CallType}; +use ethcore::evm::{self, Ext, ContractCreateResult, MessageCallResult, Schedule, CallType, CreateContractAddress}; pub struct FakeExt { schedule: Schedule, @@ -31,7 +31,7 @@ pub struct FakeExt { impl Default for FakeExt { fn default() -> Self { FakeExt { - schedule: Schedule::new_post_eip150(usize::max_value(), true, true, true), + schedule: Schedule::new_post_eip150(usize::max_value(), true, true, true, true), store: HashMap::new(), depth: 1, } @@ -68,7 +68,7 @@ impl Ext for FakeExt { unimplemented!(); } - fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> ContractCreateResult { + fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8], _address: CreateContractAddress) -> ContractCreateResult { unimplemented!(); } diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index aa2c15da4..31b5cf68a 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -56,6 +56,9 @@ pub struct Params { /// See `CommonParams` docs. #[serde(rename="validateReceiptsTransition")] pub validate_receipts_transition: Option, + /// See `CommonParams` docs. + #[serde(rename="eip86Transition")] + pub eip86_transition: Option, } #[cfg(test)] diff --git a/json/src/state/test.rs b/json/src/state/test.rs index 5cc2b5f4f..ceaccfd17 100644 --- a/json/src/state/test.rs +++ b/json/src/state/test.rs @@ -75,7 +75,7 @@ pub struct MultiTransaction { pub nonce: Uint, /// Secret key. #[serde(rename="secretKey")] - pub secret: H256, + pub secret: Option, /// To. pub to: MaybeEmpty
, /// Value set. diff --git a/json/src/state/transaction.rs b/json/src/state/transaction.rs index 608be68fe..606c40f21 100644 --- a/json/src/state/transaction.rs +++ b/json/src/state/transaction.rs @@ -36,7 +36,7 @@ pub struct Transaction { pub nonce: Uint, /// Secret key. #[serde(rename="secretKey")] - pub secret: H256, + pub secret: Option, /// To. pub to: MaybeEmpty
, /// Value. diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 15fe660b2..7643e0010 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -395,6 +395,7 @@ impl Dependencies for LightDependencies { false => None, }; handler.extend_with(light::ParityClient::new( + self.client.clone(), Arc::new(dispatcher.clone()), self.secret_store.clone(), self.logger.clone(), diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index bce460350..17835485c 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -103,6 +103,7 @@ pub struct EthClient where external_miner: Arc, seed_compute: Mutex, options: EthClientOptions, + eip86_transition: u64, } impl EthClient where @@ -131,6 +132,7 @@ impl EthClient where external_miner: em.clone(), seed_compute: Mutex::new(SeedHashCompute::new()), options: options, + eip86_transition: client.eip86_transition(), } } @@ -166,7 +168,7 @@ impl EthClient where seal_fields: view.seal().into_iter().map(Into::into).collect(), uncles: block.uncle_hashes().into_iter().map(Into::into).collect(), transactions: match include_txs { - true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(Into::into).collect()), + true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(|t| Transaction::from_localized(t, self.eip86_transition)).collect()), false => BlockTransactions::Hashes(block.transaction_hashes().into_iter().map(Into::into).collect()), }, extra_data: Bytes::new(view.extra_data()), @@ -180,7 +182,7 @@ impl EthClient where fn transaction(&self, id: TransactionId) -> Result, Error> { match take_weak!(self.client).transaction(id) { - Some(t) => Ok(Some(Transaction::from(t))), + Some(t) => Ok(Some(Transaction::from_localized(t, self.eip86_transition))), None => Ok(None), } } @@ -507,7 +509,8 @@ impl Eth for EthClient where let hash: H256 = hash.into(); let miner = take_weak!(self.miner); let client = take_weak!(self.client); - Ok(self.transaction(TransactionId::Hash(hash))?.or_else(|| miner.transaction(client.chain_info().best_block_number, &hash).map(Into::into))) + let block_number = client.chain_info().best_block_number; + Ok(self.transaction(TransactionId::Hash(hash))?.or_else(|| miner.transaction(block_number, &hash).map(|t| Transaction::from_pending(t, block_number, self.eip86_transition)))) } fn transaction_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> Result, Error> { diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs index bd55f8c63..e45397f7d 100644 --- a/rpc/src/v1/impls/light/eth.rs +++ b/rpc/src/v1/impls/light/eth.rs @@ -25,7 +25,7 @@ use jsonrpc_core::Error; use jsonrpc_macros::Trailing; use light::cache::Cache as LightDataCache; -use light::client::Client as LightClient; +use light::client::{Client as LightClient, LightChainClient}; use light::{cht, TransactionQueue}; use light::on_demand::{request, OnDemand}; @@ -123,6 +123,7 @@ impl EthClient { fn rich_block(&self, id: BlockId, include_txs: bool) -> BoxFuture, Error> { let (on_demand, sync) = (self.on_demand.clone(), self.sync.clone()); let (client, engine) = (self.client.clone(), self.client.engine().clone()); + let eip86_transition = self.client.eip86_transition(); // helper for filling out a rich block once we've got a block and a score. let fill_rich = move |block: encoded::Block, score: Option| { @@ -149,8 +150,8 @@ impl EthClient { seal_fields: header.seal().into_iter().cloned().map(Into::into).collect(), uncles: block.uncle_hashes().into_iter().map(Into::into).collect(), transactions: match include_txs { - true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(Into::into).collect()), - false => BlockTransactions::Hashes(block.transaction_hashes().into_iter().map(Into::into).collect()), + true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(|t| Transaction::from_localized(t, eip86_transition)).collect()), + _ => BlockTransactions::Hashes(block.transaction_hashes().into_iter().map(Into::into).collect()), }, extra_data: Bytes::new(header.extra_data().to_vec()), }, diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 96fdb61de..cacf33db5 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -28,6 +28,8 @@ use ethstore::random_phrase; use ethsync::LightSyncProvider; use ethcore::account_provider::AccountProvider; +use light::client::LightChainClient; + use jsonrpc_core::Error; use jsonrpc_macros::Trailing; use v1::helpers::{errors, ipfs, SigningQueue, SignerService, NetworkSettings}; @@ -53,11 +55,13 @@ pub struct ParityClient { signer: Option>, dapps_interface: Option, dapps_port: Option, + eip86_transition: u64, } impl ParityClient { /// Creates new `ParityClient`. pub fn new( + client: Arc, light_dispatch: Arc, accounts: Arc, logger: Arc, @@ -74,6 +78,7 @@ impl ParityClient { signer: signer, dapps_interface: dapps_interface, dapps_port: dapps_port, + eip86_transition: client.eip86_transition(), } } @@ -245,7 +250,7 @@ impl Parity for ParityClient { Ok( txq.ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp) .into_iter() - .map(Into::into) + .map(|tx| Transaction::from_pending(tx, chain_info.best_block_number, self.eip86_transition)) .collect::>() ) } @@ -256,7 +261,7 @@ impl Parity for ParityClient { Ok( txq.future_transactions(chain_info.best_block_number, chain_info.best_block_timestamp) .into_iter() - .map(Into::into) + .map(|tx| Transaction::from_pending(tx, chain_info.best_block_number, self.eip86_transition)) .collect::>() ) } diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 78592a91c..1b62b7f81 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -69,6 +69,7 @@ pub struct ParityClient where signer: Option>, dapps_interface: Option, dapps_port: Option, + eip86_transition: u64, } impl ParityClient where @@ -103,6 +104,7 @@ impl ParityClient where signer: signer, dapps_interface: dapps_interface, dapps_port: dapps_port, + eip86_transition: client.eip86_transition(), } } @@ -288,11 +290,13 @@ impl Parity for ParityClient where } fn pending_transactions(&self) -> Result, Error> { - Ok(take_weak!(self.miner).pending_transactions().into_iter().map(Into::into).collect::>()) + let block_number = take_weak!(self.client).chain_info().best_block_number; + Ok(take_weak!(self.miner).pending_transactions().into_iter().map(|t| Transaction::from_pending(t, block_number, self.eip86_transition)).collect::>()) } fn future_transactions(&self) -> Result, Error> { - Ok(take_weak!(self.miner).future_transactions().into_iter().map(Into::into).collect::>()) + let block_number = take_weak!(self.client).chain_info().best_block_number; + Ok(take_weak!(self.miner).future_transactions().into_iter().map(|t| Transaction::from_pending(t, block_number, self.eip86_transition)).collect::>()) } fn pending_transactions_stats(&self) -> Result, Error> { @@ -305,9 +309,10 @@ impl Parity for ParityClient where fn local_transactions(&self) -> Result, Error> { let transactions = take_weak!(self.miner).local_transactions(); + let block_number = take_weak!(self.client).chain_info().best_block_number; Ok(transactions .into_iter() - .map(|(hash, status)| (hash.into(), status.into())) + .map(|(hash, status)| (hash.into(), LocalTransactionStatus::from(status, block_number, self.eip86_transition))) .collect() ) } diff --git a/rpc/src/v1/impls/parity_set.rs b/rpc/src/v1/impls/parity_set.rs index 19483635c..7bd8bcb91 100644 --- a/rpc/src/v1/impls/parity_set.rs +++ b/rpc/src/v1/impls/parity_set.rs @@ -39,9 +39,12 @@ pub struct ParitySetClient { updater: Weak, net: Weak, fetch: F, + eip86_transition: u64, } -impl ParitySetClient { +impl ParitySetClient + where C: MiningBlockChainClient + 'static, +{ /// Creates new `ParitySetClient` with given `Fetch`. pub fn new(client: &Arc, miner: &Arc, updater: &Arc, net: &Arc, fetch: F) -> Self { ParitySetClient { @@ -50,6 +53,7 @@ impl ParitySetClient { updater: Arc::downgrade(updater), net: Arc::downgrade(net), fetch: fetch, + eip86_transition: client.eip86_transition(), } } } @@ -175,8 +179,9 @@ impl ParitySet for ParitySetClient where fn remove_transaction(&self, hash: H256) -> Result, Error> { let miner = take_weak!(self.miner); let client = take_weak!(self.client); + let block_number = take_weak!(self.client).chain_info().best_block_number; let hash = hash.into(); - Ok(miner.remove_pending_transaction(&*client, &hash).map(Into::into)) + Ok(miner.remove_pending_transaction(&*client, &hash).map(|t| Transaction::from_pending(t, block_number, self.eip86_transition))) } } diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index 65d69775a..4383ee560 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -227,7 +227,7 @@ fn rpc_parity_remove_transaction() { let hash = signed.hash(); let request = r#"{"jsonrpc": "2.0", "method": "parity_removeTransaction", "params":[""#.to_owned() + &format!("0x{:?}", hash) + r#""], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0x0072c69d780cdfbfc02fed5c7d184151f9a166971d045e55e27695aaa5bcb55e","input":"0x","networkId":null,"nonce":"0x1","publicKey":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","r":"0x0","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80808080","s":"0x0","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0xa2e0da8a8064e0b9f93e95a53c2db6d01280efb8ac72a708d25487e67dd0f8fc","input":"0x","networkId":null,"nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80800101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#; miner.pending_transactions.lock().insert(hash, signed); assert_eq!(io.handle_request_sync(&request), Some(response.to_owned())); diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 70b163023..9307d95b6 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -304,7 +304,7 @@ fn should_add_sign_transaction_to_the_queue() { 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()) + + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + &format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + &format!("\"s\":\"0x{}\",", U256::from(signature.s()).to_hex()) + diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index b40dec3fb..e8cd1602f 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -16,8 +16,9 @@ use serde::{Serialize, Serializer}; use serde::ser::SerializeStruct; +use util::Hashable; use ethcore::miner; -use ethcore::contract_address; +use ethcore::{contract_address, CreateContractAddress}; use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction}; use v1::helpers::errors; use v1::types::{Bytes, H160, H256, U256, H512, TransactionCondition}; @@ -158,9 +159,11 @@ pub struct RichRawTransaction { pub transaction: Transaction } + impl From for RichRawTransaction { fn from(t: SignedTransaction) -> Self { - let tx: Transaction = t.into(); + // TODO: change transition to 0 when EIP-86 is commonly used. + let tx: Transaction = Transaction::from_signed(t, 0, u64::max_value()); RichRawTransaction { raw: tx.raw.clone(), transaction: tx, @@ -168,9 +171,11 @@ impl From for RichRawTransaction { } } -impl From for Transaction { - fn from(mut t: LocalizedTransaction) -> Transaction { +impl Transaction { + /// Convert `LocalizedTransaction` into RPC Transaction. + pub fn from_localized(mut t: LocalizedTransaction, eip86_transition: u64) -> Transaction { let signature = t.signature(); + let scheme = if t.block_number >= eip86_transition { CreateContractAddress::FromCodeHash } else { CreateContractAddress::FromSenderAndNonce }; Transaction { hash: t.hash().into(), nonce: t.nonce.into(), @@ -187,7 +192,7 @@ impl From for Transaction { gas: t.gas.into(), input: Bytes::new(t.data.clone()), creates: match t.action { - Action::Create => Some(contract_address(&t.sender(), &t.nonce).into()), + Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data.sha3()).into()), Action::Call(_) => None, }, raw: ::rlp::encode(&t.signed).to_vec().into(), @@ -200,11 +205,11 @@ impl From for Transaction { condition: None, } } -} -impl From for Transaction { - fn from(t: SignedTransaction) -> Transaction { + /// Convert `SignedTransaction` into RPC Transaction. + pub fn from_signed(t: SignedTransaction, block_number: u64, eip86_transition: u64) -> Transaction { let signature = t.signature(); + let scheme = if block_number >= eip86_transition { CreateContractAddress::FromCodeHash } else { CreateContractAddress::FromSenderAndNonce }; Transaction { hash: t.hash().into(), nonce: t.nonce.into(), @@ -221,11 +226,11 @@ impl From for Transaction { gas: t.gas.into(), input: Bytes::new(t.data.clone()), creates: match t.action { - Action::Create => Some(contract_address(&t.sender(), &t.nonce).into()), + Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data.sha3()).into()), Action::Call(_) => None, }, raw: ::rlp::encode(&t).to_vec().into(), - public_key: Some(t.public_key().into()), + public_key: t.public_key().map(Into::into), network_id: t.network_id(), standard_v: t.standard_v().into(), v: t.original_v().into(), @@ -234,28 +239,28 @@ impl From for Transaction { condition: None, } } -} -impl From for Transaction { - fn from(t: PendingTransaction) -> Transaction { - let mut r = Transaction::from(t.transaction); + /// Convert `PendingTransaction` into RPC Transaction. + pub fn from_pending(t: PendingTransaction, block_number: u64, eip86_transition: u64) -> Transaction { + let mut r = Transaction::from_signed(t.transaction, block_number, eip86_transition); r.condition = t.condition.map(|b| b.into()); r } } -impl From for LocalTransactionStatus { - fn from(s: miner::LocalTransactionStatus) -> Self { +impl LocalTransactionStatus { + /// Convert `LocalTransactionStatus` into RPC `LocalTransactionStatus`. + pub fn from(s: miner::LocalTransactionStatus, block_number: u64, eip86_transition: u64) -> Self { use ethcore::miner::LocalTransactionStatus::*; match s { Pending => LocalTransactionStatus::Pending, Future => LocalTransactionStatus::Future, - Mined(tx) => LocalTransactionStatus::Mined(tx.into()), - Dropped(tx) => LocalTransactionStatus::Dropped(tx.into()), - Rejected(tx, err) => LocalTransactionStatus::Rejected(tx.into(), errors::transaction_message(err)), - Replaced(tx, gas_price, hash) => LocalTransactionStatus::Replaced(tx.into(), gas_price.into(), hash.into()), - Invalid(tx) => LocalTransactionStatus::Invalid(tx.into()), - Canceled(tx) => LocalTransactionStatus::Canceled(tx.into()), + Mined(tx) => LocalTransactionStatus::Mined(Transaction::from_signed(tx, block_number, eip86_transition)), + Dropped(tx) => LocalTransactionStatus::Dropped(Transaction::from_signed(tx, block_number, eip86_transition)), + Rejected(tx, err) => LocalTransactionStatus::Rejected(Transaction::from_signed(tx, block_number, eip86_transition), errors::transaction_message(err)), + Replaced(tx, gas_price, hash) => LocalTransactionStatus::Replaced(Transaction::from_signed(tx, block_number, eip86_transition), gas_price.into(), hash.into()), + Invalid(tx) => LocalTransactionStatus::Invalid(Transaction::from_signed(tx, block_number, eip86_transition)), + Canceled(tx) => LocalTransactionStatus::Canceled(Transaction::from_pending(tx, block_number, eip86_transition)), } } } diff --git a/sync/src/tests/consensus.rs b/sync/src/tests/consensus.rs index 00a7452c4..6b91b11c6 100644 --- a/sync/src/tests/consensus.rs +++ b/sync/src/tests/consensus.rs @@ -41,7 +41,7 @@ impl IoHandler for TestIoHandler { } } -fn new_tx(secret: &Secret, nonce: U256) -> PendingTransaction { +fn new_tx(secret: &Secret, nonce: U256, network_id: u64) -> PendingTransaction { let signed = Transaction { nonce: nonce.into(), gas_price: 0.into(), @@ -49,7 +49,7 @@ fn new_tx(secret: &Secret, nonce: U256) -> PendingTransaction { action: Action::Call(Address::default()), value: 0.into(), data: Vec::new(), - }.sign(secret, None); + }.sign(secret, Some(network_id)); PendingTransaction::new(signed, None) } @@ -61,6 +61,7 @@ fn authority_round() { ap.insert_account(s0.secret().clone(), "").unwrap(); ap.insert_account(s1.secret().clone(), "").unwrap(); + let network_id = Spec::new_test_round().network_id(); let mut net = TestNet::with_spec_and_accounts(2, SyncConfig::default(), Spec::new_test_round, Some(ap)); let io_handler0: Arc> = Arc::new(TestIoHandler { client: net.peer(0).chain.clone() }); let io_handler1: Arc> = Arc::new(TestIoHandler { client: net.peer(1).chain.clone() }); @@ -74,15 +75,15 @@ fn authority_round() { // exchange statuses net.sync(); // Trigger block proposal - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), network_id)).unwrap(); // Sync a block net.sync(); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), network_id)).unwrap(); // Move to next proposer step. net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); @@ -91,8 +92,8 @@ fn authority_round() { assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2); // Fork the network with equal height. - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), network_id)).unwrap(); // Let both nodes build one block. net.peer(0).chain.engine().step(); let early_hash = net.peer(0).chain.chain_info().best_block_hash; @@ -114,8 +115,8 @@ fn authority_round() { assert_eq!(ci1.best_block_hash, early_hash); // Selfish miner - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into(), network_id)).unwrap(); // Node 0 is an earlier primary. net.peer(0).chain.engine().step(); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4); @@ -126,7 +127,7 @@ fn authority_round() { // Node 1 makes 2 blocks, but is a later primary on the first one. net.peer(1).chain.engine().step(); net.peer(1).chain.engine().step(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into())).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into(), network_id)).unwrap(); net.peer(1).chain.engine().step(); net.peer(1).chain.engine().step(); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 5); @@ -147,6 +148,7 @@ fn tendermint() { ap.insert_account(s0.secret().clone(), "").unwrap(); ap.insert_account(s1.secret().clone(), "").unwrap(); + let network_id = Spec::new_test_tendermint().network_id(); let mut net = TestNet::with_spec_and_accounts(2, SyncConfig::default(), Spec::new_test_tendermint, Some(ap)); let io_handler0: Arc> = Arc::new(TestIoHandler { client: net.peer(0).chain.clone() }); let io_handler1: Arc> = Arc::new(TestIoHandler { client: net.peer(1).chain.clone() }); @@ -162,7 +164,7 @@ fn tendermint() { // Exhange statuses net.sync(); // Propose - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), network_id)).unwrap(); net.sync(); // Propose timeout, synchronous for now net.peer(0).chain.engine().step(); @@ -173,7 +175,7 @@ fn tendermint() { assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into())).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), network_id)).unwrap(); // Commit timeout net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); @@ -187,8 +189,8 @@ fn tendermint() { assert_eq!(net.peer(0).chain.chain_info().best_block_number, 2); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), network_id)).unwrap(); // Peers get disconnected. // Commit net.peer(0).chain.engine().step(); @@ -196,8 +198,8 @@ fn tendermint() { // Propose net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), network_id)).unwrap(); // Send different prevotes net.sync(); // Prevote timeout From 04e581db616416ddec966359492470b70cd56f5d Mon Sep 17 00:00:00 2001 From: arkpar Date: Wed, 19 Apr 2017 14:48:02 +0200 Subject: [PATCH 026/117] Correct contract address for EIP-86 --- ethcore/src/executive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index dc9dcec17..494d59db9 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -46,7 +46,7 @@ pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address, From::from(stream.as_raw().sha3()) }, CreateContractAddress::FromCodeHash => { - let mut buffer = [0u8; 20 + 32]; + let mut buffer = [0xffu8; 20 + 32]; &mut buffer[20..].copy_from_slice(&code_hash[..]); From::from((&buffer[..]).sha3()) }, From a278dd5a0a45510c3f593e3c8a1f8e36f58adde1 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 19 Apr 2017 14:58:19 +0200 Subject: [PATCH 027/117] store epoch transition proofs in DB --- Cargo.lock | 10 +- ethcore/light/src/provider.rs | 2 +- ethcore/src/blockchain/blockchain.rs | 18 ++++ ethcore/src/blockchain/extras.rs | 92 +++++++++++++++++- ethcore/src/blockchain/mod.rs | 1 + ethcore/src/client/client.rs | 96 ++++++++++++++----- ethcore/src/client/test_client.rs | 2 +- ethcore/src/client/traits.rs | 4 +- ethcore/src/engines/authority_round.rs | 4 +- ethcore/src/engines/basic_authority.rs | 4 +- ethcore/src/engines/epoch_verifier.rs | 5 +- ethcore/src/engines/mod.rs | 6 +- ethcore/src/engines/validator_set/contract.rs | 2 +- ethcore/src/engines/validator_set/mod.rs | 4 +- ethcore/src/engines/validator_set/multi.rs | 13 ++- .../engines/validator_set/safe_contract.rs | 13 +-- .../src/engines/validator_set/simple_list.rs | 6 +- ethcore/src/ethereum/ethash.rs | 2 +- ethcore/src/migrations/state/v7.rs | 2 +- ethcore/src/migrations/v10.rs | 2 +- ethcore/src/migrations/v9.rs | 2 +- ethcore/src/tests/client.rs | 2 +- util/Cargo.toml | 2 +- util/rlp/Cargo.toml | 2 +- util/src/journaldb/mod.rs | 4 +- util/src/kvdb.rs | 3 + 26 files changed, 234 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 830e1e75a..20887561b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,8 +339,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "elastic-array" -version = "0.6.0" -source = "git+https://github.com/paritytech/elastic-array#346f1ba5982576dab9d0b8fa178b50e1db0a21cd" +version = "0.7.0" +source = "git+https://github.com/paritytech/elastic-array?branch=0.7.0#970a11eca8a6b3591b476155c5896b4757b5c7b8" dependencies = [ "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -713,7 +713,7 @@ version = "1.7.0" dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.6.0 (git+https://github.com/paritytech/elastic-array)", + "elastic-array 0.7.0 (git+https://github.com/paritytech/elastic-array?branch=0.7.0)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)", "ethcore-bigint 0.1.2", @@ -2018,7 +2018,7 @@ name = "rlp" version = "0.1.0" dependencies = [ "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.6.0 (git+https://github.com/paritytech/elastic-array)", + "elastic-array 0.7.0 (git+https://github.com/paritytech/elastic-array?branch=0.7.0)", "ethcore-bigint 0.1.2", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2790,7 +2790,7 @@ dependencies = [ "checksum docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab32ea6e284d87987066f21a9e809a73c14720571ef34516f0890b3d355ccfd8" "checksum dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" "checksum either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2b503c86dad62aaf414ecf2b8c527439abedb3f8d812537f0b12bfd6f32a91" -"checksum elastic-array 0.6.0 (git+https://github.com/paritytech/elastic-array)" = "" +"checksum elastic-array 0.7.0 (git+https://github.com/paritytech/elastic-array?branch=0.7.0)" = "" "checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83" "checksum eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)" = "" "checksum ethabi 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63df67d0af5e3cb906b667ca1a6e00baffbed87d0d8f5f78468a1f5eb3a66345" diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index e74ab1c70..3632783ca 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -258,7 +258,7 @@ impl Provider for T { }.fake_sign(req.from); self.prove_transaction(transaction, id) - .map(|proof| ::request::ExecutionResponse { items: proof }) + .map(|(_, proof)| ::request::ExecutionResponse { items: proof }) } fn ready_transactions(&self) -> Vec { diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 8986dc6b8..00420e58f 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -797,6 +797,24 @@ impl BlockChain { } } + /// Insert an epoch transition. Provide an epoch number being transitioned to + /// and epoch transition object. + /// + /// The block the transition occurred at should have already been inserted into the chain. + pub fn insert_epoch_transition(&self, batch: &mut DBTransaction, epoch_num: u64, transition: EpochTransition) { + let mut transitions = match self.db.read(db::COL_EXTRA, &epoch_num) { + Some(existing) => existing, + None => EpochTransitions { + number: epoch_num, + candidates: Vec::with_capacity(1), + } + }; + + transitions.candidates.push(transition); + + batch.write(db::COL_EXTRA, &epoch_num, &transitions); + } + /// Add a child to a given block. Assumes that the block hash is in /// the chain and the child's parent is this block. /// diff --git a/ethcore/src/blockchain/extras.rs b/ethcore/src/blockchain/extras.rs index 0c8616a45..b9fd5d3bd 100644 --- a/ethcore/src/blockchain/extras.rs +++ b/ethcore/src/blockchain/extras.rs @@ -18,6 +18,7 @@ use bloomchain; use util::*; +use util::kvdb::PREFIX_LEN as DB_PREFIX_LEN; use rlp::*; use header::BlockNumber; use receipt::Receipt; @@ -37,6 +38,8 @@ pub enum ExtrasIndex { BlocksBlooms = 3, /// Block receipts index BlockReceipts = 4, + /// Epoch transition data index. + EpochTransitions = 5, } fn with_index(hash: &H256, i: ExtrasIndex) -> H264 { @@ -134,6 +137,39 @@ impl Key for H256 { } } +/// length of epoch keys. +pub const EPOCH_KEY_LEN: usize = DB_PREFIX_LEN + 16; + +/// epoch key prefix. +/// used to iterate over all epoch transitions in order from genesis. +pub fn epoch_key_prefix() -> [u8; DB_PREFIX_LEN] { + let mut arr = [0u8; DB_PREFIX_LEN]; + arr[0] = ExtrasIndex::EpochTransitions as u8; + + arr +} + +pub struct EpochTransitionsKey([u8; EPOCH_KEY_LEN]); +impl Deref for EpochTransitionsKey { + type Target = [u8]; + + fn deref(&self) -> &[u8] { &self.0[..] } +} + +impl Key for u64 { + type Target = EpochTransitionsKey; + + fn key(&self) -> Self::Target { + let mut arr = [0u8; EPOCH_KEY_LEN]; + arr[..DB_PREFIX_LEN].copy_from_slice(&epoch_key_prefix()[..]); + + write!(&mut arr[DB_PREFIX_LEN..], "{:016x}", self) + .expect("format arg is valid; no more than 16 chars will be written; qed"); + + EpochTransitionsKey(arr) + } +} + /// Familial details concerning a block #[derive(Debug, Clone)] pub struct BlockDetails { @@ -144,7 +180,7 @@ pub struct BlockDetails { /// Parent block hash pub parent: H256, /// List of children block hashes - pub children: Vec + pub children: Vec, } impl HeapSizeOf for BlockDetails { @@ -241,6 +277,60 @@ impl HeapSizeOf for BlockReceipts { } } +/// Candidate transitions to an epoch with specific number. +#[derive(Clone)] +pub struct EpochTransitions { + pub number: u64, + pub candidates: Vec, +} + +impl Encodable for EpochTransitions { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2).append(&self.number).append_list(&self.candidates); + } +} + +impl Decodable for EpochTransitions { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(EpochTransitions { + number: rlp.val_at(0)?, + candidates: rlp.list_at(1)?, + }) + } +} + +#[derive(Debug, Clone)] +pub struct EpochTransition { + pub block_hash: H256, // block hash at which the transition occurred. + pub proof: Vec, // "transition/epoch" proof from the engine. + pub state_proof: Vec, // state items necessary to regenerate proof. +} + +impl Encodable for EpochTransition { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3) + .append(&self.block_hash) + .append(&self.proof) + .begin_list(self.state_proof.len()); + + for item in &self.state_proof { + s.append(&&**item); + } + } +} + +impl Decodable for EpochTransition { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(EpochTransition { + block_hash: rlp.val_at(0)?, + proof: rlp.val_at(1)?, + state_proof: rlp.at(2)?.iter().map(|x| { + Ok(DBValue::from_slice(x.data()?)) + }).collect::, _>>()?, + }) + } +} + #[cfg(test)] mod tests { use rlp::*; diff --git a/ethcore/src/blockchain/mod.rs b/ethcore/src/blockchain/mod.rs index 55a67e2b6..2d6d6ddb3 100644 --- a/ethcore/src/blockchain/mod.rs +++ b/ethcore/src/blockchain/mod.rs @@ -31,5 +31,6 @@ pub mod generator; pub use self::blockchain::{BlockProvider, BlockChain}; pub use self::cache::CacheSize; pub use self::config::Config; +pub use self::extras::EpochTransition; pub use types::tree_route::TreeRoute; pub use self::import_route::ImportRoute; diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index a91294e27..ab9a07691 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -32,13 +32,13 @@ use util::kvdb::*; // other use basic_types::Seal; use block::*; -use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; +use blockchain::{BlockChain, BlockProvider, EpochTransition, TreeRoute, ImportRoute}; use blockchain::extras::TransactionAddress; use client::Error as ClientError; use client::{ BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, EngineClient, TraceFilter, CallAnalytics, BlockImportError, Mode, - ChainNotify, PruningInfo, + ChainNotify, PruningInfo, ProvingBlockChainClient, }; use encoded; use engines::Engine; @@ -578,15 +578,15 @@ impl Client { // generate validation proof if the engine requires them. // TODO: make conditional? - let generate_proof = { + let entering_new_epoch = { use engines::EpochChange; match self.engine.is_epoch_end(block.header(), Some(block_data), Some(&receipts)) { - EpochChange::Yes(_) => true, - EpochChange::No => false, + EpochChange::Yes(e, p) => Some((block.header().clone(), e, p)), + EpochChange::No => None, EpochChange::Unsure(_) => { warn!(target: "client", "Detected invalid engine implementation."); warn!(target: "client", "Engine claims to require more block data, but everything provided."); - false + None } } }; @@ -596,14 +596,53 @@ impl Client { // TODO: Prove it with a test. let mut state = block.drain(); - if generate_proof { - debug!(target: "client", "Generating validation proof for block {}", hash); - - // TODO - } - state.journal_under(&mut batch, number, hash).expect("DB commit failed"); let route = chain.insert_block(&mut batch, block_data, receipts); + + if let Some((header, epoch, expected)) = entering_new_epoch { + use std::cell::RefCell; + use std::collections::BTreeSet; + + debug!(target: "client", "Generating validation proof for block {}", hash); + + // proof is two-part. state items read in lexicographical order, + // and the secondary "proof" part. + let read_values = RefCell::new(BTreeSet::new()); + let block_id = BlockId::Hash(hash.clone()); + let proof = { + let call = |a, d| { + let tx = self.contract_call_tx(block_id, a, d); + let (result, items) = self.prove_transaction(tx, block_id) + .ok_or_else(|| format!("Unable to make call to generate epoch proof."))?; + + read_values.borrow_mut().extend(items); + Ok(result) + }; + + self.engine.epoch_proof(&header, &call) + }; + + match proof { + Ok(proof) => { + if proof != expected { + warn!(target: "client", "Extracted epoch change proof different than expected."); + warn!(target: "client", "Using a custom engine implementation?"); + } + + // insert into database, using the generated proof. + chain.insert_epoch_transition(&mut batch, epoch, EpochTransition { + block_hash: hash.clone(), + proof: proof, + state_proof: read_values.into_inner().into_iter().collect(), + }); + } + Err(e) => { + warn!(target: "client", "Error generating epoch change proof for block {}: {}", hash, e); + warn!(target: "client", "Snapshots generated by this node will be incomplete."); + } + } + } + self.tracedb.read().import(&mut batch, TraceImportRequest { traces: traces.into(), block_hash: hash.clone(), @@ -893,6 +932,20 @@ impl Client { } } } + + // transaction for calling contracts from services like engine. + // from the null sender, with 50M gas. + fn contract_call_tx(&self, block_id: BlockId, address: Address, data: Bytes) -> SignedTransaction { + let from = Address::default(); + Transaction { + nonce: self.nonce(&from, block_id).unwrap_or_else(|| self.engine.account_start_nonce()), + action: Action::Call(address), + gas: U256::from(50_000_000), + gas_price: U256::default(), + value: U256::default(), + data: data, + }.fake_sign(from) + } } impl snapshot::DatabaseRestore for Client { @@ -1483,15 +1536,7 @@ impl BlockChainClient for Client { } fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result { - let from = Address::default(); - let transaction = Transaction { - nonce: self.latest_nonce(&from), - action: Action::Call(address), - gas: U256::from(50_000_000), - gas_price: U256::default(), - value: U256::default(), - data: data, - }.fake_sign(from); + let transaction = self.contract_call_tx(block_id, address, data); self.call(&transaction, block_id, Default::default()) .map_err(|e| format!("{:?}", e)) @@ -1643,7 +1688,7 @@ impl MayPanic for Client { } } -impl ::client::ProvingBlockChainClient for Client { +impl ProvingBlockChainClient for Client { fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec, H256)> { self.state_at(id) .and_then(move |state| state.prove_storage(key1, key2).ok()) @@ -1654,7 +1699,7 @@ impl ::client::ProvingBlockChainClient for Client { .and_then(move |state| state.prove_account(key1).ok()) } - fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option> { + fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec)> { let (state, mut env_info) = match (self.state_at(id), self.env_info(id)) { (Some(s), Some(e)) => (s, e), _ => return None, @@ -1669,8 +1714,9 @@ impl ::client::ProvingBlockChainClient for Client { let res = Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(&transaction, options); match res { - Err(ExecutionError::Internal(_)) => return None, - _ => return Some(state.drop().1.extract_proof()), + Err(ExecutionError::Internal(_)) => None, + Err(_) => Some((Vec::new(), state.drop().1.extract_proof())), + Ok(res) => Some((res.output, state.drop().1.extract_proof())), } } } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 16f38203f..43d75cecd 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -767,7 +767,7 @@ impl ProvingBlockChainClient for TestBlockChainClient { None } - fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option> { + fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option<(Bytes, Vec)> { None } } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index a612d8a77..e17c324aa 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -324,5 +324,7 @@ pub trait ProvingBlockChainClient: BlockChainClient { fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec, BasicAccount)>; /// Prove execution of a transaction at the given block. - fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option>; + /// Returns the output of the call and a vector of database items necessary + /// to reproduce it. + fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec)>; } diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 1f3edf57c..55640478e 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -133,13 +133,13 @@ pub struct AuthorityRound { // header-chain validator. struct EpochVerifier { - epoch_number: U256, + epoch_number: u64, step: Arc, subchain_validators: SimpleList, } impl super::EpochVerifier for EpochVerifier { - fn epoch_number(&self) -> U256 { self.epoch_number.clone() } + fn epoch_number(&self) -> u64 { self.epoch_number.clone() } fn verify_light(&self, header: &Header) -> Result<(), Error> { // always check the seal since it's fast. // nothing heavier to do. diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 94c3fe578..35db2a486 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -52,12 +52,12 @@ impl From for BasicAuthorityParams { } struct EpochVerifier { - epoch_number: U256, + epoch_number: u64, list: SimpleList, } impl super::EpochVerifier for EpochVerifier { - fn epoch_number(&self) -> U256 { self.epoch_number.clone() } + fn epoch_number(&self) -> u64 { self.epoch_number.clone() } fn verify_light(&self, header: &Header) -> Result<(), Error> { verify_external(header, &self.list) } diff --git a/ethcore/src/engines/epoch_verifier.rs b/ethcore/src/engines/epoch_verifier.rs index fd0847e76..62740fbd4 100644 --- a/ethcore/src/engines/epoch_verifier.rs +++ b/ethcore/src/engines/epoch_verifier.rs @@ -18,14 +18,13 @@ use error::Error; use header::Header; -use util::U256; /// Verifier for all blocks within an epoch without accessing /// /// See docs on `Engine` relating to proving functions for more details. pub trait EpochVerifier: Sync { /// Get the epoch number. - fn epoch_number(&self) -> U256; + fn epoch_number(&self) -> u64; /// Lightly verify the next block header. /// This may not be a header belonging to a different epoch. @@ -41,6 +40,6 @@ pub trait EpochVerifier: Sync { pub struct NoOp; impl EpochVerifier for NoOp { - fn epoch_number(&self) -> U256 { 0.into() } + fn epoch_number(&self) -> u64 { 0 } fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index b67afdf2f..badac3cf2 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -96,7 +96,7 @@ pub enum Seal { } /// Type alias for a function we can make calls through synchronously. -pub type Call = Fn(Address, Bytes) -> Result; +pub type Call<'a> = Fn(Address, Bytes) -> Result + 'a; /// Results of a query of whether an epoch change occurred at the given block. #[derive(Debug, Clone, PartialEq)] @@ -105,8 +105,8 @@ pub enum EpochChange { Unsure(Unsure), /// No epoch change. No, - /// Validation proof required, and the expected output - Yes(Bytes), + /// Validation proof required, and the new epoch number and expected proof. + Yes(u64, Bytes), } /// More data required to determine if an epoch change occurred at a given block. diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 0fdef84a3..22506c348 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -76,7 +76,7 @@ impl ValidatorSet for ValidatorContract { self.validators.epoch_proof(header, caller) } - fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(U256, super::SimpleList), ::error::Error> { + fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(u64, super::SimpleList), ::error::Error> { self.validators.epoch_set(header, proof) } diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index e9dc333c5..a7e0f196f 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -23,7 +23,7 @@ mod multi; use std::sync::Weak; use ids::BlockId; -use util::{Address, H256, U256}; +use util::{Address, H256}; use ethjson::spec::ValidatorSet as ValidatorSpec; use client::Client; use header::Header; @@ -97,7 +97,7 @@ pub trait ValidatorSet: Send + Sync { /// the proof is invalid. /// /// Returns the epoch number and proof. - fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(U256, SimpleList), ::error::Error>; + fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(u64, SimpleList), ::error::Error>; /// Checks if a given address is a validator, with the given function /// for executing synchronous calls to contracts. diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index eecf88432..abb034698 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -19,7 +19,7 @@ use std::collections::BTreeMap; use std::sync::Weak; use engines::{Call, EpochChange}; -use util::{H256, Address, RwLock, U256}; +use util::{H256, Address, RwLock}; use ids::BlockId; use header::{BlockNumber, Header}; use client::{Client, BlockChainClient}; @@ -75,19 +75,24 @@ impl ValidatorSet for Multi { fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) -> EpochChange { - self.correct_set_by_number(header.number()).1.is_epoch_end(header, block, receipts) + let (set_block, set) = self.correct_set_by_number(header.number()); + + match set.is_epoch_end(header, block, receipts) { + EpochChange::Yes(num, proof) => EpochChange::Yes(set_block + num, proof), + other => other, + } } fn epoch_proof(&self, header: &Header, caller: &Call) -> Result, String> { self.correct_set_by_number(header.number()).1.epoch_proof(header, caller) } - fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(U256, super::SimpleList), ::error::Error> { + fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(u64, super::SimpleList), ::error::Error> { // "multi" epoch is the inner set's epoch plus the transition block to that set. // ensures epoch increases monotonically. let (set_block, set) = self.correct_set_by_number(header.number()); let (inner_epoch, list) = set.epoch_set(header, proof)?; - Ok((U256::from(set_block) + inner_epoch, list)) + Ok((set_block + inner_epoch, list)) } fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 3284ec5ab..cbfaa9883 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -165,8 +165,6 @@ impl ValidatorSet for ValidatorSafeContract { } }); - - // TODO: are multiple transitions per block possible? match decoded_events.next() { None => ::engines::EpochChange::No, Some(matched_event) => { @@ -184,8 +182,11 @@ impl ValidatorSet for ValidatorSafeContract { ); match (nonce, validators) { - (Some(nonce), Some(validators)) => - ::engines::EpochChange::Yes(encode_proof(nonce, &validators)), + (Some(nonce), Some(validators)) => { + let proof = encode_proof(nonce, &validators); + let new_epoch = nonce.low_u64(); + ::engines::EpochChange::Yes(new_epoch, proof) + } _ => { debug!(target: "engine", "Successfully decoded log turned out to be bad."); ::engines::EpochChange::No @@ -206,11 +207,11 @@ impl ValidatorSet for ValidatorSafeContract { } } - fn epoch_set(&self, _header: &Header, proof: &[u8]) -> Result<(U256, SimpleList), ::error::Error> { + fn epoch_set(&self, _header: &Header, proof: &[u8]) -> Result<(u64, SimpleList), ::error::Error> { use rlp::UntrustedRlp; let rlp = UntrustedRlp::new(proof); - let nonce: U256 = rlp.val_at(0)?; + let nonce: u64 = rlp.val_at(0)?; let validators: Vec
= rlp.list_at(1)?; Ok((nonce, SimpleList::new(validators))) diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index 59209fac0..15cf141f6 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -16,7 +16,7 @@ /// Preconfigured validator list. -use util::{H256, Address, HeapSizeOf, U256}; +use util::{H256, Address, HeapSizeOf}; use engines::Call; use header::Header; @@ -63,8 +63,8 @@ impl ValidatorSet for SimpleList { Ok(Vec::new()) } - fn epoch_set(&self, _header: &Header, _: &[u8]) -> Result<(U256, SimpleList), ::error::Error> { - Ok((0.into(), self.clone())) + fn epoch_set(&self, _header: &Header, _: &[u8]) -> Result<(u64, SimpleList), ::error::Error> { + Ok((0, self.clone())) } fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool { diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 867d4f775..548d0dbaa 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -158,7 +158,7 @@ impl Ethash { // in the future, we might move the Ethash epoch // caching onto this mechanism as well. impl ::engines::EpochVerifier for Arc { - fn epoch_number(&self) -> U256 { 0.into() } + fn epoch_number(&self) -> u64 { 0 } fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } fn verify_heavy(&self, header: &Header) -> Result<(), Error> { self.verify_block_unordered(header, None) diff --git a/ethcore/src/migrations/state/v7.rs b/ethcore/src/migrations/state/v7.rs index e9da80c31..174945c9b 100644 --- a/ethcore/src/migrations/state/v7.rs +++ b/ethcore/src/migrations/state/v7.rs @@ -238,7 +238,7 @@ impl Migration for OverlayRecentV7 { } let mut count = 0; - for (key, value) in source.iter(None) { + for (key, value) in source.iter(None).into_iter().flat_map(|inner| inner) { count += 1; if count == 100_000 { count = 0; diff --git a/ethcore/src/migrations/v10.rs b/ethcore/src/migrations/v10.rs index 45fc457bd..15abf4790 100644 --- a/ethcore/src/migrations/v10.rs +++ b/ethcore/src/migrations/v10.rs @@ -102,7 +102,7 @@ impl Migration for ToV10 { fn migrate(&mut self, source: Arc, config: &Config, dest: &mut Database, col: Option) -> Result<(), Error> { let mut batch = Batch::new(config, col); - for (key, value) in source.iter(col) { + for (key, value) in source.iter(col).into_iter().flat_map(|inner| inner) { self.progress.tick(); batch.insert(key.to_vec(), value.to_vec(), dest)?; } diff --git a/ethcore/src/migrations/v9.rs b/ethcore/src/migrations/v9.rs index 9a6eb5088..4ca6e3a82 100644 --- a/ethcore/src/migrations/v9.rs +++ b/ethcore/src/migrations/v9.rs @@ -59,7 +59,7 @@ impl Migration for ToV9 { fn migrate(&mut self, source: Arc, config: &Config, dest: &mut Database, col: Option) -> Result<(), Error> { let mut batch = Batch::new(config, self.column); - for (key, value) in source.iter(col) { + for (key, value) in source.iter(col).into_iter().flat_map(|inner| inner) { self.progress.tick(); match self.extract { Extract::Header => { diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index e61edd478..7d630fbee 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -367,7 +367,7 @@ fn transaction_proof() { data: Vec::new(), }.fake_sign(address); - let proof = client.prove_transaction(transaction.clone(), BlockId::Latest).unwrap(); + let proof = client.prove_transaction(transaction.clone(), BlockId::Latest).unwrap().1; let backend = state::backend::ProofCheck::new(&proof); let mut factories = ::factory::Factories::default(); diff --git a/util/Cargo.toml b/util/Cargo.toml index d4e2dcfb5..8b902467f 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -16,7 +16,7 @@ time = "0.1.34" rocksdb = { git = "https://github.com/paritytech/rust-rocksdb" } eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" } rust-crypto = "0.2.34" -elastic-array = { git = "https://github.com/paritytech/elastic-array" } +elastic-array = "0.7.0" rlp = { path = "rlp" } heapsize = { version = "0.3", features = ["unstable"] } itertools = "0.5" diff --git a/util/rlp/Cargo.toml b/util/rlp/Cargo.toml index 17aed5dad..643e9dc99 100644 --- a/util/rlp/Cargo.toml +++ b/util/rlp/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" authors = ["Parity Technologies "] [dependencies] -elastic-array = { git = "https://github.com/paritytech/elastic-array" } +elastic-array = "0.7.0" ethcore-bigint = { path = "../bigint" } lazy_static = "0.2" rustc-serialize = "0.3" diff --git a/util/src/journaldb/mod.rs b/util/src/journaldb/mod.rs index e949b269f..84a71339a 100644 --- a/util/src/journaldb/mod.rs +++ b/util/src/journaldb/mod.rs @@ -125,8 +125,8 @@ pub fn new(backing: Arc<::kvdb::KeyValueDB>, algorithm: Algorithm, col: Option Date: Wed, 19 Apr 2017 15:15:43 +0200 Subject: [PATCH 028/117] Store the pending requests per network version (#5405) * Store the requests in LS per network version * Fixing tests * Add network switching test * Fixes --- js/src/redux/providers/requestsActions.js | 11 +++++- js/src/views/Application/Requests/requests.js | 2 +- .../Application/Requests/savedRequests.js | 37 +++++++++++++++++-- .../Requests/savedRequests.spec.js | 25 +++++++++++-- 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/js/src/redux/providers/requestsActions.js b/js/src/redux/providers/requestsActions.js index d31f0d1b3..970bcba91 100644 --- a/js/src/redux/providers/requestsActions.js +++ b/js/src/redux/providers/requestsActions.js @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import BigNumber from 'bignumber.js'; + import { outTransaction } from '~/api/format/output'; import { trackRequest as trackRequestUtil, parseTransactionReceipt } from '~/util/tx'; import SavedRequests from '~/views/Application/Requests/savedRequests'; @@ -29,7 +31,7 @@ export const init = (api) => (dispatch) => { dispatch(watchRequest(request)); }); - api.on('connected', () => { + api.once('connected', () => { savedRequests.load(api).then((requests) => { requests.forEach((request) => dispatch(watchRequest(request))); }); @@ -48,7 +50,9 @@ export const watchRequest = (request) => (dispatch, getState) => { export const trackRequest = (requestId, { transactionHash = null } = {}) => (dispatch, getState) => { const { api } = getState(); - trackRequestUtil(api, { requestId, transactionHash }, (error, data) => { + trackRequestUtil(api, { requestId, transactionHash }, (error, _data = {}) => { + const data = { ..._data }; + if (error) { console.error(error); return dispatch(setRequest(requestId, { error })); @@ -61,6 +65,9 @@ export const trackRequest = (requestId, { transactionHash = null } = {}) => (dis const requestData = requests[requestId]; let blockSubscriptionId = -1; + // Set the block height to 0 at the beggining + data.blockHeight = new BigNumber(0); + // If the request was a contract deployment, // then add the contract with the saved metadata to the account if (requestData.metadata && requestData.metadata.deployment) { diff --git a/js/src/views/Application/Requests/requests.js b/js/src/views/Application/Requests/requests.js index 2c2224ea0..1054cc4e2 100644 --- a/js/src/views/Application/Requests/requests.js +++ b/js/src/views/Application/Requests/requests.js @@ -145,7 +145,7 @@ class Requests extends Component { />
diff --git a/js/src/views/Application/Requests/savedRequests.js b/js/src/views/Application/Requests/savedRequests.js index ee5f78811..54f1a7a9a 100644 --- a/js/src/views/Application/Requests/savedRequests.js +++ b/js/src/views/Application/Requests/savedRequests.js @@ -21,9 +21,28 @@ import { ERROR_CODES } from '~/api/transport/error'; export const LS_REQUESTS_KEY = '_parity::requests'; export default class SavedRequests { - load (api) { - const requests = this._get(); + network = null; + /** + * Load the network version, and then the related requests + */ + load (api) { + return api.net.version() + .then((network) => { + this.network = network; + return this.loadRequests(api); + }) + .catch((error) => { + console.error(error); + return []; + }); + } + + /** + * Load the requests of the current network + */ + loadRequests (api) { + const requests = this._get(); const promises = Object.values(requests).map((request) => { const { requestId, transactionHash } = request; @@ -67,11 +86,21 @@ export default class SavedRequests { } _get () { - return store.get(LS_REQUESTS_KEY) || {}; + const allRequests = store.get(LS_REQUESTS_KEY) || {}; + + return allRequests[this.network] || {}; } _set (requests = {}) { - return store.set(LS_REQUESTS_KEY, requests); + const allRequests = store.get(LS_REQUESTS_KEY) || {}; + + if (Object.keys(requests).length > 0) { + allRequests[this.network] = requests; + } else { + delete allRequests[this.network]; + } + + return store.set(LS_REQUESTS_KEY, allRequests); } _requestExists (api, requestId) { diff --git a/js/src/views/Application/Requests/savedRequests.spec.js b/js/src/views/Application/Requests/savedRequests.spec.js index 7563c93c8..acf3adc39 100644 --- a/js/src/views/Application/Requests/savedRequests.spec.js +++ b/js/src/views/Application/Requests/savedRequests.spec.js @@ -19,27 +19,37 @@ import store from 'store'; import SavedRequests, { LS_REQUESTS_KEY } from './savedRequests'; +const NETWORK_ID = 42; const DEFAULT_REQUEST = { requestId: '0x1', transaction: {} }; -const api = createApi(); +const api = createApi(NETWORK_ID); +const api2 = createApi(1); const savedRequests = new SavedRequests(); -function createApi () { +function createApi (networkVersion) { return { parity: { checkRequest: sinon.stub().resolves() + }, + net: { + version: sinon.stub().resolves(networkVersion) } }; } describe('views/Application/Requests/savedRequests', () => { - beforeEach(() => { + beforeEach((done) => { store.set(LS_REQUESTS_KEY, { - [DEFAULT_REQUEST.requestId]: DEFAULT_REQUEST + [NETWORK_ID]: { + [DEFAULT_REQUEST.requestId]: DEFAULT_REQUEST + } }); + + savedRequests.load(api) + .then(() => done()); }); afterEach(() => { @@ -85,4 +95,11 @@ describe('views/Application/Requests/savedRequests', () => { expect(requests[0]).to.deep.equal(DEFAULT_REQUEST); }); }); + + it('loads requests from the right network', () => { + return savedRequests.load(api2) + .then((requests) => { + expect(requests).to.deep.equal([]); + }); + }); }); From af868a7439fa50e6663d60fdff79a37a6a8b82de Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 19 Apr 2017 15:35:12 +0200 Subject: [PATCH 029/117] ensure genesis validator set in DB --- Cargo.lock | 8 +-- ethcore/src/blockchain/blockchain.rs | 8 ++- ethcore/src/client/client.rs | 100 +++++++++++++++------------ 3 files changed, 65 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20887561b..3298887d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -340,7 +340,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "elastic-array" version = "0.7.0" -source = "git+https://github.com/paritytech/elastic-array?branch=0.7.0#970a11eca8a6b3591b476155c5896b4757b5c7b8" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -713,7 +713,7 @@ version = "1.7.0" dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.7.0 (git+https://github.com/paritytech/elastic-array?branch=0.7.0)", + "elastic-array 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)", "ethcore-bigint 0.1.2", @@ -2018,7 +2018,7 @@ name = "rlp" version = "0.1.0" dependencies = [ "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.7.0 (git+https://github.com/paritytech/elastic-array?branch=0.7.0)", + "elastic-array 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bigint 0.1.2", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2790,7 +2790,7 @@ dependencies = [ "checksum docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab32ea6e284d87987066f21a9e809a73c14720571ef34516f0890b3d355ccfd8" "checksum dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" "checksum either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2b503c86dad62aaf414ecf2b8c527439abedb3f8d812537f0b12bfd6f32a91" -"checksum elastic-array 0.7.0 (git+https://github.com/paritytech/elastic-array?branch=0.7.0)" = "" +"checksum elastic-array 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71a64decd4b8cd06654a4e643c45cb558ad554abbffd82a7e16e34f45f51b605" "checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83" "checksum eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)" = "" "checksum ethabi 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63df67d0af5e3cb906b667ca1a6e00baffbed87d0d8f5f78468a1f5eb3a66345" diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 00420e58f..79169fd82 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -810,9 +810,11 @@ impl BlockChain { } }; - transitions.candidates.push(transition); - - batch.write(db::COL_EXTRA, &epoch_num, &transitions); + // ensure we don't write any duplicates. + if transitions.candidates.iter().find(|c| c.block_hash == transition.block_hash).is_none() { + transitions.candidates.push(transition); + batch.write(db::COL_EXTRA, &epoch_num, &transitions); + } } /// Add a child to a given block. Assumes that the block hash is in diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index ab9a07691..26d823dce 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -49,7 +49,7 @@ use evm::{Factory as EvmFactory, Schedule}; use executive::{Executive, Executed, TransactOptions, contract_address}; use factory::Factories; use futures::{future, Future}; -use header::BlockNumber; +use header::{BlockNumber, Header}; use io::*; use log_entry::LocalizedLogEntry; use miner::{Miner, MinerService, TransactionImportResult}; @@ -247,17 +247,29 @@ impl Client { exit_handler: Mutex::new(None), }); + // prune old states. { let state_db = client.state_db.lock().boxed_clone(); let chain = client.chain.read(); client.prune_ancient(state_db, &chain)?; } + // ensure genesis epoch proof in the DB. + { + let mut batch = DBTransaction::new(); + let chain = client.chain.read(); + client.generate_epoch_proof(&mut batch, &spec.genesis_header(), 0, &*chain); + client.db.read().write_buffered(batch); + } + if let Some(reg_addr) = client.additional_params().get("registrar").and_then(|s| Address::from_str(s).ok()) { trace!(target: "client", "Found registrar at {}", reg_addr); let registrar = Registry::new(reg_addr); *client.registrar.lock() = Some(registrar); } + + // ensure buffered changes are flushed. + client.db.read().flush().map_err(ClientError::Database)?; Ok(client) } @@ -581,7 +593,7 @@ impl Client { let entering_new_epoch = { use engines::EpochChange; match self.engine.is_epoch_end(block.header(), Some(block_data), Some(&receipts)) { - EpochChange::Yes(e, p) => Some((block.header().clone(), e, p)), + EpochChange::Yes(e, _) => Some((block.header().clone(), e)), EpochChange::No => None, EpochChange::Unsure(_) => { warn!(target: "client", "Detected invalid engine implementation."); @@ -599,48 +611,8 @@ impl Client { state.journal_under(&mut batch, number, hash).expect("DB commit failed"); let route = chain.insert_block(&mut batch, block_data, receipts); - if let Some((header, epoch, expected)) = entering_new_epoch { - use std::cell::RefCell; - use std::collections::BTreeSet; - - debug!(target: "client", "Generating validation proof for block {}", hash); - - // proof is two-part. state items read in lexicographical order, - // and the secondary "proof" part. - let read_values = RefCell::new(BTreeSet::new()); - let block_id = BlockId::Hash(hash.clone()); - let proof = { - let call = |a, d| { - let tx = self.contract_call_tx(block_id, a, d); - let (result, items) = self.prove_transaction(tx, block_id) - .ok_or_else(|| format!("Unable to make call to generate epoch proof."))?; - - read_values.borrow_mut().extend(items); - Ok(result) - }; - - self.engine.epoch_proof(&header, &call) - }; - - match proof { - Ok(proof) => { - if proof != expected { - warn!(target: "client", "Extracted epoch change proof different than expected."); - warn!(target: "client", "Using a custom engine implementation?"); - } - - // insert into database, using the generated proof. - chain.insert_epoch_transition(&mut batch, epoch, EpochTransition { - block_hash: hash.clone(), - proof: proof, - state_proof: read_values.into_inner().into_iter().collect(), - }); - } - Err(e) => { - warn!(target: "client", "Error generating epoch change proof for block {}: {}", hash, e); - warn!(target: "client", "Snapshots generated by this node will be incomplete."); - } - } + if let Some((header, epoch)) = entering_new_epoch { + self.generate_epoch_proof(&mut batch, &header, epoch, &chain); } self.tracedb.read().import(&mut batch, TraceImportRequest { @@ -665,6 +637,46 @@ impl Client { route } + // generate an epoch transition proof at the given block, and write it into the given blockchain. + fn generate_epoch_proof(&self, batch: &mut DBTransaction, header: &Header, epoch_number: u64, chain: &BlockChain) { + use std::cell::RefCell; + use std::collections::BTreeSet; + + let hash = header.hash(); + debug!(target: "client", "Generating validation proof for block {}", hash); + + // proof is two-part. state items read in lexicographical order, + // and the secondary "proof" part. + let read_values = RefCell::new(BTreeSet::new()); + let block_id = BlockId::Hash(hash.clone()); + let proof = { + let call = |a, d| { + let tx = self.contract_call_tx(block_id, a, d); + let (result, items) = self.prove_transaction(tx, block_id) + .ok_or_else(|| format!("Unable to make call to generate epoch proof."))?; + + read_values.borrow_mut().extend(items); + Ok(result) + }; + + self.engine.epoch_proof(&header, &call) + }; + + // insert into database, using the generated proof. + match proof { + Ok(proof) => + chain.insert_epoch_transition(batch, epoch_number, EpochTransition { + block_hash: hash.clone(), + proof: proof, + state_proof: read_values.into_inner().into_iter().collect(), + }), + Err(e) => { + warn!(target: "client", "Error generating epoch change proof for block {}: {}", hash, e); + warn!(target: "client", "Snapshots generated by this node will be incomplete."); + } + } + } + // prune ancient states until below the memory limit or only the minimum amount remain. fn prune_ancient(&self, mut state_db: StateDB, chain: &BlockChain) -> Result<(), ClientError> { let number = match state_db.journal_db().latest_era() { From 8310877f257c33a574b4350161d0baf3297f8296 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 19 Apr 2017 13:42:54 +0000 Subject: [PATCH 030/117] [ci skip] js-precompiled 20170419-133906 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8208f583e..dccc2f8df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1773,7 +1773,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/paritytech/js-precompiled.git#fb346e5f2925d1b2d533eb986bd2cefb962c7a88" +source = "git+https://github.com/paritytech/js-precompiled.git#7b0c9ffdfa680f7792751f03d3490fc2a9fea23f" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 4f4b33ab7..da567a458 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.53", + "version": "1.7.54", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From e1e2674cd23e5db4902eb85fcb8784e321dd2c19 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 19 Apr 2017 16:02:24 +0200 Subject: [PATCH 031/117] Show ETH value (even 0) if ETH transfert in transaction list (#5406) --- js/src/ui/TxList/TxRow/txRow.js | 37 +++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/js/src/ui/TxList/TxRow/txRow.js b/js/src/ui/TxList/TxRow/txRow.js index 8d123c13c..1fcf54396 100644 --- a/js/src/ui/TxList/TxRow/txRow.js +++ b/js/src/ui/TxList/TxRow/txRow.js @@ -21,9 +21,10 @@ import { Link } from 'react-router'; import { txLink } from '~/3rdparty/etherscan/links'; -import IdentityIcon from '../../IdentityIcon'; -import IdentityName from '../../IdentityName'; -import MethodDecoding from '../../MethodDecoding'; +import IdentityIcon from '~/ui/IdentityIcon'; +import IdentityName from '~/ui/IdentityName'; +import MethodDecoding from '~/ui/MethodDecoding'; +import MethodDecodingStore from '~/ui/MethodDecoding/methodDecodingStore'; import styles from '../txList.css'; @@ -48,6 +49,29 @@ class TxRow extends Component { historic: true }; + state = { + isContract: false, + isDeploy: false + }; + + methodDecodingStore = MethodDecodingStore.get(this.context.api); + + componentWillMount () { + const { address, tx } = this.props; + + this + .methodDecodingStore + .lookup(address, tx) + .then((lookup) => { + const newState = { + isContract: lookup.contract, + isDeploy: lookup.deploy + }; + + this.setState(newState); + }); + } + render () { const { address, className, historic, netVersion, tx } = this.props; @@ -117,9 +141,14 @@ class TxRow extends Component { renderEtherValue (_value) { const { api } = this.context; + const { isContract, isDeploy } = this.state; + + // Always show the value if ETH transfer, ie. not + // a contract or a deployment + const fullValue = !(isContract || isDeploy); const value = api.util.fromWei(_value); - if (value.eq(0)) { + if (value.eq(0) && !fullValue) { return
{ ' ' }
; } From 23e7b53c138a564e2ad7fd22432b63c34d05895c Mon Sep 17 00:00:00 2001 From: Bas Date: Wed, 19 Apr 2017 16:08:47 +0200 Subject: [PATCH 032/117] nl i18n updated (#5461) * Sync nl i18n to default * Syntax fix in nl i18n Tested locally * nl translation improvements --- js/src/i18n/nl/account.js | 6 +- js/src/i18n/nl/accounts.js | 7 ++- js/src/i18n/nl/addAddress.js | 3 +- js/src/i18n/nl/address.js | 28 +++++++++ js/src/i18n/nl/addresses.js | 25 ++++++++ js/src/i18n/nl/application.js | 3 + js/src/i18n/nl/contract.js | 24 ++++++- js/src/i18n/nl/contracts.js | 28 +++++++++ js/src/i18n/nl/createAccount.js | 28 +++++---- js/src/i18n/nl/createWallet.js | 5 +- js/src/i18n/nl/dapps.js | 3 +- js/src/i18n/nl/deployContract.js | 9 +++ js/src/i18n/nl/faucet.js | 28 +++++++++ js/src/i18n/nl/firstRun.js | 21 ++++++- js/src/i18n/nl/index.js | 22 ++++++- js/src/i18n/nl/passwordChange.js | 1 + js/src/i18n/nl/saveContract.js | 27 ++++++++ js/src/i18n/nl/settings.js | 20 ++++-- js/src/i18n/nl/signer.js | 103 +++++++++++++++++++++++++++++++ js/src/i18n/nl/status.js | 66 ++++++++++++++++++++ js/src/i18n/nl/transfer.js | 36 ++++++++++- js/src/i18n/nl/ui.js | 97 ++++++++++++++++++++++++++--- js/src/i18n/nl/upgradeParity.js | 9 ++- js/src/i18n/nl/vaults.js | 37 ++++++++++- js/src/i18n/nl/verification.js | 85 +++++++++++++++++++++++++ js/src/i18n/nl/wallet.js | 45 ++++++++++++++ js/src/i18n/nl/walletSettings.js | 26 +++++--- js/src/i18n/nl/writeContract.js | 62 +++++++++++++++++++ 28 files changed, 804 insertions(+), 50 deletions(-) create mode 100644 js/src/i18n/nl/address.js create mode 100644 js/src/i18n/nl/addresses.js create mode 100644 js/src/i18n/nl/contracts.js create mode 100644 js/src/i18n/nl/faucet.js create mode 100644 js/src/i18n/nl/saveContract.js create mode 100644 js/src/i18n/nl/signer.js create mode 100644 js/src/i18n/nl/status.js create mode 100644 js/src/i18n/nl/verification.js create mode 100644 js/src/i18n/nl/wallet.js create mode 100644 js/src/i18n/nl/writeContract.js diff --git a/js/src/i18n/nl/account.js b/js/src/i18n/nl/account.js index 57701292a..ee211bbca 100755 --- a/js/src/i18n/nl/account.js +++ b/js/src/i18n/nl/account.js @@ -16,13 +16,17 @@ export default { button: { - delete: `verwijder account`, + delete: `verwijder`, edit: `bewerk`, + faucet: `Kovan ETH`, password: `wachtwoord`, shapeshift: `shapeshift`, transfer: `verzend`, verify: `verifieer` }, + hardware: { + confirmDelete: `Weet je zeker dat je de volgende hardware adressen van je account lijst wilt verwijderen?` + }, header: { outgoingTransactions: `{count} uitgaande transacties`, uuid: `uuid: {uuid}` diff --git a/js/src/i18n/nl/accounts.js b/js/src/i18n/nl/accounts.js index 315324632..e213bddb2 100755 --- a/js/src/i18n/nl/accounts.js +++ b/js/src/i18n/nl/accounts.js @@ -16,8 +16,8 @@ export default { button: { - newAccount: `nieuw account`, - newWallet: `nieuw wallet`, + newAccount: `account`, + newWallet: `wallet`, vaults: `kluizen` }, summary: { @@ -27,5 +27,8 @@ export default { tooltip: { actions: `voor de huidige weergave zijn koppelingen beschikbaar op de werkbalk voor snelle toegang: het uitvoeren van acties of het creëren van een nieuw item`, overview: `hier vind je een overzichtelijke weergave van je accounts, waarin je meta informatie kunt bewerken en transacties kunt uitvoeren en bekijken` + }, + tooltips: { + owner: `{name} (eigenaar)` } }; diff --git a/js/src/i18n/nl/addAddress.js b/js/src/i18n/nl/addAddress.js index 1b2281888..e048988e9 100755 --- a/js/src/i18n/nl/addAddress.js +++ b/js/src/i18n/nl/addAddress.js @@ -19,6 +19,7 @@ export default { add: `Adres Opslaan`, close: `Annuleer` }, + header: `Om een nieuwe invoer aan je adresboek toe te voegen, heb je het netwerk adres van het account nodig en kun je optioneel een beschrijving toevoegen. Zodra de nieuwe invoer is toegevoegd, zal het in je adresboek verschijnen.`, input: { address: { hint: `het netwerk adres van het item`, @@ -33,5 +34,5 @@ export default { label: `Adres Naam` } }, - label: `voeg opgeslagen adres toe` + label: `voeg een opgeslagen adres toe` }; diff --git a/js/src/i18n/nl/address.js b/js/src/i18n/nl/address.js new file mode 100644 index 000000000..fc9d2eab1 --- /dev/null +++ b/js/src/i18n/nl/address.js @@ -0,0 +1,28 @@ +// Copyright 2015-2017 Parity Technologies (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 { + buttons: { + edit: `bewerken`, + forget: `vergeten`, + save: `opslaan` + }, + delete: { + confirmInfo: `Weet je zeker dat je het volgende adres uit je adresboek wilt verwijderen?`, + title: `bevestig verwijderen` + }, + title: `Adres Informatie` +}; diff --git a/js/src/i18n/nl/addresses.js b/js/src/i18n/nl/addresses.js new file mode 100644 index 000000000..c16c5f234 --- /dev/null +++ b/js/src/i18n/nl/addresses.js @@ -0,0 +1,25 @@ +// Copyright 2015-2017 Parity Technologies (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 { + buttons: { + add: `adres` + }, + errors: { + invalidFile: `Het opgegeven bestand is ongeldig...` + }, + title: `Opgeslagen Adressen` +}; diff --git a/js/src/i18n/nl/application.js b/js/src/i18n/nl/application.js index 1a6b2ee5c..bcb110b88 100755 --- a/js/src/i18n/nl/application.js +++ b/js/src/i18n/nl/application.js @@ -15,6 +15,9 @@ // along with Parity. If not, see . export default { + frame: { + error: `FOUT: Deze applicatie kan niet en zou niet geladen moeten worden in een embedded iFrame` + }, status: { consensus: { capable: `Capable`, diff --git a/js/src/i18n/nl/contract.js b/js/src/i18n/nl/contract.js index 5f2ca39cc..9818f5edd 100755 --- a/js/src/i18n/nl/contract.js +++ b/js/src/i18n/nl/contract.js @@ -15,5 +15,27 @@ // along with Parity. If not, see . export default { - minedBlock: `Opgenomen in blok #{blockNumber}` + buttons: { + close: `Sluit`, + details: `details`, + edit: `bewerken`, + execute: `uitvoeren`, + forget: `vergeten` + }, + details: { + title: `contract details` + }, + events: { + eventPending: `pending`, + noEvents: `Er zijn vanuit dit contract nog geen events verzonden.`, + title: `events` + }, + minedBlock: `Opgenomen in blok #{blockNumber}`, + queries: { + buttons: { + query: `Query` + }, + title: `queries` + }, + title: `Contract Informatie` }; diff --git a/js/src/i18n/nl/contracts.js b/js/src/i18n/nl/contracts.js new file mode 100644 index 000000000..18ab842a0 --- /dev/null +++ b/js/src/i18n/nl/contracts.js @@ -0,0 +1,28 @@ +// Copyright 2015-2017 Parity Technologies (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 { + buttons: { + deploy: `publiceer`, + develop: `ontwikkel`, + watch: `bekijk` + }, + sortOrder: { + date: `datum`, + minedBlock: `opgenomen blok` + }, + title: `Contracten` +}; diff --git a/js/src/i18n/nl/createAccount.js b/js/src/i18n/nl/createAccount.js index 69585df35..98b7ac26b 100755 --- a/js/src/i18n/nl/createAccount.js +++ b/js/src/i18n/nl/createAccount.js @@ -20,10 +20,6 @@ export default { hint: `Het netwerk adres van het account`, label: `adres` }, - name: { - hint: `Een beschrijvende naam van het account`, - label: `account naam` - }, phrase: { hint: `De account herstel zin`, label: `Eigenaar's herstel zin (houd deze woorden veilig en prive want hiermee kun je volledige, ongelimiteerde toegang tot het account verkrijgen).` @@ -35,31 +31,38 @@ export default { button: { back: `Terug`, cancel: `Annuleer`, - close: `Sluit`, create: `Aanmaken`, + done: `Klaar`, import: `Importeer`, next: `Volgende`, print: `Herstel zin afdrukken` }, creationType: { fromGeth: { - label: `Importeer accounts uit Geth keystore` + description: `Importeer accounts uit Geth keystore met het originele wachtwoord`, + label: `Geth keystore` }, fromJSON: { - label: `Importeer account uit een opgeslagen JSON file` + description: `Importeer account uit een JSON sleutelbestand met het originele wachtwoord`, + label: `JSON bestand` }, fromNew: { - label: `Handmatig account aanmaken` + description: `Selecteer je identiteits-icoon en kies je wachtwoord`, + label: `Nieuw Account` }, fromPhrase: { - label: `Herstel account met een herstel zin` + description: `Herstel je account met een eerder bewaarde herstel zin en een nieuw wachtwoord`, + label: `Herstel zin` }, fromPresale: { - label: `Importeer account van een Ethereum voor-verkoop (pre-sale) wallet` + description: `Importeer een Ethereum voor-verkoop (pre-sale) wallet bestand met het originele wachtwoord`, + label: `voor-verkoop wallet` }, fromRaw: { - label: `Importeer een prive sleutel (raw private key)` - } + description: `Importeer een eerder gemaakte prive sleutel (raw private key) met een nieuw wachtwoord`, + label: `Prive sleutel` + }, + info: `Selecteer de manier waarop je je account wilt aanmaken of importeren. Maak een nieuw account aan met een naam en wachtwoord, of importeer/herstel een bestaand account vanuit verschillende bronnen zoals een herstel zin of een sleutelbestand. Met behulp van deze wizard word je door het proces begeleid om een account aan te maken.` }, newAccount: { hint: { @@ -80,6 +83,7 @@ export default { } }, newGeth: { + available: `Er zijn momenteel {count} importeerbare sleutels (keys) beschikbaar vanuit Geth keystore, welke nog niet in je Parity installatie beschikbaar zijn. Selecteer de accounts die je wilt importeren en ga verder naar de volgende stap om het importeren te voltooien.`, noKeys: `Er zijn momenteel geen importeerbare sleutels (keys) beschikbaar in de Geth keystore; of ze zijn al in je Parity installatie beschikbaar` }, newImport: { diff --git a/js/src/i18n/nl/createWallet.js b/js/src/i18n/nl/createWallet.js index 6acd2e2c8..9f3419497 100644 --- a/js/src/i18n/nl/createWallet.js +++ b/js/src/i18n/nl/createWallet.js @@ -53,7 +53,7 @@ export default { label: `wallet naam` }, ownerMulti: { - hint: `het eigenaars account van dit contract`, + hint: `het account wat eigenaar is van dit contract`, label: `van account (contract eigenaar)` }, ownersMulti: { @@ -80,6 +80,7 @@ export default { }, states: { completed: `Het contract is succesvol aangemaakt`, + confirmationNeeded: `Voor het aanmaken van dit contract is bevestiging door andere eigenaren van het Wallet vereist`, preparing: `Transactie aan het voorbereiden voor verzending op het netwerk`, validatingCode: `De contract code van het aangemaakte contract wordt gevalideerd`, waitingConfirm: `Wachten tot de transactie bevestigd is in de Parity Secure Signer`, @@ -93,7 +94,7 @@ export default { }, type: { multisig: { - description: `Creëer/Maak een {link} Wallet aan`, + description: `Maak een {link} Wallet aan`, label: `Multi-Sig wallet`, link: `standaard multi-signature` }, diff --git a/js/src/i18n/nl/dapps.js b/js/src/i18n/nl/dapps.js index 3c7cf6d7b..6949c4d48 100644 --- a/js/src/i18n/nl/dapps.js +++ b/js/src/i18n/nl/dapps.js @@ -36,11 +36,10 @@ export default { }, external: { accept: `Ik begrijp dat deze toepassingen niet bij Parity zijn aangesloten`, - warning: `Deze applicaties gepuliceerd door derde partijen zijn niet bij Parity aangesloten, noch worden ze gepubliceerd door Parity. Alle applicaties blijven in beheer van hun eigen auteur. Zorg ervoor dat je snapt wat het doel van een applicatie is voordat je ermee aan de slag gaat.` + warning: `Deze applicaties zijn gepuliceerd door derde partijen welke niet verwant zijn aan Parity en zijn dus ook niet door Parity uitgebracht. Alle applicaties blijven in beheer van hun eigen auteur. Zorg ervoor dat je snapt wat het doel van een applicatie is voordat je ermee aan de slag gaat.` }, label: `Gedecentraliseerde Applicaties`, permissions: { - description: `{activeIcon} account is beschikbaar voor applicaties, {defaultIcon} account is het standaard account`, label: `zichtbare dapp accounts` } }; diff --git a/js/src/i18n/nl/deployContract.js b/js/src/i18n/nl/deployContract.js index 478992464..042d52276 100644 --- a/js/src/i18n/nl/deployContract.js +++ b/js/src/i18n/nl/deployContract.js @@ -37,6 +37,13 @@ export default { hint: `het account wat eigenaar is van dit contract`, label: `van account (contract eigenaar)` }, + advanced: { + label: `geavanceerde verzend opties` + }, + amount: { + hint: `de naar het contract te verzenden hoeveelheid`, + label: `te verzenden hoeveelheid (in {tag})` + }, code: { hint: `de gecompileerde code van het aan te maken contract`, label: `code` @@ -65,6 +72,7 @@ export default { }, state: { completed: `Het contract is succesvol aangemaakt`, + confirmationNeeded: `Deze actie vereist de bevestiging van de andere eigenaren van het contract`, preparing: `Transactie aan het voorbereiden om te verzenden op het netwerk`, validatingCode: `De contract code van het aangemaakte contract valideren`, waitReceipt: `Wachten tot het aanmaken van het contract bevestigd is`, @@ -74,6 +82,7 @@ export default { completed: `voltooid`, deployment: `aangemaakt`, details: `contract details`, + extras: `extra informatie`, failed: `aanmaken mislukt`, parameters: `contract parameters`, rejected: `afgewezen` diff --git a/js/src/i18n/nl/faucet.js b/js/src/i18n/nl/faucet.js new file mode 100644 index 000000000..f35acc0b7 --- /dev/null +++ b/js/src/i18n/nl/faucet.js @@ -0,0 +1,28 @@ +// Copyright 2015-2017 Parity Technologies (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 { + buttons: { + close: `sluit`, + done: `klaar`, + request: `verzoek` + }, + summary: { + done: `Jouw Kovan ETH is aangevraagd bij het faucet, wat reageerde met het volgende bericht -`, + info: `Om een hoeveelheid Kovan ETH aan te vragen voor dit adres, moet je ervoor zorgen dat het adres op het mainnet sms-geverifieerd is. Zodra je dit uitvoert, zal het faucet Kovan ETH naar je huidige account verzenden.` + }, + title: `Kovan ETH Faucet` +}; diff --git a/js/src/i18n/nl/firstRun.js b/js/src/i18n/nl/firstRun.js index d182c76e1..9c7c4cf5d 100755 --- a/js/src/i18n/nl/firstRun.js +++ b/js/src/i18n/nl/firstRun.js @@ -17,16 +17,33 @@ export default { button: { close: `Sluiten`, - create: `creëer`, - next: `volgende`, + create: `Creëer`, + next: `Volgende`, print: `Woorden Afdrukken`, skip: `Overslaan` }, + completed: { + congrats: `Gefeliciteerd! Je node configuratie is succesvol afgerond en het programma is klaar voor gebruikt.`, + next: `Om je snel aan de slag te laten gaan krijg je in de volgende stap een korte algemene inleiding in het gebruik van het programma en lopen we door de beschikbare funcies.` + }, title: { completed: `voltooid`, newAccount: `nieuw account`, recovery: `herstelzin`, terms: `voorwaarden`, welcome: `welkom` + }, + tnc: { + accept: `Ik accepteer de voorwaarden en condities` + }, + welcome: { + description: `Als onderdeel van een nieuwe installatie, begeleiden we je in de enkele hierna volgende stappen met het configureren van je Parity node en jouw bijbehorende accounts. Ons doel hiervan is om het je zo gemakkelijk mogelijk te maken and zodat je binnen de kortste tijd klaar bent voor gebruik, dus heb even geduld en blijf bij ons. Zodra je de wizard voltooid hebt, heb je -`, + greeting: `Welkom bij Parity, de snelste en makkelijkste manier om je eigen node te draaien.`, + next: `Klik volgende om door te gaan.`, + step: { + account: `Je eerste Parity account aangemaakt;`, + privacy: `Onze privacybeleid en bedrijfsvoorwaarden begrepen;`, + recovery: `De mogelijkheid om je account te herstellen.` + } } }; diff --git a/js/src/i18n/nl/index.js b/js/src/i18n/nl/index.js index 281e9974c..0d11be8f1 100755 --- a/js/src/i18n/nl/index.js +++ b/js/src/i18n/nl/index.js @@ -18,10 +18,13 @@ import account from './account'; import accounts from './accounts'; import addAddress from './addAddress'; import addContract from './addContract'; +import address from './address'; +import addresses from './addresses'; import addressSelect from './addressSelect'; import application from './application'; import connection from './connection'; import contract from './contract'; +import contracts from './contracts'; import createAccount from './createAccount'; import createWallet from './createWallet'; import dapp from './dapp'; @@ -32,31 +35,41 @@ import editMeta from './editMeta'; import errors from './errors'; import executeContract from './executeContract'; import extension from './extension'; +import faucet from './faucet'; import firstRun from './firstRun'; import home from './home'; import loadContract from './loadContract'; import parityBar from './parityBar'; import passwordChange from './passwordChange'; +import saveContract from './saveContract'; import settings from './settings'; import shapeshift from './shapeshift'; +import signer from './signer'; +import status from './status'; import tabBar from './tabBar'; import transfer from './transfer'; import txEditor from './txEditor'; import ui from './ui'; import upgradeParity from './upgradeParity'; import vaults from './vaults'; +import verification from './verification'; +import wallet from './wallet'; import walletSettings from './walletSettings'; import web from './web'; +import writeContract from './writeContract'; export default { account, accounts, addAddress, addContract, + address, + addresses, addressSelect, application, connection, contract, + contracts, createAccount, createWallet, dapp, @@ -67,19 +80,26 @@ export default { errors, executeContract, extension, + faucet, firstRun, home, loadContract, parityBar, passwordChange, + saveContract, settings, shapeshift, + signer, + status, tabBar, transfer, txEditor, ui, upgradeParity, vaults, + verification, + wallet, walletSettings, - web + web, + writeContract }; diff --git a/js/src/i18n/nl/passwordChange.js b/js/src/i18n/nl/passwordChange.js index c59e50eed..eca3fd919 100644 --- a/js/src/i18n/nl/passwordChange.js +++ b/js/src/i18n/nl/passwordChange.js @@ -30,6 +30,7 @@ export default { label: `nieuw wachtwoord` }, passwordHint: { + display: `Hint {hint}`, hint: `hint voor het nieuwe wachtwoord`, label: `(optioneel) nieuwe wachtwoord hint` }, diff --git a/js/src/i18n/nl/saveContract.js b/js/src/i18n/nl/saveContract.js new file mode 100644 index 000000000..e51a3be3d --- /dev/null +++ b/js/src/i18n/nl/saveContract.js @@ -0,0 +1,27 @@ +// Copyright 2015-2017 Parity Technologies (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 { + buttons: { + cancel: `Annuleer`, + save: `Opslaan` + }, + name: { + hint: `kies een naam voor dit contract`, + label: `contract naam` + }, + title: `contract opslaan` +}; diff --git a/js/src/i18n/nl/settings.js b/js/src/i18n/nl/settings.js index 81f485696..f6705df78 100644 --- a/js/src/i18n/nl/settings.js +++ b/js/src/i18n/nl/settings.js @@ -22,6 +22,18 @@ export default { label: `achtergrond` }, parity: { + chains: { + chain_classic: `Parity synchroniseert met het Ethereum Classic netwerk`, + chain_dev: `Parity gebruikt een lokale ontwikkelaars chain`, + chain_expanse: `Parity synchroniseert met het Expanse netwerk`, + chain_foundation: `Parity synchroniseert met het Ethereum netwerk wat door de Ethereum Foundation is uitgebracht`, + chain_kovan: `Parity synchroniseert met het Kovan test netwerk`, + chain_olympic: `Parity synchroniseert met het Olympic test netwerk`, + chain_ropsten: `Parity synchroniseert met het Ropsten test netwerk`, + cmorden_kovan: `Parity synchroniseert met het Morden (Classic) test netwerk`, + hint: `de chain waarmee de Parity node synchroniseert`, + label: `te synchroniseren chain/netwerk` + }, languages: { hint: `de taal waarin deze interface wordt weergegeven`, label: `Weergave taal` @@ -35,7 +47,7 @@ export default { mode_offline: `Parity synchroniseert niet`, mode_passive: `Parity synchroniseert in het begin. Daarna slaapt Parity en wordt regelmatig wakker voor synchronisatie` }, - overview_0: `Pas de Parity node instellingen aan en kies de synchronisatie modus in dit menu.`, + overview_0: `Pas de Parity node instellingen aan en kies de manier van synchroniseren in dit menu.`, label: `parity` }, proxy: { @@ -53,7 +65,7 @@ export default { label: `Accounts` }, addresses: { - description: `Een overzicht van alle contacten en adresboek items die door deze Parity installatie worden beheerd. Monitor en volg accounts waarbij je transactie details met slechts een muisklik kunt weergeven.`, + description: `Een overzicht van alle door deze Parity installatie beheerde contacten en adresboek items. Monitor en volg accounts waarbij je transactie details met slechts een muisklik kunt weergeven.`, label: `Adresboek` }, apps: { @@ -64,10 +76,10 @@ export default { description: `Monitor, volg en maak gebruik van specifieke contracten die op het netwerk zijn gezet. Dit is een meer technisch gerichte omgeving, voornamelijk bedoeld voor geavanceerde gebruikers die de werking van bepaalde contracten goed begrijpen.`, label: `Contracten` }, - overview_0: `Beheer de beschikbare weergaven van deze interface en selecteer enkel de delen van de applicatie die voor jou van belang zijn.`, + overview_0: `Beheer de beschikbare weergaven van deze interface, en selecteer enkel de delen van de applicatie die voor jou van belang zijn.`, overview_1: `Ben je een eind gebruiker? De standaard instellingen zijn geschikt voor zowel beginners als gevorderde gebruikers.`, overview_2: `Ben je een ontwikkelaar? Voeg enkele functies toe om je contracten te beheren en gebruik te maken van gedecentraliseerde applicaties.`, - overview_3: `Ben je een miner of run je een grootschalige node? Voeg enkele functies toe om je alle informatie te geven die je nodig hebt om je node te monitoren.`, + overview_3: `Ben je een miner of draai je een grootschalige node? Voeg enkele functies toe om je alle informatie te geven die je nodig hebt om je node te monitoren.`, settings: { description: `Deze weergave. Hiermee kun je Parity aan passen in termen van opties, bediening en look en feel.`, label: `Instellingen` diff --git a/js/src/i18n/nl/signer.js b/js/src/i18n/nl/signer.js new file mode 100644 index 000000000..ec7ee96f8 --- /dev/null +++ b/js/src/i18n/nl/signer.js @@ -0,0 +1,103 @@ +// Copyright 2015-2017 Parity Technologies (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 { + embedded: { + noPending: `Er zijn momenteel geen lopende verzoeken die op je goedkeuring wachten` + }, + mainDetails: { + editTx: `Bewerk condities/gas/gasprijs`, + tooltips: { + total1: `De waarde van de transactie inclusief de miningskosten is {total} {type}.`, + total2: `(Dit is inclusief een miners vergoeding van {fee} {token})`, + value1: `De waarde van de transactie.` + } + }, + requestOrigin: { + dapp: `door een dapp op {url}`, + ipc: `via IPC sessie`, + rpc: `via RPC {rpc}`, + signerCurrent: `via huidige tab`, + signerUI: `via UI sessie`, + unknownInterface: `via onbekende interface`, + unknownRpc: `niet geïdentificeerd`, + unknownUrl: `onbekende URL` + }, + requestsPage: { + noPending: `Er zijn geen verzoeken die je goedkeuring vereisen.`, + pendingTitle: `Openstaande Verzoeken`, + queueTitle: `Lokale Transacties` + }, + sending: { + hardware: { + confirm: `Bevestig de transactie op je aangesloten hardware wallet`, + connect: `Sluit je hardware wallet aan voordat je de transactie bevestigd` + } + }, + signRequest: { + request: `Een verzoek om data te ondertekenen met jouw account:`, + state: { + confirmed: `Bevestigd`, + rejected: `Afgewezen` + }, + unknownBinary: `(Onbekende binary data)`, + warning: `WAARSCHUWING: Deze gevolgen hiervan kunnen ernstig zijn. Bevestig het verzoek alleen als je het zeker weet.` + }, + title: `Trusted Signer`, + txPending: { + buttons: { + viewToggle: `bekijk transactie` + } + }, + txPendingConfirm: { + buttons: { + confirmBusy: `Bevestigen...`, + confirmRequest: `Bevestig Verzoek` + }, + errors: { + invalidWallet: `Opgegeven wallet bestand is ongeldig.` + }, + password: { + decrypt: { + hint: `open (decrypt) de sleutel met je wachtwoord`, + label: `Sleutel Wachtwoord` + }, + unlock: { + hint: `ontgrendel het account`, + label: `Account Wachtwoord` + } + }, + passwordHint: `(hint) {passwordHint}`, + selectKey: { + hint: `De sleutelbestand (keyfile) die je voor dit account wilt gebruiken`, + label: `Selecteer Lokale Sleutel (key)` + }, + tooltips: { + password: `Geef een wachtwoord voor dit account` + } + }, + txPendingForm: { + changedMind: `Ik heb me bedacht`, + reject: `wijs verzoek af` + }, + txPendingReject: { + buttons: { + reject: `Wijs Verzoek Af` + }, + info: `Weet je zeker dat je dit verzoek wilt afwijzen?`, + undone: `Dit kan niet ongedaan gemaakt worden` + } +}; diff --git a/js/src/i18n/nl/status.js b/js/src/i18n/nl/status.js new file mode 100644 index 000000000..9c0294ab0 --- /dev/null +++ b/js/src/i18n/nl/status.js @@ -0,0 +1,66 @@ +// Copyright 2015-2017 Parity Technologies (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 { + debug: { + reverse: `Omgekeerde volgorde`, + stopped: `De live weergave van de Parity logboeken is momenteel gestopt via de UI, start de live weergave om de laatste updates te zien.`, + title: `Node Logboeken` + }, + miningSettings: { + input: { + author: { + hint: `de mining auteur`, + label: `auteur` + }, + extradata: { + hint: `extra data voor mined blokken`, + label: `extra data` + }, + gasFloor: { + hint: `het gas-floor doel voor mining`, + label: `gas-floor doel` + }, + gasPrice: { + hint: `de minimale gas prijs voor mining`, + label: `minimale gas prijs` + } + }, + title: `mining instellingen` + }, + status: { + hashrate: `{hashrate} H/s`, + input: { + chain: `chain`, + enode: `enode`, + no: `nee`, + peers: `peers`, + port: `netwerk poort`, + rpcEnabled: `rpc ingeschakeld`, + rpcInterface: `rpc interface`, + rpcPort: `rpc poort`, + yes: `ja` + }, + title: { + bestBlock: `beste block`, + hashRate: `hash rate`, + network: `netwerk instellingen`, + node: `Node`, + peers: `peers` + } + }, + title: `Status` +}; diff --git a/js/src/i18n/nl/transfer.js b/js/src/i18n/nl/transfer.js index 1370711b4..29866470b 100644 --- a/js/src/i18n/nl/transfer.js +++ b/js/src/i18n/nl/transfer.js @@ -21,7 +21,41 @@ export default { label: `transactie data` } }, + buttons: { + back: `Terug`, + cancel: `Annuleer`, + close: `Sluit`, + next: `Volgende`, + send: `Verzend` + }, + details: { + advanced: { + label: `geavanceerde verzend opties` + }, + amount: { + hint: `de naar de ontvanger te verzenden hoeveelheid`, + label: `te verzenden hoeveelheid (in {tag})` + }, + fullBalance: { + label: `volledige account balans` + }, + recipient: { + hint: `het ontvangende adres`, + label: `ontvanger adres` + }, + sender: { + hint: `het verzendende adres`, + label: `Verzender adres` + }, + total: { + label: `totale transactie hoeveelheid` + } + }, + wallet: { + confirmation: `Deze transactie vereist bevestiging van andere eigenaren.`, + operationHash: `hash van deze bewerking` + }, warning: { - wallet_spent_limit: `Deze transactie waarde is boven de toegestane dag limiet en zal moeten worden bevestigd door andere eigenaren.` + wallet_spent_limit: `De waarde van deze transactie is hoger dan de toegestane dag limiet en zal moeten worden bevestigd door andere eigenaren.` } }; diff --git a/js/src/i18n/nl/ui.js b/js/src/i18n/nl/ui.js index c0454e9f6..1b7a9163b 100644 --- a/js/src/i18n/nl/ui.js +++ b/js/src/i18n/nl/ui.js @@ -15,8 +15,40 @@ // along with Parity. If not, see . export default { + actionbar: { + export: { + button: { + export: `exporteer` + } + }, + import: { + button: { + cancel: `Annuleer`, + confirm: `Bevestig`, + import: `importeer` + }, + confirm: `Bevestig dat dit is wat je wilt importeren.`, + error: `Er is een fout opgetreden: {errorText}`, + step: { + error: `fout`, + select: `selecteer een bestand`, + validate: `valideer` + }, + title: `Importeer vanuit een bestand` + }, + search: { + hint: `Voer zoekopdracht in...` + }, + sort: { + sortBy: `Sorteer op {label}`, + typeDefault: `Standaard`, + typeEth: `Sorteer op ETH`, + typeName: `Sorteer op naam`, + typeTags: `Sorteer op tags` + } + }, balance: { - none: `Er zijn geen tegoeden gekoppeld aan dit account` + none: `Geen tegoeden gekoppeld aan dit account` }, blockStatus: { bestBlock: `{blockNumber} beste blok`, @@ -28,10 +60,61 @@ export default { no: `nee`, yes: `ja` }, + copyToClipboard: { + copied: `{data} is naar het klembord gekopierd` + }, + errors: { + close: `sluit` + }, + fileSelect: { + defaultLabel: `Sleep hier een bestand naartoe, of klik om een bestand te selecteren voor uploaden` + }, + gasPriceSelector: { + customTooltip: { + transactions: `{number} {number, plural, one {transaction} other {transactions}} met een ingestelde gasprijs tussen de {minPrice} en {maxPrice}` + } + }, identityName: { null: `NUL`, unnamed: `NAAMLOOS` }, + methodDecoding: { + condition: { + block: `, {historic, select, true {Submitted} false {Submission}} in blok {blockNumber}`, + time: `, {historic, select, true {Submitted} false {Submission}} op {timestamp}` + }, + deploy: { + address: `Een contract aangemaakt op adres`, + params: `met de volgende parameters:`, + willDeploy: `Zal een contract aanmaken`, + withValue: `, verzenden van {value}` + }, + gasUsed: `({gas} gas gebruikt)`, + gasValues: `{gas} gas ({gasPrice}M/{tag})`, + input: { + data: `data`, + input: `input`, + withInput: `met de {inputDesc} {inputValue}` + }, + receive: { + contract: `het contract`, + info: `{historic, select, true {Received} false {Will receive}} {valueEth} van {aContract}{address}` + }, + signature: { + info: `{historic, select, true {Executed} false {Will execute}} the {method} function on the contract {address} transferring {ethValue}{inputLength, plural, zero {,} other {passing the following {inputLength, plural, one {parameter} other {parameters}}}}` + }, + token: { + transfer: `{historic, select, true {Transferred} false {Will transfer}} {value} naar {address}` + }, + transfer: { + contract: `het contract`, + info: `{historic, select, true {Transferred} false {Will transfer}} {valueEth} naar {aContract}{address}` + }, + txValues: `{historic, select, true {Provided} false {Provides}} {gasProvided}{gasUsed} voor een totale transactie waarde van {totalEthValue}`, + unknown: { + info: `{historic, select, true {Executed} false {Will execute}} the {method} on the contract {address} transferring {ethValue}.` + } + }, passwordStrength: { label: `wachtwoord sterkte` }, @@ -48,6 +131,10 @@ export default { posted: `De transactie is op het netwerk geplaatst met hash {hashLink}`, waiting: `wachten op bevestigingen` }, + vaultSelect: { + hint: `de kluis waaraan dit account gekoppeld is`, + label: `gekoppelde kluis` + }, verification: { gatherData: { accountHasRequested: { @@ -60,10 +147,6 @@ export default { pending: `Aan het controleren of je account is geverifieerd…`, true: `Je account is al geverifieerd.` }, - email: { - hint: `de code zal naar dit adres worden verzonden`, - label: `e-mail adres` - }, fee: `De extra vergoeding is {amount} ETH.`, isAbleToRequest: { pending: `Valideren van je invoer…` @@ -74,10 +157,6 @@ export default { true: `De verificatie server is actief.` }, nofee: `Er zijn geen extra kosten.`, - phoneNumber: { - hint: `De SMS zal naar dit nummer worden verstuurd`, - label: `telefoonnummer in internationaal formaat` - }, termsOfService: `Ik ga akkoord met de voorwaarden en condities hieronder.` } } diff --git a/js/src/i18n/nl/upgradeParity.js b/js/src/i18n/nl/upgradeParity.js index c057c9e7d..732a7b267 100644 --- a/js/src/i18n/nl/upgradeParity.js +++ b/js/src/i18n/nl/upgradeParity.js @@ -15,13 +15,13 @@ // along with Parity. If not, see . export default { - busy: `Parity wordt momenteel bijgewerkt naar versie {newversion}`, + busy: `Parity wordt momenteel bijgewerkt naar versie {newversion}. Wacht tot het proces is voltooid.`, button: { close: `sluiten`, done: `klaar`, upgrade: `werk nu bij` }, - completed: `Het bijwerken naar Parity {newversion} is succesvol voltooid.`, + completed: `Het bijwerken naar Parity {newversion} is succesvol voltooid. Klik op "klaar" om het programma automatisch opnieuw op te starten.`, consensus: { capable: `Je huidige versie van Parity voldoet aan de netwerk vereisten.`, capableUntil: `Je huidige versie van Parity voldoet aan de netwerk vereisten tot aan blok {blockNumber}`, @@ -30,7 +30,10 @@ export default { }, failed: `Het bijwerken naar Parity {newversion} gaf een fout en is mislukt.`, info: { - upgrade: `Een nieuwe versie van Parity, version {newversion} is beschikbaar als upgrade vanaf je huidige versie {currentversion}` + currentVersion: `Je huidige versie is {currentversion}`, + next: `Klik op "werk nu bij" om het bijwerken van je Parity te starten.`, + upgrade: `Een nieuwe versie {newversion} is beschikbaar`, + welcome: `Welkom bij de Parity upgrade wizard, deze stelt je in staat om Parity op zeer eenvoudige wijze bij te werken naar de nieuwste versie.` }, step: { completed: `bijwerken voltooid`, diff --git a/js/src/i18n/nl/vaults.js b/js/src/i18n/nl/vaults.js index b16c6807e..3659fb7d7 100644 --- a/js/src/i18n/nl/vaults.js +++ b/js/src/i18n/nl/vaults.js @@ -26,8 +26,9 @@ export default { button: { accounts: `accounts`, add: `Maak kluis`, - close: `sluit kluis`, - open: `open kluis` + close: `sluit`, + edit: `bewerk`, + open: `open` }, confirmClose: { info: `Je staat op het punt op een kluis te sluiten. Alle aan deze kluis verbonden accounts zullen niet meer zichtbaar zijn na het voltooien van deze actie. Om deze accounts weer zichtbaar te maken dien je de kluis weer te openen.`, @@ -70,6 +71,38 @@ export default { }, title: `Maak een nieuwe kluis aan` }, + editMeta: { + allowPassword: `Wijzig kluis wachtwoord`, + button: { + close: `sluit`, + save: `opslaan` + }, + currentPassword: { + hint: `je huidige kluis wachtwoord`, + label: `huidige wachtwoord` + }, + description: { + hint: `de omschrijving van deze kluis`, + label: `kluis omschrijving` + }, + password: { + hint: `een sterk, uniek wachtwoord`, + label: `nieuw wachtwoord` + }, + password2: { + hint: `verifieer je nieuwe wachtwoord`, + label: `nieuw wachtwoord (herhaal)` + }, + passwordHint: { + hint: `je wachtwoord hint voor deze kluis`, + label: `wachtwoord hint` + }, + title: `Bewerk Kluis Metadata` + }, empty: `Er zijn momenteel geen kluizen om weer tegeven.`, + selector: { + noneAvailable: `Er zijn momenteel geen kluizen geopend en beschikbaar voor selectie. Maak eerst een kluis aan en open deze, voordat je een kluis selecteert voor het verplaatsen van een account.`, + title: `Selecteer Account Kluis` + }, title: `Kluis Beheer` }; diff --git a/js/src/i18n/nl/verification.js b/js/src/i18n/nl/verification.js new file mode 100644 index 000000000..bd0ba1c28 --- /dev/null +++ b/js/src/i18n/nl/verification.js @@ -0,0 +1,85 @@ +// Copyright 2015-2017 Parity Technologies (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 { + button: { + cancel: `Annuleer`, + done: `Klaar`, + next: `Volgende` + }, + code: { + error: `ongeldige code`, + hint: `Voer de ontvangen code in.`, + label: `verificatie code`, + sent: `De verificatie code is verstuurd naar {receiver}.` + }, + confirmation: { + authorise: `De verificatie code zal naar het contract worden verzonden. Gebruik de Parity Signer om dit goed te keuren.`, + windowOpen: `Houd dit scherm open.` + }, + done: { + message: `Gefeliciteerd, je account is geverifieerd!` + }, + email: { + enterCode: `Voer de code in die je per e-email hebt ontvangen.` + }, + gatherData: { + email: { + hint: `de code zal naar dit adres worden verstuurd`, + label: `e-mail adres` + }, + phoneNumber: { + hint: `De SMS zal naar dit nummer worden verstuurd`, + label: `telefoon nummer in internationaal formaat` + } + }, + gatherDate: { + email: { + error: `ongeldig e-mail adres` + }, + phoneNumber: { + error: `ongeldig telefoon nummer` + } + }, + loading: `Laden van verificatie data.`, + request: { + authorise: `Een verificatie verzoek zal naar het contract worden verzonden. Gebruik de Parity Signer om dit goed te keuren.`, + requesting: `Een code aanvragen bij de Parity-server en wachten tot de puzzel in het contract opgenomen wordt.`, + windowOpen: `Houd dit scherm open.` + }, + sms: { + enterCode: `Voer de code in die je per SMS hebt ontvangen.` + }, + steps: { + code: `Voer Code in`, + completed: `Voltooi`, + confirm: `Bevestig`, + data: `Voer Data in`, + method: `Methode`, + request: `Verzoek` + }, + title: `verifieer je account`, + types: { + email: { + description: `De hash van het e-mail adres waarvan je bewijst dat het van jou is, zal worden opgeslagen in de blockchain.`, + label: `E-mail Verificatie` + }, + sms: { + description: `Het zal in de blockchain worden vast gelegd dat jij in het bezit bent van een telefoon nummer (not which).`, + label: `SMS Verificatie` + } + } +}; diff --git a/js/src/i18n/nl/wallet.js b/js/src/i18n/nl/wallet.js new file mode 100644 index 000000000..d0460e58f --- /dev/null +++ b/js/src/i18n/nl/wallet.js @@ -0,0 +1,45 @@ +// Copyright 2015-2017 Parity Technologies (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 { + buttons: { + edit: `bewerk`, + forget: `vergeet`, + settings: `instellingen`, + transfer: `verzend` + }, + confirmations: { + buttons: { + confirmAs: `Bevestig als...`, + revokeAs: `Herroep als...` + }, + none: `Er zijn momenteel geen transacties die op bevestiging wachten.`, + tooltip: { + confirmed: `Bevestigd door {number}/{required} eigenaren` + } + }, + details: { + requiredOwners: `Dit wallet vereist ten minste {owners} voor de goedkeuring van elke actie (transactions, modifications).`, + requiredOwnersNumber: `{number} {numberValue, plural, one {owner} other {owners}}`, + spent: `{spent} is vandaag besteed, van de {limit} ingestelde daglimiet. De daglimiet is op {date} opnieuw ingesteld`, + title: `Details` + }, + title: `Wallet Beheer`, + transactions: { + none: `Er zijn geen verzonden transacties.`, + title: `Transacties` + } +}; diff --git a/js/src/i18n/nl/walletSettings.js b/js/src/i18n/nl/walletSettings.js index ca0842944..66fd70001 100644 --- a/js/src/i18n/nl/walletSettings.js +++ b/js/src/i18n/nl/walletSettings.js @@ -15,6 +15,16 @@ // along with Parity. If not, see . export default { + addOwner: { + title: `Eigenaar toevoegen` + }, + buttons: { + cancel: `Annuleer`, + close: `Sluit`, + next: `Volgende`, + send: `Verzend`, + sending: `Verzenden...` + }, changes: { modificationString: `Om je wijzigingen door te voeren zullen andere eigenaren deze zelfde wijzigingen moeten verzenden. Om het @@ -25,7 +35,8 @@ export default { edit: { message: `Om de instellingen van dit contract de wijzigen zullen minimaal {owners, number} {owners, plural, one {owner } other {owners }} precies dezelfde - wijzigingen moeten verzenden. Je kunt hier de wijzigingen in string-vorm plakken.` + wijzigingen moeten verzenden. Je kunt de wijzigingen hier + in string-vorm plakken.` }, modifications: { daylimit: { @@ -47,11 +58,12 @@ export default { label: `van account (wallet eigenaar)` } }, - rejected: { - busyStep: { - state: `De wallet instellingen zullen niet worden gewijzigd. Je kunt dit venster veilig sluiten.`, - title: `De wijzigingen zijn afgewezen.` - }, - title: `afgewezen` + ownersChange: { + details: `van {from} naar {to}`, + title: `Wijzig Vereiste Eigenaren` + }, + rejected: `De transactie #{txid} is afgewezen`, + removeOwner: { + title: `Verwijder Eigenaar` } }; diff --git a/js/src/i18n/nl/writeContract.js b/js/src/i18n/nl/writeContract.js new file mode 100644 index 000000000..e264ac83a --- /dev/null +++ b/js/src/i18n/nl/writeContract.js @@ -0,0 +1,62 @@ +// Copyright 2015-2017 Parity Technologies (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 { + buttons: { + autoCompile: `Auto-Compile`, + compile: `Compileer`, + deploy: `Maak Aan`, + import: `Importeer Solidity`, + load: `Laad`, + new: `Nieuw`, + optimise: `Optimaliseer`, + save: `Opslaan` + }, + compiling: { + action: `Compileer de bron code.`, + busy: `Compileren...` + }, + details: { + saved: `(opgeslagen {timestamp})` + }, + error: { + noContract: `Er is geen contract gevonden.`, + params: `Een fout is opgetreden met de volgende omschrijving` + }, + input: { + abi: `ABI Interface`, + code: `Bytecode`, + metadata: `Metadata`, + swarm: `Swarm Metadata Hash` + }, + title: { + contract: `Selecteer een contract`, + loading: `Laden...`, + main: `Schrijf een Contract`, + messages: `Compiler berichten`, + new: `Nieuw Solidity Contract`, + parameters: `Parameters`, + saved: `opgeslagen @ {timestamp}`, + selectSolidity: `Selecteer een Solidity versie`, + solidity: `Solidity {version} laden` + }, + type: { + humanErc20: `Implementatie van het Human Token Contract`, + implementErc20: `Implementatie van ERC20 Token Contract`, + multisig: `Implementatie van een multisig Wallet`, + standardErc20: `Standaard ERC20 Token Contract` + } +}; From 4d3f137e1e12d3788040c51260d2dcf03d6715b3 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 19 Apr 2017 16:27:45 +0200 Subject: [PATCH 033/117] iterate over all epochs --- ethcore/src/blockchain/blockchain.rs | 102 +++++++++++++++++++++++++++ ethcore/src/blockchain/extras.rs | 20 +++--- ethcore/src/client/client.rs | 1 + 3 files changed, 113 insertions(+), 10 deletions(-) diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 79169fd82..0824bd351 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -419,6 +419,45 @@ impl<'a> Iterator for AncestryIter<'a> { } } +/// An iterator which walks all epoch transitions. +/// Returns epoch transitions. +pub struct EpochTransitionIter<'a> { + chain: &'a BlockChain, + prefix_iter: Box, Box<[u8]>)> + 'a>, +} + +impl<'a> Iterator for EpochTransitionIter<'a> { + type Item = (u64, EpochTransition); + + fn next(&mut self) -> Option { + loop { + match self.prefix_iter.next() { + Some((key, val)) => { + // iterator may continue beyond values beginning with this + // prefix. + if !key.starts_with(&EPOCH_KEY_PREFIX[..]) { return None } + + let transitions: EpochTransitions = ::rlp::decode(&val[..]); + + // if there are multiple candidates, at most one will be on the + // canon chain. + for transition in transitions.candidates.into_iter() { + let is_in_canon_chain = self.chain.block_hash(transition.block_number) + .map_or(false, |hash| hash == transition.block_hash); + + if is_in_canon_chain { + return Some((transitions.number, transition)) + } + } + + // some epochs never occurred on the main chain. + } + None => return None, + } + } + } +} + impl BlockChain { /// Create new instance of blockchain from given Genesis. pub fn new(config: Config, genesis: &[u8], db: Arc) -> BlockChain { @@ -817,6 +856,15 @@ impl BlockChain { } } + /// Iterate over all epoch transitions. + pub fn epoch_transitions(&self) -> EpochTransitionIter { + let iter = self.db.iter_from_prefix(db::COL_EXTRA, &EPOCH_KEY_PREFIX[..]); + EpochTransitionIter { + chain: self, + prefix_iter: iter, + } + } + /// Add a child to a given block. Assumes that the block hash is in /// the chain and the child's parent is this block. /// @@ -2130,4 +2178,58 @@ mod tests { assert_eq!(bc.rewind(), Some(genesis_hash.clone())); assert_eq!(bc.rewind(), None); } + + #[test] + fn epoch_transitions_iter() { + use blockchain::extras::EpochTransition; + + let mut canon_chain = ChainGenerator::default(); + let mut finalizer = BlockFinalizer::default(); + let genesis = canon_chain.generate(&mut finalizer).unwrap(); + + let db = new_db(); + { + let bc = new_chain(&genesis, db.clone()); + let uncle = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap(); + + let mut batch = db.transaction(); + // create a longer fork + for i in 0..5 { + let canon_block = canon_chain.generate(&mut finalizer).unwrap(); + let hash = BlockView::new(&canon_block).header_view().sha3(); + + bc.insert_block(&mut batch, &canon_block, vec![]); + bc.insert_epoch_transition(&mut batch, i, EpochTransition { + block_hash: hash, + block_number: i + 1, + proof: vec![], + state_proof: vec![], + }); + bc.commit(); + } + + assert_eq!(bc.best_block_number(), 5); + + let hash = BlockView::new(&uncle).header_view().sha3(); + bc.insert_block(&mut batch, &uncle, vec![]); + bc.insert_epoch_transition(&mut batch, 999, EpochTransition { + block_hash: hash, + block_number: 1, + proof: vec![], + state_proof: vec![] + }); + + db.write(batch).unwrap(); + bc.commit(); + + // epoch 999 not in canonical chain. + assert_eq!(bc.epoch_transitions().map(|(i, _)| i).collect::>(), vec![0, 1, 2, 3, 4]); + } + + // re-loading the blockchain should load the correct best block. + let bc = new_chain(&genesis, db); + + assert_eq!(bc.best_block_number(), 5); + assert_eq!(bc.epoch_transitions().map(|(i, _)| i).collect::>(), vec![0, 1, 2, 3, 4]); + } } diff --git a/ethcore/src/blockchain/extras.rs b/ethcore/src/blockchain/extras.rs index b9fd5d3bd..7de49f3bf 100644 --- a/ethcore/src/blockchain/extras.rs +++ b/ethcore/src/blockchain/extras.rs @@ -142,12 +142,9 @@ pub const EPOCH_KEY_LEN: usize = DB_PREFIX_LEN + 16; /// epoch key prefix. /// used to iterate over all epoch transitions in order from genesis. -pub fn epoch_key_prefix() -> [u8; DB_PREFIX_LEN] { - let mut arr = [0u8; DB_PREFIX_LEN]; - arr[0] = ExtrasIndex::EpochTransitions as u8; - - arr -} +pub const EPOCH_KEY_PREFIX: &'static [u8; DB_PREFIX_LEN] = &[ + ExtrasIndex::EpochTransitions as u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; pub struct EpochTransitionsKey([u8; EPOCH_KEY_LEN]); impl Deref for EpochTransitionsKey { @@ -161,7 +158,7 @@ impl Key for u64 { fn key(&self) -> Self::Target { let mut arr = [0u8; EPOCH_KEY_LEN]; - arr[..DB_PREFIX_LEN].copy_from_slice(&epoch_key_prefix()[..]); + arr[..DB_PREFIX_LEN].copy_from_slice(&EPOCH_KEY_PREFIX[..]); write!(&mut arr[DB_PREFIX_LEN..], "{:016x}", self) .expect("format arg is valid; no more than 16 chars will be written; qed"); @@ -302,14 +299,16 @@ impl Decodable for EpochTransitions { #[derive(Debug, Clone)] pub struct EpochTransition { pub block_hash: H256, // block hash at which the transition occurred. + pub block_number: BlockNumber, // block number at which the tranition occurred. pub proof: Vec, // "transition/epoch" proof from the engine. pub state_proof: Vec, // state items necessary to regenerate proof. } impl Encodable for EpochTransition { fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3) + s.begin_list(4) .append(&self.block_hash) + .append(&self.block_number) .append(&self.proof) .begin_list(self.state_proof.len()); @@ -323,8 +322,9 @@ impl Decodable for EpochTransition { fn decode(rlp: &UntrustedRlp) -> Result { Ok(EpochTransition { block_hash: rlp.val_at(0)?, - proof: rlp.val_at(1)?, - state_proof: rlp.at(2)?.iter().map(|x| { + block_number: rlp.val_at(1)?, + proof: rlp.val_at(2)?, + state_proof: rlp.at(3)?.iter().map(|x| { Ok(DBValue::from_slice(x.data()?)) }).collect::, _>>()?, }) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 26d823dce..88fd181bb 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -667,6 +667,7 @@ impl Client { Ok(proof) => chain.insert_epoch_transition(batch, epoch_number, EpochTransition { block_hash: hash.clone(), + block_number: header.number(), proof: proof, state_proof: read_values.into_inner().into_iter().collect(), }), From 770c7e5878cdb2b4e1783da116875022e71641a5 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 19 Apr 2017 14:32:38 +0000 Subject: [PATCH 034/117] [ci skip] js-precompiled 20170419-142847 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dccc2f8df..f3109228a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1773,7 +1773,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/paritytech/js-precompiled.git#7b0c9ffdfa680f7792751f03d3490fc2a9fea23f" +source = "git+https://github.com/paritytech/js-precompiled.git#b4927eeaa876ff4daffda80ec85382fbd95f313c" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index da567a458..ac1731b91 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.54", + "version": "1.7.55", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From fc18299869f087ed212256faaef638f0d96a8636 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 19 Apr 2017 17:47:30 +0200 Subject: [PATCH 035/117] Background-repeat round (#5475) --- js/src/index.ejs | 1 + js/src/ui/ParityBackground/parityBackground.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/index.ejs b/js/src/index.ejs index ec8592b88..305568f5b 100644 --- a/js/src/index.ejs +++ b/js/src/index.ejs @@ -8,6 +8,7 @@