diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 7ecb9e5b0f..9c09d86bad 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,6 +2,11 @@ Please provide a brief description of the issue. +## Version + +Please provide your Lighthouse and Rust version. Are you building from +`master`, which commit? + ## Present Behaviour Describe the present behaviour of the application, with regards to this diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index ae3a176500..51665d86d0 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -67,3 +67,10 @@ jobs: run: sudo npm install -g ganache-cli - name: Run the beacon chain sim without an eth1 connection run: cargo run --release --bin simulator no-eth1-sim + check-benchmarks: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Typecheck benchmark code without running it + run: make check-benches diff --git a/Cargo.lock b/Cargo.lock index b745d7ca85..1d1b0d0743 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,9 +330,13 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitvec" -version = "0.15.2" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" +checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" +dependencies = [ + "either", + "radium", +] [[package]] name = "blake2" @@ -937,7 +941,7 @@ name = "deposit_contract" version = "0.2.0" dependencies = [ "eth2_ssz", - "ethabi 9.0.1", + "ethabi 11.0.0", "reqwest", "serde_json", "tree_hash", @@ -1116,9 +1120,12 @@ dependencies = [ name = "environment" version = "0.2.0" dependencies = [ + "beacon_node", + "clap", "ctrlc", "env_logger 0.6.2", "eth2_config", + "eth2_testnet_config", "futures", "logging", "parking_lot 0.7.1", @@ -1314,17 +1321,16 @@ dependencies = [ [[package]] name = "ethabi" -version = "9.0.1" +version = "11.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965126c64662832991f5a748893577630b558e47fa94e7f35aefcd20d737cef7" +checksum = "97652a7d1f2504d6c885c87e242a06ccef5bd3054093d3fb742d8fb64806231a" dependencies = [ - "error-chain", "ethereum-types 0.8.0", "rustc-hex", "serde", - "serde_derive", "serde_json", "tiny-keccak", + "uint 0.8.2", ] [[package]] @@ -1407,7 +1413,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", "synstructure", @@ -1643,9 +1649,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" dependencies = [ "libc", ] @@ -1916,9 +1922,9 @@ checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "js-sys" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb931d43e71f560c81badb0191596562bafad2be06a3f9025b845c847c60df5" +checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" dependencies = [ "wasm-bindgen", ] @@ -1975,6 +1981,7 @@ name = "lcli" version = "0.2.0" dependencies = [ "clap", + "deposit_contract", "dirs", "environment", "eth1_test_rig", @@ -1989,6 +1996,7 @@ dependencies = [ "serde_yaml", "simple_logger", "state_processing", + "tree_hash", "types", "web3", ] @@ -2913,9 +2921,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.28" +version = "0.10.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" +checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" dependencies = [ "bitflags 1.2.1", "cfg-if", @@ -2933,9 +2941,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.54" +version = "0.9.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" +checksum = "7717097d810a0f2e2323f9e5d11e71608355e24828410b55b9d4f18aa5f9a5d8" dependencies = [ "autocfg 1.0.0", "cc", @@ -3012,9 +3020,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f509c5e67ca0605ee17dcd3f91ef41cadd685c75a298fb6261b781a5acb3f910" +checksum = "329c8f7f4244ddb5c37c103641027a76c530e65e8e4b8240b29f81ea40508b17" dependencies = [ "arrayvec 0.5.1", "bitvec", @@ -3192,9 +3200,9 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcfdefadc3d57ca21cf17990a28ef4c0f7c61383a28cb7604cf4a18e6ede1420" +checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" [[package]] name = "proc-macro2" @@ -3207,9 +3215,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" dependencies = [ "unicode-xid 0.2.0", ] @@ -3305,9 +3313,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", ] +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" + [[package]] name = "rand" version = "0.3.23" @@ -3687,15 +3701,16 @@ dependencies = [ "eth2_ssz_derive", "rayon", "serde", + "state_processing", "tree_hash", "types", ] [[package]] name = "ring" -version = "0.16.11" +version = "0.16.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "741ba1704ae21999c00942f9f5944f801e977f54302af346b596287599ad1862" +checksum = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c" dependencies = [ "cc", "lazy_static", @@ -3844,21 +3859,22 @@ dependencies = [ [[package]] name = "security-framework" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97bbedbe81904398b6ebb054b3e912f99d55807125790f3198ac990d98def5b0" +checksum = "572dfa3a0785509e7a44b5b4bebcf94d41ba34e9ed9eb9df722545c3b3c4144a" dependencies = [ "bitflags 1.2.1", "core-foundation", "core-foundation-sys", + "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06fd2f23e31ef68dd2328cc383bd493142e46107a3a0e24f7d734e3f3b80fe4c" +checksum = "8ddb15a5fec93b7021b8a9e96009c5d8d51c15673569f7c0f6b7204e5b7b404f" dependencies = [ "core-foundation-sys", "libc", @@ -3887,20 +3903,20 @@ checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" [[package]] name = "serde" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" +checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" +checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", ] @@ -3915,9 +3931,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.48" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" +checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" dependencies = [ "itoa", "ryu", @@ -3930,7 +3946,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd02c7587ec314570041b2754829f84d873ced14a96d1fd1823531e11db40573" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", ] @@ -4354,7 +4370,7 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "unicode-xid 0.2.0", ] @@ -4365,7 +4381,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", "unicode-xid 0.2.0", @@ -5145,9 +5161,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3557c397ab5a8e347d434782bcd31fc1483d927a6826804cec05cc792ee2519d" +checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -5155,14 +5171,14 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0da9c9a19850d3af6df1cb9574970b566d617ecfaf36eb0b706b6f3ef9bd2f8" +checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" dependencies = [ "bumpalo", "lazy_static", "log 0.4.8", - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", "wasm-bindgen-shared", @@ -5183,9 +5199,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457414a91863c0ec00090dba537f88ab955d93ca6555862c29b6d860990b8a8a" +checksum = "7add542ea1ac7fdaa9dc25e031a6af33b7d63376292bd24140c637d00d1c312a" dependencies = [ "cfg-if", "js-sys", @@ -5195,9 +5211,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6fde1d36e75a714b5fe0cffbb78978f222ea6baebb726af13c78869fdb4205" +checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" dependencies = [ "quote 1.0.3", "wasm-bindgen-macro-support", @@ -5205,11 +5221,11 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bda4168030a6412ea8a047e27238cadf56f0e53516e1e83fec0a8b7c786f6d" +checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", "wasm-bindgen-backend", @@ -5218,31 +5234,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc9f36ad51f25b0219a3d4d13b90eb44cd075dff8b6280cca015775d7acaddd8" +checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" [[package]] name = "wasm-bindgen-test" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449aeba7035e4a4710cd263bbac33519fa3828bff1c6f642fa8896601e7016ad" +checksum = "648da3460c6d2aa04b715a936329e2e311180efe650b2127d6267f4193ccac14" dependencies = [ "console_error_panic_hook", "js-sys", "scoped-tls 1.0.0", "wasm-bindgen", - "wasm-bindgen-futures 0.4.9", + "wasm-bindgen-futures 0.4.10", "wasm-bindgen-test-macro", ] [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49449f8dcedc192bd0cf11b5711982decdd4dbad1029f92370e2b1215031dd59" +checksum = "cf2f86cd78a2aa7b1fb4bb6ed854eccb7f9263089c79542dca1576a1518a8467" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", ] @@ -5262,9 +5278,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a" +checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" dependencies = [ "js-sys", "wasm-bindgen", @@ -5384,9 +5400,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" dependencies = [ "winapi 0.3.8", ] diff --git a/Makefile b/Makefile index 7d55593998..ad071a5e36 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,10 @@ test-debug: cargo-fmt: cargo fmt --all -- --check +# Typechecks benchmark code +check-benches: + cargo check --all --benches + # Runs only the ef-test vectors. run-ef-tests: cargo test --release --manifest-path=$(EF_TESTS)/Cargo.toml --features "ef_tests" diff --git a/README.md b/README.md index 49c6f2bb91..9b5854dd9c 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Like all Ethereum 2.0 clients, Lighthouse is a work-in-progress. Current development overview: -- Specification `v0.10.1` implemented, optimized and passing test vectors. +- Specification `v0.11.1` implemented, optimized and passing test vectors. - Rust-native libp2p with Gossipsub and Discv5. - RESTful JSON API via HTTP server. - Events via WebSocket. diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index b0c56e9db2..aa5509fd2e 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -140,6 +140,8 @@ pub struct HeadInfo { pub current_justified_checkpoint: types::Checkpoint, pub finalized_checkpoint: types::Checkpoint, pub fork: Fork, + pub genesis_time: u64, + pub genesis_validators_root: Hash256, } pub trait BeaconChainTypes: Send + Sync + 'static { @@ -176,6 +178,8 @@ pub struct BeaconChain { pub(crate) canonical_head: TimeoutRwLock>, /// The root of the genesis block. pub genesis_block_root: Hash256, + /// The root of the list of genesis validators, used during syncing. + pub genesis_validators_root: Hash256, /// A state-machine that is updated with information from the network and chooses a canonical /// head block. pub fork_choice: ForkChoice, @@ -468,6 +472,8 @@ impl BeaconChain { current_justified_checkpoint: head.beacon_state.current_justified_checkpoint.clone(), finalized_checkpoint: head.beacon_state.finalized_checkpoint.clone(), fork: head.beacon_state.fork.clone(), + genesis_time: head.beacon_state.genesis_time, + genesis_validators_root: head.beacon_state.genesis_validators_root, }) } @@ -814,7 +820,7 @@ impl BeaconChain { root: target_root, }, }, - signature: AggregateSignature::new(), + signature: AggregateSignature::empty_signature(), }) } @@ -1084,11 +1090,16 @@ impl BeaconChain { .try_read_for(VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT) .ok_or_else(|| Error::ValidatorPubkeyCacheLockTimeout)?; - let fork = self + let (fork, genesis_validators_root) = self .canonical_head .try_read_for(HEAD_LOCK_TIMEOUT) .ok_or_else(|| Error::CanonicalHeadLockTimeout) - .map(|head| head.beacon_state.fork.clone())?; + .map(|head| { + ( + head.beacon_state.fork.clone(), + head.beacon_state.genesis_validators_root, + ) + })?; let signature_set = indexed_attestation_signature_set_from_pubkeys( |validator_index| { @@ -1099,6 +1110,7 @@ impl BeaconChain { &attestation.signature, &indexed_attestation, &fork, + genesis_validators_root, &self.spec, ) .map_err(Error::SignatureSetError)?; @@ -1175,10 +1187,12 @@ impl BeaconChain { let index = attestation.data.index; let slot = attestation.data.slot; - match self - .op_pool - .insert_attestation(attestation, &fork, &self.spec) - { + match self.op_pool.insert_attestation( + attestation, + &fork, + genesis_validators_root, + &self.spec, + ) { Ok(_) => {} Err(e) => { error!( @@ -1674,6 +1688,7 @@ impl BeaconChain { let mut block = SignedBeaconBlock { message: BeaconBlock { slot: state.slot, + proposer_index: state.get_beacon_proposer_index(state.slot, &self.spec)? as u64, parent_root, state_root: Hash256::zero(), body: BeaconBlockBody { @@ -2014,7 +2029,8 @@ impl BeaconChain { // If we are unable to read the slot clock we assume that it is prior to genesis and // therefore use the genesis slot. let slot = self.slot().unwrap_or_else(|_| self.spec.genesis_slot); - self.spec.enr_fork_id(slot) + + self.spec.enr_fork_id(slot, self.genesis_validators_root) } /// Calculates the `Duration` to the next fork, if one exists. diff --git a/beacon_node/beacon_chain/src/builder.rs b/beacon_node/beacon_chain/src/builder.rs index 45231af5ee..f5df3e4ac4 100644 --- a/beacon_node/beacon_chain/src/builder.rs +++ b/beacon_node/beacon_chain/src/builder.rs @@ -187,6 +187,19 @@ where .map_err(|e| format!("DB error whilst reading eth1 cache: {:?}", e)) } + /// Returns true if `self.store` contains a persisted beacon chain. + pub fn store_contains_beacon_chain(&self) -> Result { + let store = self + .store + .clone() + .ok_or_else(|| "load_from_store requires a store.".to_string())?; + + Ok(store + .get::(&Hash256::from_slice(&BEACON_CHAIN_DB_KEY)) + .map_err(|e| format!("DB error when reading persisted beacon chain: {:?}", e))? + .is_some()) + } + /// Attempt to load an existing chain from the builder's `Store`. /// /// May initialize several components; including the op_pool and finalized checkpoints. @@ -418,6 +431,7 @@ where // TODO: allow for persisting and loading the pool from disk. naive_aggregation_pool: <_>::default(), eth1_chain: self.eth1_chain, + genesis_validators_root: canonical_head.beacon_state.genesis_validators_root, canonical_head: TimeoutRwLock::new(canonical_head.clone()), genesis_block_root: self .genesis_block_root diff --git a/beacon_node/beacon_chain/src/naive_aggregation_pool.rs b/beacon_node/beacon_chain/src/naive_aggregation_pool.rs index 6f2031dab7..6718fd2f3e 100644 --- a/beacon_node/beacon_chain/src/naive_aggregation_pool.rs +++ b/beacon_node/beacon_chain/src/naive_aggregation_pool.rs @@ -274,11 +274,12 @@ mod tests { a } - fn sign(a: &mut Attestation, i: usize) { + fn sign(a: &mut Attestation, i: usize, genesis_validators_root: Hash256) { a.sign( &generate_deterministic_keypair(i).sk, i, &Fork::default(), + genesis_validators_root, &E::default_spec(), ) .expect("should sign attestation"); @@ -302,7 +303,7 @@ mod tests { "should not accept attestation without any signatures" ); - sign(&mut a, 0); + sign(&mut a, 0, Hash256::random()); assert_eq!( pool.insert(&a), @@ -324,7 +325,7 @@ mod tests { "retrieved attestation should equal the one inserted" ); - sign(&mut a, 1); + sign(&mut a, 1, Hash256::random()); assert_eq!( pool.insert(&a), @@ -338,8 +339,9 @@ mod tests { let mut a_0 = get_attestation(Slot::new(0)); let mut a_1 = a_0.clone(); - sign(&mut a_0, 0); - sign(&mut a_1, 1); + let genesis_validators_root = Hash256::random(); + sign(&mut a_0, 0, genesis_validators_root); + sign(&mut a_1, 1, genesis_validators_root); let pool = NaiveAggregationPool::default(); @@ -374,7 +376,7 @@ mod tests { let mut a_different = a_0.clone(); let different_root = Hash256::from_low_u64_be(1337); unset_bit(&mut a_different, 0); - sign(&mut a_different, 2); + sign(&mut a_different, 2, genesis_validators_root); assert!(a_different.data.beacon_block_root != different_root); a_different.data.beacon_block_root = different_root; @@ -396,7 +398,7 @@ mod tests { #[test] fn auto_pruning() { let mut base = get_attestation(Slot::new(0)); - sign(&mut base, 0); + sign(&mut base, 0, Hash256::random()); let pool = NaiveAggregationPool::default(); @@ -450,7 +452,7 @@ mod tests { #[test] fn max_attestations() { let mut base = get_attestation(Slot::new(0)); - sign(&mut base, 0); + sign(&mut base, 0, Hash256::random()); let pool = NaiveAggregationPool::default(); diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 5d393d5112..05dc5258b7 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -307,7 +307,9 @@ where let randao_reveal = { let epoch = slot.epoch(E::slots_per_epoch()); - let domain = self.spec.get_domain(epoch, Domain::Randao, fork); + let domain = + self.spec + .get_domain(epoch, Domain::Randao, fork, state.genesis_validators_root); let message = epoch.signing_root(domain); Signature::new(message.as_bytes(), sk) }; @@ -317,7 +319,7 @@ where .produce_block_on_state(state, slot, randao_reveal) .expect("should produce block"); - let signed_block = block.sign(sk, &state.fork, &self.spec); + let signed_block = block.sign(sk, &state.fork, state.genesis_validators_root, &self.spec); (signed_block, state) } @@ -402,6 +404,7 @@ where attestation.data.target.epoch, Domain::BeaconAttester, fork, + state.genesis_validators_root, ); let message = attestation.data.signing_root(domain); diff --git a/beacon_node/beacon_chain/tests/attestation_production.rs b/beacon_node/beacon_chain/tests/attestation_production.rs index 15fece8639..3b84a78ff5 100644 --- a/beacon_node/beacon_chain/tests/attestation_production.rs +++ b/beacon_node/beacon_chain/tests/attestation_production.rs @@ -106,7 +106,7 @@ fn produces_attestations() { ); assert_eq!( attestation.signature, - AggregateSignature::new(), + AggregateSignature::empty_signature(), "bad signature" ); assert_eq!(data.index, index, "bad index"); diff --git a/beacon_node/beacon_chain/tests/import_chain_segment_tests.rs b/beacon_node/beacon_chain/tests/import_chain_segment_tests.rs index f83822b47b..ead944315c 100644 --- a/beacon_node/beacon_chain/tests/import_chain_segment_tests.rs +++ b/beacon_node/beacon_chain/tests/import_chain_segment_tests.rs @@ -89,12 +89,12 @@ fn update_proposal_signatures( .get(proposer_index) .expect("proposer keypair should be available"); - snapshot.beacon_block = - snapshot - .beacon_block - .message - .clone() - .sign(&keypair.sk, &state.fork, spec); + snapshot.beacon_block = snapshot.beacon_block.message.clone().sign( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ); } } @@ -352,7 +352,6 @@ fn invalid_signatures() { */ let mut snapshots = CHAIN_SEGMENT.clone(); let proposer_slashing = ProposerSlashing { - proposer_index: 0, signed_header_1: SignedBeaconBlockHeader { message: snapshots[block_index].beacon_block.message.block_header(), signature: junk_signature(), diff --git a/beacon_node/client/src/builder.rs b/beacon_node/client/src/builder.rs index 74cec8adef..ba5da5df00 100644 --- a/beacon_node/client/src/builder.rs +++ b/beacon_node/client/src/builder.rs @@ -153,6 +153,26 @@ where Ok((builder, spec, context)) }) .and_then(move |(builder, spec, context)| { + let chain_exists = builder + .store_contains_beacon_chain() + .unwrap_or_else(|_| false); + + // If the client is expect to resume but there's no beacon chain in the database, + // use the `DepositContract` method. This scenario is quite common when the client + // is shutdown before finding genesis via eth1. + // + // Alternatively, if there's a beacon chain in the database then always resume + // using it. + let client_genesis = if client_genesis == ClientGenesis::Resume && !chain_exists { + info!(context.log, "Defaulting to deposit contract genesis"); + + ClientGenesis::DepositContract + } else if chain_exists { + ClientGenesis::Resume + } else { + client_genesis + }; + let genesis_state_future: Box + Send> = match client_genesis { ClientGenesis::Interop { diff --git a/beacon_node/client/src/config.rs b/beacon_node/client/src/config.rs index c4b3a0823a..e3265f9606 100644 --- a/beacon_node/client/src/config.rs +++ b/beacon_node/client/src/config.rs @@ -12,7 +12,7 @@ const TESTNET_SPEC_CONSTANTS: &str = "minimal"; const DEFAULT_FREEZER_DB_DIR: &str = "freezer_db"; /// Defines how the client should initialize the `BeaconChain` and other components. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] pub enum ClientGenesis { /// Reads the genesis state and other persisted data from the `Store`. Resume, diff --git a/beacon_node/network/src/sync/block_processor.rs b/beacon_node/network/src/sync/block_processor.rs new file mode 100644 index 0000000000..25f53da25d --- /dev/null +++ b/beacon_node/network/src/sync/block_processor.rs @@ -0,0 +1,214 @@ +use crate::router::processor::FUTURE_SLOT_TOLERANCE; +use crate::sync::manager::SyncMessage; +use crate::sync::range_sync::BatchId; +use beacon_chain::{BeaconChain, BeaconChainTypes, BlockError}; +use eth2_libp2p::PeerId; +use slog::{crit, debug, error, trace, warn}; +use std::sync::{Arc, Weak}; +use tokio::sync::mpsc; +use types::SignedBeaconBlock; + +/// Id associated to a block processing request, either a batch or a single block. +#[derive(Clone, Debug, PartialEq)] +pub enum ProcessId { + /// Processing Id of a range syncing batch. + RangeBatchId(BatchId), + /// Processing Id of the parent lookup of a block + ParentLookup(PeerId), +} + +/// The result of a block processing request. +// TODO: When correct batch error handling occurs, we will include an error type. +#[derive(Debug)] +pub enum BatchProcessResult { + /// The batch was completed successfully. + Success, + /// The batch processing failed. + Failed, +} + +/// Spawns a thread handling the block processing of a request: range syncing or parent lookup. +pub fn spawn_block_processor( + chain: Weak>, + process_id: ProcessId, + downloaded_blocks: Vec>, + mut sync_send: mpsc::UnboundedSender>, + log: slog::Logger, +) { + std::thread::spawn(move || { + match process_id { + // this a request from the range sync + ProcessId::RangeBatchId(batch_id) => { + debug!(log, "Processing batch"; "id" => *batch_id, "blocks" => downloaded_blocks.len()); + let result = match process_blocks(chain, downloaded_blocks.iter(), &log) { + Ok(_) => { + debug!(log, "Batch processed"; "id" => *batch_id ); + BatchProcessResult::Success + } + Err(e) => { + debug!(log, "Batch processing failed"; "id" => *batch_id, "error" => e); + BatchProcessResult::Failed + } + }; + + let msg = SyncMessage::BatchProcessed { + batch_id: batch_id, + downloaded_blocks: downloaded_blocks, + result, + }; + sync_send.try_send(msg).unwrap_or_else(|_| { + debug!( + log, + "Block processor could not inform range sync result. Likely shutting down." + ); + }); + } + // this a parent lookup request from the sync manager + ProcessId::ParentLookup(peer_id) => { + debug!(log, "Processing parent lookup"; "last_peer_id" => format!("{}", peer_id), "blocks" => downloaded_blocks.len()); + // parent blocks are ordered from highest slot to lowest, so we need to process in + // reverse + match process_blocks(chain, downloaded_blocks.iter().rev(), &log) { + Err(e) => { + warn!(log, "Parent lookup failed"; "last_peer_id" => format!("{}", peer_id), "error" => e); + sync_send + .try_send(SyncMessage::ParentLookupFailed(peer_id)) + .unwrap_or_else(|_| { + // on failure, inform to downvote the peer + debug!( + log, + "Block processor could not inform parent lookup result. Likely shutting down." + ); + }); + } + Ok(_) => { + debug!(log, "Parent lookup processed successfully"); + } + } + } + } + }); +} + +/// Helper function to process blocks batches which only consumes the chain and blocks to process. +// TODO: Verify the fork choice logic and the correct error handling from `process_chain_segment`. +// Ensure fork-choice doesn't need to be run during the failed errors. +fn process_blocks< + 'a, + T: BeaconChainTypes, + I: Iterator>, +>( + chain: Weak>, + downloaded_blocks: I, + log: &slog::Logger, +) -> Result<(), String> { + if let Some(chain) = chain.upgrade() { + let blocks = downloaded_blocks.cloned().collect::>(); + match chain.process_chain_segment(blocks) { + Ok(roots) => { + if roots.is_empty() { + debug!(log, "All blocks already known"); + } else { + debug!( + log, "Imported blocks from network"; + "count" => roots.len(), + ); + // Batch completed successfully with at least one block, run fork choice. + // TODO: Verify this logic + run_fork_choice(chain, log); + } + } + Err(BlockError::ParentUnknown(parent)) => { + // blocks should be sequential and all parents should exist + warn!( + log, "Parent block is unknown"; + "parent_root" => format!("{}", parent), + ); + return Err(format!("Block has an unknown parent: {}", parent)); + } + Err(BlockError::BlockIsAlreadyKnown) => { + // TODO: Check handling of this + crit!(log, "Unknown handling of block error"); + } + Err(BlockError::FutureSlot { + present_slot, + block_slot, + }) => { + if present_slot + FUTURE_SLOT_TOLERANCE >= block_slot { + // The block is too far in the future, drop it. + warn!( + log, "Block is ahead of our slot clock"; + "msg" => "block for future slot rejected, check your time", + "present_slot" => present_slot, + "block_slot" => block_slot, + "FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE, + ); + } else { + // The block is in the future, but not too far. + debug!( + log, "Block is slightly ahead of our slot clock, ignoring."; + "present_slot" => present_slot, + "block_slot" => block_slot, + "FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE, + ); + } + return Err(format!( + "Block with slot {} is higher than the current slot {}", + block_slot, present_slot + )); + } + Err(BlockError::WouldRevertFinalizedSlot { .. }) => { + //TODO: Check handling. Run fork choice? + debug!( + log, "Finalized or earlier block processed"; + ); + // block reached our finalized slot or was earlier, move to the next block + // TODO: How does this logic happen for the chain segment. We would want to + // continue processing in this case. + } + Err(BlockError::GenesisBlock) => { + debug!( + log, "Genesis block was processed"; + ); + // TODO: Similarly here. Prefer to continue processing. + } + Err(BlockError::BeaconChainError(e)) => { + // TODO: Run fork choice? + warn!( + log, "BlockProcessingFailure"; + "msg" => "unexpected condition in processing block.", + "outcome" => format!("{:?}", e) + ); + return Err(format!("Internal error whilst processing block: {:?}", e)); + } + other => { + // TODO: Run fork choice? + warn!( + log, "Invalid block received"; + "msg" => "peer sent invalid block", + "outcome" => format!("{:?}", other), + ); + return Err(format!("Peer sent invalid block. Reason: {:?}", other)); + } + } + } + Ok(()) +} + +/// Runs fork-choice on a given chain. This is used during block processing after one successful +/// block import. +fn run_fork_choice(chain: Arc>, log: &slog::Logger) { + match chain.fork_choice() { + Ok(()) => trace!( + log, + "Fork choice success"; + "location" => "batch processing" + ), + Err(e) => error!( + log, + "Fork choice failed"; + "error" => format!("{:?}", e), + "location" => "batch import error" + ), + } +} diff --git a/beacon_node/network/src/sync/manager.rs b/beacon_node/network/src/sync/manager.rs index 0b6f5a4f28..5da5ba49b3 100644 --- a/beacon_node/network/src/sync/manager.rs +++ b/beacon_node/network/src/sync/manager.rs @@ -33,8 +33,9 @@ //! if an attestation references an unknown block) this manager can search for the block and //! subsequently search for parents if needed. +use super::block_processor::{spawn_block_processor, BatchProcessResult, ProcessId}; use super::network_context::SyncNetworkContext; -use super::range_sync::{Batch, BatchProcessResult, RangeSync}; +use super::range_sync::{BatchId, RangeSync}; use crate::router::processor::PeerSyncInfo; use crate::service::NetworkMessage; use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome}; @@ -99,10 +100,13 @@ pub enum SyncMessage { /// A batch has been processed by the block processor thread. BatchProcessed { - process_id: u64, - batch: Box>, + batch_id: BatchId, + downloaded_blocks: Vec>, result: BatchProcessResult, }, + + /// A parent lookup has failed for a block given by this `peer_id`. + ParentLookupFailed(PeerId), } /// Maintains a sequential list of parents to lookup and the lookup's current state. @@ -172,6 +176,9 @@ pub struct SyncManager { /// The logger for the import manager. log: Logger, + + /// The sending part of input_channel + sync_send: mpsc::UnboundedSender>, } /// Spawns a new `SyncManager` thread which has a weak reference to underlying beacon @@ -202,6 +209,7 @@ pub fn spawn( single_block_lookups: FnvHashMap::default(), full_peers: HashSet::new(), log: log.clone(), + sync_send: sync_send.clone(), }; // spawn the sync manager thread @@ -590,8 +598,6 @@ impl SyncManager { // If the last block in the queue has an unknown parent, we continue the parent // lookup-search. - let total_blocks_to_process = parent_request.downloaded_blocks.len(); - if let Some(chain) = self.chain.upgrade() { let newest_block = parent_request .downloaded_blocks @@ -606,7 +612,15 @@ impl SyncManager { return; } Ok(BlockProcessingOutcome::Processed { .. }) - | Ok(BlockProcessingOutcome::BlockIsAlreadyKnown { .. }) => {} + | Ok(BlockProcessingOutcome::BlockIsAlreadyKnown { .. }) => { + spawn_block_processor( + self.chain.clone(), + ProcessId::ParentLookup(parent_request.last_submitted_peer.clone()), + parent_request.downloaded_blocks, + self.sync_send.clone(), + self.log.clone(), + ); + } Ok(outcome) => { // all else we consider the chain a failure and downvote the peer that sent // us the last block @@ -634,64 +648,6 @@ impl SyncManager { // chain doesn't exist, drop the parent queue and return return; } - - //TODO: Shift this to a block processing thread - - // the last received block has been successfully processed, process all other blocks in the - // chain - while let Some(block) = parent_request.downloaded_blocks.pop() { - // check if the chain exists - if let Some(chain) = self.chain.upgrade() { - match BlockProcessingOutcome::shim(chain.process_block(block)) { - Ok(BlockProcessingOutcome::Processed { .. }) - | Ok(BlockProcessingOutcome::BlockIsAlreadyKnown { .. }) => {} // continue to the next block - - // all else is considered a failure - Ok(outcome) => { - // the previous blocks have failed, notify the user the chain lookup has - // failed and drop the parent queue - debug!( - self.log, "Invalid parent chain. Past blocks failure"; - "outcome" => format!("{:?}", outcome), - "peer" => format!("{:?}", parent_request.last_submitted_peer), - ); - self.network - .downvote_peer(parent_request.last_submitted_peer.clone()); - break; - } - Err(e) => { - warn!( - self.log, "Parent chain processing error."; - "error" => format!("{:?}", e) - ); - self.network - .downvote_peer(parent_request.last_submitted_peer.clone()); - break; - } - } - } else { - // chain doesn't exist, end the processing - break; - } - } - - // at least one block has been processed, run fork-choice - if let Some(chain) = self.chain.upgrade() { - match chain.fork_choice() { - Ok(()) => trace!( - self.log, - "Fork choice success"; - "block_imports" => total_blocks_to_process - parent_request.downloaded_blocks.len(), - "location" => "parent request" - ), - Err(e) => error!( - self.log, - "Fork choice failed"; - "error" => format!("{:?}", e), - "location" => "parent request" - ), - }; - } } } @@ -782,17 +738,20 @@ impl Future for SyncManager { self.inject_error(peer_id, request_id); } SyncMessage::BatchProcessed { - process_id, - batch, + batch_id, + downloaded_blocks, result, } => { self.range_sync.handle_block_process_result( &mut self.network, - process_id, - *batch, + batch_id, + downloaded_blocks, result, ); } + SyncMessage::ParentLookupFailed(peer_id) => { + self.network.downvote_peer(peer_id); + } }, Ok(Async::NotReady) => break, Ok(Async::Ready(None)) => { diff --git a/beacon_node/network/src/sync/mod.rs b/beacon_node/network/src/sync/mod.rs index b51cd78f90..26274ef97f 100644 --- a/beacon_node/network/src/sync/mod.rs +++ b/beacon_node/network/src/sync/mod.rs @@ -1,6 +1,7 @@ //! Syncing for lighthouse. //! //! Stores the various syncing methods for the beacon chain. +mod block_processor; pub mod manager; mod network_context; mod range_sync; diff --git a/beacon_node/network/src/sync/range_sync/batch_processing.rs b/beacon_node/network/src/sync/range_sync/batch_processing.rs deleted file mode 100644 index 94ce6ae471..0000000000 --- a/beacon_node/network/src/sync/range_sync/batch_processing.rs +++ /dev/null @@ -1,166 +0,0 @@ -use super::batch::Batch; -use crate::router::processor::FUTURE_SLOT_TOLERANCE; -use crate::sync::manager::SyncMessage; -use beacon_chain::{BeaconChain, BeaconChainTypes, BlockError}; -use slog::{debug, error, trace, warn}; -use std::sync::{Arc, Weak}; -use tokio::sync::mpsc; - -/// The result of attempting to process a batch of blocks. -// TODO: When correct batch error handling occurs, we will include an error type. -#[derive(Debug)] -pub enum BatchProcessResult { - /// The batch was completed successfully. - Success, - /// The batch processing failed. - Failed, -} - -// TODO: Refactor to async fn, with stable futures -pub fn spawn_batch_processor( - chain: Weak>, - process_id: u64, - batch: Batch, - mut sync_send: mpsc::UnboundedSender>, - log: slog::Logger, -) { - std::thread::spawn(move || { - debug!(log, "Processing batch"; "id" => *batch.id); - let result = match process_batch(chain, &batch, &log) { - Ok(_) => BatchProcessResult::Success, - Err(_) => BatchProcessResult::Failed, - }; - - debug!(log, "Batch processed"; "id" => *batch.id, "result" => format!("{:?}", result)); - - sync_send - .try_send(SyncMessage::BatchProcessed { - process_id, - batch: Box::new(batch), - result, - }) - .unwrap_or_else(|_| { - debug!( - log, - "Batch result could not inform sync. Likely shutting down." - ); - }); - }); -} - -// Helper function to process block batches which only consumes the chain and blocks to process -fn process_batch( - chain: Weak>, - batch: &Batch, - log: &slog::Logger, -) -> Result<(), String> { - if let Some(chain) = chain.upgrade() { - match chain.process_chain_segment(batch.downloaded_blocks.clone()) { - Ok(roots) => { - trace!( - log, "Imported blocks from network"; - "count" => roots.len(), - ); - } - Err(BlockError::ParentUnknown(parent)) => { - // blocks should be sequential and all parents should exist - warn!( - log, "Parent block is unknown"; - "parent_root" => format!("{}", parent), - ); - - return Err(format!("Block has an unknown parent: {}", parent)); - } - Err(BlockError::BlockIsAlreadyKnown) => { - // this block is already known to us, move to the next - debug!( - log, "Imported a block that is already known"; - ); - } - Err(BlockError::FutureSlot { - present_slot, - block_slot, - }) => { - if present_slot + FUTURE_SLOT_TOLERANCE >= block_slot { - // The block is too far in the future, drop it. - warn!( - log, "Block is ahead of our slot clock"; - "msg" => "block for future slot rejected, check your time", - "present_slot" => present_slot, - "block_slot" => block_slot, - "FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE, - ); - } else { - // The block is in the future, but not too far. - debug!( - log, "Block is slightly ahead of our slot clock, ignoring."; - "present_slot" => present_slot, - "block_slot" => block_slot, - "FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE, - ); - } - - return Err(format!( - "Block with slot {} is higher than the current slot {}", - block_slot, present_slot - )); - } - Err(BlockError::WouldRevertFinalizedSlot { .. }) => { - debug!( - log, "Finalized or earlier block processed"; - ); - // block reached our finalized slot or was earlier, move to the next block - } - Err(BlockError::GenesisBlock) => { - debug!( - log, "Genesis block was processed"; - ); - } - Err(BlockError::BeaconChainError(e)) => { - warn!( - log, "BlockProcessingFailure"; - "msg" => "unexpected condition in processing block.", - "outcome" => format!("{:?}", e) - ); - - return Err(format!("Internal error whilst processing block: {:?}", e)); - } - other => { - warn!( - log, "Invalid block received"; - "msg" => "peer sent invalid block", - "outcome" => format!("{:?}", other), - ); - - return Err(format!("Peer sent invalid block. Reason: {:?}", other)); - } - } - } else { - return Ok(()); // terminate early due to dropped beacon chain - } - - // Batch completed successfully, run fork choice. - if let Some(chain) = chain.upgrade() { - run_fork_choice(chain, log); - } - - Ok(()) -} - -/// Runs fork-choice on a given chain. This is used during block processing after one successful -/// block import. -fn run_fork_choice(chain: Arc>, log: &slog::Logger) { - match chain.fork_choice() { - Ok(()) => trace!( - log, - "Fork choice success"; - "location" => "batch processing" - ), - Err(e) => error!( - log, - "Fork choice failed"; - "error" => format!("{:?}", e), - "location" => "batch import error" - ), - } -} diff --git a/beacon_node/network/src/sync/range_sync/chain.rs b/beacon_node/network/src/sync/range_sync/chain.rs index dd64600ab1..95abc099ed 100644 --- a/beacon_node/network/src/sync/range_sync/chain.rs +++ b/beacon_node/network/src/sync/range_sync/chain.rs @@ -1,5 +1,5 @@ use super::batch::{Batch, BatchId, PendingBatches}; -use super::batch_processing::{spawn_batch_processor, BatchProcessResult}; +use crate::sync::block_processor::{spawn_block_processor, BatchProcessResult, ProcessId}; use crate::sync::network_context::SyncNetworkContext; use crate::sync::SyncMessage; use beacon_chain::{BeaconChain, BeaconChainTypes}; @@ -76,7 +76,7 @@ pub struct SyncingChain { /// A random id given to a batch process request. This is None if there is no ongoing batch /// process. - current_processing_id: Option, + current_processing_batch: Option>, /// A send channel to the sync manager. This is given to the batch processor thread to report /// back once batch processing has completed. @@ -120,7 +120,7 @@ impl SyncingChain { to_be_downloaded_id: BatchId(1), to_be_processed_id: BatchId(1), state: ChainSyncingState::Stopped, - current_processing_id: None, + current_processing_batch: None, sync_send, chain, log, @@ -167,15 +167,16 @@ impl SyncingChain { // An entire batch of blocks has been received. This functions checks to see if it can be processed, // remove any batches waiting to be verified and if this chain is syncing, request new // blocks for the peer. - debug!(self.log, "Completed batch received"; "id"=> *batch.id, "blocks"=>batch.downloaded_blocks.len(), "awaiting_batches" => self.completed_batches.len()); + debug!(self.log, "Completed batch received"; "id"=> *batch.id, "blocks" => &batch.downloaded_blocks.len(), "awaiting_batches" => self.completed_batches.len()); // verify the range of received blocks // Note that the order of blocks is verified in block processing if let Some(last_slot) = batch.downloaded_blocks.last().map(|b| b.slot()) { // the batch is non-empty - if batch.start_slot > batch.downloaded_blocks[0].slot() || batch.end_slot < last_slot { + let first_slot = batch.downloaded_blocks[0].slot(); + if batch.start_slot > first_slot || batch.end_slot < last_slot { warn!(self.log, "BlocksByRange response returned out of range blocks"; - "response_initial_slot" => batch.downloaded_blocks[0].slot(), + "response_initial_slot" => first_slot, "requested_initial_slot" => batch.start_slot); network.downvote_peer(batch.current_peer); self.to_be_processed_id = batch.id; // reset the id back to here, when incrementing, it will check against completed batches @@ -218,7 +219,7 @@ impl SyncingChain { } // Only process one batch at a time - if self.current_processing_id.is_some() { + if self.current_processing_batch.is_some() { return; } @@ -238,14 +239,14 @@ impl SyncingChain { } /// Sends a batch to the batch processor. - fn process_batch(&mut self, batch: Batch) { - // only spawn one instance at a time - let processing_id: u64 = rand::random(); - self.current_processing_id = Some(processing_id); - spawn_batch_processor( + fn process_batch(&mut self, mut batch: Batch) { + let downloaded_blocks = std::mem::replace(&mut batch.downloaded_blocks, Vec::new()); + let batch_id = ProcessId::RangeBatchId(batch.id.clone()); + self.current_processing_batch = Some(batch); + spawn_block_processor( self.chain.clone(), - processing_id, - batch, + batch_id, + downloaded_blocks, self.sync_send.clone(), self.log.clone(), ); @@ -256,30 +257,41 @@ impl SyncingChain { pub fn on_batch_process_result( &mut self, network: &mut SyncNetworkContext, - processing_id: u64, - batch: &mut Option>, + batch_id: BatchId, + downloaded_blocks: &mut Option>>, result: &BatchProcessResult, ) -> Option { - if Some(processing_id) != self.current_processing_id { - // batch process doesn't belong to this chain + if let Some(current_batch) = &self.current_processing_batch { + if current_batch.id != batch_id { + // batch process does not belong to this chain + return None; + } + // Continue. This is our processing request + } else { + // not waiting on a processing result return None; } - // Consume the batch option - let batch = batch.take().or_else(|| { + // claim the result by consuming the option + let downloaded_blocks = downloaded_blocks.take().or_else(|| { + // if taken by another chain, we are no longer waiting on a result. + self.current_processing_batch = None; crit!(self.log, "Processed batch taken by another chain"); None })?; + // No longer waiting on a processing result + let mut batch = self.current_processing_batch.take().unwrap(); + // These are the blocks of this batch + batch.downloaded_blocks = downloaded_blocks; + // double check batches are processed in order TODO: Remove for prod if batch.id != self.to_be_processed_id { crit!(self.log, "Batch processed out of order"; - "processed_batch_id" => *batch.id, - "expected_id" => *self.to_be_processed_id); + "processed_batch_id" => *batch.id, + "expected_id" => *self.to_be_processed_id); } - self.current_processing_id = None; - let res = match result { BatchProcessResult::Success => { *self.to_be_processed_id += 1; diff --git a/beacon_node/network/src/sync/range_sync/mod.rs b/beacon_node/network/src/sync/range_sync/mod.rs index 28cff24e31..5d7b17c07a 100644 --- a/beacon_node/network/src/sync/range_sync/mod.rs +++ b/beacon_node/network/src/sync/range_sync/mod.rs @@ -2,11 +2,10 @@ //! peers. mod batch; -mod batch_processing; mod chain; mod chain_collection; mod range; pub use batch::Batch; -pub use batch_processing::BatchProcessResult; +pub use batch::BatchId; pub use range::RangeSync; diff --git a/beacon_node/network/src/sync/range_sync/range.rs b/beacon_node/network/src/sync/range_sync/range.rs index 2d64342511..7e81af782b 100644 --- a/beacon_node/network/src/sync/range_sync/range.rs +++ b/beacon_node/network/src/sync/range_sync/range.rs @@ -41,8 +41,9 @@ use super::chain::ProcessingResult; use super::chain_collection::{ChainCollection, SyncState}; -use super::{Batch, BatchProcessResult}; +use super::BatchId; use crate::router::processor::PeerSyncInfo; +use crate::sync::block_processor::BatchProcessResult; use crate::sync::manager::SyncMessage; use crate::sync::network_context::SyncNetworkContext; use beacon_chain::{BeaconChain, BeaconChainTypes}; @@ -130,8 +131,8 @@ impl RangeSync { }, None => { return warn!(self.log, - "Beacon chain dropped. Peer not considered for sync"; - "peer_id" => format!("{:?}", peer_id)); + "Beacon chain dropped. Peer not considered for sync"; + "peer_id" => format!("{:?}", peer_id)); } }; @@ -256,15 +257,15 @@ impl RangeSync { pub fn handle_block_process_result( &mut self, network: &mut SyncNetworkContext, - processing_id: u64, - batch: Batch, + batch_id: BatchId, + downloaded_blocks: Vec>, result: BatchProcessResult, ) { - // build an option for passing the batch to each chain - let mut batch = Some(batch); + // build an option for passing the downloaded_blocks to each chain + let mut downloaded_blocks = Some(downloaded_blocks); match self.chains.finalized_request(|chain| { - chain.on_batch_process_result(network, processing_id, &mut batch, &result) + chain.on_batch_process_result(network, batch_id, &mut downloaded_blocks, &result) }) { Some((index, ProcessingResult::RemoveChain)) => { let chain = self.chains.remove_finalized_chain(index); @@ -293,7 +294,12 @@ impl RangeSync { Some((_, ProcessingResult::KeepChain)) => {} None => { match self.chains.head_request(|chain| { - chain.on_batch_process_result(network, processing_id, &mut batch, &result) + chain.on_batch_process_result( + network, + batch_id, + &mut downloaded_blocks, + &result, + ) }) { Some((index, ProcessingResult::RemoveChain)) => { let chain = self.chains.remove_head_chain(index); @@ -308,7 +314,7 @@ impl RangeSync { None => { // This can happen if a chain gets purged due to being out of date whilst a // batch process is in progress. - debug!(self.log, "No chains match the block processing id"; "id" => processing_id); + debug!(self.log, "No chains match the block processing id"; "id" => *batch_id); } } } diff --git a/beacon_node/rest_api/src/beacon.rs b/beacon_node/rest_api/src/beacon.rs index 69e3c61083..0df3673eed 100644 --- a/beacon_node/rest_api/src/beacon.rs +++ b/beacon_node/rest_api/src/beacon.rs @@ -438,7 +438,15 @@ pub fn get_genesis_time( req: Request, beacon_chain: Arc>, ) -> ApiResult { - ResponseBuilder::new(&req)?.body(&beacon_chain.head()?.beacon_state.genesis_time) + ResponseBuilder::new(&req)?.body(&beacon_chain.head_info()?.genesis_time) +} + +/// Read the `genesis_validators_root` from the current beacon chain state. +pub fn get_genesis_validators_root( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { + ResponseBuilder::new(&req)?.body(&beacon_chain.head_info()?.genesis_validators_root) } pub fn proposer_slashing( diff --git a/beacon_node/rest_api/src/consensus.rs b/beacon_node/rest_api/src/consensus.rs index 5d3749fc0c..64b5a5df32 100644 --- a/beacon_node/rest_api/src/consensus.rs +++ b/beacon_node/rest_api/src/consensus.rs @@ -4,11 +4,12 @@ use crate::{ApiError, ApiResult, BoxFut, UrlQuery}; use beacon_chain::{BeaconChain, BeaconChainTypes}; use futures::{Future, Stream}; use hyper::{Body, Request}; +use rest_types::{IndividualVotesRequest, IndividualVotesResponse}; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; -use state_processing::per_epoch_processing::{TotalBalances, ValidatorStatus, ValidatorStatuses}; +use state_processing::per_epoch_processing::{TotalBalances, ValidatorStatuses}; use std::sync::Arc; -use types::{Epoch, EthSpec, PublicKeyBytes}; +use types::EthSpec; /// The results of validators voting during an epoch. /// @@ -37,13 +38,13 @@ pub struct VoteCount { impl Into for TotalBalances { fn into(self) -> VoteCount { VoteCount { - current_epoch_active_gwei: self.current_epoch, - previous_epoch_active_gwei: self.previous_epoch, - current_epoch_attesting_gwei: self.current_epoch_attesters, - current_epoch_target_attesting_gwei: self.current_epoch_target_attesters, - previous_epoch_attesting_gwei: self.previous_epoch_attesters, - previous_epoch_target_attesting_gwei: self.previous_epoch_target_attesters, - previous_epoch_head_attesting_gwei: self.previous_epoch_head_attesters, + current_epoch_active_gwei: self.current_epoch(), + previous_epoch_active_gwei: self.previous_epoch(), + current_epoch_attesting_gwei: self.current_epoch_attesters(), + current_epoch_target_attesting_gwei: self.current_epoch_target_attesters(), + previous_epoch_attesting_gwei: self.previous_epoch_attesters(), + previous_epoch_target_attesting_gwei: self.previous_epoch_target_attesters(), + previous_epoch_head_attesting_gwei: self.previous_epoch_head_attesters(), } } } @@ -70,68 +71,6 @@ pub fn get_vote_count( ResponseBuilder::new(&req)?.body(&report) } -#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)] -pub struct IndividualVotesRequest { - pub epoch: Epoch, - pub pubkeys: Vec, -} - -#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)] -pub struct IndividualVote { - /// True if the validator has been slashed, ever. - pub is_slashed: bool, - /// True if the validator can withdraw in the current epoch. - pub is_withdrawable_in_current_epoch: bool, - /// True if the validator was active in the state's _current_ epoch. - pub is_active_in_current_epoch: bool, - /// True if the validator was active in the state's _previous_ epoch. - pub is_active_in_previous_epoch: bool, - /// The validator's effective balance in the _current_ epoch. - pub current_epoch_effective_balance_gwei: u64, - /// True if the validator had an attestation included in the _current_ epoch. - pub is_current_epoch_attester: bool, - /// True if the validator's beacon block root attestation for the first slot of the _current_ - /// epoch matches the block root known to the state. - pub is_current_epoch_target_attester: bool, - /// True if the validator had an attestation included in the _previous_ epoch. - pub is_previous_epoch_attester: bool, - /// True if the validator's beacon block root attestation for the first slot of the _previous_ - /// epoch matches the block root known to the state. - pub is_previous_epoch_target_attester: bool, - /// True if the validator's beacon block root attestation in the _previous_ epoch at the - /// attestation's slot (`attestation_data.slot`) matches the block root known to the state. - pub is_previous_epoch_head_attester: bool, -} - -impl Into for ValidatorStatus { - fn into(self) -> IndividualVote { - IndividualVote { - is_slashed: self.is_slashed, - is_withdrawable_in_current_epoch: self.is_withdrawable_in_current_epoch, - is_active_in_current_epoch: self.is_active_in_current_epoch, - is_active_in_previous_epoch: self.is_active_in_previous_epoch, - current_epoch_effective_balance_gwei: self.current_epoch_effective_balance, - is_current_epoch_attester: self.is_current_epoch_attester, - is_current_epoch_target_attester: self.is_current_epoch_target_attester, - is_previous_epoch_attester: self.is_previous_epoch_attester, - is_previous_epoch_target_attester: self.is_previous_epoch_target_attester, - is_previous_epoch_head_attester: self.is_previous_epoch_head_attester, - } - } -} - -#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)] -pub struct IndividualVotesResponse { - /// The epoch which is considered the "current" epoch. - pub epoch: Epoch, - /// The validators public key. - pub pubkey: PublicKeyBytes, - /// The index of the validator in state.validators. - pub validator_index: Option, - /// Voting statistics for the validator, if they voted in the given epoch. - pub vote: Option, -} - pub fn post_individual_votes( req: Request, beacon_chain: Arc>, @@ -156,12 +95,16 @@ pub fn post_individual_votes( // This is the last slot of the given epoch (one prior to the first slot of the next epoch). let target_slot = (epoch + 1).start_slot(T::EthSpec::slots_per_epoch()) - 1; - let (_root, state) = state_at_slot(&beacon_chain, target_slot)?; + let (_root, mut state) = state_at_slot(&beacon_chain, target_slot)?; let spec = &beacon_chain.spec; let mut validator_statuses = ValidatorStatuses::new(&state, spec)?; validator_statuses.process_attestations(&state, spec)?; + state.update_pubkey_cache().map_err(|e| { + ApiError::ServerError(format!("Unable to build pubkey cache: {:?}", e)) + })?; + body.pubkeys .into_iter() .map(|pubkey| { diff --git a/beacon_node/rest_api/src/router.rs b/beacon_node/rest_api/src/router.rs index 783fd3efff..f086cd1ff7 100644 --- a/beacon_node/rest_api/src/router.rs +++ b/beacon_node/rest_api/src/router.rs @@ -82,6 +82,9 @@ pub fn route( (&Method::GET, "/beacon/genesis_time") => { into_boxfut(beacon::get_genesis_time::(req, beacon_chain)) } + (&Method::GET, "/beacon/genesis_validators_root") => { + into_boxfut(beacon::get_genesis_validators_root::(req, beacon_chain)) + } (&Method::GET, "/beacon/validators") => { into_boxfut(beacon::get_validators::(req, beacon_chain)) } diff --git a/beacon_node/rest_api/src/validator.rs b/beacon_node/rest_api/src/validator.rs index af04bbd969..55ab8ccefc 100644 --- a/beacon_node/rest_api/src/validator.rs +++ b/beacon_node/rest_api/src/validator.rs @@ -209,19 +209,12 @@ fn return_validator_duties( // The `beacon_chain` can return a validator index that does not exist in all states. // Therefore, we must check to ensure that the validator index is valid for our // `state`. - let validator_index = if let Some(i) = beacon_chain + let validator_index = beacon_chain .validator_index(&validator_pubkey) .map_err(|e| { - ApiError::ServerError(format!("Unable to get validator index: {:?}", e)) - })? { - if i < state.validators.len() { - Some(i) - } else { - None - } - } else { - None - }; + ApiError::ServerError(format!("Unable to get validator index: {:?}", e)) + })? + .filter(|i| *i < state.validators.len()); if let Some(validator_index) = validator_index { let duties = state @@ -554,6 +547,7 @@ pub fn publish_aggregate_and_proofs( // TODO: More efficient way of getting a fork? let fork = &beacon_chain.head()?.beacon_state.fork; + // TODO: Update to shift this task to dedicated task using await signed_proofs.par_iter().try_for_each(|signed_proof| { let agg_proof = &signed_proof.message; let validator_pubkey = &beacon_chain.validator_pubkey(agg_proof.aggregator_index as usize)?.ok_or_else(|| { @@ -573,7 +567,7 @@ pub fn publish_aggregate_and_proofs( * I (Paul H) will pick this up in a future PR. */ - if signed_proof.is_valid(validator_pubkey, fork, &beacon_chain.spec) { + if signed_proof.is_valid(validator_pubkey, fork, beacon_chain.genesis_validators_root, &beacon_chain.spec) { let attestation = &agg_proof.aggregate; match beacon_chain.process_attestation(attestation.clone(), AttestationType::Aggregated) { diff --git a/beacon_node/rest_api/tests/test.rs b/beacon_node/rest_api/tests/test.rs index 165381d31d..9f34dc61d9 100644 --- a/beacon_node/rest_api/tests/test.rs +++ b/beacon_node/rest_api/tests/test.rs @@ -48,17 +48,15 @@ fn get_randao_reveal( slot: Slot, spec: &ChainSpec, ) -> Signature { - let fork = beacon_chain - .head() - .expect("should get head") - .beacon_state - .fork; + let head = beacon_chain.head().expect("should get head"); + let fork = head.beacon_state.fork; + let genesis_validators_root = head.beacon_state.genesis_validators_root; let proposer_index = beacon_chain .block_proposer(slot) .expect("should get proposer index"); let keypair = generate_deterministic_keypair(proposer_index); let epoch = slot.epoch(E::slots_per_epoch()); - let domain = spec.get_domain(epoch, Domain::Randao, &fork); + let domain = spec.get_domain(epoch, Domain::Randao, &fork, genesis_validators_root); let message = epoch.signing_root(domain); Signature::new(message.as_bytes(), &keypair.sk) } @@ -69,16 +67,14 @@ fn sign_block( block: BeaconBlock, spec: &ChainSpec, ) -> SignedBeaconBlock { - let fork = beacon_chain - .head() - .expect("should get head") - .beacon_state - .fork; + let head = beacon_chain.head().expect("should get head"); + let fork = head.beacon_state.fork; + let genesis_validators_root = head.beacon_state.genesis_validators_root; let proposer_index = beacon_chain .block_proposer(block.slot) .expect("should get proposer index"); let keypair = generate_deterministic_keypair(proposer_index); - block.sign(&keypair.sk, &fork, spec) + block.sign(&keypair.sk, &fork, genesis_validators_root, spec) } #[test] @@ -94,6 +90,7 @@ fn validator_produce_attestation() { .client .beacon_chain() .expect("client should have beacon chain"); + let genesis_validators_root = beacon_chain.genesis_validators_root; let state = beacon_chain.head().expect("should get head").beacon_state; let validator_index = 0; @@ -192,6 +189,7 @@ fn validator_produce_attestation() { .attestation_committee_position .expect("should have committee position"), &state.fork, + state.genesis_validators_root, spec, ) .expect("should sign attestation"); @@ -228,6 +226,7 @@ fn validator_produce_attestation() { aggregated_attestation, &keypair.sk, &state.fork, + genesis_validators_root, spec, ); @@ -635,6 +634,31 @@ fn genesis_time() { ); } +#[test] +fn genesis_validators_root() { + let mut env = build_env(); + + let node = build_node(&mut env, testing_client_config()); + let remote_node = node.remote_node().expect("should produce remote node"); + + let genesis_validators_root = env + .runtime() + .block_on(remote_node.http.beacon().get_genesis_validators_root()) + .expect("should fetch genesis time from http api"); + + assert_eq!( + node.client + .beacon_chain() + .expect("should have beacon chain") + .head() + .expect("should get head") + .beacon_state + .genesis_validators_root, + genesis_validators_root, + "should match genesis time from head state" + ); +} + #[test] fn fork() { let mut env = build_env(); @@ -974,6 +998,7 @@ fn proposer_slashing() { proposer_index as u64, &key, fork, + state.genesis_validators_root, spec, ); @@ -998,6 +1023,7 @@ fn proposer_slashing() { proposer_index as u64, &key, fork, + state.genesis_validators_root, spec, ); invalid_proposer_slashing.signed_header_2 = invalid_proposer_slashing.signed_header_1.clone(); @@ -1052,6 +1078,7 @@ fn attester_slashing() { &validator_indices[..], &secret_keys[..], fork, + state.genesis_validators_root, spec, ); @@ -1077,6 +1104,7 @@ fn attester_slashing() { &validator_indices[..], &secret_keys[..], fork, + state.genesis_validators_root, spec, ); invalid_attester_slashing.attestation_2 = invalid_attester_slashing.attestation_1.clone(); diff --git a/beacon_node/src/cli.rs b/beacon_node/src/cli.rs index a8bed30032..01a168af91 100644 --- a/beacon_node/src/cli.rs +++ b/beacon_node/src/cli.rs @@ -218,7 +218,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { Arg::with_name("eth1-endpoint") .long("eth1-endpoint") .value_name("HTTP-ENDPOINT") - .help("Specifies the server for a web3 connection to the Eth1 chain.") + .help("Specifies the server for a web3 connection to the Eth1 chain. Also enables the --eth1 flag.") .takes_value(true) .default_value("http://127.0.0.1:8545") ) @@ -339,14 +339,5 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { .required(true) .help("A file from which to read the state")) ) - /* - * `prysm` - * - * Connect to the Prysmatic Labs testnet. - */ - .subcommand(SubCommand::with_name("prysm") - .about("Connect to the Prysmatic Labs testnet on Goerli. Not guaranteed to be \ - up-to-date or functioning.") - ) ) } diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index d09f937991..022913ba31 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -1,5 +1,6 @@ use clap::ArgMatches; use client::{config::DEFAULT_DATADIR, ClientConfig, ClientGenesis, Eth2Config}; +use environment::ETH2_CONFIG_FILENAME; use eth2_config::{read_from_file, write_to_file}; use eth2_libp2p::{Enr, Multiaddr}; use eth2_testnet_config::Eth2TestnetConfig; @@ -14,14 +15,12 @@ use std::path::PathBuf; use types::EthSpec; pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml"; -pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml"; pub const BEACON_NODE_DIR: &str = "beacon"; pub const NETWORK_DIR: &str = "network"; type Result = std::result::Result; -type Config = (ClientConfig, Eth2Config, Logger); -/// Gets the fully-initialized global client and eth2 configuration objects. +/// Gets the fully-initialized global client. /// /// The top-level `clap` arguments should be provided as `cli_args`. /// @@ -29,26 +28,17 @@ type Config = (ClientConfig, Eth2Config, Logger); /// may be influenced by other external services like the contents of the file system or the /// response of some remote server. #[allow(clippy::cognitive_complexity)] -pub fn get_configs( +pub fn get_config( cli_args: &ArgMatches, - mut eth2_config: Eth2Config, + eth2_config: Eth2Config, core_log: Logger, -) -> Result { +) -> Result { let log = core_log.clone(); let mut client_config = ClientConfig::default(); client_config.spec_constants = eth2_config.spec_constants.clone(); - - // Read the `--datadir` flag. - // - // If it's not present, try and find the home directory (`~`) and push the default data - // directory onto it. - client_config.data_dir = cli_args - .value_of("datadir") - .map(|path| PathBuf::from(path).join(BEACON_NODE_DIR)) - .or_else(|| dirs::home_dir().map(|home| home.join(DEFAULT_DATADIR).join(BEACON_NODE_DIR))) - .unwrap_or_else(|| PathBuf::from(".")); + client_config.data_dir = get_data_dir(cli_args); // Load the client config, if it exists . let path = client_config.data_dir.join(CLIENT_CONFIG_FILENAME); @@ -58,32 +48,7 @@ pub fn get_configs( .ok_or_else(|| format!("{:?} file does not exist", path))?; } - // Load the eth2 config, if it exists . - let path = client_config.data_dir.join(ETH2_CONFIG_FILENAME); - if path.exists() { - let loaded_eth2_config: Eth2Config = read_from_file(path.clone()) - .map_err(|e| format!("Unable to parse {:?} file: {:?}", path, e))? - .ok_or_else(|| format!("{:?} file does not exist", path))?; - - // The loaded spec must be using the same spec constants (e.g., minimal, mainnet) as the - // client expects. - if loaded_eth2_config.spec_constants == client_config.spec_constants { - eth2_config = loaded_eth2_config - } else { - return Err( - format!( - "Eth2 config loaded from disk does not match client spec version. Got {} expected {}", - &loaded_eth2_config.spec_constants, - &client_config.spec_constants - ) - ); - } - } - - // Read the `--testnet-dir` flag. - if let Some(val) = cli_args.value_of("testnet-dir") { - client_config.testnet_dir = Some(PathBuf::from(val)); - } + client_config.testnet_dir = get_testnet_dir(cli_args); /* * Networking @@ -251,12 +216,13 @@ pub fn get_configs( // Defines the URL to reach the eth1 node. if let Some(val) = cli_args.value_of("eth1-endpoint") { + client_config.sync_eth1_chain = true; client_config.eth1.endpoint = val.to_string(); } match cli_args.subcommand() { ("testnet", Some(sub_cmd_args)) => { - process_testnet_subcommand(&mut client_config, &mut eth2_config, sub_cmd_args)? + process_testnet_subcommand(&mut client_config, ð2_config, sub_cmd_args)? } // No sub-command assumes a resume operation. _ => { @@ -272,7 +238,7 @@ pub fn get_configs( "Starting from an empty database"; "data_dir" => format!("{:?}", client_config.data_dir) ); - init_new_client::(&mut client_config, &mut eth2_config)? + init_new_client::(&mut client_config, ð2_config)? } else { info!( log, @@ -342,7 +308,42 @@ pub fn get_configs( client_config.websocket_server.port = 0; } - Ok((client_config, eth2_config, log)) + Ok(client_config) +} + +/// Gets the datadir which should be used. +pub fn get_data_dir(cli_args: &ArgMatches) -> PathBuf { + // Read the `--datadir` flag. + // + // If it's not present, try and find the home directory (`~`) and push the default data + // directory onto it. + cli_args + .value_of("datadir") + .map(|path| PathBuf::from(path).join(BEACON_NODE_DIR)) + .or_else(|| dirs::home_dir().map(|home| home.join(DEFAULT_DATADIR).join(BEACON_NODE_DIR))) + .unwrap_or_else(|| PathBuf::from(".")) +} + +/// Gets the testnet dir which should be used. +pub fn get_testnet_dir(cli_args: &ArgMatches) -> Option { + // Read the `--testnet-dir` flag. + if let Some(val) = cli_args.value_of("testnet-dir") { + Some(PathBuf::from(val)) + } else { + None + } +} + +pub fn get_eth2_testnet_config( + testnet_dir: &Option, +) -> Result> { + Ok(if let Some(testnet_dir) = testnet_dir { + Eth2TestnetConfig::load(testnet_dir.clone()) + .map_err(|e| format!("Unable to open testnet dir at {:?}: {}", testnet_dir, e))? + } else { + Eth2TestnetConfig::hard_coded() + .map_err(|e| format!("Unable to load hard-coded testnet dir: {}", e))? + }) } /// Load from an existing database. @@ -377,37 +378,17 @@ fn load_from_datadir(client_config: &mut ClientConfig) -> Result<()> { /// Create a new client with the default configuration. fn init_new_client( client_config: &mut ClientConfig, - eth2_config: &mut Eth2Config, + eth2_config: &Eth2Config, ) -> Result<()> { let eth2_testnet_config: Eth2TestnetConfig = - if let Some(testnet_dir) = &client_config.testnet_dir { - Eth2TestnetConfig::load(testnet_dir.clone()) - .map_err(|e| format!("Unable to open testnet dir at {:?}: {}", testnet_dir, e))? - } else { - Eth2TestnetConfig::hard_coded() - .map_err(|e| format!("Unable to load hard-coded testnet dir: {}", e))? - }; - - eth2_config.spec = eth2_testnet_config - .yaml_config - .as_ref() - .ok_or_else(|| "The testnet directory must contain a spec config".to_string())? - .apply_to_chain_spec::(ð2_config.spec) - .ok_or_else(|| { - format!( - "The loaded config is not compatible with the {} spec", - ð2_config.spec_constants - ) - })?; - - let spec = &mut eth2_config.spec; + get_eth2_testnet_config(&client_config.testnet_dir)?; client_config.eth1.deposit_contract_address = format!("{:?}", eth2_testnet_config.deposit_contract_address()?); client_config.eth1.deposit_contract_deploy_block = eth2_testnet_config.deposit_contract_deploy_block; - client_config.eth1.follow_distance = spec.eth1_follow_distance / 2; + client_config.eth1.follow_distance = eth2_config.spec.eth1_follow_distance / 2; client_config.eth1.lowest_cached_block_number = client_config .eth1 .deposit_contract_deploy_block @@ -470,7 +451,7 @@ pub fn create_new_datadir(client_config: &ClientConfig, eth2_config: &Eth2Config /// Process the `testnet` CLI subcommand arguments, updating the `builder`. fn process_testnet_subcommand( client_config: &mut ClientConfig, - eth2_config: &mut Eth2Config, + eth2_config: &Eth2Config, cli_args: &ArgMatches, ) -> Result<()> { // Specifies that a random datadir should be used. @@ -501,15 +482,6 @@ fn process_testnet_subcommand( client_config.network.propagation_percentage = Some(percentage); } - // Modify the `SECONDS_PER_SLOT` "constant". - if let Some(slot_time) = cli_args.value_of("slot-time") { - let slot_time = slot_time - .parse::() - .map_err(|e| format!("Unable to parse slot-time: {:?}", e))?; - - eth2_config.spec.milliseconds_per_slot = slot_time; - } - // Start matching on the second subcommand (e.g., `testnet bootstrap ...`). match cli_args.subcommand() { ("recent", Some(cli_args)) => { @@ -570,24 +542,6 @@ fn process_testnet_subcommand( client_config.genesis = start_method; } - ("prysm", Some(_)) => { - let mut spec = &mut eth2_config.spec; - - spec.min_deposit_amount = 100; - spec.max_effective_balance = 3_200_000_000; - spec.ejection_balance = 1_600_000_000; - spec.effective_balance_increment = 100_000_000; - spec.min_genesis_time = 0; - spec.genesis_fork_version = [0, 0, 0, 2]; - - client_config.eth1.deposit_contract_address = - "0x802dF6aAaCe28B2EEb1656bb18dF430dDC42cc2e".to_string(); - client_config.eth1.deposit_contract_deploy_block = 1_487_270; - client_config.eth1.follow_distance = 16; - client_config.dummy_eth1_backend = false; - - client_config.genesis = ClientGenesis::DepositContract; - } (cmd, Some(_)) => { return Err(format!( "Invalid valid method specified: {}. See 'testnet --help'.", diff --git a/beacon_node/src/lib.rs b/beacon_node/src/lib.rs index 4774ee8963..7a1dbd92a4 100644 --- a/beacon_node/src/lib.rs +++ b/beacon_node/src/lib.rs @@ -7,6 +7,7 @@ mod config; pub use beacon_chain; pub use cli::cli_app; pub use client::{Client, ClientBuilder, ClientConfig, ClientGenesis}; +pub use config::{get_data_dir, get_eth2_testnet_config, get_testnet_dir}; pub use eth2_config::Eth2Config; use beacon_chain::{ @@ -14,7 +15,7 @@ use beacon_chain::{ slot_clock::SystemTimeSlotClock, }; use clap::ArgMatches; -use config::get_configs; +use config::get_config; use environment::RuntimeContext; use futures::{Future, IntoFuture}; use slog::{info, warn}; @@ -51,20 +52,12 @@ impl ProductionBeaconNode { /// given `matches` and potentially configuration files on the local filesystem or other /// configurations hosted remotely. pub fn new_from_cli<'a, 'b>( - mut context: RuntimeContext, + context: RuntimeContext, matches: &ArgMatches<'b>, ) -> impl Future + 'a { - let log = context.log.clone(); - - // TODO: the eth2 config in the env is being modified. - // - // See https://github.com/sigp/lighthouse/issues/602 - get_configs::(&matches, context.eth2_config.clone(), log) + get_config::(&matches, context.eth2_config.clone(), context.log.clone()) .into_future() - .and_then(move |(client_config, eth2_config, _log)| { - context.eth2_config = eth2_config; - Self::new(context, client_config) - }) + .and_then(move |client_config| Self::new(context, client_config)) } /// Starts a new beacon node `Client` in the given `environment`. diff --git a/beacon_node/store/src/hot_cold_store.rs b/beacon_node/store/src/hot_cold_store.rs index 0c3b2bc95e..362b866ba7 100644 --- a/beacon_node/store/src/hot_cold_store.rs +++ b/beacon_node/store/src/hot_cold_store.rs @@ -10,7 +10,7 @@ use crate::{ leveldb_store::LevelDB, DBColumn, Error, PartialBeaconState, SimpleStoreItem, Store, StoreItem, }; use lru::LruCache; -use parking_lot::RwLock; +use parking_lot::{Mutex, RwLock}; use slog::{debug, trace, warn, Logger}; use ssz::{Decode, Encode}; use ssz_derive::{Decode, Encode}; @@ -45,7 +45,7 @@ pub struct HotColdDB { /// The hot database also contains all blocks. pub(crate) hot_db: LevelDB, /// LRU cache of deserialized blocks. Updated whenever a block is loaded. - block_cache: RwLock>>, + block_cache: Mutex>>, /// Chain spec. spec: ChainSpec, /// Logger. @@ -109,7 +109,7 @@ impl Store for HotColdDB { self.put(block_root, &block)?; // Update cache. - self.block_cache.write().put(*block_root, block); + self.block_cache.lock().put(*block_root, block); Ok(()) } @@ -119,7 +119,7 @@ impl Store for HotColdDB { metrics::inc_counter(&metrics::BEACON_BLOCK_GET_COUNT); // Check the cache. - if let Some(block) = self.block_cache.write().get(block_root) { + if let Some(block) = self.block_cache.lock().get(block_root) { metrics::inc_counter(&metrics::BEACON_BLOCK_CACHE_HIT_COUNT); return Ok(Some(block.clone())); } @@ -128,7 +128,7 @@ impl Store for HotColdDB { match self.get::>(block_root)? { Some(block) => { // Add to cache. - self.block_cache.write().put(*block_root, block.clone()); + self.block_cache.lock().put(*block_root, block.clone()); Ok(Some(block)) } None => Ok(None), @@ -137,7 +137,7 @@ impl Store for HotColdDB { /// Delete a block from the store and the block cache. fn delete_block(&self, block_root: &Hash256) -> Result<(), Error> { - self.block_cache.write().pop(block_root); + self.block_cache.lock().pop(block_root); self.delete::>(block_root) } @@ -338,7 +338,7 @@ impl HotColdDB { split: RwLock::new(Split::default()), cold_db: LevelDB::open(cold_path)?, hot_db: LevelDB::open(hot_path)?, - block_cache: RwLock::new(LruCache::new(config.block_cache_size)), + block_cache: Mutex::new(LruCache::new(config.block_cache_size)), config, spec, log, diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index 0af5c85a21..b0947a3cd2 100644 --- a/beacon_node/store/src/partial_beacon_state.rs +++ b/beacon_node/store/src/partial_beacon_state.rs @@ -11,7 +11,7 @@ use types::*; /// /// Utilises lazy-loading from separate storage for its vector fields. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Encode, Decode)] pub struct PartialBeaconState where @@ -19,6 +19,7 @@ where { // Versioning pub genesis_time: u64, + pub genesis_validators_root: Hash256, pub slot: Slot, pub fork: Fork, @@ -72,6 +73,7 @@ impl PartialBeaconState { // TODO: could use references/Cow for fields to avoid cloning PartialBeaconState { genesis_time: s.genesis_time, + genesis_validators_root: s.genesis_validators_root, slot: s.slot, fork: s.fork.clone(), @@ -181,6 +183,7 @@ impl TryInto> for PartialBeaconState { Ok(BeaconState { genesis_time: self.genesis_time, + genesis_validators_root: self.genesis_validators_root, slot: self.slot, fork: self.fork, diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index f9a2745e89..54546c3bc1 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -22,3 +22,4 @@ * [Database Configuration](./advanced_database.md) * [Contributing](./contributing.md) * [Development Environment](./setup.md) +* [FAQs](./faq.md) diff --git a/book/src/become-a-validator-docker.md b/book/src/become-a-validator-docker.md index 7fb03845da..29dc36045c 100644 --- a/book/src/become-a-validator-docker.md +++ b/book/src/become-a-validator-docker.md @@ -12,18 +12,28 @@ binary yourself. > experience with docker-compose to integrate your locally built docker image > with the docker-compose environment. -### 1. Clone the repository +## 0. Install Docker Compose -Once you have docker-compose -[installed](https://docs.docker.com/compose/install/), clone the -[sigp/lighthouse-docker](https://github.com/sigp/lighthouse-docker) repository. + Docker Compose relies on Docker Engine for any meaningful work, so make sure you have Docker Engine installed either locally or remote, depending on your setup. + +- On desktop systems like [Docker Desktop for Mac](https://docs.docker.com/docker-for-mac/install/) and [Docker Desktop for Windows](https://docs.docker.com/docker-for-windows/install/), Docker Compose is included as part of those desktop installs, so the desktop install is all you need. + +- On Linux systems, you'll need to first [install the Docker for your OS](https://docs.docker.com/install/#server) and then [follow the instuctions here](https://docs.docker.com/compose/install/#install-compose-on-linux-systems). + +> For more on installing Compose, see [here](https://docs.docker.com/compose/install/). + + +## 1. Clone the repository + +Once you have Docker Compose installed, clone the +[sigp/lighthouse-docker](https://github.com/sigp/lighthouse-docker) repository: ```bash -$ git clone https://github.com/sigp/lighthouse-docker -$ cd lighthouse-docker + git clone https://github.com/sigp/lighthouse-docker + cd lighthouse-docker ``` -### 2. Configure the docker environment +## 2. Configure the Docker environment Then, create a file named `.env` with the following contents (these values are documented @@ -41,36 +51,58 @@ DEPOSIT_VALUE=3200000000 _This `.env` file should live in the `lighthouse-docker` directory alongside the `docker-compose.yml` file_. -### 3. Start Lighthouse +## 3. Start Lighthouse -Start the docker-compose environment (you may need to use `sudo`): +Start the docker-compose environment (you may need to prefix the below command with `sudo`): ```bash -$ docker-compose up + docker-compose up ``` Watch the output of this command for the `Saved new validator to disk` log, as -the `voting_pubkey` is the primary identifier for your new validator. This is -useful for finding your validator in block explorers. Here's an example of the -log: +it contains your `voting_pubkey` -- the primary identifier for your new validator. This key is useful for finding your validator in block explorers. Here's an example of the log: ```bash -validator_client_1 | Jan 10 12:06:05.632 INFO Saved new validator to disk voting_pubkey: 0x8fc28504448783b10b0a7f5a321505b07ad2ad8d6a8430b8868a0fcdedee43766bee725855506626085776e020dfa472 +validator_client_1 | Jan 10 12:06:05.632 INFO Saved new validator to disk +voting_pubkey: 0x8fc28504448783b10b0a7f5a321505b07ad2ad8d6a8430b8868a0fcdedee43766bee725855506626085776e020dfa472 ``` +This is one of the first logs outputted, so you may have to scroll up or perform a search in your terminal to find it. -> Note: the docker-compose setup includes a fast-synced geth node. You can +> Note: `docker-compose up` generates a new sub-directory -- to store your validator's deposit data, along with its voting and withdrawal keys -- in the `.lighthouse/validators` directory. This sub-directory is identified by your validator's `voting_pubkey` (the same `voting_pubkey` you see in the logs). So this is another way you can find it. + +> Note: the docker-compose setup includes a fast-synced geth node. So you can > expect the `beacon_node` to log some eth1-related errors whilst the geth node > boots and becomes synced. This will only happen on the first start of the > compose environment or if geth loses sync. -### Installation complete! +To find an estimate for how long your beacon node will take to finish syncing, look for logs that look like this: -In the next step you'll need to locate your `eth1_deposit_data.rlp` file from -your `.lighthouse/validators` directory. +```bash +beacon_node_1 | Mar 16 11:33:53.979 INFO Syncing +est_time: 47 mins, speed: 16.67 slots/sec, distance: 47296 slots (7 days 14 hrs), peers: 3, service: slot_notifier +``` -The `./lighthouse` directory is in the root of the `lighthouse-docker` -repository. For example, if you ran Step 1 in `/home/karlm/` then you can find -your validator directory in -`/home/karlm/lighthouse-docker/.lighthouse/validators/`. +You'll find the estimated time under `est_time`. In the example above, that's `47 mins`. -You can now go to [Become a Validator: Step 2](become-a-validator.html#2-submit-your-deposit-to-goerli). +If your beacon node hasn't finished syncing yet, you'll see some ERRO messages indicating that your node hasn't synced yet: + +```bash +validator_client_1 | Mar 16 11:34:36.086 ERRO Beacon node is not synced current_epoch: 6999, node_head_epoch: 5531, service: duties +``` + +It's safest to wait for your node to sync before moving on to the next step, otherwise your validator may activate before you're able to produce blocks and attestations (and you may be penalized as a result). + +However, since it generally takes somewhere between [4 and 8 hours](./faq.md) after depositing for a validator to become active, if your `est_time` is less than 4 hours, you _should_ be fine to just move on to the next step. After all, this is a testnet and you're only risking Goerli ETH! + +## Installation complete! + +In the [next step](become-a-validator.html#2-submit-your-deposit-to-goerli) you'll need to upload your validator's deposit data. This data is stored in a file called `eth1_deposit_data.rlp`. + +You'll find it in `lighthouse-docker/.lighthouse/validators/` -- in the sub-directory that corresponds to your validator's public key (`voting_pubkey`). + + +> For example, if you ran [step 1](become-a-validator-docker.html#1-clone-the-repository) in `/home/karlm/`, and your validator's `voting_pubkey` is `0x8592c7..`, then you'll find your `eth1_deposit_data.rlp` file in the following directory: +> +>`/home/karlm/lighthouse-docker/.lighthouse/validators/0x8592c7../` + +Once you've located `eth1_deposit_data.rlp`, you're ready to move on to [Become a Validator: Step 2](become-a-validator.html#2-submit-your-deposit-to-goerli). diff --git a/book/src/become-a-validator-source.md b/book/src/become-a-validator-source.md index d700ee6968..09480519dd 100644 --- a/book/src/become-a-validator-source.md +++ b/book/src/become-a-validator-source.md @@ -1,103 +1,106 @@ -# Become an Validator: Building from Source +# Become a Validator: Building from Source + +## 0. Install Rust +If you don't have Rust installed already, visit [rustup.rs](https://rustup.rs/) to install it. + +> Note: if you're not familiar with Rust or you'd like more detailed instructions, see our [installation guide](./installation.md). + ## 1. Download and install Lighthouse -If you already have Rust installed, you can install Lighthouse with the -following commands (don't forget to use the `testnet5` branch): +Once you have Rust installed, you can install Lighthouse with the following commands (don't forget to use the `testnet5` branch): -- `$ git clone https://github.com/sigp/lighthouse.git` -- `$ git checkout testnet5` -- `$ cd lighthouse` -- `$ make` +1. `git clone https://github.com/sigp/lighthouse.git` +2. `cd lighthouse` +3. `git checkout testnet5` +4. `make` + +You may need to open a new terminal window before running `make`. You've completed this step when you can run `$ lighthouse --help` and see the help menu. -> - If you're not familiar with Rust or you'd like more detailed instructions, -> see the [Installation Guide](./installation.md) which contains a -> [Troubleshooting](installation.html#troubleshooting) section. ## 2. Start an Eth1 client -As Eth2 relies upon the Eth1 chain for validator on-boarding and eventually -Eth1 may use the Eth2 chain as a finality gadget, all Eth2 validators must have -a connection to an Eth1 node. +Since Eth2 relies upon the Eth1 chain for validator on-boarding, all Eth2 validators must have a connection to an Eth1 node. -We provide instructions for using Geth (this is, by chance, what we ended up -testing with), but you could use any client that implements the JSON RPC via -HTTP. At least for Geth, a fast-synced node is sufficient. +We provide instructions for using Geth (the Eth1 client that, by chance, we ended up testing with), but you could use any client that implements the JSON RPC via HTTP. A fast-synced node should be sufficient. + +### Installing Geth +If you're using a Mac, follow the instructions [listed here](https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-Mac) to install geth. Otherwise [see here](https://github.com/ethereum/go-ethereum/wiki/Installing-Geth). ### Starting Geth -[Install geth](https://github.com/ethereum/go-ethereum/wiki/Installing-Geth) -and then use this command (or equivalent) to start your Eth1 node: +Once you have geth installed, use this command to start your Eth1 node: ```bash -$ geth --goerli --rpc + geth --goerli --rpc ``` -## 3. Start your Beacon Node +## 3. Start your beacon node The beacon node is the core component of Eth2, it connects to other peers over -the Internet and maintains a view of the chain. +the internet and maintains a view of the chain. Start your beacon node with: ```bash -$ lighthouse beacon --eth1 --http + lighthouse beacon --eth1 --http ``` +>Note: the `--http` flag enables the HTTP API for the validator client. And the `--eth1` flag tells the beacon node that it should sync with an Ethereum1 node (e.g. Geth). These flags are only required if you wish to run a validator. + + Your beacon node has started syncing when you see the following (truncated) log: ``` -Dec 09 12:57:18.026 INFO Syncing est_time: 2 hrs ... +Dec 09 12:57:18.026 INFO Syncing +est_time: 2 hrs ... ``` The `distance` value reports the time since eth2 genesis, whilst the `est_time` reports an estimate of how long it will take your node to become synced. -It has finished syncing once you see the following (truncated) log: +You'll know it's finished syncing once you see the following (truncated) log: ``` -Dec 09 12:27:06.010 INFO Synced slot: 16835, ... +Dec 09 12:27:06.010 INFO Synced +slot: 16835, ... ``` -> - The `--http` flag enables the HTTP API for the validator client. -> - The `--eth1` flag tells the beacon node that it should sync with an Ethereum -> 1 node (e.g., Geth). This is only required if you wish to run a validator. ## 4. Generate your validator key Generate new validator BLS keypairs using: -```shell -$ lighthouse account validator new random +```bash + lighthouse account validator new random ``` -Take note of the `voting_pubkey` of the new validator. This will be the primary -identifier of the validator. This is how you can find your validator in block -explorers. +Take note of the `voting_pubkey` of the new validator: -You've completed this step when you see the equivalent line: +``` +INFO Saved new validator to disk +voting_pubkey: 0xa1625249d80... +``` + +It's the validator's primary identifier, and will be used to find your validator in block explorers. + +You've completed this step when you see something like the following line: ``` Dec 02 21:42:01.337 INFO Generated validator directories count: 1, base_path: "/home/karl/.lighthouse/validators" ``` -> - This will generate a new _validator directory_ in the `.lighthouse/validators` -> directory. Your validator directory will be identified by it's public key, -> which looks something like `0xc483de...`. You'll need to find this directory -> for the next step. -> - These keys are good enough for the Lighthouse testnet, however they shouldn't -> be considered secure until we've undergone a security audit (planned Jan -> 2020). +This means you've successfully generated a new sub-directory for your validator in the `.lighthouse/validators` directory. The sub-directory is identified by your validator's public key (`voting_pubkey`). And is used to store your validator's deposit data, along with its voting and withdrawal keys. + +> Note: these keypairs are good enough for the Lighthouse testnet, however they shouldn't be considered secure until we've undergone a security audit (planned March/April). ## 5. Start your validator client -For security reasons, the validator client runs separately to the beacon node. -The validator client stores private keys and signs messages generated by the -beacon node. +Since the validator client stores private keys and signs messages generated by the beacon node, for security reasons it runs separately from it. You'll need both your beacon node _and_ validator client running if you want to stake. @@ -105,32 +108,45 @@ stake. Start the validator client with: ```bash -$ lighthouse validator + lighthouse validator ``` -The validator client is running and has found your validator keys from step 3 -when you see the following log: +You know that your validator client is running and has found your validator keys from [step 3](become-a-validator-source.html#3-start-your-beacon-node) when you see the following logs: ``` Dec 09 13:08:59.171 INFO Loaded validator keypair store voting_validators: 1 Dec 09 13:09:09.000 INFO Awaiting activation slot: 17787, ... ``` + +To find an estimate for how long your beacon node will take to finish syncing, lookout for the following logs: + +```bash +beacon_node_1 | Mar 16 11:33:53.979 INFO Syncing +est_time: 47 mins, speed: 16.67 slots/sec, distance: 47296 slots (7 days 14 hrs), peers: 3, service: slot_notifier +``` + +You'll find the estimated time under `est_time`. In the example log above, that's `47 mins`. + If your beacon node hasn't finished syncing yet, you'll see some `ERRO` -messages indicating that your node isn't synced yet. It is safest to wait for -your node to sync before moving onto the next step, otherwise your validator -may activate before you're able to produce blocks and attestations. However, it -generally takes 4-8+ hours after deposit for a validator to become active. If -your `est_time` is less than 4 hours, you _should_ be fine to just move to the -next step. After all, this is a testnet and you're only risking Goerli ETH. +messages indicating that your node hasn't synced yet: + +```bash +validator_client_1 | Mar 16 11:34:36.086 ERRO Beacon node is not synced current_epoch: 6999, node_head_epoch: 5531, service: duties +``` + +It's safest to wait for your node to sync before moving on to the next step, otherwise your validator may activate before you're able to produce blocks and attestations (and you may be penalized as a result). + +However, since it generally takes somewhere between [4 and 8 hours](./faq.md) after depositing for a validator to become active, if your `est_time` is less than 4 hours, you _should_ be fine to just move on to the next step. After all, this is a testnet and you're only risking Goerli ETH! ## Installation complete! -In the next step you'll need to locate your `eth1_deposit_data.rlp` file from -your `.lighthouse/validators` directory. +In the [next step](become-a-validator.html#2-submit-your-deposit-to-goerli) you'll need to upload your validator's deposit data. This data is stored in a file called `eth1_deposit_data.rlp`. -The `./lighthouse` directory is in your `$HOME` directory. For example, if -you're in Linux and your user is `karlm`, you can find your validator directory -in `/home/karlm/.lighthouse/validators/`. +You'll find it in `/home/.lighthouse/validators` -- in the sub-directory that corresponds to your validator's public key (`voting_pubkey`). -You can now go to [Become a Validator: Step 2](become-a-validator.html#2-submit-your-deposit-to-goerli). +> For example, if your username is `karlm`, and your validator's public key (aka `voting_pubkey`) is `0x8592c7..`, then you'll find your `eth1_deposit_data.rlp` file in the following directory: +> +>`/home/karlm/.lighthouse/validators/0x8592c7../` + +Once you've located your `eth1_deposit_data.rlp` file, you're ready to move on to [Become a Validator: Step 2](become-a-validator.html#2-submit-your-deposit-to-goerli). diff --git a/book/src/become-a-validator.md b/book/src/become-a-validator.md index c86819390f..6427b414f4 100644 --- a/book/src/become-a-validator.md +++ b/book/src/become-a-validator.md @@ -1,30 +1,36 @@ # Become an Ethereum 2.0 Testnet Validator -Running Lighthouse validator is easy if you're familiar with the terminal. It -runs on Linux, MacOS and Windows and we have a Docker work-flow. +Running a Lighthouse validator is easy if you're familiar with the terminal. -Before you start, you'll need [Metamask](https://metamask.io/) and 3.2 gETH +Lighthouse runs on Linux, MacOS and Windows and has a Docker work-flow to make things as simple as possible. + + +## 0. Acquire Goerli ETH +Before you install Lighthouse, you'll need [Metamask](https://metamask.io/) and 3.2 gETH (Goerli ETH). We recommend the [mudit.blog faucet](https://faucet.goerli.mudit.blog/) for those familiar with Goerli, or [goerli.net](https://goerli.net/) for an overview of the testnet. +> If this is your first time using Metamask and/or interacting with an ethereum test network, we recommend going through the beginning of [this guide](https://hack.aragon.org/docs/guides-use-metamask) first (up to the *Signing your first transaction with MetaMask* section). + ## 1. Install and start Lighthouse There are two, different ways to install and start a Lighthouse validator: -- [Using `docker-compose`](./become-a-validator-docker.md): this is the easiest method. -- [Building from source](./become-a-validator-source.md): this is a little more involved, however it +1. [Using `docker-compose`](./become-a-validator-docker.md): this is the easiest method. + +2. [Building from source](./become-a-validator-source.md): this is a little more involved, however it gives a more hands-on experience. -Once you have completed **only one** of these steps, move onto the next step. +Once you've completed **either one** of these steps, you can move onto the next step. ## 2. Submit your deposit to Goerli