From 371e5adcf89d99a5958b802cf9925a990bd66ba6 Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Tue, 11 Feb 2020 10:19:36 +1100 Subject: [PATCH] Update to Spec v0.10 (#817) * Start updating types * WIP * Signature hacking * Existing EF tests passing with fake_crypto * Updates * Delete outdated API spec * The refactor continues * It compiles * WIP test fixes * All release tests passing bar genesis state parsing * Update and test YamlConfig * Update to spec v0.10 compatible BLS * Updates to BLS EF tests * Add EF test for AggregateVerify And delete unused hash2curve tests for uncompressed points * Update EF tests to v0.10.1 * Use optional block root correctly in block proc * Use genesis fork in deposit domain. All tests pass * Cargo fmt * Fast aggregate verify test * Update REST API docs * Cargo fmt * Fix unused import * Bump spec tags to v0.10.1 * Add `seconds_per_eth1_block` to chainspec * Update to timestamp based eth1 voting scheme * Return None from `get_votes_to_consider` if block cache is empty * Handle overflows in `is_candidate_block` * Revert to failing tests * Fix eth1 data sets test * Choose default vote according to spec * Fix collect_valid_votes tests * Fix `get_votes_to_consider` to choose all eligible blocks * Uncomment winning_vote tests * Add comments; remove unused code * Reduce seconds_per_eth1_block for simulation * Addressed review comments * Add test for default vote case * Fix logs * Remove unused functions * Meter default eth1 votes * Fix comments * Address review comments; remove unused dependency * Disable/delete two outdated tests * Bump eth1 default vote warn to error * Delete outdated eth1 test Co-authored-by: Pawan Dhananjay --- Cargo.lock | 21 +- README.md | 2 +- beacon_node/beacon_chain/src/beacon_chain.rs | 198 +- beacon_node/beacon_chain/src/builder.rs | 39 +- beacon_node/beacon_chain/src/checkpoint.rs | 10 +- beacon_node/beacon_chain/src/eth1_chain.rs | 609 ++--- beacon_node/beacon_chain/src/events.rs | 6 +- .../src/fork_choice/checkpoint_manager.rs | 7 +- beacon_node/beacon_chain/src/metrics.rs | 4 +- beacon_node/beacon_chain/src/test_utils.rs | 31 +- beacon_node/beacon_chain/tests/store_tests.rs | 8 +- beacon_node/beacon_chain/tests/tests.rs | 10 +- beacon_node/eth1/tests/test.rs | 85 - beacon_node/genesis/src/interop.rs | 13 +- beacon_node/network/src/message_handler.rs | 12 +- beacon_node/network/src/message_processor.rs | 27 +- beacon_node/network/src/sync/manager.rs | 20 +- .../network/src/sync/range_sync/batch.rs | 6 +- .../src/sync/range_sync/batch_processing.rs | 12 +- .../network/src/sync/range_sync/chain.rs | 42 +- .../network/src/sync/range_sync/range.rs | 4 +- beacon_node/rest_api/api_spec.yaml | 2064 ----------------- beacon_node/rest_api/src/beacon.rs | 37 +- beacon_node/rest_api/src/helpers.rs | 8 +- beacon_node/rest_api/src/validator.rs | 16 +- beacon_node/rest_api/tests/test.rs | 28 +- beacon_node/src/config.rs | 14 +- beacon_node/store/src/block_at_slot.rs | 194 -- beacon_node/store/src/hot_cold_store.rs | 20 +- beacon_node/store/src/impls.rs | 2 +- beacon_node/store/src/iter.rs | 20 +- beacon_node/store/src/lib.rs | 18 +- beacon_node/store/src/partial_beacon_state.rs | 2 +- book/src/http_beacon.md | 40 +- eth2/operation_pool/src/lib.rs | 10 +- eth2/operation_pool/src/persistence.rs | 4 +- .../src/common/get_attesting_indices.rs | 2 +- .../src/common/get_indexed_attestation.rs | 2 +- .../src/common/initiate_validator_exit.rs | 2 +- .../src/common/slash_validator.rs | 2 +- eth2/state_processing/src/genesis.rs | 8 +- .../src/per_block_processing.rs | 66 +- .../block_processing_builder.rs | 26 +- .../block_signature_verifier.rs | 27 +- .../src/per_block_processing/errors.rs | 5 + .../is_valid_indexed_attestation.rs | 11 +- .../per_block_processing/signature_sets.rs | 86 +- .../src/per_block_processing/tests.rs | 102 +- .../verify_attestation.rs | 13 +- .../verify_attester_slashing.rs | 4 +- .../per_block_processing/verify_deposit.rs | 8 +- .../src/per_block_processing/verify_exit.rs | 16 +- .../verify_proposer_slashing.rs | 11 +- .../src/per_epoch_processing.rs | 6 +- .../src/per_epoch_processing/apply_rewards.rs | 8 +- .../per_epoch_processing/process_slashings.rs | 2 +- .../per_epoch_processing/registry_updates.rs | 27 +- .../validator_statuses.rs | 8 +- .../src/per_slot_processing.rs | 2 +- eth2/state_processing/src/test_utils.rs | 2 +- eth2/state_processing/tests/tests.rs | 46 +- eth2/types/src/attestation.rs | 27 +- eth2/types/src/attestation_data.rs | 8 +- eth2/types/src/attester_slashing.rs | 4 +- eth2/types/src/beacon_block.rs | 59 +- eth2/types/src/beacon_block_body.rs | 6 +- eth2/types/src/beacon_block_header.rs | 50 +- eth2/types/src/beacon_state.rs | 86 +- .../types/src/beacon_state/committee_cache.rs | 16 +- eth2/types/src/beacon_state/tests.rs | 2 +- eth2/types/src/chain_spec.rs | 155 +- eth2/types/src/checkpoint.rs | 4 +- eth2/types/src/deposit.rs | 4 +- eth2/types/src/deposit_data.rs | 39 +- eth2/types/src/deposit_message.rs | 27 + eth2/types/src/eth1_data.rs | 4 +- eth2/types/src/eth_spec.rs | 23 +- eth2/types/src/fork.rs | 6 +- eth2/types/src/historical_batch.rs | 4 +- eth2/types/src/indexed_attestation.rs | 25 +- eth2/types/src/lib.rs | 16 +- eth2/types/src/pending_attestation.rs | 4 +- eth2/types/src/proposer_slashing.rs | 10 +- eth2/types/src/relative_epoch.rs | 6 +- eth2/types/src/signed_beacon_block.rs | 49 + eth2/types/src/signed_beacon_block_header.rs | 23 + eth2/types/src/signed_voluntary_exit.rs | 23 + eth2/types/src/signing_root.rs | 25 + eth2/types/src/slot_epoch.rs | 8 +- eth2/types/src/slot_epoch_macros.rs | 2 +- eth2/types/src/slot_height.rs | 41 - .../builders/testing_attestation_builder.rs | 32 +- .../testing_attestation_data_builder.rs | 19 +- .../testing_attester_slashing_builder.rs | 17 +- .../builders/testing_beacon_block_builder.rs | 65 +- .../testing_proposer_slashing_builder.rs | 56 +- .../testing_voluntary_exit_builder.rs | 21 +- eth2/types/src/test_utils/macros.rs | 18 +- .../src/test_utils/test_random/signature.rs | 2 +- eth2/types/src/validator.rs | 30 +- eth2/types/src/voluntary_exit.rs | 47 +- eth2/utils/bls/Cargo.toml | 2 +- eth2/utils/bls/src/aggregate_public_key.rs | 54 +- eth2/utils/bls/src/aggregate_signature.rs | 14 +- .../bls/src/fake_aggregate_public_key.rs | 48 + .../utils/bls/src/fake_aggregate_signature.rs | 10 +- eth2/utils/bls/src/fake_signature.rs | 6 +- eth2/utils/bls/src/lib.rs | 3 +- eth2/utils/bls/src/signature.rs | 32 +- eth2/utils/bls/src/signature_bytes.rs | 2 +- eth2/utils/bls/src/signature_set.rs | 21 +- eth2/utils/deposit_contract/build.rs | 4 +- eth2/utils/deposit_contract/src/lib.rs | 8 +- eth2/utils/eth2_interop_keypairs/Cargo.toml | 2 +- eth2/utils/eth2_testnet_config/src/lib.rs | 2 + eth2/utils/remote_beacon_node/src/lib.rs | 12 +- eth2/utils/ssz/src/lib.rs | 4 +- eth2/utils/ssz_types/src/lib.rs | 4 +- eth2/utils/swap_or_not_shuffle/src/lib.rs | 2 +- eth2/utils/tree_hash/src/lib.rs | 4 - eth2/utils/tree_hash_derive/src/lib.rs | 55 - lcli/src/deploy_deposit_contract.rs | 10 +- lcli/src/eth1_genesis.rs | 8 +- lcli/src/interop_genesis.rs | 8 +- lcli/src/transition_blocks.rs | 8 +- tests/ef_tests/Makefile | 2 +- tests/ef_tests/src/cases.rs | 14 +- .../src/cases/bls_aggregate_pubkeys.rs | 36 - .../src/cases/bls_aggregate_verify.rs | 59 + .../src/cases/bls_fast_aggregate_verify.rs | 50 + tests/ef_tests/src/cases/bls_g2_compressed.rs | 50 - .../ef_tests/src/cases/bls_g2_uncompressed.rs | 65 - tests/ef_tests/src/cases/bls_priv_to_pub.rs | 38 - tests/ef_tests/src/cases/bls_sign_msg.rs | 15 +- tests/ef_tests/src/cases/bls_verify_msg.rs | 35 + tests/ef_tests/src/cases/operations.rs | 12 +- tests/ef_tests/src/cases/sanity_blocks.rs | 13 +- tests/ef_tests/src/cases/ssz_static.rs | 34 - tests/ef_tests/src/handler.rs | 44 +- tests/ef_tests/src/lib.rs | 1 + tests/ef_tests/src/type_name.rs | 1 + tests/ef_tests/tests/tests.rs | 89 +- tests/simulator/src/main.rs | 3 +- validator_client/src/block_service.rs | 8 +- validator_client/src/validator_store.rs | 13 +- 145 files changed, 1666 insertions(+), 4437 deletions(-) delete mode 100644 beacon_node/rest_api/api_spec.yaml delete mode 100644 beacon_node/store/src/block_at_slot.rs create mode 100644 eth2/types/src/deposit_message.rs create mode 100644 eth2/types/src/signed_beacon_block.rs create mode 100644 eth2/types/src/signed_beacon_block_header.rs create mode 100644 eth2/types/src/signed_voluntary_exit.rs create mode 100644 eth2/types/src/signing_root.rs delete mode 100644 eth2/types/src/slot_height.rs delete mode 100644 tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs create mode 100644 tests/ef_tests/src/cases/bls_aggregate_verify.rs create mode 100644 tests/ef_tests/src/cases/bls_fast_aggregate_verify.rs delete mode 100644 tests/ef_tests/src/cases/bls_g2_compressed.rs delete mode 100644 tests/ef_tests/src/cases/bls_g2_uncompressed.rs delete mode 100644 tests/ef_tests/src/cases/bls_priv_to_pub.rs create mode 100644 tests/ef_tests/src/cases/bls_verify_msg.rs diff --git a/Cargo.lock b/Cargo.lock index 6972343fe..24461d835 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,7 +80,11 @@ dependencies = [ [[package]] name = "amcl" version = "0.2.0" -source = "git+https://github.com/sigp/milagro_bls?tag=v0.11.2#f01a8b6f9fd9364dfb9635038e00787f29972b2b" +source = "git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10#52fab41dd086951ade699894b690e95ede1efafd" +dependencies = [ + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "ansi_term" @@ -358,7 +362,7 @@ dependencies = [ "eth2_ssz 0.1.2", "eth2_ssz_types 0.2.0", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "milagro_bls 0.11.2 (git+https://github.com/sigp/milagro_bls?tag=v0.11.2)", + "milagro_bls 1.0.0 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1163,7 +1167,7 @@ dependencies = [ "eth2_hashing 0.1.1", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "milagro_bls 0.11.2 (git+https://github.com/sigp/milagro_bls?tag=v0.11.2)", + "milagro_bls 1.0.0 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)", "num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2500,14 +2504,13 @@ dependencies = [ [[package]] name = "milagro_bls" -version = "0.11.2" -source = "git+https://github.com/sigp/milagro_bls?tag=v0.11.2#f01a8b6f9fd9364dfb9635038e00787f29972b2b" +version = "1.0.0" +source = "git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10#52fab41dd086951ade699894b690e95ede1efafd" dependencies = [ - "amcl 0.2.0 (git+https://github.com/sigp/milagro_bls?tag=v0.11.2)", + "amcl 0.2.0 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)", "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5120,7 +5123,7 @@ dependencies = [ "checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" "checksum ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" -"checksum amcl 0.2.0 (git+https://github.com/sigp/milagro_bls?tag=v0.11.2)" = "" +"checksum amcl 0.2.0 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)" = "" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" "checksum arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" @@ -5317,7 +5320,7 @@ dependencies = [ "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" "checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" -"checksum milagro_bls 0.11.2 (git+https://github.com/sigp/milagro_bls?tag=v0.11.2)" = "" +"checksum milagro_bls 1.0.0 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)" = "" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" "checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599" diff --git a/README.md b/README.md index 06a8cb0b3..49c6f2bb9 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.8.3` implemented, optimized and passing test vectors. +- Specification `v0.10.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 281375c7a..7f90e8356 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -166,9 +166,9 @@ impl BeaconChain { .store .get_block(&beacon_block_root)? .ok_or_else(|| Error::MissingBeaconBlock(beacon_block_root))?; - let beacon_state_root = beacon_block.state_root; + let beacon_state_root = beacon_block.state_root(); let beacon_state = self - .get_state(&beacon_state_root, Some(beacon_block.slot))? + .get_state(&beacon_state_root, Some(beacon_block.slot()))? .ok_or_else(|| Error::MissingBeaconState(beacon_state_root))?; CheckPoint { @@ -216,39 +216,6 @@ impl BeaconChain { .map(|slot| slot.epoch(T::EthSpec::slots_per_epoch())) } - /// Returns the beacon block body for each beacon block root in `roots`. - /// - /// Fails if any root in `roots` does not have a corresponding block. - pub fn get_block_bodies( - &self, - roots: &[Hash256], - ) -> Result>, Error> { - let bodies: Result, _> = roots - .iter() - .map(|root| match self.block_at_root(*root)? { - Some(block) => Ok(block.body), - None => Err(Error::DBInconsistent(format!("Missing block: {}", root))), - }) - .collect(); - - Ok(bodies?) - } - - /// Returns the beacon block header for each beacon block root in `roots`. - /// - /// Fails if any root in `roots` does not have a corresponding block. - pub fn get_block_headers(&self, roots: &[Hash256]) -> Result, Error> { - let headers: Result, _> = roots - .iter() - .map(|root| match self.block_at_root(*root)? { - Some(block) => Ok(block.block_header()), - None => Err(Error::DBInconsistent("Missing block".into())), - }) - .collect(); - - Ok(headers?) - } - /// Iterates across all `(block_root, slot)` pairs from the head of the chain (inclusive) to /// the earliest reachable ancestor (may or may not be genesis). /// @@ -268,7 +235,7 @@ impl BeaconChain { let iter = BlockRootsIterator::owned(self.store.clone(), head.beacon_state); Ok(ReverseBlockRootIterator::new( - (head.beacon_block_root, head.beacon_block.slot), + (head.beacon_block_root, head.beacon_block.slot()), iter, )) } @@ -305,11 +272,11 @@ impl BeaconChain { .get_block(&block_root)? .ok_or_else(|| Error::MissingBeaconBlock(block_root))?; let state = self - .get_state(&block.state_root, Some(block.slot))? - .ok_or_else(|| Error::MissingBeaconState(block.state_root))?; + .get_state(&block.state_root(), Some(block.slot()))? + .ok_or_else(|| Error::MissingBeaconState(block.state_root()))?; let iter = BlockRootsIterator::owned(self.store.clone(), state); Ok(ReverseBlockRootIterator::new( - (block_root, block.slot), + (block_root, block.slot()), iter, )) } @@ -349,24 +316,15 @@ impl BeaconChain { )) } - /// Returns the block at the given root, if any. - /// - /// ## Errors - /// - /// May return a database error. - pub fn block_at_root( - &self, - block_root: Hash256, - ) -> Result>, Error> { - Ok(self.store.get(&block_root)?) - } - /// Returns the block at the given slot, if any. Only returns blocks in the canonical chain. /// /// ## Errors /// /// May return a database error. - pub fn block_at_slot(&self, slot: Slot) -> Result>, Error> { + pub fn block_at_slot( + &self, + slot: Slot, + ) -> Result>, Error> { let root = self .rev_iter_block_roots()? .find(|(_, this_slot)| *this_slot == slot) @@ -387,7 +345,7 @@ impl BeaconChain { pub fn get_block( &self, block_root: &Hash256, - ) -> Result>, Error> { + ) -> Result>, Error> { Ok(self.store.get_block(block_root)?) } @@ -448,7 +406,7 @@ impl BeaconChain { .ok_or_else(|| Error::CanonicalHeadLockTimeout)?; Ok(HeadInfo { - slot: head.beacon_block.slot, + slot: head.beacon_block.slot(), block_root: head.beacon_block_root, state_root: head.beacon_state_root, finalized_checkpoint: head.beacon_state.finalized_checkpoint.clone(), @@ -550,7 +508,7 @@ impl BeaconChain { pub fn best_slot(&self) -> Result { self.canonical_head .try_read_for(HEAD_LOCK_TIMEOUT) - .map(|head| head.beacon_block.slot) + .map(|head| head.beacon_block.slot()) .ok_or_else(|| Error::CanonicalHeadLockTimeout) } @@ -576,19 +534,6 @@ impl BeaconChain { .map(|(root, _slot)| root)) } - /// Reads the slot clock (see `self.read_slot_clock()` and returns the number of slots since - /// genesis. - pub fn slots_since_genesis(&self) -> Option { - let now = self.slot().ok()?; - let genesis_slot = self.spec.genesis_slot; - - if now < genesis_slot { - None - } else { - Some(SlotHeight::from(now.as_u64() - genesis_slot.as_u64())) - } - } - /// Returns the block proposer for a given slot. /// /// Information is read from the present `beacon_state` shuffling, only information from the @@ -669,7 +614,7 @@ impl BeaconChain { let data = self.produce_attestation_data_for_block( index, head.beacon_block_root, - head.beacon_block.slot, + head.beacon_block.slot(), &state, )?; @@ -696,7 +641,7 @@ impl BeaconChain { self.produce_attestation_data_for_block( index, head.beacon_block_root, - head.beacon_block.slot, + head.beacon_block.slot(), &state, ) } @@ -859,22 +804,20 @@ impl BeaconChain { // the attestation points to a block in a prior epoch, then it is necessary to // load the full state corresponding to its block, and transition it to the // attestation's epoch. + let attestation_block_root = attestation_head_block.state_root(); let attestation_epoch = attestation.data.target.epoch; let slots_per_epoch = T::EthSpec::slots_per_epoch(); let mut state = if attestation_epoch - == attestation_head_block.slot.epoch(slots_per_epoch) + == attestation_head_block.slot().epoch(slots_per_epoch) { self.store - .load_epoch_boundary_state(&attestation_head_block.state_root)? - .ok_or_else(|| Error::MissingBeaconState(attestation_head_block.state_root))? + .load_epoch_boundary_state(&attestation_block_root)? + .ok_or_else(|| Error::MissingBeaconState(attestation_block_root))? } else { let mut state = self .store - .get_state( - &attestation_head_block.state_root, - Some(attestation_head_block.slot), - )? - .ok_or_else(|| Error::MissingBeaconState(attestation_head_block.state_root))?; + .get_state(&attestation_block_root, Some(attestation_head_block.slot()))? + .ok_or_else(|| Error::MissingBeaconState(attestation_block_root))?; // Fastforward the state to the epoch in which the attestation was made. // NOTE: this looks like a potential DoS vector, we should probably limit @@ -902,7 +845,7 @@ impl BeaconChain { self.process_attestation_for_state_and_block( attestation, &state, - &attestation_head_block, + &attestation_head_block.message, ) } } else { @@ -1045,7 +988,10 @@ impl BeaconChain { } /// Accept some exit and queue it for inclusion in an appropriate block. - pub fn process_voluntary_exit(&self, exit: VoluntaryExit) -> Result<(), ExitValidationError> { + pub fn process_voluntary_exit( + &self, + exit: SignedVoluntaryExit, + ) -> Result<(), ExitValidationError> { match self.wall_clock_state() { Ok(state) => self.op_pool.insert_voluntary_exit(exit, &state, &self.spec), Err(e) => { @@ -1109,7 +1055,7 @@ impl BeaconChain { /// Will accept blocks from prior slots, however it will reject any block from a future slot. pub fn process_block( &self, - block: BeaconBlock, + block: SignedBeaconBlock, ) -> Result { let outcome = self.process_block_internal(block.clone()); @@ -1120,7 +1066,7 @@ impl BeaconChain { self.log, "Beacon block imported"; "block_root" => format!("{:?}", block_root), - "block_slot" => format!("{:?}", block.slot.as_u64()), + "block_slot" => format!("{:?}", block.slot().as_u64()), ); let _ = self.event_handler.register(EventKind::BeaconBlockImported { block_root: *block_root, @@ -1160,11 +1106,13 @@ impl BeaconChain { /// Will accept blocks from prior slots, however it will reject any block from a future slot. fn process_block_internal( &self, - block: BeaconBlock, + signed_block: SignedBeaconBlock, ) -> Result { metrics::inc_counter(&metrics::BLOCK_PROCESSING_REQUESTS); let full_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_TIMES); + let block = &signed_block.message; + let finalized_slot = self .head_info()? .finalized_checkpoint @@ -1246,9 +1194,9 @@ impl BeaconChain { // Load the parent blocks state from the database, returning an error if it is not found. // It is an error because if we know the parent block we should also know the parent state. - let parent_state_root = parent_block.state_root; + let parent_state_root = parent_block.state_root(); let parent_state = self - .get_state(&parent_state_root, Some(parent_block.slot))? + .get_state(&parent_state_root, Some(parent_block.slot()))? .ok_or_else(|| { Error::DBInconsistent(format!("Missing state {:?}", parent_state_root)) })?; @@ -1268,7 +1216,7 @@ impl BeaconChain { let distance = block.slot.as_u64().saturating_sub(state.slot.as_u64()); for i in 0..distance { let state_root = if i == 0 { - parent_block.state_root + parent_block.state_root() } else { // This is a new state we've reached, so stage it for storage in the DB. // Computing the state root here is time-equivalent to computing it during slot @@ -1302,7 +1250,7 @@ impl BeaconChain { // slot). match per_block_processing( &mut state, - &block, + &signed_block, Some(block_root), BlockSignatureStrategy::VerifyBulk, &self.spec, @@ -1372,7 +1320,7 @@ impl BeaconChain { // settles down). // See: https://github.com/sigp/lighthouse/issues/692 self.store.put_state(&state_root, state)?; - self.store.put_block(&block_root, block)?; + self.store.put_block(&block_root, signed_block)?; metrics::stop_timer(db_write_timer); @@ -1450,26 +1398,28 @@ impl BeaconChain { .deposits_for_block_inclusion(&state, ð1_data, &self.spec)? .into(); - let mut block = BeaconBlock { - slot: state.slot, - parent_root, - state_root: Hash256::zero(), + let mut block = SignedBeaconBlock { + message: BeaconBlock { + slot: state.slot, + parent_root, + state_root: Hash256::zero(), + body: BeaconBlockBody { + randao_reveal, + eth1_data, + graffiti, + proposer_slashings: proposer_slashings.into(), + attester_slashings: attester_slashings.into(), + attestations: self + .op_pool + .get_attestations(&state, &self.spec) + .map_err(BlockProductionError::OpPoolError)? + .into(), + deposits, + voluntary_exits: self.op_pool.get_voluntary_exits(&state, &self.spec).into(), + }, + }, // The block is not signed here, that is the task of a validator client. signature: Signature::empty_signature(), - body: BeaconBlockBody { - randao_reveal, - eth1_data, - graffiti, - proposer_slashings: proposer_slashings.into(), - attester_slashings: attester_slashings.into(), - attestations: self - .op_pool - .get_attestations(&state, &self.spec) - .map_err(BlockProductionError::OpPoolError)? - .into(), - deposits, - voluntary_exits: self.op_pool.get_voluntary_exits(&state, &self.spec).into(), - }, }; per_block_processing( @@ -1482,7 +1432,7 @@ impl BeaconChain { let state_root = state.update_tree_hash_cache()?; - block.state_root = state_root; + block.message.state_root = state_root; metrics::inc_counter(&metrics::BLOCK_PRODUCTION_SUCCESSES); metrics::stop_timer(timer); @@ -1490,12 +1440,12 @@ impl BeaconChain { trace!( self.log, "Produced beacon block"; - "parent" => format!("{}", block.parent_root), - "attestations" => block.body.attestations.len(), - "slot" => block.slot + "parent" => format!("{}", block.message.parent_root), + "attestations" => block.message.body.attestations.len(), + "slot" => block.message.slot ); - Ok((block, state)) + Ok((block.message, state)) } /// Execute the fork choice algorithm and enthrone the result as the canonical head. @@ -1516,13 +1466,13 @@ impl BeaconChain { .get_block(&beacon_block_root)? .ok_or_else(|| Error::MissingBeaconBlock(beacon_block_root))?; - let beacon_state_root = beacon_block.state_root; + let beacon_state_root = beacon_block.state_root(); let beacon_state: BeaconState = self - .get_state(&beacon_state_root, Some(beacon_block.slot))? + .get_state(&beacon_state_root, Some(beacon_block.slot()))? .ok_or_else(|| Error::MissingBeaconState(beacon_state_root))?; let previous_slot = self.head_info()?.slot; - let new_slot = beacon_block.slot; + let new_slot = beacon_block.slot(); // Note: this will declare a re-org if we skip `SLOTS_PER_HISTORICAL_ROOT` blocks // between calls to fork choice without swapping between chains. This seems like an @@ -1541,7 +1491,7 @@ impl BeaconChain { "Beacon chain re-org"; "previous_head" => format!("{}", self.head_info()?.block_root), "previous_slot" => previous_slot, - "new_head_parent" => format!("{}", beacon_block.parent_root), + "new_head_parent" => format!("{}", beacon_block.parent_root()), "new_head" => format!("{}", beacon_block_root), "new_slot" => new_slot ); @@ -1636,7 +1586,8 @@ impl BeaconChain { let finalized_block = self .store .get_block(&finalized_block_root)? - .ok_or_else(|| Error::MissingBeaconBlock(finalized_block_root))?; + .ok_or_else(|| Error::MissingBeaconBlock(finalized_block_root))? + .message; let new_finalized_epoch = finalized_block.slot.epoch(T::EthSpec::slots_per_epoch()); @@ -1678,7 +1629,7 @@ impl BeaconChain { pub fn is_new_block_root(&self, beacon_block_root: &Hash256) -> Result { Ok(!self .store - .exists::>(beacon_block_root)?) + .exists::>(beacon_block_root)?) } /// Dumps the entire canonical chain, from the head to genesis to a vector for analysis. @@ -1698,20 +1649,19 @@ impl BeaconChain { dump.push(last_slot.clone()); loop { - let beacon_block_root = last_slot.beacon_block.parent_root; + let beacon_block_root = last_slot.beacon_block.parent_root(); if beacon_block_root == Hash256::zero() { break; // Genesis has been reached. } - let beacon_block: BeaconBlock = - self.store.get(&beacon_block_root)?.ok_or_else(|| { - Error::DBInconsistent(format!("Missing block {}", beacon_block_root)) - })?; - let beacon_state_root = beacon_block.state_root; + let beacon_block = self.store.get_block(&beacon_block_root)?.ok_or_else(|| { + Error::DBInconsistent(format!("Missing block {}", beacon_block_root)) + })?; + let beacon_state_root = beacon_block.state_root(); let beacon_state = self .store - .get_state(&beacon_state_root, Some(beacon_block.slot))? + .get_state(&beacon_state_root, Some(beacon_block.slot()))? .ok_or_else(|| { Error::DBInconsistent(format!("Missing state {:?}", beacon_state_root)) })?; diff --git a/beacon_node/beacon_chain/src/builder.rs b/beacon_node/beacon_chain/src/builder.rs index 331d4f5f8..a5108c49b 100644 --- a/beacon_node/beacon_chain/src/builder.rs +++ b/beacon_node/beacon_chain/src/builder.rs @@ -16,7 +16,9 @@ use std::marker::PhantomData; use std::sync::Arc; use std::time::Duration; use store::Store; -use types::{BeaconBlock, BeaconState, ChainSpec, EthSpec, Hash256, Slot}; +use types::{ + BeaconBlock, BeaconState, ChainSpec, EthSpec, Hash256, Signature, SignedBeaconBlock, Slot, +}; /// An empty struct used to "witness" all the `BeaconChainTypes` traits. It has no user-facing /// functionality and only exists to satisfy the type system. @@ -205,14 +207,13 @@ where .clone() .ok_or_else(|| "genesis_state requires a store")?; - let mut beacon_block = genesis_block(&beacon_state, &self.spec); + let beacon_block = genesis_block(&mut beacon_state, &self.spec)?; beacon_state .build_all_caches(&self.spec) .map_err(|e| format!("Failed to build genesis state caches: {:?}", e))?; - let beacon_state_root = beacon_state.canonical_root(); - beacon_block.state_root = beacon_state_root; + let beacon_state_root = beacon_block.message.state_root; let beacon_block_root = beacon_block.canonical_root(); self.genesis_block_root = Some(beacon_block_root); @@ -303,7 +304,7 @@ where .build_all_caches(&self.spec) .map_err(|e| format!("Failed to build state caches: {:?}", e))?; - if canonical_head.beacon_block.state_root != canonical_head.beacon_state_root { + if canonical_head.beacon_block.state_root() != canonical_head.beacon_state_root { return Err("beacon_block.state_root != beacon_state".to_string()); } @@ -345,7 +346,7 @@ where "Beacon chain initialized"; "head_state" => format!("{}", head.beacon_state_root), "head_block" => format!("{}", head.beacon_block_root), - "head_slot" => format!("{}", head.beacon_block.slot), + "head_slot" => format!("{}", head.beacon_block.slot()), ); Ok(beacon_chain) @@ -382,7 +383,7 @@ where .ok_or_else(|| "fork_choice_backend requires a genesis_block_root")?; let backend = ProtoArrayForkChoice::new( - finalized_checkpoint.beacon_block.slot, + finalized_checkpoint.beacon_block.message.slot, // Note: here we set the `justified_epoch` to be the same as the epoch of the // finalized checkpoint. Whilst this finalized checkpoint may actually point to // a _later_ justified checkpoint, that checkpoint won't yet exist in the fork @@ -512,12 +513,20 @@ where } } -fn genesis_block(genesis_state: &BeaconState, spec: &ChainSpec) -> BeaconBlock { - let mut genesis_block = BeaconBlock::empty(&spec); - - genesis_block.state_root = genesis_state.canonical_root(); - - genesis_block +fn genesis_block( + genesis_state: &mut BeaconState, + spec: &ChainSpec, +) -> Result, String> { + let mut genesis_block = SignedBeaconBlock { + message: BeaconBlock::empty(&spec), + // Empty signature, which should NEVER be read. This isn't to-spec, but makes the genesis + // block consistent with every other block. + signature: Signature::empty_signature(), + }; + genesis_block.message.state_root = genesis_state + .update_tree_hash_cache() + .map_err(|e| format!("Error hashing genesis state: {:?}", e))?; + Ok(genesis_block) } #[cfg(test)] @@ -581,14 +590,14 @@ mod test { "should have the correct genesis time" ); assert_eq!( - block.state_root, + block.state_root(), state.canonical_root(), "block should have correct state root" ); assert_eq!( chain .store - .get::>(&Hash256::zero()) + .get_block(&Hash256::zero()) .expect("should read db") .expect("should find genesis block"), block, diff --git a/beacon_node/beacon_chain/src/checkpoint.rs b/beacon_node/beacon_chain/src/checkpoint.rs index 0ca8a1243..79d0f94b7 100644 --- a/beacon_node/beacon_chain/src/checkpoint.rs +++ b/beacon_node/beacon_chain/src/checkpoint.rs @@ -1,12 +1,12 @@ use serde_derive::Serialize; use ssz_derive::{Decode, Encode}; -use types::{BeaconBlock, BeaconState, EthSpec, Hash256}; +use types::{BeaconState, EthSpec, Hash256, SignedBeaconBlock}; -/// Represents some block and it's associated state. Generally, this will be used for tracking the +/// Represents some block and its associated state. Generally, this will be used for tracking the /// head, justified head and finalized head. #[derive(Clone, Serialize, PartialEq, Debug, Encode, Decode)] pub struct CheckPoint { - pub beacon_block: BeaconBlock, + pub beacon_block: SignedBeaconBlock, pub beacon_block_root: Hash256, pub beacon_state: BeaconState, pub beacon_state_root: Hash256, @@ -15,7 +15,7 @@ pub struct CheckPoint { impl CheckPoint { /// Create a new checkpoint. pub fn new( - beacon_block: BeaconBlock, + beacon_block: SignedBeaconBlock, beacon_block_root: Hash256, beacon_state: BeaconState, beacon_state_root: Hash256, @@ -31,7 +31,7 @@ impl CheckPoint { /// Update all fields of the checkpoint. pub fn update( &mut self, - beacon_block: BeaconBlock, + beacon_block: SignedBeaconBlock, beacon_block_root: Hash256, beacon_state: BeaconState, beacon_state_root: Hash256, diff --git a/beacon_node/beacon_chain/src/eth1_chain.rs b/beacon_node/beacon_chain/src/eth1_chain.rs index d1227e1f8..1ff1a0cfa 100644 --- a/beacon_node/beacon_chain/src/eth1_chain.rs +++ b/beacon_node/beacon_chain/src/eth1_chain.rs @@ -3,15 +3,12 @@ use eth1::{Config as Eth1Config, Eth1Block, Service as HttpService}; use eth2_hashing::hash; use exit_future::Exit; use futures::Future; -use integer_sqrt::IntegerSquareRoot; -use rand::prelude::*; -use slog::{crit, debug, error, trace, Logger}; +use slog::{debug, error, trace, Logger}; use ssz_derive::{Decode, Encode}; use state_processing::per_block_processing::get_new_eth1_data; use std::cmp::Ordering; use std::collections::HashMap; use std::iter::DoubleEndedIterator; -use std::iter::FromIterator; use std::marker::PhantomData; use std::sync::Arc; use store::{Error as StoreError, Store}; @@ -21,7 +18,6 @@ use types::{ }; type BlockNumber = u64; -type Eth1DataBlockNumber = HashMap; type Eth1DataVoteCount = HashMap<(Eth1Data, BlockNumber), u64>; #[derive(Debug, PartialEq)] @@ -280,12 +276,7 @@ impl> CachingEth1Backend { impl> Eth1ChainBackend for CachingEth1Backend { fn eth1_data(&self, state: &BeaconState, spec: &ChainSpec) -> Result { - // Note: we do not return random junk if this function call fails as it would be caused by - // an internal error. - let prev_eth1_hash = eth1_block_hash_at_start_of_voting_period(self.store.clone(), state)?; - let period = T::SlotsPerEth1VotingPeriod::to_u64(); - let eth1_follow_distance = spec.eth1_follow_distance; let voting_period_start_slot = (state.slot / period) * period; let voting_period_start_seconds = slot_start_seconds::( state.genesis_time, @@ -295,82 +286,54 @@ impl> Eth1ChainBackend for CachingEth1Backend self.core.lowest_block_number(), - "earliest_block_timestamp" => self.core.earliest_block_timestamp(), - "genesis_time" => state.genesis_time, - "outcome" => "casting random eth1 vote" - ); - - return Ok(random_eth1_data()); - }; + let votes_to_consider = + get_votes_to_consider(blocks.iter(), voting_period_start_seconds, spec); trace!( self.log, - "Found eth1 data sets"; - "all_eth1_data" => all_eth1_data.len(), - "new_eth1_data" => new_eth1_data.len(), + "Found eth1 data votes_to_consider"; + "votes_to_consider" => votes_to_consider.len(), ); - - let valid_votes = collect_valid_votes(state, new_eth1_data, all_eth1_data); + let valid_votes = collect_valid_votes(state, &votes_to_consider); let eth1_data = if let Some(eth1_data) = find_winning_vote(valid_votes) { eth1_data } else { - // In this case, there are no other viable votes (perhaps there are no votes yet or all - // the existing votes are junk). + // In this case, there are no valid votes available. // - // Here we choose the latest block in our voting window. - blocks + // Here we choose the eth1_data corresponding to the latest block in our voting window. + // If no votes exist, choose `state.eth1_data` as default vote. + let default_vote = votes_to_consider .iter() - .rev() - .skip_while(|eth1_block| eth1_block.timestamp > voting_period_start_seconds) - .nth(eth1_follow_distance as usize) - .map(|block| { - trace!( + .max_by(|(_, x), (_, y)| x.cmp(y)) + .map(|vote| { + let vote = vote.0.clone(); + debug!( self.log, - "Choosing default eth1_data"; - "eth1_block_number" => block.number, - "eth1_block_hash" => format!("{:?}", block.hash), + "No valid eth1_data votes"; + "outcome" => "Casting vote corresponding to last candidate eth1 block", ); - - block + vote }) - .and_then(|block| block.clone().eth1_data()) .unwrap_or_else(|| { - crit!( + let vote = state.eth1_data.clone(); + error!( self.log, - "Unable to find a winning eth1 vote"; - "outcome" => "casting random eth1 vote" + "No valid eth1_data votes, `votes_to_consider` empty"; + "lowest_block_number" => self.core.lowest_block_number(), + "earliest_block_timestamp" => self.core.earliest_block_timestamp(), + "genesis_time" => state.genesis_time, + "outcome" => "casting `state.eth1_data` as eth1 vote" ); - - random_eth1_data() - }) + metrics::inc_counter(&metrics::DEFAULT_ETH1_VOTES); + vote + }); + default_vote }; debug!( self.log, "Produced vote for eth1 chain"; - "is_period_tail" => is_period_tail(state), "deposit_root" => format!("{:?}", eth1_data.deposit_root), "deposit_count" => eth1_data.deposit_count, "block_hash" => format!("{:?}", eth1_data.block_hash), @@ -432,139 +395,47 @@ impl> Eth1ChainBackend for CachingEth1Backend Eth1Data { - let mut rng = rand::thread_rng(); - - metrics::inc_counter(&metrics::JUNK_ETH1_VOTES); - - macro_rules! rand_bytes { - ($num_bytes: expr) => {{ - let mut arr = [0_u8; $num_bytes]; - rng.fill(&mut arr[..]); - arr - }}; - } - - // Note: it seems easier to just use `Hash256::random(..)` to get the hash values, however I - // prefer to be explicit about the source of entropy instead of relying upon the maintainers of - // `Hash256` to ensure their entropy is suitable for our purposes. - - Eth1Data { - block_hash: Hash256::from_slice(&rand_bytes!(32)), - deposit_root: Hash256::from_slice(&rand_bytes!(32)), - deposit_count: u64::from_le_bytes(rand_bytes!(8)), - } -} - -/// Returns `state.eth1_data.block_hash` at the start of eth1 voting period defined by -/// `state.slot`. -fn eth1_block_hash_at_start_of_voting_period>( - store: Arc, - state: &BeaconState, -) -> Result { - let period = T::SlotsPerEth1VotingPeriod::to_u64(); - - if !eth1_data_change_is_possible(state) { - // If there are less than 50% of the votes in the current state, it's impossible that the - // `eth1_data.block_hash` has changed from the value at `state.eth1_data.block_hash`. - Ok(state.eth1_data.block_hash) - } else { - // If there have been more than 50% of votes in this period it's possible (but not - // necessary) that the `state.eth1_data.block_hash` has been changed since the start of the - // voting period. - let slot = (state.slot / period) * period; - let prev_state_root = state - .get_state_root(slot) - .map_err(Error::UnableToGetPreviousStateRoot)?; - - store - .get_state(&prev_state_root, Some(slot)) - .map_err(Error::StoreError)? - .map(|state| state.eth1_data.block_hash) - .ok_or_else(|| Error::PreviousStateNotInDB(*prev_state_root)) - } -} - -/// Returns true if there are enough eth1 votes in the given `state` to have updated -/// `state.eth1_data`. -fn eth1_data_change_is_possible(state: &BeaconState) -> bool { - 2 * state.eth1_data_votes.len() > E::SlotsPerEth1VotingPeriod::to_usize() -} - -/// Calculates and returns `(new_eth1_data, all_eth1_data)` for the given `state`, based upon the -/// blocks in the `block` iterator. +/// Get all votes from eth1 blocks which are in the list of candidate blocks for the +/// current eth1 voting period. /// -/// `prev_eth1_hash` is the `eth1_data.block_hash` at the start of the voting period defined by -/// `state.slot`. -fn eth1_data_sets<'a, I>( +/// Returns a hashmap of `Eth1Data` to its associated eth1 `block_number`. +fn get_votes_to_consider<'a, I>( blocks: I, - prev_eth1_hash: Hash256, voting_period_start_seconds: u64, spec: &ChainSpec, - log: &Logger, -) -> Option<(Eth1DataBlockNumber, Eth1DataBlockNumber)> +) -> HashMap where I: DoubleEndedIterator + Clone, { - let eth1_follow_distance = spec.eth1_follow_distance; - - let in_scope_eth1_data = blocks + blocks .rev() - .skip_while(|eth1_block| eth1_block.timestamp > voting_period_start_seconds) - .skip(eth1_follow_distance as usize) - .filter_map(|block| Some((block.clone().eth1_data()?, block.number))); - - if in_scope_eth1_data - .clone() - .any(|(eth1_data, _)| eth1_data.block_hash == prev_eth1_hash) - { - let new_eth1_data = in_scope_eth1_data - .clone() - .take(eth1_follow_distance as usize); - let all_eth1_data = - in_scope_eth1_data.take_while(|(eth1_data, _)| eth1_data.block_hash != prev_eth1_hash); - - Some(( - HashMap::from_iter(new_eth1_data), - HashMap::from_iter(all_eth1_data), - )) - } else { - error!( - log, - "The previous eth1 hash is not in cache"; - "previous_hash" => format!("{:?}", prev_eth1_hash) - ); - - None - } + .skip_while(|eth1_block| !is_candidate_block(eth1_block, voting_period_start_seconds, spec)) + .take_while(|eth1_block| is_candidate_block(eth1_block, voting_period_start_seconds, spec)) + .filter_map(|eth1_block| { + eth1_block + .clone() + .eth1_data() + .map(|eth1_data| (eth1_data, eth1_block.number)) + }) + .collect() } -/// Selects and counts the votes in `state.eth1_data_votes`, if they appear in `new_eth1_data` or -/// `all_eth1_data` when it is the voting period tail. +/// Collect all valid votes that are cast during the current voting period. +/// Return hashmap with count of each vote cast. fn collect_valid_votes( state: &BeaconState, - new_eth1_data: Eth1DataBlockNumber, - all_eth1_data: Eth1DataBlockNumber, + votes_to_consider: &HashMap, ) -> Eth1DataVoteCount { let mut valid_votes = HashMap::new(); - state .eth1_data_votes .iter() .filter_map(|vote| { - new_eth1_data - .get(vote) - .map(|block_number| (vote.clone(), *block_number)) - .or_else(|| { - if is_period_tail(state) { - all_eth1_data - .get(vote) - .map(|block_number| (vote.clone(), *block_number)) - } else { - None - } - }) + if let Some(block_num) = votes_to_consider.get(vote) { + Some((vote.clone(), *block_num)) + } else { + None + } }) .for_each(|(eth1_data, block_number)| { valid_votes @@ -572,19 +443,9 @@ fn collect_valid_votes( .and_modify(|count| *count += 1) .or_insert(1_u64); }); - valid_votes } -/// Indicates if the given `state` is in the tail of it's eth1 voting period (i.e., in the later -/// slots). -fn is_period_tail(state: &BeaconState) -> bool { - let slots_per_eth1_voting_period = E::SlotsPerEth1VotingPeriod::to_u64(); - let slot = state.slot % slots_per_eth1_voting_period; - - slot >= slots_per_eth1_voting_period.integer_sqrt() -} - /// Selects the winning vote from `valid_votes`. fn find_winning_vote(valid_votes: Eth1DataVoteCount) -> Option { valid_votes @@ -609,10 +470,24 @@ fn slot_start_seconds( genesis_unix_seconds + slot.as_u64() * milliseconds_per_slot / 1_000 } +/// Returns a boolean denoting if a given `Eth1Block` is a candidate for `Eth1Data` calculation +/// at the timestamp `period_start`. +/// +/// Note: `period_start` needs to be atleast (`spec.seconds_per_eth1_block * spec.eth1_follow_distance * 2`) +/// for this function to return meaningful values. +fn is_candidate_block(block: &Eth1Block, period_start: u64, spec: &ChainSpec) -> bool { + block.timestamp + <= period_start.saturating_sub(spec.seconds_per_eth1_block * spec.eth1_follow_distance) + && block.timestamp + >= period_start + .saturating_sub(spec.seconds_per_eth1_block * spec.eth1_follow_distance * 2) +} + #[cfg(test)] mod test { use super::*; use environment::null_logger; + use std::iter::FromIterator; use types::{test_utils::DepositTestTask, MinimalEthSpec}; type E = MinimalEthSpec; @@ -625,9 +500,14 @@ mod test { } } - #[test] - fn random_eth1_data_doesnt_panic() { - random_eth1_data(); + fn get_voting_period_start_seconds(state: &BeaconState, spec: &ChainSpec) -> u64 { + let period = ::SlotsPerEth1VotingPeriod::to_u64(); + let voting_period_start_slot = (state.slot / period) * period; + slot_start_seconds::( + state.genesis_time, + spec.milliseconds_per_slot, + voting_period_start_slot, + ) } #[test] @@ -709,7 +589,7 @@ mod test { assert!( eth1_chain - .deposits_for_block_inclusion(&state, &random_eth1_data(), spec) + .deposits_for_block_inclusion(&state, &Eth1Data::default(), spec) .is_ok(), "should succeed if cache is empty but no deposits are required" ); @@ -718,7 +598,7 @@ mod test { assert!( eth1_chain - .deposits_for_block_inclusion(&state, &random_eth1_data(), spec) + .deposits_for_block_inclusion(&state, &Eth1Data::default(), spec) .is_err(), "should fail to get deposits if required, but cache is empty" ); @@ -762,7 +642,7 @@ mod test { assert!( eth1_chain - .deposits_for_block_inclusion(&state, &random_eth1_data(), spec) + .deposits_for_block_inclusion(&state, &Eth1Data::default(), spec) .is_ok(), "should succeed if no deposits are required" ); @@ -774,7 +654,7 @@ mod test { state.eth1_data.deposit_count = i as u64; let deposits_for_inclusion = eth1_chain - .deposits_for_block_inclusion(&state, &random_eth1_data(), spec) + .deposits_for_block_inclusion(&state, &Eth1Data::default(), spec) .unwrap_or_else(|_| panic!("should find deposit for {}", i)); let expected_len = @@ -822,24 +702,20 @@ mod test { let a = eth1_chain .eth1_data_for_block_production(&state, &spec) - .expect("should produce first random eth1 data"); - let b = eth1_chain - .eth1_data_for_block_production(&state, &spec) - .expect("should produce second random eth1 data"); - - assert!( - a != b, - "random votes should be returned with an empty cache" + .expect("should produce default eth1 data vote"); + assert_eq!( + a, state.eth1_data, + "default vote should be same as state.eth1_data" ); } #[test] - fn eth1_data_unknown_previous_state() { + fn default_vote() { let spec = &E::default_spec(); - let period = ::SlotsPerEth1VotingPeriod::to_u64(); + let slots_per_eth1_voting_period = ::SlotsPerEth1VotingPeriod::to_u64(); + let eth1_follow_distance = spec.eth1_follow_distance; let eth1_chain = get_eth1_chain(); - let store = eth1_chain.backend.store.clone(); assert_eq!( eth1_chain.use_dummy_backend, false, @@ -847,120 +723,41 @@ mod test { ); let mut state: BeaconState = BeaconState::new(0, get_eth1_data(0), &spec); - let mut prev_state = state.clone(); - prev_state.slot = Slot::new(period * 1_000); - state.slot = Slot::new(period * 1_000 + period / 2); + state.slot = Slot::from(slots_per_eth1_voting_period * 10); + let follow_distance_seconds = eth1_follow_distance * spec.seconds_per_eth1_block; + let voting_period_start = get_voting_period_start_seconds(&state, &spec); + let start_eth1_block = voting_period_start - follow_distance_seconds * 2; + let end_eth1_block = voting_period_start - follow_distance_seconds; - // Add 50% of the votes so a lookup is required. - for _ in 0..=period / 2 { - state - .eth1_data_votes - .push(random_eth1_data()) - .expect("should push eth1 vote"); - } + // Populate blocks cache with candidate eth1 blocks + let blocks = (start_eth1_block..end_eth1_block) + .map(|i| get_eth1_block(i, i)) + .collect::>(); - (0..2048).for_each(|i| { + blocks.iter().for_each(|block| { eth1_chain .backend .core .blocks() .write() - .insert_root_or_child(get_eth1_block(i, i)) + .insert_root_or_child(block.clone()) .expect("should add blocks to cache"); }); - let expected_root = Hash256::from_low_u64_be(u64::max_value()); - prev_state.eth1_data.block_hash = expected_root; - - assert!( - prev_state.eth1_data != state.eth1_data, - "test requires state eth1_data are different" - ); - - store - .put_state( - &state - .get_state_root(prev_state.slot) - .expect("should find state root"), - prev_state, - ) - .expect("should store state"); - - let a = eth1_chain + let vote = eth1_chain .eth1_data_for_block_production(&state, &spec) - .expect("should produce first random eth1 data"); - let b = eth1_chain - .eth1_data_for_block_production(&state, &spec) - .expect("should produce second random eth1 data"); - - assert!( - a != b, - "random votes should be returned if the previous eth1 data block hash is unknown" - ); - } - } - - mod prev_block_hash { - use super::*; - use store::MemoryStore; - - #[test] - fn without_store_lookup() { - let spec = &E::default_spec(); - let store = Arc::new(MemoryStore::open()); - - let state: BeaconState = BeaconState::new(0, get_eth1_data(0), spec); + .expect("should produce default eth1 data vote"); assert_eq!( - eth1_block_hash_at_start_of_voting_period(store, &state), - Ok(state.eth1_data.block_hash), - "should return the states eth1 data in the first half of the period" - ); - } - - #[test] - fn with_store_lookup() { - let spec = &E::default_spec(); - let store = Arc::new(MemoryStore::open()); - - let period = ::SlotsPerEth1VotingPeriod::to_u64(); - - let mut state: BeaconState = BeaconState::new(0, get_eth1_data(0), spec); - let mut prev_state = state.clone(); - - state.slot = Slot::new(period / 2); - - // Add 50% of the votes so a lookup is required. - for _ in 0..=period / 2 { - state - .eth1_data_votes - .push(random_eth1_data()) - .expect("should push eth1 vote"); - } - - let expected_root = Hash256::from_low_u64_be(42); - - prev_state.eth1_data.block_hash = expected_root; - - assert!( - prev_state.eth1_data != state.eth1_data, - "test requires state eth1_data are different" - ); - - store - .put_state( - &state - .get_state_root(Slot::new(0)) - .expect("should find state root"), - prev_state, - ) - .expect("should store state"); - - assert_eq!( - eth1_block_hash_at_start_of_voting_period(store, &state), - Ok(expected_root), - "should return the eth1_data from the previous state" + vote, + blocks + .last() + .expect("should have blocks") + .clone() + .eth1_data() + .expect("should have valid eth1 data"), + "default vote must correspond to last block in candidate blocks" ); } } @@ -968,132 +765,58 @@ mod test { mod eth1_data_sets { use super::*; - fn get_voting_period_start_seconds(state: &BeaconState, spec: &ChainSpec) -> u64 { - let period = ::SlotsPerEth1VotingPeriod::to_u64(); - let voting_period_start_slot = (state.slot / period) * period; - slot_start_seconds::( - state.genesis_time, - spec.milliseconds_per_slot, - voting_period_start_slot, - ) - } - #[test] fn empty_cache() { - let log = null_logger().unwrap(); - let spec = &E::default_spec(); let state: BeaconState = BeaconState::new(0, get_eth1_data(0), spec); - let prev_eth1_hash = Hash256::zero(); let blocks = vec![]; assert_eq!( - eth1_data_sets( + get_votes_to_consider( blocks.iter(), - prev_eth1_hash, get_voting_period_start_seconds(&state, spec), &spec, - &log ), - None - ); - } - - #[test] - fn no_known_block_hash() { - let log = null_logger().unwrap(); - - let mut spec = E::default_spec(); - spec.milliseconds_per_slot = 1_000; - - let state: BeaconState = BeaconState::new(0, get_eth1_data(0), &spec); - let prev_eth1_hash = Hash256::from_low_u64_be(42); - - let blocks = vec![get_eth1_block(0, 0)]; - - assert_eq!( - eth1_data_sets( - blocks.iter(), - prev_eth1_hash, - get_voting_period_start_seconds(&state, &spec), - &spec, - &log - ), - None + HashMap::new() ); } #[test] fn ideal_scenario() { - let log = null_logger().unwrap(); - - let mut spec = E::default_spec(); - spec.milliseconds_per_slot = 1_000; + let spec = E::default_spec(); let slots_per_eth1_voting_period = ::SlotsPerEth1VotingPeriod::to_u64(); let eth1_follow_distance = spec.eth1_follow_distance; let mut state: BeaconState = BeaconState::new(0, get_eth1_data(0), &spec); state.genesis_time = 0; - state.slot = Slot::from(slots_per_eth1_voting_period * 3); + state.slot = Slot::from(slots_per_eth1_voting_period * 10); - let prev_eth1_hash = Hash256::zero(); - - let blocks = (0..eth1_follow_distance * 4) + let follow_distance_seconds = eth1_follow_distance * spec.seconds_per_eth1_block; + let voting_period_start = get_voting_period_start_seconds(&state, &spec); + let start_eth1_block = voting_period_start - follow_distance_seconds * 2; + let end_eth1_block = voting_period_start - follow_distance_seconds; + let blocks = (start_eth1_block..end_eth1_block) .map(|i| get_eth1_block(i, i)) .collect::>(); - let (new_eth1_data, all_eth1_data) = eth1_data_sets( - blocks.iter(), - prev_eth1_hash, - get_voting_period_start_seconds(&state, &spec), - &spec, - &log, - ) - .expect("should find data"); - + let votes_to_consider = + get_votes_to_consider(blocks.iter(), voting_period_start, &spec); assert_eq!( - all_eth1_data.len(), - eth1_follow_distance as usize * 2, - "all_eth1_data should have appropriate length" - ); - assert_eq!( - new_eth1_data.len(), - eth1_follow_distance as usize, - "new_eth1_data should have appropriate length" + votes_to_consider.len() as u64, + end_eth1_block - start_eth1_block, + "all produced eth1 blocks should be in votes to consider" ); - for (eth1_data, block_number) in &new_eth1_data { - assert_eq!( - all_eth1_data.get(eth1_data), - Some(block_number), - "all_eth1_data should contain all items in new_eth1_data" - ); - } - - (1..=eth1_follow_distance * 2) + (start_eth1_block..end_eth1_block) .map(|i| get_eth1_block(i, i)) .for_each(|eth1_block| { assert_eq!( eth1_block.number, - *all_eth1_data + *votes_to_consider .get(ð1_block.clone().eth1_data().unwrap()) - .expect("all_eth1_data should have expected block") - ) - }); - - (eth1_follow_distance + 1..=eth1_follow_distance * 2) - .map(|i| get_eth1_block(i, i)) - .for_each(|eth1_block| { - assert_eq!( - eth1_block.number, - *new_eth1_data - .get(ð1_block.clone().eth1_data().unwrap()) - .unwrap_or_else(|| panic!( - "new_eth1_data should have expected block #{}", - eth1_block.number - )) + .expect("votes_to_consider should have expected block numbers") ) }); } @@ -1130,13 +853,11 @@ mod test { let spec = &E::default_spec(); let state: BeaconState = BeaconState::new(0, get_eth1_data(0), spec); - let all_eth1_data = get_eth1_data_vec(slots, 0); - let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec(); + let votes_to_consider = get_eth1_data_vec(slots, 0); let votes = collect_valid_votes( &state, - HashMap::from_iter(new_eth1_data.into_iter()), - HashMap::from_iter(all_eth1_data.into_iter()), + &HashMap::from_iter(votes_to_consider.clone().into_iter()), ); assert_eq!( votes.len(), @@ -1151,10 +872,9 @@ mod test { let spec = &E::default_spec(); let mut state: BeaconState = BeaconState::new(0, get_eth1_data(0), spec); - let all_eth1_data = get_eth1_data_vec(slots, 0); - let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec(); + let votes_to_consider = get_eth1_data_vec(slots, 0); - state.eth1_data_votes = new_eth1_data[0..slots as usize / 4] + state.eth1_data_votes = votes_to_consider[0..slots as usize / 4] .iter() .map(|(eth1_data, _)| eth1_data) .cloned() @@ -1163,12 +883,11 @@ mod test { let votes = collect_valid_votes( &state, - HashMap::from_iter(new_eth1_data.clone().into_iter()), - HashMap::from_iter(all_eth1_data.into_iter()), + &HashMap::from_iter(votes_to_consider.clone().into_iter()), ); assert_votes!( votes, - new_eth1_data[0..slots as usize / 4].to_vec(), + votes_to_consider[0..slots as usize / 4].to_vec(), "should find as many votes as were in the state" ); } @@ -1179,10 +898,9 @@ mod test { let spec = &E::default_spec(); let mut state: BeaconState = BeaconState::new(0, get_eth1_data(0), spec); - let all_eth1_data = get_eth1_data_vec(slots, 0); - let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec(); + let votes_to_consider = get_eth1_data_vec(slots, 0); - let duplicate_eth1_data = new_eth1_data + let duplicate_eth1_data = votes_to_consider .last() .expect("should have some eth1 data") .clone(); @@ -1196,8 +914,7 @@ mod test { let votes = collect_valid_votes( &state, - HashMap::from_iter(new_eth1_data.into_iter()), - HashMap::from_iter(all_eth1_data.into_iter()), + &HashMap::from_iter(votes_to_consider.clone().into_iter()), ); assert_votes!( votes, @@ -1214,70 +931,6 @@ mod test { "should have four votes" ); } - - #[test] - fn non_period_tail() { - let slots = ::SlotsPerEth1VotingPeriod::to_u64(); - let spec = &E::default_spec(); - let mut state: BeaconState = BeaconState::new(0, get_eth1_data(0), spec); - state.slot = Slot::from(::SlotsPerEpoch::to_u64()) * 10; - - let all_eth1_data = get_eth1_data_vec(slots, 0); - let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec(); - - let non_new_eth1_data = all_eth1_data - .first() - .expect("should have some eth1 data") - .clone(); - - state.eth1_data_votes = vec![non_new_eth1_data.0].into(); - - let votes = collect_valid_votes( - &state, - HashMap::from_iter(new_eth1_data.into_iter()), - HashMap::from_iter(all_eth1_data.into_iter()), - ); - - assert_votes!( - votes, - vec![], - "should not find votes from all_eth1_data when it is not the period tail" - ); - } - - #[test] - fn period_tail() { - let slots_per_eth1_voting_period = ::SlotsPerEth1VotingPeriod::to_u64(); - - let slots = ::SlotsPerEth1VotingPeriod::to_u64(); - let spec = &E::default_spec(); - let mut state: BeaconState = BeaconState::new(0, get_eth1_data(0), spec); - - state.slot = Slot::from(::SlotsPerEpoch::to_u64()) * 10 - + slots_per_eth1_voting_period.integer_sqrt(); - - let all_eth1_data = get_eth1_data_vec(slots, 0); - let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec(); - - let non_new_eth1_data = all_eth1_data - .first() - .expect("should have some eth1 data") - .clone(); - - state.eth1_data_votes = vec![non_new_eth1_data.0.clone()].into(); - - let votes = collect_valid_votes( - &state, - HashMap::from_iter(new_eth1_data.into_iter()), - HashMap::from_iter(all_eth1_data.into_iter()), - ); - - assert_votes!( - votes, - vec![non_new_eth1_data], - "should find all_eth1_data votes when it is the period tail" - ); - } } mod winning_vote { diff --git a/beacon_node/beacon_chain/src/events.rs b/beacon_node/beacon_chain/src/events.rs index 91bc4a1b0..d293bcb50 100644 --- a/beacon_node/beacon_chain/src/events.rs +++ b/beacon_node/beacon_chain/src/events.rs @@ -1,6 +1,6 @@ use serde_derive::{Deserialize, Serialize}; use std::marker::PhantomData; -use types::{Attestation, BeaconBlock, Epoch, EthSpec, Hash256}; +use types::{Attestation, Epoch, EthSpec, Hash256, SignedBeaconBlock}; pub use websocket_server::WebSocketSender; pub trait EventHandler: Sized + Send + Sync { @@ -49,11 +49,11 @@ pub enum EventKind { }, BeaconBlockImported { block_root: Hash256, - block: Box>, + block: Box>, }, BeaconBlockRejected { reason: String, - block: Box>, + block: Box>, }, BeaconAttestationImported { attestation: Box>, diff --git a/beacon_node/beacon_chain/src/fork_choice/checkpoint_manager.rs b/beacon_node/beacon_chain/src/fork_choice/checkpoint_manager.rs index 14cb634b1..fd4512ce3 100644 --- a/beacon_node/beacon_chain/src/fork_choice/checkpoint_manager.rs +++ b/beacon_node/beacon_chain/src/fork_choice/checkpoint_manager.rs @@ -306,8 +306,11 @@ impl CheckpointManager { .ok_or_else(|| Error::UnknownJustifiedBlock(block_root))?; let state = chain - .get_state_caching_only_with_committee_caches(&block.state_root, Some(block.slot))? - .ok_or_else(|| Error::UnknownJustifiedState(block.state_root))?; + .get_state_caching_only_with_committee_caches( + &block.state_root(), + Some(block.slot()), + )? + .ok_or_else(|| Error::UnknownJustifiedState(block.state_root()))?; Ok(get_effective_balances(&state)) } diff --git a/beacon_node/beacon_chain/src/metrics.rs b/beacon_node/beacon_chain/src/metrics.rs index 107666396..cee354a23 100644 --- a/beacon_node/beacon_chain/src/metrics.rs +++ b/beacon_node/beacon_chain/src/metrics.rs @@ -152,8 +152,8 @@ lazy_static! { /* * Eth1 */ - pub static ref JUNK_ETH1_VOTES: Result = - try_create_int_counter("beacon_eth1_junk_votes", "Count of times we have voted junk for eth1 dat"); + pub static ref DEFAULT_ETH1_VOTES: Result = + try_create_int_counter("beacon_eth1_default_votes", "Count of times we have voted default value for eth1 data"); /* * Chain Head diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 57f05cb79..92ff1e0ca 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -16,10 +16,9 @@ use store::{ migrate::{BlockingMigrator, NullMigrator}, DiskStore, MemoryStore, Migrate, Store, }; -use tree_hash::{SignedRoot, TreeHash}; use types::{ - AggregateSignature, Attestation, BeaconBlock, BeaconState, BitList, ChainSpec, Domain, EthSpec, - Hash256, Keypair, SecretKey, Signature, Slot, + AggregateSignature, Attestation, BeaconState, BitList, ChainSpec, Domain, EthSpec, Hash256, + Keypair, SecretKey, Signature, SignedBeaconBlock, SignedRoot, Slot, }; pub use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY}; @@ -279,7 +278,7 @@ where mut state: BeaconState, slot: Slot, block_strategy: BlockStrategy, - ) -> (BeaconBlock, BeaconState) { + ) -> (SignedBeaconBlock, BeaconState) { if slot < state.slot { panic!("produce slot cannot be prior to the state slot"); } @@ -308,24 +307,19 @@ where let randao_reveal = { let epoch = slot.epoch(E::slots_per_epoch()); - let message = epoch.tree_hash_root(); let domain = self.spec.get_domain(epoch, Domain::Randao, fork); - Signature::new(&message, domain, sk) + let message = epoch.signing_root(domain); + Signature::new(message.as_bytes(), sk) }; - let (mut block, state) = self + let (block, state) = self .chain .produce_block_on_state(state, slot, randao_reveal) .expect("should produce block"); - block.signature = { - let message = block.signed_root(); - let epoch = block.slot.epoch(E::slots_per_epoch()); - let domain = self.spec.get_domain(epoch, Domain::BeaconProposer, fork); - Signature::new(&message, domain, sk) - }; + let signed_block = block.sign(sk, &state.fork, &self.spec); - (block, state) + (signed_block, state) } /// Adds attestations to the `BeaconChain` operations pool and fork choice. @@ -407,18 +401,17 @@ where .expect("should be able to set aggregation bits"); let signature = { - let message = data.tree_hash_root(); - let domain = spec.get_domain( data.target.epoch, Domain::BeaconAttester, fork, ); + let message = data.signing_root(domain); + let mut agg_sig = AggregateSignature::new(); agg_sig.add(&Signature::new( - &message, - domain, + message.as_bytes(), self.get_sk(*validator_index), )); @@ -464,7 +457,7 @@ where .head() .expect("should get head") .beacon_block - .slot; + .slot(); // Move to the next slot so we may produce some more blocks on the head. self.advance_slot(); diff --git a/beacon_node/beacon_chain/tests/store_tests.rs b/beacon_node/beacon_chain/tests/store_tests.rs index d4bb7a642..82439afe8 100644 --- a/beacon_node/beacon_chain/tests/store_tests.rs +++ b/beacon_node/beacon_chain/tests/store_tests.rs @@ -267,7 +267,7 @@ fn epoch_boundary_state_attestation_processing() { &AttestationStrategy::SomeValidators(late_validators.clone()), &head.beacon_state, head.beacon_block_root, - head.beacon_block.slot, + head.beacon_block.slot(), )); harness.advance_slot(); @@ -283,9 +283,9 @@ fn epoch_boundary_state_attestation_processing() { for attestation in late_attestations { // load_epoch_boundary_state is idempotent! let block_root = attestation.data.beacon_block_root; - let block: BeaconBlock = store.get(&block_root).unwrap().expect("block exists"); + let block = store.get_block(&block_root).unwrap().expect("block exists"); let epoch_boundary_state = store - .load_epoch_boundary_state(&block.state_root) + .load_epoch_boundary_state(&block.state_root()) .expect("no error") .expect("epoch boundary state exists"); let ebs_of_ebs = store @@ -396,7 +396,7 @@ fn check_chain_dump(harness: &TestHarness, expected_len: u64) { // Check the forwards block roots iterator against the chain dump let chain_dump_block_roots = chain_dump .iter() - .map(|checkpoint| (checkpoint.beacon_block_root, checkpoint.beacon_block.slot)) + .map(|checkpoint| (checkpoint.beacon_block_root, checkpoint.beacon_block.slot())) .collect::>(); let head = harness.chain.head().expect("should get head"); diff --git a/beacon_node/beacon_chain/tests/tests.rs b/beacon_node/beacon_chain/tests/tests.rs index 58bd10a4e..99a5276e1 100644 --- a/beacon_node/beacon_chain/tests/tests.rs +++ b/beacon_node/beacon_chain/tests/tests.rs @@ -121,7 +121,7 @@ fn iterators() { assert_eq!( *block_roots.first().expect("should have some block roots"), - (head.beacon_block_root, head.beacon_block.slot), + (head.beacon_block_root, head.beacon_block.slot()), "first block root and slot should be for the head block" ); @@ -435,7 +435,7 @@ fn attestations_with_increasing_slots() { .head() .expect("should get head") .beacon_block - .slot, + .slot(), ), ); @@ -524,7 +524,7 @@ fn run_skip_slot_test(skip_slots: u64) { .head() .expect("should get head") .beacon_block - .slot, + .slot(), Slot::new(skip_slots + 1) ); assert_eq!( @@ -533,7 +533,7 @@ fn run_skip_slot_test(skip_slots: u64) { .head() .expect("should get head") .beacon_block - .slot, + .slot(), Slot::new(0) ); @@ -566,7 +566,7 @@ fn run_skip_slot_test(skip_slots: u64) { .head() .expect("should get head") .beacon_block - .slot, + .slot(), Slot::new(skip_slots + 1) ); } diff --git a/beacon_node/eth1/tests/test.rs b/beacon_node/eth1/tests/test.rs index 2d44916c5..83c26fa2f 100644 --- a/beacon_node/eth1/tests/test.rs +++ b/beacon_node/eth1/tests/test.rs @@ -4,7 +4,6 @@ use eth1::http::{get_deposit_count, get_deposit_logs_in_range, get_deposit_root, use eth1::{Config, Service}; use eth1::{DepositCache, DepositLog}; use eth1_test_rig::GanacheEth1Instance; -use exit_future; use futures::Future; use merkle_proof::verify_merkle_proof; use std::ops::Range; @@ -101,90 +100,6 @@ fn get_block_number(runtime: &mut Runtime, web3: &Web3) -> u64 { .expect("should get block number") } -mod auto_update { - use super::*; - - #[test] - fn can_auto_update() { - let mut env = new_env(); - let log = env.core_context().log; - let runtime = env.runtime(); - - let eth1 = runtime - .block_on(GanacheEth1Instance::new()) - .expect("should start eth1 environment"); - let deposit_contract = ð1.deposit_contract; - let web3 = eth1.web3(); - - let now = get_block_number(runtime, &web3); - - let service = Service::new( - Config { - endpoint: eth1.endpoint(), - deposit_contract_address: deposit_contract.address(), - deposit_contract_deploy_block: now, - lowest_cached_block_number: now, - follow_distance: 0, - block_cache_truncation: None, - ..Config::default() - }, - log, - ); - - // NOTE: this test is sensitive to the response speed of the external web3 server. If - // you're experiencing failures, try increasing the update_interval. - let update_interval = Duration::from_millis(3000); - - assert_eq!( - service.block_cache_len(), - 0, - "should have imported no blocks" - ); - assert_eq!( - service.deposit_cache_len(), - 0, - "should have imported no deposits" - ); - - let (_exit, signal) = exit_future::signal(); - - runtime.executor().spawn(service.auto_update(signal)); - - let n = 4; - - for _ in 0..n { - deposit_contract - .deposit(runtime, random_deposit_data()) - .expect("should do first deposits"); - } - - std::thread::sleep(update_interval * 5); - - assert!( - service.deposit_cache_len() >= n, - "should have imported n deposits" - ); - - for _ in 0..n { - deposit_contract - .deposit(runtime, random_deposit_data()) - .expect("should do second deposits"); - } - - std::thread::sleep(update_interval * 4); - - assert!( - service.block_cache_len() >= n * 2, - "should have imported all blocks" - ); - assert!( - service.deposit_cache_len() >= n * 2, - "should have imported all deposits, not {}", - service.deposit_cache_len() - ); - } -} - mod eth1_cache { use super::*; diff --git a/beacon_node/genesis/src/interop.rs b/beacon_node/genesis/src/interop.rs index 49010ab0b..5a91967fb 100644 --- a/beacon_node/genesis/src/interop.rs +++ b/beacon_node/genesis/src/interop.rs @@ -4,11 +4,7 @@ use rayon::prelude::*; use ssz::Encode; use state_processing::initialize_beacon_state_from_eth1; use std::time::SystemTime; -use tree_hash::SignedRoot; -use types::{ - BeaconState, ChainSpec, DepositData, Domain, EthSpec, Fork, Hash256, Keypair, PublicKey, - Signature, -}; +use types::{BeaconState, ChainSpec, DepositData, EthSpec, Hash256, Keypair, PublicKey, Signature}; /// Builds a genesis state as defined by the Eth2 interop procedure (see below). /// @@ -39,12 +35,7 @@ pub fn interop_genesis_state( signature: Signature::empty_signature().into(), }; - let domain = spec.get_domain( - spec.genesis_slot.epoch(T::slots_per_epoch()), - Domain::Deposit, - &Fork::default(), - ); - data.signature = Signature::new(&data.signed_root()[..], domain, &keypair.sk).into(); + data.signature = data.create_signature(&keypair.sk, spec); data }) diff --git a/beacon_node/network/src/message_handler.rs b/beacon_node/network/src/message_handler.rs index a7c8ed40b..97212a55e 100644 --- a/beacon_node/network/src/message_handler.rs +++ b/beacon_node/network/src/message_handler.rs @@ -14,7 +14,7 @@ use slog::{debug, o, trace, warn}; use ssz::{Decode, DecodeError}; use std::sync::Arc; use tokio::sync::mpsc; -use types::{Attestation, AttesterSlashing, BeaconBlock, ProposerSlashing, VoluntaryExit}; +use types::{Attestation, AttesterSlashing, ProposerSlashing, SignedBeaconBlock, VoluntaryExit}; /// Handles messages received from the network and client and organises syncing. This /// functionality of this struct is to validate an decode messages from the network before @@ -319,9 +319,9 @@ impl MessageHandler { fn decode_gossip_block( &self, beacon_block: Vec, - ) -> Result, DecodeError> { + ) -> Result, DecodeError> { //TODO: Apply verification before decoding. - BeaconBlock::from_ssz_bytes(&beacon_block) + SignedBeaconBlock::from_ssz_bytes(&beacon_block) } fn decode_gossip_attestation( @@ -355,13 +355,13 @@ impl MessageHandler { /* Req/Resp Domain Decoding */ - /// Verifies and decodes an ssz-encoded `BeaconBlock`. If `None` is passed, this represents a + /// Verifies and decodes an ssz-encoded `SignedBeaconBlock`. If `None` is passed, this represents a /// stream termination. fn decode_beacon_block( &self, beacon_block: Vec, - ) -> Result, DecodeError> { + ) -> Result, DecodeError> { //TODO: Implement faster block verification before decoding entirely - BeaconBlock::from_ssz_bytes(&beacon_block) + SignedBeaconBlock::from_ssz_bytes(&beacon_block) } } diff --git a/beacon_node/network/src/message_processor.rs b/beacon_node/network/src/message_processor.rs index f12d4d0de..e22ae567a 100644 --- a/beacon_node/network/src/message_processor.rs +++ b/beacon_node/network/src/message_processor.rs @@ -11,8 +11,7 @@ use ssz::Encode; use std::sync::Arc; use store::Store; use tokio::sync::{mpsc, oneshot}; -use tree_hash::SignedRoot; -use types::{Attestation, BeaconBlock, Epoch, EthSpec, Hash256, Slot}; +use types::{Attestation, Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot}; //TODO: Rate limit requests @@ -263,7 +262,7 @@ impl MessageProcessor { } else if self .chain .store - .exists::>(&remote.head_root) + .exists::>(&remote.head_root) .unwrap_or_else(|_| false) { trace!( @@ -300,7 +299,7 @@ impl MessageProcessor { ) { let mut send_block_count = 0; for root in request.block_roots.iter() { - if let Ok(Some(block)) = self.chain.store.get::>(root) { + if let Ok(Some(block)) = self.chain.store.get_block(root) { self.network.send_rpc_response( peer_id.clone(), request_id, @@ -380,11 +379,11 @@ impl MessageProcessor { let mut blocks_sent = 0; for root in block_roots { - if let Ok(Some(block)) = self.chain.store.get::>(&root) { + if let Ok(Some(block)) = self.chain.store.get_block(&root) { // Due to skip slots, blocks could be out of the range, we ensure they are in the // range before sending - if block.slot >= req.start_slot - && block.slot < req.start_slot + req.count * req.step + if block.slot() >= req.start_slot + && block.slot() < req.start_slot + req.count * req.step { blocks_sent += 1; self.network.send_rpc_response( @@ -437,7 +436,7 @@ impl MessageProcessor { &mut self, peer_id: PeerId, request_id: RequestId, - beacon_block: Option>, + beacon_block: Option>, ) { let beacon_block = beacon_block.map(Box::new); trace!( @@ -458,7 +457,7 @@ impl MessageProcessor { &mut self, peer_id: PeerId, request_id: RequestId, - beacon_block: Option>, + beacon_block: Option>, ) { let beacon_block = beacon_block.map(Box::new); trace!( @@ -479,7 +478,11 @@ impl MessageProcessor { /// Attempts to apply to block to the beacon chain. May queue the block for later processing. /// /// Returns a `bool` which, if `true`, indicates we should forward the block to our peers. - pub fn on_block_gossip(&mut self, peer_id: PeerId, block: BeaconBlock) -> bool { + pub fn on_block_gossip( + &mut self, + peer_id: PeerId, + block: SignedBeaconBlock, + ) -> bool { match self.chain.process_block(block.clone()) { Ok(outcome) => match outcome { BlockProcessingOutcome::Processed { .. } => { @@ -528,8 +531,8 @@ impl MessageProcessor { self.log, "Invalid gossip beacon block"; "outcome" => format!("{:?}", other), - "block root" => format!("{}", Hash256::from_slice(&block.signed_root()[..])), - "block slot" => block.slot + "block root" => format!("{}", block.canonical_root()), + "block slot" => block.slot() ); trace!( self.log, diff --git a/beacon_node/network/src/sync/manager.rs b/beacon_node/network/src/sync/manager.rs index 8b7e210c9..032490554 100644 --- a/beacon_node/network/src/sync/manager.rs +++ b/beacon_node/network/src/sync/manager.rs @@ -50,7 +50,7 @@ use std::collections::HashSet; use std::ops::Sub; use std::sync::Weak; use tokio::sync::{mpsc, oneshot}; -use types::{BeaconBlock, EthSpec, Hash256}; +use types::{EthSpec, Hash256, SignedBeaconBlock}; /// The number of slots ahead of us that is allowed before requesting a long-range (batch) Sync /// from a peer. If a peer is within this tolerance (forwards or backwards), it is treated as a @@ -73,18 +73,18 @@ pub enum SyncMessage { BlocksByRangeResponse { peer_id: PeerId, request_id: RequestId, - beacon_block: Option>>, + beacon_block: Option>>, }, /// A `BlocksByRoot` response has been received. BlocksByRootResponse { peer_id: PeerId, request_id: RequestId, - beacon_block: Option>>, + beacon_block: Option>>, }, /// A block with an unknown parent has been received. - UnknownBlock(PeerId, Box>), + UnknownBlock(PeerId, Box>), /// A peer has sent an object that references a block that is unknown. This triggers the /// manager to attempt to find the block matching the unknown hash. @@ -107,7 +107,7 @@ pub enum SyncMessage { /// Maintains a sequential list of parents to lookup and the lookup's current state. struct ParentRequests { /// The blocks that have currently been downloaded. - downloaded_blocks: Vec>, + downloaded_blocks: Vec>, /// The number of failed attempts to retrieve a parent block. If too many attempts occur, this /// lookup is failed and rejected. @@ -290,7 +290,7 @@ impl SyncManager { &mut self, peer_id: PeerId, request_id: RequestId, - block: Option>, + block: Option>, ) { // check if this is a single block lookup - i.e we were searching for a specific hash if block.is_some() { @@ -344,7 +344,7 @@ impl SyncManager { fn single_block_lookup_response( &mut self, peer_id: PeerId, - block: BeaconBlock, + block: SignedBeaconBlock, expected_block_hash: Hash256, ) { // verify the hash is correct and try and process the block @@ -398,7 +398,7 @@ impl SyncManager { /// A block has been sent to us that has an unknown parent. This begins a parent lookup search /// to find the parent or chain of parents that match our current chain. - fn add_unknown_block(&mut self, peer_id: PeerId, block: BeaconBlock) { + fn add_unknown_block(&mut self, peer_id: PeerId, block: SignedBeaconBlock) { // If we are not in regular sync mode, ignore this block if self.state != ManagerState::Regular { return; @@ -516,7 +516,7 @@ impl SyncManager { // fail loudly } let previous_index = parent_request.downloaded_blocks.len() - 2; - let expected_hash = parent_request.downloaded_blocks[previous_index].parent_root; + let expected_hash = parent_request.downloaded_blocks[previous_index].parent_root(); // Note: the length must be greater than 2 so this cannot panic. let block_hash = parent_request @@ -626,7 +626,7 @@ impl SyncManager { .downloaded_blocks .last() .expect("The parent queue should never be empty") - .parent_root; + .parent_root(); let request = BlocksByRootRequest { block_roots: vec![parent_hash], }; diff --git a/beacon_node/network/src/sync/range_sync/batch.rs b/beacon_node/network/src/sync/range_sync/batch.rs index a60614969..3e516a93b 100644 --- a/beacon_node/network/src/sync/range_sync/batch.rs +++ b/beacon_node/network/src/sync/range_sync/batch.rs @@ -9,7 +9,7 @@ use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::hash::{Hash, Hasher}; use std::ops::Sub; -use types::{BeaconBlock, EthSpec, Hash256, Slot}; +use types::{EthSpec, Hash256, SignedBeaconBlock, Slot}; #[derive(Copy, Clone, Debug, PartialEq)] pub struct BatchId(pub u64); @@ -55,7 +55,7 @@ pub struct Batch { /// Marks the batch as undergoing a re-process, with a hash of the original blocks it received. pub original_hash: Option, /// The blocks that have been downloaded. - pub downloaded_blocks: Vec>, + pub downloaded_blocks: Vec>, } impl Eq for Batch {} @@ -164,7 +164,7 @@ impl PendingBatches { /// Adds a block to the batches if the request id exists. Returns None if there is no batch /// matching the request id. - pub fn add_block(&mut self, request_id: RequestId, block: BeaconBlock) -> Option<()> { + pub fn add_block(&mut self, request_id: RequestId, block: SignedBeaconBlock) -> Option<()> { let batch = self.batches.get_mut(&request_id)?; batch.downloaded_blocks.push(block); Some(()) diff --git a/beacon_node/network/src/sync/range_sync/batch_processing.rs b/beacon_node/network/src/sync/range_sync/batch_processing.rs index a054b6b31..484bab9ba 100644 --- a/beacon_node/network/src/sync/range_sync/batch_processing.rs +++ b/beacon_node/network/src/sync/range_sync/batch_processing.rs @@ -65,7 +65,7 @@ fn process_batch( // The block was valid and we processed it successfully. trace!( log, "Imported block from network"; - "slot" => block.slot, + "slot" => block.slot(), "block_root" => format!("{}", block_root), ); successful_block_import = true; @@ -75,21 +75,21 @@ fn process_batch( warn!( log, "Parent block is unknown"; "parent_root" => format!("{}", parent), - "baby_block_slot" => block.slot, + "baby_block_slot" => block.slot(), ); if successful_block_import { run_fork_choice(chain, log); } return Err(format!( "Block at slot {} has an unknown parent.", - block.slot + block.slot() )); } BlockProcessingOutcome::BlockIsAlreadyKnown => { // this block is already known to us, move to the next debug!( log, "Imported a block that is already known"; - "block_slot" => block.slot, + "block_slot" => block.slot(), ); } BlockProcessingOutcome::FutureSlot { @@ -110,7 +110,7 @@ fn process_batch( } return Err(format!( "Block at slot {} is too far in the future", - block.slot + block.slot() )); } else { // The block is in the future, but not too far. @@ -144,7 +144,7 @@ fn process_batch( if successful_block_import { run_fork_choice(chain, log); } - return Err(format!("Invalid block at slot {}", block.slot)); + return Err(format!("Invalid block at slot {}", block.slot())); } } } else { diff --git a/beacon_node/network/src/sync/range_sync/chain.rs b/beacon_node/network/src/sync/range_sync/chain.rs index d378184c9..e548134f7 100644 --- a/beacon_node/network/src/sync/range_sync/chain.rs +++ b/beacon_node/network/src/sync/range_sync/chain.rs @@ -10,7 +10,7 @@ use slog::{crit, debug, warn}; use std::collections::HashSet; use std::sync::Weak; use tokio::sync::mpsc; -use types::{BeaconBlock, Hash256, Slot}; +use types::{Hash256, SignedBeaconBlock, Slot}; /// Blocks are downloaded in batches from peers. This constant specifies how many blocks per batch /// is requested. There is a timeout for each batch request. If this value is too high, we will @@ -143,7 +143,7 @@ impl SyncingChain { &mut self, network: &mut SyncNetworkContext, request_id: RequestId, - beacon_block: &Option>, + beacon_block: &Option>, ) -> Option<()> { if let Some(block) = beacon_block { // This is not a stream termination, simply add the block to the request @@ -171,11 +171,11 @@ impl SyncingChain { // 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) { + 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 { - warn!(self.log, "BlocksByRange response returned out of range blocks"; - "response_initial_slot" => batch.downloaded_blocks[0].slot, + if batch.start_slot > batch.downloaded_blocks[0].slot() || batch.end_slot < last_slot { + warn!(self.log, "BlocksByRange response returned out of range blocks"; + "response_initial_slot" => batch.downloaded_blocks[0].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 @@ -274,7 +274,7 @@ impl SyncingChain { // 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, + "processed_batch_id" => *batch.id, "expected_id" => *self.to_be_processed_id); } @@ -298,7 +298,7 @@ impl SyncingChain { let processed_batch = self.processed_batches.remove(0); if *processed_batch.id >= *batch.id { crit!(self.log, "A processed batch had a greater id than the current process id"; - "processed_id" => *processed_batch.id, + "processed_id" => *processed_batch.id, "current_id" => *batch.id); } @@ -313,8 +313,8 @@ impl SyncingChain { // If the same peer corrected it's mistake, we allow it.... for // now. debug!(self.log, "Re-processed batch validated. Downvoting original peer"; - "batch_id" => *processed_batch.id, - "original_peer" => format!("{}",processed_batch.original_peer), + "batch_id" => *processed_batch.id, + "original_peer" => format!("{}",processed_batch.original_peer), "new_peer" => format!("{}", processed_batch.current_peer)); network.downvote_peer(processed_batch.original_peer); } @@ -325,7 +325,7 @@ impl SyncingChain { // Add the current batch to processed batches to be verified in the future. We are // only uncertain about this batch, if it has not returned all blocks. - if batch.downloaded_blocks.last().map(|block| block.slot) + if batch.downloaded_blocks.last().map(|block| block.slot()) != Some(batch.end_slot.saturating_sub(1u64)) { self.processed_batches.push(batch); @@ -436,12 +436,12 @@ impl SyncingChain { batch.current_peer = new_peer.clone(); - debug!(self.log, "Re-requesting batch"; - "start_slot" => batch.start_slot, + debug!(self.log, "Re-requesting batch"; + "start_slot" => batch.start_slot, "end_slot" => batch.end_slot, "id" => *batch.id, "peer" => format!("{}", batch.current_peer), - "head_root"=> format!("{}", batch.head_root), + "head_root"=> format!("{}", batch.head_root), "retries" => batch.retries, "re-processes" => batch.reprocess_retries); self.send_batch(network, batch); @@ -522,9 +522,9 @@ impl SyncingChain { request_id: RequestId, ) -> Option { if let Some(batch) = self.pending_batches.remove(request_id) { - warn!(self.log, "Batch failed. RPC Error"; - "id" => *batch.id, - "retries" => batch.retries, + warn!(self.log, "Batch failed. RPC Error"; + "id" => *batch.id, + "retries" => batch.retries, "peer" => format!("{:?}", peer_id)); Some(self.failed_batch(network, batch)) @@ -564,7 +564,7 @@ impl SyncingChain { batch.current_peer = new_peer.clone(); debug!(self.log, "Re-Requesting batch"; "start_slot" => batch.start_slot, - "end_slot" => batch.end_slot, + "end_slot" => batch.end_slot, "id" => *batch.id, "peer" => format!("{:?}", batch.current_peer), "head_root"=> format!("{}", batch.head_root)); @@ -587,11 +587,11 @@ impl SyncingChain { // find the next pending batch and request it from the peer if let Some(peer_id) = self.get_next_peer() { if let Some(batch) = self.get_next_batch(peer_id) { - debug!(self.log, "Requesting batch"; - "start_slot" => batch.start_slot, + debug!(self.log, "Requesting batch"; + "start_slot" => batch.start_slot, "end_slot" => batch.end_slot, "id" => *batch.id, - "peer" => format!("{}", batch.current_peer), + "peer" => format!("{}", batch.current_peer), "head_root"=> format!("{}", batch.head_root)); // send the batch self.send_batch(network, batch); diff --git a/beacon_node/network/src/sync/range_sync/range.rs b/beacon_node/network/src/sync/range_sync/range.rs index ad6162936..59c6ff598 100644 --- a/beacon_node/network/src/sync/range_sync/range.rs +++ b/beacon_node/network/src/sync/range_sync/range.rs @@ -52,7 +52,7 @@ use slog::{debug, error, trace, warn}; use std::collections::HashSet; use std::sync::Weak; use tokio::sync::mpsc; -use types::{BeaconBlock, EthSpec}; +use types::{EthSpec, SignedBeaconBlock}; /// The primary object dealing with long range/batch syncing. This contains all the active and /// non-active chains that need to be processed before the syncing is considered complete. This @@ -231,7 +231,7 @@ impl RangeSync { network: &mut SyncNetworkContext, peer_id: PeerId, request_id: RequestId, - beacon_block: Option>, + beacon_block: Option>, ) { // Find the request. Most likely the first finalized chain (the syncing chain). If there // are no finalized chains, then it will be a head chain. At most, there should only be diff --git a/beacon_node/rest_api/api_spec.yaml b/beacon_node/rest_api/api_spec.yaml deleted file mode 100644 index 23608807e..000000000 --- a/beacon_node/rest_api/api_spec.yaml +++ /dev/null @@ -1,2064 +0,0 @@ -openapi: "3.0.2" -info: - title: "Lighthouse REST API" - description: "" - version: "0.2.0" - license: - name: "Apache 2.0" - url: "https://www.apache.org/licenses/LICENSE-2.0.html" -tags: - - name: Phase0 - description: Endpoints which will be implemented for phase 0 of Ethereum Serenity - - name: Phase1 - description: Endpoints which will be implemented for phase 1 of Ethereum Serenity - - name: Future - description: Potential future endpoints or optional nice-to-haves - - name: RFC - description: Do we need these endpoints at all? This is a request for comments if you think they're useful. - -paths: - /node/version: - get: - tags: - - Phase0 - summary: "Get version string of the running beacon node." - description: "Requests that the beacon node identify information about its implementation in a format similar to a [HTTP User-Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3) field." - responses: - 200: - description: Request successful - content: - application/json: - schema: - $ref: '#/components/schemas/version' - 500: - $ref: '#/components/responses/InternalError' - - /node/genesis_time: - get: - tags: - - Phase0 - summary: "Get the genesis_time parameter from beacon node configuration." - description: "Requests the genesis_time parameter from the beacon node, which should be consistent across all beacon nodes that follow the same beacon chain." - responses: - 200: - description: Request successful - content: - application/json: - schema: - $ref: '#/components/schemas/genesis_time' - 500: - $ref: '#/components/responses/InternalError' - - - /node/syncing: - get: - tags: - - Phase0 - summary: "Poll to see if the the beacon node is syncing." - description: "Requests the beacon node to describe if it's currently syncing or not, and if it is, what block it is up to. This is modelled after the Eth1.0 JSON-RPC eth_syncing call.." - responses: - 200: - description: Request successful - content: - application/json: - schema: - type: object - properties: - is_syncing: - type: boolean - description: "A boolean of whether the node is currently syncing or not." - sync_status: - $ref: '#/components/schemas/SyncingStatus' - 500: - $ref: '#/components/responses/InternalError' - - /network/enr: - get: - tags: - - Phase0 - summary: "Get the node's Ethereum Node Record (ENR)." - description: "The Ethereum Node Record (ENR) contains a compressed public key, an IPv4 address, a TCP port and a UDP port, which is all encoded using base64. This endpoint fetches the base64 encoded version of the ENR for the running beacon node." - responses: - 200: - description: Request successful - content: - application/json: - schema: - $ref: '#/components/schemas/ENR' - 500: - $ref: '#/components/responses/InternalError' - - /network/peer_count: - get: - tags: - - Phase0 - summary: "The number of established peers" - description: "Requests the beacon node to identify the number of peers with which an established connection is currently maintained." - responses: - 200: - description: Request successful - content: - application/json: - schema: - type: integer - format: uint64 - example: 25 - 500: - $ref: '#/components/responses/InternalError' - - /network/peer_id: - get: - tags: - - Phase0 - summary: "Get the node's libp2p peer ID." - description: "Requests the node to provide it's libp2p ['peer ID'](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md), which is a base58 encoded SHA2-256 'multihash' of the node's public key struct." - responses: - 200: - description: Request successful - content: - application/json: - schema: - type: string - format: byte - example: "QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N" - pattern: "^[1-9A-HJ-NP-Za-km-z]{46}$" - 500: - $ref: '#/components/responses/InternalError' - - /network/peers: - get: - tags: - - Phase0 - summary: "List the networking peers with which the node is communicating." - description: "Requests that the beacon node identify all of the peers with which it is communicating, including established connections and connections which it is attempting to establish." - responses: - 200: - description: Request successful - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Peer' - 500: - $ref: '#/components/responses/InternalError' - - /network/listen_port: - get: - tags: - - Phase0 - summary: "Get the TCP port number for the libp2p listener." - description: "Libp2p is configured to listen to a particular TCP port upon startup of the beacon node. This endpoint returns the port number that the beacon node is listening on. Please note, this is for the libp2p communications, not for discovery." - responses: - 200: - description: Request successful - content: - application/json: - schema: - type: integer - format: uint16 - example: 9000 - 500: - $ref: '#/components/responses/InternalError' - - /network/listen_addresses: - get: - tags: - - Phase0 - summary: "Identify the port and addresses listened to by the beacon node." - description: "Libp2p is configured to listen to a particular address, on a particular port. This address is represented the [`multiaddr`](https://multiformats.io/multiaddr/) format, and this endpoint requests the beacon node to list all listening addresses in this format." - responses: - 200: - description: Request successful - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/multiaddr' - 500: - $ref: '#/components/responses/InternalError' - - /network/stats: - get: - tags: - - RFC - summary: "Get some simple network statistics from the node." - description: "Request that the beacon node provide some historical summary information about its networking interface." - #TODO: Do we actually collect these stats? Should we? - responses: - 200: - description: Request successful - content: - application/json: - schema: - type: object - properties: - bytes_sent: - type: integer - format: uint64 - description: "The total number of bytes sent by the beacon node since it was started." - bytes_received: - type: integer - format: uint64 - description: "The total number of bytes sent by the beacon node since it was started." - peers_seen: - type: integer - format: uint64 - description: "The total number of unique peers (by multiaddr) that have been discovered since the beacon node instance was started." - #TODO: This might be too difficult to collect - - /network/block_discovery: - get: - tags: - - RFC - summary: "Identify the time at which particular blocks were first seen." - description: "Request the node to provide the time at which particular blocks were first seen on the network." - parameters: - - name: block_roots - description: "Provide an array of block roots for which the discovered time is to be returned." - in: query - required: false - schema: - type: array - items: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - responses: - 200: - description: Success response - content: - application/json: - schema: - type: array - items: - type: object - properties: - root: - type: string - format: bytes - description: "The merkle root of the block." - pattern: "^0x[a-fA-F0-9]{64}$" - discovered_time: - type: integer - format: uint64 - description: "UNIX time in milliseconds that the block was first discovered, either from a network peer or the validator client." - - /beacon/head: - get: - tags: - - Phase0 - summary: "Detail the current perspective of the beacon node." - description: "Request the beacon node to identify the most up-to-date information about the beacon chain from its perspective. This includes the latest block, which slots have been finalized, etc." - responses: - 200: - description: Success response - content: - application/json: - schema: - type: object - description: "The latest information about the head of the beacon chain." - properties: - slot: - type: integer - format: uint64 - description: "The slot of the head block." - block_root: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The merkle tree root of the canonical head block in the beacon node." - state_root: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The merkle tree root of the current beacon state." - finalized_slot: - type: integer - format: uint64 - description: "The slot number of the most recent finalized slot." - finalized_block_root: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The block root for the most recent finalized block." - justified_slot: - type: integer - format: uint64 - description: "The slot number of the most recent justified slot." - justified_block_root: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The block root of the most recent justified block." - previous_justified_slot: - type: integer - format: uint64 - description: "The slot number of the second most recent justified slot." - previous_justified_block_root: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The block root of the second most recent justified block." - 500: - $ref: '#/components/responses/InternalError' - - - /beacon/block: - get: - tags: - - Phase0 - summary: 'Retrieve blocks by root or slot.' - description: "Request that the beacon node return beacon chain blocks that match the provided criteria (a block root or beacon chain slot). Only one of the parameters can be be provided at a time." - parameters: - - name: root - description: "Filter by block root." - in: query - required: false - schema: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - - name: slot - description: "Filter blocks by slot number. Only one block which has been finalized, or is believed to be the canonical block for that slot, is returned." - in: query - required: false - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response. - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/BeaconBlock' - 400: - $ref: '#/components/responses/InvalidRequest' - 500: - $ref: '#/components/responses/InternalError' - - /beacon/block_root: - get: - tags: - - Phase0 - summary: "Retrieve the canonical block root, given a particular slot." - description: "Request that the beacon node return the root of the canonical beacon chain block, which matches the provided slot number." - parameters: - - name: slot - description: "Filter blocks by slot number. Only one block which has been finalized, or is believed to be the canonical block for that slot, is returned." - in: query - required: true - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response. - content: - application/json: - schema: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The 0x prefixed block root." - 400: - $ref: '#/components/responses/InvalidRequest' - 500: - $ref: '#/components/responses/InternalError' - - /beacon/blocks: - get: - tags: - - Phase0 - summary: 'Retrieve blocks by root, slot, or epoch.' - description: "Request that the node return beacon chain blocks that match the provided criteria (a block root, beacon chain slot, or epoch). Only one of the parameters should be provided as a criteria." - parameters: - - name: root - description: "Filter by block root, returning a single block." - in: query - required: false - schema: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - - name: slot - description: "Filter blocks by slot number. It is possible that multiple blocks will be returned if the slot has not yet been finalized, or if the node has seen blocks from multiple forks." - #TODO: Is this description accurate? - in: query - required: false - schema: - type: integer - format: uint64 - - name: epoch - description: "Filter blocks by epoch, returning all blocks found for the provided epoch. It is possible that multiple blocks will be returned with the same slot number if the slot has not yet been finalized, or if the node has seen blocks from multiple forks." - #TODO: Should this actually return no more than one block per slot, if it has been finalized? i.e. not blocks on multiple forks? - in: query - required: false - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response. - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/BeaconBlock' - 400: - $ref: '#/components/responses/InvalidRequest' - #TODO: Make this request error more specific if one of the parameters is not provided correctly. - - - /beacon/fork: - get: - tags: - - Phase0 - summary: 'Retrieve the current Fork information.' - description: 'Request the beacon node identify the fork it is currently on, from the beacon state.' - responses: - 200: - description: Success response. - content: - application/json: - schema: - $ref: '#/components/schemas/Fork' - 500: - $ref: '#/components/responses/InternalError' - - - /beacon/attestations: - get: - tags: - - Phase0 - summary: 'Retrieve attestations by root, slot, or epoch.' - description: "Request that the node return all attestations which it has seen, that match the provided criteria (a block root, beacon chain slot, or epoch). Only one of the parameters should be provided as a criteria." - parameters: - - name: root - description: "Filter by block root, returning attestations associated with that block." - in: query - required: false - schema: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - - name: slot - description: "Filter attestations by slot number." - #TODO: Is this description accurate? - in: query - required: false - schema: - type: integer - format: uint64 - - name: epoch - description: "Filter attestations by epoch number." - #TODO: Should this actually return no more than one block per slot, if it has been finalized? i.e. not blocks on multiple forks? - in: query - required: false - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response. - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Attestation' - 400: - $ref: '#/components/responses/InvalidRequest' - #TODO: Make this request error more specific if one of the parameters is not provided correctly. - - /beacon/attestations/pending: - get: - tags: - - Phase0 - summary: 'Retrieve all pending attestations.' - description: "Request that the node return all attestations which is currently holding in a pending state; i.e. is not associated with a finalized slot." - responses: - 200: - description: Success response. - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Attestation' - 400: - $ref: '#/components/responses/InvalidRequest' - #TODO: Make this request error more specific if one of the parameters is not provided correctly. - - /beacon/validators: - get: - tags: - - Phase0 - summary: "List the set of active validators for an epoch." - description: "Request the beacon node to list the active validators for the specified epoch, or the current epoch if none is specified." - parameters: - - name: epoch - description: "The epoch for which the list of validators should be returned." - in: query - required: false - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response - content: - application/json: - schema: - type: object - properties: - epoch: - type: integer - format: uint64 - description: "The epoch in which the list of validators are active." - validators: - type: array - items: - $ref: '#/components/schemas/Validator' - - /beacon/validators/activesetchanges: - get: - tags: - - RFC - summary: "Retrieve the changes in active validator set." - description: "Request that the beacon node describe the changes that occurred at the specified epoch, as compared with the prior epoch." - parameters: - - name: epoch - description: "The epoch for which the validator change comparison should be made. The current epoch is used if this value is omitted." - in: query - required: false - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response - content: - application/json: - schema: - type: object - properties: - epoch: - type: integer - format: uint64 - description: "The epoch for which the returned active validator changes are provided." - activated_public_keys: - type: array - description: "The list of validator public keys which were activated in the epoch." - items: - $ref: '#/components/schemas/pubkey' - exited_public_keys: - type: array - description: "The list of validator public keys which exited in the epoch." - items: - $ref: '#/components/schemas/pubkey' - ejected_public_keys: - type: array - description: "The list of validator public keys which were ejected in the epoch." - items: - $ref: '#/components/schemas/pubkey' - - /beacon/validators/assignments: - get: - tags: - - RFC - summary: "Retrieve the assigned responsibilities for validators in a particular epoch." - description: "Request that the beacon node list the duties which have been assigned to the active validator set in a particular epoch." - parameters: - - name: epoch - description: "The epoch for which the validator assignments should be made. The current epoch is used if this value is omitted." - in: query - required: false - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response - content: - application/json: - schema: - type: object - properties: - epoch: - type: integer - format: uint64 - description: "The epoch for which the returned active validator changes are provided." - duties: - type: array - items: - $ref: '#/components/schemas/ValidatorDuty' - #TODO: This does not include the crosslink committee value, which must be included for Phase1? - - /beacon/validators/indices: - get: - tags: - - Phase0 - summary: "Resolve a set of validator public keys to their validator indices." - description: "Attempt to resolve the public key of a set of validators to their corresponding ValidatorIndex values. Generally the mapping from validator public key to index should never change, however it is possible in some scenarios." - parameters: - - name: pubkeys - in: query - required: true - description: "An array of hex-encoded BLS public keys, for which the ValidatorIndex values should be returned." - schema: - type: array - items: - $ref: '#/components/schemas/pubkey' - minItems: 1 - responses: - 200: - description: Success response - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ValidatorIndexMapping' - - /beacon/validators/pubkeys: - get: - tags: - - Phase0 - summary: "Resolve a set of validator indicies to their public keys." - description: "Attempt to resolve the ValidatorIndex of a set of validators to their corresponding public keys. Generally the mapping from ValidatorIndex to public key should never change, however it is possible in some scenarios." - parameters: - - name: indices - in: query - required: true - description: "An array of ValidatorIndex values, for which the public keys should be returned." - schema: - type: array - items: - type: integer - format: uint64 - description: "The ValidatorIndex values to be resolved." - minItems: 1 - responses: - 200: - description: Success response - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ValidatorIndexMapping' - - /beacon/validators/balances: - get: - tags: - - RFC - summary: "Retrieve the balances of validators at a specified epoch." - description: "Retrieve the balances of validators at a specified epoch (or the current epoch if none specified). The list of balances can be filtered by providing a list of validator public keys or indices." - parameters: - - name: epoch - in: query - required: false - description: "The epoch at which the balances are to be measured." - schema: - type: integer - format: uint64 - - name: validator_pubkeys - in: query - required: false - description: "An array of hex-encoded BLS public keys, for which the balances should be returned." - schema: - type: array - items: - $ref: '#/components/schemas/pubkey' - minItems: 1 - - name: validator_indices - in: query - required: false - description: "An array of ValidatorIndex values, for which the balances should be returned." - schema: - type: array - items: - $ref: '#/components/schemas/pubkey' - minItems: 1 - responses: - 200: - description: Success response - content: - application/json: - schema: - type: object - properties: - epoch: - type: integer - format: uint64 - description: "The epoch for which the returned active validator changes are provided." - balances: - type: array - items: - title: ValidatorBalances - type: object - properties: - pubkey: - $ref: '#/components/schemas/pubkey' - index: - type: integer - format: uint64 - description: "The global ValidatorIndex of the validator." - balance: - type: integer - format: uint64 - description: "The balance of the validator at the specified epoch, expressed in Gwei" - - /beacon/validators/participation: - get: - tags: - - RFC - summary: "Retrieve aggregate information about validator participation in an epoch." - description: "Retrieve some aggregate information about the participation of validators in a specified epoch (or the current epoch if none specified)." - parameters: - - name: epoch - in: query - required: false - description: "The epoch at which the participation is to be measured." - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response - content: - application/json: - schema: - type: object - properties: - epoch: - type: string - format: uint64 - description: "The epoch for which the participation information is provided." - finalized: - type: boolean - format: boolean - description: "Whether the epoch has been finalized or not." - global_participation_rate: - type: number - format: float - description: "The percentage of validator participation in the given epoch." - minimum: 0.0 - maximum: 1.0 - voted_ether: - type: integer - format: uint64 - description: "The total amount of ether, expressed in Gwei, that has been used in voting for the specified epoch." - eligible_ether: - type: integer - format: uint64 - description: "The total amount of ether, expressed in Gwei, that is eligible for voting in the specified epoch." - - /beacon/validators/queue: - get: - tags: - - RFC - summary: "Retrieve information about the validator queue at the specified epoch." - description: "Retrieve information about the queue of validators for the specified epoch (or the current epoch if none specified)." - parameters: - - name: epoch - in: query - required: false - description: "The epoch at which the validator queue is to be measured." - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response - content: - application/json: - schema: - type: object - properties: - epoch: - type: string - format: uint64 - description: "The epoch for which the validator queue information is provided." - churn_limit: - type: integer - format: uint64 - description: "The validator churn limit for the specified epoch." - activation_public_keys: - type: array - description: "The public keys of validators which are queued for activation." - items: - $ref: "#/components/schemas/pubkey" - exit_public_keys: - type: array - description: "The public keys of validators which are queued for exiting." - items: - $ref: '#/components/schemas/pubkey' - - #TODO: Add the endpoints that enable a validator to join, exit, withdraw, etc. - /beacon/validator/duties: - get: - tags: - - Phase0 - summary: "Get validator duties for the requested validators." - description: "Requests the beacon node to provide a set of _duties_, which are actions that should be performed by validators, for a particular epoch. Duties should only need to be checked once per epoch, however a chain reorganization (of > MIN_SEED_LOOKAHEAD epochs) could occur, resulting in a change of duties. For full safety, this API call should be polled at every slot to ensure that chain reorganizations are recognized, and to ensure that the beacon node is properly synchronized. If no epoch parameter is provided, then the current epoch is assumed." - parameters: - - name: validator_pubkeys - in: query - required: true - description: "An array of hex-encoded BLS public keys" - schema: - type: array - items: - $ref: '#/components/schemas/pubkey' - minItems: 1 - - name: epoch - in: query - required: false - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ValidatorDuty' - 400: - $ref: '#/components/responses/InvalidRequest' - 500: - $ref: '#/components/responses/InternalError' - 503: - $ref: '#/components/responses/CurrentlySyncing' - - /beacon/validator/block: - get: - tags: - - Phase0 - summary: "Produce a new block, without signature." - description: "Requests a beacon node to produce a valid block, which can then be signed by a validator." - parameters: - - name: slot - in: query - required: true - description: "The slot for which the block should be proposed." - schema: - type: integer - format: uint64 - - name: randao_reveal - in: query - required: true - description: "The validator's randao reveal value." - schema: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{192}$" - description: "A valid BLS signature." - responses: - 200: - description: Success response - content: - application/json: - schema: - $ref: '#/components/schemas/BeaconBlock' - 400: - $ref: '#/components/responses/InvalidRequest' - 500: - $ref: '#/components/responses/InternalError' - 503: - $ref: '#/components/responses/CurrentlySyncing' - post: - tags: - - Phase0 - summary: "Publish a signed block." - description: "Instructs the beacon node to broadcast a newly signed beacon block to the beacon network, to be included in the beacon chain. The beacon node is not required to validate the signed `BeaconBlock`, and a successful response (20X) only indicates that the broadcast has been successful. The beacon node is expected to integrate the new block into its state, and therefore validate the block internally, however blocks which fail the validation are still broadcast but a different status code is returned (202)" - requestBody: - description: "The `BeaconBlock` object, as sent from the beacon node originally, but now with the signature field completed. Must be sent in JSON format in the body of the request." - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/BeaconBlock' - responses: - 200: - description: "The block was validated successfully and has been broadcast. It has also been integrated into the beacon node's database." - 202: - description: "The block failed validation, but was successfully broadcast anyway. It was not integrated into the beacon node's database." - 400: - $ref: '#/components/responses/InvalidRequest' - 500: - $ref: '#/components/responses/InternalError' - 503: - $ref: '#/components/responses/CurrentlySyncing' - - /beacon/validator/attestation: - get: - tags: - - Phase0 - summary: "Produce an attestation, without signature." - description: "Requests that the beacon node produce an Attestation, with a blank signature field, which the validator will then sign." - parameters: - - name: validator_pubkey - in: query - required: true - description: "Uniquely identifying which validator this attestation is to be produced for." - schema: - $ref: '#/components/schemas/pubkey' - - name: poc_bit - in: query - required: true - description: "The proof-of-custody bit that is to be reported by the requesting validator. This bit will be inserted into the appropriate location in the returned `Attestation`." - schema: - type: integer - format: uint32 - minimum: 0 - maximum: 1 - - name: slot - in: query - required: true - description: "The slot for which the attestation should be proposed." - schema: - type: integer - - name: shard - in: query - required: true - description: "The shard number for which the attestation is to be proposed." - schema: - type: integer - responses: - 200: - description: Success response - content: - application/json: - schema: - $ref: '#/components/schemas/Attestation' - 400: - $ref: '#/components/responses/InvalidRequest' - 500: - $ref: '#/components/responses/InternalError' - 503: - $ref: '#/components/responses/CurrentlySyncing' - post: - tags: - - Phase0 - summary: "Publish a signed attestation." - description: "Instructs the beacon node to broadcast a newly signed Attestation object to the intended shard subnet. The beacon node is not required to validate the signed Attestation, and a successful response (20X) only indicates that the broadcast has been successful. The beacon node is expected to integrate the new attestation into its state, and therefore validate the attestation internally, however attestations which fail the validation are still broadcast but a different status code is returned (202)" - requestBody: - description: "An `Attestation` structure, as originally provided by the beacon node, but now with the signature field completed. Must be sent in JSON format in the body of the request." - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Attestation' - responses: - 200: - description: "The attestation was validated successfully and has been broadcast. It has also been integrated into the beacon node's database." - 202: - description: "The attestation failed validation, but was successfully broadcast anyway. It was not integrated into the beacon node's database." - 400: - $ref: '#/components/responses/InvalidRequest' - 500: - $ref: '#/components/responses/InternalError' - 503: - $ref: '#/components/responses/CurrentlySyncing' - - /beacon/state: - get: - tags: - - Phase0 - summary: "Get the full beacon state, at a particular slot or block root." - description: "Requests the beacon node to provide the full beacon state object, and the state root, given a particular slot number or block root. If no parameters are provided, the latest slot of the beacon node (the 'head' slot) is used." - parameters: - - name: root - description: "The block root at which the state should be provided." - in: query - required: false - schema: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - - name: slot - description: "The slot number at which the state should be provided." - in: query - required: false - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response - content: - application/json: - schema: - type: object - properties: - root: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - beacon_state: - $ref: '#/components/schemas/BeaconState' - 400: - $ref: '#/components/responses/InvalidRequest' - 500: - $ref: '#/components/responses/InternalError' - - /beacon/state_root: - get: - tags: - - Phase0 - summary: "Get the beacon state root, at a particular slot." - description: "Requests the beacon node to provide the root of the beacon state object, given a particular slot number." - parameters: - - name: slot - description: "The slot number at which the state should be provided." - in: query - required: true - schema: - type: integer - format: uint64 - responses: - 200: - description: Success response - content: - application/json: - schema: - type: object - properties: - root: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The state root" - 400: - $ref: '#/components/responses/InvalidRequest' - 500: - $ref: '#/components/responses/InternalError' - - /beacon/state/current_finalized_checkpoint: - get: - tags: - - Phase0 - summary: "Get the current finalized checkpoint." - #TODO: is this description correct? - description: "Requests the beacon node to provide the checkpoint for the current finalized epoch." - responses: - 200: - description: Success response - content: - application/json: - schema: - $ref: '#/components/schemas/Checkpoint' - 500: - $ref: '#/components/responses/InternalError' - - /beacon/state/genesis: - get: - tags: - - Phase0 - summary: "Get the full beacon state, as it was at genesis." - description: "Requests the beacon node to provide the full beacon state object and the state root, as it was for the genesis block." - responses: - 200: - description: Success response - content: - application/json: - schema: - $ref: '#/components/schemas/BeaconState' - application/yaml: - schema: - $ref: '#/components/schemas/BeaconState' - 400: - $ref: '#/components/responses/InvalidRequest' - 500: - $ref: '#/components/responses/InternalError' - - /spec: - get: - tags: - - Phase0 - summary: "Get the current ChainSpec configuration." - description: "Requests the beacon node to provide the configuration that it has used to start the beacon chain." - responses: - 200: - description: Success response - content: - application/json: - schema: - $ref: '#/components/schemas/ChainSpec' - 500: - $ref: '#/components/responses/InternalError' - - /spec/slots_per_epoch: - get: - tags: - - Phase0 - summary: "Get the configured number of slots per epoch." - description: "The number of slots in each epoch is part of the Eth2.0 spec. This function simply returns an integer representing this value." - responses: - 200: - description: Success response - content: - application/json: - schema: - type: integer - format: uint64 - example: 64 - 500: - $ref: '#/components/responses/InternalError' - - /spec/deposit_contract: - get: - tags: - - Phase0 - summary: "Get the address of the Ethereum 1 deposit contract." - description: "Requests the address of the deposit contract on the Ethereum 1 chain, which was used to start the current beacon chain." - responses: - 200: - description: Request successful - content: - application/json: - schema: - $ref: '#/components/schemas/ethereum_address' - 500: - $ref: '#/components/responses/InternalError' - - /spec/eth2_config: - get: - tags: - - Phase0 - summary: "Gets the Eth2.0 spec, including the identifying string." - description: "" - responses: - 200: - description: Success response - content: - application/json: - schema: - type: object - properties: - spec_constants: - type: string - example: "mainnet" - spec: - $ref: '#/components/schemas/ChainSpec' - 500: - $ref: '#/components/responses/InternalError' - - /metrics: - get: - tags: - - Phase0 - summary: "Get Promethius metrics for the node" - description: "Fetches a range of metrics for measuring nodes health. It is intended for this endpoint to be consumed by Promethius." - responses: - 200: - description: Request successful - content: - text/plain: - example: "# HELP beacon_head_state_active_validators_total Count of active validators at the head of the chain - # TYPE beacon_head_state_active_validators_total gauge - beacon_head_state_active_validators_total 16 - # HELP beacon_head_state_current_justified_epoch Current justified epoch at the head of the chain - # TYPE beacon_head_state_current_justified_epoch gauge - beacon_head_state_current_justified_epoch 0 - # HELP beacon_head_state_current_justified_root Current justified root at the head of the chain - # TYPE beacon_head_state_current_justified_root gauge - beacon_head_state_current_justified_root 0 - # HELP beacon_head_state_eth1_deposit_index Eth1 deposit index at the head of the chain - # TYPE beacon_head_state_eth1_deposit_index gauge - beacon_head_state_eth1_deposit_index 16 - # HELP beacon_head_state_finalized_epoch Finalized epoch at the head of the chain - # TYPE beacon_head_state_finalized_epoch gauge - beacon_head_state_finalized_epoch 0 - # HELP beacon_head_state_finalized_root Finalized root at the head of the chain - # TYPE beacon_head_state_finalized_root gauge - beacon_head_state_finalized_root 0 - # HELP beacon_head_state_latest_block_slot Latest block slot at the head of the chain - # TYPE beacon_head_state_latest_block_slot gauge - beacon_head_state_latest_block_slot 0 - # HELP beacon_head_state_previous_justified_epoch Previous justified epoch at the head of the chain - # TYPE beacon_head_state_previous_justified_epoch gauge - beacon_head_state_previous_justified_epoch 0 - # HELP beacon_head_state_previous_justified_root Previous justified root at the head of the chain - # TYPE beacon_head_state_previous_justified_root gauge - beacon_head_state_previous_justified_root 0 - # HELP beacon_head_state_root Root of the block at the head of the chain - # TYPE beacon_head_state_root gauge - beacon_head_state_root -7566315470565629000 - # HELP beacon_head_state_shard_total Count of shards in the beacon chain - # TYPE beacon_head_state_shard_total gauge - beacon_head_state_shard_total 8 - # HELP beacon_head_state_slashed_validators_total Count of all slashed validators at the head of the chain - # TYPE beacon_head_state_slashed_validators_total gauge - beacon_head_state_slashed_validators_total 0" - -components: - schemas: - pubkey: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{96}$" - description: "The validator's BLS public key, uniquely identifying them. _48-bytes, hex encoded with 0x prefix, case insensitive._" - example: "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc" - - version: - type: string - description: "A string which uniquely identifies the client implementation and its version; similar to [HTTP User-Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3)." - example: "Lighthouse / v0.1.5 (Linux x86_64)" - - genesis_time: - type: integer - format: uint64 - description: "The genesis_time configured for the beacon node, which is the unix time at which the Eth2.0 chain began." - example: 1557716289 - - connection_duration: - type: integer - format: uint64 - description: "The number of seconds that an established network connection has persisted." - #TODO: Is it reasonable to store the connection duration? Do we have this information? Need to ask Age. - example: 3600 - - multiaddr: - type: string - description: "The multiaddr address of a network peer." - nullable: true - #TODO Define an example and pattern for a multiaddr string. - - ethereum_address: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "A hex encoded ethereum address." - - ENR: - type: string - format: byte - example: "-IW4QHzEZbIB0YN47bVlsUrGbcL9vl21n7xF5gRKjMNkJ4MxfcwiqrsE7Ows8EnzOvC8P4ZyAjfOhr2ffk0bWAxDGq8BgmlwhH8AAAGDdGNwgiMog3VkcIIjKIlzZWNwMjU2azGhAjzKzqo5c33ydUUHrWJ4FWwIXJa2MN9BBsgZkj6mhthp" - pattern: "^[^-A-Za-z0-9+/=]+$" - - Shard: - type: integer - format: uint64 - description: "A shard number." - example: 5 - maximum: 1023 - minimum: 0 - - Checkpoint: - type: object - description: "A checkpoint." - properties: - epoch: - type: integer - format: uint64 - description: "The epoch to which the checkpoint applies." - root: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "A block root, which is being checkpointed." - - Peer: - type: object - properties: - connection_status: - $ref: '#/components/schemas/ConnectionStatus' - connection_duration: - $ref: '#/components/schemas/connection_duration' - multiaddr: - $ref: '#/components/schemas/multiaddr' - measured_delay: - type: integer - format: uint64 - description: "The round trip network delay to the peer, measured in milliseconds" - #TODO: Do we have the RTT information? - - ConnectionStatus: - type: string - #TODO: Define the ENUM and possible connection states - - ValidatorIndexMapping: - type: object - properties: - pubkey: - $ref: '#/components/schemas/pubkey' - index: - type: integer - format: uint64 - description: "The global ValidatorIndex value." - - Validator: - type: object - properties: - public_key: - $ref: '#/components/schemas/pubkey' - withdrawal_credentials: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The 32 byte hash of the public key which the validator uses for withdrawing their rewards." - example: "0x00ec7ef7780c9d151597924036262dd28dc60e1228f4da6fecf9d402cb3f3594" - effective_balance: - type: integer - format: uint64 - description: "The effective balance of the validator, measured in Gwei." - example: 32000000000 - slashed: - type: boolean - description: "Whether the validator has or has not been slashed." - example: false - activation_eligiblity_epoch: - type: integer - format: uint64 - description: "The epoch when the validator became or will become eligible for activation. This field may be zero if the validator was present in the Ethereum 2.0 genesis." - activation_epoch: - type: integer - format: uint64 - description: "Epoch when the validator was or will be activated. This field may be zero if the validator was present in the Ethereum 2.0 genesis." - exit_epoch: - type: integer - format: uint64 - nullable: true - description: "Epoch when the validator was exited, or null if the validator has not exited." - example: 18446744073709551615 - withdrawable_epoch: - type: integer - format: uint64 - nullable: true - description: "Epoch when the validator is eligible to withdraw their funds, or null if the validator has not exited." - example: 18446744073709551615 - - ValidatorDuty: - type: object - properties: - validator_pubkey: - $ref: '#/components/schemas/pubkey' - attestation_slot: - type: integer - format: uint64 - description: "The slot at which the validator must attest." - attestation_shard: - type: integer - format: uint64 - description: "The shard in which the validator must attest." - block_proposal_slot: - type: integer - format: uint64 - nullable: true - description: "The slot in which a validator must propose a block, or `null` if block production is not required." - - SyncingStatus: - type: object - nullable: true - properties: - starting_slot: - type: integer - format: uint64 - description: "The slot at which syncing started (will only be reset after the sync reached its head)" - current_slot: - type: integer - format: uint64 - description: "The most recent slot synchronised by the beacon node." - highest_slot: - type: integer - format: uint64 - description: "Globally, the estimated most recent slot number, or current target slot number." - - Eth1Data: - type: object - description: "The [`Eth1Data`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#eth1data) object from the Eth2.0 spec." - properties: - deposit_root: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "Root of the deposit tree." - deposit_count: - type: integer - format: uint64 - description: "Total number of deposits." - block_hash: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "Ethereum 1.x block hash." - - BeaconBlock: - description: "The [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beaconblock) object from the Eth2.0 spec." - allOf: - - $ref: '#/components/schemas/BeaconBlockCommon' - - type: object - properties: - body: - $ref: '#/components/schemas/BeaconBlockBody' - - BeaconBlockHeader: - description: "The [`BeaconBlockHeader`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beaconblockheader) object from the Eth2.0 spec." - allOf: - - $ref: '#/components/schemas/BeaconBlockCommon' - - type: object - properties: - body_root: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The tree hash merkle root of the `BeaconBlockBody` for the `BeaconBlock`" - - BeaconBlockCommon: - # An abstract object to collect the common fields between the BeaconBlockHeader and the BeaconBlock objects - type: object - properties: - slot: - type: integer - format: uint64 - description: "The slot to which this block corresponds." - parent_root: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The signing merkle root of the parent `BeaconBlock`." - state_root: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The tree hash merkle root of the `BeaconState` for the `BeaconBlock`." - signature: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{192}$" - example: "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" - description: "The BLS signature of the `BeaconBlock` made by the validator of the block." - - BeaconBlockBody: - type: object - description: "The [`BeaconBlockBody`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beaconblockbody) object from the Eth2.0 spec." - properties: - randao_reveal: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{192}$" - description: "The RanDAO reveal value provided by the validator." - eth1_data: - $ref: '#/components/schemas/Eth1Data' - graffiti: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - proposer_slashings: - type: array - items: - title: ProposerSlashings - type: object - description: "The [`ProposerSlashing`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposerslashing) object from the Eth2.0 spec." - properties: - proposer_index: - type: integer - format: uint64 - description: "The index of the proposer to be slashed." - header_1: - $ref: '#/components/schemas/BeaconBlockHeader' - header_2: - $ref: '#/components/schemas/BeaconBlockHeader' - attester_slashings: - type: array - items: - title: AttesterSlashings - type: object - description: "The [`AttesterSlashing`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attesterslashing) object from the Eth2.0 spec." - properties: - attestation_1: - $ref: '#/components/schemas/Attestation' - attestation_2: - $ref: '#/components/schemas/Attestation' - attestations: - type: array - items: - $ref: '#/components/schemas/Attestation' - deposits: - type: array - items: - title: Deposit - type: object - description: "The [`Deposit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposit) object from the Eth2.0 spec." - properties: - proof: - type: array - description: "Branch in the deposit tree." - items: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - minItems: 32 - maxItems: 32 - index: - type: integer - format: uint64 - description: "Index in the deposit tree." - data: - title: DepositData - type: object - description: "The [`DepositData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#depositdata) object from the Eth2.0 spec." - properties: - pubkey: - $ref: '#/components/schemas/pubkey' - withdrawal_credentials: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "The withdrawal credentials." - amount: - type: integer - format: uint64 - description: "Amount in Gwei." - signature: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{192}$" - description: "Container self-signature." - voluntary_exits: - type: array - items: - title: VoluntaryExit - type: object - description: "The [`VoluntaryExit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#voluntaryexit) object from the Eth2.0 spec." - properties: - epoch: - type: integer - format: uint64 - description: "Minimum epoch for processing exit." - validator_index: - type: integer - format: uint64 - description: "Index of the exiting validator." - signature: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{192}$" - description: "Validator signature." - transfers: - type: array - items: - title: Transfer - type: object - description: "The [`Transfer`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#transfer) object from the Eth2.0 spec." - properties: - sender: - type: integer - format: uint64 - description: "Sender index." - recipient: - type: integer - format: uint64 - description: "Recipient index." - amount: - type: integer - format: uint64 - description: "Amount in Gwei." - fee: - type: integer - format: uint64 - description: "Fee in Gwei for block producer." - slot: - type: integer - format: uint64 - description: "Inclusion slot." - pubkey: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{96}$" - description: "Sender withdrawal public key." - signature: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{192}$" - description: "Sender signature." - - BeaconState: - type: object - description: "The [`BeaconState`](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#beaconstate) object from the Eth2.0 spec." - properties: - genesis_time: - $ref: '#/components/schemas/genesis_time' - slot: - type: integer - format: uint64 - description: "The latest slot, which the state represents." - fork: - $ref: '#/components/schemas/Fork' - latest_block_header: - $ref: '#/components/schemas/BeaconBlockHeader' - #TODO: Are these descriptions correct? - block_roots: - type: array - description: "The historical block roots." - minLength: 8192 - maxLength: 8192 #The SLOTS_PER_HISTORICAL_ROOT value from the Eth2.0 Spec. - items: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "A block root" - state_roots: - type: array - description: "The historical state roots." - minLength: 8192 - maxLength: 8192 #The SLOTS_PER_HISTORICAL_ROOT value from the Eth2.0 Spec. - items: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "A state root" - historical_roots: - type: array - #TODO: are these historical *state* roots? - description: "The historical state roots." - maxLength: 16777216 #The HISTORICAL_ROOTS_LIMIT value from the Eth2.0 Spec. - items: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "A state root" - eth1_data: - $ref: '#/components/schemas/Eth1Data' - eth1_data_votes: - type: array - description: "The validator votes for the Eth1Data." - maxLength: 1024 #The SLOTS_PER_ETH1_VOTING_PERIOD value from the Eth2.0 spec. - items: - $ref: '#/components/schemas/Eth1Data' - eth1_deposit_index: - type: integer - format: uint64 - #TODO: Clarify this description - description: "The index of the Eth1 deposit." - validators: - type: array - description: "A list of the current validators." - maxLength: 1099511627776 - items: - $ref: '#/components/schemas/Validator' - balances: - type: array - description: "An array of the validator balances." - maxLength: 1099511627776 - items: - type: integer - format: uint64 - description: "The validator balance in GWei." - start_shard: - $ref: '#/components/schemas/Shard' - randao_mixes: - type: array - description: "The hashes for the randao mix." - minLength: 65536 - maxLength: 65536 #The EPOCHS_PER_HISTORICAL_VECTOR value from the Eth2.0 spec. - items: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "A randao mix hash." - active_index_roots: - type: array - description: "Active index digests for light clients." - minLength: 65536 - maxLength: 65536 #The EPOCHS_PER_HISTORICAL_VECTOR value from the Eth2.0 spec. - items: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "Active index digest" - compact_committees_roots: - type: array - description: "Committee digests for light clients." - minLength: 65536 - maxLength: 65536 #The EPOCHS_PER_HISTORICAL_VECTOR value from the Eth2.0 spec. - items: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "Committee digest." - slashings: - type: array - description: "Per-epoch sums of slashed effective balances." - minLength: 8192 - maxLength: 8192 #The EPOCHS_PER_SLASHINGS_VECTOR value from the Eth2.0 spec. - items: - type: integer - format: uint64 - description: "Sum of slashed balance for an epoch." - previous_epoch_attestations: - type: array - description: "A list of attestations in the previous epoch." - maxLength: 8192 # MAX_ATTESTATIONS * SLOTS_PER_EPOCH from the Eth2.0 spec. - items: - $ref: '#/components/schemas/PendingAttestation' - current_epoch_attestations: - type: array - description: "A list of attestations in the current epoch." - maxLength: 8192 # MAX_ATTESTATIONS * SLOTS_PER_EPOCH from the Eth2.0 spec. - items: - $ref: '#/components/schemas/PendingAttestation' - previous_crosslinks: - type: array - description: "The shard crosslinks from the previous epoch." - minLength: 1024 - maxLength: 1024 #The SHARD_COUNT value from the Eth2.0 spec - items: - $ref: '#/components/schemas/Crosslink' - current_crosslinks: - type: array - description: "The shard crosslinks for the current epoch." - minLength: 1024 - maxLength: 1024 #The SHARD_COUNT value from the Eth2.0 spec - items: - $ref: '#/components/schemas/Crosslink' - justification_bits: - type: array - description: "Bit set for every recent justified epoch." - minLength: 4 - maxLength: 4 #The JUSTIFICATION_BITS_LENGTH from the Eth2.0 spec. - items: - type: boolean - #TODO: Check this description - description: "Whethere the recent epochs have been finalized." - previous_justified_checkpoint: - $ref: '#/components/schemas/Checkpoint' - current_justified_checkpoint: - $ref: '#/components/schemas/Checkpoint' - finalized_checkpoint: - $ref: '#/components/schemas/Checkpoint' - - Fork: - type: object - description: "The [`Fork`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#Fork) object from the Eth2.0 spec." - properties: - previous_version: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{8}$" - description: "Previous fork version." - current_version: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{8}$" - description: "Current fork version." - epoch: - type: integer - format: uint64 - description: "Fork epoch number." - - Attestation: - type: object - description: "The [`Attestation`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestation) object from the Eth2.0 spec." - properties: - aggregation_bitfield: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]+$" - description: "Attester aggregation bitfield." - custody_bitfield: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]+$" - description: "Custody bitfield." - signature: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{192}$" - description: "BLS aggregate signature." - data: - $ref: '#/components/schemas/AttestationData' - - IndexedAttestation: - type: object - description: "The [`IndexedAttestation`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#indexedattestation) object from the Eth2.0 spec." - properties: - custody_bit_0_indices: - type: array - description: "Validator indices for 0 bits." - items: - type: integer - format: uint64 - custody_bit_1_indices: - type: array - description: "Validator indices for 1 bits." - items: - type: integer - format: uint64 - signature: - type: string - format: bytes - pattern: "^0x[a-fA-F0-9]{192}$" - example: "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" - description: "The BLS signature of the `IndexedAttestation`, created by the validator of the attestation." - data: - $ref: '#/components/schemas/AttestationData' - - PendingAttestation: - type: object - description: "The [`PendingAttestation`](https://github.com/ethereum/eth2.0-specs/blob/v0.8.3/specs/core/0_beacon-chain.md#pendingattestation) object from the Eth2.0 spec." - properties: - aggregation_bits: - type: array - description: "The bits representing aggregation of validator signatures and attestations." - maxLength: 4096 #The MAX_VALIDATORS_PER_COMMITTEE value from the Eth2.0 spec. - items: - type: boolean - description: "Whether the validator has been aggregated or not" - data: - $ref: '#/components/schemas/AttestationData' - inclusion_delay: - type: integer - format: uint64 - description: "The Slot at which it should be included." - proposer_index: - type: integer - format: uint64 - #TODO: This is the block proposer index, not the attestaion right? - description: "The ValidatorIndex of the block proposer" - - - - - - - - AttestationData: - type: object - description: "The [`AttestationData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestationdata) object from the Eth2.0 spec." - properties: - beacon_block_root: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "LMD GHOST vote." - source_epoch: - type: integer - format: uint64 - description: "Source epoch from FFG vote." - source_root: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "Source root from FFG vote." - target_epoch: - type: integer - format: uint64 - description: "Target epoch from FFG vote." - target_root: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "Target root from FFG vote." - crosslink: - $ref: '#/components/schemas/Crosslink' - - - Crosslink: - type: object - description: "The [`Crosslink`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#crosslink) object from the Eth2.0 spec, contains data from epochs [`start_epoch`, `end_epoch`)." - properties: - shard: - type: integer - format: uint64 - description: "The shard number." - start_epoch: - type: integer - format: uint64 - description: "The first epoch which the crosslinking data references." - end_epoch: - type: integer - format: uint64 - description: "The 'end' epoch referred to by the crosslinking data; no data in this Crosslink should refer to the `end_epoch` since it is not included in the crosslinking data interval." - parent_root: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "Root of the previous crosslink." - data_root: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{64}$" - description: "Root of the crosslinked shard data since the previous crosslink." - - ChainSpec: - type: object - description: "Stores all of the values which specify a particular chain. The `ChainSpec` object in Lighthouse" - properties: - base_rewards_per_epoch: - type: integer - format: uint64 - example: 5 - deposit_contract_tree_depth: - type: integer - format: uint64 - example: 32 - seconds_per_day: - type: integer - format: uint64 - example: 86400 - target_committee_size: - type: integer - format: uint64 - example: 128 - min_per_epoch_churn_limit: - type: integer - format: uint64 - example: 4 - churn_limit_quotient: - type: integer - format: uint64 - example: 65536 - shuffle_round_count: - type: integer - format: uint8 - example: 90 - min_genesis_active_validator_count: - type: integer - format: uint64 - example: 65536 - min_genesis_time: - type: integer - format: uint64 - example: 1578009600 - min_deposit_amount: - type: integer - format: uint64 - example: 1000000000 - max_effective_balance: - type: integer - format: uint64 - example: 32000000000 - ejection_balance: - type: integer - format: uint64 - example: 16000000000 - effective_balance_increment: - type: integer - format: uint64 - example: 1000000000 - genesis_slot: - type: integer - format: uint64 - example: 0 - bls_withdrawal_prefix_byte: - type: string - format: byte - pattern: "^0x[a-fA-F0-9]{2}$" - example: "0x00" - milliseconds_per_slot: - type: integer - format: uint64 - example: 6000 - min_attestation_inclusion_delay: - type: integer - format: uint64 - example: 1 - min_seed_lookahead: - type: integer - format: uint64 - example: 1 - activation_exit_delay: - type: integer - format: uint64 - example: 4 - min_validator_withdrawability_delay: - type: integer - format: uint64 - example: 256 - persistent_committee_period: - type: integer - format: uint64 - example: 2048 - max_epochs_per_crosslink: - type: integer - format: uint64 - example: 64 - min_epochs_to_inactivity_penalty: - type: integer - format: uint64 - example: 4 - base_reward_factor: - type: integer - format: uint64 - example: 64 - whistleblower_reward_quotient: - type: integer - format: uint64 - example: 512 - proposer_reward_quotient: - type: integer - format: uint64 - example: 8 - inactivity_penalty_quotient: - type: integer - format: uint64 - example: 33554432 - min_slashing_penalty_quotient: - type: integer - format: uint64 - example: 32 - domain_beacon_proposer: - type: integer - format: uint32 - example: 0 - domain_randao: - type: integer - format: uint32 - example: 1 - domain_attestation: - type: integer - format: uint32 - example: 2 - domain_deposit: - type: integer - format: uint32 - example: 3 - domain_voluntary_exit: - type: integer - format: uint32 - example: 4 - domain_transfer: - type: integer - format: uint32 - example: 5 - boot_nodes: - type: array - items: - $ref: '#/components/schemas/ENR' - network_id: - type: integer - format: uint8 - example: 2 - - - responses: - Success: - description: "Request successful." - #TODO: Make response descriptions consistent - InvalidRequest: - description: "Invalid request syntax." - InternalError: - description: "Beacon node internal error." - CurrentlySyncing: - description: "Beacon node is currently syncing, try again later." - NotFound: - description: "The requested API endpoint does not exist." - -externalDocs: - description: Ethereum 2.0 Specification on Github - url: 'https://github.com/ethereum/eth2.0-specs' - - -#TODO: Define the components of the WebSockets API diff --git a/beacon_node/rest_api/src/beacon.rs b/beacon_node/rest_api/src/beacon.rs index 5bb67dd80..e3f7367ea 100644 --- a/beacon_node/rest_api/src/beacon.rs +++ b/beacon_node/rest_api/src/beacon.rs @@ -10,8 +10,8 @@ use ssz_derive::{Decode, Encode}; use std::sync::Arc; use store::Store; use types::{ - BeaconBlock, BeaconState, CommitteeIndex, EthSpec, Hash256, PublicKeyBytes, RelativeEpoch, - Slot, Validator, + BeaconState, CommitteeIndex, EthSpec, Hash256, PublicKeyBytes, RelativeEpoch, + SignedBeaconBlock, Slot, Validator, }; /// Information about the block and state that are at head of the beacon chain. @@ -70,7 +70,7 @@ pub struct HeadBeaconBlock { pub beacon_block_slot: Slot, } -/// HTTP handler to return a list of head BeaconBlocks. +/// HTTP handler to return a list of head block roots. pub fn get_heads( req: Request, beacon_chain: Arc>, @@ -91,10 +91,10 @@ pub fn get_heads( #[serde(bound = "T: EthSpec")] pub struct BlockResponse { pub root: Hash256, - pub beacon_block: BeaconBlock, + pub beacon_block: SignedBeaconBlock, } -/// HTTP handler to return a `BeaconBlock` at a given `root` or `slot`. +/// HTTP handler to return a `SignedBeaconBlock` at a given `root` or `slot`. pub fn get_block( req: Request, beacon_chain: Arc>, @@ -107,22 +107,22 @@ pub fn get_block( let target = parse_slot(&value)?; block_root_at_slot(&beacon_chain, target)?.ok_or_else(|| { - ApiError::NotFound(format!("Unable to find BeaconBlock for slot {:?}", target)) + ApiError::NotFound(format!( + "Unable to find SignedBeaconBlock for slot {:?}", + target + )) })? } ("root", value) => parse_root(&value)?, _ => return Err(ApiError::ServerError("Unexpected query parameter".into())), }; - let block = beacon_chain - .store - .get::>(&block_root)? - .ok_or_else(|| { - ApiError::NotFound(format!( - "Unable to find BeaconBlock for root {:?}", - block_root - )) - })?; + let block = beacon_chain.store.get_block(&block_root)?.ok_or_else(|| { + ApiError::NotFound(format!( + "Unable to find SignedBeaconBlock for root {:?}", + block_root + )) + })?; let response = BlockResponse { root: block_root, @@ -132,7 +132,7 @@ pub fn get_block( ResponseBuilder::new(&req)?.body(&response) } -/// HTTP handler to return a `BeaconBlock` root at a given `slot`. +/// HTTP handler to return a `SignedBeaconBlock` root at a given `slot`. pub fn get_block_root( req: Request, beacon_chain: Arc>, @@ -141,7 +141,10 @@ pub fn get_block_root( let target = parse_slot(&slot_string)?; let root = block_root_at_slot(&beacon_chain, target)?.ok_or_else(|| { - ApiError::NotFound(format!("Unable to find BeaconBlock for slot {:?}", target)) + ApiError::NotFound(format!( + "Unable to find SignedBeaconBlock for slot {:?}", + target + )) })?; ResponseBuilder::new(&req)?.body(&root) diff --git a/beacon_node/rest_api/src/helpers.rs b/beacon_node/rest_api/src/helpers.rs index 752414e5f..9893471a2 100644 --- a/beacon_node/rest_api/src/helpers.rs +++ b/beacon_node/rest_api/src/helpers.rs @@ -13,8 +13,8 @@ use std::sync::Arc; use store::{iter::AncestorIter, Store}; use tokio::sync::mpsc; use types::{ - Attestation, BeaconBlock, BeaconState, CommitteeIndex, Epoch, EthSpec, Hash256, RelativeEpoch, - Signature, Slot, + Attestation, BeaconState, CommitteeIndex, Epoch, EthSpec, Hash256, RelativeEpoch, Signature, + SignedBeaconBlock, Slot, }; /// Parse a slot. @@ -113,7 +113,7 @@ pub fn parse_pubkey_bytes(string: &str) -> Result { } } -/// Returns the root of the `BeaconBlock` in the canonical chain of `beacon_chain` at the given +/// Returns the root of the `SignedBeaconBlock` in the canonical chain of `beacon_chain` at the given /// `slot`, if possible. /// /// May return a root for a previous slot, in the case of skip slots. @@ -227,7 +227,7 @@ pub fn implementation_pending_response(_req: Request) -> ApiResult { pub fn publish_beacon_block_to_network( chan: Arc>>, - block: BeaconBlock, + block: SignedBeaconBlock, ) -> Result<(), ApiError> { // create the network topic to send on let topic = GossipTopic::BeaconBlock; diff --git a/beacon_node/rest_api/src/validator.rs b/beacon_node/rest_api/src/validator.rs index c3c751052..1b817e105 100644 --- a/beacon_node/rest_api/src/validator.rs +++ b/beacon_node/rest_api/src/validator.rs @@ -14,7 +14,9 @@ use slog::{error, info, warn, Logger}; use ssz_derive::{Decode, Encode}; use std::sync::Arc; use types::beacon_state::EthSpec; -use types::{Attestation, BeaconBlock, BeaconState, CommitteeIndex, Epoch, RelativeEpoch, Slot}; +use types::{ + Attestation, BeaconState, CommitteeIndex, Epoch, RelativeEpoch, SignedBeaconBlock, Slot, +}; #[derive(PartialEq, Debug, Serialize, Deserialize, Clone)] pub struct ValidatorDuty { @@ -256,7 +258,7 @@ pub fn get_new_beacon_block( ResponseBuilder::new(&req)?.body(&new_block) } -/// HTTP Handler to publish a BeaconBlock, which has been signed by a validator. +/// HTTP Handler to publish a SignedBeaconBlock, which has been signed by a validator. pub fn publish_beacon_block( req: Request, beacon_chain: Arc>, @@ -272,11 +274,11 @@ pub fn publish_beacon_block( .map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e))) .and_then(|chunks| { serde_json::from_slice(&chunks).map_err(|e| { - ApiError::BadRequest(format!("Unable to parse JSON into BeaconBlock: {:?}", e)) + ApiError::BadRequest(format!("Unable to parse JSON into SignedBeaconBlock: {:?}", e)) }) }) - .and_then(move |block: BeaconBlock| { - let slot = block.slot; + .and_then(move |block: SignedBeaconBlock| { + let slot = block.slot(); match beacon_chain.process_block(block.clone()) { Ok(BlockProcessingOutcome::Processed { block_root }) => { // Block was processed, publish via gossipsub @@ -329,7 +331,7 @@ pub fn publish_beacon_block( ); Err(ApiError::ProcessingError(format!( - "The BeaconBlock could not be processed and has not been published: {:?}", + "The SignedBeaconBlock could not be processed and has not been published: {:?}", outcome ))) } @@ -386,7 +388,7 @@ pub fn publish_attestation( .and_then(|chunks| { serde_json::from_slice(&chunks.as_slice()).map_err(|e| { ApiError::BadRequest(format!( - "Unable to deserialize JSON into a BeaconBlock: {:?}", + "Unable to deserialize JSON into a SignedBeaconBlock: {:?}", e )) }) diff --git a/beacon_node/rest_api/tests/test.rs b/beacon_node/rest_api/tests/test.rs index eae9f3a32..81b8a6fb0 100644 --- a/beacon_node/rest_api/tests/test.rs +++ b/beacon_node/rest_api/tests/test.rs @@ -11,10 +11,10 @@ use remote_beacon_node::{ }; use std::convert::TryInto; use std::sync::Arc; -use tree_hash::TreeHash; use types::{ test_utils::generate_deterministic_keypair, BeaconBlock, BeaconState, ChainSpec, Domain, Epoch, - EthSpec, MinimalEthSpec, PublicKey, RelativeEpoch, Signature, Slot, Validator, + EthSpec, MinimalEthSpec, PublicKey, RelativeEpoch, Signature, SignedBeaconBlock, SignedRoot, + Slot, Validator, }; use version; @@ -54,17 +54,17 @@ fn get_randao_reveal( .expect("should get proposer index"); let keypair = generate_deterministic_keypair(proposer_index); let epoch = slot.epoch(E::slots_per_epoch()); - let message = epoch.tree_hash_root(); let domain = spec.get_domain(epoch, Domain::Randao, &fork); - Signature::new(&message, domain, &keypair.sk) + let message = epoch.signing_root(domain); + Signature::new(message.as_bytes(), &keypair.sk) } /// Signs the given block (assuming the given `beacon_chain` uses deterministic keypairs). fn sign_block( beacon_chain: Arc>, - block: &mut BeaconBlock, + block: BeaconBlock, spec: &ChainSpec, -) { +) -> SignedBeaconBlock { let fork = beacon_chain .head() .expect("should get head") @@ -74,7 +74,7 @@ fn sign_block( .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, spec) } #[test] @@ -335,7 +335,7 @@ fn validator_block_post() { let slot = Slot::new(1); let randao_reveal = get_randao_reveal(beacon_chain.clone(), slot, spec); - let mut block = env + let block = env .runtime() .block_on( remote_node @@ -346,9 +346,13 @@ fn validator_block_post() { .expect("should fetch block from http api"); // Try publishing the block without a signature, ensure it is flagged as invalid. + let empty_sig_block = SignedBeaconBlock { + message: block.clone(), + signature: Signature::empty_signature(), + }; let publish_status = env .runtime() - .block_on(remote_node.http.validator().publish_block(block.clone())) + .block_on(remote_node.http.validator().publish_block(empty_sig_block)) .expect("should publish block"); if cfg!(not(feature = "fake_crypto")) { assert!( @@ -357,12 +361,12 @@ fn validator_block_post() { ); } - sign_block(beacon_chain, &mut block, spec); - let block_root = block.canonical_root(); + let signed_block = sign_block(beacon_chain.clone(), block, spec); + let block_root = signed_block.canonical_root(); let publish_status = env .runtime() - .block_on(remote_node.http.validator().publish_block(block)) + .block_on(remote_node.http.validator().publish_block(signed_block)) .expect("should publish block"); if cfg!(not(feature = "fake_crypto")) { diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 1ce2edb2f..c9979bc3d 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -11,7 +11,7 @@ use std::fs; use std::net::{IpAddr, Ipv4Addr}; use std::net::{TcpListener, UdpSocket}; use std::path::PathBuf; -use types::{Epoch, EthSpec, Fork}; +use types::EthSpec; pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml"; pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml"; @@ -377,11 +377,7 @@ fn init_new_client( let spec = &mut eth2_config.spec; // For now, assume that all networks will use the lighthouse genesis fork. - spec.genesis_fork = Fork { - previous_version: [0, 0, 0, 0], - current_version: [1, 3, 3, 7], - epoch: Epoch::new(0), - }; + spec.genesis_fork_version = [1, 3, 3, 7]; client_config.eth1.deposit_contract_address = format!("{:?}", eth2_testnet_config.deposit_contract_address()?); @@ -559,11 +555,7 @@ fn process_testnet_subcommand( spec.ejection_balance = 1_600_000_000; spec.effective_balance_increment = 100_000_000; spec.min_genesis_time = 0; - spec.genesis_fork = Fork { - previous_version: [0; 4], - current_version: [0, 0, 0, 2], - epoch: Epoch::new(0), - }; + spec.genesis_fork_version = [0, 0, 0, 2]; client_config.eth1.deposit_contract_address = "0x802dF6aAaCe28B2EEb1656bb18dF430dDC42cc2e".to_string(); diff --git a/beacon_node/store/src/block_at_slot.rs b/beacon_node/store/src/block_at_slot.rs deleted file mode 100644 index dbb2c946e..000000000 --- a/beacon_node/store/src/block_at_slot.rs +++ /dev/null @@ -1,194 +0,0 @@ -use super::*; -use ssz::{Decode, DecodeError}; -use std::cmp::Ordering; - -fn get_block_bytes, E: EthSpec>( - store: &T, - root: Hash256, -) -> Result>, Error> { - store.get_bytes(BeaconBlock::::db_column().into(), &root[..]) -} - -fn read_slot_from_block_bytes(bytes: &[u8]) -> Result { - let end = std::cmp::min(Slot::ssz_fixed_len(), bytes.len()); - - Slot::from_ssz_bytes(&bytes[0..end]) -} - -fn read_parent_root_from_block_bytes(bytes: &[u8]) -> Result { - let previous_bytes = Slot::ssz_fixed_len(); - let slice = bytes - .get(previous_bytes..previous_bytes + Hash256::ssz_fixed_len()) - .ok_or_else(|| DecodeError::BytesInvalid("Not enough bytes.".to_string()))?; - - Hash256::from_ssz_bytes(slice) -} - -pub fn get_block_at_preceeding_slot, E: EthSpec>( - store: &T, - slot: Slot, - start_root: Hash256, -) -> Result)>, Error> { - Ok( - match get_at_preceeding_slot::<_, E>(store, slot, start_root)? { - Some((hash, bytes)) => Some((hash, BeaconBlock::::from_ssz_bytes(&bytes)?)), - None => None, - }, - ) -} - -fn get_at_preceeding_slot, E: EthSpec>( - store: &T, - slot: Slot, - mut root: Hash256, -) -> Result)>, Error> { - loop { - if let Some(bytes) = get_block_bytes::<_, E>(store, root)? { - let this_slot = read_slot_from_block_bytes(&bytes)?; - - match this_slot.cmp(&slot) { - Ordering::Equal => break Ok(Some((root, bytes))), - Ordering::Less => break Ok(None), - Ordering::Greater => root = read_parent_root_from_block_bytes(&bytes)?, - } - } else { - break Ok(None); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use ssz::Encode; - use tree_hash::TreeHash; - - type BeaconBlock = types::BeaconBlock; - - #[test] - fn read_slot() { - let spec = MinimalEthSpec::default_spec(); - - let test_slot = |slot: Slot| { - let mut block = BeaconBlock::empty(&spec); - block.slot = slot; - let bytes = block.as_ssz_bytes(); - assert_eq!(read_slot_from_block_bytes(&bytes).unwrap(), slot); - }; - - test_slot(Slot::new(0)); - test_slot(Slot::new(1)); - test_slot(Slot::new(42)); - test_slot(Slot::new(u64::max_value())); - } - - #[test] - fn bad_slot() { - for i in 0..8 { - assert!(read_slot_from_block_bytes(&vec![0; i]).is_err()); - } - } - - #[test] - fn read_parent_root() { - let spec = MinimalEthSpec::default_spec(); - - let test_root = |root: Hash256| { - let mut block = BeaconBlock::empty(&spec); - block.parent_root = root; - let bytes = block.as_ssz_bytes(); - assert_eq!(read_parent_root_from_block_bytes(&bytes).unwrap(), root); - }; - - test_root(Hash256::random()); - test_root(Hash256::random()); - test_root(Hash256::random()); - } - - fn build_chain( - store: &impl Store, - slots: &[usize], - spec: &ChainSpec, - ) -> Vec<(Hash256, BeaconBlock)> { - let mut blocks_and_roots: Vec<(Hash256, BeaconBlock)> = vec![]; - - for (i, slot) in slots.iter().enumerate() { - let mut block = BeaconBlock::empty(spec); - block.slot = Slot::from(*slot); - - if i > 0 { - block.parent_root = blocks_and_roots[i - 1].0; - } - - let root = Hash256::from_slice(&block.tree_hash_root()); - - store.put(&root, &block).unwrap(); - blocks_and_roots.push((root, block)); - } - - blocks_and_roots - } - - #[test] - fn chain_without_skips() { - let n: usize = 10; - let store = MemoryStore::open(); - let spec = MinimalEthSpec::default_spec(); - - let slots: Vec = (0..n).collect(); - let blocks_and_roots = build_chain(&store, &slots, &spec); - - for source in 1..n { - for target in 0..=source { - let (source_root, _source_block) = &blocks_and_roots[source]; - let (target_root, target_block) = &blocks_and_roots[target]; - - let (found_root, found_block) = store - .get_block_at_preceeding_slot(*source_root, target_block.slot) - .unwrap() - .unwrap(); - - assert_eq!(found_root, *target_root); - assert_eq!(found_block, *target_block); - } - } - } - - #[test] - fn chain_with_skips() { - let store = MemoryStore::::open(); - let spec = MinimalEthSpec::default_spec(); - - let slots = vec![0, 1, 2, 5]; - - let blocks_and_roots = build_chain(&store, &slots, &spec); - - // Valid slots - for target in 0..3 { - let (source_root, _source_block) = &blocks_and_roots[3]; - let (target_root, target_block) = &blocks_and_roots[target]; - - let (found_root, found_block) = store - .get_block_at_preceeding_slot(*source_root, target_block.slot) - .unwrap() - .unwrap(); - - assert_eq!(found_root, *target_root); - assert_eq!(found_block, *target_block); - } - - // Slot that doesn't exist - let (source_root, _source_block) = &blocks_and_roots[3]; - assert!(store - .get_block_at_preceeding_slot(*source_root, Slot::new(3)) - .unwrap() - .is_none()); - - // Slot too high - let (source_root, _source_block) = &blocks_and_roots[3]; - assert!(store - .get_block_at_preceeding_slot(*source_root, Slot::new(3)) - .unwrap() - .is_none()); - } -} diff --git a/beacon_node/store/src/hot_cold_store.rs b/beacon_node/store/src/hot_cold_store.rs index 1832360f2..c35fe568c 100644 --- a/beacon_node/store/src/hot_cold_store.rs +++ b/beacon_node/store/src/hot_cold_store.rs @@ -46,7 +46,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: Mutex>>, + block_cache: Mutex>>, /// LRU cache of deserialized states. Updated whenever a state is loaded. state_cache: Mutex>>, /// Chain spec. @@ -107,7 +107,7 @@ impl Store for HotColdDB { } /// Store a block and update the LRU cache. - fn put_block(&self, block_root: &Hash256, block: BeaconBlock) -> Result<(), Error> { + fn put_block(&self, block_root: &Hash256, block: SignedBeaconBlock) -> Result<(), Error> { // Store on disk. self.put(block_root, &block)?; @@ -118,7 +118,7 @@ impl Store for HotColdDB { } /// Fetch a block from the store. - fn get_block(&self, block_root: &Hash256) -> Result>, Error> { + fn get_block(&self, block_root: &Hash256) -> Result>, Error> { metrics::inc_counter(&metrics::BEACON_BLOCK_GET_COUNT); // Check the cache. @@ -128,7 +128,7 @@ impl Store for HotColdDB { } // Fetch from database. - match self.get::>(block_root)? { + match self.get::>(block_root)? { Some(block) => { // Add to cache. self.block_cache.lock().put(*block_root, block.clone()); @@ -569,15 +569,15 @@ impl HotColdDB { start_slot: Slot, end_slot: Slot, end_block_hash: Hash256, - ) -> Result>, Error> { + ) -> Result>, Error> { let mut blocks = ParentRootBlockIterator::new(self, end_block_hash) .map(|(_, block)| block) // Include the block at the end slot (if any), it needs to be // replayed in order to construct the canonical state at `end_slot`. - .filter(|block| block.slot <= end_slot) + .filter(|block| block.message.slot <= end_slot) // Exclude the block at the start slot (if any), because it has already // been applied to the starting state. - .take_while(|block| block.slot > start_slot) + .take_while(|block| block.message.slot > start_slot) .collect::>(); blocks.reverse(); Ok(blocks) @@ -590,12 +590,12 @@ impl HotColdDB { fn replay_blocks( &self, mut state: BeaconState, - blocks: Vec>, + blocks: Vec>, target_slot: Slot, ) -> Result, Error> { let state_root_from_prev_block = |i: usize, state: &BeaconState| { if i > 0 { - let prev_block = &blocks[i - 1]; + let prev_block = &blocks[i - 1].message; if prev_block.slot == state.slot { Some(prev_block.state_root) } else { @@ -607,7 +607,7 @@ impl HotColdDB { }; for (i, block) in blocks.iter().enumerate() { - while state.slot < block.slot { + while state.slot < block.message.slot { let state_root = state_root_from_prev_block(i, &state); per_slot_processing(&mut state, state_root, &self.spec) .map_err(HotColdDBError::BlockReplaySlotError)?; diff --git a/beacon_node/store/src/impls.rs b/beacon_node/store/src/impls.rs index b78a7f2ee..51346bbcf 100644 --- a/beacon_node/store/src/impls.rs +++ b/beacon_node/store/src/impls.rs @@ -4,7 +4,7 @@ use ssz::{Decode, Encode}; pub mod beacon_state; pub mod partial_beacon_state; -impl SimpleStoreItem for BeaconBlock { +impl SimpleStoreItem for SignedBeaconBlock { fn db_column() -> DBColumn { DBColumn::BeaconBlock } diff --git a/beacon_node/store/src/iter.rs b/beacon_node/store/src/iter.rs index 199d24aa9..d0c19f358 100644 --- a/beacon_node/store/src/iter.rs +++ b/beacon_node/store/src/iter.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use std::marker::PhantomData; use std::sync::Arc; use types::{ - typenum::Unsigned, BeaconBlock, BeaconState, BeaconStateError, EthSpec, Hash256, Slot, + typenum::Unsigned, BeaconState, BeaconStateError, EthSpec, Hash256, SignedBeaconBlock, Slot, }; /// Implemented for types that have ancestors (e.g., blocks, states) that may be iterated over. @@ -18,12 +18,14 @@ pub trait AncestorIter, E: EthSpec, I: Iterator> { } impl<'a, U: Store, E: EthSpec> AncestorIter> - for BeaconBlock + for SignedBeaconBlock { /// Iterates across all available prior block roots of `self`, starting at the most recent and ending /// at genesis. fn try_iter_ancestor_roots(&self, store: Arc) -> Option> { - let state = store.get_state(&self.state_root, Some(self.slot)).ok()??; + let state = store + .get_state(&self.message.state_root, Some(self.message.slot)) + .ok()??; Some(BlockRootsIterator::owned(store, state)) } @@ -120,7 +122,7 @@ impl<'a, E: EthSpec, S: Store> ParentRootBlockIterator<'a, E, S> { } impl<'a, E: EthSpec, S: Store> Iterator for ParentRootBlockIterator<'a, E, S> { - type Item = (Hash256, BeaconBlock); + type Item = (Hash256, SignedBeaconBlock); fn next(&mut self) -> Option { // Stop once we reach the zero parent, otherwise we'll keep returning the genesis @@ -129,15 +131,15 @@ impl<'a, E: EthSpec, S: Store> Iterator for ParentRootBlockIterator<'a, E, S> None } else { let block_root = self.next_block_root; - let block: BeaconBlock = self.store.get(&block_root).ok()??; - self.next_block_root = block.parent_root; + let block = self.store.get_block(&block_root).ok()??; + self.next_block_root = block.message.parent_root; Some((block_root, block)) } } } #[derive(Clone)] -/// Extends `BlockRootsIterator`, returning `BeaconBlock` instances, instead of their roots. +/// Extends `BlockRootsIterator`, returning `SignedBeaconBlock` instances, instead of their roots. pub struct BlockIterator<'a, T: EthSpec, U> { roots: BlockRootsIterator<'a, T, U>, } @@ -159,11 +161,11 @@ impl<'a, T: EthSpec, U: Store> BlockIterator<'a, T, U> { } impl<'a, T: EthSpec, U: Store> Iterator for BlockIterator<'a, T, U> { - type Item = BeaconBlock; + type Item = SignedBeaconBlock; fn next(&mut self) -> Option { let (root, _slot) = self.roots.next()?; - self.roots.store.get(&root).ok()? + self.roots.store.get_block(&root).ok()? } } diff --git a/beacon_node/store/src/lib.rs b/beacon_node/store/src/lib.rs index eaeb1992e..ad8f3ba90 100644 --- a/beacon_node/store/src/lib.rs +++ b/beacon_node/store/src/lib.rs @@ -10,7 +10,6 @@ #[macro_use] extern crate lazy_static; -mod block_at_slot; pub mod chunked_iter; pub mod chunked_vector; pub mod config; @@ -83,12 +82,12 @@ pub trait Store: Sync + Send + Sized + 'static { } /// Store a block in the store. - fn put_block(&self, block_root: &Hash256, block: BeaconBlock) -> Result<(), Error> { + fn put_block(&self, block_root: &Hash256, block: SignedBeaconBlock) -> Result<(), Error> { self.put(block_root, &block) } /// Fetch a block from the store. - fn get_block(&self, block_root: &Hash256) -> Result>, Error> { + fn get_block(&self, block_root: &Hash256) -> Result>, Error> { self.get(block_root) } @@ -124,19 +123,6 @@ pub trait Store: Sync + Send + Sized + 'static { self.get_state(state_root, slot) } - /// Given the root of an existing block in the store (`start_block_root`), return a parent - /// block with the specified `slot`. - /// - /// Returns `None` if no parent block exists at that slot, or if `slot` is greater than the - /// slot of `start_block_root`. - fn get_block_at_preceeding_slot( - &self, - start_block_root: Hash256, - slot: Slot, - ) -> Result)>, Error> { - block_at_slot::get_block_at_preceeding_slot::<_, E>(self, slot, start_block_root) - } - /// (Optionally) Move all data before the frozen slot to the freezer database. fn freeze_to_state( _store: Arc, diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index 096aea8fe..0af5c85a2 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.9.1 +/// Spec v0.10.1 #[derive(Debug, PartialEq, Clone, Encode, Decode)] pub struct PartialBeaconState where diff --git a/book/src/http_beacon.md b/book/src/http_beacon.md index c926bdb25..ca994bc4f 100644 --- a/book/src/http_beacon.md +++ b/book/src/http_beacon.md @@ -10,7 +10,7 @@ HTTP Path | Description | [`/beacon/head`](#beaconhead) | Info about the block at the head of the chain. [`/beacon/heads`](#beaconheads) | Returns a list of all known chain heads. [`/beacon/block_root`](#beaconblock_root) | Resolve a slot to a block root. -[`/beacon/block`](#beaconblock) | Get a `BeaconBlock` by slot or root. +[`/beacon/block`](#beaconblock) | Get a `SignedBeaconBlock` by slot or root. [`/beacon/state_root`](#beaconstate_root) | Resolve a slot to a state root. [`/beacon/state`](#beaconstate) | Get a `BeaconState` by slot or root. [`/beacon/validators`](#beaconvalidators) | Query for one or more validators. @@ -131,7 +131,7 @@ canonical chain. ### Returns -Returns an object containing a single [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beaconblock) and its signed root. +Returns an object containing a single [`SignedBeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.0/specs/phase0/beacon-chain.md#signedbeaconblock) and the block root of the inner [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.0/specs/phase0/beacon-chain.md#beaconblock). ### Example Response @@ -139,22 +139,24 @@ Returns an object containing a single [`BeaconBlock`](https://github.com/ethereu { "root": "0xc35ddf4e71c31774e0594bd7eb32dfe50b54dbc40abd594944254b4ec8895196", "beacon_block": { - "slot": 0, - "parent_root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "state_root": "0xf15690b6be4ed42ea1ee0741eb4bfd4619d37be8229b84b4ddd480fb028dcc8f", - "body": { - "randao_reveal": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "eth1_data": { - "deposit_root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "deposit_count": 0, - "block_hash": "0x0000000000000000000000000000000000000000000000000000000000000000" - }, - "graffiti": "0x0000000000000000000000000000000000000000000000000000000000000000", - "proposer_slashings": [], - "attester_slashings": [], - "attestations": [], - "deposits": [], - "voluntary_exits": [] + "message": { + "slot": 0, + "parent_root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "state_root": "0xf15690b6be4ed42ea1ee0741eb4bfd4619d37be8229b84b4ddd480fb028dcc8f", + "body": { + "randao_reveal": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "eth1_data": { + "deposit_root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "deposit_count": 0, + "block_hash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "graffiti": "0x0000000000000000000000000000000000000000000000000000000000000000", + "proposer_slashings": [], + "attester_slashings": [], + "attestations": [], + "deposits": [], + "voluntary_exits": [] + } }, "signature": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } @@ -214,7 +216,7 @@ canonical chain. ### Returns Returns an object containing a single -[`BeaconState`](https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beaconstate) +[`BeaconState`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#beaconstate) and its tree hash root. ### Example Response diff --git a/eth2/operation_pool/src/lib.rs b/eth2/operation_pool/src/lib.rs index e1239f375..4786be3cb 100644 --- a/eth2/operation_pool/src/lib.rs +++ b/eth2/operation_pool/src/lib.rs @@ -22,7 +22,7 @@ use std::collections::{hash_map, HashMap, HashSet}; use std::marker::PhantomData; use types::{ typenum::Unsigned, Attestation, AttesterSlashing, BeaconState, BeaconStateError, ChainSpec, - EthSpec, ProposerSlashing, RelativeEpoch, Validator, VoluntaryExit, + EthSpec, ProposerSlashing, RelativeEpoch, SignedVoluntaryExit, Validator, }; #[derive(Default, Debug)] @@ -34,7 +34,7 @@ pub struct OperationPool { /// Map from proposer index to slashing. proposer_slashings: RwLock>, /// Map from exiting validator to their exit data. - voluntary_exits: RwLock>, + voluntary_exits: RwLock>, _phantom: PhantomData, } @@ -297,14 +297,14 @@ impl OperationPool { /// Insert a voluntary exit, validating it almost-entirely (future exits are permitted). pub fn insert_voluntary_exit( &self, - exit: VoluntaryExit, + exit: SignedVoluntaryExit, state: &BeaconState, spec: &ChainSpec, ) -> Result<(), ExitValidationError> { verify_exit_time_independent_only(state, &exit, VerifySignatures::True, spec)?; self.voluntary_exits .write() - .insert(exit.validator_index, exit); + .insert(exit.message.validator_index, exit); Ok(()) } @@ -313,7 +313,7 @@ impl OperationPool { &self, state: &BeaconState, spec: &ChainSpec, - ) -> Vec { + ) -> Vec { filter_limit_operations( self.voluntary_exits.read().values(), |exit| verify_exit(state, exit, VerifySignatures::False, spec).is_ok(), diff --git a/eth2/operation_pool/src/persistence.rs b/eth2/operation_pool/src/persistence.rs index 44e4c31ad..0f6b634bc 100644 --- a/eth2/operation_pool/src/persistence.rs +++ b/eth2/operation_pool/src/persistence.rs @@ -21,7 +21,7 @@ pub struct PersistedOperationPool { /// Proposer slashings. proposer_slashings: Vec, /// Voluntary exits. - voluntary_exits: Vec, + voluntary_exits: Vec, } impl PersistedOperationPool { @@ -86,7 +86,7 @@ impl PersistedOperationPool { let voluntary_exits = RwLock::new( self.voluntary_exits .into_iter() - .map(|exit| (exit.validator_index, exit)) + .map(|exit| (exit.message.validator_index, exit)) .collect(), ); diff --git a/eth2/state_processing/src/common/get_attesting_indices.rs b/eth2/state_processing/src/common/get_attesting_indices.rs index ee9796951..c57332865 100644 --- a/eth2/state_processing/src/common/get_attesting_indices.rs +++ b/eth2/state_processing/src/common/get_attesting_indices.rs @@ -3,7 +3,7 @@ use types::*; /// Returns validator indices which participated in the attestation, sorted by increasing index. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn get_attesting_indices( state: &BeaconState, attestation_data: &AttestationData, diff --git a/eth2/state_processing/src/common/get_indexed_attestation.rs b/eth2/state_processing/src/common/get_indexed_attestation.rs index 16b298488..6804db406 100644 --- a/eth2/state_processing/src/common/get_indexed_attestation.rs +++ b/eth2/state_processing/src/common/get_indexed_attestation.rs @@ -6,7 +6,7 @@ type Result = std::result::Result>; /// Convert `attestation` to (almost) indexed-verifiable form. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn get_indexed_attestation( state: &BeaconState, attestation: &Attestation, diff --git a/eth2/state_processing/src/common/initiate_validator_exit.rs b/eth2/state_processing/src/common/initiate_validator_exit.rs index 479424fbd..efb26be1c 100644 --- a/eth2/state_processing/src/common/initiate_validator_exit.rs +++ b/eth2/state_processing/src/common/initiate_validator_exit.rs @@ -3,7 +3,7 @@ use types::{BeaconStateError as Error, *}; /// Initiate the exit of the validator of the given `index`. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn initiate_validator_exit( state: &mut BeaconState, index: usize, diff --git a/eth2/state_processing/src/common/slash_validator.rs b/eth2/state_processing/src/common/slash_validator.rs index fde3150cc..e308c8346 100644 --- a/eth2/state_processing/src/common/slash_validator.rs +++ b/eth2/state_processing/src/common/slash_validator.rs @@ -4,7 +4,7 @@ use types::{BeaconStateError as Error, *}; /// Slash the validator with index ``index``. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn slash_validator( state: &mut BeaconState, slashed_index: usize, diff --git a/eth2/state_processing/src/genesis.rs b/eth2/state_processing/src/genesis.rs index 88bb4adec..209f025c4 100644 --- a/eth2/state_processing/src/genesis.rs +++ b/eth2/state_processing/src/genesis.rs @@ -6,7 +6,7 @@ use types::*; /// Initialize a `BeaconState` from genesis data. /// -/// Spec v0.9.1 +/// Spec v0.10.1 // TODO: this is quite inefficient and we probably want to rethink how we do this pub fn initialize_beacon_state_from_eth1( eth1_block_hash: Hash256, @@ -15,7 +15,7 @@ pub fn initialize_beacon_state_from_eth1( spec: &ChainSpec, ) -> Result, BlockProcessingError> { let genesis_time = - eth1_timestamp - eth1_timestamp % spec.seconds_per_day + 2 * spec.seconds_per_day; + eth1_timestamp - eth1_timestamp % spec.min_genesis_delay + 2 * spec.min_genesis_delay; let eth1_data = Eth1Data { // Temporary deposit root deposit_root: Hash256::zero(), @@ -47,7 +47,7 @@ pub fn initialize_beacon_state_from_eth1( /// Determine whether a candidate genesis state is suitable for starting the chain. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn is_valid_genesis_state(state: &BeaconState, spec: &ChainSpec) -> bool { state.genesis_time >= spec.min_genesis_time && state.get_active_validator_indices(T::genesis_epoch()).len() as u64 @@ -56,7 +56,7 @@ pub fn is_valid_genesis_state(state: &BeaconState, spec: &ChainSp /// Activate genesis validators, if their balance is acceptable. /// -/// Spec v0.8.0 +/// Spec v0.10.1 pub fn process_activations(state: &mut BeaconState, spec: &ChainSpec) { for (index, validator) in state.validators.iter_mut().enumerate() { let balance = state.balances[index]; diff --git a/eth2/state_processing/src/per_block_processing.rs b/eth2/state_processing/src/per_block_processing.rs index f6351e671..771af88ba 100644 --- a/eth2/state_processing/src/per_block_processing.rs +++ b/eth2/state_processing/src/per_block_processing.rs @@ -3,7 +3,7 @@ use errors::{BlockOperationError, BlockProcessingError, HeaderInvalid, IntoWithI use rayon::prelude::*; use signature_sets::{block_proposal_signature_set, randao_signature_set}; use std::convert::TryInto; -use tree_hash::SignedRoot; +use tree_hash::TreeHash; use types::*; pub use self::verify_attester_slashing::{ @@ -64,23 +64,27 @@ impl VerifySignatures { /// Returns `Ok(())` if the block is valid and the state was successfully updated. Otherwise /// returns an error describing why the block was invalid or how the function failed to execute. /// -/// If `block_signed_root` is `Some`, this root is used for verification of the proposers -/// signature. If it is `None` the signed root is calculated here. This parameter only exists to -/// avoid re-calculating the root when it is already known. +/// If `block_root` is `Some`, this root is used for verification of the proposer's signature. If it +/// is `None` the signing root is computed from scratch. This parameter only exists to avoid +/// re-calculating the root when it is already known. Note `block_root` should be equal to the +/// tree hash root of the block, NOT the signing root of the block. This function takes +/// care of mixing in the domain. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn per_block_processing( mut state: &mut BeaconState, - block: &BeaconBlock, - block_signed_root: Option, + signed_block: &SignedBeaconBlock, + block_root: Option, block_signature_strategy: BlockSignatureStrategy, spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { + let block = &signed_block.message; let verify_signatures = match block_signature_strategy { BlockSignatureStrategy::VerifyBulk => { // Verify all signatures in the block at once. block_verify!( - BlockSignatureVerifier::verify_entire_block(state, block, spec).is_ok(), + BlockSignatureVerifier::verify_entire_block(state, signed_block, block_root, spec) + .is_ok(), BlockProcessingError::BulkSignatureVerificationFailed ); VerifySignatures::False @@ -89,7 +93,11 @@ pub fn per_block_processing( BlockSignatureStrategy::NoVerification => VerifySignatures::False, }; - process_block_header(state, block, block_signed_root, verify_signatures, spec)?; + process_block_header(state, block, spec)?; + + if verify_signatures.is_true() { + verify_block_signature(&state, signed_block, block_root, &spec)?; + } // Ensure the current and previous epoch caches are built. state.build_committee_cache(RelativeEpoch::Previous, spec)?; @@ -128,18 +136,16 @@ pub fn per_block_processing( /// Processes the block header. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_block_header( state: &mut BeaconState, block: &BeaconBlock, - block_signed_root: Option, - verify_signatures: VerifySignatures, spec: &ChainSpec, ) -> Result<(), BlockOperationError> { verify!(block.slot == state.slot, HeaderInvalid::StateSlotMismatch); let expected_previous_block_root = - Hash256::from_slice(&state.latest_block_header.signed_root()); + Hash256::from_slice(&state.latest_block_header.tree_hash_root()); verify!( block.parent_root == expected_previous_block_root, HeaderInvalid::ParentBlockRootMismatch { @@ -158,24 +164,20 @@ pub fn process_block_header( HeaderInvalid::ProposerSlashed(proposer_idx) ); - if verify_signatures.is_true() { - verify_block_signature(&state, &block, block_signed_root, &spec)?; - } - Ok(()) } /// Verifies the signature of a block. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn verify_block_signature( state: &BeaconState, - block: &BeaconBlock, - block_signed_root: Option, + block: &SignedBeaconBlock, + block_root: Option, spec: &ChainSpec, ) -> Result<(), BlockOperationError> { verify!( - block_proposal_signature_set(state, block, block_signed_root, spec)?.is_valid(), + block_proposal_signature_set(state, block, block_root, spec)?.is_valid(), HeaderInvalid::ProposalSignatureInvalid ); @@ -185,7 +187,7 @@ pub fn verify_block_signature( /// Verifies the `randao_reveal` against the block's proposer pubkey and updates /// `state.latest_randao_mixes`. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_randao( state: &mut BeaconState, block: &BeaconBlock, @@ -208,7 +210,7 @@ pub fn process_randao( /// Update the `state.eth1_data_votes` based upon the `eth1_data` provided. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_eth1_data( state: &mut BeaconState, eth1_data: &Eth1Data, @@ -225,7 +227,7 @@ pub fn process_eth1_data( /// Returns `Some(eth1_data)` if adding the given `eth1_data` to `state.eth1_data_votes` would /// result in a change to `state.eth1_data`. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn get_new_eth1_data( state: &BeaconState, eth1_data: &Eth1Data, @@ -249,7 +251,7 @@ pub fn get_new_eth1_data( /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// an `Err` describing the invalid object or cause of failure. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_proposer_slashings( state: &mut BeaconState, proposer_slashings: &[ProposerSlashing], @@ -278,7 +280,7 @@ pub fn process_proposer_slashings( /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// an `Err` describing the invalid object or cause of failure. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_attester_slashings( state: &mut BeaconState, attester_slashings: &[AttesterSlashing], @@ -332,7 +334,7 @@ pub fn process_attester_slashings( /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// an `Err` describing the invalid object or cause of failure. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_attestations( state: &mut BeaconState, attestations: &[Attestation], @@ -378,7 +380,7 @@ pub fn process_attestations( /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// an `Err` describing the invalid object or cause of failure. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_deposits( state: &mut BeaconState, deposits: &[Deposit], @@ -415,7 +417,7 @@ pub fn process_deposits( /// Process a single deposit, optionally verifying its merkle proof. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_deposit( state: &mut BeaconState, deposit: &Deposit, @@ -481,10 +483,10 @@ pub fn process_deposit( /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// an `Err` describing the invalid object or cause of failure. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_exits( state: &mut BeaconState, - voluntary_exits: &[VoluntaryExit], + voluntary_exits: &[SignedVoluntaryExit], verify_signatures: VerifySignatures, spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { @@ -498,7 +500,7 @@ pub fn process_exits( // Update the state in series. for exit in voluntary_exits { - initiate_validator_exit(state, exit.validator_index as usize, spec)?; + initiate_validator_exit(state, exit.message.validator_index as usize, spec)?; } Ok(()) diff --git a/eth2/state_processing/src/per_block_processing/block_processing_builder.rs b/eth2/state_processing/src/per_block_processing/block_processing_builder.rs index cba37cfbb..52aa15ec2 100644 --- a/eth2/state_processing/src/per_block_processing/block_processing_builder.rs +++ b/eth2/state_processing/src/per_block_processing/block_processing_builder.rs @@ -1,5 +1,5 @@ use std::convert::TryInto; -use tree_hash::SignedRoot; +use tree_hash::TreeHash; use types::test_utils::{ AttestationTestTask, AttesterSlashingTestTask, DepositTestTask, ExitTestTask, ProposerSlashingTestTask, TestingBeaconBlockBuilder, TestingBeaconStateBuilder, @@ -41,7 +41,7 @@ impl BlockProcessingBuilder { randao_sk: Option, previous_block_root: Option, spec: &ChainSpec, - ) -> (BeaconBlock, BeaconState) { + ) -> (SignedBeaconBlock, BeaconState) { let (mut state, keypairs) = self.state_builder.build(); let builder = &mut self.block_builder; @@ -51,7 +51,7 @@ impl BlockProcessingBuilder { match previous_block_root { Some(root) => builder.set_parent_root(root), None => builder.set_parent_root(Hash256::from_slice( - &state.latest_block_header.signed_root(), + &state.latest_block_header.tree_hash_root(), )), } @@ -84,7 +84,7 @@ impl BlockProcessingBuilder { randao_sk: Option, previous_block_root: Option, spec: &ChainSpec, - ) -> (BeaconBlock, BeaconState) { + ) -> (SignedBeaconBlock, BeaconState) { let (mut state, keypairs) = self.state_builder.build(); let builder = &mut self.block_builder; @@ -93,7 +93,7 @@ impl BlockProcessingBuilder { match previous_block_root { Some(root) => builder.set_parent_root(root), None => builder.set_parent_root(Hash256::from_slice( - &state.latest_block_header.signed_root(), + &state.latest_block_header.tree_hash_root(), )), } @@ -141,7 +141,7 @@ impl BlockProcessingBuilder { randao_sk: Option, previous_block_root: Option, spec: &ChainSpec, - ) -> (BeaconBlock, BeaconState) { + ) -> (SignedBeaconBlock, BeaconState) { let (state, keypairs) = self.state_builder.build(); let builder = &mut self.block_builder; @@ -150,7 +150,7 @@ impl BlockProcessingBuilder { match previous_block_root { Some(root) => builder.set_parent_root(root), None => builder.set_parent_root(Hash256::from_slice( - &state.latest_block_header.signed_root(), + &state.latest_block_header.tree_hash_root(), )), } @@ -184,7 +184,7 @@ impl BlockProcessingBuilder { randao_sk: Option, previous_block_root: Option, spec: &ChainSpec, - ) -> (BeaconBlock, BeaconState) { + ) -> (SignedBeaconBlock, BeaconState) { let (state, keypairs) = self.state_builder.build(); let builder = &mut self.block_builder; @@ -193,7 +193,7 @@ impl BlockProcessingBuilder { match previous_block_root { Some(root) => builder.set_parent_root(root), None => builder.set_parent_root(Hash256::from_slice( - &state.latest_block_header.signed_root(), + &state.latest_block_header.tree_hash_root(), )), } @@ -233,7 +233,7 @@ impl BlockProcessingBuilder { randao_sk: Option, previous_block_root: Option, spec: &ChainSpec, - ) -> (BeaconBlock, BeaconState) { + ) -> (SignedBeaconBlock, BeaconState) { let (state, keypairs) = self.state_builder.build(); let builder = &mut self.block_builder; @@ -242,7 +242,7 @@ impl BlockProcessingBuilder { match previous_block_root { Some(root) => builder.set_parent_root(root), None => builder.set_parent_root(Hash256::from_slice( - &state.latest_block_header.signed_root(), + &state.latest_block_header.tree_hash_root(), )), } @@ -275,7 +275,7 @@ impl BlockProcessingBuilder { randao_sk: Option, previous_block_root: Option, spec: &ChainSpec, - ) -> (BeaconBlock, BeaconState) { + ) -> (SignedBeaconBlock, BeaconState) { let (state, keypairs) = self.state_builder.build(); let builder = &mut self.block_builder; @@ -284,7 +284,7 @@ impl BlockProcessingBuilder { match previous_block_root { Some(root) => builder.set_parent_root(root), None => builder.set_parent_root(Hash256::from_slice( - &state.latest_block_header.signed_root(), + &state.latest_block_header.tree_hash_root(), )), } diff --git a/eth2/state_processing/src/per_block_processing/block_signature_verifier.rs b/eth2/state_processing/src/per_block_processing/block_signature_verifier.rs index 762af0ca7..850b439dd 100644 --- a/eth2/state_processing/src/per_block_processing/block_signature_verifier.rs +++ b/eth2/state_processing/src/per_block_processing/block_signature_verifier.rs @@ -1,10 +1,12 @@ use super::signature_sets::{Error as SignatureSetError, Result as SignatureSetResult, *}; + use crate::common::get_indexed_attestation; use crate::per_block_processing::errors::{AttestationInvalid, BlockOperationError}; use bls::{verify_signature_sets, SignatureSet}; use rayon::prelude::*; use types::{ - BeaconBlock, BeaconState, BeaconStateError, ChainSpec, EthSpec, Hash256, IndexedAttestation, + BeaconState, BeaconStateError, ChainSpec, EthSpec, Hash256, IndexedAttestation, + SignedBeaconBlock, }; pub type Result = std::result::Result; @@ -40,12 +42,12 @@ impl From> for Error { } } -/// Reads the BLS signatures and keys from a `BeaconBlock`, storing them as a `Vec`. +/// Reads the BLS signatures and keys from a `SignedBeaconBlock`, storing them as a `Vec`. /// /// This allows for optimizations related to batch BLS operations (see the /// `Self::verify_entire_block(..)` function). pub struct BlockSignatureVerifier<'a, T: EthSpec> { - block: &'a BeaconBlock, + block: &'a SignedBeaconBlock, state: &'a BeaconState, spec: &'a ChainSpec, sets: Vec>, @@ -54,7 +56,11 @@ pub struct BlockSignatureVerifier<'a, T: EthSpec> { impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> { /// Create a new verifier without any included signatures. See the `include...` functions to /// add signatures, and the `verify` - pub fn new(state: &'a BeaconState, block: &'a BeaconBlock, spec: &'a ChainSpec) -> Self { + pub fn new( + state: &'a BeaconState, + block: &'a SignedBeaconBlock, + spec: &'a ChainSpec, + ) -> Self { Self { block, state, @@ -63,7 +69,7 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> { } } - /// Verify all* the signatures in the given `BeaconBlock`, returning `Ok(())` if the signatures + /// Verify all* the signatures in the given `SignedBeaconBlock`, returning `Ok(())` if the signatures /// are valid. /// /// * : _Does not verify any signatures in `block.body.deposits`. A block is still valid if it @@ -72,12 +78,13 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> { /// See `Self::verify` for more detail. pub fn verify_entire_block( state: &'a BeaconState, - block: &'a BeaconBlock, + block: &'a SignedBeaconBlock, + block_root: Option, spec: &'a ChainSpec, ) -> Result<()> { let mut verifier = Self::new(state, block, spec); - verifier.include_block_proposal(None)?; + verifier.include_block_proposal(block_root)?; verifier.include_randao_reveal()?; verifier.include_proposer_slashings()?; verifier.include_attester_slashings()?; @@ -129,7 +136,7 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> { /// Includes the randao signature for `self.block` for verification. fn include_randao_reveal(&mut self) -> Result<()> { - let set = randao_signature_set(self.state, self.block, self.spec)?; + let set = randao_signature_set(self.state, &self.block.message, self.spec)?; self.sets.push(set); Ok(()) } @@ -138,6 +145,7 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> { fn include_proposer_slashings(&mut self) -> Result<()> { let mut sets: Vec = self .block + .message .body .proposer_slashings .iter() @@ -158,6 +166,7 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> { /// Includes all signatures in `self.block.body.attester_slashings` for verification. fn include_attester_slashings(&mut self) -> Result<()> { self.block + .message .body .attester_slashings .iter() @@ -175,6 +184,7 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> { /// Includes all signatures in `self.block.body.attestations` for verification. fn include_attestations(&mut self) -> Result>> { self.block + .message .body .attestations .iter() @@ -198,6 +208,7 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> { fn include_exits(&mut self) -> Result<()> { let mut sets = self .block + .message .body .voluntary_exits .iter() diff --git a/eth2/state_processing/src/per_block_processing/errors.rs b/eth2/state_processing/src/per_block_processing/errors.rs index 2b17a26d2..609c57bdd 100644 --- a/eth2/state_processing/src/per_block_processing/errors.rs +++ b/eth2/state_processing/src/per_block_processing/errors.rs @@ -210,6 +210,11 @@ pub enum AttestationInvalid { }, /// Attestation slot is too far in the past to be included in a block. IncludedTooLate { state: Slot, attestation: Slot }, + /// Attestation target epoch does not match attestation slot. + TargetEpochSlotMismatch { + target_epoch: Epoch, + slot_epoch: Epoch, + }, /// Attestation target epoch does not match the current or previous epoch. BadTargetEpoch, /// Attestation justified checkpoint doesn't match the state's current or previous justified diff --git a/eth2/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs b/eth2/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs index 007cf741a..323f0c5bf 100644 --- a/eth2/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs +++ b/eth2/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs @@ -11,7 +11,7 @@ fn error(reason: Invalid) -> BlockOperationError { /// Verify an `IndexedAttestation`. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn is_valid_indexed_attestation( state: &BeaconState, indexed_attestation: &IndexedAttestation, @@ -26,14 +26,13 @@ pub fn is_valid_indexed_attestation( Invalid::MaxIndicesExceed(T::MaxValidatorsPerCommittee::to_usize(), indices.len()) ); - // Check that indices are sorted + // Check that indices are sorted and unique let check_sorted = |list: &[u64]| -> Result<()> { list.windows(2).enumerate().try_for_each(|(i, pair)| { - // The spec allows duplicates, so use strict comparison (>). - if pair[0] > pair[1] { - Err(error(Invalid::BadValidatorIndicesOrdering(i))) - } else { + if pair[0] < pair[1] { Ok(()) + } else { + Err(error(Invalid::BadValidatorIndicesOrdering(i))) } })?; Ok(()) diff --git a/eth2/state_processing/src/per_block_processing/signature_sets.rs b/eth2/state_processing/src/per_block_processing/signature_sets.rs index 09563c66d..fb7742cd8 100644 --- a/eth2/state_processing/src/per_block_processing/signature_sets.rs +++ b/eth2/state_processing/src/per_block_processing/signature_sets.rs @@ -5,11 +5,12 @@ use bls::{G1Point, G1Ref, SignatureSet, SignedMessage}; use std::borrow::Cow; use std::convert::TryInto; -use tree_hash::{SignedRoot, TreeHash}; +use tree_hash::TreeHash; use types::{ - AggregateSignature, AttesterSlashing, BeaconBlock, BeaconBlockHeader, BeaconState, - BeaconStateError, ChainSpec, DepositData, Domain, EthSpec, Hash256, IndexedAttestation, - ProposerSlashing, PublicKey, Signature, VoluntaryExit, + AggregateSignature, AttesterSlashing, BeaconBlock, BeaconState, BeaconStateError, ChainSpec, + DepositData, Domain, EthSpec, Hash256, IndexedAttestation, ProposerSlashing, PublicKey, + Signature, SignedBeaconBlock, SignedBeaconBlockHeader, SignedRoot, SignedVoluntaryExit, + SigningRoot, }; pub type Result = std::result::Result; @@ -41,10 +42,11 @@ impl From for Error { /// A signature set that is valid if a block was signed by the expected block producer. pub fn block_proposal_signature_set<'a, T: EthSpec>( state: &'a BeaconState, - block: &'a BeaconBlock, - block_signed_root: Option, + signed_block: &'a SignedBeaconBlock, + block_root: Option, spec: &'a ChainSpec, ) -> Result> { + let block = &signed_block.message; let proposer_index = state.get_beacon_proposer_index(block.slot, spec)?; let domain = spec.get_domain( @@ -53,17 +55,20 @@ pub fn block_proposal_signature_set<'a, T: EthSpec>( &state.fork, ); - let message = if let Some(root) = block_signed_root { - root.as_bytes().to_vec() + let message = if let Some(root) = block_root { + SigningRoot { + object_root: root, + domain, + } + .tree_hash_root() } else { - block.signed_root() + block.signing_root(domain).as_bytes().to_vec() }; Ok(SignatureSet::single( - &block.signature, + &signed_block.signature, validator_pubkey(state, proposer_index)?, message, - domain, )) } @@ -81,13 +86,12 @@ pub fn randao_signature_set<'a, T: EthSpec>( &state.fork, ); - let message = state.current_epoch().tree_hash_root(); + let message = state.current_epoch().signing_root(domain); Ok(SignatureSet::single( &block.body.randao_reveal, validator_pubkey(state, proposer_index)?, - message, - domain, + message.as_bytes().to_vec(), )) } @@ -102,13 +106,13 @@ pub fn proposer_slashing_signature_set<'a, T: EthSpec>( Ok(( block_header_signature_set( state, - &proposer_slashing.header_1, + &proposer_slashing.signed_header_1, validator_pubkey(state, proposer_index)?, spec, )?, block_header_signature_set( state, - &proposer_slashing.header_2, + &proposer_slashing.signed_header_2, validator_pubkey(state, proposer_index)?, spec, )?, @@ -118,23 +122,26 @@ pub fn proposer_slashing_signature_set<'a, T: EthSpec>( /// Returns a signature set that is valid if the given `pubkey` signed the `header`. fn block_header_signature_set<'a, T: EthSpec>( state: &'a BeaconState, - header: &'a BeaconBlockHeader, + signed_header: &'a SignedBeaconBlockHeader, pubkey: Cow<'a, G1Point>, spec: &'a ChainSpec, ) -> Result> { let domain = spec.get_domain( - header.slot.epoch(T::slots_per_epoch()), + signed_header.message.slot.epoch(T::slots_per_epoch()), Domain::BeaconProposer, &state.fork, ); - let message = header.signed_root(); + let message = signed_header + .message + .signing_root(domain) + .as_bytes() + .to_vec(); Ok(SignatureSet::single( - &header.signature, + &signed_header.signature, pubkey, message, - domain, )) } @@ -145,23 +152,22 @@ pub fn indexed_attestation_signature_set<'a, 'b, T: EthSpec>( indexed_attestation: &'b IndexedAttestation, spec: &'a ChainSpec, ) -> Result> { - let message = indexed_attestation.data.tree_hash_root(); - let pubkeys = indexed_attestation .attesting_indices .into_iter() .map(|&validator_idx| Ok(validator_pubkey(state, validator_idx as usize)?)) .collect::>()?; - let signed_message = SignedMessage::new(pubkeys, message); - let domain = spec.get_domain( indexed_attestation.data.target.epoch, Domain::BeaconAttester, &state.fork, ); - Ok(SignatureSet::new(signature, vec![signed_message], domain)) + let message = indexed_attestation.data.signing_root(domain); + let signed_message = SignedMessage::new(pubkeys, message.as_bytes().to_vec()); + + Ok(SignatureSet::new(signature, vec![signed_message])) } /// Returns the signature set for the given `attester_slashing` and corresponding `pubkeys`. @@ -191,10 +197,16 @@ pub fn attester_slashing_signature_sets<'a, T: EthSpec>( /// This method is separate to `deposit_signature_set` to satisfy lifetime requirements. pub fn deposit_pubkey_signature_message( deposit_data: &DepositData, + spec: &ChainSpec, ) -> Option<(PublicKey, Signature, Vec)> { let pubkey = (&deposit_data.pubkey).try_into().ok()?; let signature = (&deposit_data.signature).try_into().ok()?; - let message = deposit_data.signed_root(); + let domain = spec.get_deposit_domain(); + let message = deposit_data + .as_deposit_message() + .signing_root(domain) + .as_bytes() + .to_vec(); Some((pubkey, signature, message)) } @@ -202,43 +214,37 @@ pub fn deposit_pubkey_signature_message( /// `deposit_pubkey_signature_message`. pub fn deposit_signature_set<'a>( pubkey_signature_message: &'a (PublicKey, Signature, Vec), - spec: &'a ChainSpec, ) -> SignatureSet<'a> { let (pubkey, signature, message) = pubkey_signature_message; // Note: Deposits are valid across forks, thus the deposit domain is computed // with the fork zeroed. - SignatureSet::single( - signature, - pubkey.g1_ref(), - message.clone(), - spec.get_deposit_domain(), - ) + SignatureSet::single(signature, pubkey.g1_ref(), message.clone()) } -/// Returns a signature set that is valid if the `VoluntaryExit` was signed by the indicated +/// Returns a signature set that is valid if the `SignedVoluntaryExit` was signed by the indicated /// validator. pub fn exit_signature_set<'a, T: EthSpec>( state: &'a BeaconState, - exit: &'a VoluntaryExit, + signed_exit: &'a SignedVoluntaryExit, spec: &'a ChainSpec, ) -> Result> { + let exit = &signed_exit.message; let proposer_index = exit.validator_index as usize; let domain = spec.get_domain(exit.epoch, Domain::VoluntaryExit, &state.fork); - let message = exit.signed_root(); + let message = exit.signing_root(domain).as_bytes().to_vec(); Ok(SignatureSet::single( - &exit.signature, + &signed_exit.signature, validator_pubkey(state, proposer_index)?, message, - domain, )) } /// Maps a validator index to a `PublicKey`. -fn validator_pubkey<'a, T: EthSpec>( +pub fn validator_pubkey<'a, T: EthSpec>( state: &'a BeaconState, validator_index: usize, ) -> Result> { diff --git a/eth2/state_processing/src/per_block_processing/tests.rs b/eth2/state_processing/src/per_block_processing/tests.rs index 32c460d85..f86396a61 100644 --- a/eth2/state_processing/src/per_block_processing/tests.rs +++ b/eth2/state_processing/src/per_block_processing/tests.rs @@ -3,7 +3,6 @@ use super::block_processing_builder::BlockProcessingBuilder; use super::errors::*; use crate::{per_block_processing, BlockSignatureStrategy}; -use tree_hash::SignedRoot; use types::test_utils::{ AttestationTestTask, AttesterSlashingTestTask, DepositTestTask, ExitTestTask, ProposerSlashingTestTask, @@ -16,6 +15,8 @@ pub const SLOT_OFFSET: u64 = 4; pub const EXIT_SLOT_OFFSET: u64 = 2048; pub const NUM_ATTESTATIONS: u64 = 1; +type E = MainnetEthSpec; + #[test] fn valid_block_ok() { let spec = MainnetEthSpec::default_spec(); @@ -40,7 +41,7 @@ fn invalid_block_header_state_slot() { let (mut block, mut state) = builder.build(None, None, &spec); state.slot = Slot::new(133_713); - block.slot = Slot::new(424_242); + block.message.slot = Slot::new(424_242); let result = per_block_processing( &mut state, @@ -77,8 +78,8 @@ fn invalid_parent_block_root() { result, Err(BlockProcessingError::HeaderInvalid { reason: HeaderInvalid::ParentBlockRootMismatch { - state: Hash256::from_slice(&state.latest_block_header.signed_root()), - block: block.parent_root + state: state.latest_block_header.canonical_root(), + block: block.parent_root() } }) ); @@ -88,14 +89,11 @@ fn invalid_parent_block_root() { fn invalid_block_signature() { let spec = MainnetEthSpec::default_spec(); let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); - let (mut block, mut state) = builder.build(None, None, &spec); + let (block, mut state) = builder.build(None, None, &spec); // sign the block with a keypair that is not the expected proposer let keypair = Keypair::random(); - let message = block.signed_root(); - let epoch = block.slot.epoch(MainnetEthSpec::slots_per_epoch()); - let domain = spec.get_domain(epoch, Domain::BeaconProposer, &state.fork); - block.signature = Signature::new(&message, domain, &keypair.sk); + let block = block.message.sign(&keypair.sk, &state.fork, &spec); // process block with invalid block signature let result = per_block_processing( @@ -629,60 +627,6 @@ fn invalid_attestation_wrong_justified_checkpoint() { ); } -#[test] -fn invalid_attestation_bad_target_too_low() { - let spec = MainnetEthSpec::default_spec(); - let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); - let test_task = AttestationTestTask::BadTargetTooLow; - let (block, mut state) = - builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec); - - let result = per_block_processing( - &mut state, - &block, - None, - BlockSignatureStrategy::VerifyIndividual, - &spec, - ); - - // Expecting BadTargetEpoch because we manually set the - // target field of the AttestationData object to be invalid - assert_eq!( - result, - Err(BlockProcessingError::AttestationInvalid { - index: 0, - reason: AttestationInvalid::BadTargetEpoch - }) - ); -} - -#[test] -fn invalid_attestation_bad_target_too_high() { - let spec = MainnetEthSpec::default_spec(); - let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); - let test_task = AttestationTestTask::BadTargetTooHigh; - let (block, mut state) = - builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec); - - let result = per_block_processing( - &mut state, - &block, - None, - BlockSignatureStrategy::VerifyIndividual, - &spec, - ); - - // Expecting BadTargetEpoch because we manually set the - // target field of the AttestationData object to be invalid - assert_eq!( - result, - Err(BlockProcessingError::AttestationInvalid { - index: 0, - reason: AttestationInvalid::BadTargetEpoch - }) - ); -} - #[test] fn invalid_attestation_bad_indexed_attestation_bad_signature() { let spec = MainnetEthSpec::default_spec(); @@ -787,7 +731,7 @@ fn invalid_attestation_included_too_early() { reason: AttestationInvalid::IncludedTooEarly { state: state.slot, delay: spec.min_attestation_inclusion_delay, - attestation: block.body.attestations[0].data.slot, + attestation: block.message.body.attestations[0].data.slot, } }) ); @@ -816,18 +760,18 @@ fn invalid_attestation_included_too_late() { index: 0, reason: AttestationInvalid::IncludedTooLate { state: state.slot, - attestation: block.body.attestations[0].data.slot, + attestation: block.message.body.attestations[0].data.slot, } }) ); } #[test] -fn invalid_attestation_bad_target_epoch() { +fn invalid_attestation_target_epoch_slot_mismatch() { let spec = MainnetEthSpec::default_spec(); // note to maintainer: might need to increase validator count if we get NoCommittee let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); - let test_task = AttestationTestTask::BadTargetEpoch; + let test_task = AttestationTestTask::TargetEpochSlotMismatch; let (block, mut state) = builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec); @@ -839,20 +783,16 @@ fn invalid_attestation_bad_target_epoch() { &spec, ); - // Expecting BadTargetEpoch because the target epoch is bigger by one than the epoch expected - assert!( - result - == Err(BlockProcessingError::BeaconStateError( - BeaconStateError::NoCommittee { - slot: Slot::new(0), - index: 0 - } - )) - || result - == Err(BlockProcessingError::AttestationInvalid { - index: 0, - reason: AttestationInvalid::BadTargetEpoch - }) + let attestation = &block.message.body.attestations[0].data; + assert_eq!( + result, + Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::TargetEpochSlotMismatch { + target_epoch: attestation.target.epoch, + slot_epoch: attestation.slot.epoch(E::slots_per_epoch()), + } + }) ); } diff --git a/eth2/state_processing/src/per_block_processing/verify_attestation.rs b/eth2/state_processing/src/per_block_processing/verify_attestation.rs index 1540dee93..91dba2d7c 100644 --- a/eth2/state_processing/src/per_block_processing/verify_attestation.rs +++ b/eth2/state_processing/src/per_block_processing/verify_attestation.rs @@ -15,7 +15,7 @@ fn error(reason: Invalid) -> BlockOperationError { /// /// Optionally verifies the aggregate signature, depending on `verify_signatures`. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn verify_attestation_for_block_inclusion( state: &BeaconState, attestation: &Attestation, @@ -49,7 +49,7 @@ pub fn verify_attestation_for_block_inclusion( /// Returns a descriptive `Err` if the attestation is malformed or does not accurately reflect the /// prior blocks in `state`. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn verify_attestation_for_state( state: &BeaconState, attestation: &Attestation, @@ -75,12 +75,19 @@ pub fn verify_attestation_for_state( /// Check target epoch and source checkpoint. /// -/// Spec v0.9.1 +/// Spec v0.10.1 fn verify_casper_ffg_vote( attestation: &Attestation, state: &BeaconState, ) -> Result<()> { let data = &attestation.data; + verify!( + data.target.epoch == data.slot.epoch(T::slots_per_epoch()), + Invalid::TargetEpochSlotMismatch { + target_epoch: data.target.epoch, + slot_epoch: data.slot.epoch(T::slots_per_epoch()), + } + ); if data.target.epoch == state.current_epoch() { verify!( data.source == state.current_justified_checkpoint, diff --git a/eth2/state_processing/src/per_block_processing/verify_attester_slashing.rs b/eth2/state_processing/src/per_block_processing/verify_attester_slashing.rs index cf966935f..f8b729f6a 100644 --- a/eth2/state_processing/src/per_block_processing/verify_attester_slashing.rs +++ b/eth2/state_processing/src/per_block_processing/verify_attester_slashing.rs @@ -15,7 +15,7 @@ fn error(reason: Invalid) -> BlockOperationError { /// /// Returns `Ok(())` if the `AttesterSlashing` is valid, otherwise indicates the reason for invalidity. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn verify_attester_slashing( state: &BeaconState, attester_slashing: &AttesterSlashing, @@ -47,7 +47,7 @@ pub fn verify_attester_slashing( /// /// Returns Ok(indices) if `indices.len() > 0`. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn get_slashable_indices( state: &BeaconState, attester_slashing: &AttesterSlashing, diff --git a/eth2/state_processing/src/per_block_processing/verify_deposit.rs b/eth2/state_processing/src/per_block_processing/verify_deposit.rs index 42f1f6ec6..1115f46ca 100644 --- a/eth2/state_processing/src/per_block_processing/verify_deposit.rs +++ b/eth2/state_processing/src/per_block_processing/verify_deposit.rs @@ -14,13 +14,13 @@ fn error(reason: DepositInvalid) -> BlockOperationError { /// Verify `Deposit.pubkey` signed `Deposit.signature`. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn verify_deposit_signature(deposit_data: &DepositData, spec: &ChainSpec) -> Result<()> { - let deposit_signature_message = deposit_pubkey_signature_message(&deposit_data) + let deposit_signature_message = deposit_pubkey_signature_message(&deposit_data, spec) .ok_or_else(|| error(DepositInvalid::BadBlsBytes))?; verify!( - deposit_signature_set(&deposit_signature_message, spec).is_valid(), + deposit_signature_set(&deposit_signature_message).is_valid(), DepositInvalid::BadSignature ); @@ -46,7 +46,7 @@ pub fn get_existing_validator_index( /// The deposit index is provided as a parameter so we can check proofs /// before they're due to be processed, and in parallel. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn verify_deposit_merkle_proof( state: &BeaconState, deposit: &Deposit, diff --git a/eth2/state_processing/src/per_block_processing/verify_exit.rs b/eth2/state_processing/src/per_block_processing/verify_exit.rs index 385e50610..a2d185402 100644 --- a/eth2/state_processing/src/per_block_processing/verify_exit.rs +++ b/eth2/state_processing/src/per_block_processing/verify_exit.rs @@ -13,10 +13,10 @@ fn error(reason: ExitInvalid) -> BlockOperationError { /// /// Returns `Ok(())` if the `Exit` is valid, otherwise indicates the reason for invalidity. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn verify_exit( state: &BeaconState, - exit: &VoluntaryExit, + exit: &SignedVoluntaryExit, verify_signatures: VerifySignatures, spec: &ChainSpec, ) -> Result<()> { @@ -25,10 +25,10 @@ pub fn verify_exit( /// Like `verify_exit` but doesn't run checks which may become true in future states. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn verify_exit_time_independent_only( state: &BeaconState, - exit: &VoluntaryExit, + exit: &SignedVoluntaryExit, verify_signatures: VerifySignatures, spec: &ChainSpec, ) -> Result<()> { @@ -37,14 +37,16 @@ pub fn verify_exit_time_independent_only( /// Parametric version of `verify_exit` that skips some checks if `time_independent_only` is true. /// -/// Spec v0.9.1 +/// Spec v0.10.1 fn verify_exit_parametric( state: &BeaconState, - exit: &VoluntaryExit, + signed_exit: &SignedVoluntaryExit, verify_signatures: VerifySignatures, spec: &ChainSpec, time_independent_only: bool, ) -> Result<()> { + let exit = &signed_exit.message; + let validator = state .validators .get(exit.validator_index as usize) @@ -82,7 +84,7 @@ fn verify_exit_parametric( if verify_signatures.is_true() { verify!( - exit_signature_set(state, exit, spec)?.is_valid(), + exit_signature_set(state, signed_exit, spec)?.is_valid(), ExitInvalid::BadSignature ); } diff --git a/eth2/state_processing/src/per_block_processing/verify_proposer_slashing.rs b/eth2/state_processing/src/per_block_processing/verify_proposer_slashing.rs index 9683d8762..268e56c14 100644 --- a/eth2/state_processing/src/per_block_processing/verify_proposer_slashing.rs +++ b/eth2/state_processing/src/per_block_processing/verify_proposer_slashing.rs @@ -14,7 +14,7 @@ fn error(reason: Invalid) -> BlockOperationError { /// /// Returns `Ok(())` if the `ProposerSlashing` is valid, otherwise indicates the reason for invalidity. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn verify_proposer_slashing( proposer_slashing: &ProposerSlashing, state: &BeaconState, @@ -28,16 +28,17 @@ pub fn verify_proposer_slashing( // Verify slots match verify!( - proposer_slashing.header_1.slot == proposer_slashing.header_2.slot, + proposer_slashing.signed_header_1.message.slot + == proposer_slashing.signed_header_2.message.slot, Invalid::ProposalSlotMismatch( - proposer_slashing.header_1.slot, - proposer_slashing.header_2.slot + proposer_slashing.signed_header_1.message.slot, + proposer_slashing.signed_header_2.message.slot ) ); // But the headers are different verify!( - proposer_slashing.header_1 != proposer_slashing.header_2, + proposer_slashing.signed_header_1 != proposer_slashing.signed_header_2, Invalid::ProposalsIdentical ); diff --git a/eth2/state_processing/src/per_epoch_processing.rs b/eth2/state_processing/src/per_epoch_processing.rs index f2c7e4135..72e9e49cd 100644 --- a/eth2/state_processing/src/per_epoch_processing.rs +++ b/eth2/state_processing/src/per_epoch_processing.rs @@ -19,7 +19,7 @@ pub use validator_statuses::{TotalBalances, ValidatorStatus, ValidatorStatuses}; /// Mutates the given `BeaconState`, returning early if an error is encountered. If an error is /// returned, a state might be "half-processed" and therefore in an invalid state. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn per_epoch_processing( state: &mut BeaconState, spec: &ChainSpec, @@ -66,7 +66,7 @@ pub fn per_epoch_processing( /// - `finalized_epoch` /// - `finalized_root` /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[allow(clippy::if_same_then_else)] // For readability and consistency with spec. pub fn process_justification_and_finalization( state: &mut BeaconState, @@ -134,7 +134,7 @@ pub fn process_justification_and_finalization( /// Finish up an epoch update. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_final_updates( state: &mut BeaconState, spec: &ChainSpec, diff --git a/eth2/state_processing/src/per_epoch_processing/apply_rewards.rs b/eth2/state_processing/src/per_epoch_processing/apply_rewards.rs index 4d5c69c6c..0cb271d60 100644 --- a/eth2/state_processing/src/per_epoch_processing/apply_rewards.rs +++ b/eth2/state_processing/src/per_epoch_processing/apply_rewards.rs @@ -33,7 +33,7 @@ impl std::ops::AddAssign for Delta { /// Apply attester and proposer rewards. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_rewards_and_penalties( state: &mut BeaconState, validator_statuses: &mut ValidatorStatuses, @@ -67,7 +67,7 @@ pub fn process_rewards_and_penalties( /// For each attesting validator, reward the proposer who was first to include their attestation. /// -/// Spec v0.9.1 +/// Spec v0.10.1 fn get_proposer_deltas( deltas: &mut Vec, state: &BeaconState, @@ -100,7 +100,7 @@ fn get_proposer_deltas( /// Apply rewards for participation in attestations during the previous epoch. /// -/// Spec v0.9.1 +/// Spec v0.10.1 fn get_attestation_deltas( deltas: &mut Vec, state: &BeaconState, @@ -133,7 +133,7 @@ fn get_attestation_deltas( /// Determine the delta for a single validator, sans proposer rewards. /// -/// Spec v0.9.1 +/// Spec v0.10.1 fn get_attestation_delta( validator: &ValidatorStatus, total_balances: &TotalBalances, diff --git a/eth2/state_processing/src/per_epoch_processing/process_slashings.rs b/eth2/state_processing/src/per_epoch_processing/process_slashings.rs index b96add8e2..d56894af5 100644 --- a/eth2/state_processing/src/per_epoch_processing/process_slashings.rs +++ b/eth2/state_processing/src/per_epoch_processing/process_slashings.rs @@ -2,7 +2,7 @@ use types::{BeaconStateError as Error, *}; /// Process slashings. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_slashings( state: &mut BeaconState, total_balance: u64, diff --git a/eth2/state_processing/src/per_epoch_processing/registry_updates.rs b/eth2/state_processing/src/per_epoch_processing/registry_updates.rs index ab3d52979..048be3b0a 100644 --- a/eth2/state_processing/src/per_epoch_processing/registry_updates.rs +++ b/eth2/state_processing/src/per_epoch_processing/registry_updates.rs @@ -5,7 +5,7 @@ use types::*; /// Performs a validator registry update, if required. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn process_registry_updates( state: &mut BeaconState, spec: &ChainSpec, @@ -15,10 +15,6 @@ pub fn process_registry_updates( // We assume it's safe to re-order the change in eligibility and `initiate_validator_exit`. // Rest assured exiting validators will still be exited in the same order as in the spec. let current_epoch = state.current_epoch(); - let is_eligible = |validator: &Validator| { - validator.activation_eligibility_epoch == spec.far_future_epoch - && validator.effective_balance == spec.max_effective_balance - }; let is_exiting_validator = |validator: &Validator| { validator.is_active_at(current_epoch) && validator.effective_balance <= spec.ejection_balance @@ -27,16 +23,18 @@ pub fn process_registry_updates( .validators .iter() .enumerate() - .filter(|(_, validator)| is_eligible(validator) || is_exiting_validator(validator)) + .filter(|(_, validator)| { + validator.is_eligible_for_activation_queue(spec) || is_exiting_validator(validator) + }) .partition_map(|(index, validator)| { - if is_eligible(validator) { + if validator.is_eligible_for_activation_queue(spec) { Either::Left(index) } else { Either::Right(index) } }); for index in eligible_validators { - state.validators[index].activation_eligibility_epoch = current_epoch; + state.validators[index].activation_eligibility_epoch = current_epoch + 1; } for index in exiting_validators { initiate_validator_exit(state, index, spec)?; @@ -47,22 +45,17 @@ pub fn process_registry_updates( .validators .iter() .enumerate() - .filter(|(_, validator)| { - validator.activation_eligibility_epoch != spec.far_future_epoch - && validator.activation_epoch - >= state.compute_activation_exit_epoch(state.finalized_checkpoint.epoch, spec) - }) - .sorted_by_key(|(_, validator)| validator.activation_eligibility_epoch) + .filter(|(_, validator)| validator.is_eligible_for_activation(state, spec)) + .sorted_by_key(|(index, validator)| (validator.activation_eligibility_epoch, *index)) .map(|(index, _)| index) .collect_vec(); + // Dequeue validators for activation up to churn limit let churn_limit = state.get_churn_limit(spec)? as usize; let delayed_activation_epoch = state.compute_activation_exit_epoch(current_epoch, spec); for index in activation_queue.into_iter().take(churn_limit) { let validator = &mut state.validators[index]; - if validator.activation_epoch == spec.far_future_epoch { - validator.activation_epoch = delayed_activation_epoch; - } + validator.activation_epoch = delayed_activation_epoch; } Ok(()) diff --git a/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs b/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs index 285fc94cd..cbcf42fe9 100644 --- a/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs +++ b/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs @@ -144,7 +144,7 @@ impl ValidatorStatuses { /// - Active validators /// - Total balances for the current and previous epochs. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn new( state: &BeaconState, spec: &ChainSpec, @@ -184,7 +184,7 @@ impl ValidatorStatuses { /// Process some attestations from the given `state` updating the `statuses` and /// `total_balances` fields. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn process_attestations( &mut self, state: &BeaconState, @@ -263,7 +263,7 @@ impl ValidatorStatuses { /// Returns `true` if the attestation's FFG target is equal to the hash of the `state`'s first /// beacon block in the given `epoch`. /// -/// Spec v0.9.1 +/// Spec v0.10.1 fn target_matches_epoch_start_block( a: &PendingAttestation, state: &BeaconState, @@ -278,7 +278,7 @@ fn target_matches_epoch_start_block( /// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for /// the current slot of the `PendingAttestation`. /// -/// Spec v0.9.1 +/// Spec v0.10.1 fn has_common_beacon_block_root( a: &PendingAttestation, state: &BeaconState, diff --git a/eth2/state_processing/src/per_slot_processing.rs b/eth2/state_processing/src/per_slot_processing.rs index 3748824f5..42f3ae0cc 100644 --- a/eth2/state_processing/src/per_slot_processing.rs +++ b/eth2/state_processing/src/per_slot_processing.rs @@ -13,7 +13,7 @@ pub enum Error { /// `state_root` is `None`, the root of `state` will be computed using a cached tree hash. /// Providing the `state_root` makes this function several orders of magniude faster. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn per_slot_processing( state: &mut BeaconState, state_root: Option, diff --git a/eth2/state_processing/src/test_utils.rs b/eth2/state_processing/src/test_utils.rs index 803830062..438a10b44 100644 --- a/eth2/state_processing/src/test_utils.rs +++ b/eth2/state_processing/src/test_utils.rs @@ -52,7 +52,7 @@ impl BlockBuilder { self.state_builder.build_caches(&spec).unwrap(); } - pub fn build(mut self, spec: &ChainSpec) -> (BeaconBlock, BeaconState) { + pub fn build(mut self, spec: &ChainSpec) -> (SignedBeaconBlock, BeaconState) { let (mut state, keypairs) = self.state_builder.build(); let builder = &mut self.block_builder; diff --git a/eth2/state_processing/tests/tests.rs b/eth2/state_processing/tests/tests.rs index 83f8cdd16..db4f7cde0 100644 --- a/eth2/state_processing/tests/tests.rs +++ b/eth2/state_processing/tests/tests.rs @@ -4,13 +4,13 @@ use state_processing::{ per_block_processing, test_utils::BlockBuilder, BlockProcessingError, BlockSignatureStrategy, }; use types::{ - AggregateSignature, BeaconBlock, BeaconState, ChainSpec, EthSpec, Keypair, MinimalEthSpec, - Signature, Slot, + AggregateSignature, BeaconState, ChainSpec, EthSpec, Keypair, MinimalEthSpec, Signature, + SignedBeaconBlock, Slot, }; const VALIDATOR_COUNT: usize = 64; -fn get_block(mut mutate_builder: F) -> (BeaconBlock, BeaconState) +fn get_block(mut mutate_builder: F) -> (SignedBeaconBlock, BeaconState) where T: EthSpec, F: FnMut(&mut BlockBuilder), @@ -27,7 +27,7 @@ fn test_scenario(mutate_builder: F, mut invalidate_block: G, s where T: EthSpec, F: FnMut(&mut BlockBuilder), - G: FnMut(&mut BeaconBlock), + G: FnMut(&mut SignedBeaconBlock), { let (mut block, mut state) = get_block::(mutate_builder); @@ -100,7 +100,7 @@ fn agg_sig() -> AggregateSignature { // TODO: use lazy static fn sig() -> Signature { let keypair = Keypair::random(); - Signature::new(&[42, 42], 12, &keypair.sk) + Signature::new(&[42, 42], &keypair.sk) } type TestEthSpec = MinimalEthSpec; @@ -119,7 +119,11 @@ mod signatures_minimal { fn randao() { let spec = &TestEthSpec::default_spec(); - test_scenario::(|_| {}, |block| block.body.randao_reveal = sig(), spec); + test_scenario::( + |_| {}, + |block| block.message.body.randao_reveal = sig(), + spec, + ); } #[test] @@ -130,14 +134,22 @@ mod signatures_minimal { |mut builder| { builder.num_proposer_slashings = 1; }, - |block| block.body.proposer_slashings[0].header_1.signature = sig(), + |block| { + block.message.body.proposer_slashings[0] + .signed_header_1 + .signature = sig() + }, spec, ); test_scenario::( |mut builder| { builder.num_proposer_slashings = 1; }, - |block| block.body.proposer_slashings[0].header_2.signature = sig(), + |block| { + block.message.body.proposer_slashings[0] + .signed_header_2 + .signature = sig() + }, spec, ); } @@ -150,14 +162,22 @@ mod signatures_minimal { |mut builder| { builder.num_attester_slashings = 1; }, - |block| block.body.attester_slashings[0].attestation_1.signature = agg_sig(), + |block| { + block.message.body.attester_slashings[0] + .attestation_1 + .signature = agg_sig() + }, spec, ); test_scenario::( |mut builder| { builder.num_attester_slashings = 1; }, - |block| block.body.attester_slashings[0].attestation_2.signature = agg_sig(), + |block| { + block.message.body.attester_slashings[0] + .attestation_2 + .signature = agg_sig() + }, spec, ); } @@ -170,7 +190,7 @@ mod signatures_minimal { |mut builder| { builder.num_attestations = 1; }, - |block| block.body.attestations[0].signature = agg_sig(), + |block| block.message.body.attestations[0].signature = agg_sig(), spec, ); } @@ -185,7 +205,7 @@ mod signatures_minimal { |mut builder| { builder.num_deposits = 1; }, - |block| block.body.deposits[0].data.signature = sig().into(), + |block| block.message.body.deposits[0].data.signature = sig().into(), spec, ); } @@ -201,7 +221,7 @@ mod signatures_minimal { |mut builder| { builder.num_exits = 1; }, - |block| block.body.voluntary_exits[0].signature = sig(), + |block| block.message.body.voluntary_exits[0].signature = sig(), spec, ); } diff --git a/eth2/types/src/attestation.rs b/eth2/types/src/attestation.rs index 66306ba18..40b479853 100644 --- a/eth2/types/src/attestation.rs +++ b/eth2/types/src/attestation.rs @@ -1,14 +1,13 @@ use super::{ AggregateSignature, AttestationData, BitList, ChainSpec, Domain, EthSpec, Fork, SecretKey, - Signature, + Signature, SignedRoot, }; use crate::test_utils::TestRandom; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; -use tree_hash::TreeHash; -use tree_hash_derive::{SignedRoot, TreeHash}; +use tree_hash_derive::TreeHash; #[derive(Debug, PartialEq)] pub enum Error { @@ -18,24 +17,12 @@ pub enum Error { /// Details an attestation that can be slashable. /// -/// Spec v0.9.1 -#[derive( - Debug, - Clone, - PartialEq, - Serialize, - Deserialize, - Encode, - Decode, - TreeHash, - TestRandom, - SignedRoot, -)] +/// Spec v0.10.1 +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[serde(bound = "T: EthSpec")] pub struct Attestation { pub aggregation_bits: BitList, pub data: AttestationData, - #[signed_root(skip_hashing)] pub signature: AggregateSignature, } @@ -79,11 +66,11 @@ impl Attestation { .set(committee_position, true) .map_err(Error::SszTypesError)?; - let message = self.data.tree_hash_root(); let domain = spec.get_domain(self.data.target.epoch, Domain::BeaconAttester, fork); + let message = self.data.signing_root(domain); self.signature - .add(&Signature::new(&message, domain, secret_key)); + .add(&Signature::new(message.as_bytes(), secret_key)); Ok(()) } @@ -95,5 +82,5 @@ mod tests { use super::*; use crate::*; - ssz_tests!(Attestation); + ssz_and_tree_hash_tests!(Attestation); } diff --git a/eth2/types/src/attestation_data.rs b/eth2/types/src/attestation_data.rs index e768950f0..aab230b1e 100644 --- a/eth2/types/src/attestation_data.rs +++ b/eth2/types/src/attestation_data.rs @@ -1,5 +1,5 @@ use crate::test_utils::TestRandom; -use crate::{Checkpoint, Hash256, Slot}; +use crate::{Checkpoint, Hash256, SignedRoot, Slot}; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; @@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash; /// The data upon which an attestation is based. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive( Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Encode, Decode, TreeHash, TestRandom, )] @@ -24,9 +24,11 @@ pub struct AttestationData { pub target: Checkpoint, } +impl SignedRoot for AttestationData {} + #[cfg(test)] mod tests { use super::*; - ssz_tests!(AttestationData); + ssz_and_tree_hash_tests!(AttestationData); } diff --git a/eth2/types/src/attester_slashing.rs b/eth2/types/src/attester_slashing.rs index 7284a453d..b5466bef8 100644 --- a/eth2/types/src/attester_slashing.rs +++ b/eth2/types/src/attester_slashing.rs @@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash; /// Two conflicting attestations. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[serde(bound = "T: EthSpec")] pub struct AttesterSlashing { @@ -20,5 +20,5 @@ mod tests { use super::*; use crate::*; - ssz_tests!(AttesterSlashing); + ssz_and_tree_hash_tests!(AttesterSlashing); } diff --git a/eth2/types/src/beacon_block.rs b/eth2/types/src/beacon_block.rs index 1b5757e94..629d98e6b 100644 --- a/eth2/types/src/beacon_block.rs +++ b/eth2/types/src/beacon_block.rs @@ -5,38 +5,27 @@ use bls::Signature; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; -use tree_hash::{SignedRoot, TreeHash}; -use tree_hash_derive::{SignedRoot, TreeHash}; +use tree_hash::TreeHash; +use tree_hash_derive::TreeHash; /// A block of the `BeaconChain`. /// -/// Spec v0.9.1 -#[derive( - Debug, - PartialEq, - Clone, - Serialize, - Deserialize, - Encode, - Decode, - TreeHash, - TestRandom, - SignedRoot, -)] +/// Spec v0.10.1 +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[serde(bound = "T: EthSpec")] pub struct BeaconBlock { pub slot: Slot, pub parent_root: Hash256, pub state_root: Hash256, pub body: BeaconBlockBody, - #[signed_root(skip_hashing)] - pub signature: Signature, } +impl SignedRoot for BeaconBlock {} + impl BeaconBlock { /// Returns an empty block to be used during genesis. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn empty(spec: &ChainSpec) -> Self { BeaconBlock { slot: spec.genesis_slot, @@ -56,7 +45,6 @@ impl BeaconBlock { deposits: VariableList::empty(), voluntary_exits: VariableList::empty(), }, - signature: Signature::empty_signature(), } } @@ -65,11 +53,11 @@ impl BeaconBlock { self.slot.epoch(T::slots_per_epoch()) } - /// Returns the `signed_root` of the block. + /// Returns the `tree_hash_root` of the block. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn canonical_root(&self) -> Hash256 { - Hash256::from_slice(&self.signed_root()[..]) + Hash256::from_slice(&self.tree_hash_root()[..]) } /// Returns a full `BeaconBlockHeader` of this block. @@ -79,33 +67,40 @@ impl BeaconBlock { /// /// Note: performs a full tree-hash of `self.body`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn block_header(&self) -> BeaconBlockHeader { BeaconBlockHeader { slot: self.slot, parent_root: self.parent_root, state_root: self.state_root, body_root: Hash256::from_slice(&self.body.tree_hash_root()[..]), - signature: self.signature.clone(), } } /// Returns a "temporary" header, where the `state_root` is `Hash256::zero()`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn temporary_block_header(&self) -> BeaconBlockHeader { BeaconBlockHeader { state_root: Hash256::zero(), - signature: Signature::empty_signature(), ..self.block_header() } } - /// Signs `self`. - pub fn sign(&mut self, secret_key: &SecretKey, fork: &Fork, spec: &ChainSpec) { - let message = self.signed_root(); - let domain = spec.get_domain(self.epoch(), Domain::BeaconProposer, &fork); - self.signature = Signature::new(&message, domain, &secret_key); + /// Signs `self`, producing a `SignedBeaconBlock`. + pub fn sign( + self, + secret_key: &SecretKey, + fork: &Fork, + spec: &ChainSpec, + ) -> SignedBeaconBlock { + let domain = spec.get_domain(self.epoch(), Domain::BeaconProposer, fork); + let message = self.signing_root(domain); + let signature = Signature::new(message.as_bytes(), secret_key); + SignedBeaconBlock { + message: self, + signature, + } } } @@ -113,5 +108,5 @@ impl BeaconBlock { mod tests { use super::*; - ssz_tests!(BeaconBlock); + ssz_and_tree_hash_tests!(BeaconBlock); } diff --git a/eth2/types/src/beacon_block_body.rs b/eth2/types/src/beacon_block_body.rs index 6319b1460..fcc419b15 100644 --- a/eth2/types/src/beacon_block_body.rs +++ b/eth2/types/src/beacon_block_body.rs @@ -10,7 +10,7 @@ use tree_hash_derive::TreeHash; /// The body of a `BeaconChain` block, containing operations. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[serde(bound = "T: EthSpec")] pub struct BeaconBlockBody { @@ -25,12 +25,12 @@ pub struct BeaconBlockBody { pub attester_slashings: VariableList, T::MaxAttesterSlashings>, pub attestations: VariableList, T::MaxAttestations>, pub deposits: VariableList, - pub voluntary_exits: VariableList, + pub voluntary_exits: VariableList, } #[cfg(test)] mod tests { use super::*; - ssz_tests!(BeaconBlockBody); + ssz_and_tree_hash_tests!(BeaconBlockBody); } diff --git a/eth2/types/src/beacon_block_header.rs b/eth2/types/src/beacon_block_header.rs index 0b61bed50..f93a9fa4e 100644 --- a/eth2/types/src/beacon_block_header.rs +++ b/eth2/types/src/beacon_block_header.rs @@ -1,55 +1,59 @@ use crate::test_utils::TestRandom; use crate::*; -use bls::Signature; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; -use tree_hash::{SignedRoot, TreeHash}; -use tree_hash_derive::{SignedRoot, TreeHash}; +use tree_hash::TreeHash; +use tree_hash_derive::TreeHash; /// A header of a `BeaconBlock`. /// -/// Spec v0.9.1 -#[derive( - Debug, - PartialEq, - Clone, - Serialize, - Deserialize, - Encode, - Decode, - TreeHash, - TestRandom, - SignedRoot, -)] +/// Spec v0.10.1 +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct BeaconBlockHeader { pub slot: Slot, pub parent_root: Hash256, pub state_root: Hash256, pub body_root: Hash256, - #[signed_root(skip_hashing)] - pub signature: Signature, } +impl SignedRoot for BeaconBlockHeader {} + impl BeaconBlockHeader { /// Returns the `tree_hash_root` of the header. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn canonical_root(&self) -> Hash256 { - Hash256::from_slice(&self.signed_root()[..]) + Hash256::from_slice(&self.tree_hash_root()[..]) } /// Given a `body`, consumes `self` and returns a complete `BeaconBlock`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn into_block(self, body: BeaconBlockBody) -> BeaconBlock { BeaconBlock { slot: self.slot, parent_root: self.parent_root, state_root: self.state_root, body, - signature: self.signature, + } + } + + /// Signs `self`, producing a `SignedBeaconBlockHeader`. + pub fn sign( + self, + secret_key: &SecretKey, + fork: &Fork, + spec: &ChainSpec, + ) -> SignedBeaconBlockHeader { + let epoch = self.slot.epoch(E::slots_per_epoch()); + let domain = spec.get_domain(epoch, Domain::BeaconProposer, fork); + let message = self.signing_root(domain); + let signature = Signature::new(message.as_bytes(), secret_key); + SignedBeaconBlockHeader { + message: self, + signature, } } } @@ -58,5 +62,5 @@ impl BeaconBlockHeader { mod tests { use super::*; - ssz_tests!(BeaconBlockHeader); + ssz_and_tree_hash_tests!(BeaconBlockHeader); } diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index b612f4d4a..9edee56bd 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -90,7 +90,7 @@ impl AllowNextEpoch { /// The state of the `BeaconChain` at some slot. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive( Debug, PartialEq, @@ -181,13 +181,17 @@ impl BeaconState { /// /// Not a complete genesis state, see `initialize_beacon_state_from_eth1`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn new(genesis_time: u64, eth1_data: Eth1Data, spec: &ChainSpec) -> Self { BeaconState { // Versioning genesis_time, slot: spec.genesis_slot, - fork: spec.genesis_fork.clone(), + fork: Fork { + previous_version: spec.genesis_fork_version, + current_version: spec.genesis_fork_version, + epoch: T::genesis_epoch(), + }, // History latest_block_header: BeaconBlock::::empty(spec).temporary_block_header(), @@ -234,7 +238,7 @@ impl BeaconState { /// Returns the `tree_hash_root` of the state. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn canonical_root(&self) -> Hash256 { Hash256::from_slice(&self.tree_hash_root()[..]) } @@ -263,7 +267,7 @@ impl BeaconState { /// The epoch corresponding to `self.slot`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn current_epoch(&self) -> Epoch { self.slot.epoch(T::slots_per_epoch()) } @@ -272,7 +276,7 @@ impl BeaconState { /// /// If the current epoch is the genesis epoch, the genesis_epoch is returned. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn previous_epoch(&self) -> Epoch { let current_epoch = self.current_epoch(); if current_epoch > T::genesis_epoch() { @@ -284,7 +288,7 @@ impl BeaconState { /// The epoch following `self.current_epoch()`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn next_epoch(&self) -> Epoch { self.current_epoch() + 1 } @@ -293,7 +297,7 @@ impl BeaconState { /// /// Makes use of the committee cache and will fail if no cache exists for the slot's epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_committee_count_at_slot(&self, slot: Slot) -> Result { let cache = self.committee_cache_at_slot(slot)?; Ok(cache.committees_per_slot() as u64) @@ -301,7 +305,7 @@ impl BeaconState { /// Compute the number of committees in an entire epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_epoch_committee_count(&self, relative_epoch: RelativeEpoch) -> Result { let cache = self.committee_cache(relative_epoch)?; Ok(cache.epoch_committee_count() as u64) @@ -325,7 +329,7 @@ impl BeaconState { /// /// Does not utilize the cache, performs a full iteration over the validator registry. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_active_validator_indices(&self, epoch: Epoch) -> Vec { get_active_validator_indices(&self.validators, epoch) } @@ -345,7 +349,7 @@ impl BeaconState { /// /// Utilises the committee cache. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_beacon_committee( &self, slot: Slot, @@ -364,7 +368,7 @@ impl BeaconState { /// /// Utilises the committee cache. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_beacon_committees_at_slot(&self, slot: Slot) -> Result, Error> { let cache = self.committee_cache_at_slot(slot)?; cache.get_beacon_committees_at_slot(slot) @@ -374,7 +378,7 @@ impl BeaconState { /// /// Utilises the committee cache. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_beacon_committees_at_epoch( &self, relative_epoch: RelativeEpoch, @@ -385,7 +389,7 @@ impl BeaconState { /// Compute the proposer (not necessarily for the Beacon chain) from a list of indices. /// - /// Spec v0.9.1 + /// Spec v0.10.1 // NOTE: be sure to test this bad boy. pub fn compute_proposer_index( &self, @@ -424,7 +428,7 @@ impl BeaconState { /// Returns the beacon proposer index for the `slot` in the given `relative_epoch`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_beacon_proposer_index(&self, slot: Slot, spec: &ChainSpec) -> Result { let epoch = slot.epoch(T::slots_per_epoch()); let seed = self.get_beacon_proposer_seed(slot, spec)?; @@ -435,7 +439,7 @@ impl BeaconState { /// Compute the seed to use for the beacon proposer selection at the given `slot`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn get_beacon_proposer_seed(&self, slot: Slot, spec: &ChainSpec) -> Result, Error> { let epoch = slot.epoch(T::slots_per_epoch()); let mut preimage = self @@ -450,7 +454,7 @@ impl BeaconState { /// /// It needs filling in on all slots where there isn't a skip. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_latest_block_root(&self, current_state_root: Hash256) -> Hash256 { if self.latest_block_header.state_root.is_zero() { let mut latest_block_header = self.latest_block_header.clone(); @@ -463,7 +467,7 @@ impl BeaconState { /// Safely obtains the index for latest block roots, given some `slot`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn get_latest_block_roots_index(&self, slot: Slot) -> Result { if (slot < self.slot) && (self.slot <= slot + self.block_roots.len() as u64) { Ok(slot.as_usize() % self.block_roots.len()) @@ -474,7 +478,7 @@ impl BeaconState { /// Return the block root at a recent `slot`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_block_root(&self, slot: Slot) -> Result<&Hash256, BeaconStateError> { let i = self.get_latest_block_roots_index(slot)?; Ok(&self.block_roots[i]) @@ -482,7 +486,7 @@ impl BeaconState { /// Return the block root at a recent `epoch`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 // NOTE: the spec calls this get_block_root pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result<&Hash256, BeaconStateError> { self.get_block_root(epoch.start_slot(T::slots_per_epoch())) @@ -490,7 +494,7 @@ impl BeaconState { /// Sets the block root for some given slot. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn set_block_root( &mut self, slot: Slot, @@ -508,7 +512,7 @@ impl BeaconState { /// Safely obtains the index for `randao_mixes` /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn get_randao_mix_index( &self, epoch: Epoch, @@ -530,7 +534,7 @@ impl BeaconState { /// /// See `Self::get_randao_mix`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn update_randao_mix(&mut self, epoch: Epoch, signature: &Signature) -> Result<(), Error> { let i = epoch.as_usize() % T::EpochsPerHistoricalVector::to_usize(); @@ -543,7 +547,7 @@ impl BeaconState { /// Return the randao mix at a recent ``epoch``. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, Error> { let i = self.get_randao_mix_index(epoch, AllowNextEpoch::False)?; Ok(&self.randao_mixes[i]) @@ -551,7 +555,7 @@ impl BeaconState { /// Set the randao mix at a recent ``epoch``. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), Error> { let i = self.get_randao_mix_index(epoch, AllowNextEpoch::True)?; self.randao_mixes[i] = mix; @@ -560,7 +564,7 @@ impl BeaconState { /// Safely obtains the index for latest state roots, given some `slot`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn get_latest_state_roots_index(&self, slot: Slot) -> Result { if (slot < self.slot) && (self.slot <= slot + Slot::from(self.state_roots.len())) { Ok(slot.as_usize() % self.state_roots.len()) @@ -571,7 +575,7 @@ impl BeaconState { /// Gets the state root for some slot. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_state_root(&self, slot: Slot) -> Result<&Hash256, Error> { let i = self.get_latest_state_roots_index(slot)?; Ok(&self.state_roots[i]) @@ -579,7 +583,7 @@ impl BeaconState { /// Gets the oldest (earliest slot) state root. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_oldest_state_root(&self) -> Result<&Hash256, Error> { let i = self.get_latest_state_roots_index(self.slot - Slot::from(self.state_roots.len()))?; @@ -588,7 +592,7 @@ impl BeaconState { /// Gets the oldest (earliest slot) block root. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_oldest_block_root(&self) -> Result<&Hash256, Error> { let i = self.get_latest_block_roots_index(self.slot - self.block_roots.len() as u64)?; Ok(&self.block_roots[i]) @@ -596,7 +600,7 @@ impl BeaconState { /// Sets the latest state root for slot. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn set_state_root(&mut self, slot: Slot, state_root: Hash256) -> Result<(), Error> { let i = self.get_latest_state_roots_index(slot)?; self.state_roots[i] = state_root; @@ -605,7 +609,7 @@ impl BeaconState { /// Safely obtain the index for `slashings`, given some `epoch`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn get_slashings_index( &self, epoch: Epoch, @@ -625,14 +629,14 @@ impl BeaconState { /// Get a reference to the entire `slashings` vector. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_all_slashings(&self) -> &[u64] { &self.slashings } /// Get the total slashed balances for some epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_slashings(&self, epoch: Epoch) -> Result { let i = self.get_slashings_index(epoch, AllowNextEpoch::False)?; Ok(self.slashings[i]) @@ -640,7 +644,7 @@ impl BeaconState { /// Set the total slashed balances for some epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn set_slashings(&mut self, epoch: Epoch, value: u64) -> Result<(), Error> { let i = self.get_slashings_index(epoch, AllowNextEpoch::True)?; self.slashings[i] = value; @@ -649,7 +653,7 @@ impl BeaconState { /// Get the attestations from the current or previous epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_matching_source_attestations( &self, epoch: Epoch, @@ -665,7 +669,7 @@ impl BeaconState { /// Generate a seed for the given `epoch`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_seed( &self, epoch: Epoch, @@ -696,7 +700,7 @@ impl BeaconState { /// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_effective_balance( &self, validator_index: usize, @@ -710,7 +714,7 @@ impl BeaconState { /// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn compute_activation_exit_epoch(&self, epoch: Epoch, spec: &ChainSpec) -> Epoch { epoch + 1 + spec.max_seed_lookahead } @@ -719,7 +723,7 @@ impl BeaconState { /// /// Uses the epoch cache, and will error if it isn't initialized. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result { Ok(std::cmp::max( spec.min_per_epoch_churn_limit, @@ -734,7 +738,7 @@ impl BeaconState { /// /// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_attestation_duties( &self, validator_index: usize, @@ -747,7 +751,7 @@ impl BeaconState { /// Return the combined effective balance of an array of validators. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_total_balance( &self, validator_indices: &[usize], diff --git a/eth2/types/src/beacon_state/committee_cache.rs b/eth2/types/src/beacon_state/committee_cache.rs index 78b2af3fc..099defd7c 100644 --- a/eth2/types/src/beacon_state/committee_cache.rs +++ b/eth2/types/src/beacon_state/committee_cache.rs @@ -22,7 +22,7 @@ pub struct CommitteeCache { impl CommitteeCache { /// Return a new, fully initialized cache. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn initialized( state: &BeaconState, epoch: Epoch, @@ -87,7 +87,7 @@ impl CommitteeCache { /// /// Always returns `&[]` for a non-initialized epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn active_validator_indices(&self) -> &[usize] { &self.shuffling } @@ -96,7 +96,7 @@ impl CommitteeCache { /// /// Always returns `&[]` for a non-initialized epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn shuffling(&self) -> &[usize] { &self.shuffling } @@ -202,7 +202,7 @@ impl CommitteeCache { /// /// Always returns `usize::default()` for a non-initialized epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn active_validator_count(&self) -> usize { self.shuffling.len() } @@ -211,7 +211,7 @@ impl CommitteeCache { /// /// Always returns `usize::default()` for a non-initialized epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn epoch_committee_count(&self) -> usize { self.committees_per_slot as usize * self.slots_per_epoch as usize } @@ -223,7 +223,7 @@ impl CommitteeCache { /// Returns a slice of `self.shuffling` that represents the `index`'th committee in the epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn compute_committee(&self, index: usize) -> Option<&[usize]> { Some(&self.shuffling[self.compute_committee_range(index)?]) } @@ -234,7 +234,7 @@ impl CommitteeCache { /// /// Will also return `None` if the index is out of bounds. /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn compute_committee_range(&self, index: usize) -> Option> { let count = self.epoch_committee_count(); if count == 0 || index >= count { @@ -261,7 +261,7 @@ impl CommitteeCache { /// Returns a list of all `validators` indices where the validator is active at the given /// `epoch`. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> Vec { let mut active = Vec::with_capacity(validators.len()); diff --git a/eth2/types/src/beacon_state/tests.rs b/eth2/types/src/beacon_state/tests.rs index e5d85c25b..4e12e5dfd 100644 --- a/eth2/types/src/beacon_state/tests.rs +++ b/eth2/types/src/beacon_state/tests.rs @@ -2,7 +2,7 @@ use super::*; use crate::test_utils::*; -ssz_tests!(FoundationBeaconState); +ssz_and_tree_hash_tests!(FoundationBeaconState); fn test_beacon_proposer_index() { let spec = T::default_spec(); diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 0b1e9588d..fe3990e09 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -1,11 +1,16 @@ use crate::*; use int_to_bytes::int_to_bytes4; use serde_derive::{Deserialize, Serialize}; -use utils::{u32_from_hex_str, u32_to_hex_str, u8_from_hex_str, u8_to_hex_str}; +use std::fs::File; +use std::path::Path; +use utils::{ + fork_from_hex_str, fork_to_hex_str, u32_from_hex_str, u32_to_hex_str, u8_from_hex_str, + u8_to_hex_str, +}; /// Each of the BLS signature domains. /// -/// Spec v0.9.1 +/// Spec v0.10.1 pub enum Domain { BeaconProposer, BeaconAttester, @@ -16,18 +21,18 @@ pub enum Domain { /// Holds all the "constants" for a BeaconChain. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] #[serde(default)] pub struct ChainSpec { /* * Constants */ + pub genesis_slot: Slot, #[serde(skip_serializing)] // skipped because Serde TOML has trouble with u64::max pub far_future_epoch: Epoch, pub base_rewards_per_epoch: u64, pub deposit_contract_tree_depth: u64, - pub seconds_per_day: u64, /* * Misc @@ -51,20 +56,25 @@ pub struct ChainSpec { /* * Initial Values */ - pub genesis_slot: Slot, + #[serde( + serialize_with = "fork_to_hex_str", + deserialize_with = "fork_from_hex_str" + )] + pub genesis_fork_version: [u8; 4], #[serde(deserialize_with = "u8_from_hex_str", serialize_with = "u8_to_hex_str")] pub bls_withdrawal_prefix_byte: u8, /* * Time parameters */ + pub min_genesis_delay: u64, pub milliseconds_per_slot: u64, pub min_attestation_inclusion_delay: u64, pub min_seed_lookahead: Epoch, pub max_seed_lookahead: Epoch, + pub min_epochs_to_inactivity_penalty: u64, pub min_validator_withdrawability_delay: Epoch, pub persistent_committee_period: u64, - pub min_epochs_to_inactivity_penalty: u64, /* * Reward and penalty quotients @@ -93,17 +103,16 @@ pub struct ChainSpec { * Eth1 */ pub eth1_follow_distance: u64, + pub seconds_per_eth1_block: u64, pub boot_nodes: Vec, pub network_id: u8, - - pub genesis_fork: Fork, } impl ChainSpec { /// Get the domain number, unmodified by the fork. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_domain_constant(&self, domain: Domain) -> u32 { match domain { Domain::BeaconProposer => self.domain_beacon_proposer, @@ -116,28 +125,30 @@ impl ChainSpec { /// Get the domain number that represents the fork meta and signature domain. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_domain(&self, epoch: Epoch, domain: Domain, fork: &Fork) -> u64 { - let domain_constant = self.get_domain_constant(domain); - - let mut bytes: Vec = int_to_bytes4(domain_constant); - bytes.append(&mut fork.get_fork_version(epoch).to_vec()); - - let mut fork_and_domain = [0; 8]; - fork_and_domain.copy_from_slice(&bytes); - - u64::from_le_bytes(fork_and_domain) + let fork_version = fork.get_fork_version(epoch); + self.compute_domain(domain, fork_version) } /// Get the domain for a deposit signature. /// /// Deposits are valid across forks, thus the deposit domain is computed - /// with the fork zeroed. + /// with the genesis fork version. /// - /// Spec v0.8.1 + /// Spec v0.10.1 pub fn get_deposit_domain(&self) -> u64 { - let mut bytes: Vec = int_to_bytes4(self.domain_deposit); - bytes.append(&mut vec![0; 4]); + self.compute_domain(Domain::Deposit, self.genesis_fork_version) + } + + /// Compute a domain by applying the given `fork_version`. + /// + /// Spec v0.10.1 + pub fn compute_domain(&self, domain: Domain, fork_version: [u8; 4]) -> u64 { + let domain_constant = self.get_domain_constant(domain); + + let mut bytes: Vec = int_to_bytes4(domain_constant); + bytes.append(&mut fork_version.to_vec()); let mut fork_and_domain = [0; 8]; fork_and_domain.copy_from_slice(&bytes); @@ -147,16 +158,16 @@ impl ChainSpec { /// Returns a `ChainSpec` compatible with the Ethereum Foundation specification. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn mainnet() -> Self { Self { /* * Constants */ + genesis_slot: Slot::new(0), far_future_epoch: Epoch::new(u64::max_value()), base_rewards_per_epoch: 4, deposit_contract_tree_depth: 32, - seconds_per_day: 86400, /* * Misc @@ -166,7 +177,7 @@ impl ChainSpec { min_per_epoch_churn_limit: 4, churn_limit_quotient: 65_536, shuffle_round_count: 90, - min_genesis_active_validator_count: 65_536, + min_genesis_active_validator_count: 16_384, min_genesis_time: 1_578_009_600, // Jan 3, 2020 /* @@ -180,19 +191,20 @@ impl ChainSpec { /* * Initial Values */ - genesis_slot: Slot::new(0), + genesis_fork_version: [0; 4], bls_withdrawal_prefix_byte: 0, /* * Time parameters */ + min_genesis_delay: 86400, // 1 day milliseconds_per_slot: 12_000, min_attestation_inclusion_delay: 1, min_seed_lookahead: Epoch::new(1), max_seed_lookahead: Epoch::new(4), + min_epochs_to_inactivity_penalty: 4, min_validator_withdrawability_delay: Epoch::new(256), persistent_committee_period: 2_048, - min_epochs_to_inactivity_penalty: 4, /* * Reward and penalty quotients @@ -221,15 +233,7 @@ impl ChainSpec { * Eth1 */ eth1_follow_distance: 1_024, - - /* - * Fork - */ - genesis_fork: Fork { - previous_version: [0; 4], - current_version: [0; 4], - epoch: Epoch::new(0), - }, + seconds_per_eth1_block: 14, /* * Network specific @@ -239,11 +243,9 @@ impl ChainSpec { } } - /// Ethereum Foundation minimal spec, as defined here: + /// Ethereum Foundation minimal spec, as defined in the eth2.0-specs repo. /// - /// https://github.com/ethereum/eth2.0-specs/blob/v0.9.1/configs/minimal.yaml - /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn minimal() -> Self { // Note: bootnodes to be updated when static nodes exist. let boot_nodes = vec![]; @@ -253,10 +255,12 @@ impl ChainSpec { target_committee_size: 4, shuffle_round_count: 10, min_genesis_active_validator_count: 64, + eth1_follow_distance: 16, + genesis_fork_version: [0x00, 0x00, 0x00, 0x01], + min_genesis_delay: 300, + milliseconds_per_slot: 6_000, network_id: 2, // lighthouse testnet network id boot_nodes, - eth1_follow_distance: 16, - milliseconds_per_slot: 6_000, ..ChainSpec::mainnet() } } @@ -295,7 +299,11 @@ mod tests { } fn test_domain(domain_type: Domain, raw_domain: u32, spec: &ChainSpec) { - let fork = &spec.genesis_fork; + let fork = Fork { + previous_version: spec.genesis_fork_version, + current_version: spec.genesis_fork_version, + epoch: MinimalEthSpec::genesis_epoch(), + }; let epoch = Epoch::new(0); let domain = spec.get_domain(epoch, domain_type, &fork); @@ -318,19 +326,20 @@ mod tests { } } -// Yaml Config is declared here in order to access domain fields of ChainSpec which are private fields. +/// Union of a ChainSpec struct and an EthSpec struct that holds constants used for the configs +/// from the Ethereum 2 specs repo (https://github.com/ethereum/eth2.0-specs/tree/dev/configs) +/// +/// Spec v0.10.1 +// Yaml Config is declared here in order to access domain fields of ChainSpec which are private. #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] #[serde(rename_all = "UPPERCASE")] #[serde(default)] #[serde(deny_unknown_fields)] -/// Union of a ChainSpec struct and an EthSpec struct that holds constants used for the configs folder of the Ethereum 2 spec (https://github.com/ethereum/eth2.0-specs/tree/dev/configs) -/// Spec v0.9.1 pub struct YamlConfig { // ChainSpec far_future_epoch: u64, base_rewards_per_epoch: u64, deposit_contract_tree_depth: u64, - seconds_per_day: u64, max_committees_per_slot: usize, target_committee_size: usize, min_per_epoch_churn_limit: u64, @@ -338,20 +347,26 @@ pub struct YamlConfig { shuffle_round_count: u8, min_genesis_active_validator_count: u64, min_genesis_time: u64, + min_genesis_delay: u64, min_deposit_amount: u64, max_effective_balance: u64, ejection_balance: u64, effective_balance_increment: u64, genesis_slot: u64, + #[serde( + serialize_with = "fork_to_hex_str", + deserialize_with = "fork_from_hex_str" + )] + genesis_fork_version: [u8; 4], #[serde(deserialize_with = "u8_from_hex_str", serialize_with = "u8_to_hex_str")] bls_withdrawal_prefix: u8, seconds_per_slot: u64, min_attestation_inclusion_delay: u64, min_seed_lookahead: u64, max_seed_lookahead: u64, + min_epochs_to_inactivity_penalty: u64, min_validator_withdrawability_delay: u64, persistent_committee_period: u64, - min_epochs_to_inactivity_penalty: u64, base_reward_factor: u64, whistleblower_reward_quotient: u64, proposer_reward_quotient: u64, @@ -359,9 +374,6 @@ pub struct YamlConfig { min_slashing_penalty_quotient: u64, safe_slots_to_update_justified: u64, - #[serde(skip_serializing)] - genesis_fork: Fork, - #[serde( deserialize_with = "u32_from_hex_str", serialize_with = "u32_to_hex_str" @@ -408,12 +420,14 @@ pub struct YamlConfig { max_deposits: u32, max_voluntary_exits: u32, - // Eth1 + // Validator eth1_follow_distance: u64, + target_aggregators_per_committee: u64, + random_subnets_per_validator: u64, + epochs_per_random_subnet_subscription: u64, + seconds_per_eth1_block: u64, - // Unused - #[serde(skip_serializing)] - early_derived_secret_penalty_max_future_epochs: u32, + // Deposit Contract (unused) #[serde(skip_serializing)] deposit_contract_address: String, @@ -438,6 +452,8 @@ pub struct YamlConfig { domain_shard_attester: u32, #[serde(skip_serializing)] max_epochs_per_crosslink: u64, + #[serde(skip_serializing)] + early_derived_secret_penalty_max_future_epochs: u32, } impl Default for YamlConfig { @@ -447,7 +463,7 @@ impl Default for YamlConfig { } } -/// Spec v0.8.1 +/// Spec v0.10.1 impl YamlConfig { pub fn from_spec(spec: &ChainSpec) -> Self { Self { @@ -455,7 +471,6 @@ impl YamlConfig { far_future_epoch: spec.far_future_epoch.into(), base_rewards_per_epoch: spec.base_rewards_per_epoch, deposit_contract_tree_depth: spec.deposit_contract_tree_depth, - seconds_per_day: spec.seconds_per_day, max_committees_per_slot: spec.max_committees_per_slot, target_committee_size: spec.target_committee_size, min_per_epoch_churn_limit: spec.min_per_epoch_churn_limit, @@ -463,6 +478,7 @@ impl YamlConfig { shuffle_round_count: spec.shuffle_round_count, min_genesis_active_validator_count: spec.min_genesis_active_validator_count, min_genesis_time: spec.min_genesis_time, + min_genesis_delay: spec.min_genesis_delay, min_deposit_amount: spec.min_deposit_amount, max_effective_balance: spec.max_effective_balance, ejection_balance: spec.ejection_balance, @@ -481,7 +497,7 @@ impl YamlConfig { proposer_reward_quotient: spec.proposer_reward_quotient, inactivity_penalty_quotient: spec.inactivity_penalty_quotient, min_slashing_penalty_quotient: spec.min_slashing_penalty_quotient, - genesis_fork: spec.genesis_fork.clone(), + genesis_fork_version: spec.genesis_fork_version.clone(), safe_slots_to_update_justified: spec.safe_slots_to_update_justified, domain_beacon_proposer: spec.domain_beacon_proposer, domain_beacon_attester: spec.domain_beacon_attester, @@ -506,11 +522,14 @@ impl YamlConfig { max_deposits: T::MaxDeposits::to_u32(), max_voluntary_exits: T::MaxVoluntaryExits::to_u32(), - // Eth1 + // Validator eth1_follow_distance: spec.eth1_follow_distance, + target_aggregators_per_committee: 0, + random_subnets_per_validator: 0, + epochs_per_random_subnet_subscription: 0, + seconds_per_eth1_block: spec.seconds_per_eth1_block, - // Unused - early_derived_secret_penalty_max_future_epochs: 0, + // Deposit Contract (unused) deposit_contract_address: String::new(), // Phase 1 @@ -524,9 +543,17 @@ impl YamlConfig { domain_shard_proposer: 0, domain_shard_attester: 0, max_epochs_per_crosslink: 0, + early_derived_secret_penalty_max_future_epochs: 0, } } + pub fn from_file(filename: &Path) -> Result { + let f = File::open(filename) + .map_err(|e| format!("Error opening spec at {}: {:?}", filename.display(), e))?; + serde_yaml::from_reader(f) + .map_err(|e| format!("Error parsing spec at {}: {:?}", filename.display(), e)) + } + pub fn apply_to_chain_spec(&self, chain_spec: &ChainSpec) -> Option { // Checking for EthSpec constants if self.justification_bits_length != T::JustificationBitsLength::to_u32() @@ -553,7 +580,6 @@ impl YamlConfig { far_future_epoch: Epoch::from(self.far_future_epoch), base_rewards_per_epoch: self.base_rewards_per_epoch, deposit_contract_tree_depth: self.deposit_contract_tree_depth, - seconds_per_day: self.seconds_per_day, target_committee_size: self.target_committee_size, min_per_epoch_churn_limit: self.min_per_epoch_churn_limit, churn_limit_quotient: self.churn_limit_quotient, @@ -561,6 +587,7 @@ impl YamlConfig { min_genesis_active_validator_count: self.min_genesis_active_validator_count, min_genesis_time: self.min_genesis_time, min_deposit_amount: self.min_deposit_amount, + min_genesis_delay: self.min_genesis_delay, max_effective_balance: self.max_effective_balance, ejection_balance: self.ejection_balance, effective_balance_increment: self.effective_balance_increment, @@ -585,7 +612,7 @@ impl YamlConfig { domain_deposit: self.domain_deposit, domain_voluntary_exit: self.domain_voluntary_exit, boot_nodes: chain_spec.boot_nodes.clone(), - genesis_fork: chain_spec.genesis_fork.clone(), + genesis_fork_version: chain_spec.genesis_fork_version.clone(), eth1_follow_distance: self.eth1_follow_distance, ..*chain_spec }) diff --git a/eth2/types/src/checkpoint.rs b/eth2/types/src/checkpoint.rs index d1fa49bec..a1cf187e7 100644 --- a/eth2/types/src/checkpoint.rs +++ b/eth2/types/src/checkpoint.rs @@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash; /// Casper FFG checkpoint, used in attestations. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive( Debug, Clone, @@ -31,5 +31,5 @@ pub struct Checkpoint { mod tests { use super::*; - ssz_tests!(Checkpoint); + ssz_and_tree_hash_tests!(Checkpoint); } diff --git a/eth2/types/src/deposit.rs b/eth2/types/src/deposit.rs index f60932f9c..d6781559e 100644 --- a/eth2/types/src/deposit.rs +++ b/eth2/types/src/deposit.rs @@ -11,7 +11,7 @@ pub const DEPOSIT_TREE_DEPTH: usize = 32; /// A deposit to potentially become a beacon chain validator. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct Deposit { pub proof: FixedVector, @@ -22,5 +22,5 @@ pub struct Deposit { mod tests { use super::*; - ssz_tests!(Deposit); + ssz_and_tree_hash_tests!(Deposit); } diff --git a/eth2/types/src/deposit_data.rs b/eth2/types/src/deposit_data.rs index a6a1a437f..a9607513e 100644 --- a/eth2/types/src/deposit_data.rs +++ b/eth2/types/src/deposit_data.rs @@ -1,46 +1,43 @@ use crate::test_utils::TestRandom; use crate::*; use bls::{PublicKeyBytes, SignatureBytes}; -use std::convert::From; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; -use tree_hash::{SignedRoot, TreeHash}; -use tree_hash_derive::{SignedRoot, TreeHash}; +use tree_hash_derive::TreeHash; /// The data supplied by the user to the deposit contract. /// -/// Spec v0.9.1 -#[derive( - Debug, - PartialEq, - Clone, - Serialize, - Deserialize, - Encode, - Decode, - SignedRoot, - TreeHash, - TestRandom, -)] +/// Spec v0.10.1 +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct DepositData { pub pubkey: PublicKeyBytes, pub withdrawal_credentials: Hash256, pub amount: u64, - #[signed_root(skip_hashing)] pub signature: SignatureBytes, } impl DepositData { + /// Create a `DepositMessage` corresponding to this `DepositData`, for signature verification. + /// + /// Spec v0.10.1 + pub fn as_deposit_message(&self) -> DepositMessage { + DepositMessage { + pubkey: self.pubkey.clone(), + withdrawal_credentials: self.withdrawal_credentials, + amount: self.amount, + } + } + /// Generate the signature for a given DepositData details. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn create_signature(&self, secret_key: &SecretKey, spec: &ChainSpec) -> SignatureBytes { - let msg = self.signed_root(); let domain = spec.get_deposit_domain(); + let msg = self.as_deposit_message().signing_root(domain); - SignatureBytes::from(Signature::new(msg.as_slice(), domain, secret_key)) + SignatureBytes::from(Signature::new(msg.as_bytes(), secret_key)) } } @@ -48,5 +45,5 @@ impl DepositData { mod tests { use super::*; - ssz_tests!(DepositData); + ssz_and_tree_hash_tests!(DepositData); } diff --git a/eth2/types/src/deposit_message.rs b/eth2/types/src/deposit_message.rs new file mode 100644 index 000000000..9a25c1651 --- /dev/null +++ b/eth2/types/src/deposit_message.rs @@ -0,0 +1,27 @@ +use crate::test_utils::TestRandom; +use crate::*; +use bls::PublicKeyBytes; + +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +/// The data supplied by the user to the deposit contract. +/// +/// Spec v0.10.1 +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] +pub struct DepositMessage { + pub pubkey: PublicKeyBytes, + pub withdrawal_credentials: Hash256, + pub amount: u64, +} + +impl SignedRoot for DepositMessage {} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(DepositMessage); +} diff --git a/eth2/types/src/eth1_data.rs b/eth2/types/src/eth1_data.rs index dfca3ec37..9c1215a43 100644 --- a/eth2/types/src/eth1_data.rs +++ b/eth2/types/src/eth1_data.rs @@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash; /// Contains data obtained from the Eth1 chain. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive( Debug, PartialEq, @@ -33,5 +33,5 @@ pub struct Eth1Data { mod tests { use super::*; - ssz_tests!(Eth1Data); + ssz_and_tree_hash_tests!(Eth1Data); } diff --git a/eth2/types/src/eth_spec.rs b/eth2/types/src/eth_spec.rs index 5377254af..5a86d390d 100644 --- a/eth2/types/src/eth_spec.rs +++ b/eth2/types/src/eth_spec.rs @@ -10,15 +10,12 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq { /* * Constants */ + type GenesisEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq; type JustificationBitsLength: Unsigned + Clone + Sync + Send + Debug + PartialEq + Default; /* * Misc */ type MaxValidatorsPerCommittee: Unsigned + Clone + Sync + Send + Debug + PartialEq; - /* - * Initial values - */ - type GenesisEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq; /* * Time parameters */ @@ -61,7 +58,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq { /// Note: the number of committees per slot is constant in each epoch, and depends only on /// the `active_validator_count` during the slot's epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn get_committee_count_per_slot(active_validator_count: usize, spec: &ChainSpec) -> usize { let slots_per_epoch = Self::SlotsPerEpoch::to_usize(); @@ -85,28 +82,28 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq { /// Returns the `SLOTS_PER_EPOCH` constant for this specification. /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn slots_per_epoch() -> u64 { Self::SlotsPerEpoch::to_u64() } /// Returns the `SLOTS_PER_HISTORICAL_ROOT` constant for this specification. /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn slots_per_historical_root() -> usize { Self::SlotsPerHistoricalRoot::to_usize() } /// Returns the `EPOCHS_PER_HISTORICAL_VECTOR` constant for this specification. /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn epochs_per_historical_vector() -> usize { Self::EpochsPerHistoricalVector::to_usize() } /// Returns the `SLOTS_PER_ETH1_VOTING_PERIOD` constant for this specification. /// - /// Spec v0.9.1 + /// Spec v0.10.1 fn slots_per_eth1_voting_period() -> usize { Self::SlotsPerEth1VotingPeriod::to_usize() } @@ -122,7 +119,7 @@ macro_rules! params_from_eth_spec { /// Ethereum Foundation specifications. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)] pub struct MainnetEthSpec; @@ -151,11 +148,9 @@ impl EthSpec for MainnetEthSpec { pub type FoundationBeaconState = BeaconState; -/// Ethereum Foundation minimal spec, as defined here: +/// Ethereum Foundation minimal spec, as defined in the eth2.0-specs repo. /// -/// https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/configs/constant_presets/minimal.yaml -/// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)] pub struct MinimalEthSpec; diff --git a/eth2/types/src/fork.rs b/eth2/types/src/fork.rs index 97aadddbf..1d2acc5f9 100644 --- a/eth2/types/src/fork.rs +++ b/eth2/types/src/fork.rs @@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash; /// Specifies a fork of the `BeaconChain`, to prevent replay attacks. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive( Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, )] @@ -30,7 +30,7 @@ pub struct Fork { impl Fork { /// Return the fork version of the given ``epoch``. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn get_fork_version(&self, epoch: Epoch) -> [u8; 4] { if epoch < self.epoch { return self.previous_version; @@ -43,7 +43,7 @@ impl Fork { mod tests { use super::*; - ssz_tests!(Fork); + ssz_and_tree_hash_tests!(Fork); #[test] fn get_fork_version() { diff --git a/eth2/types/src/historical_batch.rs b/eth2/types/src/historical_batch.rs index 8158ba373..a7bd8900e 100644 --- a/eth2/types/src/historical_batch.rs +++ b/eth2/types/src/historical_batch.rs @@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash; /// Historical block and state roots. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct HistoricalBatch { pub block_roots: FixedVector, @@ -22,5 +22,5 @@ mod tests { pub type FoundationHistoricalBatch = HistoricalBatch; - ssz_tests!(FoundationHistoricalBatch); + ssz_and_tree_hash_tests!(FoundationHistoricalBatch); } diff --git a/eth2/types/src/indexed_attestation.rs b/eth2/types/src/indexed_attestation.rs index 32bd134da..d9d2e5772 100644 --- a/eth2/types/src/indexed_attestation.rs +++ b/eth2/types/src/indexed_attestation.rs @@ -2,46 +2,33 @@ use crate::{test_utils::TestRandom, AggregateSignature, AttestationData, EthSpec use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; -use tree_hash::TreeHash; -use tree_hash_derive::{SignedRoot, TreeHash}; +use tree_hash_derive::TreeHash; /// Details an attestation that can be slashable. /// /// To be included in an `AttesterSlashing`. /// -/// Spec v0.9.1 -#[derive( - Debug, - PartialEq, - Clone, - Serialize, - Deserialize, - Encode, - Decode, - TreeHash, - TestRandom, - SignedRoot, -)] +/// Spec v0.10.1 +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[serde(bound = "T: EthSpec")] pub struct IndexedAttestation { /// Lists validator registry indices, not committee indices. pub attesting_indices: VariableList, pub data: AttestationData, - #[signed_root(skip_hashing)] pub signature: AggregateSignature, } impl IndexedAttestation { /// Check if ``attestation_data_1`` and ``attestation_data_2`` have the same target. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn is_double_vote(&self, other: &Self) -> bool { self.data.target.epoch == other.data.target.epoch && self.data != other.data } /// Check if ``attestation_data_1`` surrounds ``attestation_data_2``. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn is_surround_vote(&self, other: &Self) -> bool { self.data.source.epoch < other.data.source.epoch && other.data.target.epoch < self.data.target.epoch @@ -121,7 +108,7 @@ mod tests { ); } - ssz_tests!(IndexedAttestation); + ssz_and_tree_hash_tests!(IndexedAttestation); fn create_indexed_attestation( target_epoch: u64, diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index ca255dad2..6418d19d6 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -19,6 +19,7 @@ pub mod chain_spec; pub mod checkpoint; pub mod deposit; pub mod deposit_data; +pub mod deposit_message; pub mod eth1_data; pub mod eth_spec; pub mod fork; @@ -27,15 +28,18 @@ pub mod historical_batch; pub mod indexed_attestation; pub mod pending_attestation; pub mod proposer_slashing; +pub mod relative_epoch; +pub mod signed_beacon_block; +pub mod signed_beacon_block_header; +pub mod signed_voluntary_exit; +pub mod signing_root; pub mod utils; +pub mod validator; pub mod voluntary_exit; #[macro_use] pub mod slot_epoch_macros; -pub mod relative_epoch; pub mod slot_epoch; -pub mod slot_height; mod tree_hash_impls; -pub mod validator; use ethereum_types::{H160, H256}; @@ -52,6 +56,7 @@ pub use crate::chain_spec::{ChainSpec, Domain, YamlConfig}; pub use crate::checkpoint::Checkpoint; pub use crate::deposit::{Deposit, DEPOSIT_TREE_DEPTH}; pub use crate::deposit_data::DepositData; +pub use crate::deposit_message::DepositMessage; pub use crate::eth1_data::Eth1Data; pub use crate::fork::Fork; pub use crate::free_attestation::FreeAttestation; @@ -60,8 +65,11 @@ pub use crate::indexed_attestation::IndexedAttestation; pub use crate::pending_attestation::PendingAttestation; pub use crate::proposer_slashing::ProposerSlashing; pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; +pub use crate::signed_beacon_block::SignedBeaconBlock; +pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader; +pub use crate::signed_voluntary_exit::SignedVoluntaryExit; +pub use crate::signing_root::{SignedRoot, SigningRoot}; pub use crate::slot_epoch::{Epoch, Slot}; -pub use crate::slot_height::SlotHeight; pub use crate::validator::Validator; pub use crate::voluntary_exit::VoluntaryExit; diff --git a/eth2/types/src/pending_attestation.rs b/eth2/types/src/pending_attestation.rs index 8b6c0022c..900be9a66 100644 --- a/eth2/types/src/pending_attestation.rs +++ b/eth2/types/src/pending_attestation.rs @@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash; /// An attestation that has been included in the state but not yet fully processed. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct PendingAttestation { pub aggregation_bits: BitList, @@ -22,5 +22,5 @@ mod tests { use super::*; use crate::*; - ssz_tests!(PendingAttestation); + ssz_and_tree_hash_tests!(PendingAttestation); } diff --git a/eth2/types/src/proposer_slashing.rs b/eth2/types/src/proposer_slashing.rs index 678458e7e..7b4daa5e7 100644 --- a/eth2/types/src/proposer_slashing.rs +++ b/eth2/types/src/proposer_slashing.rs @@ -1,5 +1,5 @@ -use super::BeaconBlockHeader; use crate::test_utils::TestRandom; +use crate::SignedBeaconBlockHeader; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; @@ -8,17 +8,17 @@ use tree_hash_derive::TreeHash; /// Two conflicting proposals from the same proposer (validator). /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct ProposerSlashing { pub proposer_index: u64, - pub header_1: BeaconBlockHeader, - pub header_2: BeaconBlockHeader, + pub signed_header_1: SignedBeaconBlockHeader, + pub signed_header_2: SignedBeaconBlockHeader, } #[cfg(test)] mod tests { use super::*; - ssz_tests!(ProposerSlashing); + ssz_and_tree_hash_tests!(ProposerSlashing); } diff --git a/eth2/types/src/relative_epoch.rs b/eth2/types/src/relative_epoch.rs index 65971ef2f..2c8e05f02 100644 --- a/eth2/types/src/relative_epoch.rs +++ b/eth2/types/src/relative_epoch.rs @@ -9,7 +9,7 @@ pub enum Error { /// Defines the epochs relative to some epoch. Most useful when referring to the committees prior /// to and following some epoch. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive(Debug, PartialEq, Clone, Copy)] pub enum RelativeEpoch { /// The prior epoch. @@ -23,7 +23,7 @@ pub enum RelativeEpoch { impl RelativeEpoch { /// Returns the `epoch` that `self` refers to, with respect to the `base` epoch. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn into_epoch(self, base: Epoch) -> Epoch { match self { // Due to saturating nature of epoch, check for current first. @@ -40,7 +40,7 @@ impl RelativeEpoch { /// - `EpochTooLow` when `other` is more than 1 prior to `base`. /// - `EpochTooHigh` when `other` is more than 1 after `base`. /// - /// Spec v0.9.1 + /// Spec v0.10.1 pub fn from_epoch(base: Epoch, other: Epoch) -> Result { // Due to saturating nature of epoch, check for current first. if other == base { diff --git a/eth2/types/src/signed_beacon_block.rs b/eth2/types/src/signed_beacon_block.rs new file mode 100644 index 000000000..b07137de5 --- /dev/null +++ b/eth2/types/src/signed_beacon_block.rs @@ -0,0 +1,49 @@ +use crate::{test_utils::TestRandom, BeaconBlock, EthSpec, Hash256, Slot}; +use bls::Signature; + +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash::TreeHash; + +/// A `BeaconBlock` and a signature from its proposer. +/// +/// Spec v0.10.1 +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TestRandom)] +#[serde(bound = "E: EthSpec")] +pub struct SignedBeaconBlock { + pub message: BeaconBlock, + pub signature: Signature, +} + +impl SignedBeaconBlock { + /// Convenience accessor for the block's slot. + pub fn slot(&self) -> Slot { + self.message.slot + } + + /// Convenience accessor for the block's parent root. + pub fn parent_root(&self) -> Hash256 { + self.message.parent_root + } + + /// Convenience accessor for the block's state root. + pub fn state_root(&self) -> Hash256 { + self.message.state_root + } + + /// Returns the `tree_hash_root` of the block. + /// + /// Spec v0.10.1 + pub fn canonical_root(&self) -> Hash256 { + Hash256::from_slice(&self.message.tree_hash_root()[..]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::MainnetEthSpec; + + ssz_tests!(SignedBeaconBlock); +} diff --git a/eth2/types/src/signed_beacon_block_header.rs b/eth2/types/src/signed_beacon_block_header.rs new file mode 100644 index 000000000..d478c761a --- /dev/null +++ b/eth2/types/src/signed_beacon_block_header.rs @@ -0,0 +1,23 @@ +use crate::{test_utils::TestRandom, BeaconBlockHeader}; +use bls::Signature; + +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +/// An exit voluntarily submitted a validator who wishes to withdraw. +/// +/// Spec v0.10.1 +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] +pub struct SignedBeaconBlockHeader { + pub message: BeaconBlockHeader, + pub signature: Signature, +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(SignedBeaconBlockHeader); +} diff --git a/eth2/types/src/signed_voluntary_exit.rs b/eth2/types/src/signed_voluntary_exit.rs new file mode 100644 index 000000000..251840e61 --- /dev/null +++ b/eth2/types/src/signed_voluntary_exit.rs @@ -0,0 +1,23 @@ +use crate::{test_utils::TestRandom, VoluntaryExit}; +use bls::Signature; + +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +/// An exit voluntarily submitted a validator who wishes to withdraw. +/// +/// Spec v0.10.1 +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] +pub struct SignedVoluntaryExit { + pub message: VoluntaryExit, + pub signature: Signature, +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(SignedVoluntaryExit); +} diff --git a/eth2/types/src/signing_root.rs b/eth2/types/src/signing_root.rs new file mode 100644 index 000000000..2e764b821 --- /dev/null +++ b/eth2/types/src/signing_root.rs @@ -0,0 +1,25 @@ +use crate::test_utils::TestRandom; +use crate::Hash256; +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash::TreeHash; +use tree_hash_derive::TreeHash; + +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] +pub struct SigningRoot { + pub object_root: Hash256, + pub domain: u64, +} + +pub trait SignedRoot: TreeHash { + fn signing_root(&self, domain: u64) -> Hash256 { + Hash256::from_slice( + &SigningRoot { + object_root: Hash256::from_slice(&self.tree_hash_root()), + domain, + } + .tree_hash_root(), + ) + } +} diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index bd611aa0c..35dce8e38 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -10,8 +10,8 @@ //! implement `Into`, however this would allow operations between `Slots` and `Epochs` which //! may lead to programming errors which are not detected by the compiler. -use crate::slot_height::SlotHeight; use crate::test_utils::TestRandom; +use crate::SignedRoot; use rand::RngCore; use serde_derive::{Deserialize, Serialize}; use slog; @@ -41,10 +41,6 @@ impl Slot { Epoch::from(self.0 / slots_per_epoch) } - pub fn height(self, genesis_slot: Slot) -> SlotHeight { - SlotHeight::from(self.0.saturating_sub(genesis_slot.as_u64())) - } - pub fn max_value() -> Slot { Slot(u64::max_value()) } @@ -97,6 +93,8 @@ impl Epoch { } } +impl SignedRoot for Epoch {} + pub struct SlotIter<'a> { current_iteration: u64, epoch: &'a Epoch, diff --git a/eth2/types/src/slot_epoch_macros.rs b/eth2/types/src/slot_epoch_macros.rs index 3bd54ee2d..fa5a29662 100644 --- a/eth2/types/src/slot_epoch_macros.rs +++ b/eth2/types/src/slot_epoch_macros.rs @@ -562,7 +562,7 @@ macro_rules! all_tests { new_tests!($type); math_between_tests!($type, $type); math_tests!($type); - ssz_tests!($type); + ssz_and_tree_hash_tests!($type); mod u64_tests { use super::*; diff --git a/eth2/types/src/slot_height.rs b/eth2/types/src/slot_height.rs deleted file mode 100644 index 47db392a9..000000000 --- a/eth2/types/src/slot_height.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::slot_epoch::{Epoch, Slot}; -use crate::test_utils::TestRandom; - -use rand::RngCore; -use serde_derive::Serialize; -use ssz::{ssz_encode, Decode, DecodeError, Encode}; -use std::cmp::{Ord, Ordering}; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign}; - -/// Beacon block height, effectively `Slot/GENESIS_START_BLOCK`. -#[derive(Eq, Debug, Clone, Copy, Default, Serialize)] -pub struct SlotHeight(u64); - -impl_common!(SlotHeight); - -impl SlotHeight { - pub fn new(slot: u64) -> SlotHeight { - SlotHeight(slot) - } - - pub fn slot(self, genesis_slot: Slot) -> Slot { - Slot::from(self.0.saturating_add(genesis_slot.as_u64())) - } - - pub fn epoch(self, genesis_slot: u64, slots_per_epoch: u64) -> Epoch { - Epoch::from(self.0.saturating_add(genesis_slot) / slots_per_epoch) - } - - pub fn max_value() -> SlotHeight { - SlotHeight(u64::max_value()) - } -} - -#[cfg(test)] -mod slot_height_tests { - use super::*; - - all_tests!(SlotHeight); -} diff --git a/eth2/types/src/test_utils/builders/testing_attestation_builder.rs b/eth2/types/src/test_utils/builders/testing_attestation_builder.rs index a0e34128c..f2d428a90 100644 --- a/eth2/types/src/test_utils/builders/testing_attestation_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_attestation_builder.rs @@ -1,6 +1,5 @@ use crate::test_utils::{AttestationTestTask, TestingAttestationDataBuilder}; use crate::*; -use tree_hash::TreeHash; /// Builds an attestation to be used for testing purposes. /// @@ -71,31 +70,22 @@ impl TestingAttestationBuilder { .position(|v| *v == *validator_index) .expect("Signing validator not in attestation committee"); - match test_task { - AttestationTestTask::BadIndexedAttestationBadSignature => (), - _ => { - self.attestation - .aggregation_bits - .set(committee_index, true) - .unwrap(); - } - } - - let message = self.attestation.data.tree_hash_root(); - - let domain = spec.get_domain( - self.attestation.data.target.epoch, - Domain::BeaconAttester, - fork, - ); - let index = if test_task == AttestationTestTask::BadSignature { 0 } else { key_index }; - let signature = Signature::new(&message, domain, secret_keys[index]); - self.attestation.signature.add(&signature) + + self.attestation + .sign(secret_keys[index], committee_index, fork, spec) + .expect("can sign attestation"); + + if let AttestationTestTask::BadIndexedAttestationBadSignature = test_task { + self.attestation + .aggregation_bits + .set(committee_index, false) + .unwrap(); + } } self diff --git a/eth2/types/src/test_utils/builders/testing_attestation_data_builder.rs b/eth2/types/src/test_utils/builders/testing_attestation_data_builder.rs index 7be93fd47..68879da37 100644 --- a/eth2/types/src/test_utils/builders/testing_attestation_data_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_attestation_data_builder.rs @@ -55,11 +55,12 @@ impl TestingAttestationDataBuilder { slot = state.slot - spec.min_attestation_inclusion_delay + 1 } AttestationTestTask::IncludedTooLate => slot -= T::SlotsPerEpoch::to_u64(), - AttestationTestTask::BadTargetEpoch => { + AttestationTestTask::TargetEpochSlotMismatch => { target = Checkpoint { - epoch: Epoch::from(5 as u64), + epoch: current_epoch + 1, root: Hash256::zero(), - } + }; + assert_ne!(target.epoch, slot.epoch(T::slots_per_epoch())); } AttestationTestTask::WrongJustifiedCheckpoint => { source = Checkpoint { @@ -67,18 +68,6 @@ impl TestingAttestationDataBuilder { root: Hash256::zero(), } } - AttestationTestTask::BadTargetTooLow => { - target = Checkpoint { - epoch: Epoch::from(0 as u64), - root: Hash256::zero(), - } - } - AttestationTestTask::BadTargetTooHigh => { - target = Checkpoint { - epoch: Epoch::from(10 as u64), - root: Hash256::zero(), - } - } _ => (), } diff --git a/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs b/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs index f2dfca5c8..451424bb5 100644 --- a/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs @@ -1,6 +1,5 @@ use crate::test_utils::AttesterSlashingTestTask; use crate::*; -use tree_hash::TreeHash; /// Builds an `AttesterSlashing`. /// @@ -22,14 +21,15 @@ impl TestingAttesterSlashingBuilder { test_task: AttesterSlashingTestTask, validator_indices: &[u64], signer: F, + fork: &Fork, + spec: &ChainSpec, ) -> AttesterSlashing where - F: Fn(u64, &[u8], Epoch, Domain) -> Signature, + F: Fn(u64, &[u8]) -> Signature, { let slot = Slot::new(1); let index = 0; let epoch_1 = Epoch::new(1); - let epoch_2 = Epoch::new(2); let hash_1 = Hash256::from_low_u64_le(1); let hash_2 = Hash256::from_low_u64_le(2); let checkpoint_1 = Checkpoint { @@ -83,15 +83,12 @@ impl TestingAttesterSlashingBuilder { }; let add_signatures = |attestation: &mut IndexedAttestation| { - let message = attestation.data.tree_hash_root(); + let domain = + spec.get_domain(attestation.data.target.epoch, Domain::BeaconAttester, fork); + let message = attestation.data.signing_root(domain); for validator_index in validator_indices { - let signature = signer( - *validator_index, - &message[..], - epoch_2, - Domain::BeaconAttester, - ); + let signature = signer(*validator_index, message.as_bytes()); attestation.signature.add(&signature); } }; diff --git a/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs b/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs index b1c0260a5..945492201 100644 --- a/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs @@ -9,7 +9,7 @@ use crate::{ use int_to_bytes::int_to_bytes32; use merkle_proof::MerkleTree; use rayon::prelude::*; -use tree_hash::{SignedRoot, TreeHash}; +use tree_hash::TreeHash; /// Builds a beacon block to be used for testing purposes. /// @@ -46,8 +46,6 @@ pub enum AttestationTestTask { Valid, NoCommiteeForShard, WrongJustifiedCheckpoint, - BadTargetTooLow, - BadTargetTooHigh, BadShard, BadIndexedAttestationBadSignature, BadAggregationBitfieldLen, @@ -55,7 +53,9 @@ pub enum AttestationTestTask { ValidatorUnknown, IncludedTooEarly, IncludedTooLate, - BadTargetEpoch, + TargetEpochSlotMismatch, + // Note: BadTargetEpoch is unreachable in block processing due to valid inclusion window and + // slot check } /// Enum used for passing test options to builder @@ -97,24 +97,14 @@ impl TestingBeaconBlockBuilder { self.block.slot = slot; } - /// Signs the block. - /// - /// Modifying the block after signing may invalidate the signature. - pub fn sign(&mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) { - let message = self.block.signed_root(); - let epoch = self.block.slot.epoch(T::slots_per_epoch()); - let domain = spec.get_domain(epoch, Domain::BeaconProposer, fork); - self.block.signature = Signature::new(&message, domain, sk); - } - /// Sets the randao to be a signature across the blocks epoch. /// /// Modifying the block's slot after signing may invalidate the signature. pub fn set_randao_reveal(&mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) { let epoch = self.block.slot.epoch(T::slots_per_epoch()); - let message = epoch.tree_hash_root(); let domain = spec.get_domain(epoch, Domain::Randao, fork); - self.block.body.randao_reveal = Signature::new(&message, domain, sk); + let message = epoch.signing_root(domain); + self.block.body.randao_reveal = Signature::new(message.as_bytes(), sk); } /// Has the randao reveal been set? @@ -364,26 +354,23 @@ impl TestingBeaconBlockBuilder { _ => (), } - let mut builder = TestingVoluntaryExitBuilder::new(exit_epoch, validator_index); + let builder = TestingVoluntaryExitBuilder::new(exit_epoch, validator_index); + let exit = builder.build(sk, &state.fork, spec); - builder.sign(sk, &state.fork, spec); - - self.block - .body - .voluntary_exits - .push(builder.build()) - .unwrap(); + self.block.body.voluntary_exits.push(exit).unwrap(); } /// Signs and returns the block, consuming the builder. - pub fn build(mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) -> BeaconBlock { - self.sign(sk, fork, spec); - self.block + pub fn build(self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) -> SignedBeaconBlock { + self.block.sign(sk, fork, spec) } /// Returns the block, consuming the builder. - pub fn build_without_signing(self) -> BeaconBlock { - self.block + pub fn build_without_signing(self) -> SignedBeaconBlock { + SignedBeaconBlock { + message: self.block, + signature: Signature::empty_signature(), + } } } @@ -397,12 +384,13 @@ fn build_proposer_slashing( fork: &Fork, spec: &ChainSpec, ) -> ProposerSlashing { - let signer = |_validator_index: u64, message: &[u8], epoch: Epoch, domain: Domain| { - let domain = spec.get_domain(epoch, domain, fork); - Signature::new(message, domain, secret_key) - }; - - TestingProposerSlashingBuilder::double_vote::(test_task, validator_index, signer) + TestingProposerSlashingBuilder::double_vote::( + test_task, + validator_index, + secret_key, + fork, + spec, + ) } /// Builds an `AttesterSlashing` for some `validator_indices`. @@ -415,14 +403,13 @@ fn build_double_vote_attester_slashing( fork: &Fork, spec: &ChainSpec, ) -> AttesterSlashing { - let signer = |validator_index: u64, message: &[u8], epoch: Epoch, domain: Domain| { + let signer = |validator_index: u64, message: &[u8]| { let key_index = validator_indices .iter() .position(|&i| i == validator_index) .expect("Unable to find attester slashing key"); - let domain = spec.get_domain(epoch, domain, fork); - Signature::new(message, domain, secret_keys[key_index]) + Signature::new(message, secret_keys[key_index]) }; - TestingAttesterSlashingBuilder::double_vote(test_task, validator_indices, signer) + TestingAttesterSlashingBuilder::double_vote(test_task, validator_indices, signer, fork, spec) } diff --git a/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs b/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs index 182236a36..124cc423e 100644 --- a/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs @@ -1,31 +1,24 @@ use crate::test_utils::ProposerSlashingTestTask; use crate::*; -use tree_hash::SignedRoot; /// Builds a `ProposerSlashing`. /// /// This struct should **never be used for production purposes.** -pub struct TestingProposerSlashingBuilder(); +pub struct TestingProposerSlashingBuilder; impl TestingProposerSlashingBuilder { /// Builds a `ProposerSlashing` that is a double vote. /// - /// The `signer` function is used to sign the double-vote and accepts: - /// - /// - `validator_index: u64` - /// - `message: &[u8]` - /// - `epoch: Epoch` - /// - `domain: Domain` - /// /// Where domain is a domain "constant" (e.g., `spec.domain_attestation`). - pub fn double_vote( + pub fn double_vote( test_task: ProposerSlashingTestTask, mut proposer_index: u64, - signer: F, + secret_key: &SecretKey, + fork: &Fork, + spec: &ChainSpec, ) -> ProposerSlashing where T: EthSpec, - F: Fn(u64, &[u8], Epoch, Domain) -> Signature, { let slot = Slot::new(0); let hash_1 = Hash256::from([1; 32]); @@ -35,11 +28,13 @@ impl TestingProposerSlashingBuilder { Hash256::from([2; 32]) }; - let mut header_1 = BeaconBlockHeader { - slot, - parent_root: hash_1, - state_root: hash_1, - body_root: hash_1, + let mut signed_header_1 = SignedBeaconBlockHeader { + message: BeaconBlockHeader { + slot, + parent_root: hash_1, + state_root: hash_1, + body_root: hash_1, + }, signature: Signature::empty_signature(), }; @@ -49,26 +44,21 @@ impl TestingProposerSlashingBuilder { Slot::new(0) }; - let mut header_2 = BeaconBlockHeader { - parent_root: hash_2, - slot: slot_2, - ..header_1.clone() + let mut signed_header_2 = SignedBeaconBlockHeader { + message: BeaconBlockHeader { + parent_root: hash_2, + slot: slot_2, + ..signed_header_1.message.clone() + }, + signature: Signature::empty_signature(), }; - let epoch = slot.epoch(T::slots_per_epoch()); - if test_task != ProposerSlashingTestTask::BadProposal1Signature { - header_1.signature = { - let message = header_1.signed_root(); - signer(proposer_index, &message[..], epoch, Domain::BeaconProposer) - }; + signed_header_1 = signed_header_1.message.sign::(secret_key, fork, spec); } if test_task != ProposerSlashingTestTask::BadProposal2Signature { - header_2.signature = { - let message = header_2.signed_root(); - signer(proposer_index, &message[..], epoch, Domain::BeaconProposer) - }; + signed_header_2 = signed_header_2.message.sign::(secret_key, fork, spec); } if test_task == ProposerSlashingTestTask::ProposerUnknown { @@ -77,8 +67,8 @@ impl TestingProposerSlashingBuilder { ProposerSlashing { proposer_index, - header_1, - header_2, + signed_header_1, + signed_header_2, } } } diff --git a/eth2/types/src/test_utils/builders/testing_voluntary_exit_builder.rs b/eth2/types/src/test_utils/builders/testing_voluntary_exit_builder.rs index 58e8e750d..edc365a61 100644 --- a/eth2/types/src/test_utils/builders/testing_voluntary_exit_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_voluntary_exit_builder.rs @@ -1,5 +1,4 @@ use crate::*; -use tree_hash::SignedRoot; /// Builds an exit to be used for testing purposes. /// @@ -14,24 +13,20 @@ impl TestingVoluntaryExitBuilder { let exit = VoluntaryExit { epoch, validator_index, - signature: Signature::empty_signature(), }; Self { exit } } - /// Signs the exit. + /// Build and sign the exit. /// /// The signing secret key must match that of the exiting validator. - pub fn sign(&mut self, secret_key: &SecretKey, fork: &Fork, spec: &ChainSpec) { - let message = self.exit.signed_root(); - let domain = spec.get_domain(self.exit.epoch, Domain::VoluntaryExit, fork); - - self.exit.signature = Signature::new(&message, domain, secret_key); - } - - /// Builds the exit, consuming the builder. - pub fn build(self) -> VoluntaryExit { - self.exit + pub fn build( + self, + secret_key: &SecretKey, + fork: &Fork, + spec: &ChainSpec, + ) -> SignedVoluntaryExit { + self.exit.sign(secret_key, fork, spec) } } diff --git a/eth2/types/src/test_utils/macros.rs b/eth2/types/src/test_utils/macros.rs index dfb8f9a27..95a7c480c 100644 --- a/eth2/types/src/test_utils/macros.rs +++ b/eth2/types/src/test_utils/macros.rs @@ -1,4 +1,13 @@ -#[cfg(test)] +#![cfg(test)] + +#[macro_export] +macro_rules! ssz_and_tree_hash_tests { + ($type: ty) => { + ssz_tests!($type); + tree_hash_tests!($type); + }; +} + #[macro_export] macro_rules! ssz_tests { ($type: ty) => { @@ -16,7 +25,12 @@ macro_rules! ssz_tests { assert_eq!(original, decoded); } + }; +} +#[macro_export] +macro_rules! tree_hash_tests { + ($type: ty) => { #[test] pub fn test_tree_hash_root() { use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; @@ -28,8 +42,6 @@ macro_rules! ssz_tests { let result = original.tree_hash_root(); assert_eq!(result.len(), 32); - // TODO: Add further tests - // https://github.com/sigp/lighthouse/issues/170 } }; } diff --git a/eth2/types/src/test_utils/test_random/signature.rs b/eth2/types/src/test_utils/test_random/signature.rs index ef5d9a17f..61542df66 100644 --- a/eth2/types/src/test_utils/test_random/signature.rs +++ b/eth2/types/src/test_utils/test_random/signature.rs @@ -7,6 +7,6 @@ impl TestRandom for Signature { let mut message = vec![0; 32]; rng.fill_bytes(&mut message); - Signature::new(&message, 0, &secret_key) + Signature::new(&message, &secret_key) } } diff --git a/eth2/types/src/validator.rs b/eth2/types/src/validator.rs index 537c54619..2901aa510 100644 --- a/eth2/types/src/validator.rs +++ b/eth2/types/src/validator.rs @@ -1,4 +1,6 @@ -use crate::{test_utils::TestRandom, Epoch, Hash256, PublicKeyBytes}; +use crate::{ + test_utils::TestRandom, BeaconState, ChainSpec, Epoch, EthSpec, Hash256, PublicKeyBytes, +}; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; @@ -6,7 +8,7 @@ use tree_hash_derive::TreeHash; /// Information about a `BeaconChain` validator. /// -/// Spec v0.9.1 +/// Spec v0.10.1 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)] pub struct Validator { pub pubkey: PublicKeyBytes, @@ -39,6 +41,28 @@ impl Validator { pub fn is_withdrawable_at(&self, epoch: Epoch) -> bool { epoch >= self.withdrawable_epoch } + + /// Returns `true` if the validator is eligible to join the activation queue. + /// + /// Spec v0.10.1 + pub fn is_eligible_for_activation_queue(&self, spec: &ChainSpec) -> bool { + self.activation_eligibility_epoch == spec.far_future_epoch + && self.effective_balance == spec.max_effective_balance + } + + /// Returns `true` if the validator is eligible to be activated. + /// + /// Spec v0.10.1 + pub fn is_eligible_for_activation( + &self, + state: &BeaconState, + spec: &ChainSpec, + ) -> bool { + // Placement in queue is finalized + self.activation_eligibility_epoch <= state.finalized_checkpoint.epoch + // Has not yet been activated + && self.activation_epoch == spec.far_future_epoch + } } impl Default for Validator { @@ -115,5 +139,5 @@ mod tests { assert_eq!(v.is_withdrawable_at(epoch + 1), true); } - ssz_tests!(Validator); + ssz_and_tree_hash_tests!(Validator); } diff --git a/eth2/types/src/voluntary_exit.rs b/eth2/types/src/voluntary_exit.rs index b99b30983..cb39f3d08 100644 --- a/eth2/types/src/voluntary_exit.rs +++ b/eth2/types/src/voluntary_exit.rs @@ -1,38 +1,45 @@ -use crate::{test_utils::TestRandom, Epoch}; -use bls::Signature; +use crate::{ + test_utils::TestRandom, ChainSpec, Domain, Epoch, Fork, SecretKey, Signature, SignedRoot, + SignedVoluntaryExit, +}; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; -use tree_hash::TreeHash; -use tree_hash_derive::{SignedRoot, TreeHash}; +use tree_hash_derive::TreeHash; /// An exit voluntarily submitted a validator who wishes to withdraw. /// -/// Spec v0.9.1 -#[derive( - Debug, - PartialEq, - Clone, - Serialize, - Deserialize, - Encode, - Decode, - TreeHash, - TestRandom, - SignedRoot, -)] +/// Spec v0.10.1 +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct VoluntaryExit { /// Earliest epoch when voluntary exit can be processed. pub epoch: Epoch, pub validator_index: u64, - #[signed_root(skip_hashing)] - pub signature: Signature, +} + +impl SignedRoot for VoluntaryExit {} + +impl VoluntaryExit { + pub fn sign( + self, + secret_key: &SecretKey, + fork: &Fork, + spec: &ChainSpec, + ) -> SignedVoluntaryExit { + let domain = spec.get_domain(self.epoch, Domain::VoluntaryExit, fork); + let message = self.signing_root(domain); + let signature = Signature::new(message.as_bytes(), &secret_key); + SignedVoluntaryExit { + message: self, + signature, + } + } } #[cfg(test)] mod tests { use super::*; - ssz_tests!(VoluntaryExit); + ssz_and_tree_hash_tests!(VoluntaryExit); } diff --git a/eth2/utils/bls/Cargo.toml b/eth2/utils/bls/Cargo.toml index a4a67cf1f..e568e9715 100644 --- a/eth2/utils/bls/Cargo.toml +++ b/eth2/utils/bls/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Paul Hauner "] edition = "2018" [dependencies] -milagro_bls = { git = "https://github.com/sigp/milagro_bls", tag = "v0.11.2" } +milagro_bls = { git = "https://github.com/sigp/milagro_bls", branch = "eth2.0-v0.10" } eth2_hashing = "0.1.0" hex = "0.3" rand = "0.7.2" diff --git a/eth2/utils/bls/src/aggregate_public_key.rs b/eth2/utils/bls/src/aggregate_public_key.rs index dabf55aa8..4f4040d12 100644 --- a/eth2/utils/bls/src/aggregate_public_key.rs +++ b/eth2/utils/bls/src/aggregate_public_key.rs @@ -1,5 +1,9 @@ -use super::PublicKey; +use super::{PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE}; use milagro_bls::{AggregatePublicKey as RawAggregatePublicKey, G1Point}; +use serde::de::{Deserialize, Deserializer}; +use serde::ser::{Serialize, Serializer}; +use serde_hex::{encode as hex_encode, PrefixedHexVisitor}; +use ssz::{Decode, DecodeError, Encode}; /// A BLS aggregate public key. /// @@ -13,6 +17,16 @@ impl AggregatePublicKey { AggregatePublicKey(RawAggregatePublicKey::new()) } + pub fn from_bytes(bytes: &[u8]) -> Result { + let pubkey = RawAggregatePublicKey::from_bytes(&bytes).map_err(|_| { + DecodeError::BytesInvalid( + format!("Invalid AggregatePublicKey bytes: {:?}", bytes).to_string(), + ) + })?; + + Ok(AggregatePublicKey(pubkey)) + } + pub fn add_without_affine(&mut self, public_key: &PublicKey) { self.0.point.add(&public_key.as_raw().point) } @@ -34,6 +48,11 @@ impl AggregatePublicKey { &self.0 } + /// Returns the underlying point as compressed bytes. + pub fn as_bytes(&self) -> Vec { + self.as_raw().as_bytes() + } + pub fn into_raw(self) -> RawAggregatePublicKey { self.0 } @@ -41,6 +60,37 @@ impl AggregatePublicKey { /// Return a hex string representation of this key's bytes. #[cfg(test)] pub fn as_hex_string(&self) -> String { - serde_hex::encode(self.as_raw().as_bytes()) + serde_hex::encode(self.as_bytes()) + } +} + +impl_ssz!( + AggregatePublicKey, + BLS_PUBLIC_KEY_BYTE_SIZE, + "AggregatePublicKey" +); +impl_tree_hash!(AggregatePublicKey, BLS_PUBLIC_KEY_BYTE_SIZE); + +impl Serialize for AggregatePublicKey { + /// Serde serialization is compliant the Ethereum YAML test format. + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&hex_encode(self.as_bytes())) + } +} + +impl<'de> Deserialize<'de> for AggregatePublicKey { + /// Serde serialization is compliant the Ethereum YAML test format. + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?; + let agg_sig = AggregatePublicKey::from_ssz_bytes(&bytes) + .map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?; + + Ok(agg_sig) } } diff --git a/eth2/utils/bls/src/aggregate_signature.rs b/eth2/utils/bls/src/aggregate_signature.rs index 8d930f0cc..b3dc46025 100644 --- a/eth2/utils/bls/src/aggregate_signature.rs +++ b/eth2/utils/bls/src/aggregate_signature.rs @@ -47,17 +47,12 @@ impl AggregateSignature { /// /// Only returns `true` if the set of keys in the `AggregatePublicKey` match the set of keys /// that signed the `AggregateSignature`. - pub fn verify( - &self, - msg: &[u8], - domain: u64, - aggregate_public_key: &AggregatePublicKey, - ) -> bool { + pub fn verify(&self, msg: &[u8], aggregate_public_key: &AggregatePublicKey) -> bool { if self.is_empty { return false; } self.aggregate_signature - .verify(msg, domain, aggregate_public_key.as_raw()) + .verify(msg, aggregate_public_key.as_raw()) } /// Verify this AggregateSignature against multiple AggregatePublickeys with multiple Messages. @@ -67,7 +62,6 @@ impl AggregateSignature { pub fn verify_multiple( &self, messages: &[&[u8]], - domain: u64, aggregate_public_keys: &[&AggregatePublicKey], ) -> bool { if self.is_empty { @@ -83,7 +77,7 @@ impl AggregateSignature { } self.aggregate_signature - .verify_multiple(&msgs, domain, &aggregate_public_keys[..]) + .verify_multiple(&msgs, &aggregate_public_keys[..]) } /// Return AggregateSignature as bytes @@ -193,7 +187,7 @@ mod tests { let keypair = Keypair::random(); let mut original = AggregateSignature::new(); - original.add(&Signature::new(&[42, 42], 0, &keypair.sk)); + original.add(&Signature::new(&[42, 42], &keypair.sk)); let bytes = original.as_ssz_bytes(); let decoded = AggregateSignature::from_ssz_bytes(&bytes).unwrap(); diff --git a/eth2/utils/bls/src/fake_aggregate_public_key.rs b/eth2/utils/bls/src/fake_aggregate_public_key.rs index 0a231fd94..3674084e1 100644 --- a/eth2/utils/bls/src/fake_aggregate_public_key.rs +++ b/eth2/utils/bls/src/fake_aggregate_public_key.rs @@ -1,5 +1,10 @@ use super::{PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE}; +use hex::encode as hex_encode; use milagro_bls::G1Point; +use serde::de::{Deserialize, Deserializer}; +use serde::ser::{Serialize, Serializer}; +use serde_hex::PrefixedHexVisitor; +use ssz::{ssz_encode, Decode, DecodeError, Encode}; /// A BLS aggregate public key. /// @@ -17,6 +22,20 @@ impl FakeAggregatePublicKey { Self::zero() } + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != BLS_PUBLIC_KEY_BYTE_SIZE { + Err(DecodeError::InvalidByteLength { + len: bytes.len(), + expected: BLS_PUBLIC_KEY_BYTE_SIZE, + }) + } else { + Ok(Self { + bytes: bytes.to_vec(), + point: G1Point::new(), + }) + } + } + pub fn add_without_affine(&mut self, _public_key: &PublicKey) { // No nothing. } @@ -53,3 +72,32 @@ impl FakeAggregatePublicKey { self.bytes.clone() } } + +impl_ssz!( + FakeAggregatePublicKey, + BLS_PUBLIC_KEY_BYTE_SIZE, + "FakeAggregatePublicKey" +); + +impl_tree_hash!(FakeAggregatePublicKey, BLS_PUBLIC_KEY_BYTE_SIZE); + +impl Serialize for FakeAggregatePublicKey { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&hex_encode(ssz_encode(self))) + } +} + +impl<'de> Deserialize<'de> for FakeAggregatePublicKey { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?; + let pubkey = <_>::from_ssz_bytes(&bytes[..]) + .map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?; + Ok(pubkey) + } +} diff --git a/eth2/utils/bls/src/fake_aggregate_signature.rs b/eth2/utils/bls/src/fake_aggregate_signature.rs index 52495a76e..19fe40038 100644 --- a/eth2/utils/bls/src/fake_aggregate_signature.rs +++ b/eth2/utils/bls/src/fake_aggregate_signature.rs @@ -48,12 +48,7 @@ impl FakeAggregateSignature { } /// _Always_ returns `true`. - pub fn verify( - &self, - _msg: &[u8], - _domain: u64, - _aggregate_public_key: &FakeAggregatePublicKey, - ) -> bool { + pub fn verify(&self, _msg: &[u8], _aggregate_public_key: &FakeAggregatePublicKey) -> bool { true } @@ -61,7 +56,6 @@ impl FakeAggregateSignature { pub fn verify_multiple( &self, _messages: &[&[u8]], - _domain: u64, _aggregate_public_keys: &[&FakeAggregatePublicKey], ) -> bool { true @@ -127,7 +121,7 @@ mod tests { let keypair = Keypair::random(); let mut original = FakeAggregateSignature::new(); - original.add(&Signature::new(&[42, 42], 0, &keypair.sk)); + original.add(&Signature::new(&[42, 42], &keypair.sk)); let bytes = ssz_encode(&original); let decoded = FakeAggregateSignature::from_ssz_bytes(&bytes).unwrap(); diff --git a/eth2/utils/bls/src/fake_signature.rs b/eth2/utils/bls/src/fake_signature.rs index 9570196d0..ce4866c8d 100644 --- a/eth2/utils/bls/src/fake_signature.rs +++ b/eth2/utils/bls/src/fake_signature.rs @@ -20,7 +20,7 @@ pub struct FakeSignature { impl FakeSignature { /// Creates a new all-zero's signature - pub fn new(_msg: &[u8], _domain: u64, _sk: &SecretKey) -> Self { + pub fn new(_msg: &[u8], _sk: &SecretKey) -> Self { FakeSignature::zero() } @@ -39,7 +39,7 @@ impl FakeSignature { } /// _Always_ returns `true`. - pub fn verify(&self, _msg: &[u8], _domain: u64, _pk: &PublicKey) -> bool { + pub fn verify(&self, _msg: &[u8], _pk: &PublicKey) -> bool { true } @@ -124,7 +124,7 @@ mod tests { pub fn test_ssz_round_trip() { let keypair = Keypair::random(); - let original = FakeSignature::new(&[42, 42], 0, &keypair.sk); + let original = FakeSignature::new(&[42, 42], &keypair.sk); let bytes = ssz_encode(&original); let decoded = FakeSignature::from_ssz_bytes(&bytes).unwrap(); diff --git a/eth2/utils/bls/src/lib.rs b/eth2/utils/bls/src/lib.rs index be80e0258..27196fcc2 100644 --- a/eth2/utils/bls/src/lib.rs +++ b/eth2/utils/bls/src/lib.rs @@ -75,7 +75,6 @@ pub fn bls_verify_aggregate( pubkey: &AggregatePublicKey, message: &[u8], signature: &AggregateSignature, - domain: u64, ) -> bool { - signature.verify(message, domain, pubkey) + signature.verify(message, pubkey) } diff --git a/eth2/utils/bls/src/signature.rs b/eth2/utils/bls/src/signature.rs index b6ba37017..64b877bfb 100644 --- a/eth2/utils/bls/src/signature.rs +++ b/eth2/utils/bls/src/signature.rs @@ -17,39 +17,19 @@ pub struct Signature { impl Signature { /// Instantiate a new Signature from a message and a SecretKey. - pub fn new(msg: &[u8], domain: u64, sk: &SecretKey) -> Self { + pub fn new(msg: &[u8], sk: &SecretKey) -> Self { Signature { - signature: RawSignature::new(msg, domain, sk.as_raw()), - is_empty: false, - } - } - - /// Instantiate a new Signature from a message and a SecretKey, where the message has already - /// been hashed. - pub fn new_hashed(x_real_hashed: &[u8], x_imaginary_hashed: &[u8], sk: &SecretKey) -> Self { - Signature { - signature: RawSignature::new_hashed(x_real_hashed, x_imaginary_hashed, sk.as_raw()), + signature: RawSignature::new(msg, sk.as_raw()), is_empty: false, } } /// Verify the Signature against a PublicKey. - pub fn verify(&self, msg: &[u8], domain: u64, pk: &PublicKey) -> bool { + pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> bool { if self.is_empty { return false; } - self.signature.verify(msg, domain, pk.as_raw()) - } - - /// Verify the Signature against a PublicKey, where the message has already been hashed. - pub fn verify_hashed( - &self, - x_real_hashed: &[u8], - x_imaginary_hashed: &[u8], - pk: &PublicKey, - ) -> bool { - self.signature - .verify_hashed(x_real_hashed, x_imaginary_hashed, pk.as_raw()) + self.signature.verify(msg, pk.as_raw()) } /// Returns the underlying signature. @@ -141,7 +121,7 @@ mod tests { pub fn test_ssz_round_trip() { let keypair = Keypair::random(); - let original = Signature::new(&[42, 42], 0, &keypair.sk); + let original = Signature::new(&[42, 42], &keypair.sk); let bytes = ssz_encode(&original); let decoded = Signature::from_ssz_bytes(&bytes).unwrap(); @@ -153,7 +133,7 @@ mod tests { pub fn test_byte_size() { let keypair = Keypair::random(); - let signature = Signature::new(&[42, 42], 0, &keypair.sk); + let signature = Signature::new(&[42, 42], &keypair.sk); let bytes = ssz_encode(&signature); assert_eq!(bytes.len(), BLS_SIG_BYTE_SIZE); } diff --git a/eth2/utils/bls/src/signature_bytes.rs b/eth2/utils/bls/src/signature_bytes.rs index bfec269b0..56e766af7 100644 --- a/eth2/utils/bls/src/signature_bytes.rs +++ b/eth2/utils/bls/src/signature_bytes.rs @@ -16,7 +16,7 @@ mod tests { #[test] pub fn test_valid_signature() { let keypair = Keypair::random(); - let original = Signature::new(&[42, 42], 0, &keypair.sk); + let original = Signature::new(&[42, 42], &keypair.sk); let bytes = ssz_encode(&original); let signature_bytes = SignatureBytes::from_bytes(&bytes).unwrap(); diff --git a/eth2/utils/bls/src/signature_set.rs b/eth2/utils/bls/src/signature_set.rs index 10ef6d65f..ab49d1414 100644 --- a/eth2/utils/bls/src/signature_set.rs +++ b/eth2/utils/bls/src/signature_set.rs @@ -6,7 +6,6 @@ use std::borrow::Cow; use milagro_bls::AggregateSignature as RawAggregateSignature; type Message = Vec; -type Domain = u64; #[derive(Clone, Debug)] pub struct SignedMessage<'a> { @@ -27,23 +26,16 @@ impl<'a> SignedMessage<'a> { pub struct SignatureSet<'a> { pub signature: &'a G2Point, signed_messages: Vec>, - domain: Domain, } impl<'a> SignatureSet<'a> { - pub fn single( - signature: &'a S, - signing_key: Cow<'a, G1Point>, - message: Message, - domain: Domain, - ) -> Self + pub fn single(signature: &'a S, signing_key: Cow<'a, G1Point>, message: Message) -> Self where S: G2Ref, { Self { signature: signature.g2_ref(), signed_messages: vec![SignedMessage::new(vec![signing_key], message)], - domain, } } @@ -53,7 +45,6 @@ impl<'a> SignatureSet<'a> { message_0_signing_keys: Vec>, message_1: Message, message_1_signing_keys: Vec>, - domain: Domain, ) -> Self where T: G1Ref + Clone, @@ -65,18 +56,16 @@ impl<'a> SignatureSet<'a> { SignedMessage::new(message_0_signing_keys, message_0), SignedMessage::new(message_1_signing_keys, message_1), ], - domain, } } - pub fn new(signature: &'a S, signed_messages: Vec>, domain: Domain) -> Self + pub fn new(signature: &'a S, signed_messages: Vec>) -> Self where S: G2Ref, { Self { signature: signature.g2_ref(), signed_messages, - domain, } } @@ -103,7 +92,7 @@ impl<'a> SignatureSet<'a> { let pubkey_refs: Vec<&milagro_bls::AggregatePublicKey> = pubkeys.iter().map(std::borrow::Borrow::borrow).collect(); - sig.verify_multiple(&messages, self.domain, &pubkey_refs) + sig.verify_multiple(&messages, &pubkey_refs) } } @@ -118,7 +107,7 @@ pub fn verify_signature_sets<'a>(_iter: impl Iterator>) true } -type VerifySet<'a> = (G2Point, Vec, Vec>, u64); +type VerifySet<'a> = (G2Point, Vec, Vec>); impl<'a> Into> for SignatureSet<'a> { fn into(self) -> VerifySet<'a> { @@ -138,7 +127,7 @@ impl<'a> Into> for SignatureSet<'a> { }) .unzip(); - (signature, pubkeys, messages, self.domain) + (signature, pubkeys, messages) } } diff --git a/eth2/utils/deposit_contract/build.rs b/eth2/utils/deposit_contract/build.rs index ea59acc82..88e2ff3e3 100644 --- a/eth2/utils/deposit_contract/build.rs +++ b/eth2/utils/deposit_contract/build.rs @@ -9,7 +9,9 @@ use std::fs::File; use std::io::Write; use std::path::PathBuf; -const TAG: &str = "v0.9.2"; +const TAG: &str = "v0.10.1"; +// NOTE: the version of the unsafe contract lags the main tag, but the v0.9.2.1 code is compatible +// with the unmodified v0.10.1 contract const UNSAFE_TAG: &str = "v0.9.2.1"; fn spec_url() -> String { diff --git a/eth2/utils/deposit_contract/src/lib.rs b/eth2/utils/deposit_contract/src/lib.rs index ba473032c..c7ce827b1 100644 --- a/eth2/utils/deposit_contract/src/lib.rs +++ b/eth2/utils/deposit_contract/src/lib.rs @@ -7,14 +7,14 @@ pub use ethabi::Error; pub const CONTRACT_DEPLOY_GAS: usize = 4_000_000; pub const DEPOSIT_GAS: usize = 4_000_000; -pub const ABI: &[u8] = include_bytes!("../contracts/v0.9.2_validator_registration.json"); -pub const BYTECODE: &[u8] = include_bytes!("../contracts/v0.9.2_validator_registration.bytecode"); +pub const ABI: &[u8] = include_bytes!("../contracts/v0.10.1_validator_registration.json"); +pub const BYTECODE: &[u8] = include_bytes!("../contracts/v0.10.1_validator_registration.bytecode"); pub mod testnet { pub const ABI: &[u8] = - include_bytes!("../contracts/v0.9.2_testnet_validator_registration.json"); + include_bytes!("../contracts/v0.10.1_testnet_validator_registration.json"); pub const BYTECODE: &[u8] = - include_bytes!("../contracts/v0.9.2_testnet_validator_registration.bytecode"); + include_bytes!("../contracts/v0.10.1_testnet_validator_registration.bytecode"); } pub fn eth1_tx_data(deposit_data: &DepositData) -> Result, Error> { diff --git a/eth2/utils/eth2_interop_keypairs/Cargo.toml b/eth2/utils/eth2_interop_keypairs/Cargo.toml index 1ad77b201..f6a6cd346 100644 --- a/eth2/utils/eth2_interop_keypairs/Cargo.toml +++ b/eth2/utils/eth2_interop_keypairs/Cargo.toml @@ -11,7 +11,7 @@ lazy_static = "1.4.0" num-bigint = "0.2.3" eth2_hashing = "0.1.0" hex = "0.3" -milagro_bls = { git = "https://github.com/sigp/milagro_bls", tag = "v0.11.2" } +milagro_bls = { git = "https://github.com/sigp/milagro_bls", branch = "eth2.0-v0.10" } serde_yaml = "0.8.11" serde = "1.0.102" serde_derive = "1.0.102" diff --git a/eth2/utils/eth2_testnet_config/src/lib.rs b/eth2/utils/eth2_testnet_config/src/lib.rs index 2c1d455dd..5d20f51cc 100644 --- a/eth2/utils/eth2_testnet_config/src/lib.rs +++ b/eth2/utils/eth2_testnet_config/src/lib.rs @@ -202,6 +202,7 @@ mod tests { type E = MainnetEthSpec; + /* FIXME: add new testnet config and re-enable this test #[test] fn hard_coded_works() { let dir: Eth2TestnetConfig = @@ -211,6 +212,7 @@ mod tests { assert!(dir.genesis_state.is_some()); assert!(dir.yaml_config.is_some()); } + */ #[test] fn round_trip() { diff --git a/eth2/utils/remote_beacon_node/src/lib.rs b/eth2/utils/remote_beacon_node/src/lib.rs index 60668b473..dff969b38 100644 --- a/eth2/utils/remote_beacon_node/src/lib.rs +++ b/eth2/utils/remote_beacon_node/src/lib.rs @@ -15,7 +15,7 @@ use std::marker::PhantomData; use std::time::Duration; use types::{ Attestation, BeaconBlock, BeaconState, CommitteeIndex, Epoch, EthSpec, Fork, Hash256, - PublicKey, Signature, Slot, + PublicKey, Signature, SignedBeaconBlock, Slot, }; use url::Url; @@ -258,7 +258,7 @@ impl Validator { /// Posts a block to the beacon node, expecting it to verify it and publish it to the network. pub fn publish_block( &self, - block: BeaconBlock, + block: SignedBeaconBlock, ) -> impl Future { let client = self.0.clone(); self.url("block") @@ -347,7 +347,7 @@ impl Beacon { pub fn get_block_by_slot( &self, slot: Slot, - ) -> impl Future, Hash256), Error = Error> { + ) -> impl Future, Hash256), Error = Error> { self.get_block("slot".to_string(), format!("{}", slot.as_u64())) } @@ -355,7 +355,7 @@ impl Beacon { pub fn get_block_by_root( &self, root: Hash256, - ) -> impl Future, Hash256), Error = Error> { + ) -> impl Future, Hash256), Error = Error> { self.get_block("root".to_string(), root_as_string(root)) } @@ -364,7 +364,7 @@ impl Beacon { &self, query_key: String, query_param: String, - ) -> impl Future, Hash256), Error = Error> { + ) -> impl Future, Hash256), Error = Error> { let client = self.0.clone(); self.url("block") .into_future() @@ -576,7 +576,7 @@ impl Advanced { #[derive(Deserialize)] #[serde(bound = "T: EthSpec")] pub struct BlockResponse { - pub beacon_block: BeaconBlock, + pub beacon_block: SignedBeaconBlock, pub root: Hash256, } diff --git a/eth2/utils/ssz/src/lib.rs b/eth2/utils/ssz/src/lib.rs index 89e707395..046ebb5f7 100644 --- a/eth2/utils/ssz/src/lib.rs +++ b/eth2/utils/ssz/src/lib.rs @@ -2,8 +2,8 @@ //! format designed for use in Ethereum 2.0. //! //! Adheres to the Ethereum 2.0 [SSZ -//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/specs/simple-serialize.md) -//! at v0.8.1 . +//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/ssz/simple-serialize.md) +//! at v0.10.1. //! //! ## Example //! diff --git a/eth2/utils/ssz_types/src/lib.rs b/eth2/utils/ssz_types/src/lib.rs index c4bb3b164..44c4cbc88 100644 --- a/eth2/utils/ssz_types/src/lib.rs +++ b/eth2/utils/ssz_types/src/lib.rs @@ -9,8 +9,8 @@ //! for padding and verification. //! //! Adheres to the Ethereum 2.0 [SSZ -//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/specs/simple-serialize.md) -//! at v0.8.1 . +//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/ssz/simple-serialize.md) +//! at v0.10.1. //! //! ## Example //! ``` diff --git a/eth2/utils/swap_or_not_shuffle/src/lib.rs b/eth2/utils/swap_or_not_shuffle/src/lib.rs index 0ea618884..23fff3354 100644 --- a/eth2/utils/swap_or_not_shuffle/src/lib.rs +++ b/eth2/utils/swap_or_not_shuffle/src/lib.rs @@ -1,7 +1,7 @@ //! Provides list-shuffling functions matching the Ethereum 2.0 specification. //! //! See -//! [compute_shuffled_index](https://github.com/ethereum/eth2.0-specs/blob/v0.8.3/specs/core/0_beacon-chain.md#compute_shuffled_index) +//! [compute_shuffled_index](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#compute_shuffled_index) //! for specifications. //! //! There are two functions exported by this crate: diff --git a/eth2/utils/tree_hash/src/lib.rs b/eth2/utils/tree_hash/src/lib.rs index a4037a427..b98559f09 100644 --- a/eth2/utils/tree_hash/src/lib.rs +++ b/eth2/utils/tree_hash/src/lib.rs @@ -45,10 +45,6 @@ pub trait TreeHash { fn tree_hash_root(&self) -> Vec; } -pub trait SignedRoot: TreeHash { - fn signed_root(&self) -> Vec; -} - #[macro_export] macro_rules! tree_hash_ssz_encoding_as_vector { ($type: ident) => { diff --git a/eth2/utils/tree_hash_derive/src/lib.rs b/eth2/utils/tree_hash_derive/src/lib.rs index 2f35ff175..48fe751a2 100644 --- a/eth2/utils/tree_hash_derive/src/lib.rs +++ b/eth2/utils/tree_hash_derive/src/lib.rs @@ -121,58 +121,3 @@ pub fn tree_hash_derive(input: TokenStream) -> TokenStream { }; output.into() } - -#[proc_macro_derive(SignedRoot, attributes(signed_root))] -pub fn tree_hash_signed_root_derive(input: TokenStream) -> TokenStream { - let item = parse_macro_input!(input as DeriveInput); - - let name = &item.ident; - let (impl_generics, ty_generics, where_clause) = &item.generics.split_for_impl(); - - let struct_data = match &item.data { - syn::Data::Struct(s) => s, - _ => panic!("tree_hash_derive only supports structs."), - }; - - let idents = get_signed_root_named_field_idents(&struct_data); - let num_elems = idents.len(); - - let output = quote! { - impl #impl_generics tree_hash::SignedRoot for #name #ty_generics #where_clause { - fn signed_root(&self) -> Vec { - let mut leaves = Vec::with_capacity(#num_elems * tree_hash::HASHSIZE); - - #( - leaves.append(&mut self.#idents.tree_hash_root()); - )* - - tree_hash::merkle_root(&leaves, 0) - } - } - }; - output.into() -} - -fn get_signed_root_named_field_idents(struct_data: &syn::DataStruct) -> Vec<&syn::Ident> { - struct_data - .fields - .iter() - .filter_map(|f| { - if should_skip_signed_root(&f) { - None - } else { - Some(match &f.ident { - Some(ref ident) => ident, - _ => panic!("tree_hash_derive only supports named struct fields"), - }) - } - }) - .collect() -} - -fn should_skip_signed_root(field: &syn::Field) -> bool { - field.attrs.iter().any(|attr| { - attr.path.is_ident("signed_root") - && attr.tts.to_string().replace(" ", "") == "(skip_hashing)" - }) -} diff --git a/lcli/src/deploy_deposit_contract.rs b/lcli/src/deploy_deposit_contract.rs index 76662309a..1dada641f 100644 --- a/lcli/src/deploy_deposit_contract.rs +++ b/lcli/src/deploy_deposit_contract.rs @@ -5,7 +5,7 @@ use eth2_testnet_config::Eth2TestnetConfig; use std::fs::File; use std::io::Read; use std::path::PathBuf; -use types::{ChainSpec, Epoch, EthSpec, Fork, YamlConfig}; +use types::{ChainSpec, EthSpec, YamlConfig}; use web3::{transports::Http, Web3}; pub const SECONDS_PER_ETH1_BLOCK: u64 = 15; @@ -128,13 +128,9 @@ pub fn lighthouse_testnet_spec(mut spec: ChainSpec) -> ChainSpec { // slot of that new chain. // // With a follow distance of 16, this is 40mins. - spec.seconds_per_day = SECONDS_PER_ETH1_BLOCK * spec.eth1_follow_distance * 2 * 5; + spec.min_genesis_delay = SECONDS_PER_ETH1_BLOCK * spec.eth1_follow_distance * 2 * 5; - spec.genesis_fork = Fork { - previous_version: [0, 0, 0, 0], - current_version: [1, 3, 3, 7], - epoch: Epoch::new(0), - }; + spec.genesis_fork_version = [1, 3, 3, 7]; spec } diff --git a/lcli/src/eth1_genesis.rs b/lcli/src/eth1_genesis.rs index 576843ced..51081b884 100644 --- a/lcli/src/eth1_genesis.rs +++ b/lcli/src/eth1_genesis.rs @@ -5,7 +5,7 @@ use futures::Future; use genesis::{Eth1Config, Eth1GenesisService}; use std::path::PathBuf; use std::time::Duration; -use types::{Epoch, EthSpec, Fork}; +use types::EthSpec; /// Interval between polling the eth1 node for genesis information. pub const ETH1_GENESIS_UPDATE_INTERVAL: Duration = Duration::from_millis(7_000); @@ -40,11 +40,7 @@ pub fn run(mut env: Environment, matches: &ArgMatches) -> Result< ) })?; - spec.genesis_fork = Fork { - previous_version: [0, 0, 0, 0], - current_version: [1, 3, 3, 7], - epoch: Epoch::new(0), - }; + spec.genesis_fork_version = [1, 3, 3, 7]; let mut config = Eth1Config::default(); config.endpoint = endpoint.to_string(); diff --git a/lcli/src/interop_genesis.rs b/lcli/src/interop_genesis.rs index 6c0748e59..61055f9b8 100644 --- a/lcli/src/interop_genesis.rs +++ b/lcli/src/interop_genesis.rs @@ -4,7 +4,7 @@ use eth2_testnet_config::Eth2TestnetConfig; use genesis::interop_genesis_state; use std::path::PathBuf; use std::time::{SystemTime, UNIX_EPOCH}; -use types::{test_utils::generate_deterministic_keypairs, Epoch, EthSpec, Fork}; +use types::{test_utils::generate_deterministic_keypairs, EthSpec}; pub fn run(mut env: Environment, matches: &ArgMatches) -> Result<(), String> { let validator_count = matches @@ -49,11 +49,7 @@ pub fn run(mut env: Environment, matches: &ArgMatches) -> Result< ) })?; - spec.genesis_fork = Fork { - previous_version: [0, 0, 0, 0], - current_version: [1, 3, 3, 7], - epoch: Epoch::new(0), - }; + spec.genesis_fork_version = [1, 3, 3, 7]; let keypairs = generate_deterministic_keypairs(validator_count); let genesis_state = interop_genesis_state(&keypairs, genesis_time, &spec)?; diff --git a/lcli/src/transition_blocks.rs b/lcli/src/transition_blocks.rs index fb583eeb5..6a13f7ce1 100644 --- a/lcli/src/transition_blocks.rs +++ b/lcli/src/transition_blocks.rs @@ -4,7 +4,7 @@ use state_processing::{per_block_processing, per_slot_processing, BlockSignature use std::fs::File; use std::io::prelude::*; use std::path::PathBuf; -use types::{BeaconBlock, BeaconState, EthSpec}; +use types::{BeaconState, EthSpec, SignedBeaconBlock}; pub fn run_transition_blocks(matches: &ArgMatches) -> Result<(), String> { let pre_state_path = matches @@ -30,7 +30,7 @@ pub fn run_transition_blocks(matches: &ArgMatches) -> Result<(), Str info!("Block path: {:?}", block_path); let pre_state: BeaconState = load_from_ssz(pre_state_path)?; - let block: BeaconBlock = load_from_ssz(block_path)?; + let block: SignedBeaconBlock = load_from_ssz(block_path)?; let post_state = do_transition(pre_state, block)?; @@ -46,7 +46,7 @@ pub fn run_transition_blocks(matches: &ArgMatches) -> Result<(), Str fn do_transition( mut pre_state: BeaconState, - block: BeaconBlock, + block: SignedBeaconBlock, ) -> Result, String> { let spec = &T::default_spec(); @@ -55,7 +55,7 @@ fn do_transition( .map_err(|e| format!("Unable to build caches: {:?}", e))?; // Transition the parent state to the block slot. - for i in pre_state.slot.as_u64()..block.slot.as_u64() { + for i in pre_state.slot.as_u64()..block.slot().as_u64() { per_slot_processing(&mut pre_state, None, spec) .map_err(|e| format!("Failed to advance slot on iteration {}: {:?}", i, e))?; } diff --git a/tests/ef_tests/Makefile b/tests/ef_tests/Makefile index 660a8b438..d43186f37 100644 --- a/tests/ef_tests/Makefile +++ b/tests/ef_tests/Makefile @@ -1,5 +1,5 @@ # Bump the test tag here and in .gitlab-ci.yml and CI will take care of updating the cached tarballs -TESTS_TAG := v0.9.1 +TESTS_TAG := v0.10.1 TESTS = general minimal mainnet TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS)) diff --git a/tests/ef_tests/src/cases.rs b/tests/ef_tests/src/cases.rs index c5b0d8c4f..818af1a6e 100644 --- a/tests/ef_tests/src/cases.rs +++ b/tests/ef_tests/src/cases.rs @@ -3,12 +3,11 @@ use rayon::prelude::*; use std::fmt::Debug; use std::path::{Path, PathBuf}; -mod bls_aggregate_pubkeys; mod bls_aggregate_sigs; -mod bls_g2_compressed; -mod bls_g2_uncompressed; -mod bls_priv_to_pub; +mod bls_aggregate_verify; +mod bls_fast_aggregate_verify; mod bls_sign_msg; +mod bls_verify_msg; mod common; mod epoch_processing; mod genesis_initialization; @@ -20,12 +19,11 @@ mod shuffling; mod ssz_generic; mod ssz_static; -pub use bls_aggregate_pubkeys::*; pub use bls_aggregate_sigs::*; -pub use bls_g2_compressed::*; -pub use bls_g2_uncompressed::*; -pub use bls_priv_to_pub::*; +pub use bls_aggregate_verify::*; +pub use bls_fast_aggregate_verify::*; pub use bls_sign_msg::*; +pub use bls_verify_msg::*; pub use common::SszStaticType; pub use epoch_processing::*; pub use genesis_initialization::*; diff --git a/tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs b/tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs deleted file mode 100644 index 13c2fea17..000000000 --- a/tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs +++ /dev/null @@ -1,36 +0,0 @@ -use super::*; -use crate::case_result::compare_result; -use crate::cases::common::BlsCase; -use bls::{AggregatePublicKey, PublicKey}; -use serde_derive::Deserialize; - -#[derive(Debug, Clone, Deserialize)] -pub struct BlsAggregatePubkeys { - pub input: Vec, - pub output: String, -} - -impl BlsCase for BlsAggregatePubkeys {} - -impl Case for BlsAggregatePubkeys { - fn result(&self, _case_index: usize) -> Result<(), Error> { - let mut aggregate_pubkey = AggregatePublicKey::new(); - - for key_str in &self.input { - let key = hex::decode(&key_str[2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let key = PublicKey::from_bytes(&key) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - - aggregate_pubkey.add(&key); - } - - let output_bytes = Some( - hex::decode(&self.output[2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?, - ); - let aggregate_pubkey = Ok(aggregate_pubkey.as_raw().as_bytes()); - - compare_result::, Vec>(&aggregate_pubkey, &output_bytes) - } -} diff --git a/tests/ef_tests/src/cases/bls_aggregate_verify.rs b/tests/ef_tests/src/cases/bls_aggregate_verify.rs new file mode 100644 index 000000000..c6a2b2d4f --- /dev/null +++ b/tests/ef_tests/src/cases/bls_aggregate_verify.rs @@ -0,0 +1,59 @@ +use super::*; +use crate::case_result::compare_result; +use crate::cases::common::BlsCase; +use bls::{AggregatePublicKey, AggregateSignature}; +use serde_derive::Deserialize; + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsAggregatePair { + pub pubkey: AggregatePublicKey, + pub message: String, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsAggregateVerifyInput { + pub pairs: Vec, + pub signature: String, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsAggregateVerify { + pub input: BlsAggregateVerifyInput, + pub output: bool, +} + +impl BlsCase for BlsAggregateVerify {} + +impl Case for BlsAggregateVerify { + fn result(&self, _case_index: usize) -> Result<(), Error> { + let messages = self + .input + .pairs + .iter() + .map(|pair| { + hex::decode(&pair.message[2..]) + .map_err(|e| Error::FailedToParseTest(format!("{:?}", e))) + }) + .collect::>, _>>()?; + + let message_refs = messages + .iter() + .map(|x| x.as_slice()) + .collect::>(); + + let pubkey_refs = self + .input + .pairs + .iter() + .map(|p| &p.pubkey) + .collect::>(); + + let signature_ok = hex::decode(&self.input.signature[2..]) + .ok() + .and_then(|bytes: Vec| AggregateSignature::from_bytes(&bytes).ok()) + .map(|signature| signature.verify_multiple(&message_refs, &pubkey_refs)) + .unwrap_or(false); + + compare_result::(&Ok(signature_ok), &Some(self.output)) + } +} diff --git a/tests/ef_tests/src/cases/bls_fast_aggregate_verify.rs b/tests/ef_tests/src/cases/bls_fast_aggregate_verify.rs new file mode 100644 index 000000000..3f204e54f --- /dev/null +++ b/tests/ef_tests/src/cases/bls_fast_aggregate_verify.rs @@ -0,0 +1,50 @@ +use super::*; +use crate::case_result::compare_result; +use crate::cases::common::BlsCase; +use bls::{AggregatePublicKey, AggregateSignature, PublicKey, PublicKeyBytes}; +use serde_derive::Deserialize; +use std::convert::TryInto; + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsFastAggregateVerifyInput { + pub pubkeys: Vec, + pub message: String, + pub signature: String, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsFastAggregateVerify { + pub input: BlsFastAggregateVerifyInput, + pub output: bool, +} + +impl BlsCase for BlsFastAggregateVerify {} + +impl Case for BlsFastAggregateVerify { + fn result(&self, _case_index: usize) -> Result<(), Error> { + let message = hex::decode(&self.input.message[2..]) + .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + + let signature_ok = self + .input + .pubkeys + .iter() + .try_fold( + AggregatePublicKey::new(), + |mut agg, pkb| -> Option { + let pk: Result = pkb.try_into(); + agg.add(&pk.ok()?); + Some(agg) + }, + ) + .and_then(|aggregate_pubkey| { + hex::decode(&self.input.signature[2..]) + .ok() + .and_then(|bytes: Vec| AggregateSignature::from_bytes(&bytes).ok()) + .map(|signature| signature.verify(&message, &aggregate_pubkey)) + }) + .unwrap_or(false); + + compare_result::(&Ok(signature_ok), &Some(self.output)) + } +} diff --git a/tests/ef_tests/src/cases/bls_g2_compressed.rs b/tests/ef_tests/src/cases/bls_g2_compressed.rs deleted file mode 100644 index 1a9f1d561..000000000 --- a/tests/ef_tests/src/cases/bls_g2_compressed.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::*; -use crate::case_result::compare_result; -use crate::cases::common::BlsCase; -use bls::{compress_g2, hash_on_g2}; -use serde_derive::Deserialize; - -#[derive(Debug, Clone, Deserialize)] -pub struct BlsG2CompressedInput { - pub message: String, - pub domain: String, -} - -#[derive(Debug, Clone, Deserialize)] -pub struct BlsG2Compressed { - pub input: BlsG2CompressedInput, - pub output: Vec, -} - -impl BlsCase for BlsG2Compressed {} - -impl Case for BlsG2Compressed { - fn result(&self, _case_index: usize) -> Result<(), Error> { - // Convert message and domain to required types - let msg = hex::decode(&self.input.message[2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let d = hex::decode(&self.input.domain[2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let d = bytes_to_u64(&d); - - // Calculate the point and convert it to compressed bytes - let mut point = hash_on_g2(&msg, d); - let point = compress_g2(&mut point); - - // Convert the output to one set of bytes - let mut decoded = hex::decode(&self.output[0][2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let mut decoded_y = hex::decode(&self.output[1][2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - decoded.append(&mut decoded_y); - - compare_result::, Vec>(&Ok(point), &Some(decoded)) - } -} - -// Converts a vector to u64 (from little endian) -fn bytes_to_u64(array: &[u8]) -> u64 { - let mut bytes = [0u8; 8]; - bytes.copy_from_slice(array); - u64::from_le_bytes(bytes) -} diff --git a/tests/ef_tests/src/cases/bls_g2_uncompressed.rs b/tests/ef_tests/src/cases/bls_g2_uncompressed.rs deleted file mode 100644 index 3eae29967..000000000 --- a/tests/ef_tests/src/cases/bls_g2_uncompressed.rs +++ /dev/null @@ -1,65 +0,0 @@ -use super::*; -use crate::case_result::compare_result; -use crate::cases::common::BlsCase; -use bls::hash_on_g2; -use serde_derive::Deserialize; - -#[derive(Debug, Clone, Deserialize)] -pub struct BlsG2UncompressedInput { - pub message: String, - pub domain: String, -} - -impl BlsCase for BlsG2UncompressedInput {} - -#[derive(Debug, Clone, Deserialize)] -pub struct BlsG2Uncompressed { - pub input: BlsG2UncompressedInput, - pub output: Vec>, -} - -impl Case for BlsG2Uncompressed { - fn result(&self, _case_index: usize) -> Result<(), Error> { - // Convert message and domain to required types - let msg = hex::decode(&self.input.message[2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let d = hex::decode(&self.input.domain[2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let d = bytes_to_u64(&d); - - // Calculate the point and convert it to compressed bytes - let point = hash_on_g2(&msg, d); - let mut point_bytes = [0 as u8; 288]; - point.getpx().geta().tobytearray(&mut point_bytes, 0); - point.getpx().getb().tobytearray(&mut point_bytes, 48); - point.getpy().geta().tobytearray(&mut point_bytes, 96); - point.getpy().getb().tobytearray(&mut point_bytes, 144); - point.getpz().geta().tobytearray(&mut point_bytes, 192); - point.getpz().getb().tobytearray(&mut point_bytes, 240); - - // Convert the output to one set of bytes (x.a, x.b, y.a, y.b, z.a, z.b) - let mut decoded: Vec = vec![]; - for coordinate in &self.output { - let mut decoded_part = hex::decode(&coordinate[0][2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - decoded.append(&mut decoded_part); - decoded_part = hex::decode(&coordinate[1][2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - decoded.append(&mut decoded_part); - } - - compare_result::, Vec>(&Ok(point_bytes.to_vec()), &Some(decoded)) - } -} - -// Converts a vector to u64 (from big endian) -fn bytes_to_u64(array: &[u8]) -> u64 { - let mut result: u64 = 0; - for (i, value) in array.iter().rev().enumerate() { - if i == 8 { - break; - } - result += u64::pow(2, i as u32 * 8) * u64::from(*value); - } - result -} diff --git a/tests/ef_tests/src/cases/bls_priv_to_pub.rs b/tests/ef_tests/src/cases/bls_priv_to_pub.rs deleted file mode 100644 index 016e04dd1..000000000 --- a/tests/ef_tests/src/cases/bls_priv_to_pub.rs +++ /dev/null @@ -1,38 +0,0 @@ -use super::*; -use crate::case_result::compare_result; -use crate::cases::common::BlsCase; -use bls::{PublicKey, SecretKey}; -use serde_derive::Deserialize; - -#[derive(Debug, Clone, Deserialize)] -pub struct BlsPrivToPub { - pub input: String, - pub output: String, -} - -impl BlsCase for BlsPrivToPub {} - -impl Case for BlsPrivToPub { - fn result(&self, _case_index: usize) -> Result<(), Error> { - let secret = &self.input; - - // Convert message and domain to required types - let mut sk = - hex::decode(&secret[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - pad_to_48(&mut sk); - let sk = SecretKey::from_bytes(&sk).unwrap(); - let pk = PublicKey::from_secret_key(&sk); - - let decoded = hex::decode(&self.output[2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - - compare_result::, Vec>(&Ok(pk.as_raw().as_bytes()), &Some(decoded)) - } -} - -// Increase the size of an array to 48 bytes -fn pad_to_48(array: &mut Vec) { - while array.len() < 48 { - array.insert(0, 0); - } -} diff --git a/tests/ef_tests/src/cases/bls_sign_msg.rs b/tests/ef_tests/src/cases/bls_sign_msg.rs index 7ee109f81..53b09e4fd 100644 --- a/tests/ef_tests/src/cases/bls_sign_msg.rs +++ b/tests/ef_tests/src/cases/bls_sign_msg.rs @@ -8,7 +8,6 @@ use serde_derive::Deserialize; pub struct BlsSignInput { pub privkey: String, pub message: String, - pub domain: String, } #[derive(Debug, Clone, Deserialize)] @@ -21,18 +20,15 @@ impl BlsCase for BlsSign {} impl Case for BlsSign { fn result(&self, _case_index: usize) -> Result<(), Error> { - // Convert private_key, message and domain to required types + // Convert private_key and message to required types let mut sk = hex::decode(&self.input.privkey[2..]) .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; pad_to_48(&mut sk); let sk = SecretKey::from_bytes(&sk).unwrap(); let msg = hex::decode(&self.input.message[2..]) .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let d = hex::decode(&self.input.domain[2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let d = bytes_to_u64(&d); - let signature = Signature::new(&msg, d, &sk); + let signature = Signature::new(&msg, &sk); // Convert the output to one set of bytes let decoded = hex::decode(&self.output[2..]) @@ -42,13 +38,6 @@ impl Case for BlsSign { } } -// Converts a vector to u64 (from little endian) -fn bytes_to_u64(array: &[u8]) -> u64 { - let mut bytes = [0u8; 8]; - bytes.copy_from_slice(array); - u64::from_le_bytes(bytes) -} - // Increase the size of an array to 48 bytes fn pad_to_48(array: &mut Vec) { while array.len() < 48 { diff --git a/tests/ef_tests/src/cases/bls_verify_msg.rs b/tests/ef_tests/src/cases/bls_verify_msg.rs new file mode 100644 index 000000000..421006ff8 --- /dev/null +++ b/tests/ef_tests/src/cases/bls_verify_msg.rs @@ -0,0 +1,35 @@ +use super::*; +use crate::case_result::compare_result; +use crate::cases::common::BlsCase; +use bls::{PublicKey, Signature, SignatureBytes}; +use serde_derive::Deserialize; +use std::convert::TryInto; + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsVerifyInput { + pub pubkey: PublicKey, + pub message: String, + pub signature: SignatureBytes, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsVerify { + pub input: BlsVerifyInput, + pub output: bool, +} + +impl BlsCase for BlsVerify {} + +impl Case for BlsVerify { + fn result(&self, _case_index: usize) -> Result<(), Error> { + let message = hex::decode(&self.input.message[2..]) + .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + + let signature_ok = (&self.input.signature) + .try_into() + .map(|signature: Signature| signature.verify(&message, &self.input.pubkey)) + .unwrap_or(false); + + compare_result::(&Ok(signature_ok), &Some(self.output)) + } +} diff --git a/tests/ef_tests/src/cases/operations.rs b/tests/ef_tests/src/cases/operations.rs index 1d5a85c6f..c2af3aaf1 100644 --- a/tests/ef_tests/src/cases/operations.rs +++ b/tests/ef_tests/src/cases/operations.rs @@ -14,7 +14,7 @@ use std::fmt::Debug; use std::path::Path; use types::{ Attestation, AttesterSlashing, BeaconBlock, BeaconState, ChainSpec, Deposit, EthSpec, - ProposerSlashing, VoluntaryExit, + ProposerSlashing, SignedVoluntaryExit, }; #[derive(Debug, Clone, Default, Deserialize)] @@ -95,7 +95,7 @@ impl Operation for ProposerSlashing { } } -impl Operation for VoluntaryExit { +impl Operation for SignedVoluntaryExit { fn handler_name() -> String { "voluntary_exit".into() } @@ -123,13 +123,7 @@ impl Operation for BeaconBlock { state: &mut BeaconState, spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { - Ok(process_block_header( - state, - self, - None, - VerifySignatures::True, - spec, - )?) + Ok(process_block_header(state, self, spec)?) } } diff --git a/tests/ef_tests/src/cases/sanity_blocks.rs b/tests/ef_tests/src/cases/sanity_blocks.rs index acd50ab44..0cc7a02d6 100644 --- a/tests/ef_tests/src/cases/sanity_blocks.rs +++ b/tests/ef_tests/src/cases/sanity_blocks.rs @@ -6,7 +6,7 @@ use serde_derive::Deserialize; use state_processing::{ per_block_processing, per_slot_processing, BlockProcessingError, BlockSignatureStrategy, }; -use types::{BeaconBlock, BeaconState, EthSpec, RelativeEpoch}; +use types::{BeaconState, EthSpec, RelativeEpoch, SignedBeaconBlock}; #[derive(Debug, Clone, Deserialize)] pub struct Metadata { @@ -20,7 +20,7 @@ pub struct Metadata { pub struct SanityBlocks { pub metadata: Metadata, pub pre: BeaconState, - pub blocks: Vec>, + pub blocks: Vec>, pub post: Option>, } @@ -28,7 +28,7 @@ impl LoadCase for SanityBlocks { fn load_from_dir(path: &Path) -> Result { let metadata: Metadata = yaml_decode_file(&path.join("meta.yaml"))?; let pre = ssz_decode_file(&path.join("pre.ssz"))?; - let blocks: Vec> = (0..metadata.blocks_count) + let blocks: Vec> = (0..metadata.blocks_count) .map(|i| { let filename = format!("blocks_{}.ssz", i); ssz_decode_file(&path.join(filename)) @@ -75,7 +75,8 @@ impl Case for SanityBlocks { let result = self .blocks .iter() - .try_for_each(|block| { + .try_for_each(|signed_block| { + let block = &signed_block.message; while bulk_state.slot < block.slot { per_slot_processing(&mut bulk_state, None, spec).unwrap(); per_slot_processing(&mut indiv_state, None, spec).unwrap(); @@ -91,7 +92,7 @@ impl Case for SanityBlocks { per_block_processing( &mut indiv_state, - block, + signed_block, None, BlockSignatureStrategy::VerifyIndividual, spec, @@ -99,7 +100,7 @@ impl Case for SanityBlocks { per_block_processing( &mut bulk_state, - block, + signed_block, None, BlockSignatureStrategy::VerifyBulk, spec, diff --git a/tests/ef_tests/src/cases/ssz_static.rs b/tests/ef_tests/src/cases/ssz_static.rs index 8eee374bb..338ea2645 100644 --- a/tests/ef_tests/src/cases/ssz_static.rs +++ b/tests/ef_tests/src/cases/ssz_static.rs @@ -6,7 +6,6 @@ use cached_tree_hash::{CacheArena, CachedTreeHash}; use serde_derive::Deserialize; use std::fs; use std::marker::PhantomData; -use tree_hash::SignedRoot; use types::Hash256; #[derive(Debug, Clone, Deserialize)] @@ -22,13 +21,6 @@ pub struct SszStatic { value: T, } -#[derive(Debug, Clone)] -pub struct SszStaticSR { - roots: SszStaticRoots, - serialized: Vec, - value: T, -} - #[derive(Debug, Clone)] pub struct SszStaticTHC { roots: SszStaticRoots, @@ -55,16 +47,6 @@ impl LoadCase for SszStatic { } } -impl LoadCase for SszStaticSR { - fn load_from_dir(path: &Path) -> Result { - load_from_dir(path).map(|(roots, serialized, value)| Self { - roots, - serialized, - value, - }) - } -} - impl, C: Debug + Sync> LoadCase for SszStaticTHC { fn load_from_dir(path: &Path) -> Result { load_from_dir(path).map(|(roots, serialized, value)| Self { @@ -105,22 +87,6 @@ impl Case for SszStatic { } } -impl Case for SszStaticSR { - fn result(&self, _case_index: usize) -> Result<(), Error> { - check_serialization(&self.value, &self.serialized)?; - check_tree_hash(&self.roots.root, &self.value.tree_hash_root())?; - check_tree_hash( - &self - .roots - .signing_root - .as_ref() - .expect("signed root exists"), - &self.value.signed_root(), - )?; - Ok(()) - } -} - impl, C: Debug + Sync> Case for SszStaticTHC { fn result(&self, _case_index: usize) -> Result<(), Error> { check_serialization(&self.value, &self.serialized)?; diff --git a/tests/ef_tests/src/handler.rs b/tests/ef_tests/src/handler.rs index df2b6603b..47fb207a5 100644 --- a/tests/ef_tests/src/handler.rs +++ b/tests/ef_tests/src/handler.rs @@ -6,7 +6,6 @@ use std::fmt::Debug; use std::fs; use std::marker::PhantomData; use std::path::PathBuf; -use tree_hash::SignedRoot; use types::EthSpec; pub trait Handler { @@ -75,26 +74,23 @@ macro_rules! bls_handler { }; } +bls_handler!(BlsAggregateSigsHandler, BlsAggregateSigs, "aggregate"); +bls_handler!(BlsSignMsgHandler, BlsSign, "sign"); +bls_handler!(BlsVerifyMsgHandler, BlsVerify, "verify"); bls_handler!( - BlsAggregatePubkeysHandler, - BlsAggregatePubkeys, - "aggregate_pubkeys" + BlsAggregateVerifyHandler, + BlsAggregateVerify, + "aggregate_verify" ); -bls_handler!(BlsAggregateSigsHandler, BlsAggregateSigs, "aggregate_sigs"); bls_handler!( - BlsG2CompressedHandler, - BlsG2Compressed, - "msg_hash_compressed" + BlsFastAggregateVerifyHandler, + BlsFastAggregateVerify, + "fast_aggregate_verify" ); -bls_handler!(BlsPrivToPubHandler, BlsPrivToPub, "priv_to_pub"); -bls_handler!(BlsSignMsgHandler, BlsSign, "sign_msg"); -/// Handler for SSZ types that do not implement `SignedRoot`. +/// Handler for SSZ types. pub struct SszStaticHandler(PhantomData<(T, E)>); -/// Handler for SSZ types that do implement `SignedRoot`. -pub struct SszStaticSRHandler(PhantomData<(T, E)>); - /// Handler for SSZ types that implement `CachedTreeHash`. pub struct SszStaticTHCHandler(PhantomData<(T, C, E)>); @@ -118,26 +114,6 @@ where } } -impl Handler for SszStaticSRHandler -where - T: cases::SszStaticType + SignedRoot + TypeName, - E: TypeName, -{ - type Case = cases::SszStaticSR; - - fn config_name() -> &'static str { - E::name() - } - - fn runner_name() -> &'static str { - "ssz_static" - } - - fn handler_name() -> String { - T::name().into() - } -} - impl Handler for SszStaticTHCHandler where T: cases::SszStaticType + CachedTreeHash + TypeName, diff --git a/tests/ef_tests/src/lib.rs b/tests/ef_tests/src/lib.rs index 2e7b7eb57..ffc847a9c 100644 --- a/tests/ef_tests/src/lib.rs +++ b/tests/ef_tests/src/lib.rs @@ -7,6 +7,7 @@ pub use cases::{ }; pub use error::Error; pub use handler::*; +pub use type_name::TypeName; mod bls_setting; mod case_result; diff --git a/tests/ef_tests/src/type_name.rs b/tests/ef_tests/src/type_name.rs index d0ab1e164..1d8d3e14b 100644 --- a/tests/ef_tests/src/type_name.rs +++ b/tests/ef_tests/src/type_name.rs @@ -52,5 +52,6 @@ type_name_generic!(HistoricalBatch); type_name_generic!(IndexedAttestation); type_name_generic!(PendingAttestation); type_name!(ProposerSlashing); +type_name!(SignedVoluntaryExit); type_name!(Validator); type_name!(VoluntaryExit); diff --git a/tests/ef_tests/tests/tests.rs b/tests/ef_tests/tests/tests.rs index cd9cbcec4..e11e23cbd 100644 --- a/tests/ef_tests/tests/tests.rs +++ b/tests/ef_tests/tests/tests.rs @@ -1,8 +1,31 @@ #![cfg(feature = "ef_tests")] use ef_tests::*; +use std::path::PathBuf; use types::*; +// Check that the config from the Eth2.0 spec tests matches our minimal/mainnet config. +fn config_test() { + let config_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("eth2.0-spec-tests") + .join("tests") + .join(E::name()) + .join("config.yaml"); + let yaml_config = YamlConfig::from_file(&config_path).expect("config file loads OK"); + let spec = E::default_spec(); + assert_eq!(yaml_config.apply_to_chain_spec::(&spec), Some(spec)); +} + +#[test] +fn mainnet_config_ok() { + config_test::(); +} + +#[test] +fn minimal_config_ok() { + config_test::(); +} + #[test] fn shuffling() { ShufflingHandler::::run(); @@ -17,8 +40,8 @@ fn operations_deposit() { #[test] fn operations_exit() { - OperationsHandler::::run(); - OperationsHandler::::run(); + OperationsHandler::::run(); + OperationsHandler::::run(); } #[test] @@ -59,41 +82,37 @@ fn sanity_slots() { #[test] #[cfg(not(feature = "fake_crypto"))] -fn bls_aggregate_pubkeys() { - BlsAggregatePubkeysHandler::run(); -} - -#[test] -#[cfg(not(feature = "fake_crypto"))] -fn bls_aggregate_sigs() { +fn bls_aggregate() { BlsAggregateSigsHandler::run(); } #[test] #[cfg(not(feature = "fake_crypto"))] -fn bls_msg_hash_g2_compressed() { - BlsG2CompressedHandler::run(); -} - -#[test] -#[cfg(not(feature = "fake_crypto"))] -fn bls_priv_to_pub() { - BlsPrivToPubHandler::run(); -} - -#[test] -#[cfg(not(feature = "fake_crypto"))] -fn bls_sign_msg() { +fn bls_sign() { BlsSignMsgHandler::run(); } +#[test] +#[cfg(not(feature = "fake_crypto"))] +fn bls_verify() { + BlsVerifyMsgHandler::run(); +} + +#[test] +#[cfg(not(feature = "fake_crypto"))] +fn bls_aggregate_verify() { + BlsAggregateVerifyHandler::run(); +} + +#[test] +#[cfg(not(feature = "fake_crypto"))] +fn bls_fast_aggregate_verify() { + BlsFastAggregateVerifyHandler::run(); +} + #[cfg(feature = "fake_crypto")] macro_rules! ssz_static_test { - // Signed-root - ($test_name:ident, $typ:ident$(<$generics:tt>)?, SR) => { - ssz_static_test!($test_name, SszStaticSRHandler, $typ$(<$generics>)?); - }; - // Non-signed root, non-tree hash caching + // Non-tree hash caching ($test_name:ident, $typ:ident$(<$generics:tt>)?) => { ssz_static_test!($test_name, SszStaticHandler, $typ$(<$generics>)?); }; @@ -128,15 +147,15 @@ macro_rules! ssz_static_test { #[cfg(feature = "fake_crypto")] mod ssz_static { - use ef_tests::{Handler, SszStaticHandler, SszStaticSRHandler, SszStaticTHCHandler}; + use ef_tests::{Handler, SszStaticHandler, SszStaticTHCHandler}; use types::*; - ssz_static_test!(attestation, Attestation<_>, SR); + ssz_static_test!(attestation, Attestation<_>); ssz_static_test!(attestation_data, AttestationData); ssz_static_test!(attester_slashing, AttesterSlashing<_>); - ssz_static_test!(beacon_block, BeaconBlock<_>, SR); + ssz_static_test!(beacon_block, BeaconBlock<_>); ssz_static_test!(beacon_block_body, BeaconBlockBody<_>); - ssz_static_test!(beacon_block_header, BeaconBlockHeader, SR); + ssz_static_test!(beacon_block_header, BeaconBlockHeader); ssz_static_test!( beacon_state, SszStaticTHCHandler, { @@ -146,17 +165,18 @@ mod ssz_static { ); ssz_static_test!(checkpoint, Checkpoint); ssz_static_test!(deposit, Deposit); - ssz_static_test!(deposit_data, DepositData, SR); + ssz_static_test!(deposit_data, DepositData); ssz_static_test!(eth1_data, Eth1Data); ssz_static_test!(fork, Fork); ssz_static_test!(historical_batch, HistoricalBatch<_>); - ssz_static_test!(indexed_attestation, IndexedAttestation<_>, SR); + ssz_static_test!(indexed_attestation, IndexedAttestation<_>); ssz_static_test!(pending_attestation, PendingAttestation<_>); ssz_static_test!(proposer_slashing, ProposerSlashing); ssz_static_test!(validator, Validator); - ssz_static_test!(voluntary_exit, VoluntaryExit, SR); + ssz_static_test!(voluntary_exit, VoluntaryExit); } +/* NOTE: SSZ generic tests disabled, missing from v0.10.0 #[test] fn ssz_generic() { SszGenericHandler::::run(); @@ -166,6 +186,7 @@ fn ssz_generic() { SszGenericHandler::::run(); SszGenericHandler::::run(); } +*/ #[test] fn epoch_processing_justification_and_finalization() { diff --git a/tests/simulator/src/main.rs b/tests/simulator/src/main.rs index 299779fe1..85be8ccae 100644 --- a/tests/simulator/src/main.rs +++ b/tests/simulator/src/main.rs @@ -214,9 +214,10 @@ fn beacon_chain_sim( spec.milliseconds_per_slot /= speed_up_factor; spec.eth1_follow_distance = 16; - spec.seconds_per_day = eth1_block_time.as_secs() * spec.eth1_follow_distance * 2; + spec.min_genesis_delay = eth1_block_time.as_secs() * spec.eth1_follow_distance * 2; spec.min_genesis_time = 0; spec.min_genesis_active_validator_count = 64; + spec.seconds_per_eth1_block = 1; let slot_duration = Duration::from_millis(spec.milliseconds_per_slot); let initial_validator_count = spec.min_genesis_active_validator_count as usize; diff --git a/validator_client/src/block_service.rs b/validator_client/src/block_service.rs index 78731d18c..14c7043a7 100644 --- a/validator_client/src/block_service.rs +++ b/validator_client/src/block_service.rs @@ -235,15 +235,15 @@ impl BlockService { PublishStatus::Valid => info!( log_1, "Successfully published block"; - "deposits" => block.body.deposits.len(), - "attestations" => block.body.attestations.len(), - "slot" => block.slot.as_u64(), + "deposits" => block.message.body.deposits.len(), + "attestations" => block.message.body.attestations.len(), + "slot" => block.slot().as_u64(), ), PublishStatus::Invalid(msg) => crit!( log_1, "Published block was invalid"; "message" => msg, - "slot" => block.slot.as_u64(), + "slot" => block.slot().as_u64(), ), PublishStatus::Unknown => { crit!(log_1, "Unknown condition when publishing block") diff --git a/validator_client/src/validator_store.rs b/validator_client/src/validator_store.rs index ac7ebef85..8700b5c1e 100644 --- a/validator_client/src/validator_store.rs +++ b/validator_client/src/validator_store.rs @@ -11,9 +11,9 @@ use std::marker::PhantomData; use std::path::PathBuf; use std::sync::Arc; use tempdir::TempDir; -use tree_hash::TreeHash; use types::{ Attestation, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec, Fork, PublicKey, Signature, + SignedBeaconBlock, SignedRoot, }; #[derive(Clone)] @@ -144,26 +144,25 @@ impl ValidatorStore { .get(validator_pubkey) .and_then(|validator_dir| { let voting_keypair = validator_dir.voting_keypair.as_ref()?; - let message = epoch.tree_hash_root(); let domain = self.spec.get_domain(epoch, Domain::Randao, &self.fork()?); + let message = epoch.signing_root(domain); - Some(Signature::new(&message, domain, &voting_keypair.sk)) + Some(Signature::new(message.as_bytes(), &voting_keypair.sk)) }) } pub fn sign_block( &self, validator_pubkey: &PublicKey, - mut block: BeaconBlock, - ) -> Option> { + block: BeaconBlock, + ) -> Option> { // TODO: check for slashing. self.validators .read() .get(validator_pubkey) .and_then(|validator_dir| { let voting_keypair = validator_dir.voting_keypair.as_ref()?; - block.sign(&voting_keypair.sk, &self.fork()?, &self.spec); - Some(block) + Some(block.sign(&voting_keypair.sk, &self.fork()?, &self.spec)) }) }