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 <pawandhananjay@gmail.com>
This commit is contained in:
Michael Sproul 2020-02-11 10:19:36 +11:00 committed by GitHub
parent 03e77390a3
commit 371e5adcf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
145 changed files with 1666 additions and 4437 deletions

21
Cargo.lock generated
View File

@ -80,7 +80,11 @@ dependencies = [
[[package]] [[package]]
name = "amcl" name = "amcl"
version = "0.2.0" 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]] [[package]]
name = "ansi_term" name = "ansi_term"
@ -358,7 +362,7 @@ dependencies = [
"eth2_ssz 0.1.2", "eth2_ssz 0.1.2",
"eth2_ssz_types 0.2.0", "eth2_ssz_types 0.2.0",
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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 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)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1163,7 +1167,7 @@ dependencies = [
"eth2_hashing 0.1.1", "eth2_hashing 0.1.1",
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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 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)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2500,14 +2504,13 @@ dependencies = [
[[package]] [[package]]
name = "milagro_bls" name = "milagro_bls"
version = "0.11.2" version = "1.0.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 = [ 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)", "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)", "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)", "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)", "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)", "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 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 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 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)" = "<none>" "checksum amcl 0.2.0 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)" = "<none>"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "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 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" "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 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 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 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)" = "<none>" "checksum milagro_bls 1.0.0 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)" = "<none>"
"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "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 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" "checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599"

View File

@ -39,7 +39,7 @@ Like all Ethereum 2.0 clients, Lighthouse is a work-in-progress.
Current development overview: 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. - Rust-native libp2p with Gossipsub and Discv5.
- RESTful JSON API via HTTP server. - RESTful JSON API via HTTP server.
- Events via WebSocket. - Events via WebSocket.

View File

@ -166,9 +166,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.store .store
.get_block(&beacon_block_root)? .get_block(&beacon_block_root)?
.ok_or_else(|| Error::MissingBeaconBlock(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 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))?; .ok_or_else(|| Error::MissingBeaconState(beacon_state_root))?;
CheckPoint { CheckPoint {
@ -216,39 +216,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.map(|slot| slot.epoch(T::EthSpec::slots_per_epoch())) .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<Vec<BeaconBlockBody<T::EthSpec>>, Error> {
let bodies: Result<Vec<_>, _> = 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<Vec<BeaconBlockHeader>, Error> {
let headers: Result<Vec<BeaconBlockHeader>, _> = 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 /// 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). /// the earliest reachable ancestor (may or may not be genesis).
/// ///
@ -268,7 +235,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let iter = BlockRootsIterator::owned(self.store.clone(), head.beacon_state); let iter = BlockRootsIterator::owned(self.store.clone(), head.beacon_state);
Ok(ReverseBlockRootIterator::new( Ok(ReverseBlockRootIterator::new(
(head.beacon_block_root, head.beacon_block.slot), (head.beacon_block_root, head.beacon_block.slot()),
iter, iter,
)) ))
} }
@ -305,11 +272,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.get_block(&block_root)? .get_block(&block_root)?
.ok_or_else(|| Error::MissingBeaconBlock(block_root))?; .ok_or_else(|| Error::MissingBeaconBlock(block_root))?;
let state = self let state = self
.get_state(&block.state_root, Some(block.slot))? .get_state(&block.state_root(), Some(block.slot()))?
.ok_or_else(|| Error::MissingBeaconState(block.state_root))?; .ok_or_else(|| Error::MissingBeaconState(block.state_root()))?;
let iter = BlockRootsIterator::owned(self.store.clone(), state); let iter = BlockRootsIterator::owned(self.store.clone(), state);
Ok(ReverseBlockRootIterator::new( Ok(ReverseBlockRootIterator::new(
(block_root, block.slot), (block_root, block.slot()),
iter, iter,
)) ))
} }
@ -349,24 +316,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
)) ))
} }
/// 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<Option<BeaconBlock<T::EthSpec>>, Error> {
Ok(self.store.get(&block_root)?)
}
/// Returns the block at the given slot, if any. Only returns blocks in the canonical chain. /// Returns the block at the given slot, if any. Only returns blocks in the canonical chain.
/// ///
/// ## Errors /// ## Errors
/// ///
/// May return a database error. /// May return a database error.
pub fn block_at_slot(&self, slot: Slot) -> Result<Option<BeaconBlock<T::EthSpec>>, Error> { pub fn block_at_slot(
&self,
slot: Slot,
) -> Result<Option<SignedBeaconBlock<T::EthSpec>>, Error> {
let root = self let root = self
.rev_iter_block_roots()? .rev_iter_block_roots()?
.find(|(_, this_slot)| *this_slot == slot) .find(|(_, this_slot)| *this_slot == slot)
@ -387,7 +345,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
pub fn get_block( pub fn get_block(
&self, &self,
block_root: &Hash256, block_root: &Hash256,
) -> Result<Option<BeaconBlock<T::EthSpec>>, Error> { ) -> Result<Option<SignedBeaconBlock<T::EthSpec>>, Error> {
Ok(self.store.get_block(block_root)?) Ok(self.store.get_block(block_root)?)
} }
@ -448,7 +406,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.ok_or_else(|| Error::CanonicalHeadLockTimeout)?; .ok_or_else(|| Error::CanonicalHeadLockTimeout)?;
Ok(HeadInfo { Ok(HeadInfo {
slot: head.beacon_block.slot, slot: head.beacon_block.slot(),
block_root: head.beacon_block_root, block_root: head.beacon_block_root,
state_root: head.beacon_state_root, state_root: head.beacon_state_root,
finalized_checkpoint: head.beacon_state.finalized_checkpoint.clone(), finalized_checkpoint: head.beacon_state.finalized_checkpoint.clone(),
@ -550,7 +508,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
pub fn best_slot(&self) -> Result<Slot, Error> { pub fn best_slot(&self) -> Result<Slot, Error> {
self.canonical_head self.canonical_head
.try_read_for(HEAD_LOCK_TIMEOUT) .try_read_for(HEAD_LOCK_TIMEOUT)
.map(|head| head.beacon_block.slot) .map(|head| head.beacon_block.slot())
.ok_or_else(|| Error::CanonicalHeadLockTimeout) .ok_or_else(|| Error::CanonicalHeadLockTimeout)
} }
@ -576,19 +534,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.map(|(root, _slot)| root)) .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<SlotHeight> {
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. /// Returns the block proposer for a given slot.
/// ///
/// Information is read from the present `beacon_state` shuffling, only information from the /// Information is read from the present `beacon_state` shuffling, only information from the
@ -669,7 +614,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let data = self.produce_attestation_data_for_block( let data = self.produce_attestation_data_for_block(
index, index,
head.beacon_block_root, head.beacon_block_root,
head.beacon_block.slot, head.beacon_block.slot(),
&state, &state,
)?; )?;
@ -696,7 +641,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.produce_attestation_data_for_block( self.produce_attestation_data_for_block(
index, index,
head.beacon_block_root, head.beacon_block_root,
head.beacon_block.slot, head.beacon_block.slot(),
&state, &state,
) )
} }
@ -859,22 +804,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// the attestation points to a block in a prior epoch, then it is necessary to // 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 // load the full state corresponding to its block, and transition it to the
// attestation's epoch. // attestation's epoch.
let attestation_block_root = attestation_head_block.state_root();
let attestation_epoch = attestation.data.target.epoch; let attestation_epoch = attestation.data.target.epoch;
let slots_per_epoch = T::EthSpec::slots_per_epoch(); let slots_per_epoch = T::EthSpec::slots_per_epoch();
let mut state = if attestation_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 self.store
.load_epoch_boundary_state(&attestation_head_block.state_root)? .load_epoch_boundary_state(&attestation_block_root)?
.ok_or_else(|| Error::MissingBeaconState(attestation_head_block.state_root))? .ok_or_else(|| Error::MissingBeaconState(attestation_block_root))?
} else { } else {
let mut state = self let mut state = self
.store .store
.get_state( .get_state(&attestation_block_root, Some(attestation_head_block.slot()))?
&attestation_head_block.state_root, .ok_or_else(|| Error::MissingBeaconState(attestation_block_root))?;
Some(attestation_head_block.slot),
)?
.ok_or_else(|| Error::MissingBeaconState(attestation_head_block.state_root))?;
// Fastforward the state to the epoch in which the attestation was made. // Fastforward the state to the epoch in which the attestation was made.
// NOTE: this looks like a potential DoS vector, we should probably limit // NOTE: this looks like a potential DoS vector, we should probably limit
@ -902,7 +845,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.process_attestation_for_state_and_block( self.process_attestation_for_state_and_block(
attestation, attestation,
&state, &state,
&attestation_head_block, &attestation_head_block.message,
) )
} }
} else { } else {
@ -1045,7 +988,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
} }
/// Accept some exit and queue it for inclusion in an appropriate block. /// 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() { match self.wall_clock_state() {
Ok(state) => self.op_pool.insert_voluntary_exit(exit, &state, &self.spec), Ok(state) => self.op_pool.insert_voluntary_exit(exit, &state, &self.spec),
Err(e) => { Err(e) => {
@ -1109,7 +1055,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// Will accept blocks from prior slots, however it will reject any block from a future slot. /// Will accept blocks from prior slots, however it will reject any block from a future slot.
pub fn process_block( pub fn process_block(
&self, &self,
block: BeaconBlock<T::EthSpec>, block: SignedBeaconBlock<T::EthSpec>,
) -> Result<BlockProcessingOutcome, Error> { ) -> Result<BlockProcessingOutcome, Error> {
let outcome = self.process_block_internal(block.clone()); let outcome = self.process_block_internal(block.clone());
@ -1120,7 +1066,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.log, self.log,
"Beacon block imported"; "Beacon block imported";
"block_root" => format!("{:?}", block_root), "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 { let _ = self.event_handler.register(EventKind::BeaconBlockImported {
block_root: *block_root, block_root: *block_root,
@ -1160,11 +1106,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// Will accept blocks from prior slots, however it will reject any block from a future slot. /// Will accept blocks from prior slots, however it will reject any block from a future slot.
fn process_block_internal( fn process_block_internal(
&self, &self,
block: BeaconBlock<T::EthSpec>, signed_block: SignedBeaconBlock<T::EthSpec>,
) -> Result<BlockProcessingOutcome, Error> { ) -> Result<BlockProcessingOutcome, Error> {
metrics::inc_counter(&metrics::BLOCK_PROCESSING_REQUESTS); metrics::inc_counter(&metrics::BLOCK_PROCESSING_REQUESTS);
let full_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_TIMES); let full_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_TIMES);
let block = &signed_block.message;
let finalized_slot = self let finalized_slot = self
.head_info()? .head_info()?
.finalized_checkpoint .finalized_checkpoint
@ -1246,9 +1194,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// Load the parent blocks state from the database, returning an error if it is not found. // 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. // 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 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(|| { .ok_or_else(|| {
Error::DBInconsistent(format!("Missing state {:?}", parent_state_root)) Error::DBInconsistent(format!("Missing state {:?}", parent_state_root))
})?; })?;
@ -1268,7 +1216,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let distance = block.slot.as_u64().saturating_sub(state.slot.as_u64()); let distance = block.slot.as_u64().saturating_sub(state.slot.as_u64());
for i in 0..distance { for i in 0..distance {
let state_root = if i == 0 { let state_root = if i == 0 {
parent_block.state_root parent_block.state_root()
} else { } else {
// This is a new state we've reached, so stage it for storage in the DB. // 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 // Computing the state root here is time-equivalent to computing it during slot
@ -1302,7 +1250,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// slot). // slot).
match per_block_processing( match per_block_processing(
&mut state, &mut state,
&block, &signed_block,
Some(block_root), Some(block_root),
BlockSignatureStrategy::VerifyBulk, BlockSignatureStrategy::VerifyBulk,
&self.spec, &self.spec,
@ -1372,7 +1320,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// settles down). // settles down).
// See: https://github.com/sigp/lighthouse/issues/692 // See: https://github.com/sigp/lighthouse/issues/692
self.store.put_state(&state_root, state)?; 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); metrics::stop_timer(db_write_timer);
@ -1450,26 +1398,28 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.deposits_for_block_inclusion(&state, &eth1_data, &self.spec)? .deposits_for_block_inclusion(&state, &eth1_data, &self.spec)?
.into(); .into();
let mut block = BeaconBlock { let mut block = SignedBeaconBlock {
slot: state.slot, message: BeaconBlock {
parent_root, slot: state.slot,
state_root: Hash256::zero(), 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. // The block is not signed here, that is the task of a validator client.
signature: Signature::empty_signature(), 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( per_block_processing(
@ -1482,7 +1432,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let state_root = state.update_tree_hash_cache()?; 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::inc_counter(&metrics::BLOCK_PRODUCTION_SUCCESSES);
metrics::stop_timer(timer); metrics::stop_timer(timer);
@ -1490,12 +1440,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
trace!( trace!(
self.log, self.log,
"Produced beacon block"; "Produced beacon block";
"parent" => format!("{}", block.parent_root), "parent" => format!("{}", block.message.parent_root),
"attestations" => block.body.attestations.len(), "attestations" => block.message.body.attestations.len(),
"slot" => block.slot "slot" => block.message.slot
); );
Ok((block, state)) Ok((block.message, state))
} }
/// Execute the fork choice algorithm and enthrone the result as the canonical head. /// Execute the fork choice algorithm and enthrone the result as the canonical head.
@ -1516,13 +1466,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.get_block(&beacon_block_root)? .get_block(&beacon_block_root)?
.ok_or_else(|| Error::MissingBeaconBlock(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<T::EthSpec> = self let beacon_state: BeaconState<T::EthSpec> = 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))?; .ok_or_else(|| Error::MissingBeaconState(beacon_state_root))?;
let previous_slot = self.head_info()?.slot; 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 // 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 // between calls to fork choice without swapping between chains. This seems like an
@ -1541,7 +1491,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
"Beacon chain re-org"; "Beacon chain re-org";
"previous_head" => format!("{}", self.head_info()?.block_root), "previous_head" => format!("{}", self.head_info()?.block_root),
"previous_slot" => previous_slot, "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_head" => format!("{}", beacon_block_root),
"new_slot" => new_slot "new_slot" => new_slot
); );
@ -1636,7 +1586,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let finalized_block = self let finalized_block = self
.store .store
.get_block(&finalized_block_root)? .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()); let new_finalized_epoch = finalized_block.slot.epoch(T::EthSpec::slots_per_epoch());
@ -1678,7 +1629,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
pub fn is_new_block_root(&self, beacon_block_root: &Hash256) -> Result<bool, Error> { pub fn is_new_block_root(&self, beacon_block_root: &Hash256) -> Result<bool, Error> {
Ok(!self Ok(!self
.store .store
.exists::<BeaconBlock<T::EthSpec>>(beacon_block_root)?) .exists::<SignedBeaconBlock<T::EthSpec>>(beacon_block_root)?)
} }
/// Dumps the entire canonical chain, from the head to genesis to a vector for analysis. /// Dumps the entire canonical chain, from the head to genesis to a vector for analysis.
@ -1698,20 +1649,19 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
dump.push(last_slot.clone()); dump.push(last_slot.clone());
loop { 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() { if beacon_block_root == Hash256::zero() {
break; // Genesis has been reached. break; // Genesis has been reached.
} }
let beacon_block: BeaconBlock<T::EthSpec> = let beacon_block = self.store.get_block(&beacon_block_root)?.ok_or_else(|| {
self.store.get(&beacon_block_root)?.ok_or_else(|| { Error::DBInconsistent(format!("Missing block {}", beacon_block_root))
Error::DBInconsistent(format!("Missing block {}", beacon_block_root)) })?;
})?; let beacon_state_root = beacon_block.state_root();
let beacon_state_root = beacon_block.state_root;
let beacon_state = self let beacon_state = self
.store .store
.get_state(&beacon_state_root, Some(beacon_block.slot))? .get_state(&beacon_state_root, Some(beacon_block.slot()))?
.ok_or_else(|| { .ok_or_else(|| {
Error::DBInconsistent(format!("Missing state {:?}", beacon_state_root)) Error::DBInconsistent(format!("Missing state {:?}", beacon_state_root))
})?; })?;

View File

@ -16,7 +16,9 @@ use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use store::Store; 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 /// An empty struct used to "witness" all the `BeaconChainTypes` traits. It has no user-facing
/// functionality and only exists to satisfy the type system. /// functionality and only exists to satisfy the type system.
@ -205,14 +207,13 @@ where
.clone() .clone()
.ok_or_else(|| "genesis_state requires a store")?; .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 beacon_state
.build_all_caches(&self.spec) .build_all_caches(&self.spec)
.map_err(|e| format!("Failed to build genesis state caches: {:?}", e))?; .map_err(|e| format!("Failed to build genesis state caches: {:?}", e))?;
let beacon_state_root = beacon_state.canonical_root(); let beacon_state_root = beacon_block.message.state_root;
beacon_block.state_root = beacon_state_root;
let beacon_block_root = beacon_block.canonical_root(); let beacon_block_root = beacon_block.canonical_root();
self.genesis_block_root = Some(beacon_block_root); self.genesis_block_root = Some(beacon_block_root);
@ -303,7 +304,7 @@ where
.build_all_caches(&self.spec) .build_all_caches(&self.spec)
.map_err(|e| format!("Failed to build state caches: {:?}", e))?; .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()); return Err("beacon_block.state_root != beacon_state".to_string());
} }
@ -345,7 +346,7 @@ where
"Beacon chain initialized"; "Beacon chain initialized";
"head_state" => format!("{}", head.beacon_state_root), "head_state" => format!("{}", head.beacon_state_root),
"head_block" => format!("{}", head.beacon_block_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) Ok(beacon_chain)
@ -382,7 +383,7 @@ where
.ok_or_else(|| "fork_choice_backend requires a genesis_block_root")?; .ok_or_else(|| "fork_choice_backend requires a genesis_block_root")?;
let backend = ProtoArrayForkChoice::new( 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 // 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 // finalized checkpoint. Whilst this finalized checkpoint may actually point to
// a _later_ justified checkpoint, that checkpoint won't yet exist in the fork // a _later_ justified checkpoint, that checkpoint won't yet exist in the fork
@ -512,12 +513,20 @@ where
} }
} }
fn genesis_block<T: EthSpec>(genesis_state: &BeaconState<T>, spec: &ChainSpec) -> BeaconBlock<T> { fn genesis_block<T: EthSpec>(
let mut genesis_block = BeaconBlock::empty(&spec); genesis_state: &mut BeaconState<T>,
spec: &ChainSpec,
genesis_block.state_root = genesis_state.canonical_root(); ) -> Result<SignedBeaconBlock<T>, String> {
let mut genesis_block = SignedBeaconBlock {
genesis_block 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)] #[cfg(test)]
@ -581,14 +590,14 @@ mod test {
"should have the correct genesis time" "should have the correct genesis time"
); );
assert_eq!( assert_eq!(
block.state_root, block.state_root(),
state.canonical_root(), state.canonical_root(),
"block should have correct state root" "block should have correct state root"
); );
assert_eq!( assert_eq!(
chain chain
.store .store
.get::<BeaconBlock<_>>(&Hash256::zero()) .get_block(&Hash256::zero())
.expect("should read db") .expect("should read db")
.expect("should find genesis block"), .expect("should find genesis block"),
block, block,

View File

@ -1,12 +1,12 @@
use serde_derive::Serialize; use serde_derive::Serialize;
use ssz_derive::{Decode, Encode}; 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. /// head, justified head and finalized head.
#[derive(Clone, Serialize, PartialEq, Debug, Encode, Decode)] #[derive(Clone, Serialize, PartialEq, Debug, Encode, Decode)]
pub struct CheckPoint<E: EthSpec> { pub struct CheckPoint<E: EthSpec> {
pub beacon_block: BeaconBlock<E>, pub beacon_block: SignedBeaconBlock<E>,
pub beacon_block_root: Hash256, pub beacon_block_root: Hash256,
pub beacon_state: BeaconState<E>, pub beacon_state: BeaconState<E>,
pub beacon_state_root: Hash256, pub beacon_state_root: Hash256,
@ -15,7 +15,7 @@ pub struct CheckPoint<E: EthSpec> {
impl<E: EthSpec> CheckPoint<E> { impl<E: EthSpec> CheckPoint<E> {
/// Create a new checkpoint. /// Create a new checkpoint.
pub fn new( pub fn new(
beacon_block: BeaconBlock<E>, beacon_block: SignedBeaconBlock<E>,
beacon_block_root: Hash256, beacon_block_root: Hash256,
beacon_state: BeaconState<E>, beacon_state: BeaconState<E>,
beacon_state_root: Hash256, beacon_state_root: Hash256,
@ -31,7 +31,7 @@ impl<E: EthSpec> CheckPoint<E> {
/// Update all fields of the checkpoint. /// Update all fields of the checkpoint.
pub fn update( pub fn update(
&mut self, &mut self,
beacon_block: BeaconBlock<E>, beacon_block: SignedBeaconBlock<E>,
beacon_block_root: Hash256, beacon_block_root: Hash256,
beacon_state: BeaconState<E>, beacon_state: BeaconState<E>,
beacon_state_root: Hash256, beacon_state_root: Hash256,

View File

@ -3,15 +3,12 @@ use eth1::{Config as Eth1Config, Eth1Block, Service as HttpService};
use eth2_hashing::hash; use eth2_hashing::hash;
use exit_future::Exit; use exit_future::Exit;
use futures::Future; use futures::Future;
use integer_sqrt::IntegerSquareRoot; use slog::{debug, error, trace, Logger};
use rand::prelude::*;
use slog::{crit, debug, error, trace, Logger};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use state_processing::per_block_processing::get_new_eth1_data; use state_processing::per_block_processing::get_new_eth1_data;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use std::iter::DoubleEndedIterator; use std::iter::DoubleEndedIterator;
use std::iter::FromIterator;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use store::{Error as StoreError, Store}; use store::{Error as StoreError, Store};
@ -21,7 +18,6 @@ use types::{
}; };
type BlockNumber = u64; type BlockNumber = u64;
type Eth1DataBlockNumber = HashMap<Eth1Data, BlockNumber>;
type Eth1DataVoteCount = HashMap<(Eth1Data, BlockNumber), u64>; type Eth1DataVoteCount = HashMap<(Eth1Data, BlockNumber), u64>;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -280,12 +276,7 @@ impl<T: EthSpec, S: Store<T>> CachingEth1Backend<T, S> {
impl<T: EthSpec, S: Store<T>> Eth1ChainBackend<T, S> for CachingEth1Backend<T, S> { impl<T: EthSpec, S: Store<T>> Eth1ChainBackend<T, S> for CachingEth1Backend<T, S> {
fn eth1_data(&self, state: &BeaconState<T>, spec: &ChainSpec) -> Result<Eth1Data, Error> { fn eth1_data(&self, state: &BeaconState<T>, spec: &ChainSpec) -> Result<Eth1Data, Error> {
// 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 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_slot = (state.slot / period) * period;
let voting_period_start_seconds = slot_start_seconds::<T>( let voting_period_start_seconds = slot_start_seconds::<T>(
state.genesis_time, state.genesis_time,
@ -295,82 +286,54 @@ impl<T: EthSpec, S: Store<T>> Eth1ChainBackend<T, S> for CachingEth1Backend<T, S
let blocks = self.core.blocks().read(); let blocks = self.core.blocks().read();
let (new_eth1_data, all_eth1_data) = if let Some(sets) = eth1_data_sets( let votes_to_consider =
blocks.iter(), get_votes_to_consider(blocks.iter(), voting_period_start_seconds, spec);
prev_eth1_hash,
voting_period_start_seconds,
spec,
&self.log,
) {
sets
} else {
// The algorithm was unable to find the `new_eth1_data` and `all_eth1_data` sets.
//
// This is likely because the caches are empty or the previous eth1 block hash is not
// in the cache.
//
// This situation can also be caused when a testnet does not have an adequate delay
// between the eth1 genesis block and the eth2 genesis block. This delay needs to be at
// least `2 * ETH1_FOLLOW_DISTANCE`.
crit!(
self.log,
"Unable to find eth1 data sets";
"lowest_block_number" => 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());
};
trace!( trace!(
self.log, self.log,
"Found eth1 data sets"; "Found eth1 data votes_to_consider";
"all_eth1_data" => all_eth1_data.len(), "votes_to_consider" => votes_to_consider.len(),
"new_eth1_data" => new_eth1_data.len(),
); );
let valid_votes = collect_valid_votes(state, &votes_to_consider);
let valid_votes = collect_valid_votes(state, new_eth1_data, all_eth1_data);
let eth1_data = if let Some(eth1_data) = find_winning_vote(valid_votes) { let eth1_data = if let Some(eth1_data) = find_winning_vote(valid_votes) {
eth1_data eth1_data
} else { } else {
// In this case, there are no other viable votes (perhaps there are no votes yet or all // In this case, there are no valid votes available.
// the existing votes are junk).
// //
// Here we choose the latest block in our voting window. // Here we choose the eth1_data corresponding to the latest block in our voting window.
blocks // If no votes exist, choose `state.eth1_data` as default vote.
let default_vote = votes_to_consider
.iter() .iter()
.rev() .max_by(|(_, x), (_, y)| x.cmp(y))
.skip_while(|eth1_block| eth1_block.timestamp > voting_period_start_seconds) .map(|vote| {
.nth(eth1_follow_distance as usize) let vote = vote.0.clone();
.map(|block| { debug!(
trace!(
self.log, self.log,
"Choosing default eth1_data"; "No valid eth1_data votes";
"eth1_block_number" => block.number, "outcome" => "Casting vote corresponding to last candidate eth1 block",
"eth1_block_hash" => format!("{:?}", block.hash),
); );
vote
block
}) })
.and_then(|block| block.clone().eth1_data())
.unwrap_or_else(|| { .unwrap_or_else(|| {
crit!( let vote = state.eth1_data.clone();
error!(
self.log, self.log,
"Unable to find a winning eth1 vote"; "No valid eth1_data votes, `votes_to_consider` empty";
"outcome" => "casting random eth1 vote" "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"
); );
metrics::inc_counter(&metrics::DEFAULT_ETH1_VOTES);
random_eth1_data() vote
}) });
default_vote
}; };
debug!( debug!(
self.log, self.log,
"Produced vote for eth1 chain"; "Produced vote for eth1 chain";
"is_period_tail" => is_period_tail(state),
"deposit_root" => format!("{:?}", eth1_data.deposit_root), "deposit_root" => format!("{:?}", eth1_data.deposit_root),
"deposit_count" => eth1_data.deposit_count, "deposit_count" => eth1_data.deposit_count,
"block_hash" => format!("{:?}", eth1_data.block_hash), "block_hash" => format!("{:?}", eth1_data.block_hash),
@ -432,139 +395,47 @@ impl<T: EthSpec, S: Store<T>> Eth1ChainBackend<T, S> for CachingEth1Backend<T, S
} }
} }
/// Produces an `Eth1Data` with all fields sourced from `rand::thread_rng()`. /// Get all votes from eth1 blocks which are in the list of candidate blocks for the
fn random_eth1_data() -> Eth1Data { /// current eth1 voting period.
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<T: EthSpec, S: Store<T>>(
store: Arc<S>,
state: &BeaconState<T>,
) -> Result<Hash256, Error> {
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<E: EthSpec>(state: &BeaconState<E>) -> 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.
/// ///
/// `prev_eth1_hash` is the `eth1_data.block_hash` at the start of the voting period defined by /// Returns a hashmap of `Eth1Data` to its associated eth1 `block_number`.
/// `state.slot`. fn get_votes_to_consider<'a, I>(
fn eth1_data_sets<'a, I>(
blocks: I, blocks: I,
prev_eth1_hash: Hash256,
voting_period_start_seconds: u64, voting_period_start_seconds: u64,
spec: &ChainSpec, spec: &ChainSpec,
log: &Logger, ) -> HashMap<Eth1Data, u64>
) -> Option<(Eth1DataBlockNumber, Eth1DataBlockNumber)>
where where
I: DoubleEndedIterator<Item = &'a Eth1Block> + Clone, I: DoubleEndedIterator<Item = &'a Eth1Block> + Clone,
{ {
let eth1_follow_distance = spec.eth1_follow_distance; blocks
let in_scope_eth1_data = blocks
.rev() .rev()
.skip_while(|eth1_block| eth1_block.timestamp > voting_period_start_seconds) .skip_while(|eth1_block| !is_candidate_block(eth1_block, voting_period_start_seconds, spec))
.skip(eth1_follow_distance as usize) .take_while(|eth1_block| is_candidate_block(eth1_block, voting_period_start_seconds, spec))
.filter_map(|block| Some((block.clone().eth1_data()?, block.number))); .filter_map(|eth1_block| {
eth1_block
if in_scope_eth1_data .clone()
.clone() .eth1_data()
.any(|(eth1_data, _)| eth1_data.block_hash == prev_eth1_hash) .map(|eth1_data| (eth1_data, eth1_block.number))
{ })
let new_eth1_data = in_scope_eth1_data .collect()
.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
}
} }
/// Selects and counts the votes in `state.eth1_data_votes`, if they appear in `new_eth1_data` or /// Collect all valid votes that are cast during the current voting period.
/// `all_eth1_data` when it is the voting period tail. /// Return hashmap with count of each vote cast.
fn collect_valid_votes<T: EthSpec>( fn collect_valid_votes<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
new_eth1_data: Eth1DataBlockNumber, votes_to_consider: &HashMap<Eth1Data, BlockNumber>,
all_eth1_data: Eth1DataBlockNumber,
) -> Eth1DataVoteCount { ) -> Eth1DataVoteCount {
let mut valid_votes = HashMap::new(); let mut valid_votes = HashMap::new();
state state
.eth1_data_votes .eth1_data_votes
.iter() .iter()
.filter_map(|vote| { .filter_map(|vote| {
new_eth1_data if let Some(block_num) = votes_to_consider.get(vote) {
.get(vote) Some((vote.clone(), *block_num))
.map(|block_number| (vote.clone(), *block_number)) } else {
.or_else(|| { None
if is_period_tail(state) { }
all_eth1_data
.get(vote)
.map(|block_number| (vote.clone(), *block_number))
} else {
None
}
})
}) })
.for_each(|(eth1_data, block_number)| { .for_each(|(eth1_data, block_number)| {
valid_votes valid_votes
@ -572,19 +443,9 @@ fn collect_valid_votes<T: EthSpec>(
.and_modify(|count| *count += 1) .and_modify(|count| *count += 1)
.or_insert(1_u64); .or_insert(1_u64);
}); });
valid_votes 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<E: EthSpec>(state: &BeaconState<E>) -> 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`. /// Selects the winning vote from `valid_votes`.
fn find_winning_vote(valid_votes: Eth1DataVoteCount) -> Option<Eth1Data> { fn find_winning_vote(valid_votes: Eth1DataVoteCount) -> Option<Eth1Data> {
valid_votes valid_votes
@ -609,10 +470,24 @@ fn slot_start_seconds<T: EthSpec>(
genesis_unix_seconds + slot.as_u64() * milliseconds_per_slot / 1_000 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)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use environment::null_logger; use environment::null_logger;
use std::iter::FromIterator;
use types::{test_utils::DepositTestTask, MinimalEthSpec}; use types::{test_utils::DepositTestTask, MinimalEthSpec};
type E = MinimalEthSpec; type E = MinimalEthSpec;
@ -625,9 +500,14 @@ mod test {
} }
} }
#[test] fn get_voting_period_start_seconds(state: &BeaconState<E>, spec: &ChainSpec) -> u64 {
fn random_eth1_data_doesnt_panic() { let period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
random_eth1_data(); let voting_period_start_slot = (state.slot / period) * period;
slot_start_seconds::<E>(
state.genesis_time,
spec.milliseconds_per_slot,
voting_period_start_slot,
)
} }
#[test] #[test]
@ -709,7 +589,7 @@ mod test {
assert!( assert!(
eth1_chain eth1_chain
.deposits_for_block_inclusion(&state, &random_eth1_data(), spec) .deposits_for_block_inclusion(&state, &Eth1Data::default(), spec)
.is_ok(), .is_ok(),
"should succeed if cache is empty but no deposits are required" "should succeed if cache is empty but no deposits are required"
); );
@ -718,7 +598,7 @@ mod test {
assert!( assert!(
eth1_chain eth1_chain
.deposits_for_block_inclusion(&state, &random_eth1_data(), spec) .deposits_for_block_inclusion(&state, &Eth1Data::default(), spec)
.is_err(), .is_err(),
"should fail to get deposits if required, but cache is empty" "should fail to get deposits if required, but cache is empty"
); );
@ -762,7 +642,7 @@ mod test {
assert!( assert!(
eth1_chain eth1_chain
.deposits_for_block_inclusion(&state, &random_eth1_data(), spec) .deposits_for_block_inclusion(&state, &Eth1Data::default(), spec)
.is_ok(), .is_ok(),
"should succeed if no deposits are required" "should succeed if no deposits are required"
); );
@ -774,7 +654,7 @@ mod test {
state.eth1_data.deposit_count = i as u64; state.eth1_data.deposit_count = i as u64;
let deposits_for_inclusion = eth1_chain 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)); .unwrap_or_else(|_| panic!("should find deposit for {}", i));
let expected_len = let expected_len =
@ -822,24 +702,20 @@ mod test {
let a = eth1_chain let a = eth1_chain
.eth1_data_for_block_production(&state, &spec) .eth1_data_for_block_production(&state, &spec)
.expect("should produce first random eth1 data"); .expect("should produce default eth1 data vote");
let b = eth1_chain assert_eq!(
.eth1_data_for_block_production(&state, &spec) a, state.eth1_data,
.expect("should produce second random eth1 data"); "default vote should be same as state.eth1_data"
assert!(
a != b,
"random votes should be returned with an empty cache"
); );
} }
#[test] #[test]
fn eth1_data_unknown_previous_state() { fn default_vote() {
let spec = &E::default_spec(); let spec = &E::default_spec();
let period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64(); let slots_per_eth1_voting_period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
let eth1_follow_distance = spec.eth1_follow_distance;
let eth1_chain = get_eth1_chain(); let eth1_chain = get_eth1_chain();
let store = eth1_chain.backend.store.clone();
assert_eq!( assert_eq!(
eth1_chain.use_dummy_backend, false, eth1_chain.use_dummy_backend, false,
@ -847,120 +723,41 @@ mod test {
); );
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), &spec); let mut state: BeaconState<E> = 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::from(slots_per_eth1_voting_period * 10);
state.slot = Slot::new(period * 1_000 + period / 2); 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. // Populate blocks cache with candidate eth1 blocks
for _ in 0..=period / 2 { let blocks = (start_eth1_block..end_eth1_block)
state .map(|i| get_eth1_block(i, i))
.eth1_data_votes .collect::<Vec<_>>();
.push(random_eth1_data())
.expect("should push eth1 vote");
}
(0..2048).for_each(|i| { blocks.iter().for_each(|block| {
eth1_chain eth1_chain
.backend .backend
.core .core
.blocks() .blocks()
.write() .write()
.insert_root_or_child(get_eth1_block(i, i)) .insert_root_or_child(block.clone())
.expect("should add blocks to cache"); .expect("should add blocks to cache");
}); });
let expected_root = Hash256::from_low_u64_be(u64::max_value()); let vote = eth1_chain
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
.eth1_data_for_block_production(&state, &spec) .eth1_data_for_block_production(&state, &spec)
.expect("should produce first random eth1 data"); .expect("should produce default eth1 data vote");
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<E> = BeaconState::new(0, get_eth1_data(0), spec);
assert_eq!( assert_eq!(
eth1_block_hash_at_start_of_voting_period(store, &state), vote,
Ok(state.eth1_data.block_hash), blocks
"should return the states eth1 data in the first half of the period" .last()
); .expect("should have blocks")
} .clone()
.eth1_data()
#[test] .expect("should have valid eth1 data"),
fn with_store_lookup() { "default vote must correspond to last block in candidate blocks"
let spec = &E::default_spec();
let store = Arc::new(MemoryStore::open());
let period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
let mut state: BeaconState<E> = 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"
); );
} }
} }
@ -968,132 +765,58 @@ mod test {
mod eth1_data_sets { mod eth1_data_sets {
use super::*; use super::*;
fn get_voting_period_start_seconds(state: &BeaconState<E>, spec: &ChainSpec) -> u64 {
let period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
let voting_period_start_slot = (state.slot / period) * period;
slot_start_seconds::<E>(
state.genesis_time,
spec.milliseconds_per_slot,
voting_period_start_slot,
)
}
#[test] #[test]
fn empty_cache() { fn empty_cache() {
let log = null_logger().unwrap();
let spec = &E::default_spec(); let spec = &E::default_spec();
let state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec); let state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
let prev_eth1_hash = Hash256::zero();
let blocks = vec![]; let blocks = vec![];
assert_eq!( assert_eq!(
eth1_data_sets( get_votes_to_consider(
blocks.iter(), blocks.iter(),
prev_eth1_hash,
get_voting_period_start_seconds(&state, spec), get_voting_period_start_seconds(&state, spec),
&spec, &spec,
&log
), ),
None HashMap::new()
);
}
#[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<E> = 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
); );
} }
#[test] #[test]
fn ideal_scenario() { fn ideal_scenario() {
let log = null_logger().unwrap(); let spec = E::default_spec();
let mut spec = E::default_spec();
spec.milliseconds_per_slot = 1_000;
let slots_per_eth1_voting_period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64(); let slots_per_eth1_voting_period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
let eth1_follow_distance = spec.eth1_follow_distance; let eth1_follow_distance = spec.eth1_follow_distance;
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), &spec); let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), &spec);
state.genesis_time = 0; 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 follow_distance_seconds = eth1_follow_distance * spec.seconds_per_eth1_block;
let voting_period_start = get_voting_period_start_seconds(&state, &spec);
let blocks = (0..eth1_follow_distance * 4) 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)) .map(|i| get_eth1_block(i, i))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let (new_eth1_data, all_eth1_data) = eth1_data_sets( let votes_to_consider =
blocks.iter(), get_votes_to_consider(blocks.iter(), voting_period_start, &spec);
prev_eth1_hash,
get_voting_period_start_seconds(&state, &spec),
&spec,
&log,
)
.expect("should find data");
assert_eq!( assert_eq!(
all_eth1_data.len(), votes_to_consider.len() as u64,
eth1_follow_distance as usize * 2, end_eth1_block - start_eth1_block,
"all_eth1_data should have appropriate length" "all produced eth1 blocks should be in votes to consider"
);
assert_eq!(
new_eth1_data.len(),
eth1_follow_distance as usize,
"new_eth1_data should have appropriate length"
); );
for (eth1_data, block_number) in &new_eth1_data { (start_eth1_block..end_eth1_block)
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)
.map(|i| get_eth1_block(i, i)) .map(|i| get_eth1_block(i, i))
.for_each(|eth1_block| { .for_each(|eth1_block| {
assert_eq!( assert_eq!(
eth1_block.number, eth1_block.number,
*all_eth1_data *votes_to_consider
.get(&eth1_block.clone().eth1_data().unwrap()) .get(&eth1_block.clone().eth1_data().unwrap())
.expect("all_eth1_data should have expected block") .expect("votes_to_consider should have expected block numbers")
)
});
(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(&eth1_block.clone().eth1_data().unwrap())
.unwrap_or_else(|| panic!(
"new_eth1_data should have expected block #{}",
eth1_block.number
))
) )
}); });
} }
@ -1130,13 +853,11 @@ mod test {
let spec = &E::default_spec(); let spec = &E::default_spec();
let state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec); let state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
let all_eth1_data = get_eth1_data_vec(slots, 0); let votes_to_consider = get_eth1_data_vec(slots, 0);
let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec();
let votes = collect_valid_votes( let votes = collect_valid_votes(
&state, &state,
HashMap::from_iter(new_eth1_data.into_iter()), &HashMap::from_iter(votes_to_consider.clone().into_iter()),
HashMap::from_iter(all_eth1_data.into_iter()),
); );
assert_eq!( assert_eq!(
votes.len(), votes.len(),
@ -1151,10 +872,9 @@ mod test {
let spec = &E::default_spec(); let spec = &E::default_spec();
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec); let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
let all_eth1_data = get_eth1_data_vec(slots, 0); let votes_to_consider = get_eth1_data_vec(slots, 0);
let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec();
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() .iter()
.map(|(eth1_data, _)| eth1_data) .map(|(eth1_data, _)| eth1_data)
.cloned() .cloned()
@ -1163,12 +883,11 @@ mod test {
let votes = collect_valid_votes( let votes = collect_valid_votes(
&state, &state,
HashMap::from_iter(new_eth1_data.clone().into_iter()), &HashMap::from_iter(votes_to_consider.clone().into_iter()),
HashMap::from_iter(all_eth1_data.into_iter()),
); );
assert_votes!( assert_votes!(
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" "should find as many votes as were in the state"
); );
} }
@ -1179,10 +898,9 @@ mod test {
let spec = &E::default_spec(); let spec = &E::default_spec();
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec); let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
let all_eth1_data = get_eth1_data_vec(slots, 0); let votes_to_consider = get_eth1_data_vec(slots, 0);
let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec();
let duplicate_eth1_data = new_eth1_data let duplicate_eth1_data = votes_to_consider
.last() .last()
.expect("should have some eth1 data") .expect("should have some eth1 data")
.clone(); .clone();
@ -1196,8 +914,7 @@ mod test {
let votes = collect_valid_votes( let votes = collect_valid_votes(
&state, &state,
HashMap::from_iter(new_eth1_data.into_iter()), &HashMap::from_iter(votes_to_consider.clone().into_iter()),
HashMap::from_iter(all_eth1_data.into_iter()),
); );
assert_votes!( assert_votes!(
votes, votes,
@ -1214,70 +931,6 @@ mod test {
"should have four votes" "should have four votes"
); );
} }
#[test]
fn non_period_tail() {
let slots = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
let spec = &E::default_spec();
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
state.slot = Slot::from(<E as EthSpec>::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 = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
let slots = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
let spec = &E::default_spec();
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
state.slot = Slot::from(<E as EthSpec>::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 { mod winning_vote {

View File

@ -1,6 +1,6 @@
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::marker::PhantomData; use std::marker::PhantomData;
use types::{Attestation, BeaconBlock, Epoch, EthSpec, Hash256}; use types::{Attestation, Epoch, EthSpec, Hash256, SignedBeaconBlock};
pub use websocket_server::WebSocketSender; pub use websocket_server::WebSocketSender;
pub trait EventHandler<T: EthSpec>: Sized + Send + Sync { pub trait EventHandler<T: EthSpec>: Sized + Send + Sync {
@ -49,11 +49,11 @@ pub enum EventKind<T: EthSpec> {
}, },
BeaconBlockImported { BeaconBlockImported {
block_root: Hash256, block_root: Hash256,
block: Box<BeaconBlock<T>>, block: Box<SignedBeaconBlock<T>>,
}, },
BeaconBlockRejected { BeaconBlockRejected {
reason: String, reason: String,
block: Box<BeaconBlock<T>>, block: Box<SignedBeaconBlock<T>>,
}, },
BeaconAttestationImported { BeaconAttestationImported {
attestation: Box<Attestation<T>>, attestation: Box<Attestation<T>>,

View File

@ -306,8 +306,11 @@ impl CheckpointManager {
.ok_or_else(|| Error::UnknownJustifiedBlock(block_root))?; .ok_or_else(|| Error::UnknownJustifiedBlock(block_root))?;
let state = chain let state = chain
.get_state_caching_only_with_committee_caches(&block.state_root, Some(block.slot))? .get_state_caching_only_with_committee_caches(
.ok_or_else(|| Error::UnknownJustifiedState(block.state_root))?; &block.state_root(),
Some(block.slot()),
)?
.ok_or_else(|| Error::UnknownJustifiedState(block.state_root()))?;
Ok(get_effective_balances(&state)) Ok(get_effective_balances(&state))
} }

View File

@ -152,8 +152,8 @@ lazy_static! {
/* /*
* Eth1 * Eth1
*/ */
pub static ref JUNK_ETH1_VOTES: Result<IntCounter> = pub static ref DEFAULT_ETH1_VOTES: Result<IntCounter> =
try_create_int_counter("beacon_eth1_junk_votes", "Count of times we have voted junk for eth1 dat"); try_create_int_counter("beacon_eth1_default_votes", "Count of times we have voted default value for eth1 data");
/* /*
* Chain Head * Chain Head

View File

@ -16,10 +16,9 @@ use store::{
migrate::{BlockingMigrator, NullMigrator}, migrate::{BlockingMigrator, NullMigrator},
DiskStore, MemoryStore, Migrate, Store, DiskStore, MemoryStore, Migrate, Store,
}; };
use tree_hash::{SignedRoot, TreeHash};
use types::{ use types::{
AggregateSignature, Attestation, BeaconBlock, BeaconState, BitList, ChainSpec, Domain, EthSpec, AggregateSignature, Attestation, BeaconState, BitList, ChainSpec, Domain, EthSpec, Hash256,
Hash256, Keypair, SecretKey, Signature, Slot, Keypair, SecretKey, Signature, SignedBeaconBlock, SignedRoot, Slot,
}; };
pub use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY}; pub use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY};
@ -279,7 +278,7 @@ where
mut state: BeaconState<E>, mut state: BeaconState<E>,
slot: Slot, slot: Slot,
block_strategy: BlockStrategy, block_strategy: BlockStrategy,
) -> (BeaconBlock<E>, BeaconState<E>) { ) -> (SignedBeaconBlock<E>, BeaconState<E>) {
if slot < state.slot { if slot < state.slot {
panic!("produce slot cannot be prior to the state slot"); panic!("produce slot cannot be prior to the state slot");
} }
@ -308,24 +307,19 @@ where
let randao_reveal = { let randao_reveal = {
let epoch = slot.epoch(E::slots_per_epoch()); let epoch = slot.epoch(E::slots_per_epoch());
let message = epoch.tree_hash_root();
let domain = self.spec.get_domain(epoch, Domain::Randao, fork); 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 .chain
.produce_block_on_state(state, slot, randao_reveal) .produce_block_on_state(state, slot, randao_reveal)
.expect("should produce block"); .expect("should produce block");
block.signature = { let signed_block = block.sign(sk, &state.fork, &self.spec);
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)
};
(block, state) (signed_block, state)
} }
/// Adds attestations to the `BeaconChain` operations pool and fork choice. /// Adds attestations to the `BeaconChain` operations pool and fork choice.
@ -407,18 +401,17 @@ where
.expect("should be able to set aggregation bits"); .expect("should be able to set aggregation bits");
let signature = { let signature = {
let message = data.tree_hash_root();
let domain = spec.get_domain( let domain = spec.get_domain(
data.target.epoch, data.target.epoch,
Domain::BeaconAttester, Domain::BeaconAttester,
fork, fork,
); );
let message = data.signing_root(domain);
let mut agg_sig = AggregateSignature::new(); let mut agg_sig = AggregateSignature::new();
agg_sig.add(&Signature::new( agg_sig.add(&Signature::new(
&message, message.as_bytes(),
domain,
self.get_sk(*validator_index), self.get_sk(*validator_index),
)); ));
@ -464,7 +457,7 @@ where
.head() .head()
.expect("should get head") .expect("should get head")
.beacon_block .beacon_block
.slot; .slot();
// Move to the next slot so we may produce some more blocks on the head. // Move to the next slot so we may produce some more blocks on the head.
self.advance_slot(); self.advance_slot();

View File

@ -267,7 +267,7 @@ fn epoch_boundary_state_attestation_processing() {
&AttestationStrategy::SomeValidators(late_validators.clone()), &AttestationStrategy::SomeValidators(late_validators.clone()),
&head.beacon_state, &head.beacon_state,
head.beacon_block_root, head.beacon_block_root,
head.beacon_block.slot, head.beacon_block.slot(),
)); ));
harness.advance_slot(); harness.advance_slot();
@ -283,9 +283,9 @@ fn epoch_boundary_state_attestation_processing() {
for attestation in late_attestations { for attestation in late_attestations {
// load_epoch_boundary_state is idempotent! // load_epoch_boundary_state is idempotent!
let block_root = attestation.data.beacon_block_root; let block_root = attestation.data.beacon_block_root;
let block: BeaconBlock<E> = store.get(&block_root).unwrap().expect("block exists"); let block = store.get_block(&block_root).unwrap().expect("block exists");
let epoch_boundary_state = store let epoch_boundary_state = store
.load_epoch_boundary_state(&block.state_root) .load_epoch_boundary_state(&block.state_root())
.expect("no error") .expect("no error")
.expect("epoch boundary state exists"); .expect("epoch boundary state exists");
let ebs_of_ebs = store 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 // Check the forwards block roots iterator against the chain dump
let chain_dump_block_roots = chain_dump let chain_dump_block_roots = chain_dump
.iter() .iter()
.map(|checkpoint| (checkpoint.beacon_block_root, checkpoint.beacon_block.slot)) .map(|checkpoint| (checkpoint.beacon_block_root, checkpoint.beacon_block.slot()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let head = harness.chain.head().expect("should get head"); let head = harness.chain.head().expect("should get head");

View File

@ -121,7 +121,7 @@ fn iterators() {
assert_eq!( assert_eq!(
*block_roots.first().expect("should have some block roots"), *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" "first block root and slot should be for the head block"
); );
@ -435,7 +435,7 @@ fn attestations_with_increasing_slots() {
.head() .head()
.expect("should get head") .expect("should get head")
.beacon_block .beacon_block
.slot, .slot(),
), ),
); );
@ -524,7 +524,7 @@ fn run_skip_slot_test(skip_slots: u64) {
.head() .head()
.expect("should get head") .expect("should get head")
.beacon_block .beacon_block
.slot, .slot(),
Slot::new(skip_slots + 1) Slot::new(skip_slots + 1)
); );
assert_eq!( assert_eq!(
@ -533,7 +533,7 @@ fn run_skip_slot_test(skip_slots: u64) {
.head() .head()
.expect("should get head") .expect("should get head")
.beacon_block .beacon_block
.slot, .slot(),
Slot::new(0) Slot::new(0)
); );
@ -566,7 +566,7 @@ fn run_skip_slot_test(skip_slots: u64) {
.head() .head()
.expect("should get head") .expect("should get head")
.beacon_block .beacon_block
.slot, .slot(),
Slot::new(skip_slots + 1) Slot::new(skip_slots + 1)
); );
} }

View File

@ -4,7 +4,6 @@ use eth1::http::{get_deposit_count, get_deposit_logs_in_range, get_deposit_root,
use eth1::{Config, Service}; use eth1::{Config, Service};
use eth1::{DepositCache, DepositLog}; use eth1::{DepositCache, DepositLog};
use eth1_test_rig::GanacheEth1Instance; use eth1_test_rig::GanacheEth1Instance;
use exit_future;
use futures::Future; use futures::Future;
use merkle_proof::verify_merkle_proof; use merkle_proof::verify_merkle_proof;
use std::ops::Range; use std::ops::Range;
@ -101,90 +100,6 @@ fn get_block_number(runtime: &mut Runtime, web3: &Web3<Http>) -> u64 {
.expect("should get block number") .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 = &eth1.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 { mod eth1_cache {
use super::*; use super::*;

View File

@ -4,11 +4,7 @@ use rayon::prelude::*;
use ssz::Encode; use ssz::Encode;
use state_processing::initialize_beacon_state_from_eth1; use state_processing::initialize_beacon_state_from_eth1;
use std::time::SystemTime; use std::time::SystemTime;
use tree_hash::SignedRoot; use types::{BeaconState, ChainSpec, DepositData, EthSpec, Hash256, Keypair, PublicKey, Signature};
use types::{
BeaconState, ChainSpec, DepositData, Domain, EthSpec, Fork, Hash256, Keypair, PublicKey,
Signature,
};
/// Builds a genesis state as defined by the Eth2 interop procedure (see below). /// Builds a genesis state as defined by the Eth2 interop procedure (see below).
/// ///
@ -39,12 +35,7 @@ pub fn interop_genesis_state<T: EthSpec>(
signature: Signature::empty_signature().into(), signature: Signature::empty_signature().into(),
}; };
let domain = spec.get_domain( data.signature = data.create_signature(&keypair.sk, spec);
spec.genesis_slot.epoch(T::slots_per_epoch()),
Domain::Deposit,
&Fork::default(),
);
data.signature = Signature::new(&data.signed_root()[..], domain, &keypair.sk).into();
data data
}) })

View File

@ -14,7 +14,7 @@ use slog::{debug, o, trace, warn};
use ssz::{Decode, DecodeError}; use ssz::{Decode, DecodeError};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::mpsc; 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 /// 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 /// functionality of this struct is to validate an decode messages from the network before
@ -319,9 +319,9 @@ impl<T: BeaconChainTypes> MessageHandler<T> {
fn decode_gossip_block( fn decode_gossip_block(
&self, &self,
beacon_block: Vec<u8>, beacon_block: Vec<u8>,
) -> Result<BeaconBlock<T::EthSpec>, DecodeError> { ) -> Result<SignedBeaconBlock<T::EthSpec>, DecodeError> {
//TODO: Apply verification before decoding. //TODO: Apply verification before decoding.
BeaconBlock::from_ssz_bytes(&beacon_block) SignedBeaconBlock::from_ssz_bytes(&beacon_block)
} }
fn decode_gossip_attestation( fn decode_gossip_attestation(
@ -355,13 +355,13 @@ impl<T: BeaconChainTypes> MessageHandler<T> {
/* Req/Resp Domain Decoding */ /* 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. /// stream termination.
fn decode_beacon_block( fn decode_beacon_block(
&self, &self,
beacon_block: Vec<u8>, beacon_block: Vec<u8>,
) -> Result<BeaconBlock<T::EthSpec>, DecodeError> { ) -> Result<SignedBeaconBlock<T::EthSpec>, DecodeError> {
//TODO: Implement faster block verification before decoding entirely //TODO: Implement faster block verification before decoding entirely
BeaconBlock::from_ssz_bytes(&beacon_block) SignedBeaconBlock::from_ssz_bytes(&beacon_block)
} }
} }

View File

@ -11,8 +11,7 @@ use ssz::Encode;
use std::sync::Arc; use std::sync::Arc;
use store::Store; use store::Store;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::{mpsc, oneshot};
use tree_hash::SignedRoot; use types::{Attestation, Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot};
use types::{Attestation, BeaconBlock, Epoch, EthSpec, Hash256, Slot};
//TODO: Rate limit requests //TODO: Rate limit requests
@ -263,7 +262,7 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
} else if self } else if self
.chain .chain
.store .store
.exists::<BeaconBlock<T::EthSpec>>(&remote.head_root) .exists::<SignedBeaconBlock<T::EthSpec>>(&remote.head_root)
.unwrap_or_else(|_| false) .unwrap_or_else(|_| false)
{ {
trace!( trace!(
@ -300,7 +299,7 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
) { ) {
let mut send_block_count = 0; let mut send_block_count = 0;
for root in request.block_roots.iter() { for root in request.block_roots.iter() {
if let Ok(Some(block)) = self.chain.store.get::<BeaconBlock<T::EthSpec>>(root) { if let Ok(Some(block)) = self.chain.store.get_block(root) {
self.network.send_rpc_response( self.network.send_rpc_response(
peer_id.clone(), peer_id.clone(),
request_id, request_id,
@ -380,11 +379,11 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
let mut blocks_sent = 0; let mut blocks_sent = 0;
for root in block_roots { for root in block_roots {
if let Ok(Some(block)) = self.chain.store.get::<BeaconBlock<T::EthSpec>>(&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 // Due to skip slots, blocks could be out of the range, we ensure they are in the
// range before sending // range before sending
if block.slot >= req.start_slot if block.slot() >= req.start_slot
&& block.slot < req.start_slot + req.count * req.step && block.slot() < req.start_slot + req.count * req.step
{ {
blocks_sent += 1; blocks_sent += 1;
self.network.send_rpc_response( self.network.send_rpc_response(
@ -437,7 +436,7 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
&mut self, &mut self,
peer_id: PeerId, peer_id: PeerId,
request_id: RequestId, request_id: RequestId,
beacon_block: Option<BeaconBlock<T::EthSpec>>, beacon_block: Option<SignedBeaconBlock<T::EthSpec>>,
) { ) {
let beacon_block = beacon_block.map(Box::new); let beacon_block = beacon_block.map(Box::new);
trace!( trace!(
@ -458,7 +457,7 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
&mut self, &mut self,
peer_id: PeerId, peer_id: PeerId,
request_id: RequestId, request_id: RequestId,
beacon_block: Option<BeaconBlock<T::EthSpec>>, beacon_block: Option<SignedBeaconBlock<T::EthSpec>>,
) { ) {
let beacon_block = beacon_block.map(Box::new); let beacon_block = beacon_block.map(Box::new);
trace!( trace!(
@ -479,7 +478,11 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
/// Attempts to apply to block to the beacon chain. May queue the block for later processing. /// 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. /// 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<T::EthSpec>) -> bool { pub fn on_block_gossip(
&mut self,
peer_id: PeerId,
block: SignedBeaconBlock<T::EthSpec>,
) -> bool {
match self.chain.process_block(block.clone()) { match self.chain.process_block(block.clone()) {
Ok(outcome) => match outcome { Ok(outcome) => match outcome {
BlockProcessingOutcome::Processed { .. } => { BlockProcessingOutcome::Processed { .. } => {
@ -528,8 +531,8 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
self.log, self.log,
"Invalid gossip beacon block"; "Invalid gossip beacon block";
"outcome" => format!("{:?}", other), "outcome" => format!("{:?}", other),
"block root" => format!("{}", Hash256::from_slice(&block.signed_root()[..])), "block root" => format!("{}", block.canonical_root()),
"block slot" => block.slot "block slot" => block.slot()
); );
trace!( trace!(
self.log, self.log,

View File

@ -50,7 +50,7 @@ use std::collections::HashSet;
use std::ops::Sub; use std::ops::Sub;
use std::sync::Weak; use std::sync::Weak;
use tokio::sync::{mpsc, oneshot}; 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 /// 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 /// 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<T: EthSpec> {
BlocksByRangeResponse { BlocksByRangeResponse {
peer_id: PeerId, peer_id: PeerId,
request_id: RequestId, request_id: RequestId,
beacon_block: Option<Box<BeaconBlock<T>>>, beacon_block: Option<Box<SignedBeaconBlock<T>>>,
}, },
/// A `BlocksByRoot` response has been received. /// A `BlocksByRoot` response has been received.
BlocksByRootResponse { BlocksByRootResponse {
peer_id: PeerId, peer_id: PeerId,
request_id: RequestId, request_id: RequestId,
beacon_block: Option<Box<BeaconBlock<T>>>, beacon_block: Option<Box<SignedBeaconBlock<T>>>,
}, },
/// A block with an unknown parent has been received. /// A block with an unknown parent has been received.
UnknownBlock(PeerId, Box<BeaconBlock<T>>), UnknownBlock(PeerId, Box<SignedBeaconBlock<T>>),
/// A peer has sent an object that references a block that is unknown. This triggers the /// 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. /// manager to attempt to find the block matching the unknown hash.
@ -107,7 +107,7 @@ pub enum SyncMessage<T: EthSpec> {
/// Maintains a sequential list of parents to lookup and the lookup's current state. /// Maintains a sequential list of parents to lookup and the lookup's current state.
struct ParentRequests<T: EthSpec> { struct ParentRequests<T: EthSpec> {
/// The blocks that have currently been downloaded. /// The blocks that have currently been downloaded.
downloaded_blocks: Vec<BeaconBlock<T>>, downloaded_blocks: Vec<SignedBeaconBlock<T>>,
/// The number of failed attempts to retrieve a parent block. If too many attempts occur, this /// The number of failed attempts to retrieve a parent block. If too many attempts occur, this
/// lookup is failed and rejected. /// lookup is failed and rejected.
@ -290,7 +290,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
&mut self, &mut self,
peer_id: PeerId, peer_id: PeerId,
request_id: RequestId, request_id: RequestId,
block: Option<BeaconBlock<T::EthSpec>>, block: Option<SignedBeaconBlock<T::EthSpec>>,
) { ) {
// check if this is a single block lookup - i.e we were searching for a specific hash // check if this is a single block lookup - i.e we were searching for a specific hash
if block.is_some() { if block.is_some() {
@ -344,7 +344,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
fn single_block_lookup_response( fn single_block_lookup_response(
&mut self, &mut self,
peer_id: PeerId, peer_id: PeerId,
block: BeaconBlock<T::EthSpec>, block: SignedBeaconBlock<T::EthSpec>,
expected_block_hash: Hash256, expected_block_hash: Hash256,
) { ) {
// verify the hash is correct and try and process the block // verify the hash is correct and try and process the block
@ -398,7 +398,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
/// A block has been sent to us that has an unknown parent. This begins a parent lookup search /// 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. /// to find the parent or chain of parents that match our current chain.
fn add_unknown_block(&mut self, peer_id: PeerId, block: BeaconBlock<T::EthSpec>) { fn add_unknown_block(&mut self, peer_id: PeerId, block: SignedBeaconBlock<T::EthSpec>) {
// If we are not in regular sync mode, ignore this block // If we are not in regular sync mode, ignore this block
if self.state != ManagerState::Regular { if self.state != ManagerState::Regular {
return; return;
@ -516,7 +516,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
// fail loudly // fail loudly
} }
let previous_index = parent_request.downloaded_blocks.len() - 2; 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. // Note: the length must be greater than 2 so this cannot panic.
let block_hash = parent_request let block_hash = parent_request
@ -626,7 +626,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
.downloaded_blocks .downloaded_blocks
.last() .last()
.expect("The parent queue should never be empty") .expect("The parent queue should never be empty")
.parent_root; .parent_root();
let request = BlocksByRootRequest { let request = BlocksByRootRequest {
block_roots: vec![parent_hash], block_roots: vec![parent_hash],
}; };

View File

@ -9,7 +9,7 @@ use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::ops::Sub; use std::ops::Sub;
use types::{BeaconBlock, EthSpec, Hash256, Slot}; use types::{EthSpec, Hash256, SignedBeaconBlock, Slot};
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub struct BatchId(pub u64); pub struct BatchId(pub u64);
@ -55,7 +55,7 @@ pub struct Batch<T: EthSpec> {
/// Marks the batch as undergoing a re-process, with a hash of the original blocks it received. /// Marks the batch as undergoing a re-process, with a hash of the original blocks it received.
pub original_hash: Option<u64>, pub original_hash: Option<u64>,
/// The blocks that have been downloaded. /// The blocks that have been downloaded.
pub downloaded_blocks: Vec<BeaconBlock<T>>, pub downloaded_blocks: Vec<SignedBeaconBlock<T>>,
} }
impl<T: EthSpec> Eq for Batch<T> {} impl<T: EthSpec> Eq for Batch<T> {}
@ -164,7 +164,7 @@ impl<T: EthSpec> PendingBatches<T> {
/// Adds a block to the batches if the request id exists. Returns None if there is no batch /// Adds a block to the batches if the request id exists. Returns None if there is no batch
/// matching the request id. /// matching the request id.
pub fn add_block(&mut self, request_id: RequestId, block: BeaconBlock<T>) -> Option<()> { pub fn add_block(&mut self, request_id: RequestId, block: SignedBeaconBlock<T>) -> Option<()> {
let batch = self.batches.get_mut(&request_id)?; let batch = self.batches.get_mut(&request_id)?;
batch.downloaded_blocks.push(block); batch.downloaded_blocks.push(block);
Some(()) Some(())

View File

@ -65,7 +65,7 @@ fn process_batch<T: BeaconChainTypes>(
// The block was valid and we processed it successfully. // The block was valid and we processed it successfully.
trace!( trace!(
log, "Imported block from network"; log, "Imported block from network";
"slot" => block.slot, "slot" => block.slot(),
"block_root" => format!("{}", block_root), "block_root" => format!("{}", block_root),
); );
successful_block_import = true; successful_block_import = true;
@ -75,21 +75,21 @@ fn process_batch<T: BeaconChainTypes>(
warn!( warn!(
log, "Parent block is unknown"; log, "Parent block is unknown";
"parent_root" => format!("{}", parent), "parent_root" => format!("{}", parent),
"baby_block_slot" => block.slot, "baby_block_slot" => block.slot(),
); );
if successful_block_import { if successful_block_import {
run_fork_choice(chain, log); run_fork_choice(chain, log);
} }
return Err(format!( return Err(format!(
"Block at slot {} has an unknown parent.", "Block at slot {} has an unknown parent.",
block.slot block.slot()
)); ));
} }
BlockProcessingOutcome::BlockIsAlreadyKnown => { BlockProcessingOutcome::BlockIsAlreadyKnown => {
// this block is already known to us, move to the next // this block is already known to us, move to the next
debug!( debug!(
log, "Imported a block that is already known"; log, "Imported a block that is already known";
"block_slot" => block.slot, "block_slot" => block.slot(),
); );
} }
BlockProcessingOutcome::FutureSlot { BlockProcessingOutcome::FutureSlot {
@ -110,7 +110,7 @@ fn process_batch<T: BeaconChainTypes>(
} }
return Err(format!( return Err(format!(
"Block at slot {} is too far in the future", "Block at slot {} is too far in the future",
block.slot block.slot()
)); ));
} else { } else {
// The block is in the future, but not too far. // The block is in the future, but not too far.
@ -144,7 +144,7 @@ fn process_batch<T: BeaconChainTypes>(
if successful_block_import { if successful_block_import {
run_fork_choice(chain, log); run_fork_choice(chain, log);
} }
return Err(format!("Invalid block at slot {}", block.slot)); return Err(format!("Invalid block at slot {}", block.slot()));
} }
} }
} else { } else {

View File

@ -10,7 +10,7 @@ use slog::{crit, debug, warn};
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::Weak; use std::sync::Weak;
use tokio::sync::mpsc; 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 /// 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 /// is requested. There is a timeout for each batch request. If this value is too high, we will
@ -143,7 +143,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
&mut self, &mut self,
network: &mut SyncNetworkContext, network: &mut SyncNetworkContext,
request_id: RequestId, request_id: RequestId,
beacon_block: &Option<BeaconBlock<T::EthSpec>>, beacon_block: &Option<SignedBeaconBlock<T::EthSpec>>,
) -> Option<()> { ) -> Option<()> {
if let Some(block) = beacon_block { if let Some(block) = beacon_block {
// This is not a stream termination, simply add the block to the request // This is not a stream termination, simply add the block to the request
@ -171,11 +171,11 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
// verify the range of received blocks // verify the range of received blocks
// Note that the order of blocks is verified in block processing // 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 // the batch is non-empty
if batch.start_slot > batch.downloaded_blocks[0].slot || batch.end_slot < last_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"; warn!(self.log, "BlocksByRange response returned out of range blocks";
"response_initial_slot" => batch.downloaded_blocks[0].slot, "response_initial_slot" => batch.downloaded_blocks[0].slot(),
"requested_initial_slot" => batch.start_slot); "requested_initial_slot" => batch.start_slot);
network.downvote_peer(batch.current_peer); 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 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<T: BeaconChainTypes> SyncingChain<T> {
// double check batches are processed in order TODO: Remove for prod // double check batches are processed in order TODO: Remove for prod
if batch.id != self.to_be_processed_id { if batch.id != self.to_be_processed_id {
crit!(self.log, "Batch processed out of order"; 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); "expected_id" => *self.to_be_processed_id);
} }
@ -298,7 +298,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
let processed_batch = self.processed_batches.remove(0); let processed_batch = self.processed_batches.remove(0);
if *processed_batch.id >= *batch.id { if *processed_batch.id >= *batch.id {
crit!(self.log, "A processed batch had a greater id than the current process 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); "current_id" => *batch.id);
} }
@ -313,8 +313,8 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
// If the same peer corrected it's mistake, we allow it.... for // If the same peer corrected it's mistake, we allow it.... for
// now. // now.
debug!(self.log, "Re-processed batch validated. Downvoting original peer"; debug!(self.log, "Re-processed batch validated. Downvoting original peer";
"batch_id" => *processed_batch.id, "batch_id" => *processed_batch.id,
"original_peer" => format!("{}",processed_batch.original_peer), "original_peer" => format!("{}",processed_batch.original_peer),
"new_peer" => format!("{}", processed_batch.current_peer)); "new_peer" => format!("{}", processed_batch.current_peer));
network.downvote_peer(processed_batch.original_peer); network.downvote_peer(processed_batch.original_peer);
} }
@ -325,7 +325,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
// Add the current batch to processed batches to be verified in the future. We are // 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. // 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)) != Some(batch.end_slot.saturating_sub(1u64))
{ {
self.processed_batches.push(batch); self.processed_batches.push(batch);
@ -436,12 +436,12 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
batch.current_peer = new_peer.clone(); batch.current_peer = new_peer.clone();
debug!(self.log, "Re-requesting batch"; debug!(self.log, "Re-requesting batch";
"start_slot" => batch.start_slot, "start_slot" => batch.start_slot,
"end_slot" => batch.end_slot, "end_slot" => batch.end_slot,
"id" => *batch.id, "id" => *batch.id,
"peer" => format!("{}", batch.current_peer), "peer" => format!("{}", batch.current_peer),
"head_root"=> format!("{}", batch.head_root), "head_root"=> format!("{}", batch.head_root),
"retries" => batch.retries, "retries" => batch.retries,
"re-processes" => batch.reprocess_retries); "re-processes" => batch.reprocess_retries);
self.send_batch(network, batch); self.send_batch(network, batch);
@ -522,9 +522,9 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
request_id: RequestId, request_id: RequestId,
) -> Option<ProcessingResult> { ) -> Option<ProcessingResult> {
if let Some(batch) = self.pending_batches.remove(request_id) { if let Some(batch) = self.pending_batches.remove(request_id) {
warn!(self.log, "Batch failed. RPC Error"; warn!(self.log, "Batch failed. RPC Error";
"id" => *batch.id, "id" => *batch.id,
"retries" => batch.retries, "retries" => batch.retries,
"peer" => format!("{:?}", peer_id)); "peer" => format!("{:?}", peer_id));
Some(self.failed_batch(network, batch)) Some(self.failed_batch(network, batch))
@ -564,7 +564,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
batch.current_peer = new_peer.clone(); batch.current_peer = new_peer.clone();
debug!(self.log, "Re-Requesting batch"; debug!(self.log, "Re-Requesting batch";
"start_slot" => batch.start_slot, "start_slot" => batch.start_slot,
"end_slot" => batch.end_slot, "end_slot" => batch.end_slot,
"id" => *batch.id, "id" => *batch.id,
"peer" => format!("{:?}", batch.current_peer), "peer" => format!("{:?}", batch.current_peer),
"head_root"=> format!("{}", batch.head_root)); "head_root"=> format!("{}", batch.head_root));
@ -587,11 +587,11 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
// find the next pending batch and request it from the peer // find the next pending batch and request it from the peer
if let Some(peer_id) = self.get_next_peer() { if let Some(peer_id) = self.get_next_peer() {
if let Some(batch) = self.get_next_batch(peer_id) { if let Some(batch) = self.get_next_batch(peer_id) {
debug!(self.log, "Requesting batch"; debug!(self.log, "Requesting batch";
"start_slot" => batch.start_slot, "start_slot" => batch.start_slot,
"end_slot" => batch.end_slot, "end_slot" => batch.end_slot,
"id" => *batch.id, "id" => *batch.id,
"peer" => format!("{}", batch.current_peer), "peer" => format!("{}", batch.current_peer),
"head_root"=> format!("{}", batch.head_root)); "head_root"=> format!("{}", batch.head_root));
// send the batch // send the batch
self.send_batch(network, batch); self.send_batch(network, batch);

View File

@ -52,7 +52,7 @@ use slog::{debug, error, trace, warn};
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::Weak; use std::sync::Weak;
use tokio::sync::mpsc; 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 /// 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 /// non-active chains that need to be processed before the syncing is considered complete. This
@ -231,7 +231,7 @@ impl<T: BeaconChainTypes> RangeSync<T> {
network: &mut SyncNetworkContext, network: &mut SyncNetworkContext,
peer_id: PeerId, peer_id: PeerId,
request_id: RequestId, request_id: RequestId,
beacon_block: Option<BeaconBlock<T::EthSpec>>, beacon_block: Option<SignedBeaconBlock<T::EthSpec>>,
) { ) {
// Find the request. Most likely the first finalized chain (the syncing chain). If there // 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 // are no finalized chains, then it will be a head chain. At most, there should only be

File diff suppressed because it is too large Load Diff

View File

@ -10,8 +10,8 @@ use ssz_derive::{Decode, Encode};
use std::sync::Arc; use std::sync::Arc;
use store::Store; use store::Store;
use types::{ use types::{
BeaconBlock, BeaconState, CommitteeIndex, EthSpec, Hash256, PublicKeyBytes, RelativeEpoch, BeaconState, CommitteeIndex, EthSpec, Hash256, PublicKeyBytes, RelativeEpoch,
Slot, Validator, SignedBeaconBlock, Slot, Validator,
}; };
/// Information about the block and state that are at head of the beacon chain. /// 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, 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<T: BeaconChainTypes>( pub fn get_heads<T: BeaconChainTypes>(
req: Request<Body>, req: Request<Body>,
beacon_chain: Arc<BeaconChain<T>>, beacon_chain: Arc<BeaconChain<T>>,
@ -91,10 +91,10 @@ pub fn get_heads<T: BeaconChainTypes>(
#[serde(bound = "T: EthSpec")] #[serde(bound = "T: EthSpec")]
pub struct BlockResponse<T: EthSpec> { pub struct BlockResponse<T: EthSpec> {
pub root: Hash256, pub root: Hash256,
pub beacon_block: BeaconBlock<T>, pub beacon_block: SignedBeaconBlock<T>,
} }
/// 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<T: BeaconChainTypes>( pub fn get_block<T: BeaconChainTypes>(
req: Request<Body>, req: Request<Body>,
beacon_chain: Arc<BeaconChain<T>>, beacon_chain: Arc<BeaconChain<T>>,
@ -107,22 +107,22 @@ pub fn get_block<T: BeaconChainTypes>(
let target = parse_slot(&value)?; let target = parse_slot(&value)?;
block_root_at_slot(&beacon_chain, target)?.ok_or_else(|| { 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)?, ("root", value) => parse_root(&value)?,
_ => return Err(ApiError::ServerError("Unexpected query parameter".into())), _ => return Err(ApiError::ServerError("Unexpected query parameter".into())),
}; };
let block = beacon_chain let block = beacon_chain.store.get_block(&block_root)?.ok_or_else(|| {
.store ApiError::NotFound(format!(
.get::<BeaconBlock<T::EthSpec>>(&block_root)? "Unable to find SignedBeaconBlock for root {:?}",
.ok_or_else(|| { block_root
ApiError::NotFound(format!( ))
"Unable to find BeaconBlock for root {:?}", })?;
block_root
))
})?;
let response = BlockResponse { let response = BlockResponse {
root: block_root, root: block_root,
@ -132,7 +132,7 @@ pub fn get_block<T: BeaconChainTypes>(
ResponseBuilder::new(&req)?.body(&response) 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<T: BeaconChainTypes>( pub fn get_block_root<T: BeaconChainTypes>(
req: Request<Body>, req: Request<Body>,
beacon_chain: Arc<BeaconChain<T>>, beacon_chain: Arc<BeaconChain<T>>,
@ -141,7 +141,10 @@ pub fn get_block_root<T: BeaconChainTypes>(
let target = parse_slot(&slot_string)?; let target = parse_slot(&slot_string)?;
let root = block_root_at_slot(&beacon_chain, target)?.ok_or_else(|| { 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) ResponseBuilder::new(&req)?.body(&root)

View File

@ -13,8 +13,8 @@ use std::sync::Arc;
use store::{iter::AncestorIter, Store}; use store::{iter::AncestorIter, Store};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use types::{ use types::{
Attestation, BeaconBlock, BeaconState, CommitteeIndex, Epoch, EthSpec, Hash256, RelativeEpoch, Attestation, BeaconState, CommitteeIndex, Epoch, EthSpec, Hash256, RelativeEpoch, Signature,
Signature, Slot, SignedBeaconBlock, Slot,
}; };
/// Parse a slot. /// Parse a slot.
@ -113,7 +113,7 @@ pub fn parse_pubkey_bytes(string: &str) -> Result<PublicKeyBytes, ApiError> {
} }
} }
/// 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. /// `slot`, if possible.
/// ///
/// May return a root for a previous slot, in the case of skip slots. /// 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<Body>) -> ApiResult {
pub fn publish_beacon_block_to_network<T: BeaconChainTypes + 'static>( pub fn publish_beacon_block_to_network<T: BeaconChainTypes + 'static>(
chan: Arc<RwLock<mpsc::UnboundedSender<NetworkMessage>>>, chan: Arc<RwLock<mpsc::UnboundedSender<NetworkMessage>>>,
block: BeaconBlock<T::EthSpec>, block: SignedBeaconBlock<T::EthSpec>,
) -> Result<(), ApiError> { ) -> Result<(), ApiError> {
// create the network topic to send on // create the network topic to send on
let topic = GossipTopic::BeaconBlock; let topic = GossipTopic::BeaconBlock;

View File

@ -14,7 +14,9 @@ use slog::{error, info, warn, Logger};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use std::sync::Arc; use std::sync::Arc;
use types::beacon_state::EthSpec; 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)] #[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
pub struct ValidatorDuty { pub struct ValidatorDuty {
@ -256,7 +258,7 @@ pub fn get_new_beacon_block<T: BeaconChainTypes>(
ResponseBuilder::new(&req)?.body(&new_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<T: BeaconChainTypes>( pub fn publish_beacon_block<T: BeaconChainTypes>(
req: Request<Body>, req: Request<Body>,
beacon_chain: Arc<BeaconChain<T>>, beacon_chain: Arc<BeaconChain<T>>,
@ -272,11 +274,11 @@ pub fn publish_beacon_block<T: BeaconChainTypes>(
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e))) .map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))
.and_then(|chunks| { .and_then(|chunks| {
serde_json::from_slice(&chunks).map_err(|e| { 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<T::EthSpec>| { .and_then(move |block: SignedBeaconBlock<T::EthSpec>| {
let slot = block.slot; let slot = block.slot();
match beacon_chain.process_block(block.clone()) { match beacon_chain.process_block(block.clone()) {
Ok(BlockProcessingOutcome::Processed { block_root }) => { Ok(BlockProcessingOutcome::Processed { block_root }) => {
// Block was processed, publish via gossipsub // Block was processed, publish via gossipsub
@ -329,7 +331,7 @@ pub fn publish_beacon_block<T: BeaconChainTypes>(
); );
Err(ApiError::ProcessingError(format!( 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 outcome
))) )))
} }
@ -386,7 +388,7 @@ pub fn publish_attestation<T: BeaconChainTypes>(
.and_then(|chunks| { .and_then(|chunks| {
serde_json::from_slice(&chunks.as_slice()).map_err(|e| { serde_json::from_slice(&chunks.as_slice()).map_err(|e| {
ApiError::BadRequest(format!( ApiError::BadRequest(format!(
"Unable to deserialize JSON into a BeaconBlock: {:?}", "Unable to deserialize JSON into a SignedBeaconBlock: {:?}",
e e
)) ))
}) })

View File

@ -11,10 +11,10 @@ use remote_beacon_node::{
}; };
use std::convert::TryInto; use std::convert::TryInto;
use std::sync::Arc; use std::sync::Arc;
use tree_hash::TreeHash;
use types::{ use types::{
test_utils::generate_deterministic_keypair, BeaconBlock, BeaconState, ChainSpec, Domain, Epoch, 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; use version;
@ -54,17 +54,17 @@ fn get_randao_reveal<T: BeaconChainTypes>(
.expect("should get proposer index"); .expect("should get proposer index");
let keypair = generate_deterministic_keypair(proposer_index); let keypair = generate_deterministic_keypair(proposer_index);
let epoch = slot.epoch(E::slots_per_epoch()); let epoch = slot.epoch(E::slots_per_epoch());
let message = epoch.tree_hash_root();
let domain = spec.get_domain(epoch, Domain::Randao, &fork); 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). /// Signs the given block (assuming the given `beacon_chain` uses deterministic keypairs).
fn sign_block<T: BeaconChainTypes>( fn sign_block<T: BeaconChainTypes>(
beacon_chain: Arc<BeaconChain<T>>, beacon_chain: Arc<BeaconChain<T>>,
block: &mut BeaconBlock<T::EthSpec>, block: BeaconBlock<T::EthSpec>,
spec: &ChainSpec, spec: &ChainSpec,
) { ) -> SignedBeaconBlock<T::EthSpec> {
let fork = beacon_chain let fork = beacon_chain
.head() .head()
.expect("should get head") .expect("should get head")
@ -74,7 +74,7 @@ fn sign_block<T: BeaconChainTypes>(
.block_proposer(block.slot) .block_proposer(block.slot)
.expect("should get proposer index"); .expect("should get proposer index");
let keypair = generate_deterministic_keypair(proposer_index); let keypair = generate_deterministic_keypair(proposer_index);
block.sign(&keypair.sk, &fork, spec); block.sign(&keypair.sk, &fork, spec)
} }
#[test] #[test]
@ -335,7 +335,7 @@ fn validator_block_post() {
let slot = Slot::new(1); let slot = Slot::new(1);
let randao_reveal = get_randao_reveal(beacon_chain.clone(), slot, spec); let randao_reveal = get_randao_reveal(beacon_chain.clone(), slot, spec);
let mut block = env let block = env
.runtime() .runtime()
.block_on( .block_on(
remote_node remote_node
@ -346,9 +346,13 @@ fn validator_block_post() {
.expect("should fetch block from http api"); .expect("should fetch block from http api");
// Try publishing the block without a signature, ensure it is flagged as invalid. // 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 let publish_status = env
.runtime() .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"); .expect("should publish block");
if cfg!(not(feature = "fake_crypto")) { if cfg!(not(feature = "fake_crypto")) {
assert!( assert!(
@ -357,12 +361,12 @@ fn validator_block_post() {
); );
} }
sign_block(beacon_chain, &mut block, spec); let signed_block = sign_block(beacon_chain.clone(), block, spec);
let block_root = block.canonical_root(); let block_root = signed_block.canonical_root();
let publish_status = env let publish_status = env
.runtime() .runtime()
.block_on(remote_node.http.validator().publish_block(block)) .block_on(remote_node.http.validator().publish_block(signed_block))
.expect("should publish block"); .expect("should publish block");
if cfg!(not(feature = "fake_crypto")) { if cfg!(not(feature = "fake_crypto")) {

View File

@ -11,7 +11,7 @@ use std::fs;
use std::net::{IpAddr, Ipv4Addr}; use std::net::{IpAddr, Ipv4Addr};
use std::net::{TcpListener, UdpSocket}; use std::net::{TcpListener, UdpSocket};
use std::path::PathBuf; use std::path::PathBuf;
use types::{Epoch, EthSpec, Fork}; use types::EthSpec;
pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml"; pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml";
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml"; pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";
@ -377,11 +377,7 @@ fn init_new_client<E: EthSpec>(
let spec = &mut eth2_config.spec; let spec = &mut eth2_config.spec;
// For now, assume that all networks will use the lighthouse genesis fork. // For now, assume that all networks will use the lighthouse genesis fork.
spec.genesis_fork = Fork { spec.genesis_fork_version = [1, 3, 3, 7];
previous_version: [0, 0, 0, 0],
current_version: [1, 3, 3, 7],
epoch: Epoch::new(0),
};
client_config.eth1.deposit_contract_address = client_config.eth1.deposit_contract_address =
format!("{:?}", eth2_testnet_config.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.ejection_balance = 1_600_000_000;
spec.effective_balance_increment = 100_000_000; spec.effective_balance_increment = 100_000_000;
spec.min_genesis_time = 0; spec.min_genesis_time = 0;
spec.genesis_fork = Fork { spec.genesis_fork_version = [0, 0, 0, 2];
previous_version: [0; 4],
current_version: [0, 0, 0, 2],
epoch: Epoch::new(0),
};
client_config.eth1.deposit_contract_address = client_config.eth1.deposit_contract_address =
"0x802dF6aAaCe28B2EEb1656bb18dF430dDC42cc2e".to_string(); "0x802dF6aAaCe28B2EEb1656bb18dF430dDC42cc2e".to_string();

View File

@ -1,194 +0,0 @@
use super::*;
use ssz::{Decode, DecodeError};
use std::cmp::Ordering;
fn get_block_bytes<T: Store<E>, E: EthSpec>(
store: &T,
root: Hash256,
) -> Result<Option<Vec<u8>>, Error> {
store.get_bytes(BeaconBlock::<E>::db_column().into(), &root[..])
}
fn read_slot_from_block_bytes(bytes: &[u8]) -> Result<Slot, DecodeError> {
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<Hash256, DecodeError> {
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<T: Store<E>, E: EthSpec>(
store: &T,
slot: Slot,
start_root: Hash256,
) -> Result<Option<(Hash256, BeaconBlock<E>)>, Error> {
Ok(
match get_at_preceeding_slot::<_, E>(store, slot, start_root)? {
Some((hash, bytes)) => Some((hash, BeaconBlock::<E>::from_ssz_bytes(&bytes)?)),
None => None,
},
)
}
fn get_at_preceeding_slot<T: Store<E>, E: EthSpec>(
store: &T,
slot: Slot,
mut root: Hash256,
) -> Result<Option<(Hash256, Vec<u8>)>, 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<MinimalEthSpec>;
#[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<MinimalEthSpec>,
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<usize> = (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::<MinimalEthSpec>::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());
}
}

View File

@ -46,7 +46,7 @@ pub struct HotColdDB<E: EthSpec> {
/// The hot database also contains all blocks. /// The hot database also contains all blocks.
pub(crate) hot_db: LevelDB<E>, pub(crate) hot_db: LevelDB<E>,
/// LRU cache of deserialized blocks. Updated whenever a block is loaded. /// LRU cache of deserialized blocks. Updated whenever a block is loaded.
block_cache: Mutex<LruCache<Hash256, BeaconBlock<E>>>, block_cache: Mutex<LruCache<Hash256, SignedBeaconBlock<E>>>,
/// LRU cache of deserialized states. Updated whenever a state is loaded. /// LRU cache of deserialized states. Updated whenever a state is loaded.
state_cache: Mutex<LruCache<Hash256, BeaconState<E>>>, state_cache: Mutex<LruCache<Hash256, BeaconState<E>>>,
/// Chain spec. /// Chain spec.
@ -107,7 +107,7 @@ impl<E: EthSpec> Store<E> for HotColdDB<E> {
} }
/// Store a block and update the LRU cache. /// Store a block and update the LRU cache.
fn put_block(&self, block_root: &Hash256, block: BeaconBlock<E>) -> Result<(), Error> { fn put_block(&self, block_root: &Hash256, block: SignedBeaconBlock<E>) -> Result<(), Error> {
// Store on disk. // Store on disk.
self.put(block_root, &block)?; self.put(block_root, &block)?;
@ -118,7 +118,7 @@ impl<E: EthSpec> Store<E> for HotColdDB<E> {
} }
/// Fetch a block from the store. /// Fetch a block from the store.
fn get_block(&self, block_root: &Hash256) -> Result<Option<BeaconBlock<E>>, Error> { fn get_block(&self, block_root: &Hash256) -> Result<Option<SignedBeaconBlock<E>>, Error> {
metrics::inc_counter(&metrics::BEACON_BLOCK_GET_COUNT); metrics::inc_counter(&metrics::BEACON_BLOCK_GET_COUNT);
// Check the cache. // Check the cache.
@ -128,7 +128,7 @@ impl<E: EthSpec> Store<E> for HotColdDB<E> {
} }
// Fetch from database. // Fetch from database.
match self.get::<BeaconBlock<E>>(block_root)? { match self.get::<SignedBeaconBlock<E>>(block_root)? {
Some(block) => { Some(block) => {
// Add to cache. // Add to cache.
self.block_cache.lock().put(*block_root, block.clone()); self.block_cache.lock().put(*block_root, block.clone());
@ -569,15 +569,15 @@ impl<E: EthSpec> HotColdDB<E> {
start_slot: Slot, start_slot: Slot,
end_slot: Slot, end_slot: Slot,
end_block_hash: Hash256, end_block_hash: Hash256,
) -> Result<Vec<BeaconBlock<E>>, Error> { ) -> Result<Vec<SignedBeaconBlock<E>>, Error> {
let mut blocks = ParentRootBlockIterator::new(self, end_block_hash) let mut blocks = ParentRootBlockIterator::new(self, end_block_hash)
.map(|(_, block)| block) .map(|(_, block)| block)
// Include the block at the end slot (if any), it needs to be // Include the block at the end slot (if any), it needs to be
// replayed in order to construct the canonical state at `end_slot`. // 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 // Exclude the block at the start slot (if any), because it has already
// been applied to the starting state. // been applied to the starting state.
.take_while(|block| block.slot > start_slot) .take_while(|block| block.message.slot > start_slot)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
blocks.reverse(); blocks.reverse();
Ok(blocks) Ok(blocks)
@ -590,12 +590,12 @@ impl<E: EthSpec> HotColdDB<E> {
fn replay_blocks( fn replay_blocks(
&self, &self,
mut state: BeaconState<E>, mut state: BeaconState<E>,
blocks: Vec<BeaconBlock<E>>, blocks: Vec<SignedBeaconBlock<E>>,
target_slot: Slot, target_slot: Slot,
) -> Result<BeaconState<E>, Error> { ) -> Result<BeaconState<E>, Error> {
let state_root_from_prev_block = |i: usize, state: &BeaconState<E>| { let state_root_from_prev_block = |i: usize, state: &BeaconState<E>| {
if i > 0 { if i > 0 {
let prev_block = &blocks[i - 1]; let prev_block = &blocks[i - 1].message;
if prev_block.slot == state.slot { if prev_block.slot == state.slot {
Some(prev_block.state_root) Some(prev_block.state_root)
} else { } else {
@ -607,7 +607,7 @@ impl<E: EthSpec> HotColdDB<E> {
}; };
for (i, block) in blocks.iter().enumerate() { 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); let state_root = state_root_from_prev_block(i, &state);
per_slot_processing(&mut state, state_root, &self.spec) per_slot_processing(&mut state, state_root, &self.spec)
.map_err(HotColdDBError::BlockReplaySlotError)?; .map_err(HotColdDBError::BlockReplaySlotError)?;

View File

@ -4,7 +4,7 @@ use ssz::{Decode, Encode};
pub mod beacon_state; pub mod beacon_state;
pub mod partial_beacon_state; pub mod partial_beacon_state;
impl<T: EthSpec> SimpleStoreItem for BeaconBlock<T> { impl<T: EthSpec> SimpleStoreItem for SignedBeaconBlock<T> {
fn db_column() -> DBColumn { fn db_column() -> DBColumn {
DBColumn::BeaconBlock DBColumn::BeaconBlock
} }

View File

@ -3,7 +3,7 @@ use std::borrow::Cow;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use types::{ 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. /// Implemented for types that have ancestors (e.g., blocks, states) that may be iterated over.
@ -18,12 +18,14 @@ pub trait AncestorIter<U: Store<E>, E: EthSpec, I: Iterator> {
} }
impl<'a, U: Store<E>, E: EthSpec> AncestorIter<U, E, BlockRootsIterator<'a, E, U>> impl<'a, U: Store<E>, E: EthSpec> AncestorIter<U, E, BlockRootsIterator<'a, E, U>>
for BeaconBlock<E> for SignedBeaconBlock<E>
{ {
/// Iterates across all available prior block roots of `self`, starting at the most recent and ending /// Iterates across all available prior block roots of `self`, starting at the most recent and ending
/// at genesis. /// at genesis.
fn try_iter_ancestor_roots(&self, store: Arc<U>) -> Option<BlockRootsIterator<'a, E, U>> { fn try_iter_ancestor_roots(&self, store: Arc<U>) -> Option<BlockRootsIterator<'a, E, U>> {
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)) Some(BlockRootsIterator::owned(store, state))
} }
@ -120,7 +122,7 @@ impl<'a, E: EthSpec, S: Store<E>> ParentRootBlockIterator<'a, E, S> {
} }
impl<'a, E: EthSpec, S: Store<E>> Iterator for ParentRootBlockIterator<'a, E, S> { impl<'a, E: EthSpec, S: Store<E>> Iterator for ParentRootBlockIterator<'a, E, S> {
type Item = (Hash256, BeaconBlock<E>); type Item = (Hash256, SignedBeaconBlock<E>);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
// Stop once we reach the zero parent, otherwise we'll keep returning the genesis // Stop once we reach the zero parent, otherwise we'll keep returning the genesis
@ -129,15 +131,15 @@ impl<'a, E: EthSpec, S: Store<E>> Iterator for ParentRootBlockIterator<'a, E, S>
None None
} else { } else {
let block_root = self.next_block_root; let block_root = self.next_block_root;
let block: BeaconBlock<E> = self.store.get(&block_root).ok()??; let block = self.store.get_block(&block_root).ok()??;
self.next_block_root = block.parent_root; self.next_block_root = block.message.parent_root;
Some((block_root, block)) Some((block_root, block))
} }
} }
} }
#[derive(Clone)] #[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> { pub struct BlockIterator<'a, T: EthSpec, U> {
roots: BlockRootsIterator<'a, T, U>, roots: BlockRootsIterator<'a, T, U>,
} }
@ -159,11 +161,11 @@ impl<'a, T: EthSpec, U: Store<T>> BlockIterator<'a, T, U> {
} }
impl<'a, T: EthSpec, U: Store<T>> Iterator for BlockIterator<'a, T, U> { impl<'a, T: EthSpec, U: Store<T>> Iterator for BlockIterator<'a, T, U> {
type Item = BeaconBlock<T>; type Item = SignedBeaconBlock<T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let (root, _slot) = self.roots.next()?; let (root, _slot) = self.roots.next()?;
self.roots.store.get(&root).ok()? self.roots.store.get_block(&root).ok()?
} }
} }

View File

@ -10,7 +10,6 @@
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
mod block_at_slot;
pub mod chunked_iter; pub mod chunked_iter;
pub mod chunked_vector; pub mod chunked_vector;
pub mod config; pub mod config;
@ -83,12 +82,12 @@ pub trait Store<E: EthSpec>: Sync + Send + Sized + 'static {
} }
/// Store a block in the store. /// Store a block in the store.
fn put_block(&self, block_root: &Hash256, block: BeaconBlock<E>) -> Result<(), Error> { fn put_block(&self, block_root: &Hash256, block: SignedBeaconBlock<E>) -> Result<(), Error> {
self.put(block_root, &block) self.put(block_root, &block)
} }
/// Fetch a block from the store. /// Fetch a block from the store.
fn get_block(&self, block_root: &Hash256) -> Result<Option<BeaconBlock<E>>, Error> { fn get_block(&self, block_root: &Hash256) -> Result<Option<SignedBeaconBlock<E>>, Error> {
self.get(block_root) self.get(block_root)
} }
@ -124,19 +123,6 @@ pub trait Store<E: EthSpec>: Sync + Send + Sized + 'static {
self.get_state(state_root, slot) 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<Option<(Hash256, BeaconBlock<E>)>, 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. /// (Optionally) Move all data before the frozen slot to the freezer database.
fn freeze_to_state( fn freeze_to_state(
_store: Arc<Self>, _store: Arc<Self>,

View File

@ -11,7 +11,7 @@ use types::*;
/// ///
/// Utilises lazy-loading from separate storage for its vector fields. /// Utilises lazy-loading from separate storage for its vector fields.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive(Debug, PartialEq, Clone, Encode, Decode)] #[derive(Debug, PartialEq, Clone, Encode, Decode)]
pub struct PartialBeaconState<T> pub struct PartialBeaconState<T>
where where

View File

@ -10,7 +10,7 @@ HTTP Path | Description |
[`/beacon/head`](#beaconhead) | Info about the block at the head of the chain. [`/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/heads`](#beaconheads) | Returns a list of all known chain heads.
[`/beacon/block_root`](#beaconblock_root) | Resolve a slot to a block root. [`/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_root`](#beaconstate_root) | Resolve a slot to a state root.
[`/beacon/state`](#beaconstate) | Get a `BeaconState` by slot or root. [`/beacon/state`](#beaconstate) | Get a `BeaconState` by slot or root.
[`/beacon/validators`](#beaconvalidators) | Query for one or more validators. [`/beacon/validators`](#beaconvalidators) | Query for one or more validators.
@ -131,7 +131,7 @@ canonical chain.
### Returns ### 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 ### Example Response
@ -139,22 +139,24 @@ Returns an object containing a single [`BeaconBlock`](https://github.com/ethereu
{ {
"root": "0xc35ddf4e71c31774e0594bd7eb32dfe50b54dbc40abd594944254b4ec8895196", "root": "0xc35ddf4e71c31774e0594bd7eb32dfe50b54dbc40abd594944254b4ec8895196",
"beacon_block": { "beacon_block": {
"slot": 0, "message": {
"parent_root": "0x0000000000000000000000000000000000000000000000000000000000000000", "slot": 0,
"state_root": "0xf15690b6be4ed42ea1ee0741eb4bfd4619d37be8229b84b4ddd480fb028dcc8f", "parent_root": "0x0000000000000000000000000000000000000000000000000000000000000000",
"body": { "state_root": "0xf15690b6be4ed42ea1ee0741eb4bfd4619d37be8229b84b4ddd480fb028dcc8f",
"randao_reveal": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "body": {
"eth1_data": { "randao_reveal": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"deposit_root": "0x0000000000000000000000000000000000000000000000000000000000000000", "eth1_data": {
"deposit_count": 0, "deposit_root": "0x0000000000000000000000000000000000000000000000000000000000000000",
"block_hash": "0x0000000000000000000000000000000000000000000000000000000000000000" "deposit_count": 0,
}, "block_hash": "0x0000000000000000000000000000000000000000000000000000000000000000"
"graffiti": "0x0000000000000000000000000000000000000000000000000000000000000000", },
"proposer_slashings": [], "graffiti": "0x0000000000000000000000000000000000000000000000000000000000000000",
"attester_slashings": [], "proposer_slashings": [],
"attestations": [], "attester_slashings": [],
"deposits": [], "attestations": [],
"voluntary_exits": [] "deposits": [],
"voluntary_exits": []
}
}, },
"signature": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "signature": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
} }
@ -214,7 +216,7 @@ canonical chain.
### Returns ### Returns
Returns an object containing a single 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. and its tree hash root.
### Example Response ### Example Response

View File

@ -22,7 +22,7 @@ use std::collections::{hash_map, HashMap, HashSet};
use std::marker::PhantomData; use std::marker::PhantomData;
use types::{ use types::{
typenum::Unsigned, Attestation, AttesterSlashing, BeaconState, BeaconStateError, ChainSpec, typenum::Unsigned, Attestation, AttesterSlashing, BeaconState, BeaconStateError, ChainSpec,
EthSpec, ProposerSlashing, RelativeEpoch, Validator, VoluntaryExit, EthSpec, ProposerSlashing, RelativeEpoch, SignedVoluntaryExit, Validator,
}; };
#[derive(Default, Debug)] #[derive(Default, Debug)]
@ -34,7 +34,7 @@ pub struct OperationPool<T: EthSpec + Default> {
/// Map from proposer index to slashing. /// Map from proposer index to slashing.
proposer_slashings: RwLock<HashMap<u64, ProposerSlashing>>, proposer_slashings: RwLock<HashMap<u64, ProposerSlashing>>,
/// Map from exiting validator to their exit data. /// Map from exiting validator to their exit data.
voluntary_exits: RwLock<HashMap<u64, VoluntaryExit>>, voluntary_exits: RwLock<HashMap<u64, SignedVoluntaryExit>>,
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
@ -297,14 +297,14 @@ impl<T: EthSpec> OperationPool<T> {
/// Insert a voluntary exit, validating it almost-entirely (future exits are permitted). /// Insert a voluntary exit, validating it almost-entirely (future exits are permitted).
pub fn insert_voluntary_exit( pub fn insert_voluntary_exit(
&self, &self,
exit: VoluntaryExit, exit: SignedVoluntaryExit,
state: &BeaconState<T>, state: &BeaconState<T>,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), ExitValidationError> { ) -> Result<(), ExitValidationError> {
verify_exit_time_independent_only(state, &exit, VerifySignatures::True, spec)?; verify_exit_time_independent_only(state, &exit, VerifySignatures::True, spec)?;
self.voluntary_exits self.voluntary_exits
.write() .write()
.insert(exit.validator_index, exit); .insert(exit.message.validator_index, exit);
Ok(()) Ok(())
} }
@ -313,7 +313,7 @@ impl<T: EthSpec> OperationPool<T> {
&self, &self,
state: &BeaconState<T>, state: &BeaconState<T>,
spec: &ChainSpec, spec: &ChainSpec,
) -> Vec<VoluntaryExit> { ) -> Vec<SignedVoluntaryExit> {
filter_limit_operations( filter_limit_operations(
self.voluntary_exits.read().values(), self.voluntary_exits.read().values(),
|exit| verify_exit(state, exit, VerifySignatures::False, spec).is_ok(), |exit| verify_exit(state, exit, VerifySignatures::False, spec).is_ok(),

View File

@ -21,7 +21,7 @@ pub struct PersistedOperationPool<T: EthSpec> {
/// Proposer slashings. /// Proposer slashings.
proposer_slashings: Vec<ProposerSlashing>, proposer_slashings: Vec<ProposerSlashing>,
/// Voluntary exits. /// Voluntary exits.
voluntary_exits: Vec<VoluntaryExit>, voluntary_exits: Vec<SignedVoluntaryExit>,
} }
impl<T: EthSpec> PersistedOperationPool<T> { impl<T: EthSpec> PersistedOperationPool<T> {
@ -86,7 +86,7 @@ impl<T: EthSpec> PersistedOperationPool<T> {
let voluntary_exits = RwLock::new( let voluntary_exits = RwLock::new(
self.voluntary_exits self.voluntary_exits
.into_iter() .into_iter()
.map(|exit| (exit.validator_index, exit)) .map(|exit| (exit.message.validator_index, exit))
.collect(), .collect(),
); );

View File

@ -3,7 +3,7 @@ use types::*;
/// Returns validator indices which participated in the attestation, sorted by increasing index. /// 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<T: EthSpec>( pub fn get_attesting_indices<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
attestation_data: &AttestationData, attestation_data: &AttestationData,

View File

@ -6,7 +6,7 @@ type Result<T> = std::result::Result<T, BlockOperationError<Invalid>>;
/// Convert `attestation` to (almost) indexed-verifiable form. /// Convert `attestation` to (almost) indexed-verifiable form.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_indexed_attestation<T: EthSpec>( pub fn get_indexed_attestation<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
attestation: &Attestation<T>, attestation: &Attestation<T>,

View File

@ -3,7 +3,7 @@ use types::{BeaconStateError as Error, *};
/// Initiate the exit of the validator of the given `index`. /// Initiate the exit of the validator of the given `index`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn initiate_validator_exit<T: EthSpec>( pub fn initiate_validator_exit<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
index: usize, index: usize,

View File

@ -4,7 +4,7 @@ use types::{BeaconStateError as Error, *};
/// Slash the validator with index ``index``. /// Slash the validator with index ``index``.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn slash_validator<T: EthSpec>( pub fn slash_validator<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
slashed_index: usize, slashed_index: usize,

View File

@ -6,7 +6,7 @@ use types::*;
/// Initialize a `BeaconState` from genesis data. /// 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 // TODO: this is quite inefficient and we probably want to rethink how we do this
pub fn initialize_beacon_state_from_eth1<T: EthSpec>( pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
eth1_block_hash: Hash256, eth1_block_hash: Hash256,
@ -15,7 +15,7 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<BeaconState<T>, BlockProcessingError> { ) -> Result<BeaconState<T>, BlockProcessingError> {
let genesis_time = 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 { let eth1_data = Eth1Data {
// Temporary deposit root // Temporary deposit root
deposit_root: Hash256::zero(), deposit_root: Hash256::zero(),
@ -47,7 +47,7 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
/// Determine whether a candidate genesis state is suitable for starting the chain. /// 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<T: EthSpec>(state: &BeaconState<T>, spec: &ChainSpec) -> bool { pub fn is_valid_genesis_state<T: EthSpec>(state: &BeaconState<T>, spec: &ChainSpec) -> bool {
state.genesis_time >= spec.min_genesis_time state.genesis_time >= spec.min_genesis_time
&& state.get_active_validator_indices(T::genesis_epoch()).len() as u64 && state.get_active_validator_indices(T::genesis_epoch()).len() as u64
@ -56,7 +56,7 @@ pub fn is_valid_genesis_state<T: EthSpec>(state: &BeaconState<T>, spec: &ChainSp
/// Activate genesis validators, if their balance is acceptable. /// Activate genesis validators, if their balance is acceptable.
/// ///
/// Spec v0.8.0 /// Spec v0.10.1
pub fn process_activations<T: EthSpec>(state: &mut BeaconState<T>, spec: &ChainSpec) { pub fn process_activations<T: EthSpec>(state: &mut BeaconState<T>, spec: &ChainSpec) {
for (index, validator) in state.validators.iter_mut().enumerate() { for (index, validator) in state.validators.iter_mut().enumerate() {
let balance = state.balances[index]; let balance = state.balances[index];

View File

@ -3,7 +3,7 @@ use errors::{BlockOperationError, BlockProcessingError, HeaderInvalid, IntoWithI
use rayon::prelude::*; use rayon::prelude::*;
use signature_sets::{block_proposal_signature_set, randao_signature_set}; use signature_sets::{block_proposal_signature_set, randao_signature_set};
use std::convert::TryInto; use std::convert::TryInto;
use tree_hash::SignedRoot; use tree_hash::TreeHash;
use types::*; use types::*;
pub use self::verify_attester_slashing::{ 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 `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. /// 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 /// If `block_root` is `Some`, this root is used for verification of the proposer's signature. If it
/// signature. If it is `None` the signed root is calculated here. This parameter only exists to /// is `None` the signing root is computed from scratch. This parameter only exists to avoid
/// avoid re-calculating the root when it is already known. /// 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<T: EthSpec>( pub fn per_block_processing<T: EthSpec>(
mut state: &mut BeaconState<T>, mut state: &mut BeaconState<T>,
block: &BeaconBlock<T>, signed_block: &SignedBeaconBlock<T>,
block_signed_root: Option<Hash256>, block_root: Option<Hash256>,
block_signature_strategy: BlockSignatureStrategy, block_signature_strategy: BlockSignatureStrategy,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), BlockProcessingError> { ) -> Result<(), BlockProcessingError> {
let block = &signed_block.message;
let verify_signatures = match block_signature_strategy { let verify_signatures = match block_signature_strategy {
BlockSignatureStrategy::VerifyBulk => { BlockSignatureStrategy::VerifyBulk => {
// Verify all signatures in the block at once. // Verify all signatures in the block at once.
block_verify!( 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 BlockProcessingError::BulkSignatureVerificationFailed
); );
VerifySignatures::False VerifySignatures::False
@ -89,7 +93,11 @@ pub fn per_block_processing<T: EthSpec>(
BlockSignatureStrategy::NoVerification => VerifySignatures::False, 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. // Ensure the current and previous epoch caches are built.
state.build_committee_cache(RelativeEpoch::Previous, spec)?; state.build_committee_cache(RelativeEpoch::Previous, spec)?;
@ -128,18 +136,16 @@ pub fn per_block_processing<T: EthSpec>(
/// Processes the block header. /// Processes the block header.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_block_header<T: EthSpec>( pub fn process_block_header<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
block: &BeaconBlock<T>, block: &BeaconBlock<T>,
block_signed_root: Option<Hash256>,
verify_signatures: VerifySignatures,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), BlockOperationError<HeaderInvalid>> { ) -> Result<(), BlockOperationError<HeaderInvalid>> {
verify!(block.slot == state.slot, HeaderInvalid::StateSlotMismatch); verify!(block.slot == state.slot, HeaderInvalid::StateSlotMismatch);
let expected_previous_block_root = 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!( verify!(
block.parent_root == expected_previous_block_root, block.parent_root == expected_previous_block_root,
HeaderInvalid::ParentBlockRootMismatch { HeaderInvalid::ParentBlockRootMismatch {
@ -158,24 +164,20 @@ pub fn process_block_header<T: EthSpec>(
HeaderInvalid::ProposerSlashed(proposer_idx) HeaderInvalid::ProposerSlashed(proposer_idx)
); );
if verify_signatures.is_true() {
verify_block_signature(&state, &block, block_signed_root, &spec)?;
}
Ok(()) Ok(())
} }
/// Verifies the signature of a block. /// Verifies the signature of a block.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn verify_block_signature<T: EthSpec>( pub fn verify_block_signature<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
block: &BeaconBlock<T>, block: &SignedBeaconBlock<T>,
block_signed_root: Option<Hash256>, block_root: Option<Hash256>,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), BlockOperationError<HeaderInvalid>> { ) -> Result<(), BlockOperationError<HeaderInvalid>> {
verify!( 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 HeaderInvalid::ProposalSignatureInvalid
); );
@ -185,7 +187,7 @@ pub fn verify_block_signature<T: EthSpec>(
/// Verifies the `randao_reveal` against the block's proposer pubkey and updates /// Verifies the `randao_reveal` against the block's proposer pubkey and updates
/// `state.latest_randao_mixes`. /// `state.latest_randao_mixes`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_randao<T: EthSpec>( pub fn process_randao<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
block: &BeaconBlock<T>, block: &BeaconBlock<T>,
@ -208,7 +210,7 @@ pub fn process_randao<T: EthSpec>(
/// Update the `state.eth1_data_votes` based upon the `eth1_data` provided. /// 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<T: EthSpec>( pub fn process_eth1_data<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
eth1_data: &Eth1Data, eth1_data: &Eth1Data,
@ -225,7 +227,7 @@ pub fn process_eth1_data<T: EthSpec>(
/// Returns `Some(eth1_data)` if adding the given `eth1_data` to `state.eth1_data_votes` would /// Returns `Some(eth1_data)` if adding the given `eth1_data` to `state.eth1_data_votes` would
/// result in a change to `state.eth1_data`. /// result in a change to `state.eth1_data`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_new_eth1_data<T: EthSpec>( pub fn get_new_eth1_data<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
eth1_data: &Eth1Data, eth1_data: &Eth1Data,
@ -249,7 +251,7 @@ pub fn get_new_eth1_data<T: EthSpec>(
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure. /// an `Err` describing the invalid object or cause of failure.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_proposer_slashings<T: EthSpec>( pub fn process_proposer_slashings<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
proposer_slashings: &[ProposerSlashing], proposer_slashings: &[ProposerSlashing],
@ -278,7 +280,7 @@ pub fn process_proposer_slashings<T: EthSpec>(
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure. /// an `Err` describing the invalid object or cause of failure.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_attester_slashings<T: EthSpec>( pub fn process_attester_slashings<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
attester_slashings: &[AttesterSlashing<T>], attester_slashings: &[AttesterSlashing<T>],
@ -332,7 +334,7 @@ pub fn process_attester_slashings<T: EthSpec>(
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure. /// an `Err` describing the invalid object or cause of failure.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_attestations<T: EthSpec>( pub fn process_attestations<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
attestations: &[Attestation<T>], attestations: &[Attestation<T>],
@ -378,7 +380,7 @@ pub fn process_attestations<T: EthSpec>(
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure. /// an `Err` describing the invalid object or cause of failure.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_deposits<T: EthSpec>( pub fn process_deposits<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
deposits: &[Deposit], deposits: &[Deposit],
@ -415,7 +417,7 @@ pub fn process_deposits<T: EthSpec>(
/// Process a single deposit, optionally verifying its merkle proof. /// Process a single deposit, optionally verifying its merkle proof.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_deposit<T: EthSpec>( pub fn process_deposit<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
deposit: &Deposit, deposit: &Deposit,
@ -481,10 +483,10 @@ pub fn process_deposit<T: EthSpec>(
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure. /// an `Err` describing the invalid object or cause of failure.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_exits<T: EthSpec>( pub fn process_exits<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
voluntary_exits: &[VoluntaryExit], voluntary_exits: &[SignedVoluntaryExit],
verify_signatures: VerifySignatures, verify_signatures: VerifySignatures,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), BlockProcessingError> { ) -> Result<(), BlockProcessingError> {
@ -498,7 +500,7 @@ pub fn process_exits<T: EthSpec>(
// Update the state in series. // Update the state in series.
for exit in voluntary_exits { 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(()) Ok(())

View File

@ -1,5 +1,5 @@
use std::convert::TryInto; use std::convert::TryInto;
use tree_hash::SignedRoot; use tree_hash::TreeHash;
use types::test_utils::{ use types::test_utils::{
AttestationTestTask, AttesterSlashingTestTask, DepositTestTask, ExitTestTask, AttestationTestTask, AttesterSlashingTestTask, DepositTestTask, ExitTestTask,
ProposerSlashingTestTask, TestingBeaconBlockBuilder, TestingBeaconStateBuilder, ProposerSlashingTestTask, TestingBeaconBlockBuilder, TestingBeaconStateBuilder,
@ -41,7 +41,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
randao_sk: Option<SecretKey>, randao_sk: Option<SecretKey>,
previous_block_root: Option<Hash256>, previous_block_root: Option<Hash256>,
spec: &ChainSpec, spec: &ChainSpec,
) -> (BeaconBlock<T>, BeaconState<T>) { ) -> (SignedBeaconBlock<T>, BeaconState<T>) {
let (mut state, keypairs) = self.state_builder.build(); let (mut state, keypairs) = self.state_builder.build();
let builder = &mut self.block_builder; let builder = &mut self.block_builder;
@ -51,7 +51,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
match previous_block_root { match previous_block_root {
Some(root) => builder.set_parent_root(root), Some(root) => builder.set_parent_root(root),
None => builder.set_parent_root(Hash256::from_slice( 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<T: EthSpec> BlockProcessingBuilder<T> {
randao_sk: Option<SecretKey>, randao_sk: Option<SecretKey>,
previous_block_root: Option<Hash256>, previous_block_root: Option<Hash256>,
spec: &ChainSpec, spec: &ChainSpec,
) -> (BeaconBlock<T>, BeaconState<T>) { ) -> (SignedBeaconBlock<T>, BeaconState<T>) {
let (mut state, keypairs) = self.state_builder.build(); let (mut state, keypairs) = self.state_builder.build();
let builder = &mut self.block_builder; let builder = &mut self.block_builder;
@ -93,7 +93,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
match previous_block_root { match previous_block_root {
Some(root) => builder.set_parent_root(root), Some(root) => builder.set_parent_root(root),
None => builder.set_parent_root(Hash256::from_slice( 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<T: EthSpec> BlockProcessingBuilder<T> {
randao_sk: Option<SecretKey>, randao_sk: Option<SecretKey>,
previous_block_root: Option<Hash256>, previous_block_root: Option<Hash256>,
spec: &ChainSpec, spec: &ChainSpec,
) -> (BeaconBlock<T>, BeaconState<T>) { ) -> (SignedBeaconBlock<T>, BeaconState<T>) {
let (state, keypairs) = self.state_builder.build(); let (state, keypairs) = self.state_builder.build();
let builder = &mut self.block_builder; let builder = &mut self.block_builder;
@ -150,7 +150,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
match previous_block_root { match previous_block_root {
Some(root) => builder.set_parent_root(root), Some(root) => builder.set_parent_root(root),
None => builder.set_parent_root(Hash256::from_slice( 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<T: EthSpec> BlockProcessingBuilder<T> {
randao_sk: Option<SecretKey>, randao_sk: Option<SecretKey>,
previous_block_root: Option<Hash256>, previous_block_root: Option<Hash256>,
spec: &ChainSpec, spec: &ChainSpec,
) -> (BeaconBlock<T>, BeaconState<T>) { ) -> (SignedBeaconBlock<T>, BeaconState<T>) {
let (state, keypairs) = self.state_builder.build(); let (state, keypairs) = self.state_builder.build();
let builder = &mut self.block_builder; let builder = &mut self.block_builder;
@ -193,7 +193,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
match previous_block_root { match previous_block_root {
Some(root) => builder.set_parent_root(root), Some(root) => builder.set_parent_root(root),
None => builder.set_parent_root(Hash256::from_slice( 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<T: EthSpec> BlockProcessingBuilder<T> {
randao_sk: Option<SecretKey>, randao_sk: Option<SecretKey>,
previous_block_root: Option<Hash256>, previous_block_root: Option<Hash256>,
spec: &ChainSpec, spec: &ChainSpec,
) -> (BeaconBlock<T>, BeaconState<T>) { ) -> (SignedBeaconBlock<T>, BeaconState<T>) {
let (state, keypairs) = self.state_builder.build(); let (state, keypairs) = self.state_builder.build();
let builder = &mut self.block_builder; let builder = &mut self.block_builder;
@ -242,7 +242,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
match previous_block_root { match previous_block_root {
Some(root) => builder.set_parent_root(root), Some(root) => builder.set_parent_root(root),
None => builder.set_parent_root(Hash256::from_slice( 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<T: EthSpec> BlockProcessingBuilder<T> {
randao_sk: Option<SecretKey>, randao_sk: Option<SecretKey>,
previous_block_root: Option<Hash256>, previous_block_root: Option<Hash256>,
spec: &ChainSpec, spec: &ChainSpec,
) -> (BeaconBlock<T>, BeaconState<T>) { ) -> (SignedBeaconBlock<T>, BeaconState<T>) {
let (state, keypairs) = self.state_builder.build(); let (state, keypairs) = self.state_builder.build();
let builder = &mut self.block_builder; let builder = &mut self.block_builder;
@ -284,7 +284,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
match previous_block_root { match previous_block_root {
Some(root) => builder.set_parent_root(root), Some(root) => builder.set_parent_root(root),
None => builder.set_parent_root(Hash256::from_slice( None => builder.set_parent_root(Hash256::from_slice(
&state.latest_block_header.signed_root(), &state.latest_block_header.tree_hash_root(),
)), )),
} }

View File

@ -1,10 +1,12 @@
use super::signature_sets::{Error as SignatureSetError, Result as SignatureSetResult, *}; use super::signature_sets::{Error as SignatureSetError, Result as SignatureSetResult, *};
use crate::common::get_indexed_attestation; use crate::common::get_indexed_attestation;
use crate::per_block_processing::errors::{AttestationInvalid, BlockOperationError}; use crate::per_block_processing::errors::{AttestationInvalid, BlockOperationError};
use bls::{verify_signature_sets, SignatureSet}; use bls::{verify_signature_sets, SignatureSet};
use rayon::prelude::*; use rayon::prelude::*;
use types::{ use types::{
BeaconBlock, BeaconState, BeaconStateError, ChainSpec, EthSpec, Hash256, IndexedAttestation, BeaconState, BeaconStateError, ChainSpec, EthSpec, Hash256, IndexedAttestation,
SignedBeaconBlock,
}; };
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
@ -40,12 +42,12 @@ impl From<BlockOperationError<AttestationInvalid>> for Error {
} }
} }
/// Reads the BLS signatures and keys from a `BeaconBlock`, storing them as a `Vec<SignatureSet>`. /// Reads the BLS signatures and keys from a `SignedBeaconBlock`, storing them as a `Vec<SignatureSet>`.
/// ///
/// This allows for optimizations related to batch BLS operations (see the /// This allows for optimizations related to batch BLS operations (see the
/// `Self::verify_entire_block(..)` function). /// `Self::verify_entire_block(..)` function).
pub struct BlockSignatureVerifier<'a, T: EthSpec> { pub struct BlockSignatureVerifier<'a, T: EthSpec> {
block: &'a BeaconBlock<T>, block: &'a SignedBeaconBlock<T>,
state: &'a BeaconState<T>, state: &'a BeaconState<T>,
spec: &'a ChainSpec, spec: &'a ChainSpec,
sets: Vec<SignatureSet<'a>>, sets: Vec<SignatureSet<'a>>,
@ -54,7 +56,11 @@ pub struct BlockSignatureVerifier<'a, T: EthSpec> {
impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> { impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> {
/// Create a new verifier without any included signatures. See the `include...` functions to /// Create a new verifier without any included signatures. See the `include...` functions to
/// add signatures, and the `verify` /// add signatures, and the `verify`
pub fn new(state: &'a BeaconState<T>, block: &'a BeaconBlock<T>, spec: &'a ChainSpec) -> Self { pub fn new(
state: &'a BeaconState<T>,
block: &'a SignedBeaconBlock<T>,
spec: &'a ChainSpec,
) -> Self {
Self { Self {
block, block,
state, 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. /// are valid.
/// ///
/// * : _Does not verify any signatures in `block.body.deposits`. A block is still valid if it /// * : _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. /// See `Self::verify` for more detail.
pub fn verify_entire_block( pub fn verify_entire_block(
state: &'a BeaconState<T>, state: &'a BeaconState<T>,
block: &'a BeaconBlock<T>, block: &'a SignedBeaconBlock<T>,
block_root: Option<Hash256>,
spec: &'a ChainSpec, spec: &'a ChainSpec,
) -> Result<()> { ) -> Result<()> {
let mut verifier = Self::new(state, block, spec); 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_randao_reveal()?;
verifier.include_proposer_slashings()?; verifier.include_proposer_slashings()?;
verifier.include_attester_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. /// Includes the randao signature for `self.block` for verification.
fn include_randao_reveal(&mut self) -> Result<()> { 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); self.sets.push(set);
Ok(()) Ok(())
} }
@ -138,6 +145,7 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> {
fn include_proposer_slashings(&mut self) -> Result<()> { fn include_proposer_slashings(&mut self) -> Result<()> {
let mut sets: Vec<SignatureSet> = self let mut sets: Vec<SignatureSet> = self
.block .block
.message
.body .body
.proposer_slashings .proposer_slashings
.iter() .iter()
@ -158,6 +166,7 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> {
/// Includes all signatures in `self.block.body.attester_slashings` for verification. /// Includes all signatures in `self.block.body.attester_slashings` for verification.
fn include_attester_slashings(&mut self) -> Result<()> { fn include_attester_slashings(&mut self) -> Result<()> {
self.block self.block
.message
.body .body
.attester_slashings .attester_slashings
.iter() .iter()
@ -175,6 +184,7 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> {
/// Includes all signatures in `self.block.body.attestations` for verification. /// Includes all signatures in `self.block.body.attestations` for verification.
fn include_attestations(&mut self) -> Result<Vec<IndexedAttestation<T>>> { fn include_attestations(&mut self) -> Result<Vec<IndexedAttestation<T>>> {
self.block self.block
.message
.body .body
.attestations .attestations
.iter() .iter()
@ -198,6 +208,7 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> {
fn include_exits(&mut self) -> Result<()> { fn include_exits(&mut self) -> Result<()> {
let mut sets = self let mut sets = self
.block .block
.message
.body .body
.voluntary_exits .voluntary_exits
.iter() .iter()

View File

@ -210,6 +210,11 @@ pub enum AttestationInvalid {
}, },
/// Attestation slot is too far in the past to be included in a block. /// Attestation slot is too far in the past to be included in a block.
IncludedTooLate { state: Slot, attestation: Slot }, 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. /// Attestation target epoch does not match the current or previous epoch.
BadTargetEpoch, BadTargetEpoch,
/// Attestation justified checkpoint doesn't match the state's current or previous justified /// Attestation justified checkpoint doesn't match the state's current or previous justified

View File

@ -11,7 +11,7 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
/// Verify an `IndexedAttestation`. /// Verify an `IndexedAttestation`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn is_valid_indexed_attestation<T: EthSpec>( pub fn is_valid_indexed_attestation<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
indexed_attestation: &IndexedAttestation<T>, indexed_attestation: &IndexedAttestation<T>,
@ -26,14 +26,13 @@ pub fn is_valid_indexed_attestation<T: EthSpec>(
Invalid::MaxIndicesExceed(T::MaxValidatorsPerCommittee::to_usize(), indices.len()) 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<()> { let check_sorted = |list: &[u64]| -> Result<()> {
list.windows(2).enumerate().try_for_each(|(i, pair)| { list.windows(2).enumerate().try_for_each(|(i, pair)| {
// The spec allows duplicates, so use strict comparison (>). if pair[0] < pair[1] {
if pair[0] > pair[1] {
Err(error(Invalid::BadValidatorIndicesOrdering(i)))
} else {
Ok(()) Ok(())
} else {
Err(error(Invalid::BadValidatorIndicesOrdering(i)))
} }
})?; })?;
Ok(()) Ok(())

View File

@ -5,11 +5,12 @@
use bls::{G1Point, G1Ref, SignatureSet, SignedMessage}; use bls::{G1Point, G1Ref, SignatureSet, SignedMessage};
use std::borrow::Cow; use std::borrow::Cow;
use std::convert::TryInto; use std::convert::TryInto;
use tree_hash::{SignedRoot, TreeHash}; use tree_hash::TreeHash;
use types::{ use types::{
AggregateSignature, AttesterSlashing, BeaconBlock, BeaconBlockHeader, BeaconState, AggregateSignature, AttesterSlashing, BeaconBlock, BeaconState, BeaconStateError, ChainSpec,
BeaconStateError, ChainSpec, DepositData, Domain, EthSpec, Hash256, IndexedAttestation, DepositData, Domain, EthSpec, Hash256, IndexedAttestation, ProposerSlashing, PublicKey,
ProposerSlashing, PublicKey, Signature, VoluntaryExit, Signature, SignedBeaconBlock, SignedBeaconBlockHeader, SignedRoot, SignedVoluntaryExit,
SigningRoot,
}; };
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
@ -41,10 +42,11 @@ impl From<BeaconStateError> for Error {
/// A signature set that is valid if a block was signed by the expected block producer. /// 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>( pub fn block_proposal_signature_set<'a, T: EthSpec>(
state: &'a BeaconState<T>, state: &'a BeaconState<T>,
block: &'a BeaconBlock<T>, signed_block: &'a SignedBeaconBlock<T>,
block_signed_root: Option<Hash256>, block_root: Option<Hash256>,
spec: &'a ChainSpec, spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>> { ) -> Result<SignatureSet<'a>> {
let block = &signed_block.message;
let proposer_index = state.get_beacon_proposer_index(block.slot, spec)?; let proposer_index = state.get_beacon_proposer_index(block.slot, spec)?;
let domain = spec.get_domain( let domain = spec.get_domain(
@ -53,17 +55,20 @@ pub fn block_proposal_signature_set<'a, T: EthSpec>(
&state.fork, &state.fork,
); );
let message = if let Some(root) = block_signed_root { let message = if let Some(root) = block_root {
root.as_bytes().to_vec() SigningRoot {
object_root: root,
domain,
}
.tree_hash_root()
} else { } else {
block.signed_root() block.signing_root(domain).as_bytes().to_vec()
}; };
Ok(SignatureSet::single( Ok(SignatureSet::single(
&block.signature, &signed_block.signature,
validator_pubkey(state, proposer_index)?, validator_pubkey(state, proposer_index)?,
message, message,
domain,
)) ))
} }
@ -81,13 +86,12 @@ pub fn randao_signature_set<'a, T: EthSpec>(
&state.fork, &state.fork,
); );
let message = state.current_epoch().tree_hash_root(); let message = state.current_epoch().signing_root(domain);
Ok(SignatureSet::single( Ok(SignatureSet::single(
&block.body.randao_reveal, &block.body.randao_reveal,
validator_pubkey(state, proposer_index)?, validator_pubkey(state, proposer_index)?,
message, message.as_bytes().to_vec(),
domain,
)) ))
} }
@ -102,13 +106,13 @@ pub fn proposer_slashing_signature_set<'a, T: EthSpec>(
Ok(( Ok((
block_header_signature_set( block_header_signature_set(
state, state,
&proposer_slashing.header_1, &proposer_slashing.signed_header_1,
validator_pubkey(state, proposer_index)?, validator_pubkey(state, proposer_index)?,
spec, spec,
)?, )?,
block_header_signature_set( block_header_signature_set(
state, state,
&proposer_slashing.header_2, &proposer_slashing.signed_header_2,
validator_pubkey(state, proposer_index)?, validator_pubkey(state, proposer_index)?,
spec, 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`. /// Returns a signature set that is valid if the given `pubkey` signed the `header`.
fn block_header_signature_set<'a, T: EthSpec>( fn block_header_signature_set<'a, T: EthSpec>(
state: &'a BeaconState<T>, state: &'a BeaconState<T>,
header: &'a BeaconBlockHeader, signed_header: &'a SignedBeaconBlockHeader,
pubkey: Cow<'a, G1Point>, pubkey: Cow<'a, G1Point>,
spec: &'a ChainSpec, spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>> { ) -> Result<SignatureSet<'a>> {
let domain = spec.get_domain( let domain = spec.get_domain(
header.slot.epoch(T::slots_per_epoch()), signed_header.message.slot.epoch(T::slots_per_epoch()),
Domain::BeaconProposer, Domain::BeaconProposer,
&state.fork, &state.fork,
); );
let message = header.signed_root(); let message = signed_header
.message
.signing_root(domain)
.as_bytes()
.to_vec();
Ok(SignatureSet::single( Ok(SignatureSet::single(
&header.signature, &signed_header.signature,
pubkey, pubkey,
message, message,
domain,
)) ))
} }
@ -145,23 +152,22 @@ pub fn indexed_attestation_signature_set<'a, 'b, T: EthSpec>(
indexed_attestation: &'b IndexedAttestation<T>, indexed_attestation: &'b IndexedAttestation<T>,
spec: &'a ChainSpec, spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>> { ) -> Result<SignatureSet<'a>> {
let message = indexed_attestation.data.tree_hash_root();
let pubkeys = indexed_attestation let pubkeys = indexed_attestation
.attesting_indices .attesting_indices
.into_iter() .into_iter()
.map(|&validator_idx| Ok(validator_pubkey(state, validator_idx as usize)?)) .map(|&validator_idx| Ok(validator_pubkey(state, validator_idx as usize)?))
.collect::<Result<_>>()?; .collect::<Result<_>>()?;
let signed_message = SignedMessage::new(pubkeys, message);
let domain = spec.get_domain( let domain = spec.get_domain(
indexed_attestation.data.target.epoch, indexed_attestation.data.target.epoch,
Domain::BeaconAttester, Domain::BeaconAttester,
&state.fork, &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`. /// 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. /// This method is separate to `deposit_signature_set` to satisfy lifetime requirements.
pub fn deposit_pubkey_signature_message( pub fn deposit_pubkey_signature_message(
deposit_data: &DepositData, deposit_data: &DepositData,
spec: &ChainSpec,
) -> Option<(PublicKey, Signature, Vec<u8>)> { ) -> Option<(PublicKey, Signature, Vec<u8>)> {
let pubkey = (&deposit_data.pubkey).try_into().ok()?; let pubkey = (&deposit_data.pubkey).try_into().ok()?;
let signature = (&deposit_data.signature).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)) Some((pubkey, signature, message))
} }
@ -202,43 +214,37 @@ pub fn deposit_pubkey_signature_message(
/// `deposit_pubkey_signature_message`. /// `deposit_pubkey_signature_message`.
pub fn deposit_signature_set<'a>( pub fn deposit_signature_set<'a>(
pubkey_signature_message: &'a (PublicKey, Signature, Vec<u8>), pubkey_signature_message: &'a (PublicKey, Signature, Vec<u8>),
spec: &'a ChainSpec,
) -> SignatureSet<'a> { ) -> SignatureSet<'a> {
let (pubkey, signature, message) = pubkey_signature_message; let (pubkey, signature, message) = pubkey_signature_message;
// Note: Deposits are valid across forks, thus the deposit domain is computed // Note: Deposits are valid across forks, thus the deposit domain is computed
// with the fork zeroed. // with the fork zeroed.
SignatureSet::single( SignatureSet::single(signature, pubkey.g1_ref(), message.clone())
signature,
pubkey.g1_ref(),
message.clone(),
spec.get_deposit_domain(),
)
} }
/// 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. /// validator.
pub fn exit_signature_set<'a, T: EthSpec>( pub fn exit_signature_set<'a, T: EthSpec>(
state: &'a BeaconState<T>, state: &'a BeaconState<T>,
exit: &'a VoluntaryExit, signed_exit: &'a SignedVoluntaryExit,
spec: &'a ChainSpec, spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>> { ) -> Result<SignatureSet<'a>> {
let exit = &signed_exit.message;
let proposer_index = exit.validator_index as usize; let proposer_index = exit.validator_index as usize;
let domain = spec.get_domain(exit.epoch, Domain::VoluntaryExit, &state.fork); 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( Ok(SignatureSet::single(
&exit.signature, &signed_exit.signature,
validator_pubkey(state, proposer_index)?, validator_pubkey(state, proposer_index)?,
message, message,
domain,
)) ))
} }
/// Maps a validator index to a `PublicKey`. /// Maps a validator index to a `PublicKey`.
fn validator_pubkey<'a, T: EthSpec>( pub fn validator_pubkey<'a, T: EthSpec>(
state: &'a BeaconState<T>, state: &'a BeaconState<T>,
validator_index: usize, validator_index: usize,
) -> Result<Cow<'a, G1Point>> { ) -> Result<Cow<'a, G1Point>> {

View File

@ -3,7 +3,6 @@
use super::block_processing_builder::BlockProcessingBuilder; use super::block_processing_builder::BlockProcessingBuilder;
use super::errors::*; use super::errors::*;
use crate::{per_block_processing, BlockSignatureStrategy}; use crate::{per_block_processing, BlockSignatureStrategy};
use tree_hash::SignedRoot;
use types::test_utils::{ use types::test_utils::{
AttestationTestTask, AttesterSlashingTestTask, DepositTestTask, ExitTestTask, AttestationTestTask, AttesterSlashingTestTask, DepositTestTask, ExitTestTask,
ProposerSlashingTestTask, ProposerSlashingTestTask,
@ -16,6 +15,8 @@ pub const SLOT_OFFSET: u64 = 4;
pub const EXIT_SLOT_OFFSET: u64 = 2048; pub const EXIT_SLOT_OFFSET: u64 = 2048;
pub const NUM_ATTESTATIONS: u64 = 1; pub const NUM_ATTESTATIONS: u64 = 1;
type E = MainnetEthSpec;
#[test] #[test]
fn valid_block_ok() { fn valid_block_ok() {
let spec = MainnetEthSpec::default_spec(); 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); let (mut block, mut state) = builder.build(None, None, &spec);
state.slot = Slot::new(133_713); state.slot = Slot::new(133_713);
block.slot = Slot::new(424_242); block.message.slot = Slot::new(424_242);
let result = per_block_processing( let result = per_block_processing(
&mut state, &mut state,
@ -77,8 +78,8 @@ fn invalid_parent_block_root() {
result, result,
Err(BlockProcessingError::HeaderInvalid { Err(BlockProcessingError::HeaderInvalid {
reason: HeaderInvalid::ParentBlockRootMismatch { reason: HeaderInvalid::ParentBlockRootMismatch {
state: Hash256::from_slice(&state.latest_block_header.signed_root()), state: state.latest_block_header.canonical_root(),
block: block.parent_root block: block.parent_root()
} }
}) })
); );
@ -88,14 +89,11 @@ fn invalid_parent_block_root() {
fn invalid_block_signature() { fn invalid_block_signature() {
let spec = MainnetEthSpec::default_spec(); let spec = MainnetEthSpec::default_spec();
let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); 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 // sign the block with a keypair that is not the expected proposer
let keypair = Keypair::random(); let keypair = Keypair::random();
let message = block.signed_root(); let block = block.message.sign(&keypair.sk, &state.fork, &spec);
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);
// process block with invalid block signature // process block with invalid block signature
let result = per_block_processing( 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] #[test]
fn invalid_attestation_bad_indexed_attestation_bad_signature() { fn invalid_attestation_bad_indexed_attestation_bad_signature() {
let spec = MainnetEthSpec::default_spec(); let spec = MainnetEthSpec::default_spec();
@ -787,7 +731,7 @@ fn invalid_attestation_included_too_early() {
reason: AttestationInvalid::IncludedTooEarly { reason: AttestationInvalid::IncludedTooEarly {
state: state.slot, state: state.slot,
delay: spec.min_attestation_inclusion_delay, 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, index: 0,
reason: AttestationInvalid::IncludedTooLate { reason: AttestationInvalid::IncludedTooLate {
state: state.slot, state: state.slot,
attestation: block.body.attestations[0].data.slot, attestation: block.message.body.attestations[0].data.slot,
} }
}) })
); );
} }
#[test] #[test]
fn invalid_attestation_bad_target_epoch() { fn invalid_attestation_target_epoch_slot_mismatch() {
let spec = MainnetEthSpec::default_spec(); let spec = MainnetEthSpec::default_spec();
// note to maintainer: might need to increase validator count if we get NoCommittee // note to maintainer: might need to increase validator count if we get NoCommittee
let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT);
let test_task = AttestationTestTask::BadTargetEpoch; let test_task = AttestationTestTask::TargetEpochSlotMismatch;
let (block, mut state) = let (block, mut state) =
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec); builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
@ -839,20 +783,16 @@ fn invalid_attestation_bad_target_epoch() {
&spec, &spec,
); );
// Expecting BadTargetEpoch because the target epoch is bigger by one than the epoch expected let attestation = &block.message.body.attestations[0].data;
assert!( assert_eq!(
result result,
== Err(BlockProcessingError::BeaconStateError( Err(BlockProcessingError::AttestationInvalid {
BeaconStateError::NoCommittee { index: 0,
slot: Slot::new(0), reason: AttestationInvalid::TargetEpochSlotMismatch {
index: 0 target_epoch: attestation.target.epoch,
} slot_epoch: attestation.slot.epoch(E::slots_per_epoch()),
)) }
|| result })
== Err(BlockProcessingError::AttestationInvalid {
index: 0,
reason: AttestationInvalid::BadTargetEpoch
})
); );
} }

View File

@ -15,7 +15,7 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
/// ///
/// Optionally verifies the aggregate signature, depending on `verify_signatures`. /// Optionally verifies the aggregate signature, depending on `verify_signatures`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn verify_attestation_for_block_inclusion<T: EthSpec>( pub fn verify_attestation_for_block_inclusion<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
attestation: &Attestation<T>, attestation: &Attestation<T>,
@ -49,7 +49,7 @@ pub fn verify_attestation_for_block_inclusion<T: EthSpec>(
/// Returns a descriptive `Err` if the attestation is malformed or does not accurately reflect the /// Returns a descriptive `Err` if the attestation is malformed or does not accurately reflect the
/// prior blocks in `state`. /// prior blocks in `state`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn verify_attestation_for_state<T: EthSpec>( pub fn verify_attestation_for_state<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
attestation: &Attestation<T>, attestation: &Attestation<T>,
@ -75,12 +75,19 @@ pub fn verify_attestation_for_state<T: EthSpec>(
/// Check target epoch and source checkpoint. /// Check target epoch and source checkpoint.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
fn verify_casper_ffg_vote<T: EthSpec>( fn verify_casper_ffg_vote<T: EthSpec>(
attestation: &Attestation<T>, attestation: &Attestation<T>,
state: &BeaconState<T>, state: &BeaconState<T>,
) -> Result<()> { ) -> Result<()> {
let data = &attestation.data; 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() { if data.target.epoch == state.current_epoch() {
verify!( verify!(
data.source == state.current_justified_checkpoint, data.source == state.current_justified_checkpoint,

View File

@ -15,7 +15,7 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
/// ///
/// Returns `Ok(())` if the `AttesterSlashing` is valid, otherwise indicates the reason for invalidity. /// 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<T: EthSpec>( pub fn verify_attester_slashing<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
attester_slashing: &AttesterSlashing<T>, attester_slashing: &AttesterSlashing<T>,
@ -47,7 +47,7 @@ pub fn verify_attester_slashing<T: EthSpec>(
/// ///
/// Returns Ok(indices) if `indices.len() > 0`. /// Returns Ok(indices) if `indices.len() > 0`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_slashable_indices<T: EthSpec>( pub fn get_slashable_indices<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
attester_slashing: &AttesterSlashing<T>, attester_slashing: &AttesterSlashing<T>,

View File

@ -14,13 +14,13 @@ fn error(reason: DepositInvalid) -> BlockOperationError<DepositInvalid> {
/// Verify `Deposit.pubkey` signed `Deposit.signature`. /// 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<()> { 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))?; .ok_or_else(|| error(DepositInvalid::BadBlsBytes))?;
verify!( verify!(
deposit_signature_set(&deposit_signature_message, spec).is_valid(), deposit_signature_set(&deposit_signature_message).is_valid(),
DepositInvalid::BadSignature DepositInvalid::BadSignature
); );
@ -46,7 +46,7 @@ pub fn get_existing_validator_index<T: EthSpec>(
/// The deposit index is provided as a parameter so we can check proofs /// The deposit index is provided as a parameter so we can check proofs
/// before they're due to be processed, and in parallel. /// before they're due to be processed, and in parallel.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn verify_deposit_merkle_proof<T: EthSpec>( pub fn verify_deposit_merkle_proof<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
deposit: &Deposit, deposit: &Deposit,

View File

@ -13,10 +13,10 @@ fn error(reason: ExitInvalid) -> BlockOperationError<ExitInvalid> {
/// ///
/// Returns `Ok(())` if the `Exit` is valid, otherwise indicates the reason for invalidity. /// 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<T: EthSpec>( pub fn verify_exit<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
exit: &VoluntaryExit, exit: &SignedVoluntaryExit,
verify_signatures: VerifySignatures, verify_signatures: VerifySignatures,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<()> { ) -> Result<()> {
@ -25,10 +25,10 @@ pub fn verify_exit<T: EthSpec>(
/// Like `verify_exit` but doesn't run checks which may become true in future states. /// 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<T: EthSpec>( pub fn verify_exit_time_independent_only<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
exit: &VoluntaryExit, exit: &SignedVoluntaryExit,
verify_signatures: VerifySignatures, verify_signatures: VerifySignatures,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<()> { ) -> Result<()> {
@ -37,14 +37,16 @@ pub fn verify_exit_time_independent_only<T: EthSpec>(
/// Parametric version of `verify_exit` that skips some checks if `time_independent_only` is true. /// 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<T: EthSpec>( fn verify_exit_parametric<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
exit: &VoluntaryExit, signed_exit: &SignedVoluntaryExit,
verify_signatures: VerifySignatures, verify_signatures: VerifySignatures,
spec: &ChainSpec, spec: &ChainSpec,
time_independent_only: bool, time_independent_only: bool,
) -> Result<()> { ) -> Result<()> {
let exit = &signed_exit.message;
let validator = state let validator = state
.validators .validators
.get(exit.validator_index as usize) .get(exit.validator_index as usize)
@ -82,7 +84,7 @@ fn verify_exit_parametric<T: EthSpec>(
if verify_signatures.is_true() { if verify_signatures.is_true() {
verify!( verify!(
exit_signature_set(state, exit, spec)?.is_valid(), exit_signature_set(state, signed_exit, spec)?.is_valid(),
ExitInvalid::BadSignature ExitInvalid::BadSignature
); );
} }

View File

@ -14,7 +14,7 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
/// ///
/// Returns `Ok(())` if the `ProposerSlashing` is valid, otherwise indicates the reason for invalidity. /// 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<T: EthSpec>( pub fn verify_proposer_slashing<T: EthSpec>(
proposer_slashing: &ProposerSlashing, proposer_slashing: &ProposerSlashing,
state: &BeaconState<T>, state: &BeaconState<T>,
@ -28,16 +28,17 @@ pub fn verify_proposer_slashing<T: EthSpec>(
// Verify slots match // Verify slots match
verify!( 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( Invalid::ProposalSlotMismatch(
proposer_slashing.header_1.slot, proposer_slashing.signed_header_1.message.slot,
proposer_slashing.header_2.slot proposer_slashing.signed_header_2.message.slot
) )
); );
// But the headers are different // But the headers are different
verify!( verify!(
proposer_slashing.header_1 != proposer_slashing.header_2, proposer_slashing.signed_header_1 != proposer_slashing.signed_header_2,
Invalid::ProposalsIdentical Invalid::ProposalsIdentical
); );

View File

@ -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 /// 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. /// 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<T: EthSpec>( pub fn per_epoch_processing<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
spec: &ChainSpec, spec: &ChainSpec,
@ -66,7 +66,7 @@ pub fn per_epoch_processing<T: EthSpec>(
/// - `finalized_epoch` /// - `finalized_epoch`
/// - `finalized_root` /// - `finalized_root`
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[allow(clippy::if_same_then_else)] // For readability and consistency with spec. #[allow(clippy::if_same_then_else)] // For readability and consistency with spec.
pub fn process_justification_and_finalization<T: EthSpec>( pub fn process_justification_and_finalization<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
@ -134,7 +134,7 @@ pub fn process_justification_and_finalization<T: EthSpec>(
/// Finish up an epoch update. /// Finish up an epoch update.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_final_updates<T: EthSpec>( pub fn process_final_updates<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
spec: &ChainSpec, spec: &ChainSpec,

View File

@ -33,7 +33,7 @@ impl std::ops::AddAssign for Delta {
/// Apply attester and proposer rewards. /// Apply attester and proposer rewards.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_rewards_and_penalties<T: EthSpec>( pub fn process_rewards_and_penalties<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
validator_statuses: &mut ValidatorStatuses, validator_statuses: &mut ValidatorStatuses,
@ -67,7 +67,7 @@ pub fn process_rewards_and_penalties<T: EthSpec>(
/// For each attesting validator, reward the proposer who was first to include their attestation. /// 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<T: EthSpec>( fn get_proposer_deltas<T: EthSpec>(
deltas: &mut Vec<Delta>, deltas: &mut Vec<Delta>,
state: &BeaconState<T>, state: &BeaconState<T>,
@ -100,7 +100,7 @@ fn get_proposer_deltas<T: EthSpec>(
/// Apply rewards for participation in attestations during the previous epoch. /// Apply rewards for participation in attestations during the previous epoch.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
fn get_attestation_deltas<T: EthSpec>( fn get_attestation_deltas<T: EthSpec>(
deltas: &mut Vec<Delta>, deltas: &mut Vec<Delta>,
state: &BeaconState<T>, state: &BeaconState<T>,
@ -133,7 +133,7 @@ fn get_attestation_deltas<T: EthSpec>(
/// Determine the delta for a single validator, sans proposer rewards. /// Determine the delta for a single validator, sans proposer rewards.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
fn get_attestation_delta<T: EthSpec>( fn get_attestation_delta<T: EthSpec>(
validator: &ValidatorStatus, validator: &ValidatorStatus,
total_balances: &TotalBalances, total_balances: &TotalBalances,

View File

@ -2,7 +2,7 @@ use types::{BeaconStateError as Error, *};
/// Process slashings. /// Process slashings.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_slashings<T: EthSpec>( pub fn process_slashings<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
total_balance: u64, total_balance: u64,

View File

@ -5,7 +5,7 @@ use types::*;
/// Performs a validator registry update, if required. /// Performs a validator registry update, if required.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_registry_updates<T: EthSpec>( pub fn process_registry_updates<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
spec: &ChainSpec, spec: &ChainSpec,
@ -15,10 +15,6 @@ pub fn process_registry_updates<T: EthSpec>(
// We assume it's safe to re-order the change in eligibility and `initiate_validator_exit`. // 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. // Rest assured exiting validators will still be exited in the same order as in the spec.
let current_epoch = state.current_epoch(); 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| { let is_exiting_validator = |validator: &Validator| {
validator.is_active_at(current_epoch) validator.is_active_at(current_epoch)
&& validator.effective_balance <= spec.ejection_balance && validator.effective_balance <= spec.ejection_balance
@ -27,16 +23,18 @@ pub fn process_registry_updates<T: EthSpec>(
.validators .validators
.iter() .iter()
.enumerate() .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)| { .partition_map(|(index, validator)| {
if is_eligible(validator) { if validator.is_eligible_for_activation_queue(spec) {
Either::Left(index) Either::Left(index)
} else { } else {
Either::Right(index) Either::Right(index)
} }
}); });
for index in eligible_validators { 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 { for index in exiting_validators {
initiate_validator_exit(state, index, spec)?; initiate_validator_exit(state, index, spec)?;
@ -47,22 +45,17 @@ pub fn process_registry_updates<T: EthSpec>(
.validators .validators
.iter() .iter()
.enumerate() .enumerate()
.filter(|(_, validator)| { .filter(|(_, validator)| validator.is_eligible_for_activation(state, spec))
validator.activation_eligibility_epoch != spec.far_future_epoch .sorted_by_key(|(index, validator)| (validator.activation_eligibility_epoch, *index))
&& validator.activation_epoch
>= state.compute_activation_exit_epoch(state.finalized_checkpoint.epoch, spec)
})
.sorted_by_key(|(_, validator)| validator.activation_eligibility_epoch)
.map(|(index, _)| index) .map(|(index, _)| index)
.collect_vec(); .collect_vec();
// Dequeue validators for activation up to churn limit
let churn_limit = state.get_churn_limit(spec)? as usize; let churn_limit = state.get_churn_limit(spec)? as usize;
let delayed_activation_epoch = state.compute_activation_exit_epoch(current_epoch, spec); let delayed_activation_epoch = state.compute_activation_exit_epoch(current_epoch, spec);
for index in activation_queue.into_iter().take(churn_limit) { for index in activation_queue.into_iter().take(churn_limit) {
let validator = &mut state.validators[index]; 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(()) Ok(())

View File

@ -144,7 +144,7 @@ impl ValidatorStatuses {
/// - Active validators /// - Active validators
/// - Total balances for the current and previous epochs. /// - Total balances for the current and previous epochs.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn new<T: EthSpec>( pub fn new<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
spec: &ChainSpec, spec: &ChainSpec,
@ -184,7 +184,7 @@ impl ValidatorStatuses {
/// Process some attestations from the given `state` updating the `statuses` and /// Process some attestations from the given `state` updating the `statuses` and
/// `total_balances` fields. /// `total_balances` fields.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn process_attestations<T: EthSpec>( pub fn process_attestations<T: EthSpec>(
&mut self, &mut self,
state: &BeaconState<T>, state: &BeaconState<T>,
@ -263,7 +263,7 @@ impl ValidatorStatuses {
/// Returns `true` if the attestation's FFG target is equal to the hash of the `state`'s first /// Returns `true` if the attestation's FFG target is equal to the hash of the `state`'s first
/// beacon block in the given `epoch`. /// beacon block in the given `epoch`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
fn target_matches_epoch_start_block<T: EthSpec>( fn target_matches_epoch_start_block<T: EthSpec>(
a: &PendingAttestation<T>, a: &PendingAttestation<T>,
state: &BeaconState<T>, state: &BeaconState<T>,
@ -278,7 +278,7 @@ fn target_matches_epoch_start_block<T: EthSpec>(
/// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for /// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for
/// the current slot of the `PendingAttestation`. /// the current slot of the `PendingAttestation`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
fn has_common_beacon_block_root<T: EthSpec>( fn has_common_beacon_block_root<T: EthSpec>(
a: &PendingAttestation<T>, a: &PendingAttestation<T>,
state: &BeaconState<T>, state: &BeaconState<T>,

View File

@ -13,7 +13,7 @@ pub enum Error {
/// `state_root` is `None`, the root of `state` will be computed using a cached tree hash. /// `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. /// 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<T: EthSpec>( pub fn per_slot_processing<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
state_root: Option<Hash256>, state_root: Option<Hash256>,

View File

@ -52,7 +52,7 @@ impl<T: EthSpec> BlockBuilder<T> {
self.state_builder.build_caches(&spec).unwrap(); self.state_builder.build_caches(&spec).unwrap();
} }
pub fn build(mut self, spec: &ChainSpec) -> (BeaconBlock<T>, BeaconState<T>) { pub fn build(mut self, spec: &ChainSpec) -> (SignedBeaconBlock<T>, BeaconState<T>) {
let (mut state, keypairs) = self.state_builder.build(); let (mut state, keypairs) = self.state_builder.build();
let builder = &mut self.block_builder; let builder = &mut self.block_builder;

View File

@ -4,13 +4,13 @@ use state_processing::{
per_block_processing, test_utils::BlockBuilder, BlockProcessingError, BlockSignatureStrategy, per_block_processing, test_utils::BlockBuilder, BlockProcessingError, BlockSignatureStrategy,
}; };
use types::{ use types::{
AggregateSignature, BeaconBlock, BeaconState, ChainSpec, EthSpec, Keypair, MinimalEthSpec, AggregateSignature, BeaconState, ChainSpec, EthSpec, Keypair, MinimalEthSpec, Signature,
Signature, Slot, SignedBeaconBlock, Slot,
}; };
const VALIDATOR_COUNT: usize = 64; const VALIDATOR_COUNT: usize = 64;
fn get_block<T, F>(mut mutate_builder: F) -> (BeaconBlock<T>, BeaconState<T>) fn get_block<T, F>(mut mutate_builder: F) -> (SignedBeaconBlock<T>, BeaconState<T>)
where where
T: EthSpec, T: EthSpec,
F: FnMut(&mut BlockBuilder<T>), F: FnMut(&mut BlockBuilder<T>),
@ -27,7 +27,7 @@ fn test_scenario<T: EthSpec, F, G>(mutate_builder: F, mut invalidate_block: G, s
where where
T: EthSpec, T: EthSpec,
F: FnMut(&mut BlockBuilder<T>), F: FnMut(&mut BlockBuilder<T>),
G: FnMut(&mut BeaconBlock<T>), G: FnMut(&mut SignedBeaconBlock<T>),
{ {
let (mut block, mut state) = get_block::<T, _>(mutate_builder); let (mut block, mut state) = get_block::<T, _>(mutate_builder);
@ -100,7 +100,7 @@ fn agg_sig() -> AggregateSignature {
// TODO: use lazy static // TODO: use lazy static
fn sig() -> Signature { fn sig() -> Signature {
let keypair = Keypair::random(); let keypair = Keypair::random();
Signature::new(&[42, 42], 12, &keypair.sk) Signature::new(&[42, 42], &keypair.sk)
} }
type TestEthSpec = MinimalEthSpec; type TestEthSpec = MinimalEthSpec;
@ -119,7 +119,11 @@ mod signatures_minimal {
fn randao() { fn randao() {
let spec = &TestEthSpec::default_spec(); let spec = &TestEthSpec::default_spec();
test_scenario::<TestEthSpec, _, _>(|_| {}, |block| block.body.randao_reveal = sig(), spec); test_scenario::<TestEthSpec, _, _>(
|_| {},
|block| block.message.body.randao_reveal = sig(),
spec,
);
} }
#[test] #[test]
@ -130,14 +134,22 @@ mod signatures_minimal {
|mut builder| { |mut builder| {
builder.num_proposer_slashings = 1; 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, spec,
); );
test_scenario::<TestEthSpec, _, _>( test_scenario::<TestEthSpec, _, _>(
|mut builder| { |mut builder| {
builder.num_proposer_slashings = 1; 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, spec,
); );
} }
@ -150,14 +162,22 @@ mod signatures_minimal {
|mut builder| { |mut builder| {
builder.num_attester_slashings = 1; 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, spec,
); );
test_scenario::<TestEthSpec, _, _>( test_scenario::<TestEthSpec, _, _>(
|mut builder| { |mut builder| {
builder.num_attester_slashings = 1; 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, spec,
); );
} }
@ -170,7 +190,7 @@ mod signatures_minimal {
|mut builder| { |mut builder| {
builder.num_attestations = 1; builder.num_attestations = 1;
}, },
|block| block.body.attestations[0].signature = agg_sig(), |block| block.message.body.attestations[0].signature = agg_sig(),
spec, spec,
); );
} }
@ -185,7 +205,7 @@ mod signatures_minimal {
|mut builder| { |mut builder| {
builder.num_deposits = 1; builder.num_deposits = 1;
}, },
|block| block.body.deposits[0].data.signature = sig().into(), |block| block.message.body.deposits[0].data.signature = sig().into(),
spec, spec,
); );
} }
@ -201,7 +221,7 @@ mod signatures_minimal {
|mut builder| { |mut builder| {
builder.num_exits = 1; builder.num_exits = 1;
}, },
|block| block.body.voluntary_exits[0].signature = sig(), |block| block.message.body.voluntary_exits[0].signature = sig(),
spec, spec,
); );
} }

View File

@ -1,14 +1,13 @@
use super::{ use super::{
AggregateSignature, AttestationData, BitList, ChainSpec, Domain, EthSpec, Fork, SecretKey, AggregateSignature, AttestationData, BitList, ChainSpec, Domain, EthSpec, Fork, SecretKey,
Signature, Signature, SignedRoot,
}; };
use crate::test_utils::TestRandom; use crate::test_utils::TestRandom;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
use tree_hash::TreeHash; use tree_hash_derive::TreeHash;
use tree_hash_derive::{SignedRoot, TreeHash};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Error { pub enum Error {
@ -18,24 +17,12 @@ pub enum Error {
/// Details an attestation that can be slashable. /// Details an attestation that can be slashable.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive( #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
Debug,
Clone,
PartialEq,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
SignedRoot,
)]
#[serde(bound = "T: EthSpec")] #[serde(bound = "T: EthSpec")]
pub struct Attestation<T: EthSpec> { pub struct Attestation<T: EthSpec> {
pub aggregation_bits: BitList<T::MaxValidatorsPerCommittee>, pub aggregation_bits: BitList<T::MaxValidatorsPerCommittee>,
pub data: AttestationData, pub data: AttestationData,
#[signed_root(skip_hashing)]
pub signature: AggregateSignature, pub signature: AggregateSignature,
} }
@ -79,11 +66,11 @@ impl<T: EthSpec> Attestation<T> {
.set(committee_position, true) .set(committee_position, true)
.map_err(Error::SszTypesError)?; .map_err(Error::SszTypesError)?;
let message = self.data.tree_hash_root();
let domain = spec.get_domain(self.data.target.epoch, Domain::BeaconAttester, fork); let domain = spec.get_domain(self.data.target.epoch, Domain::BeaconAttester, fork);
let message = self.data.signing_root(domain);
self.signature self.signature
.add(&Signature::new(&message, domain, secret_key)); .add(&Signature::new(message.as_bytes(), secret_key));
Ok(()) Ok(())
} }
@ -95,5 +82,5 @@ mod tests {
use super::*; use super::*;
use crate::*; use crate::*;
ssz_tests!(Attestation<MainnetEthSpec>); ssz_and_tree_hash_tests!(Attestation<MainnetEthSpec>);
} }

View File

@ -1,5 +1,5 @@
use crate::test_utils::TestRandom; use crate::test_utils::TestRandom;
use crate::{Checkpoint, Hash256, Slot}; use crate::{Checkpoint, Hash256, SignedRoot, Slot};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
/// The data upon which an attestation is based. /// The data upon which an attestation is based.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive( #[derive(
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Encode, Decode, TreeHash, TestRandom, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Encode, Decode, TreeHash, TestRandom,
)] )]
@ -24,9 +24,11 @@ pub struct AttestationData {
pub target: Checkpoint, pub target: Checkpoint,
} }
impl SignedRoot for AttestationData {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
ssz_tests!(AttestationData); ssz_and_tree_hash_tests!(AttestationData);
} }

View File

@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash;
/// Two conflicting attestations. /// Two conflicting attestations.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[serde(bound = "T: EthSpec")] #[serde(bound = "T: EthSpec")]
pub struct AttesterSlashing<T: EthSpec> { pub struct AttesterSlashing<T: EthSpec> {
@ -20,5 +20,5 @@ mod tests {
use super::*; use super::*;
use crate::*; use crate::*;
ssz_tests!(AttesterSlashing<MainnetEthSpec>); ssz_and_tree_hash_tests!(AttesterSlashing<MainnetEthSpec>);
} }

View File

@ -5,38 +5,27 @@ use bls::Signature;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
use tree_hash::{SignedRoot, TreeHash}; use tree_hash::TreeHash;
use tree_hash_derive::{SignedRoot, TreeHash}; use tree_hash_derive::TreeHash;
/// A block of the `BeaconChain`. /// A block of the `BeaconChain`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive( #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
SignedRoot,
)]
#[serde(bound = "T: EthSpec")] #[serde(bound = "T: EthSpec")]
pub struct BeaconBlock<T: EthSpec> { pub struct BeaconBlock<T: EthSpec> {
pub slot: Slot, pub slot: Slot,
pub parent_root: Hash256, pub parent_root: Hash256,
pub state_root: Hash256, pub state_root: Hash256,
pub body: BeaconBlockBody<T>, pub body: BeaconBlockBody<T>,
#[signed_root(skip_hashing)]
pub signature: Signature,
} }
impl<T: EthSpec> SignedRoot for BeaconBlock<T> {}
impl<T: EthSpec> BeaconBlock<T> { impl<T: EthSpec> BeaconBlock<T> {
/// Returns an empty block to be used during genesis. /// Returns an empty block to be used during genesis.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn empty(spec: &ChainSpec) -> Self { pub fn empty(spec: &ChainSpec) -> Self {
BeaconBlock { BeaconBlock {
slot: spec.genesis_slot, slot: spec.genesis_slot,
@ -56,7 +45,6 @@ impl<T: EthSpec> BeaconBlock<T> {
deposits: VariableList::empty(), deposits: VariableList::empty(),
voluntary_exits: VariableList::empty(), voluntary_exits: VariableList::empty(),
}, },
signature: Signature::empty_signature(),
} }
} }
@ -65,11 +53,11 @@ impl<T: EthSpec> BeaconBlock<T> {
self.slot.epoch(T::slots_per_epoch()) 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 { 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. /// Returns a full `BeaconBlockHeader` of this block.
@ -79,33 +67,40 @@ impl<T: EthSpec> BeaconBlock<T> {
/// ///
/// Note: performs a full tree-hash of `self.body`. /// Note: performs a full tree-hash of `self.body`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn block_header(&self) -> BeaconBlockHeader { pub fn block_header(&self) -> BeaconBlockHeader {
BeaconBlockHeader { BeaconBlockHeader {
slot: self.slot, slot: self.slot,
parent_root: self.parent_root, parent_root: self.parent_root,
state_root: self.state_root, state_root: self.state_root,
body_root: Hash256::from_slice(&self.body.tree_hash_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()`. /// 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 { pub fn temporary_block_header(&self) -> BeaconBlockHeader {
BeaconBlockHeader { BeaconBlockHeader {
state_root: Hash256::zero(), state_root: Hash256::zero(),
signature: Signature::empty_signature(),
..self.block_header() ..self.block_header()
} }
} }
/// Signs `self`. /// Signs `self`, producing a `SignedBeaconBlock`.
pub fn sign(&mut self, secret_key: &SecretKey, fork: &Fork, spec: &ChainSpec) { pub fn sign(
let message = self.signed_root(); self,
let domain = spec.get_domain(self.epoch(), Domain::BeaconProposer, &fork); secret_key: &SecretKey,
self.signature = Signature::new(&message, domain, &secret_key); fork: &Fork,
spec: &ChainSpec,
) -> SignedBeaconBlock<T> {
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<T: EthSpec> BeaconBlock<T> {
mod tests { mod tests {
use super::*; use super::*;
ssz_tests!(BeaconBlock<MainnetEthSpec>); ssz_and_tree_hash_tests!(BeaconBlock<MainnetEthSpec>);
} }

View File

@ -10,7 +10,7 @@ use tree_hash_derive::TreeHash;
/// The body of a `BeaconChain` block, containing operations. /// 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)] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[serde(bound = "T: EthSpec")] #[serde(bound = "T: EthSpec")]
pub struct BeaconBlockBody<T: EthSpec> { pub struct BeaconBlockBody<T: EthSpec> {
@ -25,12 +25,12 @@ pub struct BeaconBlockBody<T: EthSpec> {
pub attester_slashings: VariableList<AttesterSlashing<T>, T::MaxAttesterSlashings>, pub attester_slashings: VariableList<AttesterSlashing<T>, T::MaxAttesterSlashings>,
pub attestations: VariableList<Attestation<T>, T::MaxAttestations>, pub attestations: VariableList<Attestation<T>, T::MaxAttestations>,
pub deposits: VariableList<Deposit, T::MaxDeposits>, pub deposits: VariableList<Deposit, T::MaxDeposits>,
pub voluntary_exits: VariableList<VoluntaryExit, T::MaxVoluntaryExits>, pub voluntary_exits: VariableList<SignedVoluntaryExit, T::MaxVoluntaryExits>,
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
ssz_tests!(BeaconBlockBody<MainnetEthSpec>); ssz_and_tree_hash_tests!(BeaconBlockBody<MainnetEthSpec>);
} }

View File

@ -1,55 +1,59 @@
use crate::test_utils::TestRandom; use crate::test_utils::TestRandom;
use crate::*; use crate::*;
use bls::Signature;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
use tree_hash::{SignedRoot, TreeHash}; use tree_hash::TreeHash;
use tree_hash_derive::{SignedRoot, TreeHash}; use tree_hash_derive::TreeHash;
/// A header of a `BeaconBlock`. /// A header of a `BeaconBlock`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive( #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
SignedRoot,
)]
pub struct BeaconBlockHeader { pub struct BeaconBlockHeader {
pub slot: Slot, pub slot: Slot,
pub parent_root: Hash256, pub parent_root: Hash256,
pub state_root: Hash256, pub state_root: Hash256,
pub body_root: Hash256, pub body_root: Hash256,
#[signed_root(skip_hashing)]
pub signature: Signature,
} }
impl SignedRoot for BeaconBlockHeader {}
impl BeaconBlockHeader { impl BeaconBlockHeader {
/// Returns the `tree_hash_root` of the header. /// Returns the `tree_hash_root` of the header.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn canonical_root(&self) -> Hash256 { 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`. /// Given a `body`, consumes `self` and returns a complete `BeaconBlock`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn into_block<T: EthSpec>(self, body: BeaconBlockBody<T>) -> BeaconBlock<T> { pub fn into_block<T: EthSpec>(self, body: BeaconBlockBody<T>) -> BeaconBlock<T> {
BeaconBlock { BeaconBlock {
slot: self.slot, slot: self.slot,
parent_root: self.parent_root, parent_root: self.parent_root,
state_root: self.state_root, state_root: self.state_root,
body, body,
signature: self.signature, }
}
/// Signs `self`, producing a `SignedBeaconBlockHeader`.
pub fn sign<E: EthSpec>(
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 { mod tests {
use super::*; use super::*;
ssz_tests!(BeaconBlockHeader); ssz_and_tree_hash_tests!(BeaconBlockHeader);
} }

View File

@ -90,7 +90,7 @@ impl AllowNextEpoch {
/// The state of the `BeaconChain` at some slot. /// The state of the `BeaconChain` at some slot.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive( #[derive(
Debug, Debug,
PartialEq, PartialEq,
@ -181,13 +181,17 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// Not a complete genesis state, see `initialize_beacon_state_from_eth1`. /// 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 { pub fn new(genesis_time: u64, eth1_data: Eth1Data, spec: &ChainSpec) -> Self {
BeaconState { BeaconState {
// Versioning // Versioning
genesis_time, genesis_time,
slot: spec.genesis_slot, 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 // History
latest_block_header: BeaconBlock::<T>::empty(spec).temporary_block_header(), latest_block_header: BeaconBlock::<T>::empty(spec).temporary_block_header(),
@ -234,7 +238,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Returns the `tree_hash_root` of the state. /// Returns the `tree_hash_root` of the state.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn canonical_root(&self) -> Hash256 { pub fn canonical_root(&self) -> Hash256 {
Hash256::from_slice(&self.tree_hash_root()[..]) Hash256::from_slice(&self.tree_hash_root()[..])
} }
@ -263,7 +267,7 @@ impl<T: EthSpec> BeaconState<T> {
/// The epoch corresponding to `self.slot`. /// The epoch corresponding to `self.slot`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn current_epoch(&self) -> Epoch { pub fn current_epoch(&self) -> Epoch {
self.slot.epoch(T::slots_per_epoch()) self.slot.epoch(T::slots_per_epoch())
} }
@ -272,7 +276,7 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// If the current epoch is the genesis epoch, the genesis_epoch is returned. /// 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 { pub fn previous_epoch(&self) -> Epoch {
let current_epoch = self.current_epoch(); let current_epoch = self.current_epoch();
if current_epoch > T::genesis_epoch() { if current_epoch > T::genesis_epoch() {
@ -284,7 +288,7 @@ impl<T: EthSpec> BeaconState<T> {
/// The epoch following `self.current_epoch()`. /// The epoch following `self.current_epoch()`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn next_epoch(&self) -> Epoch { pub fn next_epoch(&self) -> Epoch {
self.current_epoch() + 1 self.current_epoch() + 1
} }
@ -293,7 +297,7 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// Makes use of the committee cache and will fail if no cache exists for the slot's epoch. /// 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<u64, Error> { pub fn get_committee_count_at_slot(&self, slot: Slot) -> Result<u64, Error> {
let cache = self.committee_cache_at_slot(slot)?; let cache = self.committee_cache_at_slot(slot)?;
Ok(cache.committees_per_slot() as u64) Ok(cache.committees_per_slot() as u64)
@ -301,7 +305,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Compute the number of committees in an entire epoch. /// 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<u64, Error> { pub fn get_epoch_committee_count(&self, relative_epoch: RelativeEpoch) -> Result<u64, Error> {
let cache = self.committee_cache(relative_epoch)?; let cache = self.committee_cache(relative_epoch)?;
Ok(cache.epoch_committee_count() as u64) Ok(cache.epoch_committee_count() as u64)
@ -325,7 +329,7 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// Does not utilize the cache, performs a full iteration over the validator registry. /// 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<usize> { pub fn get_active_validator_indices(&self, epoch: Epoch) -> Vec<usize> {
get_active_validator_indices(&self.validators, epoch) get_active_validator_indices(&self.validators, epoch)
} }
@ -345,7 +349,7 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// Utilises the committee cache. /// Utilises the committee cache.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_beacon_committee( pub fn get_beacon_committee(
&self, &self,
slot: Slot, slot: Slot,
@ -364,7 +368,7 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// Utilises the committee cache. /// Utilises the committee cache.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_beacon_committees_at_slot(&self, slot: Slot) -> Result<Vec<BeaconCommittee>, Error> { pub fn get_beacon_committees_at_slot(&self, slot: Slot) -> Result<Vec<BeaconCommittee>, Error> {
let cache = self.committee_cache_at_slot(slot)?; let cache = self.committee_cache_at_slot(slot)?;
cache.get_beacon_committees_at_slot(slot) cache.get_beacon_committees_at_slot(slot)
@ -374,7 +378,7 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// Utilises the committee cache. /// Utilises the committee cache.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_beacon_committees_at_epoch( pub fn get_beacon_committees_at_epoch(
&self, &self,
relative_epoch: RelativeEpoch, relative_epoch: RelativeEpoch,
@ -385,7 +389,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Compute the proposer (not necessarily for the Beacon chain) from a list of indices. /// 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. // NOTE: be sure to test this bad boy.
pub fn compute_proposer_index( pub fn compute_proposer_index(
&self, &self,
@ -424,7 +428,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Returns the beacon proposer index for the `slot` in the given `relative_epoch`. /// 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<usize, Error> { pub fn get_beacon_proposer_index(&self, slot: Slot, spec: &ChainSpec) -> Result<usize, Error> {
let epoch = slot.epoch(T::slots_per_epoch()); let epoch = slot.epoch(T::slots_per_epoch());
let seed = self.get_beacon_proposer_seed(slot, spec)?; let seed = self.get_beacon_proposer_seed(slot, spec)?;
@ -435,7 +439,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Compute the seed to use for the beacon proposer selection at the given `slot`. /// 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<Vec<u8>, Error> { fn get_beacon_proposer_seed(&self, slot: Slot, spec: &ChainSpec) -> Result<Vec<u8>, Error> {
let epoch = slot.epoch(T::slots_per_epoch()); let epoch = slot.epoch(T::slots_per_epoch());
let mut preimage = self let mut preimage = self
@ -450,7 +454,7 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// It needs filling in on all slots where there isn't a skip. /// 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 { pub fn get_latest_block_root(&self, current_state_root: Hash256) -> Hash256 {
if self.latest_block_header.state_root.is_zero() { if self.latest_block_header.state_root.is_zero() {
let mut latest_block_header = self.latest_block_header.clone(); let mut latest_block_header = self.latest_block_header.clone();
@ -463,7 +467,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Safely obtains the index for latest block roots, given some `slot`. /// 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<usize, Error> { fn get_latest_block_roots_index(&self, slot: Slot) -> Result<usize, Error> {
if (slot < self.slot) && (self.slot <= slot + self.block_roots.len() as u64) { if (slot < self.slot) && (self.slot <= slot + self.block_roots.len() as u64) {
Ok(slot.as_usize() % self.block_roots.len()) Ok(slot.as_usize() % self.block_roots.len())
@ -474,7 +478,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Return the block root at a recent `slot`. /// 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> { pub fn get_block_root(&self, slot: Slot) -> Result<&Hash256, BeaconStateError> {
let i = self.get_latest_block_roots_index(slot)?; let i = self.get_latest_block_roots_index(slot)?;
Ok(&self.block_roots[i]) Ok(&self.block_roots[i])
@ -482,7 +486,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Return the block root at a recent `epoch`. /// Return the block root at a recent `epoch`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
// NOTE: the spec calls this get_block_root // NOTE: the spec calls this get_block_root
pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result<&Hash256, BeaconStateError> { pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result<&Hash256, BeaconStateError> {
self.get_block_root(epoch.start_slot(T::slots_per_epoch())) self.get_block_root(epoch.start_slot(T::slots_per_epoch()))
@ -490,7 +494,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Sets the block root for some given slot. /// Sets the block root for some given slot.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn set_block_root( pub fn set_block_root(
&mut self, &mut self,
slot: Slot, slot: Slot,
@ -508,7 +512,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Safely obtains the index for `randao_mixes` /// Safely obtains the index for `randao_mixes`
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
fn get_randao_mix_index( fn get_randao_mix_index(
&self, &self,
epoch: Epoch, epoch: Epoch,
@ -530,7 +534,7 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// See `Self::get_randao_mix`. /// 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> { pub fn update_randao_mix(&mut self, epoch: Epoch, signature: &Signature) -> Result<(), Error> {
let i = epoch.as_usize() % T::EpochsPerHistoricalVector::to_usize(); let i = epoch.as_usize() % T::EpochsPerHistoricalVector::to_usize();
@ -543,7 +547,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Return the randao mix at a recent ``epoch``. /// 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> { pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, Error> {
let i = self.get_randao_mix_index(epoch, AllowNextEpoch::False)?; let i = self.get_randao_mix_index(epoch, AllowNextEpoch::False)?;
Ok(&self.randao_mixes[i]) Ok(&self.randao_mixes[i])
@ -551,7 +555,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Set the randao mix at a recent ``epoch``. /// 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> { pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), Error> {
let i = self.get_randao_mix_index(epoch, AllowNextEpoch::True)?; let i = self.get_randao_mix_index(epoch, AllowNextEpoch::True)?;
self.randao_mixes[i] = mix; self.randao_mixes[i] = mix;
@ -560,7 +564,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Safely obtains the index for latest state roots, given some `slot`. /// 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<usize, Error> { fn get_latest_state_roots_index(&self, slot: Slot) -> Result<usize, Error> {
if (slot < self.slot) && (self.slot <= slot + Slot::from(self.state_roots.len())) { if (slot < self.slot) && (self.slot <= slot + Slot::from(self.state_roots.len())) {
Ok(slot.as_usize() % self.state_roots.len()) Ok(slot.as_usize() % self.state_roots.len())
@ -571,7 +575,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Gets the state root for some slot. /// 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> { pub fn get_state_root(&self, slot: Slot) -> Result<&Hash256, Error> {
let i = self.get_latest_state_roots_index(slot)?; let i = self.get_latest_state_roots_index(slot)?;
Ok(&self.state_roots[i]) Ok(&self.state_roots[i])
@ -579,7 +583,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Gets the oldest (earliest slot) state root. /// 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> { pub fn get_oldest_state_root(&self) -> Result<&Hash256, Error> {
let i = let i =
self.get_latest_state_roots_index(self.slot - Slot::from(self.state_roots.len()))?; self.get_latest_state_roots_index(self.slot - Slot::from(self.state_roots.len()))?;
@ -588,7 +592,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Gets the oldest (earliest slot) block root. /// 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> { 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)?; let i = self.get_latest_block_roots_index(self.slot - self.block_roots.len() as u64)?;
Ok(&self.block_roots[i]) Ok(&self.block_roots[i])
@ -596,7 +600,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Sets the latest state root for slot. /// 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> { pub fn set_state_root(&mut self, slot: Slot, state_root: Hash256) -> Result<(), Error> {
let i = self.get_latest_state_roots_index(slot)?; let i = self.get_latest_state_roots_index(slot)?;
self.state_roots[i] = state_root; self.state_roots[i] = state_root;
@ -605,7 +609,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Safely obtain the index for `slashings`, given some `epoch`. /// Safely obtain the index for `slashings`, given some `epoch`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
fn get_slashings_index( fn get_slashings_index(
&self, &self,
epoch: Epoch, epoch: Epoch,
@ -625,14 +629,14 @@ impl<T: EthSpec> BeaconState<T> {
/// Get a reference to the entire `slashings` vector. /// Get a reference to the entire `slashings` vector.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_all_slashings(&self) -> &[u64] { pub fn get_all_slashings(&self) -> &[u64] {
&self.slashings &self.slashings
} }
/// Get the total slashed balances for some epoch. /// Get the total slashed balances for some epoch.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_slashings(&self, epoch: Epoch) -> Result<u64, Error> { pub fn get_slashings(&self, epoch: Epoch) -> Result<u64, Error> {
let i = self.get_slashings_index(epoch, AllowNextEpoch::False)?; let i = self.get_slashings_index(epoch, AllowNextEpoch::False)?;
Ok(self.slashings[i]) Ok(self.slashings[i])
@ -640,7 +644,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Set the total slashed balances for some epoch. /// 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> { pub fn set_slashings(&mut self, epoch: Epoch, value: u64) -> Result<(), Error> {
let i = self.get_slashings_index(epoch, AllowNextEpoch::True)?; let i = self.get_slashings_index(epoch, AllowNextEpoch::True)?;
self.slashings[i] = value; self.slashings[i] = value;
@ -649,7 +653,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Get the attestations from the current or previous epoch. /// Get the attestations from the current or previous epoch.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_matching_source_attestations( pub fn get_matching_source_attestations(
&self, &self,
epoch: Epoch, epoch: Epoch,
@ -665,7 +669,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Generate a seed for the given `epoch`. /// Generate a seed for the given `epoch`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_seed( pub fn get_seed(
&self, &self,
epoch: Epoch, epoch: Epoch,
@ -696,7 +700,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``. /// 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( pub fn get_effective_balance(
&self, &self,
validator_index: usize, validator_index: usize,
@ -710,7 +714,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. /// 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 { pub fn compute_activation_exit_epoch(&self, epoch: Epoch, spec: &ChainSpec) -> Epoch {
epoch + 1 + spec.max_seed_lookahead epoch + 1 + spec.max_seed_lookahead
} }
@ -719,7 +723,7 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// Uses the epoch cache, and will error if it isn't initialized. /// 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<u64, Error> { pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
Ok(std::cmp::max( Ok(std::cmp::max(
spec.min_per_epoch_churn_limit, spec.min_per_epoch_churn_limit,
@ -734,7 +738,7 @@ impl<T: EthSpec> BeaconState<T> {
/// ///
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. /// 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( pub fn get_attestation_duties(
&self, &self,
validator_index: usize, validator_index: usize,
@ -747,7 +751,7 @@ impl<T: EthSpec> BeaconState<T> {
/// Return the combined effective balance of an array of validators. /// Return the combined effective balance of an array of validators.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_total_balance( pub fn get_total_balance(
&self, &self,
validator_indices: &[usize], validator_indices: &[usize],

View File

@ -22,7 +22,7 @@ pub struct CommitteeCache {
impl CommitteeCache { impl CommitteeCache {
/// Return a new, fully initialized cache. /// Return a new, fully initialized cache.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn initialized<T: EthSpec>( pub fn initialized<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
epoch: Epoch, epoch: Epoch,
@ -87,7 +87,7 @@ impl CommitteeCache {
/// ///
/// Always returns `&[]` for a non-initialized epoch. /// Always returns `&[]` for a non-initialized epoch.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn active_validator_indices(&self) -> &[usize] { pub fn active_validator_indices(&self) -> &[usize] {
&self.shuffling &self.shuffling
} }
@ -96,7 +96,7 @@ impl CommitteeCache {
/// ///
/// Always returns `&[]` for a non-initialized epoch. /// Always returns `&[]` for a non-initialized epoch.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn shuffling(&self) -> &[usize] { pub fn shuffling(&self) -> &[usize] {
&self.shuffling &self.shuffling
} }
@ -202,7 +202,7 @@ impl CommitteeCache {
/// ///
/// Always returns `usize::default()` for a non-initialized epoch. /// Always returns `usize::default()` for a non-initialized epoch.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn active_validator_count(&self) -> usize { pub fn active_validator_count(&self) -> usize {
self.shuffling.len() self.shuffling.len()
} }
@ -211,7 +211,7 @@ impl CommitteeCache {
/// ///
/// Always returns `usize::default()` for a non-initialized epoch. /// Always returns `usize::default()` for a non-initialized epoch.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn epoch_committee_count(&self) -> usize { pub fn epoch_committee_count(&self) -> usize {
self.committees_per_slot as usize * self.slots_per_epoch as 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. /// 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]> { fn compute_committee(&self, index: usize) -> Option<&[usize]> {
Some(&self.shuffling[self.compute_committee_range(index)?]) 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. /// 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<Range<usize>> { fn compute_committee_range(&self, index: usize) -> Option<Range<usize>> {
let count = self.epoch_committee_count(); let count = self.epoch_committee_count();
if count == 0 || index >= 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 /// Returns a list of all `validators` indices where the validator is active at the given
/// `epoch`. /// `epoch`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> Vec<usize> { pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> Vec<usize> {
let mut active = Vec::with_capacity(validators.len()); let mut active = Vec::with_capacity(validators.len());

View File

@ -2,7 +2,7 @@
use super::*; use super::*;
use crate::test_utils::*; use crate::test_utils::*;
ssz_tests!(FoundationBeaconState); ssz_and_tree_hash_tests!(FoundationBeaconState);
fn test_beacon_proposer_index<T: EthSpec>() { fn test_beacon_proposer_index<T: EthSpec>() {
let spec = T::default_spec(); let spec = T::default_spec();

View File

@ -1,11 +1,16 @@
use crate::*; use crate::*;
use int_to_bytes::int_to_bytes4; use int_to_bytes::int_to_bytes4;
use serde_derive::{Deserialize, Serialize}; 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. /// Each of the BLS signature domains.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub enum Domain { pub enum Domain {
BeaconProposer, BeaconProposer,
BeaconAttester, BeaconAttester,
@ -16,18 +21,18 @@ pub enum Domain {
/// Holds all the "constants" for a BeaconChain. /// Holds all the "constants" for a BeaconChain.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[serde(default)] #[serde(default)]
pub struct ChainSpec { pub struct ChainSpec {
/* /*
* Constants * Constants
*/ */
pub genesis_slot: Slot,
#[serde(skip_serializing)] // skipped because Serde TOML has trouble with u64::max #[serde(skip_serializing)] // skipped because Serde TOML has trouble with u64::max
pub far_future_epoch: Epoch, pub far_future_epoch: Epoch,
pub base_rewards_per_epoch: u64, pub base_rewards_per_epoch: u64,
pub deposit_contract_tree_depth: u64, pub deposit_contract_tree_depth: u64,
pub seconds_per_day: u64,
/* /*
* Misc * Misc
@ -51,20 +56,25 @@ pub struct ChainSpec {
/* /*
* Initial Values * 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")] #[serde(deserialize_with = "u8_from_hex_str", serialize_with = "u8_to_hex_str")]
pub bls_withdrawal_prefix_byte: u8, pub bls_withdrawal_prefix_byte: u8,
/* /*
* Time parameters * Time parameters
*/ */
pub min_genesis_delay: u64,
pub milliseconds_per_slot: u64, pub milliseconds_per_slot: u64,
pub min_attestation_inclusion_delay: u64, pub min_attestation_inclusion_delay: u64,
pub min_seed_lookahead: Epoch, pub min_seed_lookahead: Epoch,
pub max_seed_lookahead: Epoch, pub max_seed_lookahead: Epoch,
pub min_epochs_to_inactivity_penalty: u64,
pub min_validator_withdrawability_delay: Epoch, pub min_validator_withdrawability_delay: Epoch,
pub persistent_committee_period: u64, pub persistent_committee_period: u64,
pub min_epochs_to_inactivity_penalty: u64,
/* /*
* Reward and penalty quotients * Reward and penalty quotients
@ -93,17 +103,16 @@ pub struct ChainSpec {
* Eth1 * Eth1
*/ */
pub eth1_follow_distance: u64, pub eth1_follow_distance: u64,
pub seconds_per_eth1_block: u64,
pub boot_nodes: Vec<String>, pub boot_nodes: Vec<String>,
pub network_id: u8, pub network_id: u8,
pub genesis_fork: Fork,
} }
impl ChainSpec { impl ChainSpec {
/// Get the domain number, unmodified by the fork. /// 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 { pub fn get_domain_constant(&self, domain: Domain) -> u32 {
match domain { match domain {
Domain::BeaconProposer => self.domain_beacon_proposer, Domain::BeaconProposer => self.domain_beacon_proposer,
@ -116,28 +125,30 @@ impl ChainSpec {
/// Get the domain number that represents the fork meta and signature domain. /// 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 { pub fn get_domain(&self, epoch: Epoch, domain: Domain, fork: &Fork) -> u64 {
let domain_constant = self.get_domain_constant(domain); let fork_version = fork.get_fork_version(epoch);
self.compute_domain(domain, fork_version)
let mut bytes: Vec<u8> = 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)
} }
/// Get the domain for a deposit signature. /// Get the domain for a deposit signature.
/// ///
/// Deposits are valid across forks, thus the deposit domain is computed /// 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 { pub fn get_deposit_domain(&self) -> u64 {
let mut bytes: Vec<u8> = int_to_bytes4(self.domain_deposit); self.compute_domain(Domain::Deposit, self.genesis_fork_version)
bytes.append(&mut vec![0; 4]); }
/// 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<u8> = int_to_bytes4(domain_constant);
bytes.append(&mut fork_version.to_vec());
let mut fork_and_domain = [0; 8]; let mut fork_and_domain = [0; 8];
fork_and_domain.copy_from_slice(&bytes); fork_and_domain.copy_from_slice(&bytes);
@ -147,16 +158,16 @@ impl ChainSpec {
/// Returns a `ChainSpec` compatible with the Ethereum Foundation specification. /// Returns a `ChainSpec` compatible with the Ethereum Foundation specification.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
pub fn mainnet() -> Self { pub fn mainnet() -> Self {
Self { Self {
/* /*
* Constants * Constants
*/ */
genesis_slot: Slot::new(0),
far_future_epoch: Epoch::new(u64::max_value()), far_future_epoch: Epoch::new(u64::max_value()),
base_rewards_per_epoch: 4, base_rewards_per_epoch: 4,
deposit_contract_tree_depth: 32, deposit_contract_tree_depth: 32,
seconds_per_day: 86400,
/* /*
* Misc * Misc
@ -166,7 +177,7 @@ impl ChainSpec {
min_per_epoch_churn_limit: 4, min_per_epoch_churn_limit: 4,
churn_limit_quotient: 65_536, churn_limit_quotient: 65_536,
shuffle_round_count: 90, 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 min_genesis_time: 1_578_009_600, // Jan 3, 2020
/* /*
@ -180,19 +191,20 @@ impl ChainSpec {
/* /*
* Initial Values * Initial Values
*/ */
genesis_slot: Slot::new(0), genesis_fork_version: [0; 4],
bls_withdrawal_prefix_byte: 0, bls_withdrawal_prefix_byte: 0,
/* /*
* Time parameters * Time parameters
*/ */
min_genesis_delay: 86400, // 1 day
milliseconds_per_slot: 12_000, milliseconds_per_slot: 12_000,
min_attestation_inclusion_delay: 1, min_attestation_inclusion_delay: 1,
min_seed_lookahead: Epoch::new(1), min_seed_lookahead: Epoch::new(1),
max_seed_lookahead: Epoch::new(4), max_seed_lookahead: Epoch::new(4),
min_epochs_to_inactivity_penalty: 4,
min_validator_withdrawability_delay: Epoch::new(256), min_validator_withdrawability_delay: Epoch::new(256),
persistent_committee_period: 2_048, persistent_committee_period: 2_048,
min_epochs_to_inactivity_penalty: 4,
/* /*
* Reward and penalty quotients * Reward and penalty quotients
@ -221,15 +233,7 @@ impl ChainSpec {
* Eth1 * Eth1
*/ */
eth1_follow_distance: 1_024, eth1_follow_distance: 1_024,
seconds_per_eth1_block: 14,
/*
* Fork
*/
genesis_fork: Fork {
previous_version: [0; 4],
current_version: [0; 4],
epoch: Epoch::new(0),
},
/* /*
* Network specific * 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.10.1
///
/// Spec v0.9.1
pub fn minimal() -> Self { pub fn minimal() -> Self {
// Note: bootnodes to be updated when static nodes exist. // Note: bootnodes to be updated when static nodes exist.
let boot_nodes = vec![]; let boot_nodes = vec![];
@ -253,10 +255,12 @@ impl ChainSpec {
target_committee_size: 4, target_committee_size: 4,
shuffle_round_count: 10, shuffle_round_count: 10,
min_genesis_active_validator_count: 64, 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 network_id: 2, // lighthouse testnet network id
boot_nodes, boot_nodes,
eth1_follow_distance: 16,
milliseconds_per_slot: 6_000,
..ChainSpec::mainnet() ..ChainSpec::mainnet()
} }
} }
@ -295,7 +299,11 @@ mod tests {
} }
fn test_domain(domain_type: Domain, raw_domain: u32, spec: &ChainSpec) { 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 epoch = Epoch::new(0);
let domain = spec.get_domain(epoch, domain_type, &fork); 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)] #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(rename_all = "UPPERCASE")] #[serde(rename_all = "UPPERCASE")]
#[serde(default)] #[serde(default)]
#[serde(deny_unknown_fields)] #[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 { pub struct YamlConfig {
// ChainSpec // ChainSpec
far_future_epoch: u64, far_future_epoch: u64,
base_rewards_per_epoch: u64, base_rewards_per_epoch: u64,
deposit_contract_tree_depth: u64, deposit_contract_tree_depth: u64,
seconds_per_day: u64,
max_committees_per_slot: usize, max_committees_per_slot: usize,
target_committee_size: usize, target_committee_size: usize,
min_per_epoch_churn_limit: u64, min_per_epoch_churn_limit: u64,
@ -338,20 +347,26 @@ pub struct YamlConfig {
shuffle_round_count: u8, shuffle_round_count: u8,
min_genesis_active_validator_count: u64, min_genesis_active_validator_count: u64,
min_genesis_time: u64, min_genesis_time: u64,
min_genesis_delay: u64,
min_deposit_amount: u64, min_deposit_amount: u64,
max_effective_balance: u64, max_effective_balance: u64,
ejection_balance: u64, ejection_balance: u64,
effective_balance_increment: u64, effective_balance_increment: u64,
genesis_slot: 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")] #[serde(deserialize_with = "u8_from_hex_str", serialize_with = "u8_to_hex_str")]
bls_withdrawal_prefix: u8, bls_withdrawal_prefix: u8,
seconds_per_slot: u64, seconds_per_slot: u64,
min_attestation_inclusion_delay: u64, min_attestation_inclusion_delay: u64,
min_seed_lookahead: u64, min_seed_lookahead: u64,
max_seed_lookahead: u64, max_seed_lookahead: u64,
min_epochs_to_inactivity_penalty: u64,
min_validator_withdrawability_delay: u64, min_validator_withdrawability_delay: u64,
persistent_committee_period: u64, persistent_committee_period: u64,
min_epochs_to_inactivity_penalty: u64,
base_reward_factor: u64, base_reward_factor: u64,
whistleblower_reward_quotient: u64, whistleblower_reward_quotient: u64,
proposer_reward_quotient: u64, proposer_reward_quotient: u64,
@ -359,9 +374,6 @@ pub struct YamlConfig {
min_slashing_penalty_quotient: u64, min_slashing_penalty_quotient: u64,
safe_slots_to_update_justified: u64, safe_slots_to_update_justified: u64,
#[serde(skip_serializing)]
genesis_fork: Fork,
#[serde( #[serde(
deserialize_with = "u32_from_hex_str", deserialize_with = "u32_from_hex_str",
serialize_with = "u32_to_hex_str" serialize_with = "u32_to_hex_str"
@ -408,12 +420,14 @@ pub struct YamlConfig {
max_deposits: u32, max_deposits: u32,
max_voluntary_exits: u32, max_voluntary_exits: u32,
// Eth1 // Validator
eth1_follow_distance: u64, 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 // Deposit Contract (unused)
#[serde(skip_serializing)]
early_derived_secret_penalty_max_future_epochs: u32,
#[serde(skip_serializing)] #[serde(skip_serializing)]
deposit_contract_address: String, deposit_contract_address: String,
@ -438,6 +452,8 @@ pub struct YamlConfig {
domain_shard_attester: u32, domain_shard_attester: u32,
#[serde(skip_serializing)] #[serde(skip_serializing)]
max_epochs_per_crosslink: u64, max_epochs_per_crosslink: u64,
#[serde(skip_serializing)]
early_derived_secret_penalty_max_future_epochs: u32,
} }
impl Default for YamlConfig { impl Default for YamlConfig {
@ -447,7 +463,7 @@ impl Default for YamlConfig {
} }
} }
/// Spec v0.8.1 /// Spec v0.10.1
impl YamlConfig { impl YamlConfig {
pub fn from_spec<T: EthSpec>(spec: &ChainSpec) -> Self { pub fn from_spec<T: EthSpec>(spec: &ChainSpec) -> Self {
Self { Self {
@ -455,7 +471,6 @@ impl YamlConfig {
far_future_epoch: spec.far_future_epoch.into(), far_future_epoch: spec.far_future_epoch.into(),
base_rewards_per_epoch: spec.base_rewards_per_epoch, base_rewards_per_epoch: spec.base_rewards_per_epoch,
deposit_contract_tree_depth: spec.deposit_contract_tree_depth, 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, max_committees_per_slot: spec.max_committees_per_slot,
target_committee_size: spec.target_committee_size, target_committee_size: spec.target_committee_size,
min_per_epoch_churn_limit: spec.min_per_epoch_churn_limit, min_per_epoch_churn_limit: spec.min_per_epoch_churn_limit,
@ -463,6 +478,7 @@ impl YamlConfig {
shuffle_round_count: spec.shuffle_round_count, shuffle_round_count: spec.shuffle_round_count,
min_genesis_active_validator_count: spec.min_genesis_active_validator_count, min_genesis_active_validator_count: spec.min_genesis_active_validator_count,
min_genesis_time: spec.min_genesis_time, min_genesis_time: spec.min_genesis_time,
min_genesis_delay: spec.min_genesis_delay,
min_deposit_amount: spec.min_deposit_amount, min_deposit_amount: spec.min_deposit_amount,
max_effective_balance: spec.max_effective_balance, max_effective_balance: spec.max_effective_balance,
ejection_balance: spec.ejection_balance, ejection_balance: spec.ejection_balance,
@ -481,7 +497,7 @@ impl YamlConfig {
proposer_reward_quotient: spec.proposer_reward_quotient, proposer_reward_quotient: spec.proposer_reward_quotient,
inactivity_penalty_quotient: spec.inactivity_penalty_quotient, inactivity_penalty_quotient: spec.inactivity_penalty_quotient,
min_slashing_penalty_quotient: spec.min_slashing_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, safe_slots_to_update_justified: spec.safe_slots_to_update_justified,
domain_beacon_proposer: spec.domain_beacon_proposer, domain_beacon_proposer: spec.domain_beacon_proposer,
domain_beacon_attester: spec.domain_beacon_attester, domain_beacon_attester: spec.domain_beacon_attester,
@ -506,11 +522,14 @@ impl YamlConfig {
max_deposits: T::MaxDeposits::to_u32(), max_deposits: T::MaxDeposits::to_u32(),
max_voluntary_exits: T::MaxVoluntaryExits::to_u32(), max_voluntary_exits: T::MaxVoluntaryExits::to_u32(),
// Eth1 // Validator
eth1_follow_distance: spec.eth1_follow_distance, 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 // Deposit Contract (unused)
early_derived_secret_penalty_max_future_epochs: 0,
deposit_contract_address: String::new(), deposit_contract_address: String::new(),
// Phase 1 // Phase 1
@ -524,9 +543,17 @@ impl YamlConfig {
domain_shard_proposer: 0, domain_shard_proposer: 0,
domain_shard_attester: 0, domain_shard_attester: 0,
max_epochs_per_crosslink: 0, max_epochs_per_crosslink: 0,
early_derived_secret_penalty_max_future_epochs: 0,
} }
} }
pub fn from_file(filename: &Path) -> Result<Self, String> {
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<T: EthSpec>(&self, chain_spec: &ChainSpec) -> Option<ChainSpec> { pub fn apply_to_chain_spec<T: EthSpec>(&self, chain_spec: &ChainSpec) -> Option<ChainSpec> {
// Checking for EthSpec constants // Checking for EthSpec constants
if self.justification_bits_length != T::JustificationBitsLength::to_u32() if self.justification_bits_length != T::JustificationBitsLength::to_u32()
@ -553,7 +580,6 @@ impl YamlConfig {
far_future_epoch: Epoch::from(self.far_future_epoch), far_future_epoch: Epoch::from(self.far_future_epoch),
base_rewards_per_epoch: self.base_rewards_per_epoch, base_rewards_per_epoch: self.base_rewards_per_epoch,
deposit_contract_tree_depth: self.deposit_contract_tree_depth, deposit_contract_tree_depth: self.deposit_contract_tree_depth,
seconds_per_day: self.seconds_per_day,
target_committee_size: self.target_committee_size, target_committee_size: self.target_committee_size,
min_per_epoch_churn_limit: self.min_per_epoch_churn_limit, min_per_epoch_churn_limit: self.min_per_epoch_churn_limit,
churn_limit_quotient: self.churn_limit_quotient, 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_active_validator_count: self.min_genesis_active_validator_count,
min_genesis_time: self.min_genesis_time, min_genesis_time: self.min_genesis_time,
min_deposit_amount: self.min_deposit_amount, min_deposit_amount: self.min_deposit_amount,
min_genesis_delay: self.min_genesis_delay,
max_effective_balance: self.max_effective_balance, max_effective_balance: self.max_effective_balance,
ejection_balance: self.ejection_balance, ejection_balance: self.ejection_balance,
effective_balance_increment: self.effective_balance_increment, effective_balance_increment: self.effective_balance_increment,
@ -585,7 +612,7 @@ impl YamlConfig {
domain_deposit: self.domain_deposit, domain_deposit: self.domain_deposit,
domain_voluntary_exit: self.domain_voluntary_exit, domain_voluntary_exit: self.domain_voluntary_exit,
boot_nodes: chain_spec.boot_nodes.clone(), 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, eth1_follow_distance: self.eth1_follow_distance,
..*chain_spec ..*chain_spec
}) })

View File

@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash;
/// Casper FFG checkpoint, used in attestations. /// Casper FFG checkpoint, used in attestations.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive( #[derive(
Debug, Debug,
Clone, Clone,
@ -31,5 +31,5 @@ pub struct Checkpoint {
mod tests { mod tests {
use super::*; use super::*;
ssz_tests!(Checkpoint); ssz_and_tree_hash_tests!(Checkpoint);
} }

View File

@ -11,7 +11,7 @@ pub const DEPOSIT_TREE_DEPTH: usize = 32;
/// A deposit to potentially become a beacon chain validator. /// 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)] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct Deposit { pub struct Deposit {
pub proof: FixedVector<Hash256, U33>, pub proof: FixedVector<Hash256, U33>,
@ -22,5 +22,5 @@ pub struct Deposit {
mod tests { mod tests {
use super::*; use super::*;
ssz_tests!(Deposit); ssz_and_tree_hash_tests!(Deposit);
} }

View File

@ -1,46 +1,43 @@
use crate::test_utils::TestRandom; use crate::test_utils::TestRandom;
use crate::*; use crate::*;
use bls::{PublicKeyBytes, SignatureBytes}; use bls::{PublicKeyBytes, SignatureBytes};
use std::convert::From;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
use tree_hash::{SignedRoot, TreeHash}; use tree_hash_derive::TreeHash;
use tree_hash_derive::{SignedRoot, TreeHash};
/// The data supplied by the user to the deposit contract. /// The data supplied by the user to the deposit contract.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive( #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
SignedRoot,
TreeHash,
TestRandom,
)]
pub struct DepositData { pub struct DepositData {
pub pubkey: PublicKeyBytes, pub pubkey: PublicKeyBytes,
pub withdrawal_credentials: Hash256, pub withdrawal_credentials: Hash256,
pub amount: u64, pub amount: u64,
#[signed_root(skip_hashing)]
pub signature: SignatureBytes, pub signature: SignatureBytes,
} }
impl DepositData { 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. /// 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 { pub fn create_signature(&self, secret_key: &SecretKey, spec: &ChainSpec) -> SignatureBytes {
let msg = self.signed_root();
let domain = spec.get_deposit_domain(); 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 { mod tests {
use super::*; use super::*;
ssz_tests!(DepositData); ssz_and_tree_hash_tests!(DepositData);
} }

View File

@ -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);
}

View File

@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
/// Contains data obtained from the Eth1 chain. /// Contains data obtained from the Eth1 chain.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive( #[derive(
Debug, Debug,
PartialEq, PartialEq,
@ -33,5 +33,5 @@ pub struct Eth1Data {
mod tests { mod tests {
use super::*; use super::*;
ssz_tests!(Eth1Data); ssz_and_tree_hash_tests!(Eth1Data);
} }

View File

@ -10,15 +10,12 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
/* /*
* Constants * Constants
*/ */
type GenesisEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type JustificationBitsLength: Unsigned + Clone + Sync + Send + Debug + PartialEq + Default; type JustificationBitsLength: Unsigned + Clone + Sync + Send + Debug + PartialEq + Default;
/* /*
* Misc * Misc
*/ */
type MaxValidatorsPerCommittee: Unsigned + Clone + Sync + Send + Debug + PartialEq; type MaxValidatorsPerCommittee: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
* Initial values
*/
type GenesisEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/* /*
* Time parameters * 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 /// 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. /// 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 { fn get_committee_count_per_slot(active_validator_count: usize, spec: &ChainSpec) -> usize {
let slots_per_epoch = Self::SlotsPerEpoch::to_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. /// Returns the `SLOTS_PER_EPOCH` constant for this specification.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
fn slots_per_epoch() -> u64 { fn slots_per_epoch() -> u64 {
Self::SlotsPerEpoch::to_u64() Self::SlotsPerEpoch::to_u64()
} }
/// Returns the `SLOTS_PER_HISTORICAL_ROOT` constant for this specification. /// Returns the `SLOTS_PER_HISTORICAL_ROOT` constant for this specification.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
fn slots_per_historical_root() -> usize { fn slots_per_historical_root() -> usize {
Self::SlotsPerHistoricalRoot::to_usize() Self::SlotsPerHistoricalRoot::to_usize()
} }
/// Returns the `EPOCHS_PER_HISTORICAL_VECTOR` constant for this specification. /// Returns the `EPOCHS_PER_HISTORICAL_VECTOR` constant for this specification.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
fn epochs_per_historical_vector() -> usize { fn epochs_per_historical_vector() -> usize {
Self::EpochsPerHistoricalVector::to_usize() Self::EpochsPerHistoricalVector::to_usize()
} }
/// Returns the `SLOTS_PER_ETH1_VOTING_PERIOD` constant for this specification. /// 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 { fn slots_per_eth1_voting_period() -> usize {
Self::SlotsPerEth1VotingPeriod::to_usize() Self::SlotsPerEth1VotingPeriod::to_usize()
} }
@ -122,7 +119,7 @@ macro_rules! params_from_eth_spec {
/// Ethereum Foundation specifications. /// Ethereum Foundation specifications.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)] #[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
pub struct MainnetEthSpec; pub struct MainnetEthSpec;
@ -151,11 +148,9 @@ impl EthSpec for MainnetEthSpec {
pub type FoundationBeaconState = BeaconState<MainnetEthSpec>; pub type FoundationBeaconState = BeaconState<MainnetEthSpec>;
/// 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.10.1
///
/// Spec v0.9.1
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)] #[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
pub struct MinimalEthSpec; pub struct MinimalEthSpec;

View File

@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash;
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks. /// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive( #[derive(
Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)] )]
@ -30,7 +30,7 @@ pub struct Fork {
impl Fork { impl Fork {
/// Return the fork version of the given ``epoch``. /// 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] { pub fn get_fork_version(&self, epoch: Epoch) -> [u8; 4] {
if epoch < self.epoch { if epoch < self.epoch {
return self.previous_version; return self.previous_version;
@ -43,7 +43,7 @@ impl Fork {
mod tests { mod tests {
use super::*; use super::*;
ssz_tests!(Fork); ssz_and_tree_hash_tests!(Fork);
#[test] #[test]
fn get_fork_version() { fn get_fork_version() {

View File

@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash;
/// Historical block and state roots. /// Historical block and state roots.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct HistoricalBatch<T: EthSpec> { pub struct HistoricalBatch<T: EthSpec> {
pub block_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>, pub block_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,
@ -22,5 +22,5 @@ mod tests {
pub type FoundationHistoricalBatch = HistoricalBatch<MainnetEthSpec>; pub type FoundationHistoricalBatch = HistoricalBatch<MainnetEthSpec>;
ssz_tests!(FoundationHistoricalBatch); ssz_and_tree_hash_tests!(FoundationHistoricalBatch);
} }

View File

@ -2,46 +2,33 @@ use crate::{test_utils::TestRandom, AggregateSignature, AttestationData, EthSpec
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
use tree_hash::TreeHash; use tree_hash_derive::TreeHash;
use tree_hash_derive::{SignedRoot, TreeHash};
/// Details an attestation that can be slashable. /// Details an attestation that can be slashable.
/// ///
/// To be included in an `AttesterSlashing`. /// To be included in an `AttesterSlashing`.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive( #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
Encode,
Decode,
TreeHash,
TestRandom,
SignedRoot,
)]
#[serde(bound = "T: EthSpec")] #[serde(bound = "T: EthSpec")]
pub struct IndexedAttestation<T: EthSpec> { pub struct IndexedAttestation<T: EthSpec> {
/// Lists validator registry indices, not committee indices. /// Lists validator registry indices, not committee indices.
pub attesting_indices: VariableList<u64, T::MaxValidatorsPerCommittee>, pub attesting_indices: VariableList<u64, T::MaxValidatorsPerCommittee>,
pub data: AttestationData, pub data: AttestationData,
#[signed_root(skip_hashing)]
pub signature: AggregateSignature, pub signature: AggregateSignature,
} }
impl<T: EthSpec> IndexedAttestation<T> { impl<T: EthSpec> IndexedAttestation<T> {
/// Check if ``attestation_data_1`` and ``attestation_data_2`` have the same target. /// 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 { pub fn is_double_vote(&self, other: &Self) -> bool {
self.data.target.epoch == other.data.target.epoch && self.data != other.data self.data.target.epoch == other.data.target.epoch && self.data != other.data
} }
/// Check if ``attestation_data_1`` surrounds ``attestation_data_2``. /// 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 { pub fn is_surround_vote(&self, other: &Self) -> bool {
self.data.source.epoch < other.data.source.epoch self.data.source.epoch < other.data.source.epoch
&& other.data.target.epoch < self.data.target.epoch && other.data.target.epoch < self.data.target.epoch
@ -121,7 +108,7 @@ mod tests {
); );
} }
ssz_tests!(IndexedAttestation<MainnetEthSpec>); ssz_and_tree_hash_tests!(IndexedAttestation<MainnetEthSpec>);
fn create_indexed_attestation( fn create_indexed_attestation(
target_epoch: u64, target_epoch: u64,

View File

@ -19,6 +19,7 @@ pub mod chain_spec;
pub mod checkpoint; pub mod checkpoint;
pub mod deposit; pub mod deposit;
pub mod deposit_data; pub mod deposit_data;
pub mod deposit_message;
pub mod eth1_data; pub mod eth1_data;
pub mod eth_spec; pub mod eth_spec;
pub mod fork; pub mod fork;
@ -27,15 +28,18 @@ pub mod historical_batch;
pub mod indexed_attestation; pub mod indexed_attestation;
pub mod pending_attestation; pub mod pending_attestation;
pub mod proposer_slashing; 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 utils;
pub mod validator;
pub mod voluntary_exit; pub mod voluntary_exit;
#[macro_use] #[macro_use]
pub mod slot_epoch_macros; pub mod slot_epoch_macros;
pub mod relative_epoch;
pub mod slot_epoch; pub mod slot_epoch;
pub mod slot_height;
mod tree_hash_impls; mod tree_hash_impls;
pub mod validator;
use ethereum_types::{H160, H256}; 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::checkpoint::Checkpoint;
pub use crate::deposit::{Deposit, DEPOSIT_TREE_DEPTH}; pub use crate::deposit::{Deposit, DEPOSIT_TREE_DEPTH};
pub use crate::deposit_data::DepositData; pub use crate::deposit_data::DepositData;
pub use crate::deposit_message::DepositMessage;
pub use crate::eth1_data::Eth1Data; pub use crate::eth1_data::Eth1Data;
pub use crate::fork::Fork; pub use crate::fork::Fork;
pub use crate::free_attestation::FreeAttestation; 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::pending_attestation::PendingAttestation;
pub use crate::proposer_slashing::ProposerSlashing; pub use crate::proposer_slashing::ProposerSlashing;
pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; 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_epoch::{Epoch, Slot};
pub use crate::slot_height::SlotHeight;
pub use crate::validator::Validator; pub use crate::validator::Validator;
pub use crate::voluntary_exit::VoluntaryExit; pub use crate::voluntary_exit::VoluntaryExit;

View File

@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash;
/// An attestation that has been included in the state but not yet fully processed. /// 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)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct PendingAttestation<T: EthSpec> { pub struct PendingAttestation<T: EthSpec> {
pub aggregation_bits: BitList<T::MaxValidatorsPerCommittee>, pub aggregation_bits: BitList<T::MaxValidatorsPerCommittee>,
@ -22,5 +22,5 @@ mod tests {
use super::*; use super::*;
use crate::*; use crate::*;
ssz_tests!(PendingAttestation<MainnetEthSpec>); ssz_and_tree_hash_tests!(PendingAttestation<MainnetEthSpec>);
} }

View File

@ -1,5 +1,5 @@
use super::BeaconBlockHeader;
use crate::test_utils::TestRandom; use crate::test_utils::TestRandom;
use crate::SignedBeaconBlockHeader;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
@ -8,17 +8,17 @@ use tree_hash_derive::TreeHash;
/// Two conflicting proposals from the same proposer (validator). /// 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)] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
pub struct ProposerSlashing { pub struct ProposerSlashing {
pub proposer_index: u64, pub proposer_index: u64,
pub header_1: BeaconBlockHeader, pub signed_header_1: SignedBeaconBlockHeader,
pub header_2: BeaconBlockHeader, pub signed_header_2: SignedBeaconBlockHeader,
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
ssz_tests!(ProposerSlashing); ssz_and_tree_hash_tests!(ProposerSlashing);
} }

View File

@ -9,7 +9,7 @@ pub enum Error {
/// Defines the epochs relative to some epoch. Most useful when referring to the committees prior /// Defines the epochs relative to some epoch. Most useful when referring to the committees prior
/// to and following some epoch. /// to and following some epoch.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy)]
pub enum RelativeEpoch { pub enum RelativeEpoch {
/// The prior epoch. /// The prior epoch.
@ -23,7 +23,7 @@ pub enum RelativeEpoch {
impl RelativeEpoch { impl RelativeEpoch {
/// Returns the `epoch` that `self` refers to, with respect to the `base` epoch. /// 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 { pub fn into_epoch(self, base: Epoch) -> Epoch {
match self { match self {
// Due to saturating nature of epoch, check for current first. // 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`. /// - `EpochTooLow` when `other` is more than 1 prior to `base`.
/// - `EpochTooHigh` when `other` is more than 1 after `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<Self, Error> { pub fn from_epoch(base: Epoch, other: Epoch) -> Result<Self, Error> {
// Due to saturating nature of epoch, check for current first. // Due to saturating nature of epoch, check for current first.
if other == base { if other == base {

View File

@ -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<E: EthSpec> {
pub message: BeaconBlock<E>,
pub signature: Signature,
}
impl<E: EthSpec> SignedBeaconBlock<E> {
/// 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<MainnetEthSpec>);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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(),
)
}
}

View File

@ -10,8 +10,8 @@
//! implement `Into<u64>`, however this would allow operations between `Slots` and `Epochs` which //! implement `Into<u64>`, however this would allow operations between `Slots` and `Epochs` which
//! may lead to programming errors which are not detected by the compiler. //! may lead to programming errors which are not detected by the compiler.
use crate::slot_height::SlotHeight;
use crate::test_utils::TestRandom; use crate::test_utils::TestRandom;
use crate::SignedRoot;
use rand::RngCore; use rand::RngCore;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use slog; use slog;
@ -41,10 +41,6 @@ impl Slot {
Epoch::from(self.0 / slots_per_epoch) 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 { pub fn max_value() -> Slot {
Slot(u64::max_value()) Slot(u64::max_value())
} }
@ -97,6 +93,8 @@ impl Epoch {
} }
} }
impl SignedRoot for Epoch {}
pub struct SlotIter<'a> { pub struct SlotIter<'a> {
current_iteration: u64, current_iteration: u64,
epoch: &'a Epoch, epoch: &'a Epoch,

View File

@ -562,7 +562,7 @@ macro_rules! all_tests {
new_tests!($type); new_tests!($type);
math_between_tests!($type, $type); math_between_tests!($type, $type);
math_tests!($type); math_tests!($type);
ssz_tests!($type); ssz_and_tree_hash_tests!($type);
mod u64_tests { mod u64_tests {
use super::*; use super::*;

View File

@ -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);
}

View File

@ -1,6 +1,5 @@
use crate::test_utils::{AttestationTestTask, TestingAttestationDataBuilder}; use crate::test_utils::{AttestationTestTask, TestingAttestationDataBuilder};
use crate::*; use crate::*;
use tree_hash::TreeHash;
/// Builds an attestation to be used for testing purposes. /// Builds an attestation to be used for testing purposes.
/// ///
@ -71,31 +70,22 @@ impl<T: EthSpec> TestingAttestationBuilder<T> {
.position(|v| *v == *validator_index) .position(|v| *v == *validator_index)
.expect("Signing validator not in attestation committee"); .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 { let index = if test_task == AttestationTestTask::BadSignature {
0 0
} else { } else {
key_index 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 self

View File

@ -55,11 +55,12 @@ impl TestingAttestationDataBuilder {
slot = state.slot - spec.min_attestation_inclusion_delay + 1 slot = state.slot - spec.min_attestation_inclusion_delay + 1
} }
AttestationTestTask::IncludedTooLate => slot -= T::SlotsPerEpoch::to_u64(), AttestationTestTask::IncludedTooLate => slot -= T::SlotsPerEpoch::to_u64(),
AttestationTestTask::BadTargetEpoch => { AttestationTestTask::TargetEpochSlotMismatch => {
target = Checkpoint { target = Checkpoint {
epoch: Epoch::from(5 as u64), epoch: current_epoch + 1,
root: Hash256::zero(), root: Hash256::zero(),
} };
assert_ne!(target.epoch, slot.epoch(T::slots_per_epoch()));
} }
AttestationTestTask::WrongJustifiedCheckpoint => { AttestationTestTask::WrongJustifiedCheckpoint => {
source = Checkpoint { source = Checkpoint {
@ -67,18 +68,6 @@ impl TestingAttestationDataBuilder {
root: Hash256::zero(), 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(),
}
}
_ => (), _ => (),
} }

View File

@ -1,6 +1,5 @@
use crate::test_utils::AttesterSlashingTestTask; use crate::test_utils::AttesterSlashingTestTask;
use crate::*; use crate::*;
use tree_hash::TreeHash;
/// Builds an `AttesterSlashing`. /// Builds an `AttesterSlashing`.
/// ///
@ -22,14 +21,15 @@ impl TestingAttesterSlashingBuilder {
test_task: AttesterSlashingTestTask, test_task: AttesterSlashingTestTask,
validator_indices: &[u64], validator_indices: &[u64],
signer: F, signer: F,
fork: &Fork,
spec: &ChainSpec,
) -> AttesterSlashing<T> ) -> AttesterSlashing<T>
where where
F: Fn(u64, &[u8], Epoch, Domain) -> Signature, F: Fn(u64, &[u8]) -> Signature,
{ {
let slot = Slot::new(1); let slot = Slot::new(1);
let index = 0; let index = 0;
let epoch_1 = Epoch::new(1); let epoch_1 = Epoch::new(1);
let epoch_2 = Epoch::new(2);
let hash_1 = Hash256::from_low_u64_le(1); let hash_1 = Hash256::from_low_u64_le(1);
let hash_2 = Hash256::from_low_u64_le(2); let hash_2 = Hash256::from_low_u64_le(2);
let checkpoint_1 = Checkpoint { let checkpoint_1 = Checkpoint {
@ -83,15 +83,12 @@ impl TestingAttesterSlashingBuilder {
}; };
let add_signatures = |attestation: &mut IndexedAttestation<T>| { let add_signatures = |attestation: &mut IndexedAttestation<T>| {
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 { for validator_index in validator_indices {
let signature = signer( let signature = signer(*validator_index, message.as_bytes());
*validator_index,
&message[..],
epoch_2,
Domain::BeaconAttester,
);
attestation.signature.add(&signature); attestation.signature.add(&signature);
} }
}; };

View File

@ -9,7 +9,7 @@ use crate::{
use int_to_bytes::int_to_bytes32; use int_to_bytes::int_to_bytes32;
use merkle_proof::MerkleTree; use merkle_proof::MerkleTree;
use rayon::prelude::*; use rayon::prelude::*;
use tree_hash::{SignedRoot, TreeHash}; use tree_hash::TreeHash;
/// Builds a beacon block to be used for testing purposes. /// Builds a beacon block to be used for testing purposes.
/// ///
@ -46,8 +46,6 @@ pub enum AttestationTestTask {
Valid, Valid,
NoCommiteeForShard, NoCommiteeForShard,
WrongJustifiedCheckpoint, WrongJustifiedCheckpoint,
BadTargetTooLow,
BadTargetTooHigh,
BadShard, BadShard,
BadIndexedAttestationBadSignature, BadIndexedAttestationBadSignature,
BadAggregationBitfieldLen, BadAggregationBitfieldLen,
@ -55,7 +53,9 @@ pub enum AttestationTestTask {
ValidatorUnknown, ValidatorUnknown,
IncludedTooEarly, IncludedTooEarly,
IncludedTooLate, 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 /// Enum used for passing test options to builder
@ -97,24 +97,14 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
self.block.slot = slot; 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. /// Sets the randao to be a signature across the blocks epoch.
/// ///
/// Modifying the block's slot after signing may invalidate the signature. /// Modifying the block's slot after signing may invalidate the signature.
pub fn set_randao_reveal(&mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) { pub fn set_randao_reveal(&mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) {
let epoch = self.block.slot.epoch(T::slots_per_epoch()); 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); 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? /// Has the randao reveal been set?
@ -364,26 +354,23 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
_ => (), _ => (),
} }
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(exit).unwrap();
self.block
.body
.voluntary_exits
.push(builder.build())
.unwrap();
} }
/// Signs and returns the block, consuming the builder. /// Signs and returns the block, consuming the builder.
pub fn build(mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) -> BeaconBlock<T> { pub fn build(self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) -> SignedBeaconBlock<T> {
self.sign(sk, fork, spec); self.block.sign(sk, fork, spec)
self.block
} }
/// Returns the block, consuming the builder. /// Returns the block, consuming the builder.
pub fn build_without_signing(self) -> BeaconBlock<T> { pub fn build_without_signing(self) -> SignedBeaconBlock<T> {
self.block SignedBeaconBlock {
message: self.block,
signature: Signature::empty_signature(),
}
} }
} }
@ -397,12 +384,13 @@ fn build_proposer_slashing<T: EthSpec>(
fork: &Fork, fork: &Fork,
spec: &ChainSpec, spec: &ChainSpec,
) -> ProposerSlashing { ) -> ProposerSlashing {
let signer = |_validator_index: u64, message: &[u8], epoch: Epoch, domain: Domain| { TestingProposerSlashingBuilder::double_vote::<T>(
let domain = spec.get_domain(epoch, domain, fork); test_task,
Signature::new(message, domain, secret_key) validator_index,
}; secret_key,
fork,
TestingProposerSlashingBuilder::double_vote::<T, _>(test_task, validator_index, signer) spec,
)
} }
/// Builds an `AttesterSlashing` for some `validator_indices`. /// Builds an `AttesterSlashing` for some `validator_indices`.
@ -415,14 +403,13 @@ fn build_double_vote_attester_slashing<T: EthSpec>(
fork: &Fork, fork: &Fork,
spec: &ChainSpec, spec: &ChainSpec,
) -> AttesterSlashing<T> { ) -> AttesterSlashing<T> {
let signer = |validator_index: u64, message: &[u8], epoch: Epoch, domain: Domain| { let signer = |validator_index: u64, message: &[u8]| {
let key_index = validator_indices let key_index = validator_indices
.iter() .iter()
.position(|&i| i == validator_index) .position(|&i| i == validator_index)
.expect("Unable to find attester slashing key"); .expect("Unable to find attester slashing key");
let domain = spec.get_domain(epoch, domain, fork); Signature::new(message, secret_keys[key_index])
Signature::new(message, domain, secret_keys[key_index])
}; };
TestingAttesterSlashingBuilder::double_vote(test_task, validator_indices, signer) TestingAttesterSlashingBuilder::double_vote(test_task, validator_indices, signer, fork, spec)
} }

View File

@ -1,31 +1,24 @@
use crate::test_utils::ProposerSlashingTestTask; use crate::test_utils::ProposerSlashingTestTask;
use crate::*; use crate::*;
use tree_hash::SignedRoot;
/// Builds a `ProposerSlashing`. /// Builds a `ProposerSlashing`.
/// ///
/// This struct should **never be used for production purposes.** /// This struct should **never be used for production purposes.**
pub struct TestingProposerSlashingBuilder(); pub struct TestingProposerSlashingBuilder;
impl TestingProposerSlashingBuilder { impl TestingProposerSlashingBuilder {
/// Builds a `ProposerSlashing` that is a double vote. /// 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`). /// Where domain is a domain "constant" (e.g., `spec.domain_attestation`).
pub fn double_vote<T, F>( pub fn double_vote<T>(
test_task: ProposerSlashingTestTask, test_task: ProposerSlashingTestTask,
mut proposer_index: u64, mut proposer_index: u64,
signer: F, secret_key: &SecretKey,
fork: &Fork,
spec: &ChainSpec,
) -> ProposerSlashing ) -> ProposerSlashing
where where
T: EthSpec, T: EthSpec,
F: Fn(u64, &[u8], Epoch, Domain) -> Signature,
{ {
let slot = Slot::new(0); let slot = Slot::new(0);
let hash_1 = Hash256::from([1; 32]); let hash_1 = Hash256::from([1; 32]);
@ -35,11 +28,13 @@ impl TestingProposerSlashingBuilder {
Hash256::from([2; 32]) Hash256::from([2; 32])
}; };
let mut header_1 = BeaconBlockHeader { let mut signed_header_1 = SignedBeaconBlockHeader {
slot, message: BeaconBlockHeader {
parent_root: hash_1, slot,
state_root: hash_1, parent_root: hash_1,
body_root: hash_1, state_root: hash_1,
body_root: hash_1,
},
signature: Signature::empty_signature(), signature: Signature::empty_signature(),
}; };
@ -49,26 +44,21 @@ impl TestingProposerSlashingBuilder {
Slot::new(0) Slot::new(0)
}; };
let mut header_2 = BeaconBlockHeader { let mut signed_header_2 = SignedBeaconBlockHeader {
parent_root: hash_2, message: BeaconBlockHeader {
slot: slot_2, parent_root: hash_2,
..header_1.clone() 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 { if test_task != ProposerSlashingTestTask::BadProposal1Signature {
header_1.signature = { signed_header_1 = signed_header_1.message.sign::<T>(secret_key, fork, spec);
let message = header_1.signed_root();
signer(proposer_index, &message[..], epoch, Domain::BeaconProposer)
};
} }
if test_task != ProposerSlashingTestTask::BadProposal2Signature { if test_task != ProposerSlashingTestTask::BadProposal2Signature {
header_2.signature = { signed_header_2 = signed_header_2.message.sign::<T>(secret_key, fork, spec);
let message = header_2.signed_root();
signer(proposer_index, &message[..], epoch, Domain::BeaconProposer)
};
} }
if test_task == ProposerSlashingTestTask::ProposerUnknown { if test_task == ProposerSlashingTestTask::ProposerUnknown {
@ -77,8 +67,8 @@ impl TestingProposerSlashingBuilder {
ProposerSlashing { ProposerSlashing {
proposer_index, proposer_index,
header_1, signed_header_1,
header_2, signed_header_2,
} }
} }
} }

View File

@ -1,5 +1,4 @@
use crate::*; use crate::*;
use tree_hash::SignedRoot;
/// Builds an exit to be used for testing purposes. /// Builds an exit to be used for testing purposes.
/// ///
@ -14,24 +13,20 @@ impl TestingVoluntaryExitBuilder {
let exit = VoluntaryExit { let exit = VoluntaryExit {
epoch, epoch,
validator_index, validator_index,
signature: Signature::empty_signature(),
}; };
Self { exit } Self { exit }
} }
/// Signs the exit. /// Build and sign the exit.
/// ///
/// The signing secret key must match that of the exiting validator. /// The signing secret key must match that of the exiting validator.
pub fn sign(&mut self, secret_key: &SecretKey, fork: &Fork, spec: &ChainSpec) { pub fn build(
let message = self.exit.signed_root(); self,
let domain = spec.get_domain(self.exit.epoch, Domain::VoluntaryExit, fork); secret_key: &SecretKey,
fork: &Fork,
self.exit.signature = Signature::new(&message, domain, secret_key); spec: &ChainSpec,
} ) -> SignedVoluntaryExit {
self.exit.sign(secret_key, fork, spec)
/// Builds the exit, consuming the builder.
pub fn build(self) -> VoluntaryExit {
self.exit
} }
} }

View File

@ -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_export]
macro_rules! ssz_tests { macro_rules! ssz_tests {
($type: ty) => { ($type: ty) => {
@ -16,7 +25,12 @@ macro_rules! ssz_tests {
assert_eq!(original, decoded); assert_eq!(original, decoded);
} }
};
}
#[macro_export]
macro_rules! tree_hash_tests {
($type: ty) => {
#[test] #[test]
pub fn test_tree_hash_root() { pub fn test_tree_hash_root() {
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
@ -28,8 +42,6 @@ macro_rules! ssz_tests {
let result = original.tree_hash_root(); let result = original.tree_hash_root();
assert_eq!(result.len(), 32); assert_eq!(result.len(), 32);
// TODO: Add further tests
// https://github.com/sigp/lighthouse/issues/170
} }
}; };
} }

View File

@ -7,6 +7,6 @@ impl TestRandom for Signature {
let mut message = vec![0; 32]; let mut message = vec![0; 32];
rng.fill_bytes(&mut message); rng.fill_bytes(&mut message);
Signature::new(&message, 0, &secret_key) Signature::new(&message, &secret_key)
} }
} }

View File

@ -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 serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
@ -6,7 +8,7 @@ use tree_hash_derive::TreeHash;
/// Information about a `BeaconChain` validator. /// Information about a `BeaconChain` validator.
/// ///
/// Spec v0.9.1 /// Spec v0.10.1
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)]
pub struct Validator { pub struct Validator {
pub pubkey: PublicKeyBytes, pub pubkey: PublicKeyBytes,
@ -39,6 +41,28 @@ impl Validator {
pub fn is_withdrawable_at(&self, epoch: Epoch) -> bool { pub fn is_withdrawable_at(&self, epoch: Epoch) -> bool {
epoch >= self.withdrawable_epoch 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<E: EthSpec>(
&self,
state: &BeaconState<E>,
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 { impl Default for Validator {
@ -115,5 +139,5 @@ mod tests {
assert_eq!(v.is_withdrawable_at(epoch + 1), true); assert_eq!(v.is_withdrawable_at(epoch + 1), true);
} }
ssz_tests!(Validator); ssz_and_tree_hash_tests!(Validator);
} }

Some files were not shown because too many files have changed in this diff Show More